kubernator 1.0.16__py3-none-any.whl → 1.0.24.dev20251109010128__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kubernator/__init__.py +1 -1
- kubernator/api.py +180 -67
- kubernator/app.py +7 -3
- kubernator/plugins/gke.py +105 -0
- kubernator/plugins/helm.py +107 -18
- kubernator/plugins/istio.py +150 -37
- kubernator/plugins/k8s.py +122 -52
- kubernator/plugins/k8s_api.py +30 -26
- kubernator/plugins/kops.py +5 -4
- kubernator/plugins/kubectl.py +35 -4
- kubernator/plugins/minikube.py +48 -3
- kubernator/plugins/template.py +2 -2
- kubernator/proc.py +24 -2
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/METADATA +32 -17
- kubernator-1.0.24.dev20251109010128.dist-info/RECORD +31 -0
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/WHEEL +1 -1
- kubernator-1.0.16.dist-info/RECORD +0 -30
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/entry_points.txt +0 -0
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/namespace_packages.txt +0 -0
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/top_level.txt +0 -0
- {kubernator-1.0.16.dist-info → kubernator-1.0.24.dev20251109010128.dist-info}/zip-safe +0 -0
kubernator/__init__.py
CHANGED
kubernator/api.py
CHANGED
|
@@ -23,6 +23,7 @@ import os
|
|
|
23
23
|
import platform
|
|
24
24
|
import re
|
|
25
25
|
import sys
|
|
26
|
+
import textwrap
|
|
26
27
|
import traceback
|
|
27
28
|
import urllib.parse
|
|
28
29
|
from collections.abc import Callable
|
|
@@ -38,14 +39,16 @@ from typing import Optional, Union, MutableSequence
|
|
|
38
39
|
|
|
39
40
|
import requests
|
|
40
41
|
import yaml
|
|
41
|
-
from platformdirs import user_cache_dir
|
|
42
42
|
from diff_match_patch import diff_match_patch
|
|
43
|
+
from gevent import sleep
|
|
43
44
|
from jinja2 import (Environment,
|
|
44
45
|
ChainableUndefined,
|
|
45
46
|
make_logging_undefined,
|
|
46
47
|
Template as JinjaTemplate,
|
|
47
48
|
pass_context)
|
|
48
49
|
from jsonschema import validators
|
|
50
|
+
from platformdirs import user_cache_dir
|
|
51
|
+
from yaml import MarkedYAMLError
|
|
49
52
|
|
|
50
53
|
from kubernator._json_path import jp # noqa: F401
|
|
51
54
|
from kubernator._k8s_client_patches import (URLLIB_HEADERS_PATCH,
|
|
@@ -57,6 +60,98 @@ _CACHE_HEADER_TRANSLATION = {"etag": "if-none-match",
|
|
|
57
60
|
_CACHE_HEADERS = ("etag", "last-modified")
|
|
58
61
|
|
|
59
62
|
|
|
63
|
+
def to_json(obj: Union[dict, list]):
|
|
64
|
+
return json.dumps(obj)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def to_yaml_str(s: str):
|
|
68
|
+
return yaml.safe_dump(s)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def to_json_yaml_str(obj: Union[dict, list]):
|
|
72
|
+
"""
|
|
73
|
+
Takes `obj`, dumps as json representation, converts json representation to YAML string literal.
|
|
74
|
+
"""
|
|
75
|
+
return to_yaml_str(to_json(obj))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def to_yaml_str_block(s: str, indent: int = 4, pretty_indent: int = 2):
|
|
79
|
+
"""
|
|
80
|
+
Takes a multiline string, dedents it then indents it `indent` spaces for in-yaml alignment and
|
|
81
|
+
`pretty-indent` spaces for in-block alignment.
|
|
82
|
+
"""
|
|
83
|
+
return (f"|+{pretty_indent}\n" +
|
|
84
|
+
textwrap.indent(textwrap.dedent(s), " " * (indent + pretty_indent)))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def to_json_yaml_str_block(obj: Union[str, dict, list], indent: int = 4, pretty_indent=2):
|
|
88
|
+
"""
|
|
89
|
+
Takes an `obj`, serializes it as pretty JSON with `pretty_indent` in-json indentation and then
|
|
90
|
+
passes it to `to_yaml_str_block`.
|
|
91
|
+
"""
|
|
92
|
+
return to_yaml_str_block(json.dumps(obj, indent=pretty_indent), indent=indent, pretty_indent=pretty_indent)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def to_yaml(obj: Union[dict, list], level_indent: int, indent: int):
|
|
96
|
+
s = yaml.safe_dump(obj, indent=indent)
|
|
97
|
+
return "\n" + textwrap.indent(s, " " * level_indent)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TemplateEngine:
|
|
101
|
+
VARIABLE_START_STRING = "{${"
|
|
102
|
+
VARIABLE_END_STRING = "}$}"
|
|
103
|
+
|
|
104
|
+
def __init__(self, logger):
|
|
105
|
+
self.template_failures = 0
|
|
106
|
+
self.templates = {}
|
|
107
|
+
|
|
108
|
+
class CollectingUndefined(ChainableUndefined):
|
|
109
|
+
__slots__ = ()
|
|
110
|
+
|
|
111
|
+
def __str__(self):
|
|
112
|
+
self.template_failures += 1
|
|
113
|
+
return super().__str__()
|
|
114
|
+
|
|
115
|
+
logging_undefined = make_logging_undefined(
|
|
116
|
+
logger=logger,
|
|
117
|
+
base=CollectingUndefined
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
@pass_context
|
|
121
|
+
def variable_finalizer(ctx, value):
|
|
122
|
+
normalized_value = str(value)
|
|
123
|
+
if self.VARIABLE_START_STRING in normalized_value and self.VARIABLE_END_STRING in normalized_value:
|
|
124
|
+
value_template_content = sys.intern(normalized_value)
|
|
125
|
+
env: Environment = ctx.environment
|
|
126
|
+
value_template = self.templates.get(value_template_content)
|
|
127
|
+
if not value_template:
|
|
128
|
+
value_template = env.from_string(value_template_content, env.globals)
|
|
129
|
+
self.templates[value_template_content] = value_template
|
|
130
|
+
return value_template.render(ctx.parent)
|
|
131
|
+
|
|
132
|
+
return normalized_value
|
|
133
|
+
|
|
134
|
+
self.env = Environment(variable_start_string=self.VARIABLE_START_STRING,
|
|
135
|
+
variable_end_string=self.VARIABLE_END_STRING,
|
|
136
|
+
autoescape=False,
|
|
137
|
+
finalize=variable_finalizer,
|
|
138
|
+
undefined=logging_undefined,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
self.env.filters["to_json"] = to_json
|
|
142
|
+
self.env.filters["to_yaml_str"] = to_yaml_str
|
|
143
|
+
self.env.filters["to_yaml"] = to_yaml
|
|
144
|
+
self.env.filters["to_yaml_str_block"] = to_yaml_str_block
|
|
145
|
+
self.env.filters["to_json_yaml_str_block"] = to_json_yaml_str_block
|
|
146
|
+
self.env.filters["to_json_yaml_str"] = to_json_yaml_str
|
|
147
|
+
|
|
148
|
+
def from_string(self, template):
|
|
149
|
+
return self.env.from_string(template)
|
|
150
|
+
|
|
151
|
+
def failures(self):
|
|
152
|
+
return self.template_failures
|
|
153
|
+
|
|
154
|
+
|
|
60
155
|
def calling_frame_source(depth=2):
|
|
61
156
|
f = traceback.extract_stack(limit=depth + 1)[0]
|
|
62
157
|
return f"file {f.filename}, line {f.lineno} in {f.name}"
|
|
@@ -87,18 +182,33 @@ def scan_dir(logger, path: Path, path_filter: Callable[[os.DirEntry], bool], exc
|
|
|
87
182
|
yield path / f
|
|
88
183
|
|
|
89
184
|
|
|
185
|
+
def parse_yaml_docs(document: str, source=None):
|
|
186
|
+
try:
|
|
187
|
+
return list(d for d in yaml.safe_load_all(document) if d)
|
|
188
|
+
except MarkedYAMLError:
|
|
189
|
+
raise
|
|
190
|
+
|
|
191
|
+
|
|
90
192
|
class FileType(Enum):
|
|
91
|
-
|
|
92
|
-
|
|
193
|
+
TEXT = (lambda x: x,)
|
|
194
|
+
BINARY = (lambda x: x,)
|
|
195
|
+
JSON = (json.loads,)
|
|
196
|
+
YAML = (parse_yaml_docs,)
|
|
93
197
|
|
|
94
198
|
def __init__(self, func):
|
|
95
199
|
self.func = func
|
|
96
200
|
|
|
97
201
|
|
|
98
|
-
def _load_file(logger, path: Path, file_type: FileType, source=None
|
|
99
|
-
|
|
202
|
+
def _load_file(logger, path: Path, file_type: FileType, source=None,
|
|
203
|
+
template_engine: Optional[TemplateEngine] = None,
|
|
204
|
+
template_context: Optional[dict] = None) -> Iterable[dict]:
|
|
205
|
+
with open(path, "rb" if file_type == FileType.BINARY else "rt") as f:
|
|
100
206
|
try:
|
|
101
|
-
|
|
207
|
+
if template_engine and not file_type == FileType.BINARY:
|
|
208
|
+
raw_data = template_engine.from_string(f.read()).render(template_context)
|
|
209
|
+
else:
|
|
210
|
+
raw_data = f.read()
|
|
211
|
+
data = file_type.func(raw_data)
|
|
102
212
|
if isinstance(data, GeneratorType):
|
|
103
213
|
data = list(data)
|
|
104
214
|
return data
|
|
@@ -108,13 +218,29 @@ def _load_file(logger, path: Path, file_type: FileType, source=None) -> Iterable
|
|
|
108
218
|
|
|
109
219
|
|
|
110
220
|
def _download_remote_file(url, file_name, cache: dict):
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
221
|
+
retry_delay = 0
|
|
222
|
+
while True:
|
|
223
|
+
if retry_delay:
|
|
224
|
+
sleep(retry_delay)
|
|
225
|
+
|
|
226
|
+
with requests.get(url, headers=cache, stream=True) as r:
|
|
227
|
+
if r.status_code == 429:
|
|
228
|
+
if not retry_delay:
|
|
229
|
+
retry_delay = 0.2
|
|
230
|
+
else:
|
|
231
|
+
retry_delay *= 2.0
|
|
232
|
+
if retry_delay > 2.5:
|
|
233
|
+
retry_delay = 2.5
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
r.raise_for_status()
|
|
237
|
+
if r.status_code != 304:
|
|
238
|
+
with open(file_name, "wb") as out:
|
|
239
|
+
for chunk in r.iter_content(chunk_size=65535):
|
|
240
|
+
out.write(chunk)
|
|
241
|
+
return dict(r.headers)
|
|
242
|
+
else:
|
|
243
|
+
return None
|
|
118
244
|
|
|
119
245
|
|
|
120
246
|
def get_app_cache_dir():
|
|
@@ -174,9 +300,12 @@ def load_remote_file(logger, url, file_type: FileType, category: str = "k8s", su
|
|
|
174
300
|
return _load_file(logger, file_name, file_type, url)
|
|
175
301
|
|
|
176
302
|
|
|
177
|
-
def load_file(logger, path: Path, file_type: FileType, source=None
|
|
303
|
+
def load_file(logger, path: Path, file_type: FileType, source=None,
|
|
304
|
+
template_engine: Optional[TemplateEngine] = None,
|
|
305
|
+
template_context: Optional[dict] = None) -> Iterable[dict]:
|
|
178
306
|
logger.debug("Loading %s using %s", source or path, file_type.name)
|
|
179
|
-
return _load_file(logger, path, file_type
|
|
307
|
+
return _load_file(logger, path, file_type,
|
|
308
|
+
source, template_engine, template_context)
|
|
180
309
|
|
|
181
310
|
|
|
182
311
|
def validator_with_defaults(validator_class):
|
|
@@ -496,53 +625,6 @@ class Globs(MutableSet[Union[str, re.Pattern]]):
|
|
|
496
625
|
return f"Globs[{self._list}]"
|
|
497
626
|
|
|
498
627
|
|
|
499
|
-
class TemplateEngine:
|
|
500
|
-
VARIABLE_START_STRING = "{${"
|
|
501
|
-
VARIABLE_END_STRING = "}$}"
|
|
502
|
-
|
|
503
|
-
def __init__(self, logger):
|
|
504
|
-
self.template_failures = 0
|
|
505
|
-
self.templates = {}
|
|
506
|
-
|
|
507
|
-
class CollectingUndefined(ChainableUndefined):
|
|
508
|
-
__slots__ = ()
|
|
509
|
-
|
|
510
|
-
def __str__(self):
|
|
511
|
-
self.template_failures += 1
|
|
512
|
-
return super().__str__()
|
|
513
|
-
|
|
514
|
-
logging_undefined = make_logging_undefined(
|
|
515
|
-
logger=logger,
|
|
516
|
-
base=CollectingUndefined
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
@pass_context
|
|
520
|
-
def variable_finalizer(ctx, value):
|
|
521
|
-
normalized_value = str(value)
|
|
522
|
-
if self.VARIABLE_START_STRING in normalized_value and self.VARIABLE_END_STRING in normalized_value:
|
|
523
|
-
value_template_content = sys.intern(normalized_value)
|
|
524
|
-
env: Environment = ctx.environment
|
|
525
|
-
value_template = self.templates.get(value_template_content)
|
|
526
|
-
if not value_template:
|
|
527
|
-
value_template = env.from_string(value_template_content, env.globals)
|
|
528
|
-
self.templates[value_template_content] = value_template
|
|
529
|
-
return value_template.render(ctx.parent)
|
|
530
|
-
|
|
531
|
-
return normalized_value
|
|
532
|
-
|
|
533
|
-
self.env = Environment(variable_start_string=self.VARIABLE_START_STRING,
|
|
534
|
-
variable_end_string=self.VARIABLE_END_STRING,
|
|
535
|
-
autoescape=False,
|
|
536
|
-
finalize=variable_finalizer,
|
|
537
|
-
undefined=logging_undefined)
|
|
538
|
-
|
|
539
|
-
def from_string(self, template):
|
|
540
|
-
return self.env.from_string(template)
|
|
541
|
-
|
|
542
|
-
def failures(self):
|
|
543
|
-
return self.template_failures
|
|
544
|
-
|
|
545
|
-
|
|
546
628
|
class Template:
|
|
547
629
|
def __init__(self, name: str, template: JinjaTemplate, defaults: dict = None, path=None, source=None):
|
|
548
630
|
self.name = name
|
|
@@ -751,7 +833,8 @@ class KubernatorPlugin:
|
|
|
751
833
|
pass
|
|
752
834
|
|
|
753
835
|
|
|
754
|
-
def install_python_k8s_client(run, package_major, logger, logger_stdout, logger_stderr, disable_patching
|
|
836
|
+
def install_python_k8s_client(run, package_major, logger, logger_stdout, logger_stderr, disable_patching,
|
|
837
|
+
fallback=False):
|
|
755
838
|
cache_dir = get_cache_dir("python")
|
|
756
839
|
package_major_dir = cache_dir / str(package_major)
|
|
757
840
|
package_major_dir_str = str(package_major_dir)
|
|
@@ -763,13 +846,43 @@ def install_python_k8s_client(run, package_major, logger, logger_stdout, logger_
|
|
|
763
846
|
str(package_major), package_major_dir)
|
|
764
847
|
rmtree(package_major_dir)
|
|
765
848
|
|
|
766
|
-
if not package_major_dir.exists():
|
|
849
|
+
if not package_major_dir.exists() or not len(os.listdir(package_major_dir)):
|
|
767
850
|
package_major_dir.mkdir(parents=True, exist_ok=True)
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
851
|
+
try:
|
|
852
|
+
run([sys.executable, "-m", "pip", "install", "--no-deps", "--no-input",
|
|
853
|
+
"--root-user-action=ignore", "--break-system-packages", "--disable-pip-version-check",
|
|
854
|
+
"--target", package_major_dir_str, f"kubernetes>={package_major!s}dev0,<{int(package_major) + 1!s}"],
|
|
855
|
+
logger_stdout, logger_stderr)
|
|
856
|
+
except CalledProcessError as e:
|
|
857
|
+
if not fallback and "No matching distribution found for" in e.stderr:
|
|
858
|
+
logger.warning("Kubernetes Client %s (%s) failed to install because the version wasn't found. "
|
|
859
|
+
"Falling back to a client of the previous version - %s",
|
|
860
|
+
str(package_major), package_major_dir, int(package_major) - 1)
|
|
861
|
+
return install_python_k8s_client(run,
|
|
862
|
+
int(package_major) - 1,
|
|
863
|
+
logger,
|
|
864
|
+
logger_stdout,
|
|
865
|
+
logger_stderr,
|
|
866
|
+
disable_patching,
|
|
867
|
+
fallback=True)
|
|
868
|
+
else:
|
|
869
|
+
raise
|
|
771
870
|
|
|
772
871
|
if not patch_indicator.exists() and not disable_patching:
|
|
872
|
+
if not fallback and not len(os.listdir(package_major_dir)):
|
|
873
|
+
# Directory is empty
|
|
874
|
+
logger.warning("Kubernetes Client %s (%s) directory is empty - the client was not installed. "
|
|
875
|
+
"Falling back to a client of the previous version - %s",
|
|
876
|
+
str(package_major), package_major_dir, int(package_major) - 1)
|
|
877
|
+
|
|
878
|
+
return install_python_k8s_client(run,
|
|
879
|
+
int(package_major) - 1,
|
|
880
|
+
logger,
|
|
881
|
+
logger_stdout,
|
|
882
|
+
logger_stderr,
|
|
883
|
+
disable_patching,
|
|
884
|
+
fallback=True)
|
|
885
|
+
|
|
773
886
|
for patch_text, target_file, skip_if_found, min_version, max_version, name in (
|
|
774
887
|
URLLIB_HEADERS_PATCH, CUSTOM_OBJECT_PATCH_23, CUSTOM_OBJECT_PATCH_25):
|
|
775
888
|
patch_target = package_major_dir / target_file
|
kubernator/app.py
CHANGED
|
@@ -35,7 +35,7 @@ import kubernator
|
|
|
35
35
|
from kubernator.api import (KubernatorPlugin, Globs, scan_dir, PropertyDict, config_as_dict, config_parent,
|
|
36
36
|
download_remote_file, load_remote_file, Repository, StripNL, jp, get_app_cache_dir,
|
|
37
37
|
get_cache_dir, install_python_k8s_client)
|
|
38
|
-
from kubernator.proc import run, run_capturing_out
|
|
38
|
+
from kubernator.proc import run, run_capturing_out, run_pass_through_capturing
|
|
39
39
|
|
|
40
40
|
TRACE = 5
|
|
41
41
|
|
|
@@ -215,7 +215,7 @@ class App(KubernatorPlugin):
|
|
|
215
215
|
self.context = self._top_dir_context
|
|
216
216
|
context = self.context
|
|
217
217
|
self._run_handlers(KubernatorPlugin.handle_shutdown, True, context, None)
|
|
218
|
-
except:
|
|
218
|
+
except: # noqa E722
|
|
219
219
|
raise
|
|
220
220
|
else:
|
|
221
221
|
self.context = self._top_dir_context
|
|
@@ -348,6 +348,7 @@ class App(KubernatorPlugin):
|
|
|
348
348
|
jp=jp,
|
|
349
349
|
run=self._run,
|
|
350
350
|
run_capturing_out=self._run_capturing_out,
|
|
351
|
+
run_passthrough_capturing=self._run_passthrough_capturing,
|
|
351
352
|
repository=self.repository,
|
|
352
353
|
StripNL=StripNL,
|
|
353
354
|
default_includes=Globs(["*"], True),
|
|
@@ -465,6 +466,9 @@ class App(KubernatorPlugin):
|
|
|
465
466
|
def _run_capturing_out(self, *args, **kwargs):
|
|
466
467
|
return run_capturing_out(*args, **kwargs)
|
|
467
468
|
|
|
469
|
+
def _run_passthrough_capturing(self, *args, **kwargs):
|
|
470
|
+
return run_pass_through_capturing(*args, **kwargs)
|
|
471
|
+
|
|
468
472
|
def __repr__(self):
|
|
469
473
|
return "Kubernator"
|
|
470
474
|
|
|
@@ -493,7 +497,7 @@ def pre_cache_k8s_clients(*versions, disable_patching=False):
|
|
|
493
497
|
for v in versions:
|
|
494
498
|
logger.info("Caching K8S client library ~=v%s.0%s...", v,
|
|
495
499
|
" (no patches)" if disable_patching else "")
|
|
496
|
-
install_python_k8s_client(
|
|
500
|
+
install_python_k8s_client(run_pass_through_capturing, v, logger, stdout_logger, stderr_logger, disable_patching)
|
|
497
501
|
|
|
498
502
|
|
|
499
503
|
def main():
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2020 Express Systems USA, Inc
|
|
4
|
+
# Copyright 2025 Karellen, Inc.
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import tempfile
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from shutil import which
|
|
24
|
+
|
|
25
|
+
from kubernator.api import (KubernatorPlugin,
|
|
26
|
+
StripNL
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger("kubernator.gke")
|
|
30
|
+
proc_logger = logger.getChild("proc")
|
|
31
|
+
stdout_logger = StripNL(proc_logger.info)
|
|
32
|
+
stderr_logger = StripNL(proc_logger.warning)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GkePlugin(KubernatorPlugin):
|
|
36
|
+
logger = logger
|
|
37
|
+
|
|
38
|
+
_name = "gke"
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
self.context = None
|
|
42
|
+
self.kubeconfig_dir = tempfile.TemporaryDirectory()
|
|
43
|
+
self.name = None
|
|
44
|
+
self.region = None
|
|
45
|
+
self.project = None
|
|
46
|
+
self.gcloud_file = None
|
|
47
|
+
|
|
48
|
+
super().__init__()
|
|
49
|
+
|
|
50
|
+
def set_context(self, context):
|
|
51
|
+
self.context = context
|
|
52
|
+
|
|
53
|
+
def register(self, *, name=None, region=None, project=None):
|
|
54
|
+
context = self.context
|
|
55
|
+
|
|
56
|
+
if not name:
|
|
57
|
+
raise ValueError("`name` is required")
|
|
58
|
+
if not region:
|
|
59
|
+
raise ValueError("`region` is required")
|
|
60
|
+
if not project:
|
|
61
|
+
raise ValueError("`project` is required")
|
|
62
|
+
|
|
63
|
+
self.name = name
|
|
64
|
+
self.region = region
|
|
65
|
+
self.project = project
|
|
66
|
+
|
|
67
|
+
# Use current version
|
|
68
|
+
gcloud_file = which("gcloud")
|
|
69
|
+
if not gcloud_file:
|
|
70
|
+
raise RuntimeError("`gcloud` cannot be found")
|
|
71
|
+
logger.debug("Found gcloud in %r", gcloud_file)
|
|
72
|
+
self.gcloud_file = gcloud_file
|
|
73
|
+
|
|
74
|
+
context.app.register_plugin("kubeconfig")
|
|
75
|
+
|
|
76
|
+
context.globals.gke = dict(kubeconfig=str(Path(self.kubeconfig_dir.name) / "config"),
|
|
77
|
+
name=name,
|
|
78
|
+
region=region,
|
|
79
|
+
project=project,
|
|
80
|
+
gcloud_file=gcloud_file
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def handle_init(self):
|
|
84
|
+
context = self.context
|
|
85
|
+
|
|
86
|
+
env = dict(os.environ)
|
|
87
|
+
env["KUBECONFIG"] = str(context.gke.kubeconfig)
|
|
88
|
+
self.context.app.run([context.gke.gcloud_file, "components", "install", "gke-gcloud-auth-plugin"],
|
|
89
|
+
stdout_logger,
|
|
90
|
+
stderr_logger).wait()
|
|
91
|
+
self.context.app.run([context.gke.gcloud_file, "container", "clusters", "get-credentials",
|
|
92
|
+
context.gke.name,
|
|
93
|
+
"--region", context.gke.region,
|
|
94
|
+
"--project", context.gke.project],
|
|
95
|
+
stdout_logger,
|
|
96
|
+
stderr_logger,
|
|
97
|
+
env=env).wait()
|
|
98
|
+
|
|
99
|
+
context.kubeconfig.set(context.gke.kubeconfig)
|
|
100
|
+
|
|
101
|
+
def handle_shutdown(self):
|
|
102
|
+
self.kubeconfig_dir.cleanup()
|
|
103
|
+
|
|
104
|
+
def __repr__(self):
|
|
105
|
+
return "GKE Plugin"
|