Skip to content

Commit 877d39c

Browse files
Merge pull request #73 from splunk:dev-ksconf
feat: update dependencies and improve configuration merging with ksconf
2 parents 33f435c + 9c17639 commit 877d39c

6 files changed

Lines changed: 23 additions & 115 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
run: |
3333
python -m pip install --upgrade pip
3434
pip install --upgrade pyyaml
35-
pip install boto3 requests schema
35+
pip install boto3 requests schema ksconf
3636
- name: Deploy to ${{ matrix.environment.name }}
3737
continue-on-error: true
3838
env:

.github/workflows/manual_deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
python -m pip install --upgrade pip
2727
pip install --upgrade pyyaml
2828
pip install boto3
29-
pip install requests
29+
pip install requests ksconf
3030
- name: Deploy to ${{ matrix.environment.name }}
3131
continue-on-error: true
3232
env:

environments/test/es/buttercup_app_for_splunk/local.meta

Lines changed: 0 additions & 2 deletions
This file was deleted.

environments/test/es/deployment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
target:
2-
url: https://staging.admin.splunk.com/scv-shw-a7f6020a334e01
2+
url: https://staging.admin.splunk.com/scv-shw-4fba63dd7d6f16
33
experience: victoria
44
apps:
55
Splunk_TA_app1:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[config]
2-
log_level = error
2+
log_level = debug

modules/apps_processing.py

