Skip to content

Commit 93cc63c

Browse files
author
James Rhoat
authored
Add initialization container to import a save file from a url
2 parents 561ac4b + 9c818af commit 93cc63c

File tree

6 files changed

+273
-3
lines changed

6 files changed

+273
-3
lines changed

charts/factorio-server-charts/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ sources:
2020
# This is the chart version. This version number should be incremented each time you make changes
2121
# to the chart and its templates, including the app version.
2222
# Versions are expected to follow Semantic Versioning (https://semver.org/)
23-
version: 1.2.3
23+
version: 1.2.4
2424

2525
# This is the version number of the application being deployed. This version number should be
2626
# incremented each time you make changes to the application. Versions are not expected to

charts/factorio-server-charts/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,32 @@ serverPassword:
106106
# Existing Secret containing a `game_password` data entry
107107
passwordSecret: ''
108108
```
109+
## Importing a save file
110+
111+
> :warning: Importing a save file will **OVERWRITE THE SERVER SAVEFILE** with the name specified in `factorioServer.save_name`. Import with caution!
112+
113+
### Importing by URL
114+
115+
To import your save file from a URL, set `import_save.source_url` to a direct-download link for your savegame. By default, the file will be downloaded and imported only once.
116+
117+
If, on pod intialization, you wish to re-import the file every time the contents of the savegame change, set `import_save.reimport_on_change` to `true`.
118+
:warning: If the savegame at the source url changes, this will overwrite the server save with that file. Use with caution!
119+
120+
If you wish to reimport the save file every time the pod reinitializes, regardless of changes, set `import_save.reimport_every_time` to `true`. This could be useful for demos or testing.
121+
:warning: This will overwrite the server savegame **every time the pod reinitializes**. Use with caution!
122+
123+
### Manual Import
124+
125+
To import an existing save file, start/restart the pod at least once. This will create the import folder structure.
126+
127+
Now, copy the savegame you wish to import to the `/factorio/save-importer/import/<existing_savegame_name>.zip` on the running pod using whatever mechanism you prefer. To do this with kubectl:
128+
129+
```bash
130+
kubectl cp ./my_existing_savegame.zip <namespace>/<pod_name>:/factorio/save-importer/import
131+
```
132+
133+
Restart the pod again to import your save file.
134+
109135

110136
## Installing mods
111137

@@ -222,6 +248,10 @@ If you do run into any issues with mods, I will try to work with you on finding
222248
| `factorioServer.generate_new_save` | Generate a new save if `save_name` is not found | `true` |
223249
| `factorioServer.update_mods_on_start` | Update mods on server start | `false` |
224250
| `factorioServer.load_latest_save` | Lets the game know if you want to load the latest save | `true` |
251+
| `import_save.enabled` | Enable save importer. Importer runs at every pod initialization. **:warning: Overwrites existing save if successful** | `true` |
252+
| `import_save.source_url` | Full URL (including http(s)://). If left blank, saves can still be imported by placing in '/factorio/save-importer/import' | `""` |
253+
| `import_save.reimport_on_change` | Reimport save file from source_url when checksum changes. File will be downloaded at every pod initialization. | `false` |
254+
| `import_save.reimport_every_time` | Reimport save file from source_url at every pod intialization. Useful for resetting demos or testing. | `false` |
225255
| `account.accountSecret` | Existing secret containing a valid factorio.com username and either a password or a token (or both) | `""` |
226256
| `account.username` | Factorio.com username, ignored if `account.accountSecret` is set | `""` |
227257
| `account.password` | Factorio.com password, ignored if `account.accountSecret` is set | `""` |

charts/factorio-server-charts/doc.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ repository:
99
name: github
1010
chart:
1111
name: factorio-server-charts
12-
version: 1.0.3
12+
version: 1.2.4
1313
values: "-- generate from values file --"
1414
valuesExample: "-- generate from values file --"
1515
release:

charts/factorio-server-charts/templates/deployment.yaml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,26 @@ spec:
102102
- name: account-data
103103
mountPath: /account
104104
{{- end }}
105-
{{- end }}
105+
{{- end }}
106+
{{- if .Values.import_save.enabled }}
107+
- name: import-factorio-save
108+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
109+
imagePullPolicy: {{ .Values.image.pullPolicy }}
110+
command:
111+
- /bin/bash
112+
- -ec
113+
- |
114+
bash /scripts/save-importer.sh
115+
{{- with .Values.securityContext }}
116+
securityContext:
117+
{{- toYaml . | nindent 12 }}
118+
{{- end }}
119+
volumeMounts:
120+
- name: datadir
121+
mountPath: /factorio
122+
- name: {{ template "factorio-server-charts.fullname" . }}-save-importer-configmap
123+
mountPath: /scripts
124+
{{- end }}
106125
containers:
107126
- name: {{ template "factorio-server-charts.fullname" . }}
108127
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@@ -195,3 +214,6 @@ spec:
195214
- name: {{ template "factorio-server-charts.fullname" . }}-mod-downloader-configmap
196215
configMap:
197216
name: {{ template "factorio-server-charts.fullname" . }}-mod-downloader-configmap
217+
- name: {{ template "factorio-server-charts.fullname" . }}-save-importer-configmap
218+
configMap:
219+
name: {{ template "factorio-server-charts.fullname" . }}-save-importer-configmap
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ template "factorio-server-charts.fullname" . }}-save-importer-configmap
5+
labels:
6+
app: {{ template "factorio-server-charts.fullname" . }}
7+
chart: {{ template "factorio-server-charts.fullname" . }}
8+
release: "{{ .Release.Name }}"
9+
heritage: "{{ .Release.Service }}"
10+
data:
11+
save-importer.sh: |
12+
#directory where file at $source_url will be downloaded to
13+
download_dir="/factorio/save-importer/download"
14+
15+
#directory where files from $source_url that meet staging criteria will be downloaded to
16+
#OR where files can be manually placed for import regardless of criteria.
17+
staging_dir="/factorio/save-importer/import"
18+
19+
#file used to record checksums and urls of previously downloaded files
20+
import_data="/factorio/save-importer/import_data.txt"
21+
22+
target_dir="/factorio/saves"
23+
app_name="factorio"
24+
file_extension=".zip" #note, this is used both for searching the staging_dir and displaying messages to the user
25+
file_type="save"
26+
27+
#config read from values.yaml
28+
target_filename="{{ .Values.factorioServer.save_name }}.zip" #name for the downloaded file
29+
source_url="{{ .Values.import_save.source_url }}"
30+
reimport_on_file_change={{ .Values.import_save.reimport_on_change | int }}
31+
reimport_every_time={{ .Values.import_save.reimport_every_time | int }}
32+
33+
34+
main() {
35+
echo "starting $file_type import process"
36+
37+
build_import_structure
38+
39+
if [ "${#source_url}" -gt 0 ]; then
40+
echo "source_url $source_url provided. checking..."
41+
download_file
42+
stage_downloaded_file
43+
else
44+
echo "no download url specified. checking $staging_dir for manually staged files..."
45+
fi
46+
47+
import_file
48+
}
49+
50+
get_sha256sum() {
51+
sha256sum "$1" | awk '{ print $1 }'
52+
}
53+
54+
get_previous_sha256sum() {
55+
echo $(grep "^${source_url}::" "$import_data" | awk -F '::' '{print $NF}')
56+
}
57+
58+
build_import_structure() {
59+
# staging_dir
60+
if [ ! -d "$staging_dir" ]; then
61+
mkdir -p "$staging_dir"
62+
fi
63+
64+
# download_dir
65+
if [ ! -d "$download_dir" ]; then
66+
mkdir -p "$download_dir"
67+
fi
68+
69+
# target_dir
70+
if [ ! -d "$target_dir" ]; then
71+
mkdir -p "$target_dir"
72+
fi
73+
74+
# data file
75+
if [ ! -f "$import_data" ]; then
76+
touch "$import_data"
77+
fi
78+
}
79+
80+
download_file() {
81+
do_download=0
82+
if [ "$reimport_every_time" -eq 1 ]; then
83+
do_download=1
84+
echo "reimport_every_time is set to true."
85+
else
86+
echo "reimport_every_time is set to false."
87+
fi
88+
89+
if [ "$reimport_on_file_change" -eq 1 ]; then
90+
do_download=1
91+
echo "reimport_on_file_change is set to true."
92+
else
93+
echo "reimport_on_file_change is set to false."
94+
fi
95+
96+
if ! grep -q "$source_url" "$import_data"; then
97+
do_download=1
98+
echo "source '$source_url' not previously downloaded."
99+
else
100+
echo "source '$source_url' previously downloaded."
101+
fi
102+
103+
if [ "$do_download" -eq 1 ]; then
104+
echo "downloading '$source_url'..."
105+
if curl -L -o "$download_dir/$target_filename" "$source_url"; then
106+
echo "$file_type file downloaded from '$source_url' and renamed '$target_filename'"
107+
else
108+
echo "unable to download $file_type file from '$source_url'. skipping import process."
109+
exit 0
110+
fi
111+
else
112+
echo "conditions not met to download file."
113+
fi
114+
}
115+
116+
117+
118+
write_sha256sum () {
119+
echo "writing checksum of '$source_url' download to '$import_data' file for future runs of the import script."
120+
if grep -q "^${source_url}::" "$import_data"; then
121+
# Update the checksum if the file entry already exists (escape any ampersands!)
122+
sed -i "s~^${source_url}::.*~${source_url//&/\\&}::${checksum}~" "$import_data"
123+
else
124+
# Append the new entry to the checksum file if it doesn't exist
125+
echo "${source_url}::${checksum}" >> "$import_data"
126+
fi
127+
}
128+
129+
130+
stage_downloaded_file(){
131+
stage_file=0
132+
if [ -e "$download_dir/$target_filename" ]; then
133+
#get checksum of file, and any previous checksums that might exist for this source url
134+
checksum=$(get_sha256sum "$download_dir/$target_filename")
135+
previous_checksum=$(get_previous_sha256sum "$source_url")
136+
echo "previous checksum: $previous_checksum"
137+
138+
if [ "$reimport_every_time" -eq 1 ]; then
139+
stage_file=1
140+
echo "reimport_every_time flag is set. file will be staged for import"
141+
fi
142+
143+
if [ -z "$previous_checksum" ]; then
144+
stage_file=1
145+
echo "no record found of a previous download for this file. file will be staged for import."
146+
fi
147+
148+
if [ "$checksum" != "$previous_checksum" -a "$reimport_on_file_change" ]; then
149+
echo "file from '$source_url' has changed since we last downloaded it..."
150+
if [ "$reimport_on_file_change" -eq 1 ]; then
151+
stage_file=1
152+
echo "...and 'reimport_on_file_change' is enabled. file will be staged for import"
153+
else
154+
echo "...but 'reimport_on_file_change' is disabled. file will NOT be staged for import"
155+
fi
156+
else
157+
echo "file from '$source_url' has NOT changed since we last downloaded it..."
158+
fi
159+
160+
if [ "$stage_file" -eq 1 ]; then
161+
echo "file downloaded from $source_url meets conditions for import. Moving to $staging_dir to prepare for $file_type import."
162+
write_sha256sum
163+
mv -f "$download_dir/$target_filename" "$staging_dir"
164+
else
165+
echo "file downloaded from $source_url does not meet conditions for import. Deleting the downloaded file."
166+
rm -f "$download_dir/$target_filename"
167+
fi
168+
else
169+
echo "target file not found in download directory. checking $staging_dir for manually staged files."
170+
fi
171+
}
172+
173+
import_file() {
174+
# Count the number of files with the $file_extension in the source dir
175+
count=$(find "$staging_dir" -maxdepth 1 -type f -name "*$file_extension" | wc -l)
176+
177+
if [ "$count" -eq 1 ]; then
178+
file_to_import=$(find "$staging_dir" -maxdepth 1 -type f -name "*$file_extension")
179+
echo "Found $file_type file to import - '$file_to_import'."
180+
echo "Copying '$file_to_import' to '$target_dir/$target_filename'. This will replace any previously existing file at this destination."
181+
# Copy and rename the file
182+
cp -f "$file_to_import" "$target_dir/$target_filename"
183+
if [ $? -eq 0 ]; then
184+
# Copy was successful
185+
echo "File copied to '$target_dir/$target_filename'."
186+
187+
# Touch the new copy to be _certain_ it's the latest file
188+
touch "$target_dir/$target_filename"
189+
190+
# Delete the original file, so we don't reimport it again
191+
rm "$file_to_import"
192+
echo "staging file '$file_to_import' deleted."
193+
else
194+
echo "Error copying the file."
195+
exit 1
196+
fi
197+
elif [ "$count" -eq 0 ]; then
198+
echo "No $file_type file found in '$staging_dir'"
199+
echo "Skipping $file_type import process."
200+
else
201+
echo "Multiple $file_type files found in '$staging_dir'"
202+
echo "Put only one $app_name $file_type $file_extension file in '$staging_dir' to enable the import process."
203+
echo "Skipping $file_type import process."
204+
fi
205+
}
206+
207+
main

charts/factorio-server-charts/values.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,17 @@ factorioServer:
155155
# lets the game know if you want to load the latest save
156156
load_latest_save: true
157157

158+
import_save:
159+
# enable save importer
160+
enabled: true
161+
# url to download save from (http or https only).
162+
# if no url is specified, saves can still be manually imported by placing the file in "/factorio/save-importer/import"
163+
source_url: ""
164+
# reimport from the source_url if the checksum of the file changes
165+
reimport_on_change: false
166+
# reimport from the source_url AND OVERWRITE PREVIOUS SAVE every time pod is initialized. good for demos or testing
167+
reimport_every_time: false
168+
158169
## @param account.accountSecret Existing secret containing a valid factorio.com username and either a password or a token (or both)
159170
## @param account.username Factorio.com username, ignored if `account.accountSecret` is set
160171
## @param account.password Factorio.com password, ignored if `account.accountSecret` is set

0 commit comments

Comments
 (0)