atlas-init 0.1.1__py3-none-any.whl → 0.1.4__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.
- atlas_init/__init__.py +3 -3
- atlas_init/atlas_init.yaml +18 -1
- atlas_init/cli.py +62 -70
- atlas_init/cli_cfn/app.py +40 -117
- atlas_init/cli_cfn/{cfn.py → aws.py} +129 -14
- atlas_init/cli_cfn/cfn_parameter_finder.py +89 -6
- atlas_init/cli_cfn/example.py +203 -0
- atlas_init/cli_cfn/files.py +63 -0
- atlas_init/cli_helper/run.py +18 -2
- atlas_init/cli_helper/tf_runner.py +4 -6
- atlas_init/cli_root/__init__.py +0 -0
- atlas_init/cli_root/trigger.py +153 -0
- atlas_init/cli_tf/app.py +211 -4
- atlas_init/cli_tf/changelog.py +103 -0
- atlas_init/cli_tf/debug_logs.py +221 -0
- atlas_init/cli_tf/debug_logs_test_data.py +253 -0
- atlas_init/cli_tf/github_logs.py +229 -0
- atlas_init/cli_tf/go_test_run.py +194 -0
- atlas_init/cli_tf/go_test_run_format.py +31 -0
- atlas_init/cli_tf/go_test_summary.py +144 -0
- atlas_init/cli_tf/hcl/__init__.py +0 -0
- atlas_init/cli_tf/hcl/cli.py +161 -0
- atlas_init/cli_tf/hcl/cluster_mig.py +348 -0
- atlas_init/cli_tf/hcl/parser.py +140 -0
- atlas_init/cli_tf/schema.py +222 -18
- atlas_init/cli_tf/schema_go_parser.py +236 -0
- atlas_init/cli_tf/schema_table.py +150 -0
- atlas_init/cli_tf/schema_table_models.py +155 -0
- atlas_init/cli_tf/schema_v2.py +599 -0
- atlas_init/cli_tf/schema_v2_api_parsing.py +298 -0
- atlas_init/cli_tf/schema_v2_sdk.py +361 -0
- atlas_init/cli_tf/schema_v3.py +222 -0
- atlas_init/cli_tf/schema_v3_sdk.py +279 -0
- atlas_init/cli_tf/schema_v3_sdk_base.py +68 -0
- atlas_init/cli_tf/schema_v3_sdk_create.py +216 -0
- atlas_init/humps.py +253 -0
- atlas_init/repos/cfn.py +6 -1
- atlas_init/repos/path.py +3 -3
- atlas_init/settings/config.py +14 -4
- atlas_init/settings/env_vars.py +16 -1
- atlas_init/settings/path.py +12 -1
- atlas_init/settings/rich_utils.py +2 -0
- atlas_init/terraform.yaml +77 -1
- atlas_init/tf/.terraform.lock.hcl +59 -83
- atlas_init/tf/always.tf +7 -0
- atlas_init/tf/main.tf +3 -0
- atlas_init/tf/modules/aws_s3/provider.tf +1 -1
- atlas_init/tf/modules/aws_vars/aws_vars.tf +2 -0
- atlas_init/tf/modules/aws_vpc/provider.tf +4 -1
- atlas_init/tf/modules/cfn/cfn.tf +47 -33
- atlas_init/tf/modules/cfn/kms.tf +54 -0
- atlas_init/tf/modules/cfn/resource_actions.yaml +1 -0
- atlas_init/tf/modules/cfn/variables.tf +31 -0
- atlas_init/tf/modules/cloud_provider/cloud_provider.tf +1 -0
- atlas_init/tf/modules/cloud_provider/provider.tf +1 -1
- atlas_init/tf/modules/cluster/cluster.tf +34 -24
- atlas_init/tf/modules/cluster/provider.tf +1 -1
- atlas_init/tf/modules/federated_vars/federated_vars.tf +3 -0
- atlas_init/tf/modules/federated_vars/provider.tf +1 -1
- atlas_init/tf/modules/project_extra/project_extra.tf +15 -1
- atlas_init/tf/modules/stream_instance/stream_instance.tf +1 -1
- atlas_init/tf/modules/vpc_peering/vpc_peering.tf +1 -1
- atlas_init/tf/modules/vpc_privatelink/versions.tf +1 -1
- atlas_init/tf/outputs.tf +11 -3
- atlas_init/tf/providers.tf +2 -1
- atlas_init/tf/variables.tf +12 -0
- atlas_init/typer_app.py +76 -0
- {atlas_init-0.1.1.dist-info → atlas_init-0.1.4.dist-info}/METADATA +36 -18
- atlas_init-0.1.4.dist-info/RECORD +91 -0
- {atlas_init-0.1.1.dist-info → atlas_init-0.1.4.dist-info}/WHEEL +1 -1
- atlas_init-0.1.1.dist-info/RECORD +0 -62
- /atlas_init/tf/modules/aws_vpc/{aws-vpc.tf → aws_vpc.tf} +0 -0
- {atlas_init-0.1.1.dist-info → atlas_init-0.1.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,216 @@
|
|
1
|
+
import logging
|
2
|
+
from functools import singledispatch
|
3
|
+
from queue import Queue
|
4
|
+
|
5
|
+
from atlas_init.cli_tf.schema_v2 import (
|
6
|
+
extend_import_urls,
|
7
|
+
go_fmt,
|
8
|
+
import_lines,
|
9
|
+
package_name,
|
10
|
+
)
|
11
|
+
from atlas_init.cli_tf.schema_v2_sdk import GoVarName, SDKAttribute, SDKModel
|
12
|
+
from atlas_init.cli_tf.schema_v3 import (
|
13
|
+
TF_MODEL_NAME,
|
14
|
+
Attribute,
|
15
|
+
ComputedOptionalRequired,
|
16
|
+
ListNestedAttribute,
|
17
|
+
Resource,
|
18
|
+
SingleNestedAttribute,
|
19
|
+
)
|
20
|
+
from atlas_init.cli_tf.schema_v3_sdk_base import (
|
21
|
+
SDKAndSchemaAttribute,
|
22
|
+
find_attribute,
|
23
|
+
name_schema_struct,
|
24
|
+
name_struct_attribute,
|
25
|
+
schema_attributes,
|
26
|
+
)
|
27
|
+
from atlas_init.humps import pascalize
|
28
|
+
|
29
|
+
logger = logging.getLogger(__name__)
|
30
|
+
|
31
|
+
|
32
|
+
def generate_schema_to_model(resource: Resource, sdk_model: SDKModel) -> str:
|
33
|
+
func_lines = tf_to_sdk_create_func(resource, sdk_model)
|
34
|
+
import_urls = set()
|
35
|
+
extend_import_urls(import_urls, func_lines)
|
36
|
+
unformatted = "\n".join(
|
37
|
+
[
|
38
|
+
f"package {package_name(resource.name)}",
|
39
|
+
"",
|
40
|
+
*import_lines(import_urls),
|
41
|
+
"",
|
42
|
+
*func_lines,
|
43
|
+
]
|
44
|
+
)
|
45
|
+
return go_fmt(resource.name, unformatted)
|
46
|
+
|
47
|
+
|
48
|
+
def tf_to_sdk_create_func(resource: Resource, sdk_model: SDKModel) -> list[str]:
|
49
|
+
lines = [
|
50
|
+
f"func NewAtlasReq({GoVarName.CTX} context.Context, {GoVarName.INPUT} *{TF_MODEL_NAME}, {GoVarName.DIAGS} *diag.Diagnostics) *admin.{sdk_model.name} {{"
|
51
|
+
f" return &admin.{sdk_model.name}{{"
|
52
|
+
]
|
53
|
+
nested_attributes, call_lines = convert_or_call_lines(resource, sdk_model)
|
54
|
+
lines.extend(call_lines)
|
55
|
+
lines.extend(
|
56
|
+
[
|
57
|
+
" }" # end struct
|
58
|
+
"}" # end function
|
59
|
+
]
|
60
|
+
)
|
61
|
+
lines.extend(process_nested_attributes(nested_attributes))
|
62
|
+
return lines
|
63
|
+
|
64
|
+
|
65
|
+
_tf_to_sdk_funcs = {
|
66
|
+
("string", "string"): lambda tf_ref: f"{tf_ref}.ValueString()",
|
67
|
+
("string", "*string"): lambda tf_ref: f"{tf_ref}.ValueStringPointer()",
|
68
|
+
("*string", "*string"): lambda tf_ref: f"{tf_ref}.ValueStringPointer()",
|
69
|
+
(
|
70
|
+
"*string",
|
71
|
+
"*time.Time",
|
72
|
+
): lambda tf_ref: f"conversion.StringPtrToTimePtr({tf_ref}.ValueStringPointer())",
|
73
|
+
("*bool", "*bool"): lambda tf_ref: f"{tf_ref}.ValueBoolPointer()",
|
74
|
+
(
|
75
|
+
"int64",
|
76
|
+
"*int",
|
77
|
+
): lambda tf_ref: f"conversion.Int64PtrToIntPtr({tf_ref}.ValueInt64Pointer())",
|
78
|
+
(
|
79
|
+
"*int64",
|
80
|
+
"*int",
|
81
|
+
): lambda tf_ref: f"conversion.Int64PtrToIntPtr({tf_ref}.ValueInt64Pointer())",
|
82
|
+
("*float64", "*float64"): lambda tf_ref: f"{tf_ref}.ValueFloat64Pointer()",
|
83
|
+
("*int64", "*int64"): lambda tf_ref: f"{tf_ref}.ValueInt64Pointer()",
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
def tf_to_sdk_attribute_value(
|
88
|
+
schema_attribute: Attribute,
|
89
|
+
sdk_attribute: SDKAttribute,
|
90
|
+
variable_name: GoVarName = GoVarName.INPUT,
|
91
|
+
) -> str:
|
92
|
+
key = (schema_attribute.go_type_optional, sdk_attribute.go_type)
|
93
|
+
if key in _tf_to_sdk_funcs:
|
94
|
+
return _tf_to_sdk_funcs[key](f"{variable_name}.{pascalize(schema_attribute.name)}")
|
95
|
+
raise ValueError(f"Could not find conversion function for {key} for attribute: {schema_attribute.name}")
|
96
|
+
|
97
|
+
|
98
|
+
def convert_or_call_lines(
|
99
|
+
root: Resource | Attribute,
|
100
|
+
sdk_model: SDKModel,
|
101
|
+
variable_name: GoVarName = GoVarName.INPUT,
|
102
|
+
) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
|
103
|
+
call_lines = []
|
104
|
+
nested_attributes: list[SDKAndSchemaAttribute] = []
|
105
|
+
tf_attributes = schema_attributes(root)
|
106
|
+
for sdk_attr in sorted(sdk_model.attributes.values()):
|
107
|
+
try:
|
108
|
+
tf_attribute = find_attribute(tf_attributes, sdk_attr.tf_name, root.name)
|
109
|
+
except ValueError as e:
|
110
|
+
logger.warning(e)
|
111
|
+
continue
|
112
|
+
if tf_attribute.computed_optional_required == ComputedOptionalRequired.computed:
|
113
|
+
continue
|
114
|
+
if sdk_attr.is_nested:
|
115
|
+
call_lines.append(
|
116
|
+
f" {sdk_attr.struct_name}: new{sdk_attr.struct_type_name}({GoVarName.CTX}, {variable_name}.{name_struct_attribute(tf_attribute.name)}, {GoVarName.DIAGS}),"
|
117
|
+
)
|
118
|
+
nested_attributes.append(SDKAndSchemaAttribute(sdk_attribute=sdk_attr, schema_attribute=tf_attribute))
|
119
|
+
elif tf_attribute.is_required:
|
120
|
+
call_lines.append(
|
121
|
+
f" {sdk_attr.struct_name}: {tf_to_sdk_attribute_value(tf_attribute, sdk_attr, variable_name)},"
|
122
|
+
)
|
123
|
+
else:
|
124
|
+
call_lines.append(
|
125
|
+
f" {sdk_attr.struct_name}: conversion.NilForUnknown({variable_name}.{tf_attribute.name_pascal}, {tf_to_sdk_attribute_value(tf_attribute, sdk_attr, variable_name)}),"
|
126
|
+
)
|
127
|
+
return nested_attributes, call_lines
|
128
|
+
|
129
|
+
|
130
|
+
def process_nested_attributes(
|
131
|
+
nested_attributes: list[SDKAndSchemaAttribute],
|
132
|
+
) -> list[str]:
|
133
|
+
lines = []
|
134
|
+
queue = Queue()
|
135
|
+
|
136
|
+
def add_nested_to_queue(attributes: list[SDKAndSchemaAttribute]):
|
137
|
+
for nested in attributes:
|
138
|
+
logger.info(f"found nested attribute: {nested.schema_attribute.name}")
|
139
|
+
queue.put(nested)
|
140
|
+
|
141
|
+
add_nested_to_queue(nested_attributes)
|
142
|
+
while not queue.empty():
|
143
|
+
sdk_attribute, schema_attribute = queue.get()
|
144
|
+
more_nested_attributes, nested_lines = convert_nested_attribute(
|
145
|
+
schema_attribute.nested_model, schema_attribute, sdk_attribute
|
146
|
+
)
|
147
|
+
lines.extend(nested_lines)
|
148
|
+
add_nested_to_queue(more_nested_attributes)
|
149
|
+
return lines
|
150
|
+
|
151
|
+
|
152
|
+
@singledispatch
|
153
|
+
def convert_nested_attribute(
|
154
|
+
nested_model: object, schema_attribute: Attribute, _: SDKAttribute
|
155
|
+
) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
|
156
|
+
raise NotImplementedError(f"unsupported nested attribute: {schema_attribute.name} of type {type(nested_model)}")
|
157
|
+
|
158
|
+
|
159
|
+
@convert_nested_attribute.register
|
160
|
+
def _convert_single_nested_attribute(
|
161
|
+
_: SingleNestedAttribute,
|
162
|
+
schema_attribute: Attribute,
|
163
|
+
sdk_attribute: SDKAttribute,
|
164
|
+
) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
|
165
|
+
sdk_model = sdk_attribute.as_sdk_model()
|
166
|
+
lines: list[str] = [
|
167
|
+
f"func new{sdk_model.name}(ctx context.Context, {GoVarName.INPUT} types.Object, diags *diag.Diagnostics) *admin.{sdk_model.name} {{",
|
168
|
+
f" var resp *admin.{sdk_model.name}",
|
169
|
+
f" if {GoVarName.INPUT}.IsUnknown() || {GoVarName.INPUT}.IsNull() {{",
|
170
|
+
" return resp",
|
171
|
+
" }",
|
172
|
+
f" {GoVarName.ITEM} := &{name_schema_struct(schema_attribute.name)}{{}}",
|
173
|
+
f" if localDiags := {GoVarName.INPUT}.As({GoVarName.CTX}, {GoVarName.ITEM}, basetypes.ObjectAsOptions{{}}); len(localDiags) > 0 {{",
|
174
|
+
f" {GoVarName.DIAGS}.Append(localDiags...)",
|
175
|
+
" return resp",
|
176
|
+
" }",
|
177
|
+
f" return &admin.{sdk_model.name}{{",
|
178
|
+
]
|
179
|
+
nested_attributes, call_lines = convert_or_call_lines(schema_attribute, sdk_model, GoVarName.ITEM)
|
180
|
+
lines.extend([*call_lines, " }", "}"]) # end struct # end function
|
181
|
+
return nested_attributes, lines
|
182
|
+
|
183
|
+
|
184
|
+
@convert_nested_attribute.register
|
185
|
+
def _convert_list_nested_attriute(
|
186
|
+
_: ListNestedAttribute,
|
187
|
+
schema_attribute: Attribute,
|
188
|
+
sdk_attribute: SDKAttribute,
|
189
|
+
) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
|
190
|
+
sdk_model = sdk_attribute.as_sdk_model()
|
191
|
+
lines: list[str] = [
|
192
|
+
f"func new{sdk_model.name}(ctx context.Context, {GoVarName.INPUT} types.List, diags *diag.Diagnostics) *[]admin.{sdk_model.name} {{",
|
193
|
+
f" if {GoVarName.INPUT}.IsUnknown() || {GoVarName.INPUT}.IsNull() {{",
|
194
|
+
" return nil",
|
195
|
+
" }",
|
196
|
+
f" {GoVarName.ELEMENTS} := make([]{name_schema_struct(schema_attribute.name)}, len({GoVarName.INPUT}.Elements()))",
|
197
|
+
f" if localDiags := {GoVarName.INPUT}.ElementsAs({GoVarName.CTX}, &{GoVarName.ELEMENTS}, false); len(localDiags) > 0 {{",
|
198
|
+
f" {GoVarName.DIAGS}.Append(localDiags...)",
|
199
|
+
" return nil",
|
200
|
+
" }",
|
201
|
+
f" {GoVarName.RESP} := make([]admin.{sdk_model.name}, len({GoVarName.INPUT}.Elements()))",
|
202
|
+
f" for i := range {GoVarName.ELEMENTS} {{",
|
203
|
+
f" {GoVarName.ITEM} := &{GoVarName.ELEMENTS}[i]",
|
204
|
+
f" resp[i] = admin.{sdk_model.name}{{",
|
205
|
+
]
|
206
|
+
nested_attributes, call_lines = convert_or_call_lines(schema_attribute, sdk_model, GoVarName.ITEM)
|
207
|
+
lines.extend(
|
208
|
+
[
|
209
|
+
*[f" {line}" for line in call_lines],
|
210
|
+
" }", # end struct
|
211
|
+
" }", # end loop
|
212
|
+
" return &resp",
|
213
|
+
"}", # end function
|
214
|
+
]
|
215
|
+
)
|
216
|
+
return nested_attributes, lines
|
atlas_init/humps.py
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
"""
|
2
|
+
# https://raw.githubusercontent.com/nficano/humps/master/humps/main.py
|
3
|
+
This module contains all the core logic for humps.
|
4
|
+
"""
|
5
|
+
import re
|
6
|
+
from collections.abc import Mapping
|
7
|
+
from typing import TypeVar # pylint: disable-msg=E0611
|
8
|
+
|
9
|
+
ACRONYM_RE = re.compile(r"([A-Z\d]+)(?=[A-Z\d]|$)")
|
10
|
+
PASCAL_RE = re.compile(r"([^\-_]+)")
|
11
|
+
SPLIT_RE = re.compile(r"([\-_]*(?<=[^0-9])(?=[A-Z])[^A-Z]*[\-_]*)")
|
12
|
+
UNDERSCORE_RE = re.compile(r"(?<=[^\-_])[\-_]+[^\-_]")
|
13
|
+
T = TypeVar("T")
|
14
|
+
|
15
|
+
def pascalize(str_or_iter: T) -> T:
|
16
|
+
"""
|
17
|
+
Convert a string, dict, or list of dicts to pascal case.
|
18
|
+
|
19
|
+
:param str_or_iter:
|
20
|
+
A string or iterable.
|
21
|
+
:type str_or_iter: Union[list, dict, str]
|
22
|
+
:rtype: Union[list, dict, str]
|
23
|
+
:returns:
|
24
|
+
pascalized string, dictionary, or list of dictionaries.
|
25
|
+
"""
|
26
|
+
if isinstance(str_or_iter, (list, Mapping)):
|
27
|
+
return _process_keys(str_or_iter, pascalize)
|
28
|
+
|
29
|
+
s = _is_none(str_or_iter)
|
30
|
+
if s.isupper() or s.isnumeric():
|
31
|
+
return str_or_iter
|
32
|
+
|
33
|
+
def _replace_fn(match):
|
34
|
+
"""
|
35
|
+
:rtype: str
|
36
|
+
"""
|
37
|
+
return match.group(1)[0].upper() + match.group(1)[1:]
|
38
|
+
|
39
|
+
s = camelize(PASCAL_RE.sub(_replace_fn, s))
|
40
|
+
return s[0].upper() + s[1:] if len(s) != 0 else s
|
41
|
+
|
42
|
+
|
43
|
+
def camelize(str_or_iter: T) -> T:
|
44
|
+
"""
|
45
|
+
Convert a string, dict, or list of dicts to camel case.
|
46
|
+
|
47
|
+
:param str_or_iter:
|
48
|
+
A string or iterable.
|
49
|
+
:type str_or_iter: Union[list, dict, str]
|
50
|
+
:rtype: Union[list, dict, str]
|
51
|
+
:returns:
|
52
|
+
camelized string, dictionary, or list of dictionaries.
|
53
|
+
"""
|
54
|
+
if isinstance(str_or_iter, (list, Mapping)):
|
55
|
+
return _process_keys(str_or_iter, camelize)
|
56
|
+
|
57
|
+
s = _is_none(str_or_iter)
|
58
|
+
if s.isupper() or s.isnumeric():
|
59
|
+
return str_or_iter
|
60
|
+
|
61
|
+
if len(s) != 0 and not s[:2].isupper():
|
62
|
+
s = s[0].lower() + s[1:]
|
63
|
+
|
64
|
+
# For string "hello_world", match will contain
|
65
|
+
# the regex capture group for "_w".
|
66
|
+
return UNDERSCORE_RE.sub(lambda m: m.group(0)[-1].upper(), s)
|
67
|
+
|
68
|
+
|
69
|
+
def kebabize(str_or_iter: T) -> T:
|
70
|
+
"""
|
71
|
+
Convert a string, dict, or list of dicts to kebab case.
|
72
|
+
:param str_or_iter:
|
73
|
+
A string or iterable.
|
74
|
+
:type str_or_iter: Union[list, dict, str]
|
75
|
+
:rtype: Union[list, dict, str]
|
76
|
+
:returns:
|
77
|
+
kebabized string, dictionary, or list of dictionaries.
|
78
|
+
"""
|
79
|
+
if isinstance(str_or_iter, (list, Mapping)):
|
80
|
+
return _process_keys(str_or_iter, kebabize)
|
81
|
+
|
82
|
+
s = _is_none(str_or_iter)
|
83
|
+
if s.isnumeric():
|
84
|
+
return str_or_iter
|
85
|
+
|
86
|
+
if not (s.isupper()) and (is_camelcase(s) or is_pascalcase(s)):
|
87
|
+
return (
|
88
|
+
_separate_words(
|
89
|
+
string=_fix_abbreviations(s),
|
90
|
+
separator="-"
|
91
|
+
).lower()
|
92
|
+
)
|
93
|
+
|
94
|
+
return UNDERSCORE_RE.sub(lambda m: "-" + m.group(0)[-1], s)
|
95
|
+
|
96
|
+
|
97
|
+
def decamelize(str_or_iter: T) -> T:
|
98
|
+
"""
|
99
|
+
Convert a string, dict, or list of dicts to snake case.
|
100
|
+
|
101
|
+
:param str_or_iter:
|
102
|
+
A string or iterable.
|
103
|
+
:type str_or_iter: Union[list, dict, str]
|
104
|
+
:rtype: Union[list, dict, str]
|
105
|
+
:returns:
|
106
|
+
snake cased string, dictionary, or list of dictionaries.
|
107
|
+
"""
|
108
|
+
if isinstance(str_or_iter, (list, Mapping)):
|
109
|
+
return _process_keys(str_or_iter, decamelize)
|
110
|
+
|
111
|
+
s = _is_none(str_or_iter)
|
112
|
+
if s.isupper() or s.isnumeric():
|
113
|
+
return str_or_iter
|
114
|
+
|
115
|
+
return _separate_words(_fix_abbreviations(s)).lower()
|
116
|
+
|
117
|
+
|
118
|
+
def depascalize(str_or_iter: T) -> T:
|
119
|
+
"""
|
120
|
+
Convert a string, dict, or list of dicts to snake case.
|
121
|
+
|
122
|
+
:param str_or_iter: A string or iterable.
|
123
|
+
:type str_or_iter: Union[list, dict, str]
|
124
|
+
:rtype: Union[list, dict, str]
|
125
|
+
:returns:
|
126
|
+
snake cased string, dictionary, or list of dictionaries.
|
127
|
+
"""
|
128
|
+
return decamelize(str_or_iter)
|
129
|
+
|
130
|
+
|
131
|
+
def dekebabize(str_or_iter: T) -> T:
|
132
|
+
"""
|
133
|
+
Convert a string, dict, or list of dicts to snake case.
|
134
|
+
:param str_or_iter:
|
135
|
+
A string or iterable.
|
136
|
+
:type str_or_iter: Union[list, dict, str]
|
137
|
+
:rtype: Union[list, dict, str]
|
138
|
+
:returns:
|
139
|
+
snake cased string, dictionary, or list of dictionaries.
|
140
|
+
"""
|
141
|
+
if isinstance(str_or_iter, (list, Mapping)):
|
142
|
+
return _process_keys(str_or_iter, dekebabize)
|
143
|
+
|
144
|
+
s = _is_none(str_or_iter)
|
145
|
+
if s.isnumeric():
|
146
|
+
return str_or_iter
|
147
|
+
|
148
|
+
return s.replace("-", "_")
|
149
|
+
|
150
|
+
|
151
|
+
def is_camelcase(str_or_iter: T) -> T:
|
152
|
+
"""
|
153
|
+
Determine if a string, dict, or list of dicts is camel case.
|
154
|
+
|
155
|
+
:param str_or_iter:
|
156
|
+
A string or iterable.
|
157
|
+
:type str_or_iter: Union[list, dict, str]
|
158
|
+
:rtype: bool
|
159
|
+
:returns:
|
160
|
+
True/False whether string or iterable is camel case
|
161
|
+
"""
|
162
|
+
return str_or_iter == camelize(str_or_iter)
|
163
|
+
|
164
|
+
|
165
|
+
def is_pascalcase(str_or_iter: T) -> T:
|
166
|
+
"""
|
167
|
+
Determine if a string, dict, or list of dicts is pascal case.
|
168
|
+
|
169
|
+
:param str_or_iter: A string or iterable.
|
170
|
+
:type str_or_iter: Union[list, dict, str]
|
171
|
+
:rtype: bool
|
172
|
+
:returns:
|
173
|
+
True/False whether string or iterable is pascal case
|
174
|
+
"""
|
175
|
+
return str_or_iter == pascalize(str_or_iter)
|
176
|
+
|
177
|
+
|
178
|
+
def is_kebabcase(str_or_iter: T) -> T:
|
179
|
+
"""
|
180
|
+
Determine if a string, dict, or list of dicts is camel case.
|
181
|
+
:param str_or_iter:
|
182
|
+
A string or iterable.
|
183
|
+
:type str_or_iter: Union[list, dict, str]
|
184
|
+
:rtype: bool
|
185
|
+
:returns:
|
186
|
+
True/False whether string or iterable is camel case
|
187
|
+
"""
|
188
|
+
return str_or_iter == kebabize(str_or_iter)
|
189
|
+
|
190
|
+
|
191
|
+
def is_snakecase(str_or_iter: T) -> T:
|
192
|
+
"""
|
193
|
+
Determine if a string, dict, or list of dicts is snake case.
|
194
|
+
|
195
|
+
:param str_or_iter:
|
196
|
+
A string or iterable.
|
197
|
+
:type str_or_iter: Union[list, dict, str]
|
198
|
+
:rtype: bool
|
199
|
+
:returns:
|
200
|
+
True/False whether string or iterable is snake case
|
201
|
+
"""
|
202
|
+
if is_kebabcase(str_or_iter) and not is_camelcase(str_or_iter):
|
203
|
+
return False
|
204
|
+
|
205
|
+
return str_or_iter == decamelize(str_or_iter)
|
206
|
+
|
207
|
+
|
208
|
+
def _is_none(_in):
|
209
|
+
"""
|
210
|
+
Determine if the input is None
|
211
|
+
and returns a string with white-space removed
|
212
|
+
:param _in: input
|
213
|
+
:return:
|
214
|
+
an empty sting if _in is None,
|
215
|
+
else the input is returned with white-space removed
|
216
|
+
"""
|
217
|
+
return "" if _in is None else re.sub(r"\s+", "", str(_in))
|
218
|
+
|
219
|
+
|
220
|
+
def _process_keys(str_or_iter, fn):
|
221
|
+
if isinstance(str_or_iter, list):
|
222
|
+
return [_process_keys(k, fn) for k in str_or_iter]
|
223
|
+
if isinstance(str_or_iter, Mapping):
|
224
|
+
return {fn(k): _process_keys(v, fn) for k, v in str_or_iter.items()}
|
225
|
+
return str_or_iter
|
226
|
+
|
227
|
+
|
228
|
+
def _fix_abbreviations(string):
|
229
|
+
"""
|
230
|
+
Rewrite incorrectly cased acronyms, initialisms, and abbreviations,
|
231
|
+
allowing them to be decamelized correctly. For example, given the string
|
232
|
+
"APIResponse", this function is responsible for ensuring the output is
|
233
|
+
"api_response" instead of "a_p_i_response".
|
234
|
+
|
235
|
+
:param string: A string that may contain an incorrectly cased abbreviation.
|
236
|
+
:type string: str
|
237
|
+
:rtype: str
|
238
|
+
:returns:
|
239
|
+
A rewritten string that is safe for decamelization.
|
240
|
+
"""
|
241
|
+
return ACRONYM_RE.sub(lambda m: m.group(0).title(), string)
|
242
|
+
|
243
|
+
|
244
|
+
def _separate_words(string, separator="_"):
|
245
|
+
"""
|
246
|
+
Split words that are separated by case differentiation.
|
247
|
+
:param string: Original string.
|
248
|
+
:param separator: String by which the individual
|
249
|
+
words will be put back together.
|
250
|
+
:returns:
|
251
|
+
New string.
|
252
|
+
"""
|
253
|
+
return separator.join(s for s in SPLIT_RE.split(string) if s)
|
atlas_init/repos/cfn.py
CHANGED
@@ -9,6 +9,7 @@ from zero_3rdparty.dict_nested import read_nested_or_none
|
|
9
9
|
from zero_3rdparty.str_utils import ensure_prefix
|
10
10
|
|
11
11
|
from atlas_init.cloud.aws import REGIONS, AwsRegion
|
12
|
+
from atlas_init.humps import pascalize
|
12
13
|
from atlas_init.repos.path import current_dir
|
13
14
|
|
14
15
|
logger = logging.getLogger(__name__)
|
@@ -41,9 +42,13 @@ class CfnType(Entity):
|
|
41
42
|
|
42
43
|
@model_validator(mode="after")
|
43
44
|
def ensure_type_name_prefix(self):
|
44
|
-
self.type_name = ensure_prefix(self.type_name
|
45
|
+
self.type_name = ensure_prefix(pascalize(self.type_name), self.MONGODB_ATLAS_CFN_TYPE_PREFIX)
|
45
46
|
return self
|
46
47
|
|
48
|
+
@classmethod
|
49
|
+
def resource_name(cls, type_name: str) -> str:
|
50
|
+
return type_name.removeprefix(cls.MONGODB_ATLAS_CFN_TYPE_PREFIX).lower()
|
51
|
+
|
47
52
|
|
48
53
|
class Operation(StrEnum):
|
49
54
|
DELETE = "delete"
|
atlas_init/repos/path.py
CHANGED
@@ -29,11 +29,11 @@ _resource_roots = {
|
|
29
29
|
}
|
30
30
|
|
31
31
|
|
32
|
-
def _default_is_resource(
|
33
|
-
|
32
|
+
def _default_is_resource(p: Path) -> bool:
|
33
|
+
return "internal/service" in str(p)
|
34
34
|
|
35
35
|
|
36
|
-
_resource_is_resource = {
|
36
|
+
_resource_is_resource: dict[str, Callable[[Path], bool]] = {
|
37
37
|
GH_OWNER_MONGODBATLAS_CLOUDFORMATION_RESOURCES: lambda p: (p / "cmd/main.go").exists(),
|
38
38
|
GH_OWNER_TERRAFORM_PROVIDER_MONGODBATLAS: _default_is_resource,
|
39
39
|
}
|
atlas_init/settings/config.py
CHANGED
@@ -4,6 +4,7 @@ import fnmatch
|
|
4
4
|
import logging
|
5
5
|
from collections.abc import Iterable
|
6
6
|
from functools import total_ordering
|
7
|
+
from os import getenv
|
7
8
|
from pathlib import Path
|
8
9
|
from typing import Any
|
9
10
|
|
@@ -59,7 +60,8 @@ class TerraformVars(Entity):
|
|
59
60
|
if self.use_federated_vars:
|
60
61
|
config["use_federated_vars"] = True
|
61
62
|
if self.stream_instance:
|
62
|
-
|
63
|
+
# hack until backend bug with stream instance is fixed
|
64
|
+
config["stream_instance_config"] = {"name": getenv("ATLAS_STREAM_INSTANCE_NAME", "atlas-init")}
|
63
65
|
return config
|
64
66
|
|
65
67
|
|
@@ -154,10 +156,18 @@ def active_suites(
|
|
154
156
|
forced_test_suites: list[str],
|
155
157
|
) -> list[TestSuite]: # type: ignore
|
156
158
|
repo_url_path = owner_project_name(repo_path)
|
157
|
-
|
158
|
-
|
159
|
+
try:
|
160
|
+
repo_alias = config.repo_alias(repo_url_path)
|
161
|
+
except RepoAliasNotFoundError:
|
162
|
+
if forced_test_suites:
|
163
|
+
# still want to use the forced test suites
|
164
|
+
repo_alias = None
|
165
|
+
else:
|
166
|
+
raise
|
167
|
+
logger.info(
|
168
|
+
f"repo_alias={repo_alias}, repo_path={repo_path}, repo_url_path={repo_url_path}, cwd_rel_path={cwd_rel_path}"
|
169
|
+
)
|
159
170
|
change_paths = [cwd_rel_path]
|
160
|
-
|
161
171
|
active_suites = config.active_test_suites(repo_alias, change_paths, forced_test_suites)
|
162
172
|
logger.info(f"active_suites: {[s.name for s in active_suites]}")
|
163
173
|
return active_suites
|
atlas_init/settings/env_vars.py
CHANGED
@@ -53,6 +53,10 @@ class ExternalSettings(BaseSettings):
|
|
53
53
|
def is_interactive(self) -> bool:
|
54
54
|
return not self.non_interactive
|
55
55
|
|
56
|
+
@property
|
57
|
+
def is_mongodbgov_cloud(self) -> bool:
|
58
|
+
return "mongodbgov" in self.MONGODB_ATLAS_BASE_URL
|
59
|
+
|
56
60
|
|
57
61
|
def as_env_var_name(field_name: str) -> str:
|
58
62
|
names = set(field_names(AtlasInitSettings))
|
@@ -137,6 +141,10 @@ class AtlasInitPaths(BaseSettings):
|
|
137
141
|
def env_vars_vs_code(self) -> Path:
|
138
142
|
return self.profile_dir / ".env-vscode"
|
139
143
|
|
144
|
+
@property
|
145
|
+
def env_vars_trigger(self) -> Path:
|
146
|
+
return self.profile_dir / ".env-trigger"
|
147
|
+
|
140
148
|
@property
|
141
149
|
def tf_data_dir(self) -> Path:
|
142
150
|
return self.profile_dir / ".terraform"
|
@@ -181,6 +189,7 @@ class AtlasInitSettings(AtlasInitPaths, ExternalSettings):
|
|
181
189
|
|
182
190
|
cfn_profile: str = ""
|
183
191
|
cfn_region: str = ""
|
192
|
+
cfn_use_kms_key: bool = False
|
184
193
|
project_name: str = ""
|
185
194
|
|
186
195
|
skip_copy: bool = False
|
@@ -246,7 +255,13 @@ class AtlasInitSettings(AtlasInitPaths, ExternalSettings):
|
|
246
255
|
|
247
256
|
def cfn_config(self) -> dict[str, Any]:
|
248
257
|
if self.cfn_profile:
|
249
|
-
return {
|
258
|
+
return {
|
259
|
+
"cfn_config": {
|
260
|
+
"profile": self.cfn_profile,
|
261
|
+
"region": self.cfn_region,
|
262
|
+
"use_kms_key": self.cfn_use_kms_key,
|
263
|
+
}
|
264
|
+
}
|
250
265
|
return {}
|
251
266
|
|
252
267
|
|
atlas_init/settings/path.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import os
|
2
|
+
from collections.abc import Callable
|
2
3
|
from pathlib import Path
|
3
4
|
|
4
5
|
import dotenv
|
@@ -21,15 +22,18 @@ DEFAULT_PROFILES_PATH.mkdir(exist_ok=True, parents=True)
|
|
21
22
|
DEFAULT_TF_PATH = ROOT_PATH / "tf"
|
22
23
|
DEFAULT_CONFIG_PATH = ROOT_PATH / "atlas_init.yaml"
|
23
24
|
DEFAULT_SCHEMA_CONFIG_PATH = ROOT_PATH / "terraform.yaml"
|
25
|
+
DEFAULT_GITHUB_CI_RUN_LOGS = ROOT_PATH / "github_ci_run_logs"
|
26
|
+
DEFAULT_GITHUB_SUMMARY_DIR = ROOT_PATH / "github_ci_summary"
|
24
27
|
|
25
28
|
|
26
29
|
def load_dotenv(env_path: Path) -> dict[str, str]:
|
27
30
|
return {k: v for k, v in dotenv.dotenv_values(env_path).items() if v}
|
28
31
|
|
29
32
|
|
30
|
-
def dump_vscode_dotenv(generated_path: Path, vscode_env_path: Path) -> None:
|
33
|
+
def dump_vscode_dotenv(generated_path: Path, vscode_env_path: Path, **extras: str) -> None:
|
31
34
|
vscode_env_vars = load_dotenv(generated_path)
|
32
35
|
vscode_env_vars.pop("TF_CLI_CONFIG_FILE", None) # migration tests will use local provider instead of online
|
36
|
+
vscode_env_vars.update(extras)
|
33
37
|
dump_dotenv(vscode_env_path, vscode_env_vars)
|
34
38
|
|
35
39
|
|
@@ -43,6 +47,13 @@ def current_dir():
|
|
43
47
|
return Path(os.path.curdir).absolute()
|
44
48
|
|
45
49
|
|
50
|
+
def default_factory_cwd(rel_path: str) -> Callable[[], Path]:
|
51
|
+
def default_factory():
|
52
|
+
return current_dir() / rel_path
|
53
|
+
|
54
|
+
return default_factory
|
55
|
+
|
56
|
+
|
46
57
|
def repo_path_rel_path() -> tuple[Path, str]:
|
47
58
|
cwd = current_dir()
|
48
59
|
rel_path = []
|
@@ -35,6 +35,8 @@ def hide_secrets(handler: logging.Handler, secrets_dict: dict[str, str]) -> None
|
|
35
35
|
if not isinstance(value, str):
|
36
36
|
continue
|
37
37
|
key_lower = key.lower()
|
38
|
+
if key_lower in {"true", "false"}:
|
39
|
+
continue
|
38
40
|
if any(safe in key_lower for safe in safe_keys):
|
39
41
|
continue
|
40
42
|
if any(danger in key_lower for danger in dangerous_keys):
|