mcp-souschef 2.8.0__py3-none-any.whl → 3.2.0__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.
- {mcp_souschef-2.8.0.dist-info → mcp_souschef-3.2.0.dist-info}/METADATA +159 -384
- mcp_souschef-3.2.0.dist-info/RECORD +47 -0
- {mcp_souschef-2.8.0.dist-info → mcp_souschef-3.2.0.dist-info}/WHEEL +1 -1
- souschef/__init__.py +31 -7
- souschef/assessment.py +1451 -105
- souschef/ci/common.py +126 -0
- souschef/ci/github_actions.py +3 -92
- souschef/ci/gitlab_ci.py +2 -52
- souschef/ci/jenkins_pipeline.py +2 -59
- souschef/cli.py +149 -16
- souschef/converters/playbook.py +378 -138
- souschef/converters/resource.py +12 -11
- souschef/converters/template.py +177 -0
- souschef/core/__init__.py +6 -1
- souschef/core/metrics.py +313 -0
- souschef/core/path_utils.py +233 -19
- souschef/core/validation.py +53 -0
- souschef/deployment.py +71 -12
- souschef/generators/__init__.py +13 -0
- souschef/generators/repo.py +695 -0
- souschef/parsers/attributes.py +1 -1
- souschef/parsers/habitat.py +1 -1
- souschef/parsers/inspec.py +25 -2
- souschef/parsers/metadata.py +5 -3
- souschef/parsers/recipe.py +1 -1
- souschef/parsers/resource.py +1 -1
- souschef/parsers/template.py +1 -1
- souschef/server.py +1039 -121
- souschef/ui/app.py +486 -374
- souschef/ui/pages/ai_settings.py +74 -8
- souschef/ui/pages/cookbook_analysis.py +3216 -373
- souschef/ui/pages/validation_reports.py +274 -0
- mcp_souschef-2.8.0.dist-info/RECORD +0 -42
- souschef/converters/cookbook_specific.py.backup +0 -109
- {mcp_souschef-2.8.0.dist-info → mcp_souschef-3.2.0.dist-info}/entry_points.txt +0 -0
- {mcp_souschef-2.8.0.dist-info → mcp_souschef-3.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""Validation Reports Page for SousChef UI."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import streamlit as st
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _run_ansible_lint(playbook_path: str) -> tuple[bool, str]:
|
|
12
|
+
"""
|
|
13
|
+
Run ansible-lint on a playbook and return results.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
playbook_path: Path to the playbook file.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Tuple of (success: bool, output: str).
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
result = subprocess.run(
|
|
24
|
+
[sys.executable, "-m", "ansible_lint", str(playbook_path)],
|
|
25
|
+
capture_output=True,
|
|
26
|
+
text=True,
|
|
27
|
+
timeout=30,
|
|
28
|
+
)
|
|
29
|
+
return result.returncode == 0, result.stdout + result.stderr
|
|
30
|
+
except subprocess.TimeoutExpired:
|
|
31
|
+
return False, "Validation timeout after 30 seconds"
|
|
32
|
+
except Exception as e:
|
|
33
|
+
return False, f"Error running ansible-lint: {e}"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _validate_playbooks_in_directory(
|
|
37
|
+
directory: str,
|
|
38
|
+
) -> dict[str, tuple[bool, str]]:
|
|
39
|
+
"""
|
|
40
|
+
Validate all playbooks in a directory.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
directory: Path to directory containing playbook files.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Dictionary mapping playbook names to (success, output) tuples.
|
|
47
|
+
|
|
48
|
+
"""
|
|
49
|
+
playbook_dir = Path(directory)
|
|
50
|
+
results = {}
|
|
51
|
+
|
|
52
|
+
if not playbook_dir.exists():
|
|
53
|
+
return {"error": (False, f"Directory not found: {directory}")}
|
|
54
|
+
|
|
55
|
+
# Find all YAML files
|
|
56
|
+
playbook_files = list(playbook_dir.glob("*.yml")) + list(
|
|
57
|
+
playbook_dir.glob("*.yaml")
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if not playbook_files:
|
|
61
|
+
return {"no_playbooks": (False, "No playbook files found in directory")}
|
|
62
|
+
|
|
63
|
+
for playbook_file in playbook_files:
|
|
64
|
+
success, output = _run_ansible_lint(str(playbook_file))
|
|
65
|
+
results[playbook_file.name] = (success, output)
|
|
66
|
+
|
|
67
|
+
return results
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _parse_ansible_lint_output(output: str) -> dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
Parse ansible-lint output into structured format.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
output: Raw ansible-lint output.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Dictionary with parsed results (warnings, errors, etc.).
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
lines = output.strip().split("\n")
|
|
82
|
+
parsed: dict[str, Any] = {
|
|
83
|
+
"warnings": 0,
|
|
84
|
+
"errors": 0,
|
|
85
|
+
"info": 0,
|
|
86
|
+
"details": [],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for line in lines:
|
|
90
|
+
if "warning" in line.lower():
|
|
91
|
+
parsed["warnings"] = int(parsed["warnings"]) + 1
|
|
92
|
+
elif "error" in line.lower():
|
|
93
|
+
parsed["errors"] = int(parsed["errors"]) + 1
|
|
94
|
+
elif "info" in line.lower():
|
|
95
|
+
parsed["info"] = int(parsed["info"]) + 1
|
|
96
|
+
|
|
97
|
+
if line.strip():
|
|
98
|
+
parsed["details"].append(line)
|
|
99
|
+
|
|
100
|
+
return parsed
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def show_validation_reports_page():
|
|
104
|
+
"""Show the validation reports page."""
|
|
105
|
+
# Add back to dashboard button
|
|
106
|
+
col1, _ = st.columns([1, 4])
|
|
107
|
+
with col1:
|
|
108
|
+
if st.button(
|
|
109
|
+
"← Back to Dashboard",
|
|
110
|
+
help="Return to main dashboard",
|
|
111
|
+
key="back_to_dashboard_from_validation",
|
|
112
|
+
):
|
|
113
|
+
st.session_state.current_page = "Dashboard"
|
|
114
|
+
st.rerun()
|
|
115
|
+
|
|
116
|
+
st.header("✅ Validation Reports")
|
|
117
|
+
|
|
118
|
+
# Check if we have converted playbooks to validate
|
|
119
|
+
if not hasattr(st.session_state, "converted_playbooks_path"):
|
|
120
|
+
st.info(
|
|
121
|
+
"No converted playbooks available for validation. "
|
|
122
|
+
"Please run a cookbook analysis first to generate Ansible playbooks."
|
|
123
|
+
)
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
converted_path = st.session_state.converted_playbooks_path
|
|
127
|
+
playbook_path = Path(converted_path)
|
|
128
|
+
|
|
129
|
+
if not playbook_path.exists():
|
|
130
|
+
st.warning(f"Converted playbooks directory not found: {converted_path}")
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
st.subheader("Running Ansible Validation")
|
|
134
|
+
st.markdown("Validating converted Ansible playbooks using ansible-lint...")
|
|
135
|
+
|
|
136
|
+
# Run validation
|
|
137
|
+
with st.spinner("Validating playbooks..."):
|
|
138
|
+
results = _validate_playbooks_in_directory(converted_path)
|
|
139
|
+
|
|
140
|
+
if not results:
|
|
141
|
+
st.warning("No playbooks found to validate")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Display summary
|
|
145
|
+
total_playbooks = len(results)
|
|
146
|
+
passed_count = sum(1 for success, _ in results.values() if success)
|
|
147
|
+
failed_count = total_playbooks - passed_count
|
|
148
|
+
|
|
149
|
+
col1, col2, col3 = st.columns(3)
|
|
150
|
+
with col1:
|
|
151
|
+
st.metric("Total Playbooks", total_playbooks)
|
|
152
|
+
with col2:
|
|
153
|
+
st.metric("Passed Validation", passed_count, delta=f"✓ {passed_count}")
|
|
154
|
+
with col3:
|
|
155
|
+
st.metric(
|
|
156
|
+
"Failed Validation",
|
|
157
|
+
failed_count,
|
|
158
|
+
delta=f"✗ {failed_count}" if failed_count > 0 else "✓ None",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Display detailed results
|
|
162
|
+
st.subheader("Detailed Validation Results")
|
|
163
|
+
|
|
164
|
+
if passed_count == total_playbooks:
|
|
165
|
+
st.success("✅ All playbooks passed validation!")
|
|
166
|
+
else:
|
|
167
|
+
st.warning(f"⚠️ {failed_count} playbook(s) have validation issues")
|
|
168
|
+
|
|
169
|
+
# Create tabs for each playbook
|
|
170
|
+
if len(results) > 1:
|
|
171
|
+
tabs = st.tabs(list(results.keys()))
|
|
172
|
+
for tab, (playbook_name, (success, output)) in zip(
|
|
173
|
+
tabs, results.items(), strict=True
|
|
174
|
+
):
|
|
175
|
+
with tab:
|
|
176
|
+
_display_validation_result(playbook_name, success, output)
|
|
177
|
+
else:
|
|
178
|
+
# Single playbook, no tabs needed
|
|
179
|
+
for playbook_name, (success, output) in results.items():
|
|
180
|
+
_display_validation_result(playbook_name, success, output)
|
|
181
|
+
|
|
182
|
+
# Export validation report
|
|
183
|
+
st.subheader("Export Report")
|
|
184
|
+
if st.button("📥 Download Validation Report"):
|
|
185
|
+
report = _generate_validation_report(results)
|
|
186
|
+
st.download_button(
|
|
187
|
+
label="Download as Text",
|
|
188
|
+
data=report,
|
|
189
|
+
file_name="validation_report.txt",
|
|
190
|
+
mime="text/plain",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _display_validation_result(playbook_name: str, success: bool, output: str) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Display validation result for a single playbook.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
playbook_name: Name of the playbook.
|
|
200
|
+
success: Whether validation passed.
|
|
201
|
+
output: Validation output/errors.
|
|
202
|
+
|
|
203
|
+
"""
|
|
204
|
+
if success:
|
|
205
|
+
st.success(f"✅ {playbook_name} passed validation")
|
|
206
|
+
else:
|
|
207
|
+
st.error(f"❌ {playbook_name} failed validation")
|
|
208
|
+
|
|
209
|
+
with st.expander("View Details"):
|
|
210
|
+
if output:
|
|
211
|
+
st.code(output, language="text")
|
|
212
|
+
else:
|
|
213
|
+
st.info("No detailed output available")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _generate_validation_report(results: dict[str, tuple[bool, str]]) -> str:
|
|
217
|
+
"""
|
|
218
|
+
Generate a text validation report.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
results: Dictionary of validation results.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Formatted validation report as string.
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
report_lines = [
|
|
228
|
+
"=" * 80,
|
|
229
|
+
"ANSIBLE PLAYBOOK VALIDATION REPORT",
|
|
230
|
+
"=" * 80,
|
|
231
|
+
"",
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
# Summary
|
|
235
|
+
total = len(results)
|
|
236
|
+
passed = sum(1 for success, _ in results.values() if success)
|
|
237
|
+
failed = total - passed
|
|
238
|
+
|
|
239
|
+
report_lines.extend(
|
|
240
|
+
[
|
|
241
|
+
"SUMMARY",
|
|
242
|
+
"-" * 80,
|
|
243
|
+
f"Total Playbooks: {total}",
|
|
244
|
+
f"Passed: {passed}",
|
|
245
|
+
f"Failed: {failed}",
|
|
246
|
+
"",
|
|
247
|
+
]
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Detailed results
|
|
251
|
+
report_lines.extend(
|
|
252
|
+
[
|
|
253
|
+
"DETAILED RESULTS",
|
|
254
|
+
"-" * 80,
|
|
255
|
+
"",
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
for playbook_name, (success, output) in results.items():
|
|
260
|
+
status = "PASSED" if success else "FAILED"
|
|
261
|
+
report_lines.extend(
|
|
262
|
+
[
|
|
263
|
+
f"Playbook: {playbook_name}",
|
|
264
|
+
f"Status: {status}",
|
|
265
|
+
"",
|
|
266
|
+
"Output:",
|
|
267
|
+
output if output else "(No output)",
|
|
268
|
+
"",
|
|
269
|
+
"-" * 80,
|
|
270
|
+
"",
|
|
271
|
+
]
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
return "\n".join(report_lines)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
souschef/__init__.py,sha256=oJgStDVoLf97poc0CoNXfD6ZTRe23R9vE_37Q8L2OhE,650
|
|
2
|
-
souschef/assessment.py,sha256=QRU3bgGrASYyp2f0aAgdnqt8t1Uvpz-0oj7Z5nuhMqw,53933
|
|
3
|
-
souschef/ci/__init__.py,sha256=GHaqDk4auB2kI7_fQtF2FRg5nP-iX6wJchj_uTlXcYg,411
|
|
4
|
-
souschef/ci/github_actions.py,sha256=-Ov7pD2cEyImMgOY3ZYBC7qTc6Z9Rq0WUUVCZ0HjBSE,11215
|
|
5
|
-
souschef/ci/gitlab_ci.py,sha256=p7N5t5AavQyJMUFN1JwIBENltNggBBnpdQGkNbD6ieY,8373
|
|
6
|
-
souschef/ci/jenkins_pipeline.py,sha256=rKeyc0fozKJRektAFz75J5rSODE5JpZXGwfvJs_v28w,9264
|
|
7
|
-
souschef/cli.py,sha256=q7p9bY75PHFPAyHtXboWbMorxFoJ-ruojLS6JnzUrpw,33772
|
|
8
|
-
souschef/converters/__init__.py,sha256=kkPm758XiFDdUYvdVPAVjssFvf15LasJcob1EY1EMIs,681
|
|
9
|
-
souschef/converters/cookbook_specific.py,sha256=ZvE9-bZtyXuf2E3HW4E9_GWzAqtm7e4YHj9qm1Xu_XI,3767
|
|
10
|
-
souschef/converters/cookbook_specific.py.backup,sha256=9XycZz6oNNTEtLINo0hHNR3df-pbZIkLprhLniE2ENI,3468
|
|
11
|
-
souschef/converters/habitat.py,sha256=4eVGAcX0576zLXW9yqYvuaRxOK2g6BOIJo_ws-PonHU,22516
|
|
12
|
-
souschef/converters/playbook.py,sha256=fHOBNwO2a1-_mFwdJpME3xQzgrtfvqrmfLcZOoChDIo,84425
|
|
13
|
-
souschef/converters/resource.py,sha256=MTfesZ9aGj1GeYZXxoyeupFS6ErcxiZg81CQ1zK7FiE,13428
|
|
14
|
-
souschef/core/__init__.py,sha256=80a0G6wQGfh-Z7U1gsFaeC97bM4sp_3YGil-lN02Pl0,1894
|
|
15
|
-
souschef/core/constants.py,sha256=AyIpc9cJjQEDc1yOcoKc_2-cJB4PekSpPKEK2S9jDl8,5122
|
|
16
|
-
souschef/core/errors.py,sha256=zj_LHDRxUvHmcs-nZjOWu4B_MhY5WVq2okyZOxyycCI,8605
|
|
17
|
-
souschef/core/path_utils.py,sha256=2hG6joupZR_08MMpHHbv2KKOFu-8tMafuQMqV0_1Nqw,1790
|
|
18
|
-
souschef/core/ruby_utils.py,sha256=vUeFZBdjbFFFJlwo4fIboR23rXmbcYL2Tztt4RYhps0,1043
|
|
19
|
-
souschef/core/validation.py,sha256=f37CFiJwPVaC0tN1nKlLFHIAdcZwT2CPNPnj2SHavss,17896
|
|
20
|
-
souschef/deployment.py,sha256=EANWsnMN0VxGS17gQdJU4yXhwmCzMe73TVXcRfQ7Eas,60332
|
|
21
|
-
souschef/filesystem/__init__.py,sha256=2H7Pdeedz0MfmgpRlmii_vx61qr0jtxFndT0APPm6Qs,142
|
|
22
|
-
souschef/filesystem/operations.py,sha256=OMMozBfV_o70b47KioiZ2i6HViiUQPE5mVBeKcKFepo,1654
|
|
23
|
-
souschef/parsers/__init__.py,sha256=gF-vPslzs9iLxsaDzFnWDPpYFDAIwyAifjUwzYurPLg,1104
|
|
24
|
-
souschef/parsers/attributes.py,sha256=JmgFaYjlT0i2__hCk6hSqZRjq4xJaTdGsBDgHEu3ItY,18239
|
|
25
|
-
souschef/parsers/habitat.py,sha256=CtVgLfmpo7SLp-ADyR-DXRYaRUur3bwJjlnpLyocMhc,10483
|
|
26
|
-
souschef/parsers/inspec.py,sha256=zhlIDZmE6A9qws5gnrLxhGGBfHOebNTLWpZZI-XXTkU,34209
|
|
27
|
-
souschef/parsers/metadata.py,sha256=laSxlsebsgTzRDM_B8eIXddeApbR4ATm-W4GAGucZpc,6324
|
|
28
|
-
souschef/parsers/recipe.py,sha256=guhEttS_AyT2kckbde3_-kAyGoR4JyFMNZFAP5V1XIM,7090
|
|
29
|
-
souschef/parsers/resource.py,sha256=EHxmojbQ7Hx5GsL5-x9mFBYk3FhGTpx-qw1uUVKQkRo,5348
|
|
30
|
-
souschef/parsers/template.py,sha256=iOHMoQH7KkPzigTyyoxav8rb2ENYmfxgcvKGqvoYkgU,10532
|
|
31
|
-
souschef/profiling.py,sha256=a6Pn57CZR3UPR1YIY8gfbuTPRuPkFrfIFBmctOcaZgY,17315
|
|
32
|
-
souschef/server.py,sha256=O5WAYc992M9QzowcURDsw91q-WUnErDL68ftjMLRuXo,87150
|
|
33
|
-
souschef/ui/__init__.py,sha256=U3W6X4ww7CxQVuetcocObWn5iY6lUFiNX7WoDBJeD34,195
|
|
34
|
-
souschef/ui/app.py,sha256=pKzoVQE_qy4FzjLIxF5BUpfe76evxz6a6Ossarib37Q,98112
|
|
35
|
-
souschef/ui/health_check.py,sha256=mEzec8L6XPRekOBAXngqQz5dnzal-a4p3D70mkPriiA,903
|
|
36
|
-
souschef/ui/pages/ai_settings.py,sha256=HRmTQjs0EPg8FdSkcozonpDDXnTf0WHn9IvFAhigGM4,15911
|
|
37
|
-
souschef/ui/pages/cookbook_analysis.py,sha256=Llx3o1QJsD9PCHhZD_SjjVyz_JwrA9K3iToMtp9ZF9g,47108
|
|
38
|
-
mcp_souschef-2.8.0.dist-info/METADATA,sha256=yc21kT6owG-dLD6pXdkEJOzpmQuSj7XS0QcLnyKZETo,51221
|
|
39
|
-
mcp_souschef-2.8.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
40
|
-
mcp_souschef-2.8.0.dist-info/entry_points.txt,sha256=NVSk61tLG4W0xEkWGOMXAVCIUyodyZCY_j3Z_0m6rkQ,80
|
|
41
|
-
mcp_souschef-2.8.0.dist-info/licenses/LICENSE,sha256=t31dYSuvYYNw6trj-coWSsLK-Tg_Iyl8ObcolQcrUKM,1078
|
|
42
|
-
mcp_souschef-2.8.0.dist-info/RECORD,,
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"""Cookbook-specific resource conversions for common infrastructure cookbooks."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
# Cookbook-specific include_recipe mappings
|
|
6
|
-
# Maps cookbook names to their primary package installation parameters
|
|
7
|
-
COOKBOOK_PACKAGE_MAPPINGS: dict[str, dict[str, Any]] = {
|
|
8
|
-
"nodejs": {
|
|
9
|
-
"module": "ansible.builtin.apt",
|
|
10
|
-
"params": {"name": ["nodejs", "npm"], "state": "present", "update_cache": True}
|
|
11
|
-
},
|
|
12
|
-
"apache2": {
|
|
13
|
-
"module": "ansible.builtin.apt",
|
|
14
|
-
"params": {"name": "apache2", "state": "present", "update_cache": True}
|
|
15
|
-
},
|
|
16
|
-
"mysql": {
|
|
17
|
-
"module": "ansible.builtin.apt",
|
|
18
|
-
"params": {"name": "mysql-server", "state": "present", "update_cache": True}
|
|
19
|
-
},
|
|
20
|
-
"docker": {
|
|
21
|
-
"module": "ansible.builtin.apt",
|
|
22
|
-
"params": {
|
|
23
|
-
"name": ["docker-ce", "docker-ce-cli", "containerd.io"],
|
|
24
|
-
"state": "present",
|
|
25
|
-
"update_cache": True
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def get_cookbook_package_config(cookbook_name: str) -> dict[str, Any] | None:
|
|
32
|
-
"""
|
|
33
|
-
Get package installation configuration for a specific cookbook.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
cookbook_name: Name of the cookbook (e.g., 'nodejs', 'apache2').
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
Dictionary with 'module' and 'params' keys, or None if not found.
|
|
40
|
-
"""
|
|
41
|
-
return COOKBOOK_PACKAGE_MAPPINGS.get(cookbook_name)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Cookbook-specific resource type mappings
|
|
45
|
-
# Maps resource types that are specific to certain cookbooks
|
|
46
|
-
COOKBOOK_RESOURCE_MAPPINGS: dict[str, dict[str, Any]] = {
|
|
47
|
-
"nodejs_npm": {
|
|
48
|
-
"description": "Node.js npm package installation",
|
|
49
|
-
"params_builder": "_build_nodejs_npm_params",
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _build_nodejs_npm_params(
|
|
55
|
-
resource_name: str, action: str, props: dict[str, Any]
|
|
56
|
-
) -> dict[str, Any]:
|
|
57
|
-
"""Build parameters for nodejs_npm resources."""
|
|
58
|
-
from souschef.converters.resource import _normalize_template_value
|
|
59
|
-
|
|
60
|
-
params = {"name": resource_name, "global": True}
|
|
61
|
-
if "version" in props:
|
|
62
|
-
params["version"] = _normalize_template_value(props["version"])
|
|
63
|
-
if action == "install":
|
|
64
|
-
params["state"] = "present"
|
|
65
|
-
elif action == "remove":
|
|
66
|
-
params["state"] = "absent"
|
|
67
|
-
else:
|
|
68
|
-
params["state"] = "present"
|
|
69
|
-
return params
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def get_cookbook_resource_config(resource_type: str) -> dict[str, Any] | None:
|
|
73
|
-
"""
|
|
74
|
-
Get configuration for cookbook-specific resource types.
|
|
75
|
-
|
|
76
|
-
Args:
|
|
77
|
-
resource_type: The resource type (e.g., 'nodejs_npm').
|
|
78
|
-
|
|
79
|
-
Returns:
|
|
80
|
-
Dictionary with resource configuration, or None if not found.
|
|
81
|
-
"""
|
|
82
|
-
return COOKBOOK_RESOURCE_MAPPINGS.get(resource_type)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def build_cookbook_resource_params(
|
|
86
|
-
resource_type: str, resource_name: str, action: str, props: dict[str, Any]
|
|
87
|
-
) -> dict[str, Any] | None:
|
|
88
|
-
"""
|
|
89
|
-
Build parameters for cookbook-specific resource types.
|
|
90
|
-
|
|
91
|
-
Args:
|
|
92
|
-
resource_type: The resource type.
|
|
93
|
-
resource_name: The resource name.
|
|
94
|
-
action: The Chef action.
|
|
95
|
-
props: Parsed properties dictionary.
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
Dictionary of Ansible module parameters, or None if not supported.
|
|
99
|
-
"""
|
|
100
|
-
config = get_cookbook_resource_config(resource_type)
|
|
101
|
-
if not config:
|
|
102
|
-
return None
|
|
103
|
-
|
|
104
|
-
builder_name = config.get("params_builder")
|
|
105
|
-
if builder_name == "_build_nodejs_npm_params":
|
|
106
|
-
return _build_nodejs_npm_params(resource_name, action, props)
|
|
107
|
-
|
|
108
|
-
return None</content>
|
|
109
|
-
<parameter name="filePath">/workspaces/souschef/souschef/converters/cookbook_specific.py
|
|
File without changes
|
|
File without changes
|