Lines changed: 19 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import configparser
66
import tarfile
77
import json
8+
import ksconf
89
from io import StringIO
910
from schema import (
1011
Schema,
@@ -134,36 +135,8 @@ class AppFilesProcessor:
134135
def __init__(self, deployment_parser: DeploymentParser):
135136
self.deployment_config = deployment_parser
136137

137-
def _preprocess_empty_headers(self, file_path: str) -> list:
138-
"""
139-
Preprocess the file to handle empty section headers by replacing `[]` with a valid section name.
140-
"""
141-
valid_lines = []
142-
with open(file_path, "r") as file:
143-
for line in file:
144-
# Replace empty section headers with a placeholder
145-
if line.strip() == "[]":
146-
valid_lines.append("[DEFAULT]\n") # Or any placeholder section name
147-
else:
148-
valid_lines.append(line)
149-
return valid_lines
150-
151-
def _replace_default_with_empty_header(self, file_path: str) -> None:
152-
"""
153-
Replace '[DEFAULT]' header with '[]' in the specified file.
154-
"""
155-
with open(file_path, "r") as file:
156-
lines = file.readlines()
157-
158-
with open(file_path, "w") as file:
159-
for line in lines:
160-
# Replace '[DEFAULT]' with '[]'
161-
if line.strip() == "[DEFAULT]":
162-
file.write("[]\n")
163-
else:
164-
file.write(line)
165-
166138
def merge_or_copy_conf(self, source_path: str, dest_path: str) -> None:
139+
"""Function to copy local configuration files to default or merge them using ksconf"""
167140
# Get the filename from the source path
168141
filename = os.path.basename(source_path)
169142
dest_file = os.path.join(dest_path, filename)
@@ -174,84 +147,28 @@ def merge_or_copy_conf(self, source_path: str, dest_path: str) -> None:
174147
shutil.copy(source_path, dest_path)
175148
print(f"Copied {filename} to {dest_path}")
176149
else:
177-
# If the file exists, merge the configurations
150+
# If the file exists, merge the configurations using ksconf command
178151
print(f"Merging {filename} with existing file in {dest_path}")
152+
command = ["ksconf", "promote", filename, dest_file]
153+
try:
154+
# Run the command and capture the output
155+
result = subprocess.run(
156+
command,
157+
capture_output=True, # Capture stdout and stderr
158+
text=True, # Decode output as text (Python 3.6+)
159+
check=True # Raise an exception on non-zero exit code
160+
)
161+
print("Command succeeded:")
162+
print(result.stdout)
163+
return result
164+
except subprocess.CalledProcessError as e:
165+
print("Command failed with an error:")
166+
print(e.stderr)
167+
raise
179168

180-
# Read the source file
181-
source_config = configparser.ConfigParser()
182-
source_config.read(source_path)
183169

184-
# Read the destination file
185-
dest_config = configparser.ConfigParser()
186-
dest_config.read(dest_file)
187-
188-
# Merge source into destination
189-
for section in source_config.sections():
190-
if not dest_config.has_section(section):
191-
dest_config.add_section(section)
192-
for option, value in source_config.items(section):
193-
dest_config.set(section, option, value)
194-
195-
# Write the merged configuration back to the destination file
196-
with open(dest_file, "w") as file:
197-
dest_config.write(file)
198170
print(f"Merged configuration saved to {dest_file}")
199171

200-
def merge_or_copy_meta(self, local_meta_file: str, default_dir: str) -> None:
201-
"""Merge local.meta with default.meta"""
202-
filename = os.path.basename(local_meta_file)
203-
dest_file = os.path.join(default_dir, "default.meta")
204-
205-
# Check if the file exists in the destination directory
206-
if not os.path.exists(dest_file):
207-
# If the file doesn't exist, copy it
208-
shutil.copy(local_meta_file, dest_file)
209-
print(f"Copied {filename} to {dest_file}")
210-
else:
211-
# If the file exists, merge the configurations
212-
print(f"Merging {filename} with existing file in {dest_file}")
213-
214-
# Preprocess the default file
215-
default_preprocessed_lines = self._preprocess_empty_headers(dest_file)
216-
default_preprocessed_content = StringIO("".join(default_preprocessed_lines))
217-
218-
# Read the default.meta file
219-
default_meta = configparser.ConfigParser()
220-
default_meta.read_file(default_preprocessed_content)
221-
222-
# Preprocess the local file
223-
local_preprocessed_lines = self._preprocess_empty_headers(local_meta_file)
224-
local_preprocessed_content = StringIO("".join(local_preprocessed_lines))
225-
226-
# Read the local.meta file
227-
local_meta = configparser.ConfigParser()
228-
local_meta.read_file(local_preprocessed_content)
229-
230-
# Merge local.meta into default.meta
231-
for section in local_meta.sections():
232-
if not default_meta.has_section(section):
233-
default_meta.add_section(section)
234-
for option, value in local_meta.items(section):
235-
if default_meta.has_option(section, option):
236-
# Merge logic: Option exists in both, decide whether to overwrite
237-
default_value = default_meta.get(section, option)
238-
if value != default_value:
239-
print(
240-
f"Conflict detected: {section} {option} - {default_value} -> {value}"
241-
)
242-
# Overwrite the option in default.meta
243-
default_meta.set(section, option, value)
244-
default_meta.set(section, option, value)
245-
246-
# Write the merged configuration back to the output file
247-
with open(dest_file, "w") as file:
248-
default_meta.write(file)
249-
250-
# Replace '[DEFAULT]' with '[]' in the output file
251-
self._replace_default_with_empty_header(dest_file)
252-
253-
print(f"Merged metadata saved to {dest_file}")
254-
255172
def unpack_merge_conf_and_meta_repack(self, app: str, path: str) -> None:
256173
"""Unpack the app, load environment configuration files and repack the app."""
257174
temp_dir = "temp_unpack"
@@ -271,13 +188,6 @@ def unpack_merge_conf_and_meta_repack(self, app: str, path: str) -> None:
271188
os.makedirs(default_dir, exist_ok=True)
272189
source_path = os.path.join(app_dir, file)
273190
self.merge_or_copy_conf(source_path, default_dir)
274-
# Copy all metadata files in app_dir to temp_dir of unpacked app
275-
for file in os.listdir(app_dir):
276-
if file.endswith(".meta"):
277-
default_dir = base_default_dir + "/metadata"
278-
os.makedirs(default_dir, exist_ok=True)
279-
source_path = os.path.join(app_dir, file)
280-
self.merge_or_copy_meta(source_path, default_dir)
281191
# Repack the app and place it in the root directory
282192
with tarfile.open(f"{app}.tgz", "w:gz") as tar:
283193
for root, _, files in os.walk(f"{temp_dir}/{app}"):

0 commit comments

Comments
 (0)