Compare commits
56 commits
main
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8cb7e49c1 | ||
|
|
c611aa3de7 | ||
|
|
f7ca9880a4 | ||
|
|
d59a691eb7 | ||
|
|
4e32be953c | ||
|
|
4d60e5ffda | ||
|
|
9accd4a7c9 | ||
|
|
93ad9366e2 | ||
|
|
6ccdcf8d55 | ||
|
|
161d4fe17e | ||
|
|
14c652c8eb | ||
|
|
626ba21122 | ||
|
|
a8b11eea7b | ||
|
|
6ba8309617 | ||
|
|
82a6b48314 | ||
|
|
1c78614bbf | ||
|
|
8b1f29975c | ||
|
|
94f5b79390 | ||
|
|
5fc12d44b7 | ||
|
|
b0ca7ce577 | ||
|
|
caacf92507 | ||
|
|
016fe585b5 | ||
|
|
b23775dc7b | ||
|
|
d093ec2eb0 | ||
|
|
1433adf88f | ||
|
|
5bf0112bed | ||
|
|
06dce029c9 | ||
|
|
5674ce73e6 | ||
|
|
69ccaa772e | ||
|
|
198c57c1ea | ||
|
|
8d1628df49 | ||
|
|
0726e1aa51 | ||
|
|
168ebe1a3e | ||
|
|
3b2d6a6325 | ||
|
|
ec480b0617 | ||
|
|
a0209327b4 | ||
|
|
1b32142ce3 | ||
|
|
27638a14b5 | ||
|
|
52a16dc518 | ||
|
|
d68f56ae19 | ||
|
|
6de6ca1abf | ||
|
|
aefd8b45ff | ||
|
|
f16ef08a98 | ||
|
|
3ab03d3bce | ||
|
|
56f0484eb0 | ||
|
|
9a1b45b49d | ||
|
|
e3336bf6f5 | ||
|
|
064294d870 | ||
|
|
f6ce580126 | ||
|
|
091f21fbd2 | ||
|
|
02e200372b | ||
|
|
982a6508a9 | ||
|
|
0eefd0b236 | ||
|
|
56e6178685 | ||
|
|
4d92b6362b | ||
|
|
7eca74675a |
76 changed files with 1464 additions and 664 deletions
30
.github/workflows/auto-release.yaml
vendored
30
.github/workflows/auto-release.yaml
vendored
|
|
@ -20,6 +20,14 @@ jobs:
|
|||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -28,27 +36,27 @@ jobs:
|
|||
|
||||
- name: Configure git
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
git config --unset-all http.https://github.com/.extraheader || true
|
||||
|
||||
- name: Process release branches
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Configure git to use PAT for authentication
|
||||
execSync('git config user.name "cozystack-bot"', { encoding: 'utf8' });
|
||||
execSync('git config user.email "217169706+cozystack-bot@users.noreply.github.com"', { encoding: 'utf8' });
|
||||
execSync(`git remote set-url origin https://cozystack-bot:${process.env.GH_PAT}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' });
|
||||
// Remove GITHUB_TOKEN extraheader to ensure PAT is used (needed to trigger other workflows)
|
||||
// Configure git to use GitHub App token for authentication
|
||||
execSync('git config user.name "cozystack-ci[bot]"', { encoding: 'utf8' });
|
||||
execSync('git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"', { encoding: 'utf8' });
|
||||
execSync(`git remote set-url origin https://x-access-token:${process.env.APP_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' });
|
||||
// Remove GITHUB_TOKEN extraheader to ensure App token is used (needed to trigger other workflows)
|
||||
execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' });
|
||||
|
||||
// Get all release-X.Y branches
|
||||
|
|
|
|||
18
.github/workflows/pull-requests-release.yaml
vendored
18
.github/workflows/pull-requests-release.yaml
vendored
|
|
@ -23,6 +23,14 @@ jobs:
|
|||
contains(github.event.pull_request.labels.*.name, 'release')
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
# Extract tag from branch name (branch = release-X.Y.Z*)
|
||||
- name: Extract tag from branch name
|
||||
id: get_tag
|
||||
|
|
@ -47,11 +55,11 @@ jobs:
|
|||
|
||||
- name: Create tag on merge commit
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }}
|
||||
git push -f origin ${{ steps.get_tag.outputs.tag }}
|
||||
|
||||
|
|
@ -59,7 +67,7 @@ jobs:
|
|||
- name: Ensure maintenance branch release-X.Y
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const tag = '${{ steps.get_tag.outputs.tag }}'; // e.g. v0.1.3 or v0.1.3-rc3
|
||||
const match = tag.match(/^v(\d+)\.(\d+)\.\d+(?:[-\w\.]+)?$/);
|
||||
|
|
|
|||
23
.github/workflows/pull-requests.yaml
vendored
23
.github/workflows/pull-requests.yaml
vendored
|
|
@ -99,6 +99,14 @@ jobs:
|
|||
disk_id: ${{ steps.fetch_assets.outputs.disk_id }}
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
- name: Checkout code
|
||||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -125,7 +133,7 @@ jobs:
|
|||
id: fetch_assets
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const tag = '${{ steps.get_tag.outputs.tag }}';
|
||||
const releases = await github.rest.repos.listReleases({
|
||||
|
|
@ -159,6 +167,15 @@ jobs:
|
|||
if: ${{ always() && (needs.build.result == 'success' || needs.resolve_assets.result == 'success') }}
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
# ▸ Checkout and prepare the codebase
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -188,11 +205,11 @@ jobs:
|
|||
if: contains(github.event.pull_request.labels.*.name, 'release')
|
||||
run: |
|
||||
mkdir -p _out/assets
|
||||
curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \
|
||||
curl -sSL -H "Authorization: token ${APP_TOKEN}" -H "Accept: application/octet-stream" \
|
||||
-o _out/assets/nocloud-amd64.raw.xz \
|
||||
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}"
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Set sandbox ID
|
||||
run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV
|
||||
|
|
|
|||
83
.github/workflows/tags.yaml
vendored
83
.github/workflows/tags.yaml
vendored
|
|
@ -25,6 +25,14 @@ jobs:
|
|||
actions: write
|
||||
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
# Check if a non-draft release with this tag already exists
|
||||
- name: Check if release already exists
|
||||
id: check_release
|
||||
|
|
@ -115,34 +123,29 @@ jobs:
|
|||
- name: Commit release artifacts
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
git config --unset-all http.https://github.com/.extraheader || true
|
||||
git add .
|
||||
git commit -m "Prepare release ${GITHUB_REF#refs/tags/}" -s || echo "No changes to commit"
|
||||
git push origin HEAD || true
|
||||
|
||||
# Tag the api/apps/v1alpha1 submodule for pkg.go.dev
|
||||
- name: Tag API submodule
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
VTAG="${{ steps.tag.outputs.tag }}"
|
||||
SUBTAG="api/apps/v1alpha1/${VTAG}"
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
TARGET="$(git rev-parse "${VTAG}^{}")"
|
||||
if git rev-parse -q --verify "refs/tags/${SUBTAG}" >/dev/null; then
|
||||
test "$(git rev-list -n1 "${SUBTAG}")" = "$TARGET"
|
||||
else
|
||||
git tag "${SUBTAG}" "$TARGET"
|
||||
fi
|
||||
git push origin "${SUBTAG}"
|
||||
git tag -f "${SUBTAG}" "$TARGET"
|
||||
git push -f origin "refs/tags/${SUBTAG}"
|
||||
|
||||
# Create or reuse draft release
|
||||
- name: Create / reuse draft release
|
||||
|
|
@ -188,11 +191,11 @@ jobs:
|
|||
- name: Create release branch
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
BRANCH="release-${GITHUB_REF#refs/tags/v}"
|
||||
git branch -f "$BRANCH"
|
||||
git push -f origin "$BRANCH"
|
||||
|
|
@ -202,7 +205,7 @@ jobs:
|
|||
if: steps.check_release.outputs.skip == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const version = context.ref.replace('refs/tags/v', '');
|
||||
const base = '${{ steps.get_base.outputs.branch }}';
|
||||
|
|
@ -244,6 +247,14 @@ jobs:
|
|||
pull-requests: write
|
||||
if: needs.prepare-release.result == 'success'
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
- name: Parse tag
|
||||
id: tag
|
||||
uses: actions/github-script@v7
|
||||
|
|
@ -266,7 +277,7 @@ jobs:
|
|||
ref: main
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Check if changelog already exists
|
||||
id: check_changelog
|
||||
|
|
@ -294,7 +305,7 @@ jobs:
|
|||
if: steps.check_changelog.outputs.exists == 'false'
|
||||
env:
|
||||
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
copilot --prompt "prepare changelog file for tagged release v${{ steps.tag.outputs.version }}, use @docs/agents/changelog.md for it. Create the changelog file at docs/changelogs/v${{ steps.tag.outputs.version }}.md" \
|
||||
--allow-all-tools --allow-all-paths < /dev/null
|
||||
|
|
@ -302,11 +313,11 @@ jobs:
|
|||
- name: Create changelog branch and commit
|
||||
if: steps.check_changelog.outputs.exists == 'false'
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
APP_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY}
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}
|
||||
|
||||
CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md"
|
||||
CHANGELOG_BRANCH="changelog-v${{ steps.tag.outputs.version }}"
|
||||
|
|
@ -341,7 +352,7 @@ jobs:
|
|||
if: steps.check_changelog.outputs.exists == 'false'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
github-token: ${{ steps.app-token.outputs.token }}
|
||||
script: |
|
||||
const version = '${{ steps.tag.outputs.version }}';
|
||||
const changelogBranch = `changelog-v${version}`;
|
||||
|
|
@ -400,6 +411,14 @@ jobs:
|
|||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.COZYSTACK_CI_APP_ID }}
|
||||
private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }}
|
||||
owner: cozystack
|
||||
|
||||
- name: Parse tag
|
||||
id: tag
|
||||
uses: actions/github-script@v7
|
||||
|
|
@ -419,19 +438,19 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: cozystack/website
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
ref: main
|
||||
|
||||
- name: Update docs from release branch
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: make update-all BRANCH=release-${{ steps.tag.outputs.version }} RELEASE_TAG=${{ steps.tag.outputs.tag }}
|
||||
|
||||
- name: Commit and push
|
||||
id: commit
|
||||
run: |
|
||||
git config user.name "cozystack-bot"
|
||||
git config user.email "217169706+cozystack-bot@users.noreply.github.com"
|
||||
git config user.name "cozystack-ci[bot]"
|
||||
git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"
|
||||
git add content
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
|
|
@ -448,7 +467,7 @@ jobs:
|
|||
- name: Open pull request
|
||||
if: steps.commit.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
BRANCH="update-docs-v${{ steps.tag.outputs.version }}"
|
||||
pr_state=$(gh pr view "$BRANCH" --repo cozystack/website --json state --jq .state 2>/dev/null || echo "")
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -27,6 +27,7 @@ build: build-deps
|
|||
make -C packages/system/dashboard image
|
||||
make -C packages/system/metallb image
|
||||
make -C packages/system/kamaji image
|
||||
make -C packages/system/multus image
|
||||
make -C packages/system/bucket image
|
||||
make -C packages/system/objectstorage-controller image
|
||||
make -C packages/system/grafana-operator image
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ else
|
|||
endif
|
||||
|
||||
REGISTRY ?= ghcr.io/cozystack/cozystack
|
||||
TAG = $(shell git describe --tags --exact-match 2>/dev/null || echo latest)
|
||||
TAG = $(shell git describe --tags --exact-match --match 'v*' 2>/dev/null || echo latest)
|
||||
PUSH := 1
|
||||
LOAD := 0
|
||||
BUILDER ?=
|
||||
PLATFORM ?=
|
||||
BUILDX_EXTRA_ARGS ?=
|
||||
COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags))
|
||||
COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*'))
|
||||
|
||||
BUILDX_ARGS := --provenance=false --push=$(PUSH) --load=$(LOAD) \
|
||||
--label org.opencontainers.image.source=https://github.com/cozystack/cozystack \
|
||||
|
|
@ -28,6 +28,6 @@ endef
|
|||
ifeq ($(COZYSTACK_VERSION),)
|
||||
$(shell git remote add upstream https://github.com/cozystack/cozystack.git || true)
|
||||
$(shell git fetch upstream --tags)
|
||||
COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags))
|
||||
COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*'))
|
||||
endif
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ metadata:
|
|||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $ingress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
spec:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:2f987017ff95d3c782e16ef0d99928831f520daf154d08a2fa38c2d68363e036
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:d397781152ab9123b11b8191d92eba7a0d2faa376aa2c15ddeb67842a9b59bab
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:5b209248acba3d8cf0d999edd8a915915a2e565a1c8ac1f5dde0dffec20fa02e
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e70f4391b344c71a9567e1ed22ca24910116a619cb681381059b8c39f382219
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:51e272db23d64eb20bc44e38cdbb70199fd00a49a958b76c2a89d4a46fec9256
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:f460ed1fd5a721aed423e6c3b8be0105d7f693cd86ad992d9c15fd4b27e58cec
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:31c3290305159fe484caf6c5780960cf071dc3939528295336da4abfa731a81b
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:aaa2dfa8ee53ae26295f44d2491330f51412457bbe4aa6ba256297cc0bd8a0da
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:9efb1fe18cffb045530b43ac4b39910f1802d3c16798d733165dcd1c67de9237
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:7acb4728ef6b9c0ff3344bf486bb00f2a083f2098452344a362242235013db01
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a0afca1d6f720cfff764a1bc912bfe404b5e97ff731f6c126a7abdb51ed23f85
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a2024339ab9edb980a96b43ad2744b7aa31afaef0983ddbc6a546068b4cfa87a
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:51dfe5ce3e2f8765fde9422ae7e9fba677016ec5e2be41559c5f11a56958b0e6
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:97bcf946a5687889c6a421d14e5adece11f0978151b2165dc87b28f77f7607db
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:c4ae418b2a2c139794cd9630c3b2c2171870cc7748ac200344d2c4ed4283cf0b
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:364c6d454891f1eb1a598fddb69cf328a14dbc451a8ac65812b038a7756da60a
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ spec:
|
|||
memory:
|
||||
guest: {{ .group.resources.memory }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- include "cozy-lib.resources.sanitize" (list (dict "ephemeral-storage" .group.ephemeralStorage) $) | nindent 14 }}
|
||||
evictionStrategy: External
|
||||
volumes:
|
||||
- name: system
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:3841eb171416711977dea0cf8cd45d32344caac9727af760c37d5e1dd41ee4bb
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ metadata:
|
|||
apps.cozystack.io/user-service: "true"
|
||||
{{- include "virtual-machine.labels" . | nindent 4 }}
|
||||
{{- if .Values.external }}
|
||||
service.kubernetes.io/service-proxy-name: "cozy-proxy"
|
||||
annotations:
|
||||
networking.cozystack.io/wholeIP: "true"
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
cozystackOperator:
|
||||
# Deployment variant: talos, generic, hosted
|
||||
variant: talos
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.0@sha256:dbc0cbd99dbc2405769bed0d33d5e5d3f53e79e0269ad9ddaa26d9eebbcc6da2
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.4@sha256:1cd36a4643ecf4f06782f050ff9544b316ea52af67d738bf593f40e60c4acbec
|
||||
platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages'
|
||||
platformSourceRef: 'digest=sha256:c659905000e8279d32965169cde0817e16a5b46f81d305a3606defcd5446abae'
|
||||
platformSourceRef: 'digest=sha256:98d595ded030ee0b71ff6570419b81ae6cd1bcc8fb7a31c5e29556fbdfa5ec4a'
|
||||
# Generic variant configuration (only used when cozystackOperator.variant=generic)
|
||||
cozystack:
|
||||
# Kubernetes API server host (IP only, no protocol/port)
|
||||
|
|
|
|||
78
packages/core/platform/images/migrations/migrations/37
Executable file
78
packages/core/platform/images/migrations/migrations/37
Executable file
|
|
@ -0,0 +1,78 @@
|
|||
#!/bin/bash
|
||||
# Migration 37 --> 38
|
||||
# Pin PostgreSQL image to 17.7-standard-trixie for system databases.
|
||||
#
|
||||
# This migration updates the imageName for all CNPG clusters belonging to
|
||||
# system components (keycloak-db, grafana-db, alerta-db, seaweedfs-db, and
|
||||
# harbor's dynamically-named DB):
|
||||
# - If imageName is not set: pins to 17.7-standard-trixie
|
||||
# - If imageName has any PG 17 tag: forces 17.7-standard-trixie
|
||||
# - If imageName has a bare version tag (e.g. :18.1): appends -standard-trixie
|
||||
#
|
||||
# NOTE: This migration ONLY updates system CNPG Cluster resources (clusters.postgresql.cnpg.io),
|
||||
# NOT user-created Postgres applications.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_IMAGE="ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie"
|
||||
|
||||
# Static system database names
|
||||
STATIC_DB_NAMES="keycloak-db grafana-db alerta-db seaweedfs-db"
|
||||
|
||||
echo "=== Updating PostgreSQL images for system databases ==="
|
||||
echo "Target image: $TARGET_IMAGE"
|
||||
|
||||
# Fetch all CNPG clusters with their name, namespace, imageName, and Helm release annotation in one call
|
||||
ALL_CLUSTERS=$(kubectl get clusters.postgresql.cnpg.io -A \
|
||||
-o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.imageName}{"\t"}{.metadata.annotations.meta\.helm\.sh/release-name}{"\n"}{end}' 2>/dev/null || true)
|
||||
|
||||
while IFS=$'\t' read -r NAMESPACE CLUSTER_NAME CURRENT_IMAGE HELM_RELEASE; do
|
||||
[ -z "$NAMESPACE" ] && continue
|
||||
|
||||
# Check if cluster name matches one of the static system databases
|
||||
MATCH=false
|
||||
for db_name in $STATIC_DB_NAMES; do
|
||||
if [ "$CLUSTER_NAME" = "$db_name" ]; then
|
||||
MATCH=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# For harbor: match clusters ending with -db whose Helm release ends with -system
|
||||
if [ "$MATCH" = false ] && [[ "$CLUSTER_NAME" == *-db ]] && [[ "$HELM_RELEASE" == *-system ]]; then
|
||||
MATCH=true
|
||||
fi
|
||||
|
||||
if [ "$MATCH" = false ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
PATCH_IMAGE=""
|
||||
|
||||
if [ -z "$CURRENT_IMAGE" ]; then
|
||||
# imageName not set — pin to target
|
||||
PATCH_IMAGE="$TARGET_IMAGE"
|
||||
echo "PATCH $NAMESPACE/$CLUSTER_NAME: imageName not set, setting to $PATCH_IMAGE"
|
||||
elif [[ "$CURRENT_IMAGE" =~ :17\. ]]; then
|
||||
# Any PG 17 image — force to the pinned 17.7-standard-trixie
|
||||
PATCH_IMAGE="$TARGET_IMAGE"
|
||||
echo "PATCH $NAMESPACE/$CLUSTER_NAME: PG 17 detected, $CURRENT_IMAGE -> $PATCH_IMAGE"
|
||||
elif [[ "$CURRENT_IMAGE" =~ :[0-9]+\.[0-9]+$ ]]; then
|
||||
# Bare version tag for other majors (e.g. :18.1) — append -standard-trixie suffix
|
||||
PATCH_IMAGE="${CURRENT_IMAGE}-standard-trixie"
|
||||
echo "PATCH $NAMESPACE/$CLUSTER_NAME: bare tag detected, $CURRENT_IMAGE -> $PATCH_IMAGE"
|
||||
else
|
||||
echo "SKIP $NAMESPACE/$CLUSTER_NAME: imageName already set to $CURRENT_IMAGE"
|
||||
continue
|
||||
fi
|
||||
|
||||
kubectl patch clusters.postgresql.cnpg.io -n "$NAMESPACE" "$CLUSTER_NAME" \
|
||||
--type=merge \
|
||||
--patch "{\"spec\":{\"imageName\":\"${PATCH_IMAGE}\"}}"
|
||||
done <<< "$ALL_CLUSTERS"
|
||||
|
||||
echo "=== PostgreSQL image update completed ==="
|
||||
|
||||
# Stamp version
|
||||
kubectl create configmap -n cozy-system cozystack-version \
|
||||
--from-literal=version=38 --dry-run=client -o yaml | kubectl apply -f-
|
||||
|
|
@ -66,6 +66,7 @@ spec:
|
|||
path: system/cilium
|
||||
valuesFiles:
|
||||
- values.yaml
|
||||
- values-apparmor.yaml
|
||||
install:
|
||||
privileged: true
|
||||
namespace: cozy-cilium
|
||||
|
|
@ -117,6 +118,7 @@ spec:
|
|||
path: system/cilium
|
||||
valuesFiles:
|
||||
- values.yaml
|
||||
- values-apparmor.yaml
|
||||
- values-kubeovn.yaml
|
||||
install:
|
||||
privileged: true
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ apiVersion: cozystack.io/v1alpha1
|
|||
kind: Package
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
spec:
|
||||
variant: {{ $variant }}
|
||||
{{- if $components }}
|
||||
|
|
@ -40,6 +42,8 @@ apiVersion: cozystack.io/v1alpha1
|
|||
kind: Package
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
spec:
|
||||
variant: {{ $variant }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ stringData:
|
|||
branding:
|
||||
{{- . | toYaml | nindent 8 }}
|
||||
{{- end }}
|
||||
cpu-allocation-ratio: {{ .Values.resources.cpuAllocationRatio | quote }}
|
||||
memory-allocation-ratio: {{ .Values.resources.memoryAllocationRatio | quote }}
|
||||
ephemeral-storage-allocation-ratio: {{ .Values.resources.ephemeralStorageAllocationRatio | quote }}
|
||||
{{- with .Values.scheduling }}
|
||||
scheduling:
|
||||
{{- . | toYaml | nindent 8 }}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
{{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-full-generic'" }}
|
||||
{{- end }}
|
||||
{{- if and .Values.bundles.iaas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic")) }}
|
||||
{{include "cozystack.platform.package.default" (list "cozystack.kubevirt" $) }}
|
||||
{{- $kubevirtComponents := dict -}}
|
||||
{{- if .Values.resources.cpuAllocationRatio -}}
|
||||
{{- $_ := set $kubevirtComponents "kubevirt" (dict "values" (dict "cpuAllocationRatio" (.Values.resources.cpuAllocationRatio | int))) -}}
|
||||
{{- end -}}
|
||||
{{include "cozystack.platform.package" (list "cozystack.kubevirt" "default" $ $kubevirtComponents) }}
|
||||
{{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }}
|
||||
{{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }}
|
||||
{{include "cozystack.platform.package.default" (list "cozystack.kamaji" $) }}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ sourceRef:
|
|||
path: /
|
||||
migrations:
|
||||
enabled: false
|
||||
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.0@sha256:3a3d8cfa4323d8023b7c7800d8b552b250bc2de01ca7550fd024a10b12324f6d
|
||||
targetVersion: 36
|
||||
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.4@sha256:72af209576eb42793dbf241c024d713fd23cc1435673dc2f9a81f70c237df124
|
||||
targetVersion: 38
|
||||
# Bundle deployment configuration
|
||||
bundles:
|
||||
system:
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
e2e:
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.0@sha256:7964a3e4b11053887be201d62f94138c3a7836c861036c26fb833aa1fcb934e1
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.4@sha256:0367a03b981df2a3ea13f411d4cb7869c2bf2c89c07d3d5c8971b9a28921ccef
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/matchbox:v1.2.0@sha256:313f36db2e8b9dea0cd1a80d73f27a0694e996631d430bbd1b8391fbb9859020
|
||||
ghcr.io/cozystack/cozystack/matchbox:v1.2.4@sha256:a7bfd50d2b18eaf644c5b84e95bb115b07479feed8fe2664ecf2810766dc322b
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ metadata:
|
|||
app: bootbox
|
||||
annotations:
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $ingress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
{{- if .Values.whitelistHTTP }}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.0@sha256:b508ca451c6051b64e71cedd6209c80ac0e0bea8aa6bb47e838ec9d99bf4796a
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.4@sha256:cc1f60ef2cb40ab61c4787e234f83e57a7b8aca0ffe7b17f4c4fa6e3de1669f4
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ spec:
|
|||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $ingress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
tls:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
backupController:
|
||||
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.0@sha256:07bcd89bd725951801870d819f0fc536705069608f0fe5c20617607af6f8a61a"
|
||||
image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.4@sha256:318bd6986190142b1a46de2df63975931d6669c138831c9da51a324439097689"
|
||||
replicas: 2
|
||||
debug: false
|
||||
metrics:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
backupStrategyController:
|
||||
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.0@sha256:0039ec58f531faf5a5ba3f70993c2c71d74dae4cc8e8e83bd21d2cc2adf10d8a"
|
||||
image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.4@sha256:e620387a891d7834d77d09d7106f7ab7d4c0dc9f288cafb484b6bfe1a7c47f28"
|
||||
replicas: 2
|
||||
debug: false
|
||||
metrics:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:73382d7911274e6528cc2272ca9bfdb095c59e78845a3cdffd39d6c8ac29d920
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:be364c6ebc27b8dc51f41965ed56523a6a03bb66e95d25cc5b6aaaf4e203bde4
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ metadata:
|
|||
nginx.ingress.kubernetes.io/proxy-read-timeout: "99999"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "99999"
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $ingress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
spec:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{{- $solver := (index .Values._cluster "solver") | default "http01" }}
|
||||
{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }}
|
||||
|
||||
apiVersion: cert-manager.io/v1
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
|
|
@ -17,9 +18,9 @@ spec:
|
|||
name: cloudflare-api-token-secret
|
||||
key: api-token
|
||||
{{- else }}
|
||||
http01:
|
||||
ingress:
|
||||
class: nginx
|
||||
http01:
|
||||
ingress:
|
||||
ingressClassName: {{ $exposeIngress }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
|
@ -41,9 +42,9 @@ spec:
|
|||
name: cloudflare-api-token-secret
|
||||
key: api-token
|
||||
{{- else }}
|
||||
http01:
|
||||
ingress:
|
||||
class: nginx
|
||||
http01:
|
||||
ingress:
|
||||
ingressClassName: {{ $exposeIngress }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -12,6 +12,15 @@ update:
|
|||
helm repo update cilium
|
||||
helm pull cilium/cilium --untar --untardir charts --version 1.19
|
||||
$(SED_INPLACE) -e '/Used in iptables/d' -e '/SYS_MODULE/d' charts/cilium/values.yaml
|
||||
# Drop the whole k8s<1.30 AppArmor annotations block from the cilium-agent
|
||||
# DaemonSet. Cozystack manages these annotations through cilium.podAnnotations
|
||||
# in values.yaml on every k8s version, so keeping the upstream block would
|
||||
# produce duplicate mapping keys on k8s<1.30.
|
||||
perl -i -0pe 's|\n \{\{- if not \.Values\.securityContext\.privileged \}\}\n \{\{- if semverCompare "<1\.30\.0".*?\n \{\{- end \}\}\n \{\{- end \}\}\n \{\{- end \}\}||s' \
|
||||
charts/cilium/templates/cilium-agent/daemonset.yaml
|
||||
@! grep -q 'container\.apparmor\.security\.beta\.kubernetes\.io' \
|
||||
charts/cilium/templates/cilium-agent/daemonset.yaml || \
|
||||
{ echo 'ERROR: perl patch did not remove the upstream AppArmor block from cilium-agent/daemonset.yaml (upstream template format may have changed)' >&2; exit 1; }
|
||||
version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \
|
||||
$(SED_INPLACE) "s/ARG VERSION=.*/ARG VERSION=v$${version}/" images/cilium/Dockerfile
|
||||
|
||||
|
|
|
|||
|
|
@ -57,19 +57,6 @@ spec:
|
|||
# ensure pods roll when configmap updates
|
||||
cilium.io/cilium-configmap-checksum: {{ include (print $.Template.BasePath "/cilium-configmap.yaml") . | sha256sum | quote }}
|
||||
{{- end }}
|
||||
{{- if not .Values.securityContext.privileged }}
|
||||
{{- if semverCompare "<1.30.0" (printf "%d.%d.0" (semver .Capabilities.KubeVersion.Version).Major (semver .Capabilities.KubeVersion.Version).Minor) }}
|
||||
# Set app AppArmor's profile to "unconfined". The value of this annotation
|
||||
# can be modified as long users know which profiles they have available
|
||||
# in AppArmor.
|
||||
container.apparmor.security.beta.kubernetes.io/cilium-agent: "unconfined"
|
||||
container.apparmor.security.beta.kubernetes.io/clean-cilium-state: "unconfined"
|
||||
{{- if .Values.cgroup.autoMount.enabled }}
|
||||
container.apparmor.security.beta.kubernetes.io/mount-cgroup: "unconfined"
|
||||
container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: "unconfined"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
|
|||
20
packages/system/cilium/values-apparmor.yaml
Normal file
20
packages/system/cilium/values-apparmor.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
cilium:
|
||||
# Opt out of the cri-containerd.apparmor.d profile for containers that invoke
|
||||
# nsenter to join the host cgroup/mount namespace. The kernel reports the
|
||||
# denial as a blocked "ptrace" operation. Required on Ubuntu 22.04+ and
|
||||
# other distros that load this AppArmor profile by default, where the
|
||||
# denial otherwise puts cilium-agent into Init:CrashLoopBackOff.
|
||||
# The deprecated annotations are used because k8s >= 1.30 pod-level
|
||||
# appArmorProfile: Unconfined is not honoured by containerd CRI today
|
||||
# (kubernetes/kubernetes#125069).
|
||||
# Only applied on non-Talos cilium variants where cgroup.autoMount.enabled is
|
||||
# true — on Talos mount-cgroup is not rendered and the kube-apiserver would
|
||||
# reject an annotation referencing a non-existent container.
|
||||
# mount-bpf-fs is intentionally excluded: it renders with its own
|
||||
# securityContext.privileged=true, and the kernel does not apply AppArmor
|
||||
# profiles to privileged containers.
|
||||
podAnnotations:
|
||||
container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined
|
||||
container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined
|
||||
container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined
|
||||
container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
cozystackAPI:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.0@sha256:b40ba5c87bb625d646dd69075d21dcc1aca18775b3de8e63d04466d60d42cee5
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.4@sha256:daf48b09d79df112953a43d6cfdb10a4440dd63779ecbfa62436805076dcf977
|
||||
replicas: 2
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
cozystackController:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.0@sha256:f56c8222411fed890331502939c8d4c5700489e697da39e8e46896cf674cce71
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.4@sha256:e2472fd4f12efd3a7763f76b276e5a5f465031fa8bb113df585ce4b8ebede717
|
||||
debug: false
|
||||
disableTelemetry: false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{{- $brandingConfig := .Values._cluster.branding | default dict }}
|
||||
|
||||
{{- $tenantText := "v1.2.0" }}
|
||||
{{- $tenantText := "v1.2.4" }}
|
||||
{{- $footerText := "Cozystack" }}
|
||||
{{- $titleText := "Cozystack Dashboard" }}
|
||||
{{- $logoText := "" }}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ metadata:
|
|||
annotations:
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $exposeIngress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $exposeIngress }}
|
||||
{{- end }}
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/client-max-body-size: 100m
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
openapiUI:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.0@sha256:bf68676a809be8b37ae3ab7416b2ae3185a4efa216b8b09358d916df081d0a3e
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.4@sha256:ee273aaacc6fcb58c180cbdc58c047fd85068724c6478ea0cdfe7b8396877078
|
||||
openapiUIK8sBff:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.0@sha256:89b5c62df0ed9a1984023ba909765e4b60af3f9a61e42ebdd67437d71ebbb645
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.4@sha256:7dd82ebe1d0c1925bbc440bc65a86852f3dd2bc0f6f12856904df847f15913d3
|
||||
tokenProxy:
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.0@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.4@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.0@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204
|
||||
ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.4@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ metadata:
|
|||
name: {{ .Values.harbor.fullnameOverride }}-db
|
||||
spec:
|
||||
instances: {{ .Values.db.replicas }}
|
||||
{{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace (printf "%s-db" .Values.harbor.fullnameOverride) }}
|
||||
{{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }}
|
||||
imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }}
|
||||
storage:
|
||||
size: {{ .Values.db.size }}
|
||||
{{- with .Values.db.storageClass }}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ kamaji:
|
|||
deploy: false
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: v1.2.0@sha256:071f5f893f16918e6f03c3cf42315a3bce1c7624f10c155f3b3e6dfcd4d60c2a
|
||||
tag: v1.2.4@sha256:441ead7178dd429a16cebe506a9df489a3cd38c8fb8255437f6927b2dd0a7a04
|
||||
repository: ghcr.io/cozystack/cozystack/kamaji
|
||||
resources:
|
||||
limits:
|
||||
|
|
@ -13,4 +13,4 @@ kamaji:
|
|||
cpu: 100m
|
||||
memory: 100Mi
|
||||
extraArgs:
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.0@sha256:071f5f893f16918e6f03c3cf42315a3bce1c7624f10c155f3b3e6dfcd4d60c2a
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.4@sha256:441ead7178dd429a16cebe506a9df489a3cd38c8fb8255437f6927b2dd0a7a04
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ metadata:
|
|||
name: keycloak-db
|
||||
spec:
|
||||
instances: 2
|
||||
{{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "keycloak-db" }}
|
||||
{{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }}
|
||||
imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }}
|
||||
storage:
|
||||
size: 20Gi
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ metadata:
|
|||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $exposeIngress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $exposeIngress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.0@sha256:39f9f0e6ec42afdd91082f7c025699425096244bb10f4e2f7a1d17155ac0cebb
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.4@sha256:174768084b30b070aaa411fd55f461439449a4e5fe0e31298986066c4fb2e104
|
||||
ovnCentralName: ovn-central
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.0@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.4@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
storageClass: replicated
|
||||
csiDriver:
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:5b209248acba3d8cf0d999edd8a915915a2e565a1c8ac1f5dde0dffec20fa02e
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e70f4391b344c71a9567e1ed22ca24910116a619cb681381059b8c39f382219
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
lineageControllerWebhook:
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.0@sha256:fdbd4ed09c2e825880e84b8668d8bb6e0795da5c51d7c20b9d5eb4abe31d3ae0
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.4@sha256:b7bd8300a06c87ba4f5815e240e6f9843d80baaddd1f549004ddbfe9858e9310
|
||||
debug: false
|
||||
localK8sAPIEndpoint:
|
||||
enabled: true
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export NAMESPACE=cozy-$(NAME)
|
|||
include ../../../hack/common-envs.mk
|
||||
include ../../../hack/package.mk
|
||||
|
||||
LINSTOR_VERSION ?= 1.32.3
|
||||
LINSTOR_VERSION ?= 1.33.2
|
||||
LINSTOR_CSI_VERSION ?= v1.10.5
|
||||
|
||||
image: image-piraeus-server image-linstor-csi
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ RUN test -d .gradlehome && echo ".gradlehome found in tarball" || (echo ".gradle
|
|||
# Build DEB packages from tarball
|
||||
# Override GRADLE_FLAGS to remove --offline flag, allowing Gradle to download missing dependencies
|
||||
RUN sed -i 's/GRADLE_FLAGS = --offline/GRADLE_FLAGS =/' debian/rules || true
|
||||
# Skip dh_strip_nondeterminism to avoid failures on some JAR files (logback-core)
|
||||
RUN printf '\noverride_dh_strip_nondeterminism:\n\ttrue\n' >> debian/rules
|
||||
RUN LD_LIBRARY_PATH='' dpkg-buildpackage -rfakeroot -b -uc
|
||||
|
||||
# Copy built .deb packages to a location accessible from final image
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
# LINSTOR Server Patches
|
||||
|
||||
Custom patches for piraeus-server (linstor-server) v1.32.3.
|
||||
Custom patches for piraeus-server (linstor-server) v1.33.2.
|
||||
|
||||
- **adjust-on-resfile-change.diff** — Use actual device path in res file during toggle-disk; fix LUKS data offset
|
||||
- Upstream: [#473](https://github.com/LINBIT/linstor-server/pull/473), [#472](https://github.com/LINBIT/linstor-server/pull/472)
|
||||
- **allow-toggle-disk-retry.diff** — Allow retry and cancellation of failed toggle-disk operations
|
||||
- Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475)
|
||||
- **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful
|
||||
- Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474)
|
||||
- **fix-duplicate-tcp-ports.diff** — Prevent duplicate TCP ports after toggle-disk operations
|
||||
- Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476)
|
||||
- **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion
|
||||
- Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477)
|
||||
- **allow-toggle-disk-retry.diff** — Backport maintainer implementation of toggle-disk retry/abort
|
||||
- Source PR/comment: [#475](https://github.com/LINBIT/linstor-server/pull/475), [maintainer note](https://github.com/LINBIT/linstor-server/pull/475#issuecomment-3949630419)
|
||||
- Backported from upstream commit: [`3d97f71c9`](https://github.com/LINBIT/linstor-server/commit/3d97f71c95a493588d3d521c63eac4d846935fb3)
|
||||
- **fix-duplicate-tcp-ports.diff** — Preserve DRBD TCP ports during toggle-disk and avoid redundant `ensureStackDataExists()`
|
||||
- Source PR/review: [#476](https://github.com/LINBIT/linstor-server/pull/476), [review suggestion](https://github.com/LINBIT/linstor-server/pull/476#discussion_r3007725079)
|
||||
- Backported from commits: [`79d6375c5`](https://github.com/kvaps/linstor-server/commit/79d6375c55d6181b35a7b7f0fe8dbdfb86e126cd), [`bcc89902f`](https://github.com/kvaps/linstor-server/commit/bcc89902f4f61ac1589dd07ebb7f5aae1935370d)
|
||||
- **fix-luks-header-size.diff** — Account for LUKS2 `--offset`, metadata/keyslots sizing, and device `optimal_io_size`
|
||||
- Source PR/comment: [#472](https://github.com/LINBIT/linstor-server/pull/472), [maintainer note](https://github.com/LINBIT/linstor-server/pull/472#issuecomment-3949687603)
|
||||
- Backported from commits: [`ccc85fbd2`](https://github.com/LINBIT/linstor-server/commit/ccc85fbd2c65f0b97c52403fa80f1efdb886ec4e), [`71b601554`](https://github.com/LINBIT/linstor-server/commit/71b601554f41bcb50cd5bd06989c5b0d3a814acd)
|
||||
- Note: upstream commit [`3d0402a0c`](https://github.com/LINBIT/linstor-server/commit/3d0402a0c25f0a4b57b380321f10e89982f26e7a) is already included in `v1.33.1`
|
||||
- **retry-adjust-after-stale-bitmap.diff** — Retry `drbdadm adjust` after detaching a stale local bitmap state
|
||||
- Source PR: [#491](https://github.com/LINBIT/linstor-server/pull/491)
|
||||
- Backported from commit: [`51ae50a84`](https://github.com/kvaps/linstor-server/commit/51ae50a84dcb98093f543b819652c750a94d96c9)
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
index 36c52ccf8..c0bb7b967 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
@@ -894,12 +894,16 @@ public class ConfFileBuilder
|
||||
if (((Volume) vlmData.getVolume()).getFlags().isUnset(localAccCtx, Volume.Flags.DELETE))
|
||||
{
|
||||
final String disk;
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAdding = vlmData.getVolume().getAbsResource().getStateFlags().isSomeSet(
|
||||
+ localAccCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
if ((!isPeerRsc && vlmData.getDataDevice() == null) ||
|
||||
(isPeerRsc &&
|
||||
// FIXME: vlmData.getRscLayerObject().getFlags should be used here
|
||||
vlmData.getVolume().getAbsResource().disklessForDrbdPeers(accCtx)
|
||||
) ||
|
||||
- (!isPeerRsc &&
|
||||
+ // For toggle-disk: if dataDevice is set and we're adding disk, use the actual device
|
||||
+ (!isPeerRsc && !isDiskAdding &&
|
||||
// FIXME: vlmData.getRscLayerObject().getFlags should be used here
|
||||
vlmData.getVolume().getAbsResource().isDrbdDiskless(accCtx)
|
||||
)
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
index 54dd5c19f..018de58cf 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
@@ -34,6 +34,9 @@ public class CryptSetupCommands implements Luks
|
||||
private static final Version V2_1_0 = new Version(2, 1, 0);
|
||||
private static final Version V2_0_0 = new Version(2, 0, 0);
|
||||
private static final String PBDKF_MAX_MEMORY_KIB = "262144"; // 256 MiB
|
||||
+ // Fixed LUKS2 data offset in 512-byte sectors (16 MiB = 32768 sectors)
|
||||
+ // This ensures consistent LUKS header size across all nodes regardless of system defaults
|
||||
+ private static final String LUKS2_DATA_OFFSET_SECTORS = "32768";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final ErrorReporter errorReporter;
|
||||
@@ -78,6 +81,11 @@ public class CryptSetupCommands implements Luks
|
||||
command.add(CRYPTSETUP);
|
||||
command.add("-q");
|
||||
command.add("luksFormat");
|
||||
+ // Always specify explicit offset to ensure consistent LUKS header size across all nodes
|
||||
+ // Without this, different systems may create LUKS with different header sizes (16MiB vs 32MiB)
|
||||
+ // which causes "Low.dev. smaller than requested DRBD-dev. size" errors during toggle-disk
|
||||
+ command.add("--offset");
|
||||
+ command.add(LUKS2_DATA_OFFSET_SECTORS);
|
||||
if (version.greaterOrEqual(V2_0_0))
|
||||
{
|
||||
command.add("--pbkdf-memory");
|
||||
|
|
@ -1,168 +1,232 @@
|
|||
diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
index d93a18014..cc8ce4f04 100644
|
||||
index d93a18014..a944cb809 100644
|
||||
--- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
+++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
@@ -57,7 +57,9 @@ import com.linbit.linstor.stateflags.StateFlags;
|
||||
import com.linbit.linstor.storage.StorageException;
|
||||
import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData;
|
||||
import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject;
|
||||
+import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject;
|
||||
import com.linbit.linstor.storage.kinds.DeviceLayerKind;
|
||||
+import com.linbit.linstor.storage.kinds.DeviceProviderKind;
|
||||
import com.linbit.linstor.storage.utils.LayerUtils;
|
||||
import com.linbit.linstor.tasks.AutoDiskfulTask;
|
||||
import com.linbit.linstor.utils.layer.LayerRscUtils;
|
||||
@@ -387,21 +389,84 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
@@ -111,6 +111,14 @@ import reactor.util.function.Tuple2;
|
||||
@Singleton
|
||||
public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionListener
|
||||
{
|
||||
+ private enum ToggleDiskAction
|
||||
+ {
|
||||
+ NOOP,
|
||||
+ NORMAL,
|
||||
+ ABORT,
|
||||
+ RETRY
|
||||
+ }
|
||||
+
|
||||
private final AccessContext apiCtx;
|
||||
private final ScopeRunner scopeRunner;
|
||||
private final BackgroundRunner backgroundRunner;
|
||||
@@ -386,69 +394,33 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
"Toggle Disk on %s/%s %s", nodeNameStr, rscNameStr, removeDisk ? "removing disk" : "adding disk");
|
||||
|
||||
Resource rsc = ctrlApiDataLoader.loadRsc(nodeName, rscName, true);
|
||||
+ ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
|
||||
+ // Allow retry of the same operation if the previous attempt failed
|
||||
+ // (the requested flag remains set for retry on reconnection, but we should also allow manual retry)
|
||||
+ // Also allow cancellation of a failed operation by requesting the opposite operation
|
||||
if (hasDiskAddRequested(rsc))
|
||||
{
|
||||
- if (hasDiskAddRequested(rsc))
|
||||
- {
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_RSC_BUSY,
|
||||
- "Addition of disk to resource already requested",
|
||||
- true
|
||||
- ));
|
||||
+ if (removeDisk)
|
||||
+ {
|
||||
+ // User wants to cancel the failed add-disk operation and go back to diskless
|
||||
+ // Use the existing disk removal flow to properly cleanup storage on satellite
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_ADD_REQUESTED, reverting to diskless",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ unmarkDiskAddRequested(rsc);
|
||||
+ // Also clear DISK_ADDING if it was set
|
||||
+ unmarkDiskAdding(rsc);
|
||||
+
|
||||
+ // Set storage pool to diskless pool (overwrite the diskful pool that was set)
|
||||
+ Props rscProps = ctrlPropsHelper.getProps(rsc);
|
||||
+ rscProps.map().put(ApiConsts.KEY_STOR_POOL_NAME, LinStor.DISKLESS_STOR_POOL_NAME);
|
||||
+
|
||||
+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow
|
||||
+ // This will:
|
||||
+ // 1. updateAndAdjustDisk sets DISK_REMOVING flag
|
||||
+ // 2. Satellite sees DISK_REMOVING and deletes LUKS/storage devices
|
||||
+ // 3. finishOperation rebuilds layer stack as diskless
|
||||
+ // We keep the existing layer data so satellite can properly cleanup
|
||||
+ markDiskRemoveRequested(rsc);
|
||||
+
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+
|
||||
+ // Use existing disk removal flow - this will properly cleanup storage on satellite
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cancelling disk addition, reverting to diskless"
|
||||
+ ))
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
+ }
|
||||
+ // If adding disk and DISK_ADD_REQUESTED is already set, treat as retry
|
||||
+ // Simply retry the operation with existing layer data - satellite will handle it idempotently
|
||||
+ // NOTE: We don't remove/recreate layer data here because removeLayerData() only deletes
|
||||
+ // from controller DB without calling drbdadm down on satellite, leaving orphaned DRBD devices
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk retry on %s/%s - DISK_ADD_REQUESTED already set, retrying operation",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(new ApiCallRcImpl())
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, false, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
}
|
||||
if (hasDiskRemoveRequested(rsc))
|
||||
{
|
||||
- }
|
||||
- if (hasDiskRemoveRequested(rsc))
|
||||
- {
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_RSC_BUSY,
|
||||
- "Removal of disk from resource already requested",
|
||||
- true
|
||||
- ));
|
||||
+ if (!removeDisk)
|
||||
+ {
|
||||
+ // User wants to cancel the failed remove-disk operation
|
||||
- }
|
||||
+ ToggleDiskAction action = determineToggleDiskAction(rsc, removeDisk);
|
||||
+ errorReporter.logDebug("Toggle Disk action: %s", action);
|
||||
|
||||
- if (!removeDisk && !ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
+ switch (action)
|
||||
{
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.WARN_RSC_ALREADY_HAS_DISK,
|
||||
- "Resource already has disk",
|
||||
- true
|
||||
- ));
|
||||
- }
|
||||
- if (removeDisk && ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
- {
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.WARN_RSC_ALREADY_DISKLESS,
|
||||
- "Resource already diskless",
|
||||
- true
|
||||
- ));
|
||||
+ case NOOP:
|
||||
+ return handleNoopAction(rsc, removeDisk);
|
||||
+ case RETRY:
|
||||
+ return handleRetryAction(rsc, removeDisk, toggleIntoTiebreakerRef, context);
|
||||
+ case ABORT:
|
||||
+ clearToggleDiskFlags(rsc);
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_REMOVE_REQUESTED",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ unmarkDiskRemoveRequested(rsc);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux.<ApiCallRc>just(
|
||||
+ ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cancelled disk removal request"
|
||||
+ )
|
||||
+ "Aborting previous toggle disk transition, starting new transition to %s",
|
||||
+ removeDisk ? "diskless" : "diskful"
|
||||
+ );
|
||||
+ }
|
||||
+ // If removing disk and DISK_REMOVE_REQUESTED is already set, treat as retry
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk retry on %s/%s - DISK_REMOVE_REQUESTED already set, continuing operation",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(new ApiCallRcImpl())
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
+ break;
|
||||
+ case NORMAL:
|
||||
+ break;
|
||||
+ default:
|
||||
+ throw new ImplementationError("Unhandled case: " + action);
|
||||
}
|
||||
|
||||
if (!removeDisk && !ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
@@ -412,17 +477,43 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
true
|
||||
));
|
||||
}
|
||||
+ ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
+ AccessContext peerCtx = peerAccCtx.get();
|
||||
+
|
||||
if (removeDisk && ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
{
|
||||
+ // Resource is marked as diskless - check if it has orphaned storage layers that need cleanup
|
||||
+ AbsRscLayerObject<Resource> layerData = getLayerData(peerCtx, rsc);
|
||||
+ if (layerData != null && (LayerUtils.hasLayer(layerData, DeviceLayerKind.LUKS) ||
|
||||
+ hasNonDisklessStorageLayer(layerData)))
|
||||
+ {
|
||||
+ // Resource is marked as diskless but has orphaned storage layers - need cleanup
|
||||
+ // Use the existing disk removal flow to properly cleanup storage on satellite
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cleanup on %s/%s - resource is diskless but has orphaned storage layers, cleaning up",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+
|
||||
+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow
|
||||
+ // This will trigger proper satellite cleanup via DISK_REMOVING flag
|
||||
+ markDiskRemoveRequested(rsc);
|
||||
+
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+
|
||||
+ // Use existing disk removal flow - this will properly cleanup storage on satellite
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cleaning up orphaned storage layers"
|
||||
+ ))
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
+ }
|
||||
throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
ApiConsts.WARN_RSC_ALREADY_DISKLESS,
|
||||
"Resource already diskless",
|
||||
true
|
||||
));
|
||||
}
|
||||
-
|
||||
- ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
- AccessContext peerCtx = peerAccCtx.get();
|
||||
if (removeDisk)
|
||||
{
|
||||
// Prevent removal of the last disk
|
||||
@@ -1446,6 +1537,30 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
- if (removeDisk)
|
||||
- {
|
||||
- // Prevent removal of the last disk
|
||||
- int haveDiskCount = countDisksAndIsOnline(rscDfn);
|
||||
- if (haveDiskCount <= 1)
|
||||
- {
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_INSUFFICIENT_REPLICA_COUNT,
|
||||
- "Cannot remove the disk from the only online resource with a disk",
|
||||
- true
|
||||
- ));
|
||||
- }
|
||||
+ validateToggleDiskPreconditions(rsc, removeDisk);
|
||||
|
||||
- if (!LayerUtils.hasLayer(getLayerData(peerCtx, rsc), DeviceLayerKind.DRBD))
|
||||
- {
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_INVLD_LAYER_STACK,
|
||||
- "Toggle disk is only supported in combination with DRBD",
|
||||
- true
|
||||
- ));
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- ensureAllPeersHavePeerSlotLeft(rscDfn);
|
||||
- }
|
||||
+ AccessContext peerCtx = peerAccCtx.get();
|
||||
|
||||
// Save the requested storage pool in the resource properties.
|
||||
// This does not cause the storage pool to be used automatically.
|
||||
@@ -628,10 +600,10 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
|
||||
ctrlTransactionHelper.commit();
|
||||
|
||||
- String action = removeDisk ? "Removal of disk from" : "Addition of disk to";
|
||||
+ String actionStr = removeDisk ? "Removal of disk from" : "Addition of disk to";
|
||||
responses.addEntry(ApiCallRcImpl.simpleEntry(
|
||||
ApiConsts.MODIFIED,
|
||||
- action + " resource '" + rscDfn.getName().displayValue + "' " +
|
||||
+ actionStr + " resource '" + rscDfn.getName().displayValue + "' " +
|
||||
"on node '" + rsc.getNode().getName().displayValue + "' registered"
|
||||
));
|
||||
|
||||
@@ -651,6 +623,40 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
);
|
||||
}
|
||||
|
||||
+ private Flux<ApiCallRc> handleNoopAction(Resource rscRef, boolean removeDiskRef)
|
||||
+ {
|
||||
+ String state = removeDiskRef ? "diskless" : "diskful";
|
||||
+ ApiCallRcImpl responses = new ApiCallRcImpl();
|
||||
+ responses.addEntry(ApiCallRcImpl.simpleEntry(
|
||||
+ ApiConsts.INFO_NOOP,
|
||||
+ "Resource '" + rscRef.getResourceDefinition().getName().displayValue + "' on node '" +
|
||||
+ rscRef.getNode().getName().displayValue + "' is already " + state
|
||||
+ ));
|
||||
+ return Flux.just(responses);
|
||||
+ }
|
||||
+
|
||||
+ private Flux<ApiCallRc> handleRetryAction(
|
||||
+ Resource rscRef,
|
||||
+ boolean removeDisk,
|
||||
+ boolean toggleIntoTiebreakerRef,
|
||||
+ ResponseContext context
|
||||
+ )
|
||||
+ {
|
||||
+ String direction = removeDisk ? "diskless" : "diskful";
|
||||
+ ApiCallRcImpl responses = new ApiCallRcImpl();
|
||||
+ NodeName nodeName = rscRef.getNode().getName();
|
||||
+ ResourceName rscName = rscRef.getResourceDefinition().getName();
|
||||
+ responses.addEntry(ApiCallRcImpl.simpleEntry(
|
||||
+ ApiConsts.INFO_NOOP,
|
||||
+ "Retrying toggle disk to " + direction + " for resource '" + rscName.displayValue +
|
||||
+ "' on node '" + nodeName.displayValue + "'"
|
||||
+ ));
|
||||
+
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(responses)
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, removeDisk, toggleIntoTiebreakerRef, context));
|
||||
+ }
|
||||
+
|
||||
private long getVlmDfnSizePrivileged(VolumeDefinition vlmDfnRef)
|
||||
{
|
||||
try
|
||||
@@ -781,6 +787,96 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
}
|
||||
}
|
||||
|
||||
+ private void unmarkDiskAddRequested(Resource rsc)
|
||||
+ private ToggleDiskAction determineToggleDiskAction(Resource rsc, boolean removeDisk)
|
||||
+ {
|
||||
+ boolean isDiskless = ctrlVlmCrtApiHelper.isDiskless(rsc);
|
||||
+ boolean diskAddRequested = hasDiskAddRequested(rsc);
|
||||
+ boolean diskRemoveRequested = hasDiskRemoveRequested(rsc);
|
||||
+
|
||||
+ ToggleDiskAction action;
|
||||
+ if (isDiskless)
|
||||
+ {
|
||||
+ if (diskAddRequested)
|
||||
+ {
|
||||
+ action = removeDisk ? ToggleDiskAction.ABORT : ToggleDiskAction.RETRY;
|
||||
+ }
|
||||
+ else if (diskRemoveRequested)
|
||||
+ {
|
||||
+ action = removeDisk ? ToggleDiskAction.RETRY : ToggleDiskAction.ABORT;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ action = removeDisk ? ToggleDiskAction.NOOP : ToggleDiskAction.NORMAL;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (diskRemoveRequested)
|
||||
+ {
|
||||
+ action = removeDisk ? ToggleDiskAction.RETRY : ToggleDiskAction.ABORT;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ action = removeDisk ? ToggleDiskAction.NORMAL : ToggleDiskAction.NOOP;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return action;
|
||||
+ }
|
||||
+
|
||||
+ private void validateToggleDiskPreconditions(Resource rsc, boolean removeDisk)
|
||||
+ {
|
||||
+ ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
+ if (removeDisk)
|
||||
+ {
|
||||
+ validateDiskRemovalAllowed(rsc, rscDfn);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ ensureAllPeersHavePeerSlotLeft(rscDfn);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void clearToggleDiskFlags(Resource rsc)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_ADD_REQUESTED);
|
||||
+ rsc.getStateFlags().disableFlags(
|
||||
+ apiCtx,
|
||||
+ Resource.Flags.DISK_ADD_REQUESTED,
|
||||
+ Resource.Flags.DISK_ADDING,
|
||||
+ Resource.Flags.DISK_REMOVE_REQUESTED,
|
||||
+ Resource.Flags.DISK_REMOVING
|
||||
+ );
|
||||
+ }
|
||||
+ catch (AccessDeniedException | DatabaseException exc)
|
||||
+ {
|
||||
|
|
@ -170,60 +234,28 @@ index d93a18014..cc8ce4f04 100644
|
|||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void unmarkDiskRemoveRequested(Resource rsc)
|
||||
+ private void validateDiskRemovalAllowed(Resource rsc, ResourceDefinition rscDfn)
|
||||
+ {
|
||||
+ try
|
||||
+ int haveDiskCount = countDisksAndIsOnline(rscDfn);
|
||||
+ if (haveDiskCount <= 1)
|
||||
+ {
|
||||
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_REMOVE_REQUESTED);
|
||||
+ throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
+ ApiConsts.FAIL_INSUFFICIENT_REPLICA_COUNT,
|
||||
+ "Cannot remove the disk from the only online resource with a disk",
|
||||
+ true
|
||||
+ ));
|
||||
+ }
|
||||
+ catch (AccessDeniedException | DatabaseException exc)
|
||||
+
|
||||
+ if (!LayerUtils.hasLayer(getLayerData(peerAccCtx.get(), rsc), DeviceLayerKind.DRBD))
|
||||
+ {
|
||||
+ throw new ImplementationError(exc);
|
||||
+ throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
+ ApiConsts.FAIL_INVLD_LAYER_STACK,
|
||||
+ "Toggle disk is only supported in combination with DRBD",
|
||||
+ true
|
||||
+ ));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private void markDiskAdded(Resource rscData)
|
||||
{
|
||||
try
|
||||
@@ -1511,6 +1626,41 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
return layerData;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * Check if the layer stack has a non-diskless STORAGE layer.
|
||||
+ * This is used to detect orphaned storage layers that need cleanup.
|
||||
+ */
|
||||
+ private boolean hasNonDisklessStorageLayer(AbsRscLayerObject<Resource> layerDataRef)
|
||||
+ {
|
||||
+ boolean hasNonDiskless = false;
|
||||
+ if (layerDataRef != null)
|
||||
+ {
|
||||
+ if (layerDataRef.getLayerKind() == DeviceLayerKind.STORAGE)
|
||||
+ {
|
||||
+ for (VlmProviderObject<Resource> vlmData : layerDataRef.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ if (vlmData.getProviderKind() != DeviceProviderKind.DISKLESS)
|
||||
+ {
|
||||
+ hasNonDiskless = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (!hasNonDiskless)
|
||||
+ {
|
||||
+ for (AbsRscLayerObject<Resource> child : layerDataRef.getChildren())
|
||||
+ {
|
||||
+ if (hasNonDisklessStorageLayer(child))
|
||||
+ {
|
||||
+ hasNonDiskless = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return hasNonDiskless;
|
||||
+ }
|
||||
+
|
||||
private LockGuard createLockGuard()
|
||||
{
|
||||
return lockGuardFactory.buildDeferred(LockType.WRITE, LockObj.NODES_MAP, LockObj.RSC_DFN_MAP);
|
||||
/**
|
||||
* Although we need to rebuild the layerData as the layerList might have changed, if we do not
|
||||
* deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData
|
||||
|
|
|
|||
|
|
@ -1,87 +1,137 @@
|
|||
From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Kvapil <kvapss@gmail.com>
|
||||
Date: Mon, 12 Jan 2026 13:44:46 +0100
|
||||
Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk
|
||||
operations
|
||||
|
||||
Remove redundant ensureStackDataExists() call with empty payload from
|
||||
resetStoragePools() method that was causing TCP port conflicts after
|
||||
toggle-disk operations.
|
||||
|
||||
Root Cause:
|
||||
-----------
|
||||
The resetStoragePools() method, introduced in 2019 (commit 95cc17d0b8),
|
||||
calls ensureStackDataExists() with an empty LayerPayload. This worked
|
||||
correctly when TCP ports were stored at RscDfn level.
|
||||
|
||||
After the TCP port migration to per-node level (commit f754943463, May
|
||||
2025), this empty payload results in DrbdRscData being created without
|
||||
TCP ports assigned. The controller then sends a Pojo with an empty port
|
||||
Set to satellites.
|
||||
|
||||
On satellites, when DrbdRscData is initialized with an empty port list,
|
||||
initPorts() uses preferredNewPortsRef from peer resources. Since
|
||||
SatelliteDynamicNumberPool.tryAllocate() always returns true (no-op),
|
||||
any port from preferredNewPortsRef is accepted without conflict checking,
|
||||
leading to duplicate TCP port assignments.
|
||||
|
||||
Impact:
|
||||
-------
|
||||
This regression affects toggle-disk operations, particularly:
|
||||
- Snapshot creation/restore operations
|
||||
- Manual toggle-disk operations
|
||||
- Any operation calling resetStoragePools()
|
||||
|
||||
Symptoms include:
|
||||
- DRBD resources failing to adjust with "port is also used" errors
|
||||
- Resources stuck in StandAlone or Connecting states
|
||||
- Multiple resources on the same node using identical TCP ports
|
||||
|
||||
Solution:
|
||||
---------
|
||||
Remove the ensureStackDataExists() call from resetStoragePools() as it
|
||||
is redundant. The calling code (e.g., CtrlRscToggleDiskApiCallHandler
|
||||
line 1071) already invokes ensureStackDataExists() with the correct
|
||||
payload immediately after resetStoragePools().
|
||||
|
||||
This fix ensures:
|
||||
1. resetStoragePools() only resets storage pool assignments
|
||||
2. Layer data creation with proper TCP ports happens via the caller's
|
||||
ensureStackDataExists() with correct payload
|
||||
3. No DrbdRscData objects are created without TCP port assignments
|
||||
|
||||
Related Issues:
|
||||
---------------
|
||||
Fixes #454 - Duplicate TCP ports after backup/restore operations
|
||||
Related to user reports of resources stuck in StandAlone after node
|
||||
reboots when toggle-disk or backup operations were in progress.
|
||||
|
||||
Testing:
|
||||
--------
|
||||
Verified that:
|
||||
- Toggle-disk operations no longer create resources without TCP ports
|
||||
- Backup/restore operations complete without TCP port conflicts
|
||||
- Resources maintain unique TCP ports across toggle-disk cycles
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||||
---
|
||||
.../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
index d93a18014..01cfbbacf 100644
|
||||
--- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
+++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
@@ -37,6 +37,7 @@ import com.linbit.linstor.core.objects.StorPool;
|
||||
import com.linbit.linstor.core.objects.Volume;
|
||||
import com.linbit.linstor.core.objects.VolumeDefinition;
|
||||
import com.linbit.linstor.core.objects.utils.MixedStorPoolHelper;
|
||||
+import com.linbit.linstor.core.types.TcpPortNumber;
|
||||
import com.linbit.linstor.dbdrivers.DatabaseException;
|
||||
import com.linbit.linstor.event.EventWaiter;
|
||||
import com.linbit.linstor.event.ObjectIdentifier;
|
||||
@@ -85,6 +86,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
+import java.util.TreeSet;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -575,12 +577,13 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
|
||||
/*
|
||||
* We also have to remove the currently diskless DrbdRscData and free up the node-id as now we must
|
||||
- * use the shared resource's node-id
|
||||
+ * use the shared resource's node-id. We still need to preserve TCP ports though.
|
||||
*/
|
||||
+ copyDrbdTcpPortsIfExists(rsc, payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
- copyDrbdNodeIdIfExists(rsc, payload);
|
||||
+ copyDrbdSettings(rsc, payload);
|
||||
}
|
||||
/*
|
||||
* rebuilds the layerdata in case we just removed it..
|
||||
@@ -782,10 +785,20 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
}
|
||||
|
||||
/**
|
||||
- * Although we need to rebuild the layerData as the layerList might have changed, if we do not
|
||||
- * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData
|
||||
- * and recreating a new DrbdRscData ends up with the same node-id as before.
|
||||
+ * Copies DRBD settings (node-id and TCP ports) from the existing DrbdRscData into the payload
|
||||
+ * before removeLayerData() deletes them. This ensures that recreated DrbdRscData ends up with
|
||||
+ * the same node-id and TCP ports as before.
|
||||
+ *
|
||||
+ * TCP ports must be preserved because if the satellite misses the update (e.g. due to controller
|
||||
+ * restart or connectivity issues), it will keep the old ports while peers receive the new ones,
|
||||
+ * causing DRBD connections to fail with StandAlone state.
|
||||
*/
|
||||
+ private void copyDrbdSettings(Resource rsc, LayerPayload payload) throws ImplementationError
|
||||
+ {
|
||||
+ copyDrbdNodeIdIfExists(rsc, payload);
|
||||
+ copyDrbdTcpPortsIfExists(rsc, payload);
|
||||
+ }
|
||||
+
|
||||
private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws ImplementationError
|
||||
{
|
||||
Set<AbsRscLayerObject<Resource>> drbdRscDataSet = LayerRscUtils.getRscDataByLayer(
|
||||
@@ -804,6 +817,28 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
}
|
||||
}
|
||||
|
||||
+ private void copyDrbdTcpPortsIfExists(Resource rsc, LayerPayload payload) throws ImplementationError
|
||||
+ {
|
||||
+ Set<AbsRscLayerObject<Resource>> drbdRscDataSet = LayerRscUtils.getRscDataByLayer(
|
||||
+ getLayerData(apiCtx, rsc),
|
||||
+ DeviceLayerKind.DRBD
|
||||
+ );
|
||||
+ if (!drbdRscDataSet.isEmpty())
|
||||
+ {
|
||||
+ DrbdRscData<Resource> drbdRscData = (DrbdRscData<Resource>) drbdRscDataSet.iterator().next();
|
||||
+ Collection<TcpPortNumber> tcpPorts = drbdRscData.getTcpPortList();
|
||||
+ if (tcpPorts != null && !tcpPorts.isEmpty())
|
||||
+ {
|
||||
+ Set<Integer> portInts = new TreeSet<>();
|
||||
+ for (TcpPortNumber port : tcpPorts)
|
||||
+ {
|
||||
+ portInts.add(port.value);
|
||||
+ }
|
||||
+ payload.drbdRsc.tcpPorts = portInts;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private List<DeviceLayerKind> removeLayerData(Resource rscRef)
|
||||
{
|
||||
List<DeviceLayerKind> layerList;
|
||||
@@ -1058,15 +1093,15 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
/*
|
||||
* We also have to remove the possible meta-children of previous StorageRscData.
|
||||
* LayerData will be recreated with ensureStackDataExists.
|
||||
- * However, we still need to remember our node-id if we had / have DRBD in the list
|
||||
+ * However, we still need to remember our DRBD settings if we had / have DRBD in the list
|
||||
*/
|
||||
- copyDrbdNodeIdIfExists(rsc, payload);
|
||||
+ copyDrbdSettings(rsc, payload);
|
||||
layerList = removeLayerData(rsc);
|
||||
}
|
||||
else
|
||||
{
|
||||
markDiskAdded(rsc);
|
||||
- ctrlLayerStackHelper.resetStoragePools(rsc);
|
||||
+ ctrlLayerStackHelper.resetStoragePools(rsc, false);
|
||||
}
|
||||
ctrlLayerStackHelper.ensureStackDataExists(rsc, layerList, payload);
|
||||
|
||||
diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
index 3538b380c..4f589145e 100644
|
||||
index 3538b380c..f9733b6f1 100644
|
||||
--- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
+++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
@@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory
|
||||
@@ -263,6 +263,11 @@ public class CtrlRscLayerDataFactory
|
||||
}
|
||||
|
||||
public void resetStoragePools(Resource rscRef)
|
||||
+ {
|
||||
+ resetStoragePools(rscRef, true);
|
||||
+ }
|
||||
+
|
||||
+ public void resetStoragePools(Resource rscRef, boolean callEnsureStackDataExistsRef)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -276,8 +281,10 @@ public class CtrlRscLayerDataFactory
|
||||
|
||||
rscDataToProcess.addAll(rscData.getChildren());
|
||||
}
|
||||
-
|
||||
- ensureStackDataExists(rscRef, null, new LayerPayload());
|
||||
+ if (callEnsureStackDataExistsRef)
|
||||
+ {
|
||||
+ ensureStackDataExists(rscRef, null, new LayerPayload());
|
||||
+ }
|
||||
}
|
||||
catch (AccessDeniedException exc)
|
||||
{
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,570 @@
|
|||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java b/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java
|
||||
@@ -2081,7 +2081,6 @@ public abstract class AbsStorageProvider<
|
||||
)
|
||||
throws IOException, AccessDeniedException
|
||||
{
|
||||
- final StorPoolName storPoolObjName = storPoolObj.getName();
|
||||
if (storDevicePath != null)
|
||||
{
|
||||
errorReporter.logDebug("updateMinIoSize: Have storDevicePath \"%s\"", storDevicePath);
|
||||
@@ -2094,35 +2093,18 @@ public abstract class AbsStorageProvider<
|
||||
"updateMinIoSize: Block device path is \"%s\"",
|
||||
blockDevicePath.toString()
|
||||
);
|
||||
- final long minIoSize = BlockSizeInfo.getBlockSize(blockDevicePath);
|
||||
-
|
||||
- boolean updateValue = true;
|
||||
-
|
||||
- final String propKey = StorageConstants.NAMESPACE_INTERNAL + '/' +
|
||||
- StorageConstants.BLK_DEV_MIN_IO_SIZE;
|
||||
- final Props storPoolProps = storPoolObj.getProps(storDriverAccCtx);
|
||||
- final @Nullable String currentPropValue = storPoolProps.getProp(propKey);
|
||||
- if (currentPropValue != null)
|
||||
- {
|
||||
- try
|
||||
- {
|
||||
- final long currentPropMinIoSize = Long.parseLong(currentPropValue);
|
||||
- updateValue = currentPropMinIoSize != minIoSize;
|
||||
- }
|
||||
- catch (NumberFormatException ignored)
|
||||
- {
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (updateValue)
|
||||
- {
|
||||
- final String propValue = Long.toString(minIoSize);
|
||||
- errorReporter.logDebug(
|
||||
- "Storage pool \"%s\": Set property \"%s\" = \"%s\"",
|
||||
- storPoolObjName.displayValue, propKey, propValue
|
||||
- );
|
||||
- propsChange.changeStorPoolProp(storPoolObj, propKey, propValue);
|
||||
- }
|
||||
+ updatePropIfNeeded(
|
||||
+ propsChange,
|
||||
+ storPoolObj,
|
||||
+ StorageConstants.NAMESPACE_INTERNAL + '/' + StorageConstants.BLK_DEV_MIN_IO_SIZE,
|
||||
+ Long.toString(BlockSizeInfo.getBlockSize(blockDevicePath))
|
||||
+ );
|
||||
+ updatePropIfNeeded(
|
||||
+ propsChange,
|
||||
+ storPoolObj,
|
||||
+ StorageConstants.NAMESPACE_INTERNAL + '/' + StorageConstants.BLK_DEV_OPT_IO_SIZE,
|
||||
+ Long.toString(BlockSizeInfo.getOptimalIoSize(blockDevicePath))
|
||||
+ );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2130,6 +2112,28 @@ public abstract class AbsStorageProvider<
|
||||
}
|
||||
}
|
||||
|
||||
+ private void updatePropIfNeeded(
|
||||
+ LocalPropsChangePojo propsChangeRef,
|
||||
+ StorPool storPoolObjRef,
|
||||
+ String propKeyRef,
|
||||
+ String propValueRef
|
||||
+ )
|
||||
+ throws AccessDeniedException
|
||||
+ {
|
||||
+ final Props storPoolProps = storPoolObjRef.getProps(storDriverAccCtx);
|
||||
+ final @Nullable String currentPropValue = storPoolProps.getProp(propKeyRef);
|
||||
+ if (currentPropValue == null || !currentPropValue.equals(propValueRef))
|
||||
+ {
|
||||
+ errorReporter.logDebug(
|
||||
+ "Storage pool \"%s\": Set property \"%s\" = \"%s\"",
|
||||
+ storPoolObjRef.getName().displayValue,
|
||||
+ propKeyRef,
|
||||
+ propValueRef
|
||||
+ );
|
||||
+ propsChangeRef.changeStorPoolProp(storPoolObjRef, propKeyRef, propValueRef);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void updateAllocatedSize(VlmProviderObject<Resource> vlmDataRef)
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java b/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java
|
||||
@@ -3,8 +3,11 @@ package com.linbit.linstor.layer.storage.utils;
|
||||
import com.linbit.utils.MathUtils;
|
||||
import com.linbit.utils.SymbolicLinkResolver;
|
||||
|
||||
+import static com.linbit.linstor.layer.storage.BlockSizeConsts.DFLT_OPT_IO_SIZE;
|
||||
import static com.linbit.linstor.layer.storage.BlockSizeConsts.DFLT_IO_SIZE;
|
||||
+import static com.linbit.linstor.layer.storage.BlockSizeConsts.MAX_OPT_IO_SIZE;
|
||||
import static com.linbit.linstor.layer.storage.BlockSizeConsts.MAX_IO_SIZE;
|
||||
+import static com.linbit.linstor.layer.storage.BlockSizeConsts.MIN_OPT_IO_SIZE;
|
||||
import static com.linbit.linstor.layer.storage.BlockSizeConsts.MIN_IO_SIZE;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
@@ -13,6 +16,8 @@ import java.nio.file.Path;
|
||||
|
||||
public class BlockSizeInfo
|
||||
{
|
||||
+ private static final int NUMBER_BUFFER_SIZE = 32;
|
||||
+
|
||||
/**
|
||||
* Determines the blocksize, aka minimum I/O size, for the specified backing storage path.
|
||||
*
|
||||
@@ -51,7 +56,7 @@ public class BlockSizeInfo
|
||||
final Path infoSourceName = blockDevice.getFileName();
|
||||
final Path infoSource = Path.of("/sys/block", infoSourceName.toString(), "queue/physical_block_size");
|
||||
|
||||
- final byte[] data = new byte[32];
|
||||
+ final byte[] data = new byte[NUMBER_BUFFER_SIZE];
|
||||
try (final FileInputStream fileIn = new FileInputStream(infoSource.toString()))
|
||||
{
|
||||
final int readCount = fileIn.read(data);
|
||||
@@ -75,4 +80,38 @@ public class BlockSizeInfo
|
||||
}
|
||||
return blockSize;
|
||||
}
|
||||
+
|
||||
+ public static long getOptimalIoSize(final Path storageObj)
|
||||
+ {
|
||||
+ long optIoSize = DFLT_OPT_IO_SIZE;
|
||||
+ try
|
||||
+ {
|
||||
+ final Path blockDevice = SymbolicLinkResolver.resolveSymLink(storageObj);
|
||||
+ final Path infoSourceName = blockDevice.getFileName();
|
||||
+ final Path infoSource = Path.of("/sys/block", infoSourceName.toString(), "queue/optimal_io_size");
|
||||
+
|
||||
+ final byte[] data = new byte[NUMBER_BUFFER_SIZE];
|
||||
+ try (final FileInputStream fileIn = new FileInputStream(infoSource.toString()))
|
||||
+ {
|
||||
+ final int readCount = fileIn.read(data);
|
||||
+ if (readCount > 0)
|
||||
+ {
|
||||
+ String numberStr = new String(data, 0, readCount);
|
||||
+ numberStr = numberStr.trim();
|
||||
+ try
|
||||
+ {
|
||||
+ final long unboundedOptIoSize = Long.parseLong(numberStr);
|
||||
+ optIoSize = MathUtils.bounds(MIN_OPT_IO_SIZE, unboundedOptIoSize, MAX_OPT_IO_SIZE);
|
||||
+ }
|
||||
+ catch (NumberFormatException ignored)
|
||||
+ {
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ catch (IOException ignored)
|
||||
+ {
|
||||
+ }
|
||||
+ return optIoSize;
|
||||
+ }
|
||||
}
|
||||
diff --git a/server/src/main/java/com/linbit/SizeConv.java b/server/src/main/java/com/linbit/SizeConv.java
|
||||
--- a/server/src/main/java/com/linbit/SizeConv.java
|
||||
+++ b/server/src/main/java/com/linbit/SizeConv.java
|
||||
@@ -16,6 +16,7 @@ public class SizeConv
|
||||
public enum SizeUnit
|
||||
{
|
||||
UNIT_B,
|
||||
+ UNIT_SECTORS,
|
||||
UNIT_KiB,
|
||||
UNIT_MiB,
|
||||
UNIT_GiB,
|
||||
@@ -41,6 +42,9 @@ public class SizeConv
|
||||
case UNIT_B:
|
||||
factor = FACTOR_B;
|
||||
break;
|
||||
+ case UNIT_SECTORS:
|
||||
+ factor = FACTOR_SECTORS;
|
||||
+ break;
|
||||
case UNIT_KiB:
|
||||
factor = FACTOR_KiB;
|
||||
break;
|
||||
@@ -111,6 +115,9 @@ public class SizeConv
|
||||
case "b":
|
||||
unit = SizeUnit.UNIT_B;
|
||||
break;
|
||||
+ case "s":
|
||||
+ unit = SizeUnit.UNIT_SECTORS;
|
||||
+ break;
|
||||
case "k":
|
||||
// fall-through
|
||||
case "kb":
|
||||
@@ -217,6 +224,9 @@ public class SizeConv
|
||||
// Factor 1
|
||||
public static final BigInteger FACTOR_B = BigInteger.valueOf(1L);
|
||||
|
||||
+ // Factor 512
|
||||
+ public static final BigInteger FACTOR_SECTORS = BigInteger.valueOf(512L);
|
||||
+
|
||||
// Factor 1,024
|
||||
// Naming convention exception: SI unit capitalization rules
|
||||
@SuppressWarnings("checkstyle:constantname")
|
||||
diff --git a/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java b/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java
|
||||
--- a/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java
|
||||
+++ b/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java
|
||||
@@ -1,27 +1,62 @@
|
||||
package com.linbit.linstor.layer.luks;
|
||||
|
||||
+import com.linbit.SizeConv;
|
||||
+import com.linbit.SizeConv.SizeUnit;
|
||||
import com.linbit.exceptions.InvalidSizeException;
|
||||
+import com.linbit.linstor.PriorityProps;
|
||||
+import com.linbit.linstor.annotation.Nullable;
|
||||
+import com.linbit.linstor.api.ApiConsts;
|
||||
+import com.linbit.linstor.core.objects.AbsResource;
|
||||
+import com.linbit.linstor.core.objects.AbsVolume;
|
||||
+import com.linbit.linstor.core.objects.Resource;
|
||||
+import com.linbit.linstor.core.objects.ResourceDefinition;
|
||||
+import com.linbit.linstor.core.objects.ResourceGroup;
|
||||
+import com.linbit.linstor.core.objects.Snapshot;
|
||||
+import com.linbit.linstor.core.objects.SnapshotVolume;
|
||||
+import com.linbit.linstor.core.objects.StorPool;
|
||||
+import com.linbit.linstor.core.objects.Volume;
|
||||
+import com.linbit.linstor.core.objects.VolumeDefinition;
|
||||
import com.linbit.linstor.dbdrivers.DatabaseException;
|
||||
import com.linbit.linstor.layer.AbsLayerSizeCalculator;
|
||||
+import com.linbit.linstor.netcom.Peer;
|
||||
+import com.linbit.linstor.propscon.InvalidKeyException;
|
||||
+import com.linbit.linstor.propscon.ReadOnlyProps;
|
||||
import com.linbit.linstor.security.AccessDeniedException;
|
||||
+import com.linbit.linstor.storage.StorageConstants;
|
||||
import com.linbit.linstor.storage.data.adapter.luks.LuksVlmData;
|
||||
import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject;
|
||||
import com.linbit.linstor.storage.kinds.DeviceLayerKind;
|
||||
import com.linbit.linstor.storage.kinds.ExtTools;
|
||||
import com.linbit.linstor.storage.kinds.ExtToolsInfo;
|
||||
import com.linbit.linstor.storage.kinds.ExtToolsInfo.Version;
|
||||
+import com.linbit.linstor.utils.layer.LayerVlmUtils;
|
||||
+import com.linbit.utils.ShellUtils;
|
||||
+import com.linbit.utils.SignedAlign;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
+import java.util.Iterator;
|
||||
+import java.util.LinkedList;
|
||||
+import java.util.List;
|
||||
+import java.util.Set;
|
||||
+import java.util.regex.Matcher;
|
||||
+import java.util.regex.Pattern;
|
||||
+
|
||||
@Singleton
|
||||
public class LuksLayerSizeCalculator extends AbsLayerSizeCalculator<LuksVlmData<?>>
|
||||
{
|
||||
+ public static final String LUKS2_OPT_METADATA_SIZE = "--luks2-metadata-size";
|
||||
+ public static final String LUKS2_OPT_KEYSLOTS_SIZE = "--luks2-keyslots-size";
|
||||
+ public static final String LUKS2_OPT_OFFSET = "--offset";
|
||||
+ private static final String LUKS2_OPT_ALIGN_PAYLOAD = "--align-payload";
|
||||
+ private static final long DFLT_LUKS2_METADATA_SIZE_IN_BYTES = 16L << 10;
|
||||
+ private static final long DFLT_ALIGNMENT_1MIB_IN_BYTES = 1L << 20;
|
||||
+ private static final long LUKS1_HEADER_SIZE_IN_KIB = 2L << 10;
|
||||
+ private static final long LUKS2_HEADER_SIZE_IN_KIB = 16L << 10;
|
||||
+ private static final long LUKS2_HEADER_SIZE_IN_BYTES = LUKS2_HEADER_SIZE_IN_KIB << 10;
|
||||
|
||||
- // linstor calculates in KiB
|
||||
- private static final int MIB = 1024;
|
||||
- private static final int LUKS1_HEADER_SIZE = 2 * MIB;
|
||||
- private static final int LUKS2_HEADER_SIZE = 16 * MIB;
|
||||
+ private static final Pattern PATTERN_SIZE = Pattern.compile("(\\d+)([kKmMgGtT]i?[bB]?|[sS]|)");
|
||||
|
||||
@Inject
|
||||
public LuksLayerSizeCalculator(AbsLayerSizeCalculatorInit initRef)
|
||||
@@ -74,30 +109,266 @@ public class LuksLayerSizeCalculator extends AbsLayerSizeCalculator<LuksVlmData<
|
||||
}
|
||||
|
||||
private long getLuksHeaderSize(VlmProviderObject<?> vlmDataRef)
|
||||
- throws AccessDeniedException
|
||||
+ throws AccessDeniedException, InvalidSizeException
|
||||
{
|
||||
- ExtToolsInfo cryptSetupInfo = vlmDataRef.getRscLayerObject()
|
||||
+ @Nullable Peer peer = vlmDataRef.getRscLayerObject()
|
||||
.getAbsResource()
|
||||
.getNode()
|
||||
- .getPeer(sysCtx)
|
||||
- .getExtToolsManager()
|
||||
+ .getPeer(sysCtx);
|
||||
+ if (peer == null)
|
||||
+ {
|
||||
+ throw new InvalidSizeException(
|
||||
+ "Could not calculate size of LUKS volume, since cryptsetup's version could not be determined",
|
||||
+ null
|
||||
+ );
|
||||
+ }
|
||||
+ ExtToolsInfo cryptSetupInfo = peer.getExtToolsManager()
|
||||
.getExtToolInfo(ExtTools.CRYPT_SETUP);
|
||||
- long luksHeaderSize;
|
||||
+
|
||||
+ final long luksHeaderSize;
|
||||
if (cryptSetupInfo != null && cryptSetupInfo.isSupported())
|
||||
{
|
||||
if (cryptSetupInfo.hasVersionOrHigher(new Version(2, 1)))
|
||||
{
|
||||
- luksHeaderSize = LUKS2_HEADER_SIZE;
|
||||
+ luksHeaderSize = calcLuks2HeaderSize(vlmDataRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
- luksHeaderSize = LUKS1_HEADER_SIZE;
|
||||
+ luksHeaderSize = LUKS1_HEADER_SIZE_IN_KIB;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
- luksHeaderSize = -1;
|
||||
+ throw new InvalidSizeException(
|
||||
+ "Could not calculate size of LUKS volume, since cryptsetup's version could not be determined",
|
||||
+ null
|
||||
+ );
|
||||
}
|
||||
return luksHeaderSize;
|
||||
}
|
||||
+
|
||||
+ private long calcLuks2HeaderSize(VlmProviderObject<?> vlmDataRef) throws AccessDeniedException
|
||||
+ {
|
||||
+ PriorityProps prioProps = getPrioProps(vlmDataRef);
|
||||
+
|
||||
+ final @Nullable String userOptProp = prioProps.getProp(
|
||||
+ ApiConsts.KEY_STOR_DRIVER_LUKS_FORMAT_OPTIONS,
|
||||
+ ApiConsts.NAMESPC_STORAGE_DRIVER
|
||||
+ );
|
||||
+ List<String> userOptions = userOptProp != null ?
|
||||
+ ShellUtils.shellSplit(userOptProp) :
|
||||
+ new LinkedList<>();
|
||||
+
|
||||
+ final long alignedHeaderSizeInBytes;
|
||||
+ final long unalignedHeaderSizeInBytes;
|
||||
+ @Nullable Long cryptsetupOffsetInBytes = getLongOptionValue(
|
||||
+ userOptions,
|
||||
+ LUKS2_OPT_OFFSET,
|
||||
+ SizeUnit.UNIT_SECTORS
|
||||
+ );
|
||||
+ if (cryptsetupOffsetInBytes != null)
|
||||
+ {
|
||||
+ alignedHeaderSizeInBytes = cryptsetupOffsetInBytes;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ @Nullable Long cryptsetupLuks2KeyslotsSize = getLongOptionValue(
|
||||
+ userOptions,
|
||||
+ LUKS2_OPT_KEYSLOTS_SIZE
|
||||
+ );
|
||||
+ if (cryptsetupLuks2KeyslotsSize == null)
|
||||
+ {
|
||||
+ unalignedHeaderSizeInBytes = LUKS2_HEADER_SIZE_IN_BYTES;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ @Nullable Long cryptsetupLuks2MetadataSize = getLongOptionValue(
|
||||
+ userOptions,
|
||||
+ LUKS2_OPT_METADATA_SIZE
|
||||
+ );
|
||||
+ cryptsetupLuks2MetadataSize = cryptsetupLuks2MetadataSize == null ?
|
||||
+ DFLT_LUKS2_METADATA_SIZE_IN_BYTES :
|
||||
+ cryptsetupLuks2MetadataSize;
|
||||
+
|
||||
+ unalignedHeaderSizeInBytes = 2 * cryptsetupLuks2MetadataSize + cryptsetupLuks2KeyslotsSize;
|
||||
+ }
|
||||
+
|
||||
+ long alignment = getAlignment(vlmDataRef, userOptions);
|
||||
+ alignedHeaderSizeInBytes = new SignedAlign(alignment).ceiling(unalignedHeaderSizeInBytes);
|
||||
+ }
|
||||
+ return SizeConv.convert(
|
||||
+ alignedHeaderSizeInBytes,
|
||||
+ SizeUnit.UNIT_B,
|
||||
+ SizeUnit.UNIT_KiB
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ private long getAlignment(VlmProviderObject<?> vlmDataRef, List<String> userOptions) throws AccessDeniedException
|
||||
+ {
|
||||
+ @Nullable Long cryptsetupAlignPayloadInBytes = getLongOptionValue(
|
||||
+ userOptions,
|
||||
+ LUKS2_OPT_ALIGN_PAYLOAD,
|
||||
+ SizeUnit.UNIT_SECTORS
|
||||
+ );
|
||||
+
|
||||
+ long alignment = DFLT_ALIGNMENT_1MIB_IN_BYTES;
|
||||
+ if (cryptsetupAlignPayloadInBytes != null)
|
||||
+ {
|
||||
+ alignment = cryptsetupAlignPayloadInBytes;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ final long maxOptIoSize = getMaxOptIoSize(vlmDataRef);
|
||||
+ alignment = Math.max(alignment, maxOptIoSize);
|
||||
+ }
|
||||
+ return alignment;
|
||||
+ }
|
||||
+
|
||||
+ private long getMaxOptIoSize(VlmProviderObject<?> vlmDataRef) throws InvalidKeyException, AccessDeniedException
|
||||
+ {
|
||||
+ long ret = 0;
|
||||
+ Set<StorPool> storPoolSet = LayerVlmUtils.getStorPoolSet(vlmDataRef, sysCtx);
|
||||
+ for (StorPool sp : storPoolSet)
|
||||
+ {
|
||||
+ @Nullable String strValue = sp.getProps(sysCtx)
|
||||
+ .getProp(
|
||||
+ StorageConstants.BLK_DEV_OPT_IO_SIZE,
|
||||
+ StorageConstants.NAMESPACE_INTERNAL
|
||||
+ );
|
||||
+ if (strValue != null)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ long parsed = Long.parseLong(strValue);
|
||||
+ ret = Math.max(parsed, ret);
|
||||
+ }
|
||||
+ catch (NumberFormatException ignored)
|
||||
+ {
|
||||
+ errorReporter.logWarning(
|
||||
+ "LuksHeaderSize: Failed to parse '%s' from prop %s. Defaulting to 0 opt_io_size " +
|
||||
+ "(no recommendation/hint)",
|
||||
+ strValue,
|
||||
+ StorageConstants.NAMESPACE_INTERNAL + "/" + StorageConstants.BLK_DEV_OPT_IO_SIZE
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable Long getLongOptionValue(List<String> userOptionsRef, String optRef)
|
||||
+ {
|
||||
+ return getLongOptionValue(userOptionsRef, optRef, SizeUnit.UNIT_B);
|
||||
+ }
|
||||
+
|
||||
+ @SuppressWarnings("checkstyle:magicnumber")
|
||||
+ private @Nullable Long getLongOptionValue(List<String> userOptionsRef, String optRef, SizeUnit dfltSizeUnit)
|
||||
+ {
|
||||
+ @Nullable Long ret = null;
|
||||
+ @Nullable String val = findLastValue(userOptionsRef, optRef);
|
||||
+ if (val != null && !val.isBlank())
|
||||
+ {
|
||||
+ Matcher matcher = PATTERN_SIZE.matcher(val);
|
||||
+ if (matcher.matches())
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ ret = Long.parseLong(matcher.group(1));
|
||||
+ String unit = matcher.group(2);
|
||||
+ SizeUnit sizeUnit;
|
||||
+ if (unit.isBlank())
|
||||
+ {
|
||||
+ sizeUnit = dfltSizeUnit;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ boolean forcePowerOfTwo = unit.length() == 1 || unit.length() == 3;
|
||||
+ sizeUnit = SizeUnit.parse(unit, forcePowerOfTwo);
|
||||
+ }
|
||||
+
|
||||
+ ret = SizeConv.convert(ret, sizeUnit, SizeUnit.UNIT_B);
|
||||
+ }
|
||||
+ catch (NumberFormatException ignored)
|
||||
+ {
|
||||
+ errorReporter.logWarning(
|
||||
+ "LuksHeaderSize: Failed to parse '%s' from option '%s %s'.",
|
||||
+ matcher.group(1),
|
||||
+ optRef,
|
||||
+ val
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable String findLastValue(List<String> userOptionsRef, String optRef)
|
||||
+ {
|
||||
+ Iterator<String> it = userOptionsRef.iterator();
|
||||
+ @Nullable String val = null;
|
||||
+ while (it.hasNext())
|
||||
+ {
|
||||
+ String opt = it.next();
|
||||
+ if (opt.equals(optRef))
|
||||
+ {
|
||||
+ if (it.hasNext())
|
||||
+ {
|
||||
+ val = it.next();
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ val = null;
|
||||
+ }
|
||||
+ }
|
||||
+ else if (opt.startsWith(optRef + "="))
|
||||
+ {
|
||||
+ val = opt.substring(optRef.length() + 1);
|
||||
+ if (val.isBlank())
|
||||
+ {
|
||||
+ val = null;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return val;
|
||||
+ }
|
||||
+
|
||||
+ private PriorityProps getPrioProps(VlmProviderObject<?> vlmDataRef) throws AccessDeniedException
|
||||
+ {
|
||||
+ final AbsVolume<?> vlm = vlmDataRef.getVolume();
|
||||
+ final AbsResource<?> rsc = vlm.getAbsResource();
|
||||
+ final ResourceDefinition rscDfn = vlm.getResourceDefinition();
|
||||
+ final VolumeDefinition vlmDfn = vlm.getVolumeDefinition();
|
||||
+ final ResourceGroup rscGrp = rscDfn.getResourceGroup();
|
||||
+
|
||||
+ final ReadOnlyProps vlmProps;
|
||||
+ final ReadOnlyProps rscProps;
|
||||
+ if (vlm instanceof Volume)
|
||||
+ {
|
||||
+ vlmProps = ((Volume) vlm).getProps(sysCtx);
|
||||
+ rscProps = ((Resource) rsc).getProps(sysCtx);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ vlmProps = ((SnapshotVolume) vlm).getVlmProps(sysCtx);
|
||||
+ rscProps = ((Snapshot) rsc).getRscProps(sysCtx);
|
||||
+ }
|
||||
+
|
||||
+ final PriorityProps prioProps = new PriorityProps(
|
||||
+ vlmProps,
|
||||
+ rscProps
|
||||
+ );
|
||||
+ for (StorPool storPool : LayerVlmUtils.getStorPoolSet(vlmDataRef, sysCtx))
|
||||
+ {
|
||||
+ prioProps.addProps(storPool.getProps(sysCtx));
|
||||
+ }
|
||||
+ prioProps.addProps(
|
||||
+ rsc.getNode().getProps(sysCtx),
|
||||
+ vlmDfn.getProps(sysCtx),
|
||||
+ rscDfn.getProps(sysCtx),
|
||||
+ rscGrp.getVolumeGroupProps(sysCtx, vlmDfn.getVolumeNumber()),
|
||||
+ rscGrp.getProps(sysCtx),
|
||||
+ stltProps
|
||||
+ );
|
||||
+ return prioProps;
|
||||
+ }
|
||||
}
|
||||
diff --git a/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java b/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java
|
||||
--- a/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java
|
||||
+++ b/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java
|
||||
@@ -13,4 +13,9 @@ public class BlockSizeConsts
|
||||
|
||||
// Default value for the minimum_io_size value of non-storage layers
|
||||
public static final long DFLT_SPECIAL_IO_SIZE = (1L << 12);
|
||||
+
|
||||
+ // optimal_io_size may be 0 to indicate no recommendation.
|
||||
+ public static final long MIN_OPT_IO_SIZE = 0;
|
||||
+ public static final long DFLT_OPT_IO_SIZE = 0;
|
||||
+ public static final long MAX_OPT_IO_SIZE = Long.MAX_VALUE;
|
||||
}
|
||||
diff --git a/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java b/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java
|
||||
--- a/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java
|
||||
+++ b/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java
|
||||
@@ -11,6 +11,7 @@ public class StorageConstants
|
||||
public static final String NAMESPACE_NVME = ApiConsts.NAMESPC_STORAGE_DRIVER + "/NVME";
|
||||
public static final String NAMESPACE_INTERNAL = NAMESPACE_STOR_DRIVER + "/internal/";
|
||||
|
||||
+ public static final String BLK_DEV_OPT_IO_SIZE = "optIoSize";
|
||||
public static final String BLK_DEV_MIN_IO_SIZE = "minIoSize";
|
||||
public static final String BLK_DEV_MIN_IO_SIZE_AUTO = "minIoSizeAuto";
|
||||
public static final String BLK_DEV_MAX_BIO_SIZE = "maxBioSize";
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
index a302ee835..01967a31f 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
@@ -371,10 +371,13 @@ public class DrbdLayer implements DeviceLayer
|
||||
boolean isDiskless = drbdRscData.getAbsResource().isDrbdDiskless(workerCtx);
|
||||
StateFlags<Flags> rscFlags = drbdRscData.getAbsResource().getStateFlags();
|
||||
boolean isDiskRemoving = rscFlags.isSet(workerCtx, Resource.Flags.DISK_REMOVING);
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAdding = rscFlags.isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
|
||||
boolean contProcess = isDiskless;
|
||||
|
||||
- boolean processChildren = !isDiskless || isDiskRemoving;
|
||||
+ // Process children when: has disk, removing disk, OR adding disk (toggle-disk)
|
||||
+ boolean processChildren = !isDiskless || isDiskRemoving || isDiskAdding;
|
||||
// do not process children when ONLY DRBD_DELETE flag is set (DELETE flag is still unset)
|
||||
processChildren &= (!rscFlags.isSet(workerCtx, Resource.Flags.DRBD_DELETE) ||
|
||||
rscFlags.isSet(workerCtx, Resource.Flags.DELETE));
|
||||
@@ -570,7 +573,11 @@ public class DrbdLayer implements DeviceLayer
|
||||
{
|
||||
// hasMetaData needs to be run after child-resource processed
|
||||
List<DrbdVlmData<Resource>> createMetaData = new ArrayList<>();
|
||||
- if (!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) && !skipDisk)
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAddingForMd = drbdRscData.getAbsResource().getStateFlags()
|
||||
+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
+ // Create metadata when: has disk OR adding disk (toggle-disk), and skipDisk is disabled
|
||||
+ if ((!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) || isDiskAddingForMd) && !skipDisk)
|
||||
{
|
||||
// do not try to create meta data while the resource is diskless or skipDisk is enabled
|
||||
for (DrbdVlmData<Resource> drbdVlmData : checkMetaData)
|
||||
@@ -988,8 +995,10 @@ public class DrbdLayer implements DeviceLayer
|
||||
{
|
||||
List<DrbdVlmData<Resource>> checkMetaData = new ArrayList<>();
|
||||
Resource rsc = drbdRscData.getAbsResource();
|
||||
+ // Include DISK_ADD_REQUESTED/DISK_ADDING for toggle-disk scenario where we need to check/create metadata
|
||||
if (!rsc.isDrbdDiskless(workerCtx) ||
|
||||
- rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING)
|
||||
+ rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING) ||
|
||||
+ rsc.getStateFlags().isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING)
|
||||
)
|
||||
{
|
||||
// using a dedicated list to prevent concurrentModificationException
|
||||
@@ -1177,9 +1186,16 @@ public class DrbdLayer implements DeviceLayer
|
||||
|
||||
boolean hasMetaData;
|
||||
|
||||
+ // Check if we need to verify/create metadata
|
||||
+ // Force metadata check when:
|
||||
+ // 1. checkMetaData is enabled
|
||||
+ // 2. volume doesn't have disk yet (diskless -> diskful transition)
|
||||
+ // 3. DISK_ADD_REQUESTED/DISK_ADDING flag is set (retry scenario where storage exists but no metadata)
|
||||
+ boolean isDiskAddingState = drbdVlmData.getRscLayerObject().getAbsResource().getStateFlags()
|
||||
+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
if (drbdVlmData.checkMetaData() ||
|
||||
- // when adding a disk, DRBD believes that it is diskless but we still need to create metadata
|
||||
- !drbdVlmData.hasDisk())
|
||||
+ !drbdVlmData.hasDisk() ||
|
||||
+ isDiskAddingState)
|
||||
{
|
||||
if (mdUtils.hasMetaData())
|
||||
{
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java
|
||||
index 5627d1be8..ece191292 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java
|
||||
@@ -42,6 +42,9 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
+import java.nio.charset.StandardCharsets;
|
||||
+import java.util.regex.Matcher;
|
||||
+import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
@@ -56,6 +58,9 @@ public class DrbdAdm
|
||||
|
||||
public static final int WAIT_CONNECT_RES_TIME = 10;
|
||||
private static final long DOWN_WAIT_TIMEOUT_SEC = 5;
|
||||
+ private static final long FORCE_DETACH_RETRY_WAIT_MS = 250;
|
||||
+ private static final String BITMAP_LEAK_ERR_MSG = "already has a bitmap, this should not happen";
|
||||
+ private static final Pattern BITMAP_LEAK_MINOR_PATTERN = Pattern.compile("\\bminor\\s+(\\d+)\\b");
|
||||
|
||||
private final ExtCmdFactory extCmdFactory;
|
||||
private final AccessContext sysCtx;
|
||||
@@ -131,8 +136,38 @@ public class DrbdAdm
|
||||
// command.add(resName);
|
||||
command.add(drbdRscData.getSuffixedResourceName());
|
||||
// execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName()));
|
||||
- execute(command);
|
||||
- // execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName()));
|
||||
+ String[] commandArr = command.toArray(new String[0]);
|
||||
+ try
|
||||
+ {
|
||||
+ File nullDevice = new File(Platform.nullDevice());
|
||||
+ ExtCmd extCmd = extCmdFactory.create();
|
||||
+ if (Platform.isWindows())
|
||||
+ {
|
||||
+ extCmd.setTimeout(TimeoutType.WAIT, 5 * 60 * 1000);
|
||||
+ }
|
||||
+
|
||||
+ OutputData outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr);
|
||||
+ if (
|
||||
+ outputData.exitCode != 0 &&
|
||||
+ isBitmapLeakOnAttach(outputData) &&
|
||||
+ cleanupStaleBitmapAndRetry(extCmd, nullDevice, outputData)
|
||||
+ )
|
||||
+ {
|
||||
+ outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr);
|
||||
+ }
|
||||
+ if (outputData.exitCode != 0)
|
||||
+ {
|
||||
+ throw new ExtCmdFailedException(commandArr, outputData);
|
||||
+ }
|
||||
+ }
|
||||
+ catch (ChildProcessTimeoutException timeoutExc)
|
||||
+ {
|
||||
+ throw new ExtCmdFailedException(commandArr, timeoutExc);
|
||||
+ }
|
||||
+ catch (IOException ioExc)
|
||||
+ {
|
||||
+ throw new ExtCmdFailedException(commandArr, ioExc);
|
||||
+ }
|
||||
|
||||
drbdRscData.setAdjustRequired(false);
|
||||
}
|
||||
@@ -805,6 +840,75 @@ public class DrbdAdm
|
||||
}
|
||||
}
|
||||
|
||||
+ static boolean isBitmapLeakOnAttach(OutputData outputData)
|
||||
+ {
|
||||
+ return extractBitmapLeakMinor(outputData) != null;
|
||||
+ }
|
||||
+
|
||||
+ static @Nullable Integer extractBitmapLeakMinor(OutputData outputData)
|
||||
+ {
|
||||
+ String stderr = new String(outputData.stderrData, StandardCharsets.UTF_8);
|
||||
+ if (!stderr.contains(BITMAP_LEAK_ERR_MSG))
|
||||
+ {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ Matcher matcher = BITMAP_LEAK_MINOR_PATTERN.matcher(stderr);
|
||||
+ if (!matcher.find())
|
||||
+ {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ return Integer.parseInt(matcher.group(1));
|
||||
+ }
|
||||
+
|
||||
+ private boolean cleanupStaleBitmapAndRetry(
|
||||
+ ExtCmd extCmd,
|
||||
+ File nullDevice,
|
||||
+ OutputData outputData
|
||||
+ )
|
||||
+ throws IOException, ChildProcessTimeoutException, ExtCmdFailedException
|
||||
+ {
|
||||
+ @Nullable Integer minor = extractBitmapLeakMinor(outputData);
|
||||
+ if (minor == null)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ OutputData detachOut = extCmd.pipeExec(
|
||||
+ ProcessBuilder.Redirect.from(nullDevice),
|
||||
+ DRBDSETUP_UTIL,
|
||||
+ "detach",
|
||||
+ Integer.toString(minor)
|
||||
+ );
|
||||
+ if (detachOut.exitCode == 0)
|
||||
+ {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ OutputData forceDetachOut = extCmd.pipeExec(
|
||||
+ ProcessBuilder.Redirect.from(nullDevice),
|
||||
+ DRBDSETUP_UTIL,
|
||||
+ "detach",
|
||||
+ Integer.toString(minor),
|
||||
+ "--force"
|
||||
+ );
|
||||
+ if (forceDetachOut.exitCode != 0)
|
||||
+ {
|
||||
+ throw new ExtCmdFailedException(forceDetachOut.executedCommand, forceDetachOut);
|
||||
+ }
|
||||
+
|
||||
+ try
|
||||
+ {
|
||||
+ Thread.sleep(FORCE_DETACH_RETRY_WAIT_MS);
|
||||
+ }
|
||||
+ catch (InterruptedException ignored)
|
||||
+ {
|
||||
+ Thread.currentThread().interrupt();
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
public static class DrbdPrimary implements AutoCloseable
|
||||
{
|
||||
private final DrbdAdm drbdAdm;
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
index 49138a8fd..2f768ca0d 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
@@ -83,6 +83,8 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
+import java.nio.file.Files;
|
||||
+import java.nio.file.Paths;
|
||||
|
||||
@Singleton
|
||||
public class DeviceHandlerImpl implements DeviceHandler
|
||||
@@ -1646,7 +1648,10 @@ public class DeviceHandlerImpl implements DeviceHandler
|
||||
private void updateDiscGran(VlmProviderObject<Resource> vlmData) throws DatabaseException, StorageException
|
||||
{
|
||||
String devicePath = vlmData.getDevicePath();
|
||||
- if (devicePath != null && vlmData.exists())
|
||||
+ // Check if device path physically exists before calling lsblk
|
||||
+ // This is important for DRBD devices which might be temporarily unavailable during adjust
|
||||
+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet)
|
||||
+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath)))
|
||||
{
|
||||
if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE)
|
||||
{
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
index 01967a31f..78b8195a4 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
@@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer
|
||||
// The .res file might not have been generated in the prepare method since it was
|
||||
// missing information from the child-layers. Now that we have processed them, we
|
||||
// need to make sure the .res file exists in all circumstances.
|
||||
- regenerateResFile(drbdRscData);
|
||||
+ // However, if the underlying devices are not accessible (e.g., LUKS device is closed
|
||||
+ // during resource deletion), we skip regenerating the res file to avoid errors
|
||||
+ boolean canRegenerateResFile = true;
|
||||
+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
+ {
|
||||
+ AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
|
||||
+ if (dataChild != null)
|
||||
+ {
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ VlmProviderObject<Resource> childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr());
|
||||
+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null)
|
||||
+ {
|
||||
+ canRegenerateResFile = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (canRegenerateResFile)
|
||||
+ {
|
||||
+ regenerateResFile(drbdRscData);
|
||||
+ }
|
||||
|
||||
// createMetaData needs rendered resFile
|
||||
for (DrbdVlmData<Resource> drbdVlmData : createMetaData)
|
||||
@@ -766,19 +788,72 @@ public class DrbdLayer implements DeviceLayer
|
||||
|
||||
if (drbdRscData.isAdjustRequired())
|
||||
{
|
||||
- try
|
||||
+ // Check if underlying devices are accessible before adjusting
|
||||
+ // This is important for encrypted resources (LUKS) where the device
|
||||
+ // might be closed during deletion
|
||||
+ boolean canAdjust = true;
|
||||
+
|
||||
+ // IMPORTANT: Check child volumes only when disk access is actually needed.
|
||||
+ // For network reconnect (StandAlone -> Connected), disk access is not required.
|
||||
+ boolean needsDiskAccess = false;
|
||||
+
|
||||
+ // Check if there are pending operations that require disk access
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
{
|
||||
- drbdUtils.adjust(
|
||||
- drbdRscData,
|
||||
- false,
|
||||
- skipDisk,
|
||||
- false
|
||||
- );
|
||||
+ Volume vlm = (Volume) drbdVlmData.getVolume();
|
||||
+ StateFlags<Volume.Flags> vlmFlags = vlm.getFlags();
|
||||
+
|
||||
+ // Disk access is needed if:
|
||||
+ // - creating a new volume
|
||||
+ // - resizing
|
||||
+ // - checking/creating metadata
|
||||
+ if (!drbdVlmData.exists() ||
|
||||
+ drbdVlmData.checkMetaData() ||
|
||||
+ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE))
|
||||
+ {
|
||||
+ needsDiskAccess = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check child volumes only if disk access is actually needed
|
||||
+ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
+ {
|
||||
+ AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
|
||||
+ if (dataChild != null)
|
||||
+ {
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ VlmProviderObject<Resource> childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr());
|
||||
+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null)
|
||||
+ {
|
||||
+ canAdjust = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
- catch (ExtCmdFailedException extCmdExc)
|
||||
+
|
||||
+ if (canAdjust)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ drbdUtils.adjust(
|
||||
+ drbdRscData,
|
||||
+ false,
|
||||
+ skipDisk,
|
||||
+ false
|
||||
+ );
|
||||
+ }
|
||||
+ catch (ExtCmdFailedException extCmdExc)
|
||||
+ {
|
||||
+ restoreBackupResFile(drbdRscData);
|
||||
+ throw extCmdExc;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
{
|
||||
- restoreBackupResFile(drbdRscData);
|
||||
- throw extCmdExc;
|
||||
+ drbdRscData.setAdjustRequired(false);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java
|
||||
index cdca0b6d2..89c8be9da 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java
|
||||
@@ -383,6 +383,7 @@ public class LuksLayer implements DeviceLayer
|
||||
vlmData.setSizeState(Size.AS_EXPECTED);
|
||||
|
||||
vlmData.setOpened(true);
|
||||
+ vlmData.setExists(true);
|
||||
vlmData.setFailed(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,8 @@ spec:
|
|||
- name: DrbdOptions/auto-diskful-allow-cleanup
|
||||
value: {{ .Values.linstor.autoDiskful.allowCleanup | quote }}
|
||||
{{- end }}
|
||||
- name: DrbdOptions/Net/verify-alg
|
||||
value: "crc32c"
|
||||
- name: DrbdOptions/Net/connect-int
|
||||
value: "15"
|
||||
- name: DrbdOptions/Net/ping-int
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
piraeusServer:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/piraeus-server
|
||||
tag: 1.32.3@sha256:64c91357affc6317d9544fc35b3ff8b2fcb065ad8fc5ed26f7a2fa8ac991a1c1
|
||||
tag: 1.33.2@sha256:553f313ab35dc2e345ef3683156d29e75c23177e2750e9af3a83aa9e23941cbb
|
||||
# Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian)
|
||||
talos:
|
||||
enabled: true
|
||||
|
|
@ -13,4 +13,4 @@ linstor:
|
|||
linstorCSI:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/linstor-csi
|
||||
tag: v1.10.5@sha256:585ee6336036cb9e0b2183a6ab3f67e1ccb6d35a1df484d36e734444ee09eb4f
|
||||
tag: v1.10.5@sha256:44154467c2f13c30ad09d932f51a935b143028bce54f877f00a51d1e43979f61
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ metadata:
|
|||
name: alerta-db
|
||||
spec:
|
||||
instances: 2
|
||||
{{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "alerta-db" }}
|
||||
{{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }}
|
||||
imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }}
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
{{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }}
|
||||
{{- if $rawConstraints }}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ metadata:
|
|||
app: alerta
|
||||
annotations:
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: {{ $ingress }}
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }}
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
spec:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ spec:
|
|||
url: http://grafana-dashboards.cozy-grafana-operator.svc/{{ . }}.json
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if eq .Release.Namespace "tenant-root" }}
|
||||
{{- if or (eq .Release.Namespace "tenant-root") (eq .Release.Namespace "cozy-monitoring") }}
|
||||
{{- range (split "\n" (.Files.Get "dashboards-infra.list")) }}
|
||||
{{- $parts := split "/" . }}
|
||||
{{- if eq (len $parts) 2 }}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ metadata:
|
|||
name: grafana-db
|
||||
spec:
|
||||
instances: 2
|
||||
{{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "grafana-db" }}
|
||||
{{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }}
|
||||
imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }}
|
||||
storage:
|
||||
size: {{ .Values.grafana.db.size }}
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ spec:
|
|||
metadata:
|
||||
annotations:
|
||||
{{- if eq $solver "http01" }}
|
||||
acme.cert-manager.io/http01-ingress-class: "{{ $ingress }}"
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: "{{ $ingress }}"
|
||||
{{- end }}
|
||||
cert-manager.io/cluster-issuer: {{ $clusterIssuer }}
|
||||
spec:
|
||||
|
|
|
|||
|
|
@ -11,3 +11,15 @@ update:
|
|||
wget -q https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/refs/tags/$(RELEASE)/deployments/multus-daemonset-thick.yml -O templates/multus-daemonset-thick.yml
|
||||
sed -i '/multus-cni/s/snapshot-thick/$(RELEASE)-thick/' templates/multus-daemonset-thick.yml && \
|
||||
patch --no-backup-if-mismatch -p4 < patches/customize-deployment.patch
|
||||
$(SED_INPLACE) "/ARG VERSION/ s|=.*|=$(RELEASE)|g" images/multus-cni/Dockerfile
|
||||
|
||||
image:
|
||||
docker buildx build images/multus-cni \
|
||||
--tag $(REGISTRY)/multus-cni:$(call settag,$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/multus-cni:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/multus-cni.json \
|
||||
$(BUILDX_ARGS)
|
||||
DIGEST=$$(yq e '."containerimage.digest"' images/multus-cni.json -o json -r) && \
|
||||
sed -i "s|image: .*multus-cni.*|image: $(REGISTRY)/multus-cni:$(TAG)@$${DIGEST}|g" templates/multus-daemonset-thick.yml
|
||||
rm -f images/multus-cni.json
|
||||
|
|
|
|||
25
packages/system/multus/images/multus-cni/Dockerfile
Normal file
25
packages/system/multus/images/multus-cni/Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
FROM --platform=$BUILDPLATFORM golang:1.24 AS build
|
||||
|
||||
ARG VERSION=v4.2.3
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
WORKDIR /usr/src/multus-cni
|
||||
|
||||
RUN curl -sSL https://github.com/k8snetworkplumbingwg/multus-cni/archive/refs/tags/${VERSION}.tar.gz | \
|
||||
tar -xzf - --strip=1
|
||||
|
||||
COPY patches /patches
|
||||
RUN git init && git config user.email "build@cozystack" && git config user.name "build" && \
|
||||
git add -A && git commit -m "init" && git tag ${VERSION} && \
|
||||
git apply /patches/*.diff
|
||||
|
||||
RUN ./hack/build-go.sh
|
||||
|
||||
FROM debian:stable-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/cozystack/cozystack
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
COPY --from=build /usr/src/multus-cni/bin/cert-approver /
|
||||
WORKDIR /
|
||||
|
||||
ENTRYPOINT ["/usr/src/multus-cni/bin/multus-daemon"]
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
diff --git a/pkg/multus/multus.go b/pkg/multus/multus.go
|
||||
index a7941fe..eff0406 100644
|
||||
--- a/pkg/multus/multus.go
|
||||
+++ b/pkg/multus/multus.go
|
||||
@@ -954,33 +954,19 @@ func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
|
||||
}
|
||||
|
||||
if !useCacheConf {
|
||||
- // Fetch delegates again if cache is not exist and pod info can be read
|
||||
- if os.IsNotExist(err) && pod != nil {
|
||||
- if in.ClusterNetwork != "" {
|
||||
- _, err = k8s.GetDefaultNetworks(pod, in, kubeClient, nil)
|
||||
- if err != nil {
|
||||
- return cmdErr(k8sArgs, "failed to get clusterNetwork/defaultNetworks: %v", err)
|
||||
- }
|
||||
- // First delegate is always the master plugin
|
||||
- in.Delegates[0].MasterPlugin = true
|
||||
- }
|
||||
-
|
||||
- // Get pod annotation and so on
|
||||
- _, _, err := k8s.TryLoadPodDelegates(pod, in, kubeClient, nil)
|
||||
- if err != nil {
|
||||
- if len(in.Delegates) == 0 {
|
||||
- // No delegate available so send error
|
||||
- return cmdErr(k8sArgs, "failed to get delegates: %v", err)
|
||||
- }
|
||||
- // Get clusterNetwork before, so continue to delete
|
||||
- logging.Errorf("Multus: failed to get delegates: %v, but continue to delete clusterNetwork", err)
|
||||
- }
|
||||
- } else {
|
||||
- // The options to continue with a delete have been exhausted (cachefile + API query didn't work)
|
||||
- // We cannot exit with an error as this may cause a sandbox to never get deleted.
|
||||
- logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err)
|
||||
+ // If cache does not exist, ADD was never completed for this sandbox.
|
||||
+ // Attempting DEL with a config reconstructed from the API will fail because
|
||||
+ // delegate plugins (e.g. kube-ovn) require runtime state that only exists
|
||||
+ // after a successful ADD (socket paths, cached results, etc.).
|
||||
+ // Return success to allow containerd to release the sandbox name reservation.
|
||||
+ if os.IsNotExist(err) {
|
||||
+ logging.Verbosef("Multus: no cache found for container %s (ADD was never completed), skip DEL", args.ContainerID)
|
||||
return nil
|
||||
}
|
||||
+ // The options to continue with a delete have been exhausted
|
||||
+ // We cannot exit with an error as this may cause a sandbox to never get deleted.
|
||||
+ logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err)
|
||||
+ return nil
|
||||
}
|
||||
|
||||
// set CNIVersion in delegate CNI config if there is no CNIVersion and multus conf have CNIVersion.
|
||||
|
|
@ -119,6 +119,7 @@ data:
|
|||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"multusMasterCNI": "05-cilium.conflist",
|
||||
"socketDir": "/host/run/multus/"
|
||||
}
|
||||
---
|
||||
|
|
@ -154,7 +155,7 @@ spec:
|
|||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:v4.2.3-thick
|
||||
image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.4@sha256:6735ffc12e5e660951f5a42943b38c308f33774a55836e94c191001405b58ec0
|
||||
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
|
||||
resources:
|
||||
requests:
|
||||
|
|
@ -200,7 +201,7 @@ spec:
|
|||
fieldPath: spec.nodeName
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:v4.2.3-thick
|
||||
image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.4@sha256:6735ffc12e5e660951f5a42943b38c308f33774a55836e94c191001405b58ec0
|
||||
command:
|
||||
- "/usr/src/multus-cni/bin/install_multus"
|
||||
- "-d"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
objectstorage:
|
||||
controller:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.0@sha256:05aee8bf8b292150090868163c6bb49962d874ac63858c93f8e130f53dd40b85"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.4@sha256:e176668b8daecc5d462b843285c907c873edeb2d888af31254b30bb02320c394"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ metadata:
|
|||
name: seaweedfs-db
|
||||
spec:
|
||||
instances: {{ .Values.db.replicas }}
|
||||
{{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "seaweedfs-db" }}
|
||||
{{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }}
|
||||
imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }}
|
||||
storage:
|
||||
size: {{ .Values.db.size }}
|
||||
{{- with .Values.db.storageClass }}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ seaweedfs:
|
|||
nginx.ingress.kubernetes.io/client-body-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/client-header-timeout: "120"
|
||||
nginx.ingress.kubernetes.io/service-upstream: "true"
|
||||
acme.cert-manager.io/http01-ingress-class: tenant-root
|
||||
acme.cert-manager.io/http01-ingress-ingressclassname: tenant-root
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
tls:
|
||||
- hosts:
|
||||
|
|
@ -177,7 +177,7 @@ seaweedfs:
|
|||
bucketClassName: "seaweedfs"
|
||||
region: ""
|
||||
sidecar:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.0@sha256:b508ca451c6051b64e71cedd6209c80ac0e0bea8aa6bb47e838ec9d99bf4796a"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.4@sha256:cc1f60ef2cb40ab61c4787e234f83e57a7b8aca0ffe7b17f4c4fa6e3de1669f4"
|
||||
certificates:
|
||||
commonName: "SeaweedFS CA"
|
||||
ipAddresses: []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue