underpost 2.95.3 → 2.96.0
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.
- package/README.md +2 -2
- package/baremetal/commission-workflows.json +44 -0
- package/baremetal/packer-workflows.json +13 -0
- package/bin/deploy.js +6 -26
- package/cli.md +40 -43
- package/conf.js +4 -1
- package/examples/{QUICK-REFERENCE.md → static-page/QUICK-REFERENCE.md} +0 -18
- package/examples/{README.md → static-page/README.md} +3 -44
- package/examples/{STATIC-GENERATOR-GUIDE.md → static-page/STATIC-GENERATOR-GUIDE.md} +0 -50
- package/examples/{ssr-components → static-page/ssr-components}/CustomPage.js +0 -13
- package/examples/{static-config-simple.json → static-page/static-config-example.json} +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/packer/images/Rocky9Amd64/Makefile +62 -0
- package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
- package/packer/images/Rocky9Amd64/README.md +122 -0
- package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
- package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +160 -0
- package/packer/scripts/fuse-nbd +64 -0
- package/packer/scripts/fuse-tar-root +63 -0
- package/scripts/maas-setup.sh +13 -2
- package/scripts/maas-upload-boot-resource.sh +183 -0
- package/scripts/packer-init-vars-file.sh +30 -0
- package/scripts/packer-setup.sh +52 -0
- package/src/cli/baremetal.js +262 -65
- package/src/cli/cloud-init.js +11 -5
- package/src/cli/cron.js +161 -29
- package/src/cli/db.js +59 -92
- package/src/cli/env.js +24 -3
- package/src/cli/index.js +18 -58
- package/src/cli/repository.js +178 -0
- package/src/cli/run.js +2 -3
- package/src/cli/static.js +99 -194
- package/src/client/services/default/default.management.js +7 -0
- package/src/index.js +1 -1
- package/src/server/backup.js +4 -53
- package/src/server/conf.js +3 -4
- package/examples/static-config-example.json +0 -183
- package/src/client/ssr/pages/404.js +0 -12
- package/src/client/ssr/pages/500.js +0 -12
- package/src/client/ssr/pages/maintenance.js +0 -14
- package/src/client/ssr/pages/offline.js +0 -21
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Script to upload boot resources to MAAS using the REST API with OAuth
|
|
3
|
+
# Usage: ./maas-upload-boot-resource.sh <profile> <name> <title> <architecture> <base_image> <filetype> <file_path>
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
if [ $# -lt 7 ]; then
|
|
8
|
+
echo "Usage: $0 <profile> <name> <title> <architecture> <base_image> <filetype> <file_path>"
|
|
9
|
+
echo ""
|
|
10
|
+
echo "Example:"
|
|
11
|
+
echo " $0 maas custom/rocky9 'Rocky 9 Custom' amd64/generic rhel/9 tgz rocky9.tar.gz"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
PROFILE="$1"
|
|
16
|
+
NAME="$2"
|
|
17
|
+
TITLE="$3"
|
|
18
|
+
ARCHITECTURE="$4"
|
|
19
|
+
BASE_IMAGE="$5"
|
|
20
|
+
FILETYPE="$6"
|
|
21
|
+
FILE_PATH="$7"
|
|
22
|
+
|
|
23
|
+
# Verify file exists
|
|
24
|
+
if [ ! -f "$FILE_PATH" ]; then
|
|
25
|
+
echo "Error: File not found: $FILE_PATH"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Verify jq is installed
|
|
30
|
+
if ! command -v jq &> /dev/null; then
|
|
31
|
+
echo "Error: jq is required for this script but not installed."
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Get MAAS API URL and credentials from profile
|
|
36
|
+
MAAS_INFO=$(maas list | grep "^${PROFILE}" || true)
|
|
37
|
+
if [ -z "$MAAS_INFO" ]; then
|
|
38
|
+
echo "Error: MAAS profile '${PROFILE}' not found"
|
|
39
|
+
echo "Available profiles:"
|
|
40
|
+
maas list
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
API_URL=$(echo "$MAAS_INFO" | awk '{print $2}')
|
|
45
|
+
API_KEY=$(echo "$MAAS_INFO" | awk '{print $3}')
|
|
46
|
+
|
|
47
|
+
# Parse OAuth credentials
|
|
48
|
+
CONSUMER_KEY=$(echo "$API_KEY" | cut -d: -f1)
|
|
49
|
+
TOKEN_KEY=$(echo "$API_KEY" | cut -d: -f2)
|
|
50
|
+
TOKEN_SECRET=$(echo "$API_KEY" | cut -d: -f3)
|
|
51
|
+
|
|
52
|
+
# Calculate file hash and size
|
|
53
|
+
echo "Calculating SHA256 checksum..."
|
|
54
|
+
SHA256=$(sha256sum "$FILE_PATH" | awk '{print $1}')
|
|
55
|
+
SIZE=$(stat -c%s "$FILE_PATH")
|
|
56
|
+
|
|
57
|
+
echo "File: $FILE_PATH"
|
|
58
|
+
echo "Size: $SIZE bytes ($(numfmt --to=iec-i --suffix=B $SIZE))"
|
|
59
|
+
echo "SHA256: $SHA256"
|
|
60
|
+
echo ""
|
|
61
|
+
|
|
62
|
+
# Endpoint for boot resources
|
|
63
|
+
ENDPOINT="${API_URL}boot-resources/"
|
|
64
|
+
|
|
65
|
+
# Generate OAuth timestamp and nonce
|
|
66
|
+
TIMESTAMP=$(date +%s)
|
|
67
|
+
NONCE=$(openssl rand -hex 16)
|
|
68
|
+
|
|
69
|
+
# OAuth parameters
|
|
70
|
+
OAUTH_VERSION="1.0"
|
|
71
|
+
OAUTH_SIGNATURE_METHOD="PLAINTEXT"
|
|
72
|
+
OAUTH_SIGNATURE="&${TOKEN_SECRET}"
|
|
73
|
+
|
|
74
|
+
echo "Initiating upload to MAAS..."
|
|
75
|
+
echo "API URL: $ENDPOINT"
|
|
76
|
+
echo "Name: $NAME"
|
|
77
|
+
echo "Title: $TITLE"
|
|
78
|
+
echo "Architecture: $ARCHITECTURE"
|
|
79
|
+
echo "Base Image: $BASE_IMAGE"
|
|
80
|
+
echo "Filetype: $FILETYPE"
|
|
81
|
+
echo ""
|
|
82
|
+
|
|
83
|
+
# 1. Initiate Upload (POST metadata)
|
|
84
|
+
# We do NOT send the content here, just the metadata to create the resource and get the upload URI.
|
|
85
|
+
RESPONSE=$(curl -s -X POST "${ENDPOINT}" \
|
|
86
|
+
-H "Authorization: OAuth oauth_version=\"${OAUTH_VERSION}\", oauth_signature_method=\"${OAUTH_SIGNATURE_METHOD}\", oauth_consumer_key=\"${CONSUMER_KEY}\", oauth_token=\"${TOKEN_KEY}\", oauth_signature=\"${OAUTH_SIGNATURE}\", oauth_timestamp=\"${TIMESTAMP}\", oauth_nonce=\"${NONCE}\"" \
|
|
87
|
+
-F "name=${NAME}" \
|
|
88
|
+
-F "title=${TITLE}" \
|
|
89
|
+
-F "architecture=${ARCHITECTURE}" \
|
|
90
|
+
-F "base_image=${BASE_IMAGE}" \
|
|
91
|
+
-F "filetype=${FILETYPE}" \
|
|
92
|
+
-F "sha256=${SHA256}" \
|
|
93
|
+
-F "size=${SIZE}")
|
|
94
|
+
CURL_RET=$?
|
|
95
|
+
|
|
96
|
+
if [ $CURL_RET -ne 0 ]; then
|
|
97
|
+
echo "Error: curl failed with exit code $CURL_RET"
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Validate JSON before parsing
|
|
102
|
+
if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then
|
|
103
|
+
echo "Error: MAAS returned invalid JSON."
|
|
104
|
+
echo "Raw response:"
|
|
105
|
+
echo "$RESPONSE"
|
|
106
|
+
exit 1
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Extract Upload URI
|
|
110
|
+
UPLOAD_URI=$(echo "$RESPONSE" | jq -r '.sets | to_entries | sort_by(.key) | reverse | .[0].value.files | to_entries | .[0].value.upload_uri // empty')
|
|
111
|
+
|
|
112
|
+
if [ -z "$UPLOAD_URI" ]; then
|
|
113
|
+
echo "✗ Failed to get upload URI from MAAS response."
|
|
114
|
+
echo "Response:"
|
|
115
|
+
echo "$RESPONSE" | jq . 2>/dev/null || echo "$RESPONSE"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
echo "Upload URI obtained: $UPLOAD_URI"
|
|
120
|
+
|
|
121
|
+
# Construct the full upload URL
|
|
122
|
+
if [[ "$UPLOAD_URI" == http* ]]; then
|
|
123
|
+
FULL_UPLOAD_URL="$UPLOAD_URI"
|
|
124
|
+
else
|
|
125
|
+
# Extract scheme and authority from API_URL
|
|
126
|
+
# e.g. http://192.168.1.5:5240/MAAS/api/2.0/ -> http://192.168.1.5:5240
|
|
127
|
+
MAAS_ROOT=$(echo "$API_URL" | sed -E 's|^(https?://[^/]+).*|\1|')
|
|
128
|
+
|
|
129
|
+
# Ensure UPLOAD_URI starts with /
|
|
130
|
+
[[ "$UPLOAD_URI" != /* ]] && UPLOAD_URI="/$UPLOAD_URI"
|
|
131
|
+
|
|
132
|
+
FULL_UPLOAD_URL="${MAAS_ROOT}${UPLOAD_URI}"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
echo "Full Upload URL: $FULL_UPLOAD_URL"
|
|
136
|
+
|
|
137
|
+
# 2. Split file into chunks
|
|
138
|
+
CHUNK_SIZE=$((4 * 1024 * 1024)) # 4MB
|
|
139
|
+
TMP_DIR=$(mktemp -d)
|
|
140
|
+
echo "Splitting file into 4MB chunks in $TMP_DIR..."
|
|
141
|
+
split -b ${CHUNK_SIZE} "${FILE_PATH}" "${TMP_DIR}/chunk_"
|
|
142
|
+
|
|
143
|
+
# 3. Upload chunks
|
|
144
|
+
echo "Starting chunked upload..."
|
|
145
|
+
CHUNK_COUNT=$(ls "${TMP_DIR}"/chunk_* | wc -l)
|
|
146
|
+
CURRENT_CHUNK=0
|
|
147
|
+
|
|
148
|
+
for chunk in "${TMP_DIR}"/chunk_*; do
|
|
149
|
+
CURRENT_CHUNK=$((CURRENT_CHUNK + 1))
|
|
150
|
+
CHUNK_SIZE_BYTES=$(stat -c%s "$chunk")
|
|
151
|
+
|
|
152
|
+
# Progress indicator
|
|
153
|
+
echo -ne "Uploading chunk $CURRENT_CHUNK of $CHUNK_COUNT ($CHUNK_SIZE_BYTES bytes)...\r"
|
|
154
|
+
|
|
155
|
+
# Generate new nonce/timestamp for each request
|
|
156
|
+
TIMESTAMP=$(date +%s)
|
|
157
|
+
NONCE=$(openssl rand -hex 16)
|
|
158
|
+
|
|
159
|
+
# Upload chunk
|
|
160
|
+
CHUNK_RESPONSE=$(curl -s -X PUT "${FULL_UPLOAD_URL}" \
|
|
161
|
+
-H "Content-Type: application/octet-stream" \
|
|
162
|
+
-H "Content-Length: ${CHUNK_SIZE_BYTES}" \
|
|
163
|
+
-H "Authorization: OAuth oauth_version=\"${OAUTH_VERSION}\", oauth_signature_method=\"${OAUTH_SIGNATURE_METHOD}\", oauth_consumer_key=\"${CONSUMER_KEY}\", oauth_token=\"${TOKEN_KEY}\", oauth_signature=\"${OAUTH_SIGNATURE}\", oauth_timestamp=\"${TIMESTAMP}\", oauth_nonce=\"${NONCE}\"" \
|
|
164
|
+
--data-binary @"${chunk}" \
|
|
165
|
+
-w "%{http_code}")
|
|
166
|
+
|
|
167
|
+
HTTP_CODE="${CHUNK_RESPONSE: -3}"
|
|
168
|
+
|
|
169
|
+
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
|
|
170
|
+
echo ""
|
|
171
|
+
echo "✗ Chunk upload failed with status: $HTTP_CODE"
|
|
172
|
+
echo "Response: ${CHUNK_RESPONSE::-3}"
|
|
173
|
+
rm -r "${TMP_DIR}"
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
rm "$chunk"
|
|
178
|
+
done
|
|
179
|
+
|
|
180
|
+
echo ""
|
|
181
|
+
echo "✓ Upload complete!"
|
|
182
|
+
rm -r "${TMP_DIR}"
|
|
183
|
+
exit 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Script to Initialize VARS files for Packer builds
|
|
3
|
+
# Usage: sudo ./scripts/packer-setup.sh
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# Packer needs writable VARS files for UEFI boot
|
|
8
|
+
PACKER_DIR="$(dirname "$0")/../packer/images"
|
|
9
|
+
|
|
10
|
+
# Find all packer image directories and create VARS files
|
|
11
|
+
for image_dir in "$PACKER_DIR"/*; do
|
|
12
|
+
if [ -d "$image_dir" ]; then
|
|
13
|
+
image_name=$(basename "$image_dir")
|
|
14
|
+
echo "Checking UEFI VARS files for $image_name..."
|
|
15
|
+
|
|
16
|
+
# Create x86_64 VARS file if it doesn't exist
|
|
17
|
+
if [ -f /usr/share/edk2/ovmf/OVMF_VARS.fd ] && [ ! -f "$image_dir/x86_64_VARS.fd" ]; then
|
|
18
|
+
cp /usr/share/edk2/ovmf/OVMF_VARS.fd "$image_dir/x86_64_VARS.fd"
|
|
19
|
+
echo "Created $image_dir/x86_64_VARS.fd"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Create aarch64 VARS file if it doesn't exist
|
|
23
|
+
if [ -f /usr/share/edk2/aarch64/AAVMF_VARS.fd ] && [ ! -f "$image_dir/aarch64_VARS.fd" ]; then
|
|
24
|
+
cp /usr/share/edk2/aarch64/AAVMF_VARS.fd "$image_dir/aarch64_VARS.fd"
|
|
25
|
+
echo "Created $image_dir/aarch64_VARS.fd"
|
|
26
|
+
fi
|
|
27
|
+
fi
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
echo "Packer and QEMU setup complete!"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Script to install Packer and libvirt/qemu tooling on Rocky Linux.
|
|
3
|
+
# Usage: sudo ./scripts/packer-setup.sh
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# 1) Replace/add the correct HashiCorp repo for RHEL/Rocky
|
|
9
|
+
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
|
|
10
|
+
|
|
11
|
+
# Refresh dnf metadata
|
|
12
|
+
sudo dnf clean all
|
|
13
|
+
sudo dnf makecache --refresh
|
|
14
|
+
|
|
15
|
+
# 2) Try to install packer from the repo
|
|
16
|
+
sudo dnf install -y packer || echo "packer install from repo failed, try manual install (see below)"
|
|
17
|
+
|
|
18
|
+
# 3) Install libvirt/qemu tooling then start the service
|
|
19
|
+
sudo dnf install -y libvirt libvirt-daemon qemu-kvm qemu-img virt-install bridge-utils
|
|
20
|
+
sudo systemctl enable --now libvirtd
|
|
21
|
+
sudo systemctl status libvirtd --no-pager
|
|
22
|
+
|
|
23
|
+
# 3a) Install NBD and filesystem tools required for MAAS image creation
|
|
24
|
+
sudo dnf install -y libnbd nbdkit e2fsprogs kmod-kvdo kmod
|
|
25
|
+
|
|
26
|
+
# 4) Install UEFI firmware for x86_64 and ARM64
|
|
27
|
+
sudo dnf install -y edk2-ovmf edk2-aarch64
|
|
28
|
+
|
|
29
|
+
# 5) Create symlinks for qemu-system-* binaries (Rocky/RHEL uses qemu-kvm instead)
|
|
30
|
+
# Packer expects standard qemu-system-* names, but RHEL-based distros use qemu-kvm
|
|
31
|
+
if [ -f /usr/libexec/qemu-kvm ] && [ ! -f /usr/bin/qemu-system-x86_64 ]; then
|
|
32
|
+
echo "Creating symlink: /usr/bin/qemu-system-x86_64 -> /usr/libexec/qemu-kvm"
|
|
33
|
+
sudo ln -sf /usr/libexec/qemu-kvm /usr/bin/qemu-system-x86_64
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if [ -f /usr/libexec/qemu-kvm ] && [ ! -f /usr/bin/qemu-system-aarch64 ]; then
|
|
37
|
+
echo "Creating symlink: /usr/bin/qemu-system-aarch64 -> /usr/libexec/qemu-kvm"
|
|
38
|
+
sudo ln -sf /usr/libexec/qemu-kvm /usr/bin/qemu-system-aarch64
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# 6) Create symlinks for OVMF/AAVMF firmware files in expected locations
|
|
42
|
+
# Rocky/RHEL stores OVMF in /usr/share/edk2/ovmf, but Packer expects /usr/share/OVMF
|
|
43
|
+
if [ -f /usr/share/edk2/ovmf/OVMF_CODE.fd ] && [ ! -f /usr/share/OVMF/OVMF_CODE.fd ]; then
|
|
44
|
+
echo "Creating symlink: /usr/share/OVMF/OVMF_CODE.fd -> /usr/share/edk2/ovmf/OVMF_CODE.fd"
|
|
45
|
+
sudo ln -sf /usr/share/edk2/ovmf/OVMF_CODE.fd /usr/share/OVMF/OVMF_CODE.fd
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Create AAVMF symlinks for ARM64 support
|
|
49
|
+
if [ -d /usr/share/edk2/aarch64 ] && [ ! -d /usr/share/AAVMF ]; then
|
|
50
|
+
echo "Creating symlink: /usr/share/AAVMF -> /usr/share/edk2/aarch64"
|
|
51
|
+
sudo ln -sf /usr/share/edk2/aarch64 /usr/share/AAVMF
|
|
52
|
+
fi
|