Skip to content

Commit 4e56eb0

Browse files
Add kubernetes connector.
1 parent d710b53 commit 4e56eb0

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import os
2+
3+
from tempfile import mkstemp
4+
5+
import click
6+
import six
7+
8+
from pyinfra import local, logger
9+
from pyinfra.api import QuoteString, StringCommand
10+
from pyinfra.api.exceptions import ConnectError, InventoryError, PyinfraError
11+
from pyinfra.api.util import get_file_io, memoize
12+
from pyinfra.progress import progress_spinner
13+
14+
from .local import run_shell_command as run_local_shell_command
15+
from .util import make_unix_command
16+
17+
@memoize
18+
def show_warning():
19+
logger.warning('The @kubernetes connector is in beta!')
20+
21+
def make_names_data(pod=None):
22+
if not pod:
23+
raise InventoryError('No pod provided!')
24+
25+
namespace = 'default'
26+
if '/' in pod:
27+
namespace, pod = pod.split('/', 2)
28+
29+
show_warning()
30+
31+
# Save the namespace and pod name as the hostname, @kubernetes group
32+
yield '@kubernetes/{0}/{1}'.format(namespace, pod), \
33+
{'namespace': namespace, 'pod': pod}, ['@kubernetes']
34+
35+
def connect(state, host, for_fact=None):
36+
return True
37+
38+
def disconnect(state, host):
39+
return True
40+
41+
def run_shell_command(
42+
state, host, command,
43+
get_pty=False,
44+
timeout=None,
45+
stdin=None,
46+
success_exit_codes=None,
47+
print_output=False,
48+
print_input=False,
49+
return_combined_output=False,
50+
**command_kwargs
51+
):
52+
# Don't sudo/su, see docker connector.
53+
for key in ('sudo', 'su_user'):
54+
command_kwargs.pop(key, None)
55+
56+
print(host.host_data)
57+
58+
command = make_unix_command(command, **command_kwargs)
59+
command = QuoteString(command)
60+
61+
if 'container' in host.host_data:
62+
container = ['-c', host.host_data['container']]
63+
else:
64+
container = []
65+
66+
kubectl_flags = '-it' if get_pty else '-i'
67+
kubectl_command = StringCommand(
68+
'kubectl', 'exec', kubectl_flags,
69+
'-n', host.host_data['namespace'],
70+
*container,
71+
host.host_data['pod'],
72+
'--', 'sh', '-c', command,
73+
)
74+
75+
return run_local_shell_command(
76+
state, host, kubectl_command,
77+
timeout=timeout,
78+
stdin=stdin,
79+
success_exit_codes=success_exit_codes,
80+
print_output=print_output,
81+
print_input=print_input,
82+
return_combined_output=return_combined_output,
83+
)
84+
85+
def put_file(
86+
state, host, filename_or_io, remote_filename,
87+
print_output=False, print_input=False,
88+
**kwargs # ignored (sudo/etc)
89+
):
90+
'''
91+
Upload a file/IO object to the target pod by copying it to a
92+
temporary location and then uploading it into the container using
93+
``kubectl cp``.
94+
'''
95+
96+
_, temp_filename = mkstemp()
97+
98+
try:
99+
# Load our file or IO object and write it to the temporary file
100+
with get_file_io(filename_or_io) as file_io:
101+
with open(temp_filename, 'wb') as temp_f:
102+
data = file_io.read()
103+
104+
if isinstance(data, six.text_type):
105+
data = data.encode()
106+
107+
temp_f.write(data)
108+
109+
if 'container' in host.host_data:
110+
container = ['-c', host.host_data['container']]
111+
else:
112+
container = []
113+
114+
kubectl_command = StringCommand(
115+
'kubectl', 'cp',
116+
temp_filename,
117+
'{0}/{1}:{2}'.format(host.host_data['namespace'],
118+
host.host_data['pod'],
119+
remote_filename),
120+
*container,
121+
)
122+
123+
status, _, stderr = run_local_shell_command(
124+
state, host, kubectl_command,
125+
print_output=print_output,
126+
print_input=print_input,
127+
)
128+
129+
finally:
130+
os.remove(temp_filename)
131+
132+
if not status:
133+
raise IOError('\n'.join(stderr))
134+
135+
if print_output:
136+
click.echo('{0}file uploaded to container: {1}'.format(
137+
host.print_prefix, remote_filename,
138+
), err=True)
139+
140+
return status
141+
142+
143+
def get_file(
144+
state, host, remote_filename, filename_or_io,
145+
print_output=False, print_input=False,
146+
**kwargs # ignored (sudo/etc)
147+
):
148+
'''
149+
Download a file from the target pod by copying it to a temporary
150+
location and then reading that into our final file/IO object.
151+
'''
152+
153+
_, temp_filename = mkstemp()
154+
155+
try:
156+
if 'container' in host.host_data:
157+
container = ['-c', host.host_data['container']]
158+
else:
159+
container = []
160+
161+
kubectl_command = StringCommand(
162+
'kubectl', 'cp',
163+
'{0}/{1}:{2}'.format(host.host_data['namespace'],
164+
host.host_data['pod'],
165+
remote_filename),
166+
temp_filename,
167+
*container,
168+
)
169+
170+
status, _, stderr = run_local_shell_command(
171+
state, host, kubectl_command,
172+
print_output=print_output,
173+
print_input=print_input,
174+
)
175+
176+
# Load the temporary file and write it to our file or IO object
177+
with open(temp_filename) as temp_f:
178+
with get_file_io(filename_or_io, 'wb') as file_io:
179+
data = temp_f.read()
180+
181+
if isinstance(data, six.text_type):
182+
data = data.encode()
183+
184+
file_io.write(data)
185+
finally:
186+
os.remove(temp_filename)
187+
188+
if not status:
189+
raise IOError('\n'.join(stderr))
190+
191+
if print_output:
192+
click.echo('{0}file downloaded from pod: {1}'.format(
193+
host.print_prefix, remote_filename,
194+
), err=True)
195+
196+
return status
197+
198+
199+
EXECUTION_CONNECTOR = True

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
'ansible = pyinfra.api.connectors.ansible',
105105
'chroot = pyinfra.api.connectors.chroot',
106106
'docker = pyinfra.api.connectors.docker',
107+
'kubernetes = pyinfra.api.connectors.kubernetes',
107108
'local = pyinfra.api.connectors.local',
108109
'mech = pyinfra.api.connectors.mech',
109110
'ssh = pyinfra.api.connectors.ssh',

0 commit comments

Comments
 (0)