Merge ~sylvain-pineau/checkbox-ng:fix-1841874 into checkbox-ng:master

Proposed by Sylvain Pineau
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 983da89c370c3deb4f54b7dd64930c6e771bd9b1
Merged at revision: b09afbb6132f73ff0c95ebee521d1970c8d57b9a
Proposed branch: ~sylvain-pineau/checkbox-ng:fix-1841874
Merge into: checkbox-ng:master
Diff against target: 148 lines (+72/-13)
1 file modified
plainbox/impl/execution.py (+72/-13)
Reviewer Review Type Date Requested Status
Maciej Kisielewski (community) Approve
Review via email: mp+373195@code.launchpad.net

Description of the change

The old runner/ctrl code did prepare a differential env for jobs running as root to only carry on some specific user var bit not all.

The unified runner did not have this distinction. This patch brings back the get_differential_execution_environment function.

To post a comment you must log in.
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

tested with the stress auto test plan with both power30 and reboot30 selected.

Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Hmmm. Now I finally understand what this was for :D

Thanks for fixing it!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/plainbox/impl/execution.py b/plainbox/impl/execution.py
2index f73aec2..410df68 100644
3--- a/plainbox/impl/execution.py
4+++ b/plainbox/impl/execution.py
5@@ -278,12 +278,13 @@ class UnifiedRunner(IJobRunner):
6 {"ID": job.id, "CMD": cmd,
7 "ENV": env})
8 if 'preserve-cwd' in job.get_flag_set() or os.getenv("SNAP"):
9- return_code = call(extcmd_popen, cmd, stdin=subprocess.PIPE,
10- env=env)
11+ return_code = call(
12+ extcmd_popen, cmd, stdin=subprocess.PIPE, env=env)
13 else:
14 with self.temporary_cwd(job, config) as cwd_dir:
15- return_code = call(extcmd_popen, cmd,
16- stdin=subprocess.PIPE, env=env, cwd=cwd_dir)
17+ return_code = call(
18+ extcmd_popen, cmd, stdin=subprocess.PIPE, env=env,
19+ cwd=cwd_dir)
20 if 'noreturn' in job.get_flag_set():
21 import signal
22 signal.pause()
23@@ -365,10 +366,11 @@ class UnifiedRunner(IJobRunner):
24 if leftovers:
25 logger.warning(
26 _("Job {0} created leftover filesystem artefacts"
27- " in its working directory").format(job.id))
28+ " in its working directory").format(job.id))
29 for item in leftovers:
30- logger.warning(_("Leftover file/directory: %r"),
31- os.path.relpath(item, cwd_dir))
32+ logger.warning(_(
33+ "Leftover file/directory: %r"),
34+ os.path.relpath(item, cwd_dir))
35 logger.warning(
36 _("Please store desired files in $PLAINBOX_SESSION_SHARE"
37 "and use regular temporary files for everything else"))
38@@ -385,8 +387,8 @@ class UnifiedRunner(IJobRunner):
39 in_r, in_w = os.pipe()
40 os.write(in_w, self._password_provider() + b'\n')
41 cmd = ['sudo', '--prompt', '', '--reset-timestamp', '--stdin',
42- '--user', 'root', 'kill', '-s', str(signal),
43- '-{}'.format(self._running_jobs_pid)]
44+ '--user', 'root', 'kill', '-s', str(signal),
45+ '-{}'.format(self._running_jobs_pid)]
46 try:
47 subprocess.check_call(cmd, stdin=in_r)
48 except subprocess.CalledProcessError:
49@@ -418,14 +420,13 @@ class FakeJobRunner(UnifiedRunner):
50 builder.return_code = 0
51 return builder.get_result()
52
53+
54 def get_execution_environment(job, config, session_dir, nest_dir):
55 """
56 Get the environment required to execute the specified job:
57
58 :param job:
59 job definition with the command and environment definitions
60- :param job_state:
61- The JobState associated to the job to execute.
62 :param config:
63 A PlainBoxConfig instance which can be used to load missing environment
64 definitions that apply to all jobs. It is used to provide values for
65@@ -477,6 +478,7 @@ def get_execution_environment(job, config, session_dir, nest_dir):
66 [nest_dir] + env.get("PATH", "").split(os.pathsep))
67 # Add per-session shared state directory
68 env['PLAINBOX_SESSION_SHARE'] = os.path.join(session_dir, "CHECKBOX_DATA")
69+
70 def set_if_not_none(envvar, source):
71 """Update env if the source variable is not None"""
72 if source is not None:
73@@ -497,6 +499,59 @@ def get_execution_environment(job, config, session_dir, nest_dir):
74 env[env_var] = config.environment[env_var]
75 return env
76
77+
78+def get_differential_execution_environment(job, config, session_dir, nest_dir):
79+ """
80+ Get the environment required to execute the specified job:
81+
82+ :param job:
83+ job definition with the command and environment definitions
84+ :param config:
85+ A PlainBoxConfig instance which can be used to load missing
86+ environment definitions that apply to all jobs. It is used to
87+ provide values for missing environment variables that are required
88+ by the job (as expressed by the environ key in the job definition
89+ file).
90+ :param session_dir:
91+ Base directory of the session this job will execute in.
92+ This directory is used to co-locate some data that is unique to
93+ this execution as well as data that is shared by all executions.
94+ :param nest_dir:
95+ A directory with a nest of symlinks to all executables required to
96+ execute the specified job. This is simply passed to
97+ :meth:`get_execution_environment()` directly.
98+ :returns:
99+ Differential environment (see below).
100+
101+ This implementation computes the desired environment (as it was
102+ computed in the base class) and then discards all of the environment
103+ variables that are identical in both sets. The exception are variables
104+ that are mentioned in
105+ :meth:`plainbox.impl.job.JobDefinition.get_environ_settings()` which
106+ are always retained.
107+ """
108+ base_env = os.environ
109+ target_env = get_execution_environment(job, config, session_dir, nest_dir)
110+ delta_env = {
111+ key: value
112+ for key, value in target_env.items()
113+ if key not in base_env or base_env[key] != value
114+ or key in job.get_environ_settings()
115+ }
116+ if 'reset-locale' in job.get_flag_set():
117+ delta_env['LANG'] = 'C.UTF-8'
118+ delta_env['LANGUAGE'] = ''
119+ delta_env['LC_ALL'] = 'C.UTF-8'
120+ # Preserve the copy_vars variables + those prefixed with SNAP on Snappy
121+ if (os.getenv("SNAP") or os.getenv("SNAP_APP_PATH")):
122+ copy_vars = ['PYTHONHOME', 'PYTHONUSERBASE', 'LD_LIBRARY_PATH',
123+ 'GI_TYPELIB_PATH']
124+ for key, value in base_env.items():
125+ if key in copy_vars or key.startswith('SNAP'):
126+ delta_env[key] = value
127+ return delta_env
128+
129+
130 def get_execution_command(job, config, session_dir,
131 nest_dir, target_user=None):
132 """Generate a command argv to run in the shell."""
133@@ -509,9 +564,13 @@ def get_execution_command(job, config, session_dir,
134 # - gets password as the first line of stdin (--stdin)
135 # - change the user to the target user (--user)
136 cmd = ['sudo', '--prompt', '', '--reset-timestamp', '--stdin',
137- '--user', target_user]
138+ '--user', target_user]
139 cmd += ['env']
140- env = get_execution_environment(job, config, session_dir, nest_dir)
141+ if target_user:
142+ env = get_differential_execution_environment(
143+ job, config, session_dir, nest_dir)
144+ else:
145+ env = get_execution_environment(job, config, session_dir, nest_dir)
146 cmd += ["{key}={value}".format(key=key, value=value)
147 for key, value in sorted(env.items())]
148 cmd += [job.shell, '-c', job.command]

Subscribers

People subscribed via source and target branches