Skip to content

Commit d9710f6

Browse files
committed
Initial extensions persistence impl
**Description** Initial extensions persistence implementation
1 parent ba7b1cf commit d9710f6

File tree

2 files changed

+100
-4
lines changed

2 files changed

+100
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import json
2+
import os
3+
import shutil
4+
import sys
5+
from datetime import datetime
6+
from typing import Set
7+
8+
9+
def backup_file(file_path):
10+
directory, filename = os.path.split(file_path)
11+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
12+
backup_filename = f"{filename}.malformed_{timestamp}.bak"
13+
backup_path = os.path.join(directory, backup_filename)
14+
shutil.copy2(file_path, backup_path)
15+
print(f"A backup of the malformed file has been created: {backup_path}")
16+
17+
18+
def load_json_file(file_path):
19+
try:
20+
with open(file_path, "r") as f:
21+
return json.load(f)
22+
except json.JSONDecodeError:
23+
print(f"Error: {file_path} contains malformed JSON.")
24+
if file_path == sys.argv[1]: # If it's file1
25+
backup_file(file_path)
26+
print(f"Overwriting {file_path} with content from {sys.argv[2]}")
27+
shutil.copy2(sys.argv[2], file_path)
28+
with open(file_path, "r") as f:
29+
return json.load(f)
30+
else:
31+
raise
32+
33+
34+
def main():
35+
if len(sys.argv) != 3:
36+
print("Usage: python script.py <file1> <file2>")
37+
sys.exit(1)
38+
39+
file1, file2 = sys.argv[1], sys.argv[2]
40+
41+
try:
42+
data1 = load_json_file(file1)
43+
data2 = load_json_file(file2)
44+
45+
# Construct sets of extensions based on ID
46+
data1_exts = {ext["identifier"]["id"]: ext for ext in data1}
47+
data2_exts = {ext["identifier"]["id"]: ext for ext in data2}
48+
all_exts: Set[str] = set(data1_exts.keys()) | set(data2_exts.keys())
49+
50+
# Merge extensions
51+
merged_exts = []
52+
for ext in all_exts:
53+
ext_data = data2_exts.get(ext, data1_exts.get(ext, {}))
54+
if "relativeLocation" in ext_data:
55+
del ext_data["relativeLocation"]
56+
merged_exts.append(ext_data)
57+
58+
# Write the merged data back to file1
59+
print("Merged extensions: ", json.dumps(merged_exts, indent=2))
60+
with open(file1, "w") as f:
61+
json.dump(merged_exts, f, indent=2)
62+
63+
print(f"Successfully merged extensions and wrote to {file1}")
64+
65+
except FileNotFoundError as e:
66+
print(f"Error: File not found - {e.filename}")
67+
except json.JSONDecodeError as e:
68+
print(f"Error: JSON decoding failed - {e}")
69+
except Exception as e:
70+
print(f"An unexpected error occurred: {e}")
71+
72+
73+
if __name__ == "__main__":
74+
main()

template/v2/dirs/usr/local/bin/start-code-editor

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#!/bin/bash
22
set -e
33

4-
EFS_MOUNT_POINT="/opt/amazon/sagemaker"
4+
SERVICE_DIRECTORY="/opt/amazon/sagemaker"
55
EBS_MOUNT_POINT="/home/sagemaker-user"
66

77
persistent_settings_folder="${EBS_MOUNT_POINT}/sagemaker-code-editor-server-data"
8-
default_settings_folder="${EFS_MOUNT_POINT}/sagemaker-code-editor-server-data"
8+
default_settings_folder="${SERVICE_DIRECTORY}/sagemaker-code-editor-server-data"
99

1010
override_machine_settings() {
1111
# create a new settings file with preset defaults or merge the defaults into the existing settings file
@@ -39,6 +39,27 @@ copy_user_settings() {
3939
fi
4040
}
4141

42+
merge_prepackaged_extensions() {
43+
local extensions_metadata_relative_path="/extensions"
44+
local default_extensions_metadata_file="${default_settings_folder}/${extensions_metadata_relative_path}/extensions.json"
45+
local persistent_extensions_metadata_file="${persistent_settings_folder}/${extensions_metadata_relative_path}/extensions.json"
46+
local obsolete_file="${persistent_settings_folder}/${extensions_metadata_relative_path}/.obsolete"
47+
if [ ! -f "$persistent_extensions_metadata_file" ]; then
48+
# copy settings file to EBS if it doesn't exist in EBS
49+
mkdir -p "${persistent_settings_folder}/${extensions_metadata_relative_path}"
50+
echo "[]" > $persistent_extensions_metadata_file
51+
echo "Created persistent extensions metadata file with default settings at $persistent_extensions_metadata_file"
52+
else
53+
# if it does exist then merge settings
54+
echo "File already exists: ${persistent_extensions_metadata_file}. Overwriting extensions with pre-packaged extensions."
55+
fi
56+
57+
if [ -f "$obsolete_file" ]; then
58+
rm $obsolete_file
59+
fi
60+
python3 /usr/local/bin/merge-extensions-util.py "$persistent_extensions_metadata_file" "$default_extensions_metadata_file"
61+
}
62+
4263
eval "$(micromamba shell hook --shell=bash)"
4364

4465
# Activate conda environment 'base', which is the default environment for sagemaker-distribution
@@ -49,17 +70,18 @@ if [ -n "$SAGEMAKER_APP_TYPE_LOWERCASE" ]; then
4970
# SAGEMAKER_APP_TYPE is set, indicating the server is running within a SageMaker app.
5071
override_machine_settings
5172
copy_user_settings
73+
merge_prepackaged_extensions
5274
# Configure the base url to be `/<app-type-in-lower-case>/default`.
5375
sagemaker-code-editor --host 0.0.0.0 --port 8888 \
5476
--without-connection-token \
5577
--base-path "/$SAGEMAKER_APP_TYPE_LOWERCASE/default" \
5678
--server-data-dir $persistent_settings_folder \
57-
--extensions-dir /opt/amazon/sagemaker/sagemaker-code-editor-server-data/extensions \
79+
--extensions-dir $persistent_settings_folder/extensions \
5880
--user-data-dir /opt/amazon/sagemaker/sagemaker-code-editor-user-data
5981
else
6082
sagemaker-code-editor --host 0.0.0.0 --port 8888 \
6183
--without-connection-token \
6284
--server-data-dir /opt/amazon/sagemaker/sagemaker-code-editor-server-data \
6385
--extension-dir /opt/amazon/sagemaker/sagemaker-code-editor-server-data/extensions \
6486
--user-data-dir /opt/amazon/sagemaker/sagemaker-code-editor-user-data
65-
fi
87+
fi

0 commit comments

Comments
 (0)