feat: initial
This commit is contained in:
commit
638004c7ee
8 changed files with 444 additions and 0 deletions
23
.forgejo/workflows/check-pr-title.yaml
Normal file
23
.forgejo/workflows/check-pr-title.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: Check PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- edited
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: stackit-docker
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.forgejo
|
||||
- name: Check PR title
|
||||
uses: https://stackit-solutions.git.onstackit.cloud/actions/check-conventional-commit@v1
|
||||
with:
|
||||
value: ${{ github.event.pull_request.title }}
|
||||
203
.gitignore
vendored
Normal file
203
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
.output
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp directory
|
||||
.temp
|
||||
|
||||
# Sveltekit cache directory
|
||||
.svelte-kit/
|
||||
|
||||
# vitepress build output
|
||||
**/.vitepress/dist
|
||||
|
||||
# vitepress cache directory
|
||||
**/.vitepress/cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Firebase cache directory
|
||||
.firebase/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# pnpm
|
||||
.pnpm-store
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Vite files
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.vite/
|
||||
|
||||
# General
|
||||
.DS_Store
|
||||
.localized
|
||||
__MACOSX/
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon[
|
||||
]
|
||||
|
||||
# Resource forks
|
||||
._*
|
||||
|
||||
# Files and directories that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
.com.apple.timemachine.supported
|
||||
.PKInstallSandboxManager
|
||||
.PKInstallSandboxManager-SystemSoftware
|
||||
.hotfiles.btree
|
||||
.vol
|
||||
.file
|
||||
.disk_label*
|
||||
lost+found
|
||||
.HFS+ Private Directory Data[
|
||||
]
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Mac OS 6 to 9
|
||||
Desktop DB
|
||||
Desktop DF
|
||||
TheFindByContentFolder
|
||||
TheVolumeSettingsFolder
|
||||
.FBCIndex
|
||||
.FBCSemaphoreFile
|
||||
.FBCLockFolder
|
||||
|
||||
# Quota system
|
||||
.quota.group
|
||||
.quota.user
|
||||
.quota.ops.group
|
||||
.quota.ops.user
|
||||
|
||||
# TimeMachine
|
||||
Backups.backupdb
|
||||
.MobileBackups
|
||||
.MobileBackups.trash
|
||||
MobileBackups.trash
|
||||
tmbootpicker.efi
|
||||
3
.prettierrc
Normal file
3
.prettierrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"printWidth": 120
|
||||
}
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
47
README.md
Normal file
47
README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# SKE Operations Action
|
||||
|
||||
This action calls SKE cluster operations from your pipelines.
|
||||
|
||||
## Usage
|
||||
|
||||
To use this action in your workflow, reference it as a step.
|
||||
|
||||
The action assumes that the STACKIT CLI and jq are installed.
|
||||
If you're using the [install-stackit-cli action](https://stackit-solutions.git.onstackit.cloud/actions/install-stackit-cli), then these applications will already be installed for you.
|
||||
|
||||
You also require a STACKIT service account key.
|
||||
The key must be tied to a service account which has editor privileges on SKE resources.
|
||||
In the following example, the service account key is provided to the runner as a base64-encoded secret.
|
||||
|
||||
```yaml
|
||||
- name: Install STACKIT CLI
|
||||
uses: https://stackit-solutions.git.onstackit.cloud/actions/install-stackit-cli@v1
|
||||
- name: Set up STACKIT service account
|
||||
env:
|
||||
SA_KEY_B64: ${{ secrets.STACKIT_SERVICE_ACCOUNT_KEY_B64 }}
|
||||
run: |
|
||||
set -eo pipefail
|
||||
|
||||
TMP_SA_KEY_PATH="$(mktemp)"
|
||||
echo "Writing STACKIT service account key to $TMP_SA_KEY_PATH"
|
||||
|
||||
echo "$SA_KEY_B64" | base64 -d > "$TMP_SA_KEY_PATH"
|
||||
echo "STACKIT_SERVICE_ACCOUNT_KEY_PATH=$TMP_SA_KEY_PATH" >> "$GITHUB_ENV"
|
||||
- name: Hibernate cluster
|
||||
uses: https://stackit-solutions.git.onstackit.cloud/actions/ske-operations@v1
|
||||
with:
|
||||
action: hibernate
|
||||
region: eu01
|
||||
project-id: 01234567-89ab-cdef-0123-456789abcdef
|
||||
cluster-name: foo-cluster
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- `action` (Required): Operation to perform. Must be one of: `wake`, `hibernate`, `reconcile`, `maintenance`.
|
||||
- `region` (Required): Data center region of SKE cluster.
|
||||
- `project-id` (Required): STACKIT project ID.
|
||||
- `cluster-name` (Required): Name of SKE cluster.
|
||||
- `timeout-seconds` (Optional): Time in seconds after which target state check fails. Default is `900`.
|
||||
- `interval-seconds` (Optional): Time in seconds between target state checks. Default is `5`.
|
||||
- `wait` (Optional): Wait until target state of operation has been reached. Default is `true`.
|
||||
130
action.yml
Normal file
130
action.yml
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
name: "Trigger SKE operations"
|
||||
description: "Performs operations on SKE clusters (wake, hibernate, reconcile, maintenance)."
|
||||
inputs:
|
||||
action:
|
||||
description: "Operation to perform."
|
||||
required: true
|
||||
region:
|
||||
description: "Data center region of SKE cluster."
|
||||
required: true
|
||||
project-id:
|
||||
description: "STACKIT project ID."
|
||||
required: true
|
||||
cluster-name:
|
||||
description: "Name of SKE cluster."
|
||||
required: true
|
||||
timeout-seconds:
|
||||
description: "Time in seconds after which target state check fails."
|
||||
required: false
|
||||
default: "900"
|
||||
interval-seconds:
|
||||
description: "Time in seconds between target state checks."
|
||||
required: false
|
||||
default: "5"
|
||||
wait:
|
||||
description: "Wait until target state of operation has been reached."
|
||||
required: false
|
||||
default: "true"
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Trigger SKE operation
|
||||
shell: sh
|
||||
env:
|
||||
INPUT_ACTION: ${{ inputs.action }}
|
||||
INPUT_REGION: ${{ inputs.region }}
|
||||
INPUT_PROJECT_ID: ${{ inputs.project-id }}
|
||||
INPUT_CLUSTER_NAME: ${{ inputs.cluster-name }}
|
||||
INPUT_TIMEOUT_SECS: ${{ inputs.timeout-seconds }}
|
||||
INPUT_INTERVAL_SECS: ${{ inputs.interval-seconds }}
|
||||
INPUT_WAIT: ${{ inputs.wait }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
for cmd in stackit jq grep; do
|
||||
if ! command -v "$cmd" > /dev/null 2>&1; then
|
||||
echo "'$cmd' not installed."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
URL_SAFE_REGEX="^[a-zA-Z0-9._~-]+$"
|
||||
|
||||
for input in "$INPUT_ACTION" "$INPUT_REGION" "$INPUT_PROJECT_ID" "$INPUT_CLUSTER_NAME"; do
|
||||
if ! printf "%s\n" "$input" | grep -Eq "$URL_SAFE_REGEX"; then
|
||||
echo "'$input' is not a URL-safe input."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
TIMEOUT_SECS="${INPUT_TIMEOUT_SECS:-900}"
|
||||
INTERVAL_SECS="${INPUT_INTERVAL_SECS:-5}"
|
||||
WAIT="${INPUT_WAIT:-true}"
|
||||
|
||||
case "$INPUT_ACTION" in
|
||||
hibernate)
|
||||
TARGET_STATUS="STATE_HIBERNATED"
|
||||
INITIAL_STATUS="STATE_HEALTHY"
|
||||
ENDPOINT="hibernate"
|
||||
;;
|
||||
wake)
|
||||
TARGET_STATUS="STATE_HEALTHY"
|
||||
INITIAL_STATUS="STATE_HIBERNATED"
|
||||
ENDPOINT="wakeup"
|
||||
;;
|
||||
reconcile)
|
||||
TARGET_STATUS="STATE_HEALTHY"
|
||||
INITIAL_STATUS="STATE_HEALTHY"
|
||||
ENDPOINT="reconcile"
|
||||
;;
|
||||
maintenance)
|
||||
TARGET_STATUS="STATE_HEALTHY"
|
||||
INITIAL_STATUS="STATE_HEALTHY"
|
||||
ENDPOINT="maintenance"
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected action '$INPUT_ACTION'. Must be one of: wake, hibernate, reconcile, maintenance."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
CURRENT_STATUS="$(stackit ske cluster describe "$INPUT_CLUSTER_NAME" -ojson | jq -r '.status.aggregated')"
|
||||
|
||||
if [ "$CURRENT_STATUS" != "$INITIAL_STATUS" ]; then
|
||||
if [ "$CURRENT_STATUS" = "$TARGET_STATUS" ]; then
|
||||
echo "Cluster is already in desired state."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Unexpected cluster status: $CURRENT_STATUS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
stackit curl --fail -X POST "https://ske.api.stackit.cloud/v2/projects/$INPUT_PROJECT_ID/regions/$INPUT_REGION/clusters/$INPUT_CLUSTER_NAME/$ENDPOINT"
|
||||
|
||||
if [ "$WAIT" != "true" ]; then
|
||||
echo "Operation triggered. No wait requested, exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
START_TIME="$(date +%s)"
|
||||
SECS_ELAPSED=0
|
||||
|
||||
while [ "$SECS_ELAPSED" -lt "$TIMEOUT_SECS" ]; do
|
||||
CURRENT_STATUS="$(stackit ske cluster describe "$INPUT_CLUSTER_NAME" -ojson | jq -r '.status.aggregated')"
|
||||
|
||||
if [ "$CURRENT_STATUS" = "$TARGET_STATUS" ]; then
|
||||
echo "Targeted state reached after $SECS_ELAPSED seconds, exiting."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Target state not reached, waiting ${INTERVAL_SECS}s... (elapsed: ${SECS_ELAPSED}s)"
|
||||
sleep "$INTERVAL_SECS"
|
||||
|
||||
CURRENT_TIME="$(date +%s)"
|
||||
SECS_ELAPSED=$((CURRENT_TIME - START_TIME))
|
||||
done
|
||||
|
||||
echo "::error::Target state not reached after $SECS_ELAPSED seconds, exiting."
|
||||
exit 1
|
||||
28
package-lock.json
generated
Normal file
28
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "ske-operations",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"prettier": "^3.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz",
|
||||
"integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
package.json
Normal file
5
package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"prettier": "^3.8.3"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue