Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build_providers: new build provider using multipass (#2100)
build_providers is a new package to handle different providers snapcraft can use to create a snap. In addition to the scaffolding, an implementation to handle multipass is provided. This is the MVP for multipass integration.
- Loading branch information
1 parent
95badff
commit c44a4ec
Showing
20 changed files
with
1,433 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
# | ||
# Copyright (C) 2018 Canonical Ltd | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
# published by the Free Software Foundation. | ||
# | ||
# 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 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 <http://www.gnu.org/licenses/>. | ||
|
||
from . import errors # noqa: F401 | ||
from ._factory import get_provider_for # noqa: F401 | ||
from ._multipass import Multipass # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
# | ||
# Copyright (C) 2018 Canonical Ltd | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
# published by the Free Software Foundation. | ||
# | ||
# 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 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 <http://www.gnu.org/licenses/>. | ||
|
||
import abc | ||
import shlex | ||
from typing import List | ||
|
||
import petname | ||
|
||
|
||
class Provider(): | ||
|
||
def __init__(self, *, project, echoer) -> None: | ||
self.project = project | ||
self.echoer = echoer | ||
# Once https://github.com/CanonicalLtd/multipass/issues/220 is | ||
# closed we can prepend snapcraft- again. | ||
self.instance_name = petname.Generate(2, '-') | ||
self.project_dir = shlex.quote(project.info.name) | ||
|
||
if project.info.version: | ||
self.snap_filename = '{}_{}_{}.snap'.format( | ||
project.info.name, project.info.version, project.deb_arch) | ||
else: | ||
self.snap_filename = '{}_{}.snap'.format( | ||
project.info.name, project.deb_arch) | ||
|
||
def __enter__(self): | ||
self.create() | ||
return self | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
self.destroy() | ||
|
||
@abc.abstractmethod | ||
def _run(self, command: List) -> None: | ||
"""Run a command on the instance.""" | ||
|
||
@abc.abstractmethod | ||
def _launch(self): | ||
"""Launch the instance.""" | ||
|
||
@abc.abstractmethod | ||
def create(self) -> None: | ||
"""Provider steps needed to create a fully functioning environemnt.""" | ||
|
||
@abc.abstractmethod | ||
def destroy(self) -> None: | ||
"""Provider steps needed to ensure the instance is destroyed. | ||
This method should be safe to call multiple times and do nothing | ||
if the instance to destroy is already destroyed. | ||
""" | ||
|
||
@abc.abstractmethod | ||
def provision_project(self, tarball: str) -> None: | ||
"""Provider steps needed to copy project assests to the instance.""" | ||
|
||
@abc.abstractmethod | ||
def build_project(self) -> None: | ||
"""Provider steps needed build the project on the instance.""" | ||
|
||
@abc.abstractmethod | ||
def retrieve_snap(self) -> str: | ||
""" | ||
Provider steps needed to retrieve the built snap from the instance. | ||
:returns: the filename of the retrieved snap. | ||
:rtype: str | ||
""" | ||
|
||
def launch_instance(self) -> None: | ||
self.echoer.info('Creating a build environment named {!r}'.format( | ||
self.instance_name)) | ||
self._launch() | ||
|
||
def setup_snapcraft(self) -> None: | ||
self.echoer.info('Setting up snapcraft in {!r}'.format( | ||
self.instance_name)) | ||
install_cmd = ['sudo', 'snap', 'install', 'snapcraft', '--classic'] | ||
self._run(install_cmd) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
# | ||
# Copyright (C) 2018 Canonical Ltd | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
# published by the Free Software Foundation. | ||
# | ||
# 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 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 <http://www.gnu.org/licenses/>. | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from . import errors | ||
from ._multipass import Multipass | ||
|
||
if TYPE_CHECKING: | ||
from typing import Type # noqa: F401 | ||
|
||
from ._base_provider import Provider # noqa: F401 | ||
|
||
|
||
def get_provider_for(provider_name: str) -> 'Type[Provider]': | ||
"""Returns a Type that can build with provider_name.""" | ||
if provider_name == 'multipass': | ||
return Multipass | ||
else: | ||
raise errors.ProviderNotSupportedError(provider=provider_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
# | ||
# Copyright (C) 2018 Canonical Ltd | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
# published by the Free Software Foundation. | ||
# | ||
# 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 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 <http://www.gnu.org/licenses/>. | ||
|
||
from ._multipass import Multipass # noqa: F401 | ||
from ._multipass_command import MultipassCommand # noqa: F401 |
79 changes: 79 additions & 0 deletions
79
snapcraft/internal/build_providers/_multipass/_instance_info.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
# | ||
# Copyright (C) 2018 Canonical Ltd | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License version 3 as | ||
# published by the Free Software Foundation. | ||
# | ||
# 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 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 <http://www.gnu.org/licenses/>. | ||
|
||
import json | ||
from typing import Type | ||
|
||
from snapcraft.internal.build_providers import errors | ||
|
||
|
||
class InstanceInfo: | ||
|
||
@classmethod | ||
def from_json(cls: Type['InstanceInfo'], *, instance_name: str, | ||
json_info: str) -> 'InstanceInfo': | ||
"""Create an InstanceInfo from json_info retrieved from multipass. | ||
:param str instance_name: the name of the instance. | ||
:param str json_info: a json formatted string with the structure | ||
that would follow the output of a json formatted | ||
multipass info command. | ||
:returns: an InstanceInfo. | ||
:rtype: InstanceInfo | ||
:raises snapcraft.internal.build_providers.ProviderInfoDataKeyError: | ||
if the instance name cannot be found in the given json or if a | ||
required key is missing from that data structure for the instance. | ||
""" | ||
try: | ||
json_data = json.loads(json_info) | ||
except json.decoder.JSONDecodeError as decode_error: | ||
raise errors.ProviderBadDataError( | ||
provider_name='multipass', | ||
data=json_info) from decode_error | ||
try: | ||
instance_info = json_data['info'][instance_name] | ||
except KeyError as missing_key: | ||
raise errors.ProviderInfoDataKeyError( | ||
provider_name='multipass', | ||
missing_key=str(missing_key), | ||
data=json_data) from missing_key | ||
try: | ||
return cls(name=instance_name, | ||
state=instance_info['state'], | ||
image_release=instance_info['image_release']) | ||
except KeyError as missing_key: | ||
raise errors.ProviderInfoDataKeyError( | ||
provider_name='multipass', | ||
missing_key=str(missing_key), | ||
data=instance_info) from missing_key | ||
|
||
def __init__(self, *, name: str, state: str, image_release: str) -> None: | ||
"""Initialize an InstanceInfo. | ||
:param str name: the instance name. | ||
:param str state: the state of the instance which can be any one of | ||
RUNNING, STOPPED, DELETED. | ||
:param str image_release: the Operating System release string for the | ||
image. | ||
""" | ||
# We do not check for validity of state given that new states could | ||
# be introduced. | ||
self.name = name | ||
self.state = state | ||
self.image_release = image_release | ||
|
||
def is_stopped(self): | ||
return self.state == 'STOPPED' |
Oops, something went wrong.