Merge lp:~larryprice/libertine/libertine-shell-ssh into lp:libertine
- libertine-shell-ssh
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Christopher Townsend | ||||
Approved revision: | 423 | ||||
Merged at revision: | 459 | ||||
Proposed branch: | lp:~larryprice/libertine/libertine-shell-ssh | ||||
Merge into: | lp:libertine | ||||
Diff against target: |
373 lines (+337/-3) 5 files modified
debian/libertine-tools.install (+3/-0) tools/CMakeLists.txt (+3/-3) tools/completions/libertine-shell (+50/-0) tools/libertine-shell (+255/-0) tools/libertine-shell.1 (+26/-0) |
||||
To merge this branch: | bzr merge lp:~larryprice/libertine/libertine-shell-ssh | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Townsend | Approve | ||
Libertine CI Bot | continuous-integration | Approve | |
Review via email: mp+319369@code.launchpad.net |
Commit message
Add tool to easily ssh into libertine containers.
Description of the change
Add tool to easily ssh into libertine containers.
Libertine CI Bot (libertine-ci-bot) wrote : | # |
- 417. By Larry Price
-
how did i forget to add libertine-shell to install...
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:417
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 418. By Larry Price
-
prevent non-lx* containers from using libertine-shell
- 419. By Larry Price
-
merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:419
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 420. By Larry Price
-
merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:420
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
I built the packages, installed them, and rebooted the system. I then logged in to U8, and this is what I get when running libertine-shell:
$ libertine-shell -i test-lxd
Traceback (most recent call last):
File "/usr/bin/
main()
File "/usr/bin/
with open(os.
FileNotFoundError: [Errno 2] No such file or directory: '/home/
- 421. By Larry Price
-
use bind mounted dir if necessary
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:421
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
Ok, next issue:)
I'm trying this on a machine that I have never used ssh on. I get the following error:
$ libertine-shell -i test-lxd
Traceback (most recent call last):
File "/usr/bin/
main()
File "/usr/bin/
config.
File "/usr/bin/
all_lines, host_line = self._ssh_
File "/usr/bin/
with open(config_path, 'r') as f:
FileNotFoundError: [Errno 2] No such file or directory: '/home/
- 422. By Larry Price
-
don't read non-existent config
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:422
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 423. By Larry Price
-
specify container type in help/manpage
Christopher Townsend (townsend) wrote : | # |
Ok, works fine. +1
Preview Diff
1 | === modified file 'debian/libertine-tools.install' | |||
2 | --- debian/libertine-tools.install 2017-03-13 15:50:54 +0000 | |||
3 | +++ debian/libertine-tools.install 2017-04-07 13:51:54 +0000 | |||
4 | @@ -1,3 +1,6 @@ | |||
5 | 1 | usr/bin/libertine-container-manager | 1 | usr/bin/libertine-container-manager |
6 | 2 | usr/bin/libertine-shell | ||
7 | 2 | usr/share/bash-completion/completions/libertine-container-manager | 3 | usr/share/bash-completion/completions/libertine-container-manager |
8 | 3 | usr/share/man/*/libertine-container-manager.1 | 4 | usr/share/man/*/libertine-container-manager.1 |
9 | 5 | usr/share/bash-completion/completions/libertine-shell | ||
10 | 6 | usr/share/man/*/libertine-shell.1 | ||
11 | 4 | 7 | ||
12 | === modified file 'tools/CMakeLists.txt' | |||
13 | --- tools/CMakeLists.txt 2017-01-31 16:28:31 +0000 | |||
14 | +++ tools/CMakeLists.txt 2017-04-07 13:51:54 +0000 | |||
15 | @@ -1,9 +1,9 @@ | |||
17 | 1 | install(PROGRAMS libertine-container-manager libertine-launch | 1 | install(PROGRAMS libertine-container-manager libertine-launch libertine-shell |
18 | 2 | libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined | 2 | libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined |
19 | 3 | DESTINATION ${CMAKE_INSTALL_BINDIR}) | 3 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
20 | 4 | install(FILES libertine-launch.1 libertine-container-manager.1 | 4 | install(FILES libertine-launch.1 libertine-container-manager.1 |
22 | 5 | libertine-xmir.1 | 5 | libertine-xmir.1 libertine-shell.1 |
23 | 6 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 | 6 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
24 | 7 | COMPONENT doc) | 7 | COMPONENT doc) |
26 | 8 | install(FILES completions/libertine-container-manager | 8 | install(FILES completions/libertine-container-manager completions/libertine-shell |
27 | 9 | DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/) | 9 | DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/) |
28 | 10 | 10 | ||
29 | === added file 'tools/completions/libertine-shell' | |||
30 | --- tools/completions/libertine-shell 1970-01-01 00:00:00 +0000 | |||
31 | +++ tools/completions/libertine-shell 2017-04-07 13:51:54 +0000 | |||
32 | @@ -0,0 +1,50 @@ | |||
33 | 1 | # Copyright (C) 2017 Canonical Ltd. | ||
34 | 2 | # | ||
35 | 3 | # This program is free software: you can redistribute it and/or modify | ||
36 | 4 | # it under the terms of the GNU General Public License as published by | ||
37 | 5 | # the Free Software Foundation; version 3 of the License. | ||
38 | 6 | # | ||
39 | 7 | # This program is distributed in the hope that it will be useful, | ||
40 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
41 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
42 | 10 | # GNU General Public License for more details. | ||
43 | 11 | # | ||
44 | 12 | # You should have received a copy of the GNU General Public License | ||
45 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
46 | 14 | |||
47 | 15 | |||
48 | 16 | _libertine-shell() | ||
49 | 17 | { | ||
50 | 18 | local cur cmd opts | ||
51 | 19 | COMPREPLY=() | ||
52 | 20 | cur=${COMP_WORDS[COMP_CWORD]} | ||
53 | 21 | |||
54 | 22 | if [[ ${COMP_CWORD} -gt 1 ]]; then | ||
55 | 23 | cmd="$cur" | ||
56 | 24 | if [[ ${cmd} != -* ]]; then | ||
57 | 25 | cmd=${COMP_WORDS[COMP_CWORD-1]} | ||
58 | 26 | fi | ||
59 | 27 | |||
60 | 28 | case "${cmd}" in | ||
61 | 29 | "--username" ) | ||
62 | 30 | opts=$(users) | ||
63 | 31 | ;; | ||
64 | 32 | "--identity-file" ) | ||
65 | 33 | compopt -o filenames | ||
66 | 34 | local files=("${cur}"*) | ||
67 | 35 | [[ -e ${files[0]} ]] && COMPREPLY=( "${files[@]// /\ }" ) | ||
68 | 36 | return 0 | ||
69 | 37 | ;; | ||
70 | 38 | esac | ||
71 | 39 | fi | ||
72 | 40 | |||
73 | 41 | if [[ -z ${opts} ]]; then | ||
74 | 42 | opts="--help --identity-file --username --assume-yes --id" | ||
75 | 43 | fi | ||
76 | 44 | |||
77 | 45 | if [[ -n "${opts}" ]]; then | ||
78 | 46 | COMPREPLY=( $(compgen -W "${opts}" -- "${COMP_WORDS[COMP_CWORD]}") ) | ||
79 | 47 | return 0 | ||
80 | 48 | fi | ||
81 | 49 | } | ||
82 | 50 | complete -F _libertine-shell libertine-shell | ||
83 | 0 | 51 | ||
84 | === added file 'tools/libertine-shell' | |||
85 | --- tools/libertine-shell 1970-01-01 00:00:00 +0000 | |||
86 | +++ tools/libertine-shell 2017-04-07 13:51:54 +0000 | |||
87 | @@ -0,0 +1,255 @@ | |||
88 | 1 | #!/usr/bin/python3 | ||
89 | 2 | # -*- coding: utf-8 -*- | ||
90 | 3 | # | ||
91 | 4 | # Copyright (C) 2017 Canonical Ltd. | ||
92 | 5 | # | ||
93 | 6 | # This program is free software: you can redistribute it and/or modify | ||
94 | 7 | # it under the terms of the GNU General Public License as published by | ||
95 | 8 | # the Free Software Foundation; version 3 of the License. | ||
96 | 9 | # | ||
97 | 10 | # This program is distributed in the hope that it will be useful, | ||
98 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
99 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
100 | 13 | # GNU General Public License for more details. | ||
101 | 14 | # | ||
102 | 15 | # You should have received a copy of the GNU General Public License | ||
103 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
104 | 17 | |||
105 | 18 | import argparse | ||
106 | 19 | import os | ||
107 | 20 | import shlex | ||
108 | 21 | import subprocess | ||
109 | 22 | import sys | ||
110 | 23 | |||
111 | 24 | from libertine import ContainersConfig, utils, Libertine | ||
112 | 25 | from shutil import copyfile | ||
113 | 26 | |||
114 | 27 | import gettext | ||
115 | 28 | gettext.textdomain('libertine') | ||
116 | 29 | _ = gettext.gettext | ||
117 | 30 | |||
118 | 31 | class ShellConfig(object): | ||
119 | 32 | def __init__(self, args): | ||
120 | 33 | super().__init__() | ||
121 | 34 | self._validate(self._parse(args)) | ||
122 | 35 | |||
123 | 36 | @property | ||
124 | 37 | def public_key(self): | ||
125 | 38 | return "{}.pub".format(self.identity_file) | ||
126 | 39 | |||
127 | 40 | def _parse(self, args): | ||
128 | 41 | arg_parser = argparse.ArgumentParser(description=_('Launch an SSH session within a lxc/lxd Libertine container')) | ||
129 | 42 | arg_parser.add_argument('-i', '--id', | ||
130 | 43 | help=_('Container identifier')) | ||
131 | 44 | arg_parser.add_argument('-u', '--username', | ||
132 | 45 | help=_('Container username')) | ||
133 | 46 | arg_parser.add_argument('-f', '--identity-file', | ||
134 | 47 | help=_('SSH key to be used')) | ||
135 | 48 | arg_parser.add_argument('-y', '--assume-yes', | ||
136 | 49 | action='store_true', | ||
137 | 50 | help=_('Assume yes to all prompts')) | ||
138 | 51 | |||
139 | 52 | return arg_parser.parse_args(args=args) | ||
140 | 53 | |||
141 | 54 | def _validate(self, options): | ||
142 | 55 | self.assume_yes = options.assume_yes or False | ||
143 | 56 | self.username = options.username | ||
144 | 57 | |||
145 | 58 | config = ContainersConfig.ContainersConfig() | ||
146 | 59 | self.container_id = config.check_container_id(options.id) | ||
147 | 60 | self.container_type = config.get_container_type(self.container_id) | ||
148 | 61 | utils.get_logger().debug("Using Container ID: {}".format(self.container_id)) | ||
149 | 62 | |||
150 | 63 | self.identity_file = None | ||
151 | 64 | |||
152 | 65 | if options.identity_file: | ||
153 | 66 | utils.get_logger().debug("Public key file path: {}".format(options.identity_file)) | ||
154 | 67 | |||
155 | 68 | if not os.path.exists(options.identity_file): | ||
156 | 69 | if not os.path.dirname(options.identity_file) and os.path.exists(os.path.join(os.environ['HOME'], '.ssh', options.identity_file)): | ||
157 | 70 | self.identity_file = os.path.join(os.environ['HOME'], '.ssh', options.identity_file) | ||
158 | 71 | else: | ||
159 | 72 | utils.get_logger().error(_("Identity file not found at '{}'. Leave blank for default.").format(options.identity_file)) | ||
160 | 73 | sys.exit(1) | ||
161 | 74 | else: | ||
162 | 75 | self.identity_file = options.identity_file | ||
163 | 76 | |||
164 | 77 | if not os.path.exists(self.public_key): | ||
165 | 78 | utils.get_logger().error(_("Corresponding public key not found for '{}'.").format(self.identity_file)) | ||
166 | 79 | sys.exit(1) | ||
167 | 80 | |||
168 | 81 | def setup_public_keys(self, hostname): | ||
169 | 82 | should_save = False | ||
170 | 83 | all_lines, host_line = self._ssh_config_lines(hostname) | ||
171 | 84 | |||
172 | 85 | if host_line: | ||
173 | 86 | identity_files = [line for line in host_line.split('\n') if "IdentityFile " in line] | ||
174 | 87 | if identity_files: | ||
175 | 88 | if not self.identity_file: | ||
176 | 89 | self.identity_file = identity_files[-1].split(' ')[-1] | ||
177 | 90 | elif not [i for i in identity_files if self.identity_file in i]: | ||
178 | 91 | should_save = True | ||
179 | 92 | host_line += "\n\tIdentityFile {}".format(self.identity_file) | ||
180 | 93 | if self.username and not "User {}".format(self.username) in host_line: | ||
181 | 94 | host_line += "\tUser {}\n".format(self.username) | ||
182 | 95 | elif self.identity_file: | ||
183 | 96 | should_save = True | ||
184 | 97 | host_line = "Host {}\n\tIdentityFile {}".format(hostname, self.identity_file) | ||
185 | 98 | all_lines.append(host_line) | ||
186 | 99 | |||
187 | 100 | if host_line and self.username and not "User {}".format(self.username) in host_line: | ||
188 | 101 | should_save = True | ||
189 | 102 | host_line += "\n\tUser {}\n".format(self.username) | ||
190 | 103 | |||
191 | 104 | if not self.identity_file: | ||
192 | 105 | DEFAULT_KEYS = ["id_dsa", "id_rsa", "id_ecdsa", "id_ed25519"] | ||
193 | 106 | keys = [key for key in os.listdir(os.path.join(os.environ['HOME'], '.ssh')) if key in DEFAULT_KEYS] | ||
194 | 107 | if keys: | ||
195 | 108 | self.identity_file = os.path.join(os.environ['HOME'], '.ssh', keys[0]) | ||
196 | 109 | |||
197 | 110 | if self.identity_file and not (os.path.exists(self.identity_file) or os.path.exists(self.public_key)): | ||
198 | 111 | utils.get_logger().error(_("Configured identity file or public key matching '{}' do not exist.").format(self.identity_file)) | ||
199 | 112 | sys.exit(1) | ||
200 | 113 | |||
201 | 114 | if should_save and self._ask_save_ssh_config(): | ||
202 | 115 | try: | ||
203 | 116 | config_path = os.path.join(os.environ['HOME'], ".ssh", "config") | ||
204 | 117 | copyfile(config_path, config_path + ".bak") | ||
205 | 118 | with open(config_path, 'w') as f: | ||
206 | 119 | for i in range(0, len(all_lines)): | ||
207 | 120 | if i == (len(all_lines)-1): | ||
208 | 121 | newlines = '\n' | ||
209 | 122 | else: | ||
210 | 123 | newlines = '\n\n' | ||
211 | 124 | |||
212 | 125 | if not all_lines[i].startswith("Host {}".format(hostname)): | ||
213 | 126 | f.write(all_lines[i] + newlines) | ||
214 | 127 | else: | ||
215 | 128 | f.write(host_line + newlines) | ||
216 | 129 | except Exception as e: | ||
217 | 130 | utils.get_logger().warning("Error caught during config edit: {}.".format(str(e))) | ||
218 | 131 | utils.get_logger().warning("Restoring previous version of '{}' and continuing".format(config_path)) | ||
219 | 132 | copyfile(config_path + ".bak", config_path) | ||
220 | 133 | |||
221 | 134 | os.remove(config_path + ".bak") | ||
222 | 135 | |||
223 | 136 | def _ssh_config_lines(self, hostname): | ||
224 | 137 | ssh_dir = os.path.join(os.environ['HOME'], ".ssh") | ||
225 | 138 | if not os.path.exists(ssh_dir): | ||
226 | 139 | utils.get_logger().error(_("It looks like no SSH keys are set up. Please generate a key and try again. " | ||
227 | 140 | "You can use the following command to generate an appropriate key:\n" | ||
228 | 141 | "\tssh-keygen -t rsa -b 4096 -C 'your_email@example.com'")) | ||
229 | 142 | sys.exit(1) | ||
230 | 143 | |||
231 | 144 | config_path = os.path.join(ssh_dir, "config") | ||
232 | 145 | |||
233 | 146 | all_lines = [] | ||
234 | 147 | host_line = [] | ||
235 | 148 | if os.path.exists(config_path): | ||
236 | 149 | with open(config_path, 'r') as f: | ||
237 | 150 | all_lines = [line.strip() for line in f.read().split('\n\n')] | ||
238 | 151 | host_line = [line for line in all_lines if line.startswith("Host {}".format(hostname))] | ||
239 | 152 | if host_line: | ||
240 | 153 | host_line = host_line[-1] | ||
241 | 154 | |||
242 | 155 | return (all_lines, host_line or '') | ||
243 | 156 | |||
244 | 157 | def _ask_save_ssh_config(self): | ||
245 | 158 | if not self.assume_yes: | ||
246 | 159 | if self.username: | ||
247 | 160 | prompt = input(_("Always use '{}' as identity file and username '{}' " | ||
248 | 161 | "when connecting to '{}'? [Yn]").format(self.identity_file, self.username, self.container_id)) | ||
249 | 162 | else: | ||
250 | 163 | prompt = input(_("Always use '{}' as identity file when connecting to '{}'? [Yn]").format(self.identity_file, self.container_id)) | ||
251 | 164 | |||
252 | 165 | if not (prompt == _('Y') or prompt == _('y') or prompt == ''): | ||
253 | 166 | utils.get_logger().debug("Responded 'no' to saving identity configuration") | ||
254 | 167 | return False | ||
255 | 168 | |||
256 | 169 | return True | ||
257 | 170 | |||
258 | 171 | def command(self, hostname): | ||
259 | 172 | if self.username: | ||
260 | 173 | hostname = "{}@{}".format(self.username, hostname) | ||
261 | 174 | |||
262 | 175 | options = "" | ||
263 | 176 | if self.identity_file: | ||
264 | 177 | options += " -i {} ".format(self.identity_file) | ||
265 | 178 | |||
266 | 179 | return "ssh {} {} -t 'bash -s'".format(options, hostname) | ||
267 | 180 | |||
268 | 181 | def get_ip_host_file_path(self, home): | ||
269 | 182 | if home in ContainersConfig.ContainersConfig().get_container_bind_mounts(self.container_id): | ||
270 | 183 | return os.path.join(home, 'libertine-shell-found-ip') | ||
271 | 184 | |||
272 | 185 | return os.path.join(utils.get_libertine_container_home_dir(self.container_id), 'libertine-shell-found-ip') | ||
273 | 186 | |||
274 | 187 | |||
275 | 188 | def main(): | ||
276 | 189 | if subprocess.Popen(shlex.split("bash -c \"which sshd &> /dev/null\"")).wait() != 0: | ||
277 | 190 | utils.get_logger().error(_("No sshd found. You can install openssh with the following command:\n" | ||
278 | 191 | "\tapt install openssh-client")) | ||
279 | 192 | sys.exit(1) | ||
280 | 193 | |||
281 | 194 | config = ShellConfig(sys.argv[1:]) | ||
282 | 195 | |||
283 | 196 | if not (config.container_type == 'lxd' or config.container_type == 'lxc'): | ||
284 | 197 | utils.get_logger().error(_("'{}' is a '{}' container. Only 'lxd' or 'lxc' " | ||
285 | 198 | "containers are able to use this tool.".format( | ||
286 | 199 | config.container_id, config.container_type))) | ||
287 | 200 | sys.exit(1) | ||
288 | 201 | |||
289 | 202 | container = Libertine.LibertineContainer(config.container_id) | ||
290 | 203 | |||
291 | 204 | |||
292 | 205 | with Libertine.ContainerRunning(container.container): | ||
293 | 206 | if not container.container._binary_exists("sshd"): | ||
294 | 207 | if not config.assume_yes: | ||
295 | 208 | prompt = input(_("openssh-server not detected in container '{}'. Install now? [Yn]").format(config.container_id)) | ||
296 | 209 | if not (prompt == _('Y') or prompt == _('y') or prompt == ''): | ||
297 | 210 | utils.get_logger().debug("Responded 'no' to openssh-server installation") | ||
298 | 211 | sys.exit(1) | ||
299 | 212 | |||
300 | 213 | if not container.install_package("openssh-server") or not container.exec_command("update-rc.d ssh defaults"): | ||
301 | 214 | utils.get_logger().error(_("Failed to install openssh-server")) | ||
302 | 215 | sys.exit(1) | ||
303 | 216 | |||
304 | 217 | username = config.username or os.environ['USER'] | ||
305 | 218 | |||
306 | 219 | if not container.exec_command('bash -c "hostname -I | awk \'{ print $1 }\' > /home/%s/libertine-shell-found-ip"' % username): | ||
307 | 220 | utils.get_logger().error(_("Unable to get IP address for '{}'".format(config.container_id))) | ||
308 | 221 | sys.exit(1) | ||
309 | 222 | |||
310 | 223 | with open(config.get_ip_host_file_path('/home/%s' % username)) as f: | ||
311 | 224 | hostname = f.read().strip() | ||
312 | 225 | utils.get_logger().debug(hostname) | ||
313 | 226 | if not hostname: | ||
314 | 227 | utils.get_logger().error(_("Unable to get IP address for '{}'".format(config.container_id))) | ||
315 | 228 | sys.exit(1) | ||
316 | 229 | |||
317 | 230 | config.setup_public_keys(hostname) | ||
318 | 231 | |||
319 | 232 | if not container.exec_command('test -e /home/{}/.ssh/authorized_keys'.format(username)): | ||
320 | 233 | container.exec_command("bash -c 'mkdir -p /home/{user}/.ssh && " | ||
321 | 234 | "chown {user}:{user} /home/{user}/.ssh'".format(user=username)) | ||
322 | 235 | container.exec_command("bash -c 'touch /home/{user}/.ssh/authorized_keys && " | ||
323 | 236 | "chown {user}:{user} /home/{user}/.ssh/authorized_keys'".format(user=username)) | ||
324 | 237 | |||
325 | 238 | with open(config.public_key, 'r') as f: | ||
326 | 239 | public_key = f.read().strip() | ||
327 | 240 | if not container.exec_command('grep -q "{}$" /home/{}/.ssh/authorized_keys'.format(public_key, username)): | ||
328 | 241 | if not config.assume_yes: | ||
329 | 242 | prompt = input(_("OK to add public key '{}' to container '{}'? [Yn]").format(config.public_key, config.container_id)) | ||
330 | 243 | if not (prompt == _('Y') or prompt == _('y') or prompt == ''): | ||
331 | 244 | utils.get_logger().error(_("Public key must be added to container to continue.")) | ||
332 | 245 | sys.exit(1) | ||
333 | 246 | |||
334 | 247 | if not container.exec_command('bash -c "echo {} >> /home/{}/.ssh/authorized_keys"'.format(public_key, username)): | ||
335 | 248 | utils.get_logger().error(_("Failed to add public key to container's authorized keys.")) | ||
336 | 249 | sys.exit(1) | ||
337 | 250 | |||
338 | 251 | subprocess.call(shlex.split(config.command(hostname))) | ||
339 | 252 | |||
340 | 253 | |||
341 | 254 | if __name__ == '__main__': | ||
342 | 255 | main() | ||
343 | 0 | 256 | ||
344 | === added file 'tools/libertine-shell.1' | |||
345 | --- tools/libertine-shell.1 1970-01-01 00:00:00 +0000 | |||
346 | +++ tools/libertine-shell.1 2017-04-07 13:51:54 +0000 | |||
347 | @@ -0,0 +1,26 @@ | |||
348 | 1 | .TH libertine-shell "1" "March 2017" "libertine-shell 1.0" "User Commands" | ||
349 | 2 | |||
350 | 3 | .SH NAME | ||
351 | 4 | libertine-shell \- Launch an SSH session connected to a lxc/lxd Libertine container | ||
352 | 5 | |||
353 | 6 | .SH DESCRIPTION | ||
354 | 7 | usage: libertine\-shell [\-h] [-i ID] [-u USER] [-f IDENTITY_FILE_PATH] [-y] | ||
355 | 8 | .PP | ||
356 | 9 | Launch an SSH session connected to a Libertine container | ||
357 | 10 | |||
358 | 11 | .SS "optional arguments:" | ||
359 | 12 | .TP | ||
360 | 13 | \fB\-h\fR, \fB\-\-help\fR | ||
361 | 14 | show this help message and exit | ||
362 | 15 | .TP | ||
363 | 16 | \fB\-i\fR, \fB\-\-id\fR | ||
364 | 17 | Container identifier (default container if none specified) | ||
365 | 18 | .TP | ||
366 | 19 | \fB\-u\fR, \fB\-\-username\fR | ||
367 | 20 | Container username (current username if none specified) | ||
368 | 21 | .TP | ||
369 | 22 | \fB\-f\fR, \fB\-\-identity-file\fR | ||
370 | 23 | Full path of SSH key to be used | ||
371 | 24 | .TP | ||
372 | 25 | \fB\-y\fR, \fB\-\-assume-yes\fR | ||
373 | 26 | Assume yes to all prompts (installations, public key copy, configuration changes) |
FAILED: Continuous integration, rev:416 /jenkins. canonical. com/libertine/ job/lp- libertine- ci/452/ /jenkins. canonical. com/libertine/ job/build/ 837/console /jenkins. canonical. com/libertine/ job/build- 0-fetch/ 847 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 839/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/839/ console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 839/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/839/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/libertine/ job/lp- libertine- ci/452/ rebuild
https:/