Jonathan Maw pushed to branch juerg/remote at BuildStream / buildstream
Commits:
-
c5778941
by Josh Smith at 2018-10-04T13:43:10Z
-
788cde6a
by Josh Smith at 2018-10-04T13:43:10Z
-
8630bac4
by Tristan Van Berkom at 2018-10-04T14:11:27Z
-
a7984218
by Valentin David at 2018-10-04T14:56:09Z
-
b7b20d9e
by Valentin David at 2018-10-04T15:31:22Z
-
42ea143d
by Chandan Singh at 2018-10-05T12:40:48Z
-
b38f9f9d
by Jonathan Maw at 2018-10-05T13:05:38Z
-
47df1e36
by Jürg Billeter at 2018-10-05T13:30:38Z
4 changed files:
Changes:
... | ... | @@ -212,7 +212,7 @@ class Element(Plugin): |
212 | 212 |
self.__staged_sources_directory = None # Location where Element.stage_sources() was called
|
213 | 213 |
self.__tainted = None # Whether the artifact is tainted and should not be shared
|
214 | 214 |
self.__required = False # Whether the artifact is required in the current session
|
215 |
- self.__build_result = None # The result of assembling this Element
|
|
215 |
+ self.__build_result = None # The result of assembling this Element (success, description, detail)
|
|
216 | 216 |
self._build_log_path = None # The path of the build log for this Element
|
217 | 217 |
|
218 | 218 |
# hash tables of loaded artifact metadata, hashed by key
|
... | ... | @@ -1316,7 +1316,8 @@ class Element(Plugin): |
1316 | 1316 |
#
|
1317 | 1317 |
@contextmanager
|
1318 | 1318 |
def _prepare_sandbox(self, scope, directory, deps='run', integrate=True):
|
1319 |
- with self.__sandbox(directory, config=self.__sandbox_config) as sandbox:
|
|
1319 |
+ # bst shell and bst checkout require a local sandbox.
|
|
1320 |
+ with self.__sandbox(directory, config=self.__sandbox_config, allow_remote=False) as sandbox:
|
|
1320 | 1321 |
|
1321 | 1322 |
# Configure always comes first, and we need it.
|
1322 | 1323 |
self.configure_sandbox(sandbox)
|
... | ... | @@ -1479,11 +1480,13 @@ class Element(Plugin): |
1479 | 1480 |
|
1480 | 1481 |
self._update_state()
|
1481 | 1482 |
|
1482 |
- if self._get_workspace() and self._cached():
|
|
1483 |
+ if self._get_workspace() and self._cached_success():
|
|
1484 |
+ assert utils._is_main_process(), \
|
|
1485 |
+ "Attempted to save workspace configuration from child process"
|
|
1483 | 1486 |
#
|
1484 | 1487 |
# Note that this block can only happen in the
|
1485 |
- # main process, since `self._cached()` cannot
|
|
1486 |
- # be true when assembly is completed in the task.
|
|
1488 |
+ # main process, since `self._cached_success()` cannot
|
|
1489 |
+ # be true when assembly is successful in the task.
|
|
1487 | 1490 |
#
|
1488 | 1491 |
# For this reason, it is safe to update and
|
1489 | 1492 |
# save the workspaces configuration
|
... | ... | @@ -2147,17 +2150,18 @@ class Element(Plugin): |
2147 | 2150 |
# stdout (fileobject): The stream for stdout for the sandbox
|
2148 | 2151 |
# stderr (fileobject): The stream for stderr for the sandbox
|
2149 | 2152 |
# config (SandboxConfig): The SandboxConfig object
|
2153 |
+ # allow_remote (bool): Whether the sandbox is allowed to be remote
|
|
2150 | 2154 |
#
|
2151 | 2155 |
# Yields:
|
2152 | 2156 |
# (Sandbox): A usable sandbox
|
2153 | 2157 |
#
|
2154 | 2158 |
@contextmanager
|
2155 |
- def __sandbox(self, directory, stdout=None, stderr=None, config=None):
|
|
2159 |
+ def __sandbox(self, directory, stdout=None, stderr=None, config=None, allow_remote=True):
|
|
2156 | 2160 |
context = self._get_context()
|
2157 | 2161 |
project = self._get_project()
|
2158 | 2162 |
platform = Platform.get_platform()
|
2159 | 2163 |
|
2160 |
- if directory is not None and self.__use_remote_execution():
|
|
2164 |
+ if directory is not None and allow_remote and self.__use_remote_execution():
|
|
2161 | 2165 |
|
2162 | 2166 |
self.info("Using a remote sandbox for artifact {} with directory '{}'".format(self.name, directory))
|
2163 | 2167 |
|
... | ... | @@ -2171,7 +2175,7 @@ class Element(Plugin): |
2171 | 2175 |
yield sandbox
|
2172 | 2176 |
|
2173 | 2177 |
elif directory is not None and os.path.exists(directory):
|
2174 |
- if self.__remote_execution_url:
|
|
2178 |
+ if allow_remote and self.__remote_execution_url:
|
|
2175 | 2179 |
self.warn("Artifact {} is configured to use remote execution but element plugin does not support it."
|
2176 | 2180 |
.format(self.name), detail="Element plugin '{kind}' does not support virtual directories."
|
2177 | 2181 |
.format(kind=self.get_kind()), warning_token="remote-failure")
|
... | ... | @@ -2191,7 +2195,8 @@ class Element(Plugin): |
2191 | 2195 |
rootdir = tempfile.mkdtemp(prefix="{}-".format(self.normal_name), dir=context.builddir)
|
2192 | 2196 |
|
2193 | 2197 |
# Recursive contextmanager...
|
2194 |
- with self.__sandbox(rootdir, stdout=stdout, stderr=stderr, config=config) as sandbox:
|
|
2198 |
+ with self.__sandbox(rootdir, stdout=stdout, stderr=stderr, config=config,
|
|
2199 |
+ allow_remote=allow_remote) as sandbox:
|
|
2195 | 2200 |
yield sandbox
|
2196 | 2201 |
|
2197 | 2202 |
# Cleanup the build dir
|
... | ... | @@ -686,7 +686,7 @@ def _force_rmtree(rootpath, **kwargs): |
686 | 686 |
|
687 | 687 |
try:
|
688 | 688 |
shutil.rmtree(rootpath, **kwargs)
|
689 |
- except shutil.Error as e:
|
|
689 |
+ except OSError as e:
|
|
690 | 690 |
raise UtilError("Failed to remove cache directory '{}': {}"
|
691 | 691 |
.format(rootpath, e))
|
692 | 692 |
|
1 |
+#!/bin/bash
|
|
2 |
+#
|
|
3 |
+# Copyright 2018 Bloomberg Finance LP
|
|
4 |
+#
|
|
5 |
+# This program is free software; you can redistribute it and/or
|
|
6 |
+# modify it under the terms of the GNU Lesser General Public
|
|
7 |
+# License as published by the Free Software Foundation; either
|
|
8 |
+# version 2 of the License, or (at your option) any later version.
|
|
9 |
+#
|
|
10 |
+# This library is distributed in the hope that it will be useful,
|
|
11 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 |
+# Lesser General Public License for more details.
|
|
14 |
+#
|
|
15 |
+# You should have received a copy of the GNU Lesser General Public
|
|
16 |
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
17 |
+#
|
|
18 |
+# Authors:
|
|
19 |
+# Chadnan Singh <csingh43 bloomberg net>
|
|
20 |
+ |
|
21 |
+# This is a helper script to generate Docker images using checkouts of
|
|
22 |
+# BuildStream elements.
|
|
23 |
+ |
|
24 |
+usage() {
|
|
25 |
+ cat <<EOF
|
|
26 |
+ |
|
27 |
+USAGE: $(basename "$0") [-c BST_CMD] [-m MESSAGE] [-t TAG] [-h] ELEMENT
|
|
28 |
+ |
|
29 |
+Create a Docker image from bst checkout of an element.
|
|
30 |
+ |
|
31 |
+OPTIONS:
|
|
32 |
+ -c BST_CMD Path to BuildStream command (default: bst).
|
|
33 |
+ -m MESSAGE Commit message for the imported image.
|
|
34 |
+ -t TAG Tag of the imported image.
|
|
35 |
+ -h Print this help text and exit.
|
|
36 |
+ |
|
37 |
+EXAMPLES:
|
|
38 |
+ |
|
39 |
+ # Import hello.bst as a Docker image with tag "bst-hello" and message "hello"
|
|
40 |
+ $(basename "$0") -m hello -t bst-hello hello.bst
|
|
41 |
+ |
|
42 |
+ # Import hello.bst as a Docker image with tag "bst-hello" using bst-here
|
|
43 |
+ $(basename "$0") -c bst-here -t bst-hello hello.bst
|
|
44 |
+ |
|
45 |
+EOF
|
|
46 |
+ exit "$1"
|
|
47 |
+}
|
|
48 |
+ |
|
49 |
+die() {
|
|
50 |
+ echo "FATAL: $1" >&2
|
|
51 |
+ exit 1
|
|
52 |
+}
|
|
53 |
+ |
|
54 |
+bst_cmd=bst
|
|
55 |
+docker_import_cmd=(docker import)
|
|
56 |
+docker_image_tag=
|
|
57 |
+ |
|
58 |
+while getopts c:m:t:h arg
|
|
59 |
+do
|
|
60 |
+ case $arg in
|
|
61 |
+ c)
|
|
62 |
+ bst_cmd="$OPTARG"
|
|
63 |
+ ;;
|
|
64 |
+ m)
|
|
65 |
+ docker_import_cmd+=('-m' "$OPTARG")
|
|
66 |
+ ;;
|
|
67 |
+ t)
|
|
68 |
+ docker_image_tag="$OPTARG"
|
|
69 |
+ ;;
|
|
70 |
+ h)
|
|
71 |
+ usage 0
|
|
72 |
+ ;;
|
|
73 |
+ \?)
|
|
74 |
+ usage 1
|
|
75 |
+ esac
|
|
76 |
+done
|
|
77 |
+ |
|
78 |
+shift $((OPTIND-1))
|
|
79 |
+if [[ "$#" != 1 ]]; then
|
|
80 |
+ echo "$0: No element specified" >&2
|
|
81 |
+ usage 1
|
|
82 |
+fi
|
|
83 |
+element="$1"
|
|
84 |
+ |
|
85 |
+# Dump to a temporary file in the current directory.
|
|
86 |
+# NOTE: We use current directory to try to ensure compatibility with scripts
|
|
87 |
+# like bst-here, assuming that the current working directory is mounted
|
|
88 |
+# inside the container.
|
|
89 |
+ |
|
90 |
+checkout_tar="bst-checkout-$(basename "$element")-$RANDOM.tar"
|
|
91 |
+ |
|
92 |
+echo "INFO: Checking out $element ..." >&2
|
|
93 |
+$bst_cmd checkout --tar "$element" "$checkout_tar" || die "Failed to checkout $element"
|
|
94 |
+echo "INFO: Successfully checked out $element" >&2
|
|
95 |
+ |
|
96 |
+echo "INFO: Importing Docker image ..."
|
|
97 |
+"${docker_import_cmd[@]}" "$checkout_tar" "$docker_image_tag" || die "Failed to import Docker image from tarball"
|
|
98 |
+echo "INFO: Successfully import Docker image $docker_image_tag"
|
|
99 |
+ |
|
100 |
+echo "INFO: Cleaning up ..."
|
|
101 |
+rm "$checkout_tar" || die "Failed to remove $checkout_tar"
|
|
102 |
+echo "INFO: Clean up finished"
|
... | ... | @@ -43,7 +43,8 @@ DATA_DIR = os.path.join( |
43 | 43 |
)
|
44 | 44 |
|
45 | 45 |
|
46 |
-def open_workspace(cli, tmpdir, datafiles, kind, track, suffix='', workspace_dir=None, project_path=None):
|
|
46 |
+def open_workspace(cli, tmpdir, datafiles, kind, track, suffix='', workspace_dir=None,
|
|
47 |
+ project_path=None, element_attrs=None):
|
|
47 | 48 |
if not workspace_dir:
|
48 | 49 |
workspace_dir = os.path.join(str(tmpdir), 'workspace{}'.format(suffix))
|
49 | 50 |
if not project_path:
|
... | ... | @@ -69,6 +70,8 @@ def open_workspace(cli, tmpdir, datafiles, kind, track, suffix='', workspace_dir |
69 | 70 |
repo.source_config(ref=ref)
|
70 | 71 |
]
|
71 | 72 |
}
|
73 |
+ if element_attrs:
|
|
74 |
+ element = {**element, **element_attrs}
|
|
72 | 75 |
_yaml.dump(element,
|
73 | 76 |
os.path.join(element_path,
|
74 | 77 |
element_name))
|
... | ... | @@ -854,3 +857,22 @@ def test_cache_key_workspace_in_dependencies(cli, tmpdir, datafiles, strict): |
854 | 857 |
|
855 | 858 |
# Check that the original /usr/bin/hello is not in the checkout
|
856 | 859 |
assert not os.path.exists(os.path.join(checkout, 'usr', 'bin', 'hello'))
|
860 |
+ |
|
861 |
+ |
|
862 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
863 |
+def test_multiple_failed_builds(cli, tmpdir, datafiles):
|
|
864 |
+ element_config = {
|
|
865 |
+ "kind": "manual",
|
|
866 |
+ "config": {
|
|
867 |
+ "configure-commands": [
|
|
868 |
+ "unknown_command_that_will_fail"
|
|
869 |
+ ]
|
|
870 |
+ }
|
|
871 |
+ }
|
|
872 |
+ element_name, project, _ = open_workspace(cli, tmpdir, datafiles,
|
|
873 |
+ "git", False, element_attrs=element_config)
|
|
874 |
+ |
|
875 |
+ for _ in range(2):
|
|
876 |
+ result = cli.run(project=project, args=["build", element_name])
|
|
877 |
+ assert "BUG" not in result.stderr
|
|
878 |
+ assert cli.get_element_state(project, element_name) != "cached"
|