예제 #1
0
 def _ensure_container(self):
     new_container = not self._get_container_status()
     if new_container:
         try:
             subprocess.check_call(
                 ['lxc', 'init', self._image, self._container_name])
         except subprocess.CalledProcessError as e:
             raise ContainerConnectionError('Failed to setup container')
     if self._get_container_status()['status'] == 'Stopped':
         self._configure_container()
         try:
             subprocess.check_call(['lxc', 'start', self._container_name])
         except subprocess.CalledProcessError:
             msg = 'The container could not be started.'
             if self._remote == 'local':
                 msg += ('\nThe files /etc/subuid and /etc/subgid need to '
                         'contain this line for mounting the local folder:'
                         '\n    root:1000:1'
                         '\nNote: Add the line to both files, do not '
                         'remove any existing lines.'
                         '\nRestart lxd after making this change.')
             raise ContainerConnectionError(msg)
     self._wait_for_network()
     if new_container:
         self._container_run(['apt-get', 'update'])
         # Because of https://bugs.launchpad.net/snappy/+bug/1628289
         # Needed to run snapcraft as a snap and build-snaps
         self._container_run(['apt-get', 'install', 'squashfuse', '-y'])
     self._inject_snapcraft(new_container=new_container)
예제 #2
0
 def _ensure_container(self):
     new_container = not self._get_container_status()
     if new_container:
         try:
             subprocess.check_call([
                 'lxc', 'init', self._image, self._container_name])
         except subprocess.CalledProcessError as e:
             raise ContainerConnectionError('Failed to setup container')
     if self._get_container_status()['status'] == 'Stopped':
         self._configure_container()
         try:
             subprocess.check_call([
                 'lxc', 'start', self._container_name])
         except subprocess.CalledProcessError:
             msg = 'The container could not be started.'
             if self._container_name.startswith('local:'):
                 msg += ('\nThe files /etc/subuid and /etc/subgid need to '
                         'contain this line for mounting the local folder:'
                         '\n    root:1000:1'
                         '\nNote: Add the line to both files, do not '
                         'remove any existing lines.'
                         '\nRestart lxd after making this change.')
             raise ContainerConnectionError(msg)
     self._wait_for_network()
     if new_container:
         self._container_run(['apt-get', 'update'])
         self._inject_snapcraft()
예제 #3
0
    def __init__(self, *, output, source, project_options,
                 metadata, container_name, remote=None):
        if not output:
            output = common.format_snap_name(metadata)
        self._snap_output = output
        self._source = os.path.realpath(source)
        self._project_options = project_options
        self._metadata = metadata
        self._project_folder = '/root/build_{}'.format(metadata['name'])

        if not remote:
            remote = _get_default_remote()
        _verify_remote(remote)
        self._container_name = '{}:snapcraft-{}'.format(remote, container_name)
        server_environment = self._get_remote_info()['environment']
        # Use the server architecture to avoid emulation overhead
        try:
            kernel = server_environment['kernel_architecture']
        except KeyError:
            kernel = server_environment['kernelarchitecture']
        self._server_arch = _get_deb_arch(kernel)
        if not self._server_arch:
            raise ContainerConnectionError(
                'Unrecognized server architecture {}'.format(kernel))
        self._image = 'ubuntu:xenial/{}'.format(self._server_arch)
        # Use a temporary folder the 'lxd' snap can access
        lxd_common_dir = os.path.expanduser(
            os.path.join('~', 'snap', 'lxd', 'common'))
        os.makedirs(lxd_common_dir, exist_ok=True)
        self.tmp_dir = tempfile.mkdtemp(prefix='snapcraft', dir=lxd_common_dir)
