#1263 lxd: pass through commands into the container

Open kalikiana Pseudo commit used to compare (edb56c9...0667868)
Missing base report.

Unable to compare commits because the base of the pull request did not upload a coverage report.

Learn more here.

Showing 4 of 5 files from the diff.
Newly tracked file
snapcraft/main.py changed.
Other files not tracked by Codecov
manual-tests.md has changed.

@@ -19,15 +19,15 @@
Loading
19 19
snapcraft
20 20
21 21
Usage:
22 -
  snapcraft [options] [--enable-geoip --no-parallel-build --remote=<remote>]
22 +
  snapcraft [options] [--enable-geoip --no-parallel-build]
23 23
  snapcraft [options] init
24 24
  snapcraft [options] pull [<part> ...]  [--enable-geoip]
25 25
  snapcraft [options] build [<part> ...] [--no-parallel-build]
26 26
  snapcraft [options] stage [<part> ...]
27 27
  snapcraft [options] prime [<part> ...]
28 28
  snapcraft [options] strip [<part> ...]
29 29
  snapcraft [options] clean [<part> ...] [--step <step>]
30 -
  snapcraft [options] snap [<directory> --output <snap-file>] [--remote=<remote>]
30 +
  snapcraft [options] snap [<directory> --output <snap-file>]
31 31
  snapcraft [options] cleanbuild [--remote=<remote>]
32 32
  snapcraft [options] login
33 33
  snapcraft [options] logout
@@ -281,16 +281,29 @@
Loading
281 281
    return functions[function[0]]
282 282
283 283
284 +
def _is_containerbuild():
285 +
    return os.environ.get('SNAPCRAFT_CONTAINER_BUILDS')
286 +
287 +
284 288
def run(args, project_options):  # noqa
285 289
    lifecycle_command = _get_lifecycle_command(args)
286 290
    argless_command = _get_command_from_arg(args)
287 291
    if lifecycle_command:
288 -
        lifecycle.execute(
289 -
            lifecycle_command, project_options, args['<part>'])
292 +
        if _is_containerbuild():
293 +
            lifecycle.containerbuild(lifecycle_command, project_options,
294 +
                                     args['<part>'])
295 +
        else:
296 +
            lifecycle.execute(
297 +
                lifecycle_command, project_options, args['<part>'])
290 298
    elif argless_command:
291 299
        argless_command()
292 300
    elif args['clean']:
293 -
        _run_clean(args, project_options)
301 +
        if _is_containerbuild():
302 +
            step = args['--step'] or 'pull'
303 +
            lifecycle.containerbuild('clean', project_options,
304 +
                                     args=['--step', step] + args['<part>'])
305 +
        else:
306 +
            _run_clean(args, project_options)
294 307
    elif args['cleanbuild']:
295 308
        lifecycle.cleanbuild(project_options, remote=args['--remote']),
296 309
    elif _is_store_command(args):
@@ -303,19 +316,29 @@
Loading
303 316
    elif args['enable-ci']:
304 317
        enable_ci(args['<ci-system>'], args['--refresh'])
305 318
    elif args['update']:
306 -
        parts.update()
319 +
        if _is_containerbuild():
320 +
            lifecycle.containerbuild('update', project_options)
321 +
        else:
322 +
            parts.update()
307 323
    elif args['define']:
308 -
        parts.define(args['<part-name>'])
324 +
        if _is_containerbuild():
325 +
            lifecycle.containerbuild('update', project_options,
326 +
                                     args=args['<part-name>'])
327 +
        else:
328 +
            parts.define(args['<part-name>'])
309 329
    elif args['search']:
310 -
        parts.search(' '.join(args['<query>']))
311 -
    elif os.environ.get('SNAPCRAFT_CONTAINER_BUILDS'):
312 -
        lifecycle.containerbuild(project_options,
313 -
                                 args['--output'], args['--remote'])
330 +
        if _is_containerbuild():
331 +
            lifecycle.containerbuild('search', project_options,
332 +
                                     args=' '.join(args['<query>']))
333 +
        else:
334 +
            parts.search(' '.join(args['<query>']))
314 335
    else:  # snap by default:
315 -
        if args['--remote']:
316 -
            raise RuntimeError(
317 -
                '--remote can only be used with SNAPCRAFT_CONTAINER_BUILDS')
318 -
        lifecycle.snap(project_options, args['<directory>'], args['--output'])
336 +
        if _is_containerbuild():
337 +
            lifecycle.containerbuild('snap', project_options, args['--output'],
338 +
                                     args['<directory>'])
339 +
        else:
340 +
            lifecycle.snap(project_options, args['<directory>'],
341 +
                           args['--output'])
319 342
320 343
    return project_options
321 344
@@ -322 +345 @@
Loading

@@ -325,11 +325,11 @@
Loading
325 325
    return _tar_filter
326 326
327 327
328 -
def containerbuild(project_options, output=None, remote=''):
328 +
def containerbuild(step, project_options, output=None, args=[]):
329 329
    config = snapcraft.internal.load_config(project_options)
330 330
    lxd.Project(output=output, source=os.path.curdir,
331 -
                project_options=project_options, remote=remote,
332 -
                metadata=config.get_metadata()).execute()
331 +
                project_options=project_options,
332 +
                metadata=config.get_metadata()).execute(step, args)
333 333
334 334
335 335
def cleanbuild(project_options, remote=''):
@@ -336 +336 @@
Loading

@@ -32,75 +32,6 @@
Loading
32 32
from snapcraft import tests
33 33
34 34
35 -
class SnapCommandContainerized(tests.TestCase):
36 -
37 -
    yaml_template = """name: snap-test
38 -
version: 1.0
39 -
summary: test containerized snap
40 -
description: if snap is succesful a snap package will be available
41 -
architectures: ['amd64']
42 -
confinement: strict
43 -
grade: stable
44 -
45 -
parts:
46 -
    part1:
47 -
      plugin: nil
48 -
"""
49 -
50 -
    scenarios = [
51 -
        ('local', dict(args=[], remote='local')),
52 -
        ('remote', dict(args=['--remote=my-remote'], remote='my-remote')),
53 -
    ]
54 -
55 -
    def make_snapcraft_yaml(self, n=1):
56 -
        super().make_snapcraft_yaml(self.yaml_template)
57 -
58 -
    def test_snap_defaults(self):
59 -
        fake_lxd = tests.fixture_setup.FakeLXD()
60 -
        self.useFixture(fake_lxd)
61 -
        fake_logger = fixtures.FakeLogger(level=logging.INFO)
62 -
        self.useFixture(fake_logger)
63 -
        self.useFixture(fixtures.EnvironmentVariable(
64 -
                'SNAPCRAFT_CONTAINER_BUILDS', '1'))
65 -
        self.make_snapcraft_yaml()
66 -
67 -
        main(['snap', '--debug', *self.args])
68 -
69 -
        source = os.path.realpath(os.path.curdir)
70 -
        self.assertIn(
71 -
            'Mounting {} into container\n'
72 -
            'Waiting for a network connection...\n'
73 -
            'Network connection established\n'.format(source),
74 -
            fake_logger.output)
75 -
76 -
        container_name = '{}:snapcraft-snap-test'.format(self.remote)
77 -
        project_folder = 'build_snap-test'
78 -
        fake_lxd.check_call_mock.assert_has_calls([
79 -
            call(['lxc', 'start', container_name]),
80 -
            call(['lxc', 'config', 'device', 'add', container_name,
81 -
                  project_folder, 'disk', 'source={}'.format(source),
82 -
                  'path=/{}'.format(project_folder)]),
83 -
            call(['lxc', 'exec', container_name,
84 -
                  '--env', 'HOME=/{}'.format(project_folder), '--',
85 -
                  'python3', '-c',
86 -
                  'import urllib.request; '
87 -
                  'urllib.request.urlopen('
88 -
                  '"http://start.ubuntu.com/connectivity-check.html", '
89 -
                  'timeout=5)']),
90 -
            call(['lxc', 'exec', container_name,
91 -
                  '--env', 'HOME=/{}'.format(project_folder), '--',
92 -
                  'apt-get', 'update']),
93 -
            call(['lxc', 'exec', container_name,
94 -
                  '--env', 'HOME=/{}'.format(project_folder), '--',
95 -
                  'apt-get', 'install', 'snapcraft', '-y']),
96 -
            call(['lxc', 'exec', container_name,
97 -
                  '--env', 'HOME=/{}'.format(project_folder), '--',
98 -
                  'snapcraft', 'snap', '--output',
99 -
                  'snap-test_1.0_amd64.snap']),
100 -
            call(['lxc', 'stop', '-f', container_name]),
101 -
        ])