예제 #4
0
    def __init__(self,
                 *,
                 output,
                 source,
                 project_options,
                 metadata,
                 container_name,
                 remote=None):
        if not output:
            output = common.format_snap_name(metadata)
        self._snap_output = output
        self._source = os.path.realpath(source)
        self._project_options = project_options
        self._metadata = metadata
        self._project_folder = '/root/build_{}'.format(metadata['name'])

        if not remote:
            remote = _get_default_remote()
        _verify_remote(remote)
        self._container_name = '{}:snapcraft-{}'.format(remote, container_name)
        server_environment = self._get_remote_info()['environment']
        # Use the server architecture to avoid emulation overhead
        try:
            kernel = server_environment['kernel_architecture']
        except KeyError:
            kernel = server_environment['kernelarchitecture']
        deb_arch = _get_deb_arch(kernel)
        if not deb_arch:
            raise ContainerConnectionError(
                'Unrecognized server architecture {}'.format(kernel))
        self._host_arch = deb_arch
        self._image = 'ubuntu:xenial/{}'.format(deb_arch)
예제 #5
0
 def _ensure_container(self):
     try:
         subprocess.check_call(
             ['lxc', 'launch', '-e', self._image, self._container_name])
     except subprocess.CalledProcessError as e:
         raise ContainerConnectionError('Failed to setup container')
     self._configure_container()
     self._wait_for_network()
     self._container_run(['apt-get', 'update'])
     self._inject_snapcraft()
예제 #6
0
def _verify_remote(remote):
    """Verify that the lxd remote exists.

    :param str remote: the lxd remote to verify.
    :raises snapcraft.internal.errors.ContainerConnectionError:
        raised if the lxc call listing the remote fails.
    """
    # There is no easy way to grep the results from `lxc remote list`
    # so we try and execute a simple operation against the remote.
    try:
        subprocess.check_output(['lxc', 'list', '{}:'.format(remote)])
    except FileNotFoundError:
        raise ContainerConnectionError(
            'You must have LXD installed in order to use cleanbuild.')
    except subprocess.CalledProcessError as e:
        raise ContainerConnectionError(
            'There are either no permissions or the remote {!r} '
            'does not exist.\n'
            'Verify the existing remotes by running `lxc remote list`\n'
            .format(remote)) from e
예제 #7
0
파일: lxd.py 프로젝트: fnordahl/snapcraft
def _get_default_remote():
    """Query and return the default lxd remote.

    Use the lxc command to query for the default lxd remote. In most
    cases this will return the local remote.

    :returns: default lxd remote.
    :rtype: string.
    :raises snapcraft.internal.errors.ContainerConnectionError:
        raised if the lxc call fails.
    """
    try:
        default_remote = check_output(['lxc', 'remote', 'get-default'])
    except FileNotFoundError:
        raise ContainerConnectionError(
            'You must have LXD installed in order to use cleanbuild.')
    except CalledProcessError:
        raise ContainerConnectionError(
            'Something seems to be wrong with your installation of LXD.')
    return default_remote.decode(sys.getfilesystemencoding()).strip()
예제 #8
0
 def _ensure_container(self):
     try:
         subprocess.check_call([
             'lxc', 'launch', '-e', self._image, self._container_name])
     except subprocess.CalledProcessError as e:
         raise ContainerConnectionError('Failed to setup container')
     self._configure_container()
     self._wait_for_network()
     self._container_run(['apt-get', 'update'])
     # Because of https://bugs.launchpad.net/snappy/+bug/1628289
     # Needed to run snapcraft as a snap and build-snaps
     self._container_run(['apt-get', 'install', 'squashfuse', '-y'])
     self._inject_snapcraft(new_container=True)
예제 #9
0
 def _ensure_container(self):
     if not self._get_container_status():
         check_call(['lxc', 'init', self._image, self._container_name])
     if self._get_container_status()['status'] == 'Stopped':
         check_call([
             'lxc', 'config', 'set', self._container_name,
             'environment.SNAPCRAFT_SETUP_CORE', '1'
         ])
         if os.getenv('SNAPCRAFT_PARTS_URI'):
             check_call([
                 'lxc', 'config', 'set', self._container_name,
                 'environment.SNAPCRAFT_PARTS_URI',
                 os.getenv('SNAPCRAFT_PARTS_URI')
             ])
         # Necessary to read asset files with non-ascii characters.
         check_call([
             'lxc', 'config', 'set', self._container_name,
             'environment.LC_ALL', 'C.UTF-8'
         ])
         if self._container_name.startswith('local:'):
             # Map host user to root inside container
             check_call([
                 'lxc', 'config', 'set', self._container_name, 'raw.idmap',
                 'both {} 0'.format(os.getuid())
             ])
         # Remove existing device (to ensure we update old containers)
         devices = self._get_container_status()['devices']
         if self._project_folder in devices:
             check_call([
                 'lxc', 'config', 'device', 'remove', self._container_name,
                 self._project_folder
             ])
         if 'fuse' not in devices:
             check_call([
                 'lxc', 'config', 'device', 'add', self._container_name,
                 'fuse', 'unix-char', 'path=/dev/fuse'
             ])
         try:
             check_call(['lxc', 'start', self._container_name])
         except CalledProcessError:
             msg = 'The container could not be started.'
             if self._container_name.startswith('local:'):
                 msg += ('\nThe files /etc/subuid and /etc/subgid need to '
                         'contain this line for mounting the local folder:'
                         '\n    root:1000:1'
                         '\nNote: Add the line to both files, do not '
                         'remove any existing lines.'
                         '\nRestart lxd after making this change.')
             raise ContainerConnectionError(msg)
 def _wait_for_network(self):
     logger.info('Waiting for a network connection...')
     not_connected = True
     retry_count = 5
     while not_connected:
         time.sleep(5)
         try:
             self._container_run(['python3', '-c', _NETWORK_PROBE_COMMAND])
             not_connected = False
         except ContainerRunError as e:
             retry_count -= 1
             if retry_count == 0:
                 raise ContainerConnectionError(
                     'No network connection in the container.\n'
                     'If using a proxy, check its configuration.')
     logger.info('Network connection established')
예제 #11
0
    def _remote_mount(self, destination, source):
        # Pipes for sshfs and sftp-server to communicate
        stdin1, stdout1 = os.pipe()
        stdin2, stdout2 = os.pipe()
        # XXX: This needs to be extended once we support other distros
        try:
            self._background_process_run(['/usr/lib/sftp-server'],
                                         stdin=stdin1, stdout=stdout2)
        except FileNotFoundError:
            raise SnapcraftEnvironmentError(
                'You must have openssh-sftp-server installed to use a LXD '
                'remote on a different host.\n'
                )
        except subprocess.CalledProcessError:
            raise SnapcraftEnvironmentError(
                'sftp-server seems to be installed but could not be run.\n'
                )

        # Use sshfs in slave mode to reverse mount the destination
        self._container_run(['apt-get', 'install', '-y', 'sshfs'])
        self._container_run(['mkdir', '-p', destination], user=self._user)
        self._background_process_run([
            'lxc', 'exec', self._container_name, '--',
            'sudo', '-H', '-u', self._user,
            'sshfs', '-o', 'slave', '-o', 'nonempty',
            ':{}'.format(source), destination],
            stdin=stdin2, stdout=stdout1)

        # It may take a second or two for sshfs to come up
        retry_count = 5
        while retry_count:
            time.sleep(1)
            if subprocess.check_output([
                    'lxc', 'exec', self._container_name, '--',
                    'sudo', '-H', '-u', self._user,
                    'ls', self._project_folder]):
                return
            retry_count -= 1
        raise ContainerConnectionError(
            'The project folder could not be mounted.\n'
            'Fuse must be enabled on the LXD host.\n'
            'You can run the following command to enable it:\n'
            'echo Y | sudo tee /sys/module/fuse/parameters/userns_mounts')