102 -
103 -
104 35
class SnapCommandTestCase(tests.TestCase):
105 36
106 37
    yaml_template = """name: snap-test
@@ -169,6 +100,51 @@
Loading
169 100
            '-noappend', '-comp', 'xz', '-no-xattrs', '-all-root'],
170 101
            stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
171 102
103 +
    def test_snap_containerized(self):
104 +
        fake_lxd = tests.fixture_setup.FakeLXD()
105 +
        self.useFixture(fake_lxd)
106 +
        fake_logger = fixtures.FakeLogger(level=logging.INFO)
107 +
        self.useFixture(fake_logger)
108 +
        self.useFixture(fixtures.EnvironmentVariable(
109 +
                'SNAPCRAFT_CONTAINER_BUILDS', '1'))
110 +
        self.make_snapcraft_yaml()
111 +
112 +
        main(['snap', '--debug'])
113 +
114 +
        source = os.path.realpath(os.path.curdir)
115 +
        self.assertIn(
116 +
            'Mounting {} into container\n'
117 +
            'Waiting for a network connection...\n'
118 +
            'Network connection established\n'.format(source),
119 +
            fake_logger.output)
120 +
121 +
        container_name = 'local:snapcraft-snap-test'
122 +
        project_folder = 'build_snap-test'
123 +
        fake_lxd.check_call_mock.assert_has_calls([
124 +
            call(['lxc', 'start', container_name]),
125 +
            call(['lxc', 'config', 'device', 'add', container_name,
126 +
                  project_folder, 'disk', 'source={}'.format(source),
127 +
                  'path=/{}'.format(project_folder)]),
128 +
            call(['lxc', 'exec', container_name,
129 +
                  '--env', 'HOME=/{}'.format(project_folder), '--',
130 +
                  'python3', '-c',
131 +
                  'import urllib.request; '
132 +
                  'urllib.request.urlopen('
133 +
                  '"http://start.ubuntu.com/connectivity-check.html", '
134 +
                  'timeout=5)']),
135 +
            call(['lxc', 'exec', container_name,
136 +
                  '--env', 'HOME=/{}'.format(project_folder), '--',
137 +
                  'apt-get', 'update']),
138 +
            call(['lxc', 'exec', container_name,
139 +
                  '--env', 'HOME=/{}'.format(project_folder), '--',
140 +
                  'apt-get', 'install', 'snapcraft', '-y']),
141 +
            call(['lxc', 'exec', container_name,
142 +
                  '--env', 'HOME=/{}'.format(project_folder), '--',
143 +
                  'snapcraft', 'snap', '--output',
144 +
                  'snap-test_1.0_amd64.snap']),
145 +
            call(['lxc', 'stop', '-f', container_name]),
146 +
        ])
147 +
172 148
    @mock.patch('snapcraft.internal.lifecycle.ProgressBar')
173 149
    def test_snap_defaults_on_a_tty(self, progress_mock):
174 150
        fake_logger = fixtures.FakeLogger(level=logging.INFO)
@@ -365,12 +341,6 @@
Loading
365 341
366 342
        self.assertThat('mysnap.snap', FileExists())
367 343
368 -
    def test_snap_containerless_no_remote(self):
369 -
        raised = self.assertRaises(RuntimeError,
370 -
                                   main, ['--debug', '--remote=my-remote'])
371 -
        expected = '--remote can only be used with SNAPCRAFT_CONTAINER_BUILDS'
372 -
        self.assertIn(expected, str(raised))
373 -
374 344
    @mock.patch('time.time')
375 345
    def test_snap_renames_stale_snap_build(self, mocked_time):
376 346
        fake_logger = fixtures.FakeLogger(level=logging.INFO)
@@ -377 +347 @@
Loading

@@ -85,15 +85,19 @@
Loading
85 85
            print('Stopping {}'.format(self._container_name))
86 86
            check_call(['lxc', 'stop', '-f', self._container_name])
87 87
88 -
    def execute(self):
88 +
    def execute(self, step='snap', args=None):
89 89
        with self._ensure_started():
90 90
            self._setup_project()
91 91
            self._wait_for_network()
92 92
            self._container_run(['apt-get', 'update'])
93 93
            self._container_run(['apt-get', 'install', 'snapcraft', '-y'])
94 +
            command = ['snapcraft', step]
95 +
            if step == 'snap':
96 +
                command += ['--output', self._snap_output]
97 +
            if args:
98 +
                command += args
94 99
            try:
95 -
                self._container_run(
96 -
                    ['snapcraft', 'snap', '--output', self._snap_output])
100 +
                self._container_run(command)
97 101
            except CalledProcessError as e:
98 102
                if self._project_options.debug:
99 103
                    logger.info('Debug mode enabled, dropping into a shell')
@@ -192,6 +196,12 @@
Loading
192 196
        # Nothing to do
193 197
        pass
194 198
199 +
    def execute(self, step='snap', args=None):
200 +
        super().execute(step, args)
201 +
        if step == 'clean' and not args:
202 +
            print('Deleting {}'.format(self._container_name))
203 +
            check_call(['lxc', 'delete', '-f', self._container_name])
204 +
195 205
196 206
def _get_default_remote():
197 207
    """Query and return the default lxd remote.
@@ -198 +208 @@
Loading

Unable to process changes.

No base report to compare against.

Files Coverage
snapcraft 95.27%
Project Totals (218 files) 95.27%
Loading