mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-03 21:48:57 +00:00
Compare commits
136 Commits
refactor-e
...
v0.40.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c4507b58a | ||
|
|
b82604718c | ||
|
|
82435cda9c | ||
|
|
fc5de97dc0 | ||
|
|
85b8ad3984 | ||
|
|
d1746a5f81 | ||
|
|
d94f5c24b4 | ||
|
|
3595eaea13 | ||
|
|
37e84be573 | ||
|
|
7e58850fb1 | ||
|
|
ef00445322 | ||
|
|
8bd3861164 | ||
|
|
d11dc983b8 | ||
|
|
5e9bff0e50 | ||
|
|
cfbe41d8e0 | ||
|
|
d13a45c8d6 | ||
|
|
6b3cd40ba5 | ||
|
|
838d1abff6 | ||
|
|
4cdd1113d1 | ||
|
|
4d7ee809e5 | ||
|
|
125a57eb97 | ||
|
|
d33a2357ed | ||
|
|
a8a7e510ac | ||
|
|
cf0598bcf1 | ||
|
|
6169220317 | ||
|
|
fbe33e1191 | ||
|
|
9507f24332 | ||
|
|
2aa839b68f | ||
|
|
40683e28a7 | ||
|
|
4ddb374680 | ||
|
|
e07fafb393 | ||
|
|
7b932a400d | ||
|
|
8f317c9065 | ||
|
|
e73bf9905d | ||
|
|
c5222aae97 | ||
|
|
e176cdec87 | ||
|
|
477d391440 | ||
|
|
9ba76d4839 | ||
|
|
f5ccbe94f9 | ||
|
|
f900db6338 | ||
|
|
3bcc0e5cc0 | ||
|
|
2a8317291d | ||
|
|
acc6ed26bb | ||
|
|
3af7430074 | ||
|
|
800ca3b3d2 | ||
|
|
4a83d2c7c8 | ||
|
|
c0b1539d3e | ||
|
|
1c42f8bd10 | ||
|
|
de2bbd35c2 | ||
|
|
fe82f66330 | ||
|
|
c4bf72b44f | ||
|
|
33452d65e5 | ||
|
|
9039011252 | ||
|
|
2bbeb1b474 | ||
|
|
a51e2078a8 | ||
|
|
e40a95849d | ||
|
|
31bcbbd5bf | ||
|
|
e7b62ca3c8 | ||
|
|
868a7497d0 | ||
|
|
666eeffa47 | ||
|
|
8d166e0754 | ||
|
|
967f0bdc9f | ||
|
|
30c1041e7a | ||
|
|
68a639b32c | ||
|
|
2bf1168dcf | ||
|
|
526af29459 | ||
|
|
b65fc64e17 | ||
|
|
9a2d39a4b0 | ||
|
|
fe565aff4a | ||
|
|
29bd12d52d | ||
|
|
12023b7f02 | ||
|
|
f3c178e30d | ||
|
|
654eb7c3f9 | ||
|
|
ffd10e99f1 | ||
|
|
f400310f28 | ||
|
|
9ab7430cec | ||
|
|
87fa8c93fc | ||
|
|
d1c0af1db4 | ||
|
|
eb042b4ef1 | ||
|
|
ecb9a223aa | ||
|
|
77de2bdda0 | ||
|
|
b3179d032a | ||
|
|
a939b2bbd0 | ||
|
|
682f83e71c | ||
|
|
66cfbe7c0e | ||
|
|
67109c33b1 | ||
|
|
f9bfcfd125 | ||
|
|
1beb11a6a9 | ||
|
|
f7c6a54b0c | ||
|
|
35c57798a6 | ||
|
|
aba147571b | ||
|
|
17138d825d | ||
|
|
f5eb211312 | ||
|
|
c44b198a60 | ||
|
|
9b5f3726b6 | ||
|
|
66b68e3798 | ||
|
|
bd443bd578 | ||
|
|
2c37611136 | ||
|
|
a4a432e2aa | ||
|
|
da6c053d3f | ||
|
|
38d6b98a70 | ||
|
|
28c933161a | ||
|
|
e50950b7a1 | ||
|
|
b1a55f5a38 | ||
|
|
1ee6eb8482 | ||
|
|
cbfa99148b | ||
|
|
54d0f52245 | ||
|
|
d0bfb6e2fc | ||
|
|
f86896eceb | ||
|
|
cb320f9d48 | ||
|
|
b1a7e9560e | ||
|
|
fe9d334880 | ||
|
|
1e2b66131c | ||
|
|
8d14bcb598 | ||
|
|
825390c209 | ||
|
|
09fd7c4094 | ||
|
|
8928731abf | ||
|
|
153635379a | ||
|
|
fbb2ea095a | ||
|
|
50c1d1a067 | ||
|
|
52ebcae8a2 | ||
|
|
5314d61987 | ||
|
|
8b29c53a45 | ||
|
|
4e4a5606d7 | ||
|
|
f2a4f1b1c8 | ||
|
|
33128748e6 | ||
|
|
7e1cad26e7 | ||
|
|
3d5118f5b3 | ||
|
|
06a25c1c45 | ||
|
|
67e47256e2 | ||
|
|
df277b350c | ||
|
|
644d71eef7 | ||
|
|
8e351f1827 | ||
|
|
38a4adfaa3 | ||
|
|
03885f5ae2 | ||
|
|
d26d3e1f40 |
188
.github/workflows/auto-release.yaml
vendored
Normal file
188
.github/workflows/auto-release.yaml
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
name: Auto Patch Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run daily at 2:00 AM CET (1:00 UTC in winter, 0:00 UTC in summer)
|
||||
# Using 1:00 UTC to approximate 2:00 AM CET
|
||||
- cron: '0 1 * * *'
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
concurrency:
|
||||
group: auto-release-${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
auto-release:
|
||||
name: Auto Patch Release
|
||||
runs-on: [self-hosted]
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Configure git
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
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 --unset-all http.https://github.com/.extraheader || true
|
||||
|
||||
- name: Process release branches
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_PAT }}
|
||||
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)
|
||||
execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' });
|
||||
|
||||
// Get all release-X.Y branches
|
||||
const branches = execSync('git branch -r | grep -E "origin/release-[0-9]+\\.[0-9]+$" | sed "s|origin/||" | tr -d " "', { encoding: 'utf8' })
|
||||
.split('\n')
|
||||
.filter(b => b.trim())
|
||||
.filter(b => /^release-\d+\.\d+$/.test(b));
|
||||
|
||||
console.log(`Found ${branches.length} release branches: ${branches.join(', ')}`);
|
||||
|
||||
// Get all published releases (not draft)
|
||||
const allReleases = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
// Filter to only published releases (not draft) with tags matching vX.Y.Z (no suffixes)
|
||||
const publishedReleases = allReleases.data
|
||||
.filter(r => !r.draft)
|
||||
.filter(r => /^v\d+\.\d+\.\d+$/.test(r.tag_name));
|
||||
|
||||
console.log(`Found ${publishedReleases.length} published releases without suffixes`);
|
||||
|
||||
for (const branch of branches) {
|
||||
console.log(`\n=== Processing branch: ${branch} ===`);
|
||||
|
||||
// Extract X.Y from branch name (release-X.Y)
|
||||
const match = branch.match(/^release-(\d+\.\d+)$/);
|
||||
if (!match) {
|
||||
console.log(` ⚠️ Branch ${branch} doesn't match pattern, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const [major, minor] = match[1].split('.');
|
||||
const versionPrefix = `v${major}.${minor}.`;
|
||||
|
||||
console.log(` Looking for releases with prefix: ${versionPrefix}`);
|
||||
|
||||
// Find the latest published release for this branch (vX.Y.Z without suffixes)
|
||||
const branchReleases = publishedReleases
|
||||
.filter(r => r.tag_name.startsWith(versionPrefix))
|
||||
.filter(r => /^v\d+\.\d+\.\d+$/.test(r.tag_name)); // Ensure no suffixes
|
||||
|
||||
if (branchReleases.length === 0) {
|
||||
console.log(` ⚠️ No published releases found for ${branch}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sort by version (descending) to get the latest
|
||||
branchReleases.sort((a, b) => {
|
||||
const aVersion = a.tag_name.match(/^v(\d+)\.(\d+)\.(\d+)$/);
|
||||
const bVersion = b.tag_name.match(/^v(\d+)\.(\d+)\.(\d+)$/);
|
||||
if (!aVersion || !bVersion) return 0;
|
||||
|
||||
const aNum = parseInt(aVersion[1]) * 10000 + parseInt(aVersion[2]) * 100 + parseInt(aVersion[3]);
|
||||
const bNum = parseInt(bVersion[1]) * 10000 + parseInt(bVersion[2]) * 100 + parseInt(bVersion[3]);
|
||||
return bNum - aNum;
|
||||
});
|
||||
|
||||
const latestRelease = branchReleases[0];
|
||||
console.log(` ✅ Latest published release: ${latestRelease.tag_name}`);
|
||||
|
||||
// Get the commit SHA for this release tag
|
||||
let releaseCommitSha;
|
||||
try {
|
||||
releaseCommitSha = execSync(`git rev-list -n 1 ${latestRelease.tag_name}`, { encoding: 'utf8' }).trim();
|
||||
console.log(` Release commit SHA: ${releaseCommitSha}`);
|
||||
} catch (error) {
|
||||
console.log(` ⚠️ Could not find commit for tag ${latestRelease.tag_name}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checkout the branch
|
||||
execSync(`git fetch origin ${branch}:${branch}`, { encoding: 'utf8' });
|
||||
execSync(`git checkout ${branch}`, { encoding: 'utf8' });
|
||||
|
||||
// Get the latest commit on the branch
|
||||
const latestBranchCommit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim();
|
||||
console.log(` Latest branch commit: ${latestBranchCommit}`);
|
||||
|
||||
// Check if there are new commits after the release
|
||||
const commitsAfterRelease = execSync(
|
||||
`git rev-list ${releaseCommitSha}..HEAD --oneline`,
|
||||
{ encoding: 'utf8' }
|
||||
).trim();
|
||||
|
||||
if (!commitsAfterRelease) {
|
||||
console.log(` ℹ️ No new commits after ${latestRelease.tag_name}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(` ✅ Found new commits after release:`);
|
||||
console.log(commitsAfterRelease);
|
||||
|
||||
// Calculate next version (Z+1)
|
||||
const versionMatch = latestRelease.tag_name.match(/^v(\d+)\.(\d+)\.(\d+)$/);
|
||||
if (!versionMatch) {
|
||||
console.log(` ❌ Could not parse version from ${latestRelease.tag_name}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const nextPatch = parseInt(versionMatch[3]) + 1;
|
||||
const nextTag = `v${versionMatch[1]}.${versionMatch[2]}.${nextPatch}`;
|
||||
|
||||
console.log(` 🏷️ Creating new tag: ${nextTag} on commit ${latestBranchCommit}`);
|
||||
|
||||
// Create and push the tag with base_ref for workflow triggering
|
||||
try {
|
||||
// Delete local tag if exists to force update
|
||||
try {
|
||||
execSync(`git tag -d ${nextTag}`, { encoding: 'utf8' });
|
||||
} catch (e) {
|
||||
// Tag doesn't exist locally, that's fine
|
||||
}
|
||||
|
||||
// Delete remote tag if exists
|
||||
try {
|
||||
execSync(`git push origin :refs/tags/${nextTag}`, { encoding: 'utf8' });
|
||||
} catch (e) {
|
||||
// Tag doesn't exist remotely, that's fine
|
||||
}
|
||||
|
||||
// Create tag locally
|
||||
execSync(`git tag ${nextTag} ${latestBranchCommit}`, { encoding: 'utf8' });
|
||||
|
||||
// Push tag with HEAD reference to preserve base_ref
|
||||
execSync(`git push origin HEAD:refs/tags/${nextTag}`, { encoding: 'utf8' });
|
||||
console.log(` ✅ Successfully created and pushed tag ${nextTag}`);
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error creating/pushing tag ${nextTag}: ${error.message}`);
|
||||
core.setFailed(`Failed to create tag ${nextTag} for branch ${branch}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Finished processing all release branches`);
|
||||
|
||||
104
.github/workflows/backport.yaml
vendored
104
.github/workflows/backport.yaml
vendored
@@ -2,7 +2,7 @@ name: Automatic Backport
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed] # fires when PR is closed (merged)
|
||||
types: [closed, labeled] # fires when PR is closed (merged) or labeled
|
||||
|
||||
concurrency:
|
||||
group: backport-${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
@@ -13,22 +13,46 @@ permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
# Determine which backports are needed
|
||||
prepare:
|
||||
if: |
|
||||
github.event.pull_request.merged == true &&
|
||||
contains(github.event.pull_request.labels.*.name, 'backport')
|
||||
(
|
||||
contains(github.event.pull_request.labels.*.name, 'backport') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'backport-previous') ||
|
||||
(github.event.action == 'labeled' && (github.event.label.name == 'backport' || github.event.label.name == 'backport-previous'))
|
||||
)
|
||||
runs-on: [self-hosted]
|
||||
|
||||
outputs:
|
||||
backport_current: ${{ steps.labels.outputs.backport }}
|
||||
backport_previous: ${{ steps.labels.outputs.backport_previous }}
|
||||
current_branch: ${{ steps.branches.outputs.current_branch }}
|
||||
previous_branch: ${{ steps.branches.outputs.previous_branch }}
|
||||
steps:
|
||||
# 1. Decide which maintenance branch should receive the back‑port
|
||||
- name: Determine target maintenance branch
|
||||
id: target
|
||||
- name: Check which labels are present
|
||||
id: labels
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
let rel;
|
||||
const pr = context.payload.pull_request;
|
||||
const labels = pr.labels.map(l => l.name);
|
||||
const isBackport = labels.includes('backport');
|
||||
const isBackportPrevious = labels.includes('backport-previous');
|
||||
|
||||
core.setOutput('backport', isBackport ? 'true' : 'false');
|
||||
core.setOutput('backport_previous', isBackportPrevious ? 'true' : 'false');
|
||||
|
||||
console.log(`backport label: ${isBackport}, backport-previous label: ${isBackportPrevious}`);
|
||||
|
||||
- name: Determine target branches
|
||||
id: branches
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// Get latest release
|
||||
let latestRelease;
|
||||
try {
|
||||
rel = await github.rest.repos.getLatestRelease({
|
||||
latestRelease = await github.rest.repos.getLatestRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
@@ -36,18 +60,70 @@ jobs:
|
||||
core.setFailed('No existing releases found; cannot determine backport target.');
|
||||
return;
|
||||
}
|
||||
const [maj, min] = rel.data.tag_name.replace(/^v/, '').split('.');
|
||||
const branch = `release-${maj}.${min}`;
|
||||
core.setOutput('branch', branch);
|
||||
console.log(`Latest release ${rel.data.tag_name}; backporting to ${branch}`);
|
||||
|
||||
const [maj, min] = latestRelease.data.tag_name.replace(/^v/, '').split('.');
|
||||
const currentBranch = `release-${maj}.${min}`;
|
||||
const prevMin = parseInt(min) - 1;
|
||||
const previousBranch = prevMin >= 0 ? `release-${maj}.${prevMin}` : '';
|
||||
|
||||
core.setOutput('current_branch', currentBranch);
|
||||
core.setOutput('previous_branch', previousBranch);
|
||||
|
||||
console.log(`Current branch: ${currentBranch}, Previous branch: ${previousBranch || 'N/A'}`);
|
||||
|
||||
// Verify previous branch exists if we need it
|
||||
if (previousBranch && '${{ steps.labels.outputs.backport_previous }}' === 'true') {
|
||||
try {
|
||||
await github.rest.repos.getBranch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
branch: previousBranch
|
||||
});
|
||||
console.log(`Previous branch ${previousBranch} exists`);
|
||||
} catch (e) {
|
||||
core.setFailed(`Previous branch ${previousBranch} does not exist.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
backport:
|
||||
needs: prepare
|
||||
if: |
|
||||
github.event.pull_request.merged == true &&
|
||||
(needs.prepare.outputs.backport_current == 'true' || needs.prepare.outputs.backport_previous == 'true')
|
||||
runs-on: [self-hosted]
|
||||
strategy:
|
||||
matrix:
|
||||
backport_type: [current, previous]
|
||||
steps:
|
||||
# 1. Determine target branch based on matrix
|
||||
- name: Set target branch
|
||||
id: target
|
||||
if: |
|
||||
(matrix.backport_type == 'current' && needs.prepare.outputs.backport_current == 'true') ||
|
||||
(matrix.backport_type == 'previous' && needs.prepare.outputs.backport_previous == 'true')
|
||||
run: |
|
||||
if [ "${{ matrix.backport_type }}" == "current" ]; then
|
||||
echo "branch=${{ needs.prepare.outputs.current_branch }}" >> $GITHUB_OUTPUT
|
||||
echo "Target branch: ${{ needs.prepare.outputs.current_branch }}"
|
||||
else
|
||||
echo "branch=${{ needs.prepare.outputs.previous_branch }}" >> $GITHUB_OUTPUT
|
||||
echo "Target branch: ${{ needs.prepare.outputs.previous_branch }}"
|
||||
fi
|
||||
|
||||
# 2. Checkout (required by backport‑action)
|
||||
- name: Checkout repository
|
||||
if: steps.target.outcome == 'success'
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 3. Create the back‑port pull request
|
||||
- name: Create back‑port PR
|
||||
uses: korthout/backport-action@v3
|
||||
id: backport
|
||||
if: steps.target.outcome == 'success'
|
||||
uses: korthout/backport-action@v3.2.1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
label_pattern: '' # don't read labels for targets
|
||||
target_branches: ${{ steps.target.outputs.branch }}
|
||||
merge_commits: skip
|
||||
conflict_resolution: draft_commit_conflicts
|
||||
|
||||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
- name: Install generate
|
||||
run: |
|
||||
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.0.5/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
|
||||
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.0.6/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
|
||||
|
||||
- name: Run pre-commit hooks
|
||||
run: |
|
||||
|
||||
143
.github/workflows/pull-requests-release.yaml
vendored
143
.github/workflows/pull-requests-release.yaml
vendored
@@ -46,7 +46,12 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Create tag on merge commit
|
||||
env:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
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 tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }}
|
||||
git push -f origin ${{ steps.get_tag.outputs.tag }}
|
||||
|
||||
@@ -105,67 +110,95 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
# Get the latest published release
|
||||
- name: Get the latest published release
|
||||
id: latest_release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
try {
|
||||
const rel = await github.rest.repos.getLatestRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
core.setOutput('tag', rel.data.tag_name);
|
||||
} catch (_) {
|
||||
core.setOutput('tag', '');
|
||||
}
|
||||
|
||||
# Compare current tag vs latest using semver-utils
|
||||
- name: Semver compare
|
||||
id: semver
|
||||
uses: madhead/semver-utils@v4.3.0
|
||||
with:
|
||||
version: ${{ steps.get_tag.outputs.tag }}
|
||||
compare-to: ${{ steps.latest_release.outputs.tag }}
|
||||
|
||||
# Derive flags: prerelease? make_latest?
|
||||
- name: Calculate publish flags
|
||||
id: flags
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const tag = '${{ steps.get_tag.outputs.tag }}'; // v0.31.5-rc.1
|
||||
const m = tag.match(/^v(\d+\.\d+\.\d+)(-(?:alpha|beta|rc)\.\d+)?$/);
|
||||
if (!m) {
|
||||
core.setFailed(`❌ tag '${tag}' must match 'vX.Y.Z' or 'vX.Y.Z-(alpha|beta|rc).N'`);
|
||||
return;
|
||||
}
|
||||
const version = m[1] + (m[2] ?? ''); // 0.31.5-rc.1
|
||||
const isRc = Boolean(m[2]);
|
||||
core.setOutput('is_rc', isRc);
|
||||
const outdated = '${{ steps.semver.outputs.comparison-result }}' === '<';
|
||||
core.setOutput('make_latest', isRc || outdated ? 'false' : 'legacy');
|
||||
|
||||
# Publish draft release with correct flags
|
||||
# Publish draft release and ensure correct latest flag
|
||||
- name: Publish draft release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const tag = '${{ steps.get_tag.outputs.tag }}';
|
||||
const m = tag.match(/^v(\d+\.\d+\.\d+)(-(?:alpha|beta|rc)\.\d+)?$/);
|
||||
if (!m) {
|
||||
core.setFailed(`❌ tag '${tag}' must match 'vX.Y.Z' or 'vX.Y.Z-(alpha|beta|rc).N'`);
|
||||
return;
|
||||
}
|
||||
const isRc = Boolean(m[2]);
|
||||
|
||||
// Parse semver string to comparable numbers
|
||||
function parseSemver(v) {
|
||||
const match = v.replace(/^v/, '').match(/^(\d+)\.(\d+)\.(\d+)/);
|
||||
if (!match) return null;
|
||||
return {
|
||||
major: parseInt(match[1]),
|
||||
minor: parseInt(match[2]),
|
||||
patch: parseInt(match[3])
|
||||
};
|
||||
}
|
||||
|
||||
// Compare two semver objects
|
||||
function compareSemver(a, b) {
|
||||
if (a.major !== b.major) return a.major - b.major;
|
||||
if (a.minor !== b.minor) return a.minor - b.minor;
|
||||
return a.patch - b.patch;
|
||||
}
|
||||
|
||||
const currentSemver = parseSemver(tag);
|
||||
|
||||
// Get all releases
|
||||
const releases = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
const draft = releases.data.find(r => r.tag_name === tag && r.draft);
|
||||
if (!draft) throw new Error(`Draft release for ${tag} not found`);
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: draft.id,
|
||||
draft: false,
|
||||
prerelease: ${{ steps.flags.outputs.is_rc }},
|
||||
make_latest: '${{ steps.flags.outputs.make_latest }}'
|
||||
repo: context.repo.repo,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
console.log(`🚀 Published release for ${tag}`);
|
||||
// Find draft release to publish
|
||||
const draft = releases.data.find(r => r.tag_name === tag && r.draft);
|
||||
if (!draft) throw new Error(`Draft release for ${tag} not found`);
|
||||
|
||||
// Find max semver among published releases (excluding current draft)
|
||||
const publishedReleases = releases.data
|
||||
.filter(r => !r.draft && !r.prerelease)
|
||||
.filter(r => /^v\d+\.\d+\.\d+$/.test(r.tag_name))
|
||||
.map(r => ({ id: r.id, tag: r.tag_name, semver: parseSemver(r.tag_name) }))
|
||||
.filter(r => r.semver !== null);
|
||||
|
||||
let maxRelease = null;
|
||||
for (const rel of publishedReleases) {
|
||||
if (!maxRelease || compareSemver(rel.semver, maxRelease.semver) > 0) {
|
||||
maxRelease = rel;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if this release should be latest
|
||||
const isOutdated = maxRelease && compareSemver(currentSemver, maxRelease.semver) < 0;
|
||||
const makeLatest = (isRc || isOutdated) ? 'false' : 'true';
|
||||
|
||||
if (isRc) {
|
||||
console.log(`🏷️ ${tag} is a prerelease, make_latest: false`);
|
||||
} else if (isOutdated) {
|
||||
console.log(`🏷️ ${tag} < ${maxRelease.tag} (max semver), make_latest: false`);
|
||||
} else {
|
||||
console.log(`🏷️ ${tag} is the highest version, make_latest: true`);
|
||||
}
|
||||
|
||||
// Publish the release
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: draft.id,
|
||||
draft: false,
|
||||
prerelease: isRc,
|
||||
make_latest: makeLatest
|
||||
});
|
||||
console.log(`🚀 Published release ${tag}`);
|
||||
|
||||
// If this is a backport/outdated release, ensure the correct release is marked as latest
|
||||
if (isOutdated && maxRelease) {
|
||||
console.log(`🔧 Ensuring ${maxRelease.tag} remains the latest release...`);
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: maxRelease.id,
|
||||
make_latest: 'true'
|
||||
});
|
||||
console.log(`✅ Restored ${maxRelease.tag} as latest release`);
|
||||
}
|
||||
|
||||
2
.github/workflows/pull-requests.yaml
vendored
2
.github/workflows/pull-requests.yaml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
DOCKER_CONFIG: ${{ runner.temp }}/.docker
|
||||
|
||||
- name: Build Talos image
|
||||
run: make -C packages/core/installer talos-nocloud
|
||||
run: make -C packages/core/talos talos-nocloud
|
||||
|
||||
- name: Save git diff as patch
|
||||
if: "!contains(github.event.pull_request.labels.*.name, 'release')"
|
||||
|
||||
78
.github/workflows/retest.yaml
vendored
Normal file
78
.github/workflows/retest.yaml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
name: Retest
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
retest:
|
||||
name: Retest PR
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/retest')
|
||||
permissions:
|
||||
actions: write
|
||||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Rerun from Prepare environment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const prNumber = context.issue.number;
|
||||
|
||||
// Get the PR to find the head SHA
|
||||
const pr = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber
|
||||
});
|
||||
|
||||
// Find the latest workflow run for this PR
|
||||
const runs = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'pull-requests.yaml',
|
||||
head_sha: pr.data.head.sha
|
||||
});
|
||||
|
||||
if (runs.data.workflow_runs.length === 0) {
|
||||
core.setFailed('No workflow runs found for this PR');
|
||||
return;
|
||||
}
|
||||
|
||||
const latestRun = runs.data.workflow_runs[0];
|
||||
console.log(`Found workflow run: ${latestRun.id} (${latestRun.status})`);
|
||||
|
||||
// Check if workflow is waiting for approval (fork PRs)
|
||||
if (latestRun.conclusion === 'action_required') {
|
||||
core.setFailed('Workflow is waiting for approval. A maintainer must approve the workflow first.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get jobs for this run
|
||||
const jobs = await github.rest.actions.listJobsForWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: latestRun.id
|
||||
});
|
||||
|
||||
// Find "Prepare environment" job
|
||||
const prepareJob = jobs.data.jobs.find(j => j.name === 'Prepare environment');
|
||||
|
||||
if (!prepareJob) {
|
||||
core.setFailed('Could not find "Prepare environment" job');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found job: ${prepareJob.name} (id: ${prepareJob.id}, status: ${prepareJob.status})`);
|
||||
|
||||
// Rerun the job
|
||||
await github.rest.actions.reRunJobForWorkflowRun({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
job_id: prepareJob.id
|
||||
});
|
||||
|
||||
console.log(`✅ Triggered rerun of job "${prepareJob.name}" (${prepareJob.id})`);
|
||||
26
.github/workflows/tags.yaml
vendored
26
.github/workflows/tags.yaml
vendored
@@ -123,32 +123,6 @@ jobs:
|
||||
git commit -m "Prepare release ${GITHUB_REF#refs/tags/}" -s || echo "No changes to commit"
|
||||
git push origin HEAD || true
|
||||
|
||||
# Get `latest_version` from latest published release
|
||||
- name: Get latest published release
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
id: latest_release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
try {
|
||||
const rel = await github.rest.repos.getLatestRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
core.setOutput('tag', rel.data.tag_name);
|
||||
} catch (_) {
|
||||
core.setOutput('tag', '');
|
||||
}
|
||||
|
||||
# Compare tag (A) with latest (B)
|
||||
- name: Semver compare
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
id: semver
|
||||
uses: madhead/semver-utils@v4.3.0
|
||||
with:
|
||||
version: ${{ steps.tag.outputs.tag }} # A
|
||||
compare-to: ${{ steps.latest_release.outputs.tag }} # B
|
||||
|
||||
# Create or reuse draft release
|
||||
- name: Create / reuse draft release
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
|
||||
92
.github/workflows/update-releasenotes.yaml
vendored
Normal file
92
.github/workflows/update-releasenotes.yaml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
name: Update Release Notes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: update-releasenotes-${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
update-releasenotes:
|
||||
name: Update Release Notes
|
||||
runs-on: [self-hosted]
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Update release notes from changelogs
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const changelogDir = 'docs/changelogs';
|
||||
|
||||
// Get releases from first page
|
||||
const releases = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
per_page: 30
|
||||
});
|
||||
|
||||
console.log(`Found ${releases.data.length} releases (first page only)`);
|
||||
|
||||
// Process each release
|
||||
for (const release of releases.data) {
|
||||
const tag = release.tag_name;
|
||||
const changelogFile = `${tag}.md`;
|
||||
const changelogPath = path.join(changelogDir, changelogFile);
|
||||
|
||||
console.log(`\nProcessing release: ${tag}`);
|
||||
|
||||
// Check if changelog file exists
|
||||
if (!fs.existsSync(changelogPath)) {
|
||||
console.log(` ⚠️ Changelog file ${changelogFile} does not exist, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read changelog file content
|
||||
let changelogContent;
|
||||
try {
|
||||
changelogContent = fs.readFileSync(changelogPath, 'utf8');
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error reading file ${changelogPath}: ${error.message}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!changelogContent.trim()) {
|
||||
console.log(` ⚠️ Changelog file ${changelogFile} is empty, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if content is already up to date
|
||||
const currentBody = release.body || '';
|
||||
if (currentBody.trim() === changelogContent.trim()) {
|
||||
console.log(` ✓ Content is already up to date, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update release notes
|
||||
try {
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: release.id,
|
||||
body: changelogContent
|
||||
});
|
||||
console.log(` ✅ Successfully updated release notes for ${tag}`);
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error updating release ${tag}: ${error.message}`);
|
||||
core.setFailed(`Failed to update release notes for ${tag}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,4 @@ This list is sorted in chronological order, based on the submission date.
|
||||
| [Urmanac](https://urmanac.com) | @kingdonb | 2024-12-04 | Urmanac is the future home of a hosting platform for the knowledge base of a community of personal server enthusiasts. We use Cozystack to provide support services for web sites hosted using both conventional deployments and on SpinKube, with WASM. |
|
||||
| [Hidora](https://hikube.cloud) | @matthieu-robin | 2025-09-17 | Hidora is a Swiss cloud provider delivering managed services and infrastructure solutions through datacenters located in Switzerland, ensuring data sovereignty and reliability. Its sovereign cloud platform, Hikube, is designed to run workloads with high availability across multiple datacenters, providing enterprises with a secure and scalable foundation for their applications based on Cozystack. |
|
||||
| [QOSI](https://qosi.kz) | @tabu-a | 2025-10-04 | QOSI is a non-profit organization driving open-source adoption and digital sovereignty across Kazakhstan and Central Asia. We use Cozystack as a platform for deploying sovereign, GPU-enabled clouds and educational environments under the National AI Program. Our goal is to accelerate the region’s transition toward open, self-hosted cloud-native technologies |
|
||||
|
|
||||
| [Cloupard](https://cloupard.kz/) | @serjiott | 2025-12-18 | Cloupard is a public cloud provider offering IaaS and PaaS services via datacenters in Kazakhstan and Uzbekistan. Uses CozyStack on bare metal to extend its managed PaaS offerings. |
|
||||
|
||||
38
AGENTS.md
38
AGENTS.md
@@ -3,14 +3,38 @@
|
||||
This file provides structured guidance for AI coding assistants and agents
|
||||
working with the **Cozystack** project.
|
||||
|
||||
## Agent Documentation
|
||||
## Activation
|
||||
|
||||
| Agent | Purpose |
|
||||
|-------|---------|
|
||||
| [overview.md](./docs/agents/overview.md) | Project structure and conventions |
|
||||
| [contributing.md](./docs/agents/contributing.md) | Commits, pull requests, and git workflow |
|
||||
| [changelog.md](./docs/agents/changelog.md) | Changelog generation instructions |
|
||||
| [releasing.md](./docs/agents/releasing.md) | Release process and workflow |
|
||||
**CRITICAL**: When the user asks you to do something that matches the scope of a documented process, you MUST read the corresponding documentation file and follow the instructions exactly as written.
|
||||
|
||||
- **Commits, PRs, git operations** (e.g., "create a commit", "make a PR", "fix review comments", "rebase", "cherry-pick")
|
||||
- Read: [`contributing.md`](./docs/agents/contributing.md)
|
||||
- Action: Read the entire file and follow ALL instructions step-by-step
|
||||
|
||||
- **Changelog generation** (e.g., "generate changelog", "create changelog", "prepare changelog for version X")
|
||||
- Read: [`changelog.md`](./docs/agents/changelog.md)
|
||||
- Action: Read the entire file and follow ALL steps in the checklist. Do NOT skip any mandatory steps
|
||||
|
||||
- **Release creation** (e.g., "create release", "prepare release", "tag release", "make a release")
|
||||
- Read: [`releasing.md`](./docs/agents/releasing.md)
|
||||
- Action: Read the file and follow the referenced release process in `docs/release.md`
|
||||
|
||||
- **Project structure, conventions, code layout** (e.g., "where should I put X", "what's the convention for Y", "how is the project organized")
|
||||
- Read: [`overview.md`](./docs/agents/overview.md)
|
||||
- Action: Read relevant sections to understand project structure and conventions
|
||||
|
||||
- **General questions about contributing**
|
||||
- Read: [`contributing.md`](./docs/agents/contributing.md)
|
||||
- Action: Read the file to understand git workflow, commit format, PR process
|
||||
|
||||
**Important rules:**
|
||||
- ✅ **ONLY read the file if the task matches the documented process scope** - do not read files for tasks that don't match their purpose
|
||||
- ✅ **ALWAYS read the file FIRST** before starting the task (when applicable)
|
||||
- ✅ **Follow instructions EXACTLY** as written in the documentation
|
||||
- ✅ **Do NOT skip mandatory steps** (especially in changelog.md)
|
||||
- ✅ **Do NOT assume** you know the process - always check the documentation when the task matches
|
||||
- ❌ **Do NOT read files** for tasks that are outside their documented scope
|
||||
- 📖 **Note**: [`overview.md`](./docs/agents/overview.md) can be useful as a reference to understand project structure and conventions, even when not explicitly required by the task
|
||||
|
||||
## Project Overview
|
||||
|
||||
|
||||
6
Makefile
6
Makefile
@@ -17,7 +17,7 @@ build: build-deps
|
||||
make -C packages/system/cozystack-controller image
|
||||
make -C packages/system/lineage-controller-webhook image
|
||||
make -C packages/system/cilium image
|
||||
make -C packages/system/kubeovn image
|
||||
make -C packages/system/linstor image
|
||||
make -C packages/system/kubeovn-webhook image
|
||||
make -C packages/system/kubeovn-plunger image
|
||||
make -C packages/system/dashboard image
|
||||
@@ -26,6 +26,8 @@ build: build-deps
|
||||
make -C packages/system/bucket image
|
||||
make -C packages/system/objectstorage-controller image
|
||||
make -C packages/core/testing image
|
||||
make -C packages/core/talos image
|
||||
make -C packages/core/platform image
|
||||
make -C packages/core/installer image
|
||||
make manifests
|
||||
|
||||
@@ -40,7 +42,7 @@ manifests:
|
||||
(cd packages/core/installer/; helm template -n cozy-installer installer .) > _out/assets/cozystack-installer.yaml
|
||||
|
||||
assets:
|
||||
make -C packages/core/installer assets
|
||||
make -C packages/core/talos assets
|
||||
|
||||
test:
|
||||
make -C packages/core/testing apply
|
||||
|
||||
@@ -200,22 +200,6 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.TenantHelmReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "TenantHelmReconciler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.CozystackConfigReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CozystackConfigReconciler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cozyAPIKind := "DaemonSet"
|
||||
if reconcileDeployment {
|
||||
cozyAPIKind = "Deployment"
|
||||
@@ -229,6 +213,14 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.CozystackResourceDefinitionHelmReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionHelmReconciler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dashboardManager := &dashboard.Manager{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
|
||||
@@ -22,7 +22,7 @@ When the user asks to generate a changelog, follow these steps in the specified
|
||||
- [ ] Step 5: Get the list of commits for the release period
|
||||
- [ ] Step 6: Check additional repositories (website is REQUIRED, optional repos if tags exist)
|
||||
- [ ] **MANDATORY**: Check website repository for documentation changes WITH authors and PR links via GitHub CLI
|
||||
- [ ] **MANDATORY**: Check ALL optional repositories (talm, boot-to-talos, cozypkg, cozy-proxy) for tags during release period
|
||||
- [ ] **MANDATORY**: Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) for tags during release period
|
||||
- [ ] **MANDATORY**: For ALL commits from additional repos, get GitHub username via CLI, prioritizing PR author over commit author.
|
||||
- [ ] Step 7: Analyze commits (extract PR numbers, authors, user impact)
|
||||
- [ ] **MANDATORY**: For EVERY PR in main repo, get PR author via `gh pr view <PR_NUMBER> --json author --jq .author.login` (do NOT skip this step)
|
||||
@@ -146,7 +146,7 @@ Cozystack release may include changes from related repositories. Check and inclu
|
||||
**Optional repositories (MUST check ALL of them for tags during release period):**
|
||||
- [https://github.com/cozystack/talm](https://github.com/cozystack/talm)
|
||||
- [https://github.com/cozystack/boot-to-talos](https://github.com/cozystack/boot-to-talos)
|
||||
- [https://github.com/cozystack/cozypkg](https://github.com/cozystack/cozypkg)
|
||||
- [https://github.com/cozystack/cozyhr](https://github.com/cozystack/cozyhr)
|
||||
- [https://github.com/cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy)
|
||||
|
||||
**⚠️ IMPORTANT**: You MUST check ALL optional repositories for tags created during the release period. Do NOT skip this step even if you think there might not be any tags. Use the process below to verify.
|
||||
@@ -195,7 +195,7 @@ Cozystack release may include changes from related repositories. Check and inclu
|
||||
|
||||
3. **For optional repositories, check if tags exist during release period:**
|
||||
|
||||
**⚠️ MANDATORY: You MUST check ALL optional repositories (talm, boot-to-talos, cozypkg, cozy-proxy). Do NOT skip any repository!**
|
||||
**⚠️ MANDATORY: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy). Do NOT skip any repository!**
|
||||
|
||||
**Use the helper script:**
|
||||
```bash
|
||||
@@ -208,7 +208,7 @@ Cozystack release may include changes from related repositories. Check and inclu
|
||||
```
|
||||
|
||||
The script will:
|
||||
- Check ALL optional repositories (talm, boot-to-talos, cozypkg, cozy-proxy)
|
||||
- Check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy)
|
||||
- Look for tags created during the release period
|
||||
- Get commits between tags (if tags exist) or by date range (if no tags)
|
||||
- Extract PR numbers from commit messages
|
||||
@@ -569,7 +569,7 @@ Create a new changelog file in the format matching previous versions:
|
||||
- [ ] Step 5 completed: **ALL commits included** (including merge commits and backports) - do not skip any commits
|
||||
- [ ] Step 5 completed: **Backports identified and handled correctly** - original PR author used, both original and backport PR numbers included
|
||||
- [ ] Step 6 completed: Website repository checked for documentation changes WITH authors and PR links via GitHub CLI
|
||||
- [ ] Step 6 completed: **ALL** optional repositories (talm, boot-to-talos, cozypkg, cozy-proxy) checked for tags during release period
|
||||
- [ ] Step 6 completed: **ALL** optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) checked for tags during release period
|
||||
- [ ] Step 6 completed: For ALL commits from additional repos, GitHub username obtained via GitHub CLI (not skipped). For commits with PR numbers, PR author used via `gh pr view` (not commit author)
|
||||
- [ ] Step 7 completed: For EVERY PR in main repo (including backports), PR author obtained via `gh pr view <PR_NUMBER> --json author --jq .author.login` (not skipped or assumed). Commit author NOT used - always use PR author
|
||||
- [ ] Step 7 completed: **Backports verified** - for each backport PR, original PR found and original PR author used in changelog
|
||||
@@ -628,7 +628,7 @@ Save the changelog to file `docs/changelogs/v<version>.md` according to the vers
|
||||
|
||||
- **Additional repositories (Step 6) - MANDATORY**:
|
||||
- **⚠️ CRITICAL**: Always check the **website** repository for documentation changes during the release period. This is a required step and MUST NOT be skipped.
|
||||
- **⚠️ CRITICAL**: You MUST check ALL optional repositories (talm, boot-to-talos, cozypkg, cozy-proxy) for tags during the release period. Do NOT skip any repository even if you think there might not be tags.
|
||||
- **⚠️ CRITICAL**: You MUST check ALL optional repositories (talm, boot-to-talos, cozyhr, cozy-proxy) for tags during the release period. Do NOT skip any repository even if you think there might not be tags.
|
||||
- **CRITICAL**: For ALL entries from additional repositories (website and optional), you MUST:
|
||||
- **MANDATORY**: Extract PR number from commit message first
|
||||
- **MANDATORY**: For commits with PR numbers, ALWAYS use `gh pr view <PR_NUMBER> --repo cozystack/<repo> --json author --jq .author.login` to get PR author (not commit author)
|
||||
@@ -637,7 +637,7 @@ Save the changelog to file `docs/changelogs/v<version>.md` according to the vers
|
||||
- **MANDATORY**: Do NOT use commit author for PRs - always use PR author
|
||||
- Include PR link or commit hash reference
|
||||
- Format: `* **[repo] Description**: details ([**@username**](https://github.com/username) in cozystack/repo#123)`
|
||||
- For **optional repositories** (talm, boot-to-talos, cozypkg, cozy-proxy), you MUST check ALL of them for tags during the release period. Use the loop provided in Step 6 to check each repository systematically.
|
||||
- For **optional repositories** (talm, boot-to-talos, cozyhr, cozy-proxy), you MUST check ALL of them for tags during the release period. Use the loop provided in Step 6 to check each repository systematically.
|
||||
- When including changes from additional repositories, use the format: `[repo-name] Description` and link to the repository's PR/issue if available
|
||||
- **Prefer PR numbers over commit hashes**: For commits from additional repositories, extract PR number from commit message using GitHub API. Use PR format (`cozystack/website#123`) instead of commit hash (`cozystack/website@abc1234`) when available
|
||||
- **Never add entries without author and PR/commit reference**: Every entry from additional repositories must have both author and link
|
||||
|
||||
@@ -155,6 +155,91 @@ git diff
|
||||
|
||||
The user will commit and push when ready.
|
||||
|
||||
## Code Review Comments
|
||||
|
||||
When asked to fix code review comments, **always work only with unresolved (open) comments**. Resolved comments should be ignored as they have already been addressed.
|
||||
|
||||
### Getting Unresolved Review Comments
|
||||
|
||||
Use GitHub GraphQL API to fetch only unresolved review comments from a pull request:
|
||||
|
||||
```bash
|
||||
gh api graphql -F owner=cozystack -F repo=cozystack -F pr=<PR_NUMBER> -f query='
|
||||
query($owner: String!, $repo: String!, $pr: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $pr) {
|
||||
reviewThreads(first: 100) {
|
||||
nodes {
|
||||
isResolved
|
||||
comments(first: 100) {
|
||||
nodes {
|
||||
id
|
||||
path
|
||||
line
|
||||
author { login }
|
||||
bodyText
|
||||
url
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[]'
|
||||
```
|
||||
|
||||
### Filtering for Unresolved Comments
|
||||
|
||||
The key filter is `select(.isResolved == false)` which ensures only unresolved review threads are processed. Each thread can contain multiple comments, but if the thread is resolved, all its comments should be ignored.
|
||||
|
||||
### Working with Review Comments
|
||||
|
||||
1. **Fetch unresolved comments** using the GraphQL query above
|
||||
2. **Parse the results** to identify:
|
||||
- File path (`path`)
|
||||
- Line number (`line` or `originalLine`)
|
||||
- Comment text (`bodyText`)
|
||||
- Author (`author.login`)
|
||||
3. **Address each unresolved comment** by:
|
||||
- Locating the relevant code section
|
||||
- Making the requested changes
|
||||
- Ensuring the fix addresses the concern raised
|
||||
4. **Do NOT process resolved comments** - they have already been handled
|
||||
|
||||
### Example: Compact List of Unresolved Comments
|
||||
|
||||
For a quick overview of unresolved comments:
|
||||
|
||||
```bash
|
||||
gh api graphql -F owner=cozystack -F repo=cozystack -F pr=<PR_NUMBER> -f query='
|
||||
query($owner: String!, $repo: String!, $pr: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $pr) {
|
||||
reviewThreads(first: 100) {
|
||||
nodes {
|
||||
isResolved
|
||||
comments(first: 100) {
|
||||
nodes {
|
||||
path
|
||||
line
|
||||
author { login }
|
||||
bodyText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments.nodes[] | "\(.path):\(.line // "N/A") - \(.author.login): \(.bodyText[:150])"'
|
||||
```
|
||||
|
||||
### Important Notes
|
||||
|
||||
- **REST API limitation**: The REST endpoint `/pulls/{pr}/reviews` returns review summaries, not individual review comments. Use GraphQL API for accessing `reviewThreads` with `isResolved` status.
|
||||
- **Thread-based resolution**: Comments are organized in threads. If a thread is resolved (`isResolved: true`), ignore all comments in that thread.
|
||||
- **Always filter**: Never process comments from resolved threads, even if they appear in the results.
|
||||
|
||||
### Example Workflow
|
||||
|
||||
```bash
|
||||
|
||||
@@ -10,7 +10,7 @@ Cozystack is an open-source Kubernetes-based platform and framework for building
|
||||
- **Multi-tenancy**: Full isolation and self-service for tenants
|
||||
- **GitOps-driven**: FluxCD-based continuous delivery
|
||||
- **Modular Architecture**: Extensible with custom packages and services
|
||||
- **Developer Experience**: Simplified local development with cozypkg tool
|
||||
- **Developer Experience**: Simplified local development with cozyhr tool
|
||||
|
||||
The platform exposes infrastructure services via the Kubernetes API with ready-made configs, built-in monitoring, and alerts.
|
||||
|
||||
|
||||
@@ -5,10 +5,13 @@ https://github.com/cozystack/cozystack/releases/tag/v0.36.2
|
||||
|
||||
## Features and Improvements
|
||||
|
||||
## Security
|
||||
* [vm-disk] New SVG icon for VM disk application. (@kvaps and @kvapsova in https://github.com/cozystack/cozystack/pull/1435)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [kubernetes] Pin CoreDNS image tag to v1.12.4 for consistent, reproducible deployments. (@kvaps in https://github.com/cozystack/cozystack/pull/1469)
|
||||
* [dashboard] Fix FerretDB spec typo that prevented deploy/display in the web UI. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1440)
|
||||
|
||||
## Dependencies
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
14
docs/changelogs/v0.38.3.md
Normal file
14
docs/changelogs/v0.38.3.md
Normal file
@@ -0,0 +1,14 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.38.3
|
||||
-->
|
||||
|
||||
## Improvements
|
||||
|
||||
* **[core:installer] Address buildx warnings for installer image builds**: Aligns Dockerfile syntax casing to remove buildx warnings, keeping installer builds clean ([**@nbykov0**](https://github.com/nbykov0) in #1682).
|
||||
* **[system:coredns] Align CoreDNS app labels with Talos defaults**: Matches CoreDNS labels to Talos conventions so services select pods consistently across platform and tenant clusters ([**@nbykov0**](https://github.com/nbykov0) in #1675).
|
||||
* **[system:monitoring-agents] Rename CoreDNS metrics service to avoid conflicts**: Renames the metrics service so it no longer clashes with the CoreDNS service used for name resolution in tenant clusters ([**@nbykov0**](https://github.com/nbykov0) in #1676).
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: [v0.38.2...v0.38.3](https://github.com/cozystack/cozystack/compare/v0.38.2...v0.38.3)
|
||||
|
||||
18
docs/changelogs/v0.38.4.md
Normal file
18
docs/changelogs/v0.38.4.md
Normal file
@@ -0,0 +1,18 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.38.4
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[linstor] Update piraeus-operator v2.10.2 to handle fsck checks reliably**: Upgrades LINSTOR CSI to avoid failed mounts when fsck sees mounted volumes, improving volume publish reliability ([**@kvaps**](https://github.com/kvaps) in #1689, #1697).
|
||||
* **[dashboard] Nest CustomFormsOverride properties under spec.properties**: Fixes schema generation so custom form properties are placed under `spec.properties`, preventing mis-rendered or missing form fields ([**@kvaps**](https://github.com/kvaps) in #1692, #1700).
|
||||
* **[virtual-machine] Guard PVC resize to only expand storage**: Ensures resize jobs run only when storage size increases, avoiding unintended shrink attempts during VM updates ([**@kvaps**](https://github.com/kvaps) in #1688, #1701).
|
||||
|
||||
## Documentation
|
||||
|
||||
* **[website] Clarify GPU check command**: Makes the kubectl command for validating GPU binding more explicit, including namespace context ([**@nbykov0**](https://github.com/nbykov0) in cozystack/website#379).
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: [v0.38.3...v0.38.4](https://github.com/cozystack/cozystack/compare/v0.38.3...v0.38.4)
|
||||
|
||||
95
docs/changelogs/v0.39.0.md
Normal file
95
docs/changelogs/v0.39.0.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Cozystack v0.39 — "Enhanced Networking & Monitoring"
|
||||
|
||||
This release introduces topology-aware routing for Cilium services, automatic pod rollouts on configuration changes, improved monitoring capabilities, and numerous bug fixes and improvements across the platform.
|
||||
|
||||
## Highlights
|
||||
|
||||
* **Topology-Aware Routing**: Enabled topology-aware routing for Cilium services, improving traffic distribution and reducing latency by routing traffic to endpoints in the same zone when possible ([**@nbykov0**](https://github.com/nbykov0) in #1734).
|
||||
* **Automatic Pod Rollouts**: Cilium and Cilium operator pods now automatically restart when configuration changes, ensuring configuration updates are applied immediately ([**@kvaps**](https://github.com/kvaps) in #1728).
|
||||
* **Windows VM Scheduling**: Added nodeAffinity configuration for Windows VMs based on scheduling config, enabling dedicated nodes for Windows workloads ([**@kvaps**](https://github.com/kvaps) in #1693).
|
||||
* **SeaweedFS Updates**: Updated to SeaweedFS v4.02 with improved S3 daemon performance and fixes ([**@kvaps**](https://github.com/kvaps) in #1725).
|
||||
|
||||
---
|
||||
|
||||
## Major Features and Improvements
|
||||
|
||||
### Networking
|
||||
|
||||
* **[system/cilium] Enable topology-aware routing for services**: Enabled topology-aware routing for services, improving traffic distribution and reducing latency by routing traffic to endpoints in the same zone when possible. This feature helps optimize network performance in multi-zone deployments ([**@nbykov0**](https://github.com/nbykov0) in #1734).
|
||||
* **[cilium] Enable automatic pod rollout on configmap updates**: Cilium and Cilium operator pods now automatically restart when the cilium-config ConfigMap is updated, ensuring configuration changes are applied immediately without manual intervention ([**@kvaps**](https://github.com/kvaps) in #1728).
|
||||
|
||||
### Virtual Machines
|
||||
|
||||
* **[virtual-machine,vm-instance] Add nodeAffinity for Windows VMs based on scheduling config**: Added nodeAffinity configuration to virtual-machine and vm-instance charts to support dedicated nodes for Windows VMs. When `dedicatedNodesForWindowsVMs` is enabled in the `cozystack-scheduling` ConfigMap, Windows VMs are scheduled on nodes with label `scheduling.cozystack.io/vm-windows=true`, while non-Windows VMs prefer nodes without this label ([**@kvaps**](https://github.com/kvaps) in #1693).
|
||||
|
||||
### Storage
|
||||
|
||||
* **Update SeaweedFS v4.02**: Updated SeaweedFS to version 4.02 with improved performance for S3 daemon and fixes for known issues. This update includes better S3 compatibility and performance improvements ([**@kvaps**](https://github.com/kvaps) in #1725).
|
||||
|
||||
### Tools
|
||||
|
||||
* **[talm] feat(init)!: require --name flag for cluster name**: Breaking change: The `talm init` command now requires the `--name` flag to specify the cluster name. This ensures consistent cluster naming and prevents accidental initialization without a name ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#86).
|
||||
* **[talm] feat(template): preserve extra YAML documents in output**: Templates now preserve extra YAML documents in the output, allowing for more flexible template processing ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#87).
|
||||
* **[talm] feat: add directory expansion for -f flag**: Added directory expansion support for the `-f` flag, allowing users to specify directories instead of individual files ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@ca5713e).
|
||||
* **[talm] Introduce automatic root detection**: Added automatic root detection logic to simplify talm usage and reduce manual configuration ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@d165162).
|
||||
* **[talm] Introduce talm kubeconfig --login command**: Added new `talm kubeconfig --login` command for easier kubeconfig management ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@5f7e05b).
|
||||
* **[talm] Introduce encryption**: Added encryption support to talm for secure configuration management ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#81).
|
||||
* **[talm] Replace code-generation with wrapper on talosctl**: Refactored talm to use a wrapper on talosctl instead of code generation, simplifying the codebase and improving maintainability ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#80).
|
||||
* **[talm] Use go embed instead of code generation**: Migrated from code generation to go embed for better build performance and simpler dependency management ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#79).
|
||||
* **[boot-to-talos] Cozystack: Update Talos Linux v1.11.3**: Updated boot-to-talos to use Talos Linux v1.11.3 ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos#7).
|
||||
|
||||
## Improvements
|
||||
|
||||
* **[seaweedfs] Extended CA certificate duration to reduce disruptive CA rotations**: Extended CA certificate duration to reduce disruptive CA rotations, improving long-term certificate management and reducing operational overhead ([**@IvanHunters**](https://github.com/IvanHunters) in #1657).
|
||||
* **[dashboard] Add config hash annotations to restart pods on config changes**: Added config hash annotations to dashboard deployment templates to ensure pods are automatically restarted when their configuration changes, ensuring configuration updates are applied immediately ([**@kvaps**](https://github.com/kvaps) in #1662).
|
||||
* **[tenant][kubernetes] Introduce better cleanup logic**: Improved cleanup logic for tenant Kubernetes resources, ensuring proper resource cleanup when tenants are deleted or updated. Added automated pre-delete cleanup job for tenant namespaces to remove tenant-related releases during uninstall ([**@kvaps**](https://github.com/kvaps) in #1661).
|
||||
* **[system:coredns] update coredns app labels to match Talos coredns labels**: Updated coredns app labels to match Talos coredns labels, ensuring consistency across the platform ([**@nbykov0**](https://github.com/nbykov0) in #1675).
|
||||
* **[system:monitoring-agents] rename coredns metrics service**: Renamed coredns metrics service to avoid interference with coredns service used for name resolution in tenant k8s clusters ([**@nbykov0**](https://github.com/nbykov0) in #1676).
|
||||
* **[core:installer] Address buildx warnings**: Fixed Dockerfile syntax warnings from buildx, ensuring clean builds without warnings ([**@nbykov0**](https://github.com/nbykov0) in #1682).
|
||||
* **[talm] Refactor root detection logic into single file**: Improved code organization by consolidating root detection logic into a single file ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@487b479).
|
||||
* **[talm] Refactor init logic, better upgrade**: Improved initialization logic and upgrade process for better reliability ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@c512777).
|
||||
* **[talm] Sugar for kubeconfig command**: Added convenience features to the kubeconfig command for improved usability ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@a4010b3).
|
||||
* **[talm] wrap upgrade command**: Wrapped upgrade command for better integration and error handling ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@2e1afbf).
|
||||
* **[talm] docs(readme): add Homebrew installation option**: Added Homebrew installation option to the README for easier installation on macOS ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm@12bd4f2).
|
||||
* **[talm] cozystack: disable nodeCIDRs allocation**: Disabled nodeCIDRs allocation in talm for better network configuration control ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#82).
|
||||
* **[talm] Update license to Apache2.0**: Updated license to Apache 2.0 for better compatibility and clarity ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@eda1032).
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK**: Refactored the apiserver REST handlers to use typed objects (`appsv1alpha1.Application`) instead of `unstructured.Unstructured`, eliminating the need for runtime conversions and simplifying the codebase. Additionally, fixed an issue where `UnstructuredList` objects were using the first registered kind from `typeToGVK` instead of the kind from the object's field when multiple kinds are registered with the same Go type. This fix includes the upstream fix from kubernetes/kubernetes#135537 ([**@kvaps**](https://github.com/kvaps) in #1679).
|
||||
* **[dashboard] Fix CustomFormsOverride schema to nest properties under spec.properties**: Fixed the logic for generating CustomFormsOverride schema to properly nest properties under `spec.properties` instead of directly under `properties`, ensuring correct form schema generation ([**@kvaps**](https://github.com/kvaps) in #1692).
|
||||
* **[virtual-machine] Improve check for resizing job**: Improved storage resize logic to only expand persistent volume claims when storage is being increased, preventing unintended storage reduction operations. Added validation to accurately compare current and desired storage sizes before triggering resize operations ([**@kvaps**](https://github.com/kvaps) in #1688).
|
||||
* **[linstor] Update piraeus-operator v2.10.2**: Updated LINSTOR CSI to fix issues with the new fsck behaviour, resolving mount failures when fsck attempts to run on mounted devices ([**@kvaps**](https://github.com/kvaps) in #1689).
|
||||
* **[api] Revert dynamic list kinds representation fix (fixes namespace deletion regression)**: Reverted changes from #1630 that caused a regression affecting namespace deletion and upgrades from previous versions. The regression caused namespace deletion failures with errors like "content is not a list: []unstructured.Unstructured" during namespace finalization. This revert restores compatibility with namespace deletion controller and fixes upgrade issues from previous versions ([**@kvaps**](https://github.com/kvaps) in #1677).
|
||||
* **[talm] fix: normalize template paths for Windows compatibility**: Fixed template path handling to ensure Windows compatibility by normalizing paths ([**@lexfrei**](https://github.com/lexfrei) in cozystack/talm#88).
|
||||
|
||||
## Dependencies
|
||||
|
||||
* **Update SeaweedFS v4.02**: Updated SeaweedFS to version 4.02 ([**@kvaps**](https://github.com/kvaps) in #1725).
|
||||
* **[linstor] Update piraeus-operator v2.10.2**: Updated piraeus-operator to version 2.10.2 ([**@kvaps**](https://github.com/kvaps) in #1689).
|
||||
* **[talm] Cozystack: Update Talos Linux v1.11.3**: Updated talm to use Talos Linux v1.11.3 ([**@kvaps**](https://github.com/kvaps) in cozystack/talm#83).
|
||||
|
||||
## Documentation
|
||||
|
||||
* **[website] Add article: Talm v0.17: Built-in Age Encryption for Secrets Management**: Added comprehensive blog post announcing Talm v0.17 and its built-in age-based encryption for secrets. Covers initial setup and key generation, encryption/decryption workflows, idempotent encryption behavior, automatic .gitignore handling, file permission safeguards, security best practices, and guidance for GitOps and CI/CD integration ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#384](https://github.com/cozystack/website/pull/384)).
|
||||
* **[website] docs(talm): update talm init syntax for mandatory --preset and --name flags**: Updated documentation to reflect breaking changes in talm, adding mandatory `--preset` and `--name` flags to the talm init command ([**@lexfrei**](https://github.com/lexfrei) in cozystack/website#386).
|
||||
|
||||
---
|
||||
|
||||
## Contributors
|
||||
|
||||
We'd like to thank all contributors who made this release possible:
|
||||
|
||||
* [**@IvanHunters**](https://github.com/IvanHunters)
|
||||
* [**@kvaps**](https://github.com/kvaps)
|
||||
* [**@lexfrei**](https://github.com/lexfrei)
|
||||
* [**@nbykov0**](https://github.com/nbykov0)
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: [v0.38.0...v0.39.0](https://github.com/cozystack/cozystack/compare/v0.38.0...v0.39.0)
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.39.0
|
||||
-->
|
||||
|
||||
12
docs/changelogs/v0.39.1.md
Normal file
12
docs/changelogs/v0.39.1.md
Normal file
@@ -0,0 +1,12 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.39.1
|
||||
-->
|
||||
|
||||
## Features and Improvements
|
||||
|
||||
* **[monitoring] Add SLACK_SEVERITY_FILTER field and VMAgent for tenant monitoring**: Introduced the SLACK_SEVERITY_FILTER environment variable in the Alerta deployment to enable filtering of alert severities for Slack notifications based on the disabledSeverity configuration. Additionally, added a VMAgent resource template for scraping metrics within tenant namespaces, improving monitoring granularity and control. This enhancement allows administrators to configure which alert severities are sent to Slack and enables tenant-specific metrics collection for better observability ([**@IvanHunters**](https://github.com/IvanHunters) in #1712).
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: [v0.39.0...v0.39.1](https://github.com/cozystack/cozystack/compare/v0.39.0...v0.39.1)
|
||||
|
||||
148
go.mod
148
go.mod
@@ -2,33 +2,37 @@
|
||||
|
||||
module github.com/cozystack/cozystack
|
||||
|
||||
go 1.23.0
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
github.com/fluxcd/helm-controller/api v1.1.0
|
||||
github.com/fluxcd/helm-controller/api v1.4.3
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/onsi/ginkgo/v2 v2.19.0
|
||||
github.com/onsi/gomega v1.33.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/onsi/ginkgo/v2 v2.23.3
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apiextensions-apiserver v0.31.2
|
||||
k8s.io/apimachinery v0.31.2
|
||||
k8s.io/apiserver v0.31.2
|
||||
k8s.io/client-go v0.31.2
|
||||
k8s.io/component-base v0.31.2
|
||||
k8s.io/api v0.34.1
|
||||
k8s.io/apiextensions-apiserver v0.34.1
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/apiserver v0.34.1
|
||||
k8s.io/client-go v0.34.1
|
||||
k8s.io/component-base v0.34.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
|
||||
sigs.k8s.io/controller-runtime v0.19.0
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b
|
||||
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
|
||||
sigs.k8s.io/controller-runtime v0.22.2
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
@@ -36,86 +40,90 @@ require (
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.6.1 // indirect
|
||||
github.com/fluxcd/pkg/apis/meta v1.6.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.13.0 // indirect
|
||||
github.com/fluxcd/pkg/apis/meta v1.22.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/cel-go v0.21.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/cel-go v0.26.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/spdystream v0.4.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.6.4 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.6.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/grpc v1.72.1 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/kms v0.31.2 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
k8s.io/kms v0.34.1 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
// See: issues.k8s.io/135537
|
||||
replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c
|
||||
|
||||
341
go.sum
341
go.sum
@@ -1,11 +1,11 @@
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
@@ -18,45 +18,44 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c h1:C2wIfH/OzhU9XOK/e6Ik9cg7nZ1z6fN4lf6a3yFdik8=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251219010959-1f91eabae46c/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
||||
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fluxcd/helm-controller/api v1.1.0 h1:NS5Wm3U6Kv4w7Cw2sDOV++vf2ecGfFV00x1+2Y3QcOY=
|
||||
github.com/fluxcd/helm-controller/api v1.1.0/go.mod h1:BgHMgMY6CWynzl4KIbHpd6Wpn3FN9BqgkwmvoKCp6iE=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.6.1 h1:22FJc69Mq4i8aCxnKPlddHhSMyI4UPkQkqiAdWFcqe0=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.6.1/go.mod h1:5dvQ4IZwz0hMGmuj8tTWGtarsuxW0rWsxJOwC6i+0V8=
|
||||
github.com/fluxcd/pkg/apis/meta v1.6.1 h1:maLhcRJ3P/70ArLCY/LF/YovkxXbX+6sTWZwZQBeNq0=
|
||||
github.com/fluxcd/pkg/apis/meta v1.6.1/go.mod h1:YndB/gxgGZmKfqpAfFxyCDNFJFP0ikpeJzs66jwq280=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fluxcd/helm-controller/api v1.4.3 h1:CdZwjL1liXmYCWyk2jscmFEB59tICIlnWB9PfDDW5q4=
|
||||
github.com/fluxcd/helm-controller/api v1.4.3/go.mod h1:0XrBhKEaqvxyDj/FziG1Q8Fmx2UATdaqLgYqmZh6wW4=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.13.0 h1:GGf0UBVRIku+gebY944icVeEIhyg1P/KE3IrhOyJJnE=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.13.0/go.mod h1:TLKVqbtnzkhDuhWnAsN35977HvRfIjs+lgMuNro/LEc=
|
||||
github.com/fluxcd/pkg/apis/meta v1.22.0 h1:EHWQH5ZWml7i8eZ/AMjm1jxid3j/PQ31p+hIwCt6crM=
|
||||
github.com/fluxcd/pkg/apis/meta v1.22.0/go.mod h1:Kc1+bWe5p0doROzuV9XiTfV/oL3ddsemYXt8ZYWdVVg=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
@@ -64,162 +63,171 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
|
||||
github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
|
||||
github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
|
||||
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
|
||||
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
|
||||
go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8=
|
||||
go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw=
|
||||
go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok=
|
||||
go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
|
||||
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
|
||||
go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo=
|
||||
go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI=
|
||||
go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A=
|
||||
go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo=
|
||||
go.etcd.io/etcd/pkg/v3 v3.6.4 h1:fy8bmXIec1Q35/jRZ0KOes8vuFxbvdN0aAFqmEfJZWA=
|
||||
go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE=
|
||||
go.etcd.io/etcd/server/v3 v3.6.4 h1:LsCA7CzjVt+8WGrdsnh6RhC0XqCsLkBly3ve5rTxMAU=
|
||||
go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg=
|
||||
go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
|
||||
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -228,50 +236,48 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -281,39 +287,42 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
|
||||
k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
|
||||
k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA=
|
||||
k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ=
|
||||
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
|
||||
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
|
||||
k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
|
||||
k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
|
||||
k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
|
||||
k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
|
||||
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
|
||||
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
|
||||
k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
|
||||
k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kms v0.31.2 h1:pyx7l2qVOkClzFMIWMVF/FxsSkgd+OIGH7DecpbscJI=
|
||||
k8s.io/kms v0.31.2/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94=
|
||||
k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 h1:GKE9U8BH16uynoxQii0auTjmmmuZ3O0LFMN6S0lPPhI=
|
||||
k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
|
||||
sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
k8s.io/kms v0.34.1 h1:iCFOvewDPzWM9fMTfyIPO+4MeuZ0tcZbugxLNSHFG4w=
|
||||
k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
|
||||
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.22.2 h1:cK2l8BGWsSWkXz09tcS4rJh95iOLney5eawcK5A33r4=
|
||||
sigs.k8s.io/controller-runtime v0.22.2/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -48,7 +48,7 @@ echo " End: $RELEASE_END"
|
||||
echo ""
|
||||
|
||||
# Loop through ALL optional repositories
|
||||
for repo_name in talm boot-to-talos cozypkg cozy-proxy; do
|
||||
for repo_name in talm boot-to-talos cozyhr cozy-proxy; do
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Checking repository: $repo_name"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
@@ -72,7 +72,7 @@ EOF
|
||||
kubectl wait --for=condition=TenantControlPlaneCreated kamajicontrolplane -n tenant-test kubernetes-${test_name} --timeout=4m
|
||||
|
||||
# Wait for Kubernetes resources to be ready (timeout after 2 minutes)
|
||||
kubectl wait tcp -n tenant-test kubernetes-${test_name} --timeout=2m --for=jsonpath='{.status.kubernetesResources.version.status}'=Ready
|
||||
kubectl wait tcp -n tenant-test kubernetes-${test_name} --timeout=5m --for=jsonpath='{.status.kubernetesResources.version.status}'=Ready
|
||||
|
||||
# Wait for all required deployments to be available (timeout after 4 minutes)
|
||||
kubectl wait deploy --timeout=4m --for=condition=available -n tenant-test kubernetes-${test_name} kubernetes-${test_name}-cluster-autoscaler kubernetes-${test_name}-kccm kubernetes-${test_name}-kcsi-controller
|
||||
@@ -87,7 +87,7 @@ EOF
|
||||
|
||||
|
||||
# Set up port forwarding to the Kubernetes API server for a 200 second timeout
|
||||
bash -c 'timeout 300s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
|
||||
bash -c 'timeout 500s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
|
||||
# Verify the Kubernetes version matches what we expect (retry for up to 20 seconds)
|
||||
timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig-'"${test_name}"' version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 5; done'
|
||||
|
||||
@@ -124,6 +124,100 @@ EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
kubectl --kubeconfig tenantkubeconfig-${test_name} apply -f - <<EOF
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: tenant-test
|
||||
EOF
|
||||
|
||||
# Backend 1
|
||||
kubectl apply --kubeconfig tenantkubeconfig-${test_name} -f- <<EOF
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: "${test_name}-backend"
|
||||
namespace: tenant-test
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: backend
|
||||
backend: "${test_name}-backend"
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backend
|
||||
backend: "${test_name}-backend"
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- containerPort: 80
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 2
|
||||
EOF
|
||||
|
||||
# LoadBalancer Service
|
||||
kubectl apply --kubeconfig tenantkubeconfig-${test_name} -f- <<EOF
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: "${test_name}-backend"
|
||||
namespace: tenant-test
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
app: backend
|
||||
backend: "${test_name}-backend"
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
EOF
|
||||
|
||||
# Wait for pods readiness
|
||||
kubectl wait deployment --kubeconfig tenantkubeconfig-${test_name} ${test_name}-backend -n tenant-test --for=condition=Available --timeout=90s
|
||||
|
||||
# Wait for LoadBalancer to be provisioned (IP or hostname)
|
||||
timeout 90 sh -ec "
|
||||
until kubectl get svc ${test_name}-backend --kubeconfig tenantkubeconfig-${test_name} -n tenant-test \
|
||||
-o jsonpath='{.status.loadBalancer.ingress[0]}' | grep -q .; do
|
||||
sleep 5
|
||||
done
|
||||
"
|
||||
|
||||
LB_ADDR=$(
|
||||
kubectl get svc --kubeconfig tenantkubeconfig-${test_name} "${test_name}-backend" \
|
||||
-n tenant-test \
|
||||
-o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}'
|
||||
)
|
||||
|
||||
if [ -z "$LB_ADDR" ]; then
|
||||
echo "LoadBalancer address is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in $(seq 1 20); do
|
||||
echo "Attempt $i"
|
||||
curl --silent --fail "http://${LB_ADDR}" && break
|
||||
sleep 3
|
||||
done
|
||||
|
||||
if [ "$i" -eq 20 ]; then
|
||||
echo "LoadBalancer not reachable" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
kubectl delete deployment --kubeconfig tenantkubeconfig-${test_name} "${test_name}-backend" -n tenant-test
|
||||
kubectl delete service --kubeconfig tenantkubeconfig-${test_name} "${test_name}-backend" -n tenant-test
|
||||
|
||||
# Wait for all machine deployment replicas to be ready (timeout after 10 minutes)
|
||||
kubectl wait machinedeployment kubernetes-${test_name}-md0 -n tenant-test --timeout=10m --for=jsonpath='{.status.v1beta2.readyReplicas}'=2
|
||||
|
||||
|
||||
@@ -21,14 +21,33 @@
|
||||
}
|
||||
|
||||
@test "Test kinds" {
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.kind')
|
||||
if [ "$val" != "TenantList" ]; then
|
||||
echo "Expected kind to be TenantList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Tenant" ]; then
|
||||
echo "Expected kind to be Tenant, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.kind')
|
||||
if [ "$val" != "IngressList" ]; then
|
||||
echo "Expected kind to be IngressList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Ingress" ]; then
|
||||
echo "Expected kind to be Ingress, got $val"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@test "Create and delete namespace" {
|
||||
kubectl create ns cozy-test-create-and-delete-namespace --dry-run=client -o yaml | kubectl apply -f -
|
||||
if ! kubectl delete ns cozy-test-create-and-delete-namespace; then
|
||||
echo "Failed to delete namespace"
|
||||
kubectl describe ns cozy-test-create-and-delete-namespace
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ need yq; need jq; need base64
|
||||
CHART_YAML="${CHART_YAML:-Chart.yaml}"
|
||||
VALUES_YAML="${VALUES_YAML:-values.yaml}"
|
||||
SCHEMA_JSON="${SCHEMA_JSON:-values.schema.json}"
|
||||
CRD_DIR="../../system/cozystack-resource-definitions/cozyrds"
|
||||
|
||||
[[ -f "$CHART_YAML" ]] || { echo "No $CHART_YAML found"; exit 1; }
|
||||
[[ -f "$SCHEMA_JSON" ]] || { echo "No $SCHEMA_JSON found"; exit 1; }
|
||||
@@ -22,6 +21,8 @@ if [[ -z "$NAME" ]]; then
|
||||
echo "Chart.yaml: .name is empty"; exit 1
|
||||
fi
|
||||
|
||||
CRD_DIR="../../system/${NAME}-rd/cozyrds"
|
||||
|
||||
# Resolve icon path
|
||||
# Accepts:
|
||||
# /logos/foo.svg -> ./logos/foo.svg
|
||||
|
||||
@@ -37,6 +37,8 @@ type CozystackResourceDefinitionReconciler struct {
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
// Only handle debounced restart logic
|
||||
// HelmRelease reconciliation is handled by CozystackResourceDefinitionHelmReconciler
|
||||
return r.debouncedRestart(ctx)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch;update;patch
|
||||
|
||||
// CozystackResourceDefinitionHelmReconciler reconciles CozystackResourceDefinitions
|
||||
// and updates related HelmReleases when a CozyRD changes.
|
||||
// This controller does NOT watch HelmReleases to avoid mutual reconciliation storms
|
||||
// with Flux's helm-controller.
|
||||
type CozystackResourceDefinitionHelmReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Get the CozystackResourceDefinition that triggered this reconciliation
|
||||
crd := &cozyv1alpha1.CozystackResourceDefinition{}
|
||||
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
|
||||
logger.Error(err, "failed to get CozystackResourceDefinition", "name", req.Name)
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// Update HelmReleases related to this specific CozyRD
|
||||
if err := r.updateHelmReleasesForCRD(ctx, crd); err != nil {
|
||||
logger.Error(err, "failed to update HelmReleases for CRD", "crd", crd.Name)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionHelmReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("cozystackresourcedefinition-helm-reconciler").
|
||||
For(&cozyv1alpha1.CozystackResourceDefinition{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
// updateHelmReleasesForCRD updates all HelmReleases that match the application labels from CozystackResourceDefinition
|
||||
func (r *CozystackResourceDefinitionHelmReconciler) updateHelmReleasesForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Use application labels to find HelmReleases
|
||||
// Labels: apps.cozystack.io/application.kind and apps.cozystack.io/application.group
|
||||
applicationKind := crd.Spec.Application.Kind
|
||||
|
||||
// Validate that applicationKind is non-empty
|
||||
if applicationKind == "" {
|
||||
logger.V(4).Info("Skipping HelmRelease update: Application.Kind is empty", "crd", crd.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
applicationGroup := "apps.cozystack.io" // All applications use this group
|
||||
|
||||
// Build label selector for HelmReleases
|
||||
// Only reconcile HelmReleases with cozystack.io/ui=true label
|
||||
labelSelector := client.MatchingLabels{
|
||||
"apps.cozystack.io/application.kind": applicationKind,
|
||||
"apps.cozystack.io/application.group": applicationGroup,
|
||||
"cozystack.io/ui": "true",
|
||||
}
|
||||
|
||||
// List all HelmReleases with matching labels
|
||||
hrList := &helmv2.HelmReleaseList{}
|
||||
if err := r.List(ctx, hrList, labelSelector); err != nil {
|
||||
logger.Error(err, "failed to list HelmReleases", "kind", applicationKind, "group", applicationGroup)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.V(4).Info("Found HelmReleases to update", "crd", crd.Name, "kind", applicationKind, "count", len(hrList.Items))
|
||||
|
||||
// Update each HelmRelease
|
||||
for i := range hrList.Items {
|
||||
hr := &hrList.Items[i]
|
||||
if err := r.updateHelmReleaseChart(ctx, hr, crd); err != nil {
|
||||
logger.Error(err, "failed to update HelmRelease", "name", hr.Name, "namespace", hr.Namespace)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// expectedValuesFrom returns the expected valuesFrom configuration for HelmReleases
|
||||
func expectedValuesFrom() []helmv2.ValuesReference {
|
||||
return []helmv2.ValuesReference{
|
||||
{
|
||||
Kind: "Secret",
|
||||
Name: "cozystack-values",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// valuesFromEqual compares two ValuesReference slices
|
||||
func valuesFromEqual(a, b []helmv2.ValuesReference) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i].Kind != b[i].Kind ||
|
||||
a[i].Name != b[i].Name ||
|
||||
a[i].ValuesKey != b[i].ValuesKey ||
|
||||
a[i].TargetPath != b[i].TargetPath ||
|
||||
a[i].Optional != b[i].Optional {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// updateHelmReleaseChart updates the chart and valuesFrom in HelmRelease based on CozystackResourceDefinition
|
||||
func (r *CozystackResourceDefinitionHelmReconciler) updateHelmReleaseChart(ctx context.Context, hr *helmv2.HelmRelease, crd *cozyv1alpha1.CozystackResourceDefinition) error {
|
||||
logger := log.FromContext(ctx)
|
||||
hrCopy := hr.DeepCopy()
|
||||
updated := false
|
||||
|
||||
// Validate Chart configuration exists
|
||||
if crd.Spec.Release.Chart.Name == "" {
|
||||
logger.V(4).Info("Skipping HelmRelease chart update: Chart.Name is empty", "crd", crd.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate SourceRef fields
|
||||
if crd.Spec.Release.Chart.SourceRef.Kind == "" ||
|
||||
crd.Spec.Release.Chart.SourceRef.Name == "" ||
|
||||
crd.Spec.Release.Chart.SourceRef.Namespace == "" {
|
||||
logger.Error(fmt.Errorf("invalid SourceRef in CRD"), "Skipping HelmRelease chart update: SourceRef fields are incomplete",
|
||||
"crd", crd.Name,
|
||||
"kind", crd.Spec.Release.Chart.SourceRef.Kind,
|
||||
"name", crd.Spec.Release.Chart.SourceRef.Name,
|
||||
"namespace", crd.Spec.Release.Chart.SourceRef.Namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get version and reconcileStrategy from CRD or use defaults
|
||||
version := ">= 0.0.0-0"
|
||||
reconcileStrategy := "Revision"
|
||||
// TODO: Add Version and ReconcileStrategy fields to CozystackResourceDefinitionChart if needed
|
||||
|
||||
// Build expected SourceRef
|
||||
expectedSourceRef := helmv2.CrossNamespaceObjectReference{
|
||||
Kind: crd.Spec.Release.Chart.SourceRef.Kind,
|
||||
Name: crd.Spec.Release.Chart.SourceRef.Name,
|
||||
Namespace: crd.Spec.Release.Chart.SourceRef.Namespace,
|
||||
}
|
||||
|
||||
if hrCopy.Spec.Chart == nil {
|
||||
// Need to create Chart spec
|
||||
hrCopy.Spec.Chart = &helmv2.HelmChartTemplate{
|
||||
Spec: helmv2.HelmChartTemplateSpec{
|
||||
Chart: crd.Spec.Release.Chart.Name,
|
||||
Version: version,
|
||||
ReconcileStrategy: reconcileStrategy,
|
||||
SourceRef: expectedSourceRef,
|
||||
},
|
||||
}
|
||||
updated = true
|
||||
} else {
|
||||
// Update existing Chart spec
|
||||
if hrCopy.Spec.Chart.Spec.Chart != crd.Spec.Release.Chart.Name ||
|
||||
hrCopy.Spec.Chart.Spec.SourceRef != expectedSourceRef {
|
||||
hrCopy.Spec.Chart.Spec.Chart = crd.Spec.Release.Chart.Name
|
||||
hrCopy.Spec.Chart.Spec.SourceRef = expectedSourceRef
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
// Check and update valuesFrom configuration
|
||||
expected := expectedValuesFrom()
|
||||
if !valuesFromEqual(hrCopy.Spec.ValuesFrom, expected) {
|
||||
logger.V(4).Info("Updating HelmRelease valuesFrom", "name", hr.Name, "namespace", hr.Namespace)
|
||||
hrCopy.Spec.ValuesFrom = expected
|
||||
updated = true
|
||||
}
|
||||
|
||||
if updated {
|
||||
logger.V(4).Info("Updating HelmRelease chart", "name", hr.Name, "namespace", hr.Namespace)
|
||||
if err := r.Update(ctx, hrCopy); err != nil {
|
||||
return fmt.Errorf("failed to update HelmRelease: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -105,8 +105,26 @@ func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) {
|
||||
"properties": map[string]any{},
|
||||
}
|
||||
|
||||
// Check if there's a spec property
|
||||
specProp, ok := props["spec"].(map[string]any)
|
||||
if !ok {
|
||||
return map[string]any{}, nil
|
||||
}
|
||||
|
||||
specProps, ok := specProp["properties"].(map[string]any)
|
||||
if !ok {
|
||||
return map[string]any{}, nil
|
||||
}
|
||||
|
||||
// Create spec.properties structure in schema
|
||||
schemaProps := schema["properties"].(map[string]any)
|
||||
specSchema := map[string]any{
|
||||
"properties": map[string]any{},
|
||||
}
|
||||
schemaProps["spec"] = specSchema
|
||||
|
||||
// Process spec properties recursively
|
||||
processSpecProperties(props, schema["properties"].(map[string]any))
|
||||
processSpecProperties(specProps, specSchema["properties"].(map[string]any))
|
||||
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
@@ -9,41 +9,46 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
// Test OpenAPI schema with various field types
|
||||
openAPISchema := `{
|
||||
"properties": {
|
||||
"simpleString": {
|
||||
"type": "string",
|
||||
"description": "A simple string field"
|
||||
},
|
||||
"stringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["option1", "option2"],
|
||||
"description": "String with enum should be skipped"
|
||||
},
|
||||
"numberField": {
|
||||
"type": "number",
|
||||
"description": "Number field should be skipped"
|
||||
},
|
||||
"nestedObject": {
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nestedString": {
|
||||
"simpleString": {
|
||||
"type": "string",
|
||||
"description": "Nested string should get multilineString"
|
||||
"description": "A simple string field"
|
||||
},
|
||||
"nestedStringWithEnum": {
|
||||
"stringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b"],
|
||||
"description": "Nested string with enum should be skipped"
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayOfObjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemString": {
|
||||
"type": "string",
|
||||
"description": "String in array item"
|
||||
"enum": ["option1", "option2"],
|
||||
"description": "String with enum should be skipped"
|
||||
},
|
||||
"numberField": {
|
||||
"type": "number",
|
||||
"description": "Number field should be skipped"
|
||||
},
|
||||
"nestedObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nestedString": {
|
||||
"type": "string",
|
||||
"description": "Nested string should get multilineString"
|
||||
},
|
||||
"nestedStringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b"],
|
||||
"description": "Nested string with enum should be skipped"
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayOfObjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemString": {
|
||||
"type": "string",
|
||||
"description": "String in array item"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,33 +75,44 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
t.Fatal("schema.properties is not a map")
|
||||
}
|
||||
|
||||
// Check simpleString
|
||||
simpleString, ok := props["simpleString"].(map[string]any)
|
||||
// Check spec property exists
|
||||
spec, ok := props["spec"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("simpleString not found in properties")
|
||||
t.Fatal("spec not found in properties")
|
||||
}
|
||||
|
||||
specProps, ok := spec["properties"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("spec.properties is not a map")
|
||||
}
|
||||
|
||||
// Check simpleString
|
||||
simpleString, ok := specProps["simpleString"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("simpleString not found in spec.properties")
|
||||
}
|
||||
if simpleString["type"] != "multilineString" {
|
||||
t.Errorf("simpleString should have type multilineString, got %v", simpleString["type"])
|
||||
}
|
||||
|
||||
// Check stringWithEnum should not be present (or should not have multilineString)
|
||||
if stringWithEnum, ok := props["stringWithEnum"].(map[string]any); ok {
|
||||
if stringWithEnum, ok := specProps["stringWithEnum"].(map[string]any); ok {
|
||||
if stringWithEnum["type"] == "multilineString" {
|
||||
t.Error("stringWithEnum should not have multilineString type")
|
||||
}
|
||||
}
|
||||
|
||||
// Check numberField should not be present
|
||||
if numberField, ok := props["numberField"].(map[string]any); ok {
|
||||
if numberField, ok := specProps["numberField"].(map[string]any); ok {
|
||||
if numberField["type"] != nil {
|
||||
t.Error("numberField should not have any type override")
|
||||
}
|
||||
}
|
||||
|
||||
// Check nested object
|
||||
nestedObject, ok := props["nestedObject"].(map[string]any)
|
||||
nestedObject, ok := specProps["nestedObject"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("nestedObject not found in properties")
|
||||
t.Fatal("nestedObject not found in spec.properties")
|
||||
}
|
||||
nestedProps, ok := nestedObject["properties"].(map[string]any)
|
||||
if !ok {
|
||||
@@ -113,9 +129,9 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check array of objects
|
||||
arrayOfObjects, ok := props["arrayOfObjects"].(map[string]any)
|
||||
arrayOfObjects, ok := specProps["arrayOfObjects"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("arrayOfObjects not found in properties")
|
||||
t.Fatal("arrayOfObjects not found in spec.properties")
|
||||
}
|
||||
items, ok := arrayOfObjects["items"].(map[string]any)
|
||||
if !ok {
|
||||
|
||||
@@ -134,7 +134,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
createCustomColumnsOverride("factory-details-v1.services", []any{
|
||||
createCustomColumnWithSpecificColor("Name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createStringColumn("ClusterIP", ".spec.clusterIP"),
|
||||
createStringColumn("LoadbalancerIP", ".spec.loadBalancerIP"),
|
||||
createStringColumn("LoadbalancerIP", ".status.loadBalancer.ingress[0].ip"),
|
||||
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
)
|
||||
|
||||
type CozystackConfigReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
var configMapNames = []string{"cozystack", "cozystack-branding", "cozystack-scheduling"}
|
||||
|
||||
const configMapNamespace = "cozy-system"
|
||||
const digestAnnotation = "cozystack.io/cozy-config-digest"
|
||||
const forceReconcileKey = "reconcile.fluxcd.io/forceAt"
|
||||
const requestedAt = "reconcile.fluxcd.io/requestedAt"
|
||||
|
||||
func (r *CozystackConfigReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
|
||||
log := log.FromContext(ctx)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
digest, err := r.computeDigest(ctx)
|
||||
if err != nil {
|
||||
log.Error(err, "failed to compute config digest")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
var helmList helmv2.HelmReleaseList
|
||||
if err := r.List(ctx, &helmList); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("failed to list HelmReleases: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now().Format(time.RFC3339Nano)
|
||||
updated := 0
|
||||
|
||||
for _, hr := range helmList.Items {
|
||||
isSystemApp := hr.Labels["cozystack.io/system-app"] == "true"
|
||||
isTenantRoot := hr.Namespace == "tenant-root" && hr.Name == "tenant-root"
|
||||
if !isSystemApp && !isTenantRoot {
|
||||
continue
|
||||
}
|
||||
patchTarget := hr.DeepCopy()
|
||||
|
||||
if hr.Annotations == nil {
|
||||
hr.Annotations = map[string]string{}
|
||||
}
|
||||
|
||||
if hr.Annotations[digestAnnotation] == digest {
|
||||
continue
|
||||
}
|
||||
patchTarget.Annotations[digestAnnotation] = digest
|
||||
patchTarget.Annotations[forceReconcileKey] = now
|
||||
patchTarget.Annotations[requestedAt] = now
|
||||
|
||||
patch := client.MergeFrom(hr.DeepCopy())
|
||||
if err := r.Patch(ctx, patchTarget, patch); err != nil {
|
||||
log.Error(err, "failed to patch HelmRelease", "name", hr.Name, "namespace", hr.Namespace)
|
||||
continue
|
||||
}
|
||||
updated++
|
||||
log.Info("patched HelmRelease with new config digest", "name", hr.Name, "namespace", hr.Namespace)
|
||||
}
|
||||
|
||||
log.Info("finished reconciliation", "updatedHelmReleases", updated)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *CozystackConfigReconciler) computeDigest(ctx context.Context) (string, error) {
|
||||
hash := sha256.New()
|
||||
|
||||
for _, name := range configMapNames {
|
||||
var cm corev1.ConfigMap
|
||||
err := r.Get(ctx, client.ObjectKey{Namespace: configMapNamespace, Name: name}, &cm)
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
continue // ignore missing
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Sort keys for consistent hashing
|
||||
var keys []string
|
||||
for k := range cm.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
v := cm.Data[k]
|
||||
fmt.Fprintf(hash, "%s:%s=%s\n", name, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func (r *CozystackConfigReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
WithEventFilter(predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
cm, ok := e.ObjectNew.(*corev1.ConfigMap)
|
||||
return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name)
|
||||
},
|
||||
CreateFunc: func(e event.CreateEvent) bool {
|
||||
cm, ok := e.Object.(*corev1.ConfigMap)
|
||||
return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name)
|
||||
},
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
cm, ok := e.Object.(*corev1.ConfigMap)
|
||||
return ok && cm.Namespace == configMapNamespace && contains(configMapNames, cm.Name)
|
||||
},
|
||||
}).
|
||||
For(&corev1.ConfigMap{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func contains(slice []string, val string) bool {
|
||||
for _, s := range slice {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
e "errors"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
"gopkg.in/yaml.v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
type TenantHelmReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *TenantHelmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
hr := &helmv2.HelmRelease{}
|
||||
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
logger.Error(err, "unable to fetch HelmRelease")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(hr.Name, "tenant-") {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if len(hr.Status.Conditions) == 0 || hr.Status.Conditions[0].Type != "Ready" {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if len(hr.Status.History) == 0 {
|
||||
logger.Info("no history in HelmRelease status", "name", hr.Name)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if hr.Status.History[0].Status != "deployed" {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
newDigest := hr.Status.History[0].Digest
|
||||
var hrList helmv2.HelmReleaseList
|
||||
childNamespace := getChildNamespace(hr.Namespace, hr.Name)
|
||||
if childNamespace == "tenant-root" && hr.Name == "tenant-root" {
|
||||
if hr.Spec.Values == nil {
|
||||
logger.Error(e.New("hr.Spec.Values is nil"), "cant annotate tenant-root ns")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
err := annotateTenantRootNs(*hr.Spec.Values, r.Client)
|
||||
if err != nil {
|
||||
logger.Error(err, "cant annotate tenant-root ns")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
logger.Info("namespace 'tenant-root' annotated")
|
||||
}
|
||||
|
||||
if err := r.List(ctx, &hrList, client.InNamespace(childNamespace)); err != nil {
|
||||
logger.Error(err, "unable to list HelmReleases in namespace", "namespace", hr.Name)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
for _, item := range hrList.Items {
|
||||
if item.Name == hr.Name {
|
||||
continue
|
||||
}
|
||||
oldDigest := item.GetAnnotations()["cozystack.io/tenant-config-digest"]
|
||||
if oldDigest == newDigest {
|
||||
continue
|
||||
}
|
||||
patchTarget := item.DeepCopy()
|
||||
|
||||
if patchTarget.Annotations == nil {
|
||||
patchTarget.Annotations = map[string]string{}
|
||||
}
|
||||
ts := time.Now().Format(time.RFC3339Nano)
|
||||
|
||||
patchTarget.Annotations["cozystack.io/tenant-config-digest"] = newDigest
|
||||
patchTarget.Annotations["reconcile.fluxcd.io/forceAt"] = ts
|
||||
patchTarget.Annotations["reconcile.fluxcd.io/requestedAt"] = ts
|
||||
|
||||
patch := client.MergeFrom(item.DeepCopy())
|
||||
if err := r.Patch(ctx, patchTarget, patch); err != nil {
|
||||
logger.Error(err, "failed to patch HelmRelease", "name", patchTarget.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info("patched HelmRelease with new digest", "name", patchTarget.Name, "digest", newDigest, "version", hr.Status.History[0].Version)
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *TenantHelmReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&helmv2.HelmRelease{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func getChildNamespace(currentNamespace, hrName string) string {
|
||||
tenantName := strings.TrimPrefix(hrName, "tenant-")
|
||||
|
||||
switch {
|
||||
case currentNamespace == "tenant-root" && hrName == "tenant-root":
|
||||
// 1) root tenant inside root namespace
|
||||
return "tenant-root"
|
||||
|
||||
case currentNamespace == "tenant-root":
|
||||
// 2) any other tenant in root namespace
|
||||
return fmt.Sprintf("tenant-%s", tenantName)
|
||||
|
||||
default:
|
||||
// 3) tenant in a dedicated namespace
|
||||
return fmt.Sprintf("%s-%s", currentNamespace, tenantName)
|
||||
}
|
||||
}
|
||||
|
||||
func annotateTenantRootNs(values apiextensionsv1.JSON, c client.Client) error {
|
||||
var data map[string]interface{}
|
||||
if err := yaml.Unmarshal(values.Raw, &data); err != nil {
|
||||
return fmt.Errorf("failed to parse HelmRelease values: %w", err)
|
||||
}
|
||||
|
||||
host, ok := data["host"].(string)
|
||||
if !ok || host == "" {
|
||||
return fmt.Errorf("host field not found or not a string")
|
||||
}
|
||||
|
||||
var ns corev1.Namespace
|
||||
if err := c.Get(context.TODO(), client.ObjectKey{Name: "tenant-root"}, &ns); err != nil {
|
||||
return fmt.Errorf("failed to get namespace tenant-root: %w", err)
|
||||
}
|
||||
|
||||
if ns.Annotations == nil {
|
||||
ns.Annotations = map[string]string{}
|
||||
}
|
||||
ns.Annotations["namespace.cozystack.io/host"] = host
|
||||
|
||||
if err := c.Update(context.TODO(), &ns); err != nil {
|
||||
return fmt.Errorf("failed to update namespace: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2,49 +2,72 @@ package lineagecontrollerwebhook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
)
|
||||
|
||||
type chartRef struct {
|
||||
repo string
|
||||
chart string
|
||||
}
|
||||
|
||||
type appRef struct {
|
||||
group string
|
||||
kind string
|
||||
}
|
||||
|
||||
type runtimeConfig struct {
|
||||
chartAppMap map[chartRef]*cozyv1alpha1.CozystackResourceDefinition
|
||||
appCRDMap map[appRef]*cozyv1alpha1.CozystackResourceDefinition
|
||||
appCRDMap map[appRef]*cozyv1alpha1.CozystackResourceDefinition
|
||||
}
|
||||
|
||||
func (l *LineageControllerWebhook) initConfig() {
|
||||
l.initOnce.Do(func() {
|
||||
if l.config.Load() == nil {
|
||||
l.config.Store(&runtimeConfig{
|
||||
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
cfg, ok := l.config.Load().(*runtimeConfig)
|
||||
// getApplicationLabel safely extracts an application label from HelmRelease
|
||||
func getApplicationLabel(hr *helmv2.HelmRelease, key string) (string, error) {
|
||||
if hr.Labels == nil {
|
||||
return "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: labels are nil", hr.Namespace, hr.Name)
|
||||
}
|
||||
val, ok := hr.Labels[key]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
|
||||
return "", fmt.Errorf("cannot map helm release %s/%s to dynamic app: missing %s label", hr.Namespace, hr.Name, key)
|
||||
}
|
||||
if hr.Spec.Chart == nil {
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
s := hr.Spec.Chart.Spec
|
||||
val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
// Extract application metadata from labels
|
||||
appKind, err := getApplicationLabel(hr, "apps.cozystack.io/application.kind")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
appGroup, err := getApplicationLabel(hr, "apps.cozystack.io/application.group")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
appName, err := getApplicationLabel(hr, "apps.cozystack.io/application.name")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
// Construct API version from group
|
||||
apiVersion := fmt.Sprintf("%s/v1alpha1", appGroup)
|
||||
|
||||
// Extract prefix from HelmRelease name by removing the application name
|
||||
// HelmRelease name format: <prefix><application-name>
|
||||
prefix := strings.TrimSuffix(hr.Name, appName)
|
||||
|
||||
// Validate the derived prefix
|
||||
// This ensures correctness when appName appears multiple times in hr.Name
|
||||
if prefix+appName != hr.Name {
|
||||
return "", "", "", fmt.Errorf("cannot derive prefix from helm release %s/%s: name does not end with application name %s", hr.Namespace, hr.Name, appName)
|
||||
}
|
||||
|
||||
return apiVersion, appKind, prefix, nil
|
||||
}
|
||||
|
||||
@@ -24,25 +24,15 @@ func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Reque
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
cfg := &runtimeConfig{
|
||||
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
|
||||
}
|
||||
for _, crd := range crds.Items {
|
||||
chRef := chartRef{
|
||||
crd.Spec.Release.Chart.SourceRef.Name,
|
||||
crd.Spec.Release.Chart.Name,
|
||||
}
|
||||
appRef := appRef{
|
||||
"apps.cozystack.io",
|
||||
crd.Spec.Application.Kind,
|
||||
}
|
||||
|
||||
newRef := crd
|
||||
if _, exists := cfg.chartAppMap[chRef]; exists {
|
||||
l.Info("duplicate chart mapping detected; ignoring subsequent entry", "key", chRef)
|
||||
} else {
|
||||
cfg.chartAppMap[chRef] = &newRef
|
||||
}
|
||||
if _, exists := cfg.appCRDMap[appRef]; exists {
|
||||
l.Info("duplicate app mapping detected; ignoring subsequent entry", "key", appRef)
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $seaweedfs := index $myNS.metadata.annotations "namespace.cozystack.io/seaweedfs" }}
|
||||
{{- $seaweedfs := .Values._namespace.seaweedfs }}
|
||||
apiVersion: objectstorage.k8s.io/v1alpha1
|
||||
kind: BucketClaim
|
||||
metadata:
|
||||
|
||||
@@ -21,5 +21,8 @@ spec:
|
||||
force: true
|
||||
remediation:
|
||||
retries: -1
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
name: cozystack-values
|
||||
values:
|
||||
bucketName: {{ .Release.Name }}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
|
||||
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
apiVersion: "clickhouse-keeper.altinity.com/v1"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
|
||||
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-credentials" .Release.Name) }}
|
||||
{{- $passwords := dict }}
|
||||
{{- $users := .Values.users }}
|
||||
|
||||
@@ -50,9 +50,8 @@ spec:
|
||||
postgresUID: 999
|
||||
postgresGID: 999
|
||||
enableSuperuserAccess: true
|
||||
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }}
|
||||
{{- if $configMap }}
|
||||
{{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }}
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
{{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }}
|
||||
{{- if $rawConstraints }}
|
||||
{{- $rawConstraints | fromYaml | toYaml | nindent 2 }}
|
||||
labelSelector:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" | default (dict "data" (dict)) }}
|
||||
{{- $clusterDomain := index $cozyConfig.data "cluster-domain" | default "cozy.local" }}
|
||||
{{- $clusterDomain := index .Values._cluster "cluster-domain" | default "cozy.local" }}
|
||||
---
|
||||
apiVersion: apps.foundationdb.org/v1beta2
|
||||
kind: FoundationDBCluster
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:9e34fd50393b418d9516aadb488067a3a63675b045811beb1c0afc9c61e149e8
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.helmignore
|
||||
/logos
|
||||
/Makefile
|
||||
/hack
|
||||
|
||||
@@ -6,9 +6,12 @@ include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
yq -o=json -i '.properties.version.enum = (load("files/versions.yaml") | keys)' values.schema.json
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
update:
|
||||
hack/update-versions.sh
|
||||
make generate
|
||||
|
||||
image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-csi-driver image-cluster-autoscaler
|
||||
|
||||
image-ubuntu-container-disk:
|
||||
|
||||
@@ -104,7 +104,7 @@ See the reference for components utilized in this service:
|
||||
| `nodeGroups[name].resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `nodeGroups[name].gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` |
|
||||
| `nodeGroups[name].gpus[i].name` | Name of GPU, such as "nvidia.com/AD102GL_L40S". | `string` | `""` |
|
||||
| `version` | Kubernetes version (vMAJOR.MINOR). Supported: 1.28–1.33. | `string` | `v1.33` |
|
||||
| `version` | Kubernetes major.minor version to deploy | `string` | `v1.33` |
|
||||
| `host` | External hostname for Kubernetes cluster. Defaults to `<cluster-name>.<tenant-host>` if empty. | `string` | `""` |
|
||||
|
||||
|
||||
@@ -145,31 +145,31 @@ See the reference for components utilized in this service:
|
||||
|
||||
### Kubernetes Control Plane Configuration
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| --------------------------------------------------- | ------------------------------------------------ | ---------- | -------- |
|
||||
| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` |
|
||||
| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` |
|
||||
| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `medium` |
|
||||
| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
| Name | Description | Type | Value |
|
||||
| --------------------------------------------------- | ------------------------------------------------ | ---------- | ------- |
|
||||
| `controlPlane` | Kubernetes control-plane configuration. | `object` | `{}` |
|
||||
| `controlPlane.replicas` | Number of control-plane replicas. | `int` | `2` |
|
||||
| `controlPlane.apiServer` | API Server configuration. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources` | CPU and memory resources for API Server. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.apiServer.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.apiServer.resourcesPreset` | Preset if `resources` omitted. | `string` | `large` |
|
||||
| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources` | CPU and memory resources for Controller Manager. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.controllerManager.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources` | CPU and memory resources for Scheduler. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.scheduler.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.scheduler.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server` | Konnectivity Server configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources` | CPU and memory resources for Konnectivity. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources.cpu` | CPU available. | `quantity` | `""` |
|
||||
| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available. | `quantity` | `""` |
|
||||
| `controlPlane.konnectivity.server.resourcesPreset` | Preset if `resources` omitted. | `string` | `micro` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"v1.28": "v1.28.15"
|
||||
"v1.29": "v1.29.15"
|
||||
"v1.30": "v1.30.14"
|
||||
"v1.31": "v1.31.10"
|
||||
"v1.32": "v1.32.6"
|
||||
"v1.33": "v1.33.0"
|
||||
"v1.32": "v1.32.10"
|
||||
"v1.31": "v1.31.14"
|
||||
"v1.30": "v1.30.14"
|
||||
"v1.29": "v1.29.15"
|
||||
"v1.28": "v1.28.15"
|
||||
|
||||
260
packages/apps/kubernetes/hack/update-versions.sh
Executable file
260
packages/apps/kubernetes/hack/update-versions.sh
Executable file
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KUBERNETES_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
VALUES_FILE="${KUBERNETES_DIR}/values.yaml"
|
||||
VERSIONS_FILE="${KUBERNETES_DIR}/files/versions.yaml"
|
||||
MAKEFILE="${KUBERNETES_DIR}/Makefile"
|
||||
KAMAJI_DOCKERFILE="${KUBERNETES_DIR}/../../system/kamaji/images/kamaji/Dockerfile"
|
||||
|
||||
# Check if skopeo is installed
|
||||
if ! command -v skopeo &> /dev/null; then
|
||||
echo "Error: skopeo is not installed. Please install skopeo and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is not installed. Please install jq and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get kamaji version from Dockerfile
|
||||
echo "Reading kamaji version from Dockerfile..."
|
||||
if [ ! -f "$KAMAJI_DOCKERFILE" ]; then
|
||||
echo "Error: Kamaji Dockerfile not found at $KAMAJI_DOCKERFILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KAMAJI_VERSION=$(grep "^ARG VERSION=" "$KAMAJI_DOCKERFILE" | cut -d= -f2 | tr -d '"')
|
||||
if [ -z "$KAMAJI_VERSION" ]; then
|
||||
echo "Error: Could not extract kamaji version from Dockerfile" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Kamaji version: $KAMAJI_VERSION"
|
||||
|
||||
# Get Kubernetes version from kamaji repository
|
||||
echo "Fetching Kubernetes version from kamaji repository..."
|
||||
KUBERNETES_VERSION_FROM_KAMAJI=$(curl -sSL "https://raw.githubusercontent.com/clastix/kamaji/${KAMAJI_VERSION}/internal/upgrade/kubeadm_version.go" | grep "KubeadmVersion" | sed -E 's/.*KubeadmVersion = "([^"]+)".*/\1/')
|
||||
|
||||
if [ -z "$KUBERNETES_VERSION_FROM_KAMAJI" ]; then
|
||||
echo "Error: Could not fetch Kubernetes version from kamaji repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Kubernetes version from kamaji: $KUBERNETES_VERSION_FROM_KAMAJI"
|
||||
|
||||
# Extract major.minor version (e.g., "1.33" from "v1.33.0")
|
||||
KUBERNETES_MAJOR_MINOR=$(echo "$KUBERNETES_VERSION_FROM_KAMAJI" | sed -E 's/v([0-9]+)\.([0-9]+)\.[0-9]+/\1.\2/')
|
||||
KUBERNETES_MAJOR=$(echo "$KUBERNETES_MAJOR_MINOR" | cut -d. -f1)
|
||||
KUBERNETES_MINOR=$(echo "$KUBERNETES_MAJOR_MINOR" | cut -d. -f2)
|
||||
|
||||
echo "Kubernetes major.minor: $KUBERNETES_MAJOR_MINOR"
|
||||
|
||||
# Get available image tags
|
||||
echo "Fetching available image tags from registry..."
|
||||
AVAILABLE_TAGS=$(skopeo list-tags docker://registry.k8s.io/kube-apiserver | jq -r '.Tags[] | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))' | sort -V)
|
||||
|
||||
if [ -z "$AVAILABLE_TAGS" ]; then
|
||||
echo "Error: Could not fetch available image tags" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Filter out versions higher than KUBERNETES_VERSION_FROM_KAMAJI
|
||||
echo "Filtering versions above ${KUBERNETES_VERSION_FROM_KAMAJI}..."
|
||||
FILTERED_TAGS=$(echo "$AVAILABLE_TAGS" | while read tag; do
|
||||
if [ -n "$tag" ]; then
|
||||
# Compare tag with KUBERNETES_VERSION_FROM_KAMAJI using version sort
|
||||
# Include tag if it's less than or equal to KUBERNETES_VERSION_FROM_KAMAJI
|
||||
if [ "$(printf '%s\n%s\n' "$tag" "$KUBERNETES_VERSION_FROM_KAMAJI" | sort -V | head -1)" = "$tag" ] || [ "$tag" = "$KUBERNETES_VERSION_FROM_KAMAJI" ]; then
|
||||
echo "$tag"
|
||||
fi
|
||||
fi
|
||||
done)
|
||||
|
||||
if [ -z "$FILTERED_TAGS" ]; then
|
||||
echo "Error: No versions found after filtering" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AVAILABLE_TAGS="$FILTERED_TAGS"
|
||||
echo "Filtered to $(echo "$AVAILABLE_TAGS" | wc -l | tr -d ' ') versions"
|
||||
|
||||
# Find the latest patch version for the supported major.minor version
|
||||
echo "Finding latest patch version for ${KUBERNETES_MAJOR_MINOR}..."
|
||||
SUPPORTED_PATCH_TAGS=$(echo "$AVAILABLE_TAGS" | grep "^v${KUBERNETES_MAJOR}\\.${KUBERNETES_MINOR}\\.")
|
||||
if [ -z "$SUPPORTED_PATCH_TAGS" ]; then
|
||||
echo "Error: Could not find any patch versions for ${KUBERNETES_MAJOR_MINOR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
KUBERNETES_VERSION=$(echo "$SUPPORTED_PATCH_TAGS" | tail -n1)
|
||||
echo "Using latest patch version: $KUBERNETES_VERSION"
|
||||
|
||||
# Build versions map: major.minor -> latest patch version
|
||||
# First, collect all unique major.minor versions from available tags
|
||||
echo "Collecting all available major.minor versions..."
|
||||
ALL_MAJOR_MINOR_VERSIONS=$(echo "$AVAILABLE_TAGS" | sed -E 's/v([0-9]+)\.([0-9]+)\.[0-9]+/v\1.\2/' | sort -V -u)
|
||||
|
||||
# Find the position of the supported version in the sorted list
|
||||
SUPPORTED_MAJOR_MINOR="v${KUBERNETES_MAJOR}.${KUBERNETES_MINOR}"
|
||||
echo "Looking for supported version: $SUPPORTED_MAJOR_MINOR"
|
||||
|
||||
# Get all versions that are <= supported version
|
||||
# Create a temporary file for filtering
|
||||
TEMP_VERSIONS=$(mktemp)
|
||||
|
||||
echo "$ALL_MAJOR_MINOR_VERSIONS" | while read version; do
|
||||
# Compare versions using sort -V (version sort)
|
||||
# If version <= supported, include it
|
||||
if [ "$(printf '%s\n%s\n' "$version" "$SUPPORTED_MAJOR_MINOR" | sort -V | head -1)" = "$version" ] || [ "$version" = "$SUPPORTED_MAJOR_MINOR" ]; then
|
||||
echo "$version"
|
||||
fi
|
||||
done > "$TEMP_VERSIONS"
|
||||
|
||||
# Get the supported version and 5 previous versions (total 6 versions)
|
||||
# First, find the position of supported version
|
||||
SUPPORTED_POS=$(grep -n "^${SUPPORTED_MAJOR_MINOR}$" "$TEMP_VERSIONS" | cut -d: -f1)
|
||||
|
||||
if [ -z "$SUPPORTED_POS" ]; then
|
||||
echo "Error: Supported version $SUPPORTED_MAJOR_MINOR not found in available versions" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Calculate start position (5 versions before supported, or from beginning if less than 5 available)
|
||||
TOTAL_LINES=$(wc -l < "$TEMP_VERSIONS" | tr -d ' ')
|
||||
START_POS=$((SUPPORTED_POS - 5))
|
||||
if [ $START_POS -lt 1 ]; then
|
||||
START_POS=1
|
||||
fi
|
||||
|
||||
# Extract versions from START_POS to SUPPORTED_POS (inclusive)
|
||||
CANDIDATE_VERSIONS=$(sed -n "${START_POS},${SUPPORTED_POS}p" "$TEMP_VERSIONS")
|
||||
|
||||
if [ -z "$CANDIDATE_VERSIONS" ]; then
|
||||
echo "Error: Could not find supported version $SUPPORTED_MAJOR_MINOR in available versions" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -A VERSION_MAP
|
||||
VERSIONS=()
|
||||
|
||||
# Process each candidate version
|
||||
for major_minor_key in $CANDIDATE_VERSIONS; do
|
||||
# Extract major and minor for matching
|
||||
major=$(echo "$major_minor_key" | sed -E 's/v([0-9]+)\.([0-9]+)/\1/')
|
||||
minor=$(echo "$major_minor_key" | sed -E 's/v([0-9]+)\.([0-9]+)/\2/')
|
||||
|
||||
# Find all tags that match this major.minor version
|
||||
matching_tags=$(echo "$AVAILABLE_TAGS" | grep "^v${major}\\.${minor}\\.")
|
||||
|
||||
if [ -n "$matching_tags" ]; then
|
||||
# Get the latest patch version for this major.minor version
|
||||
latest_tag=$(echo "$matching_tags" | tail -n1)
|
||||
|
||||
VERSION_MAP["${major_minor_key}"]="${latest_tag}"
|
||||
VERSIONS+=("${major_minor_key}")
|
||||
echo "Found version: ${major_minor_key} -> ${latest_tag}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#VERSIONS[@]} -eq 0 ]; then
|
||||
echo "Error: No matching versions found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sort versions in descending order (newest first)
|
||||
IFS=$'\n' VERSIONS=($(printf '%s\n' "${VERSIONS[@]}" | sort -V -r))
|
||||
unset IFS
|
||||
|
||||
echo "Versions to add: ${VERSIONS[*]}"
|
||||
|
||||
# Create/update versions.yaml file
|
||||
echo "Updating $VERSIONS_FILE..."
|
||||
{
|
||||
for ver in "${VERSIONS[@]}"; do
|
||||
echo "\"${ver}\": \"${VERSION_MAP[$ver]}\""
|
||||
done
|
||||
} > "$VERSIONS_FILE"
|
||||
|
||||
echo "Successfully updated $VERSIONS_FILE"
|
||||
|
||||
# Update values.yaml - enum with major.minor versions only
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE $TEMP_VERSIONS" EXIT
|
||||
|
||||
# Build new version section
|
||||
NEW_VERSION_SECTION="## @enum {string} Version"
|
||||
for ver in "${VERSIONS[@]}"; do
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
## @value $ver"
|
||||
done
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
|
||||
## @param {Version} version - Kubernetes major.minor version to deploy
|
||||
version: \"${VERSIONS[0]}\""
|
||||
|
||||
# Check if version section already exists
|
||||
if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then
|
||||
# Version section exists, update it using awk
|
||||
echo "Updating existing version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to replace the section from "## @enum {string} Version" to "version: " (inclusive)
|
||||
# Delete the old section and insert the new one
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @enum {string} Version/ {
|
||||
in_section = 1
|
||||
print new_section
|
||||
next
|
||||
}
|
||||
in_section && /^version: / {
|
||||
in_section = 0
|
||||
next
|
||||
}
|
||||
in_section {
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
else
|
||||
# Version section doesn't exist, insert it before Application-specific parameters section
|
||||
echo "Inserting new version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to insert before "## @section Application-specific parameters"
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @section Application-specific parameters/ {
|
||||
print new_section
|
||||
print ""
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
fi
|
||||
|
||||
echo "Successfully updated $VALUES_FILE with versions: ${VERSIONS[*]}"
|
||||
|
||||
# Update KUBERNETES_VERSION in Makefile
|
||||
# Extract major.minor from KUBERNETES_VERSION (e.g., "v1.33" from "v1.33.4")
|
||||
KUBERNETES_MAJOR_MINOR_FOR_MAKEFILE=$(echo "$KUBERNETES_VERSION" | sed -E 's/v([0-9]+)\.([0-9]+)\.[0-9]+/v\1.\2/')
|
||||
|
||||
if grep -q "^KUBERNETES_VERSION" "$MAKEFILE"; then
|
||||
# Update existing KUBERNETES_VERSION line using awk
|
||||
echo "Updating KUBERNETES_VERSION in $MAKEFILE..."
|
||||
awk -v new_version="${KUBERNETES_MAJOR_MINOR_FOR_MAKEFILE}" '
|
||||
/^KUBERNETES_VERSION = / {
|
||||
print "KUBERNETES_VERSION = " new_version
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$MAKEFILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$MAKEFILE"
|
||||
echo "Successfully updated KUBERNETES_VERSION in $MAKEFILE to ${KUBERNETES_MAJOR_MINOR_FOR_MAKEFILE}"
|
||||
else
|
||||
echo "Warning: KUBERNETES_VERSION not found in $MAKEFILE" >&2
|
||||
fi
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:598331326f0c2aac420187f0cc3a49fedcb22ed5de4afe50c6ccf8e05d9fa537
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:dee69d15fa8616aa6a1e5a67fc76370e7698a7f58b25e30650eb39c9fb826de8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller.go b/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
index 53388eb8e..28644236f 100644
|
||||
index 53388eb8e..873060251 100644
|
||||
--- a/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
+++ b/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
@@ -10,12 +10,17 @@ index 53388eb8e..28644236f 100644
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
@@ -669,35 +668,50 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
@@ -666,38 +665,62 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
// for extracting the nodes it does not matter what type of address we are dealing with
|
||||
// all nodes with an endpoint for a corresponding slice will be selected.
|
||||
nodeSet := sets.Set[string]{}
|
||||
+ hasEndpointsWithoutNodeName := false
|
||||
for _, slice := range tenantSlices {
|
||||
for _, endpoint := range slice.Endpoints {
|
||||
// find all unique nodes that correspond to an endpoint in a tenant slice
|
||||
+ if endpoint.NodeName == nil {
|
||||
+ klog.Warningf("Skipping endpoint without NodeName in slice %s/%s", slice.Namespace, slice.Name)
|
||||
+ hasEndpointsWithoutNodeName = true
|
||||
+ continue
|
||||
+ }
|
||||
nodeSet.Insert(*endpoint.NodeName)
|
||||
@@ -23,6 +28,13 @@ index 53388eb8e..28644236f 100644
|
||||
}
|
||||
|
||||
- klog.Infof("Desired nodes for service %s in namespace %s: %v", service.Name, service.Namespace, sets.List(nodeSet))
|
||||
+ // Fallback: if no endpoints with NodeName were found, but there are endpoints without NodeName,
|
||||
+ // distribute traffic to all VMIs (similar to ExternalTrafficPolicy=Cluster behavior)
|
||||
+ if nodeSet.Len() == 0 && hasEndpointsWithoutNodeName {
|
||||
+ klog.Infof("No endpoints with NodeName found for service %s/%s, falling back to all VMIs", service.Namespace, service.Name)
|
||||
+ return c.getAllVMIEndpoints()
|
||||
+ }
|
||||
+
|
||||
+ klog.Infof("Desired nodes for service %s/%s: %v", service.Namespace, service.Name, sets.List(nodeSet))
|
||||
|
||||
for _, node := range sets.List(nodeSet) {
|
||||
@@ -68,7 +80,7 @@ index 53388eb8e..28644236f 100644
|
||||
desiredEndpoints = append(desiredEndpoints, &discovery.Endpoint{
|
||||
Addresses: []string{i.IP},
|
||||
Conditions: discovery.EndpointConditions{
|
||||
@@ -705,9 +719,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
@@ -705,9 +728,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
Serving: &serving,
|
||||
Terminating: &terminating,
|
||||
},
|
||||
@@ -80,6 +92,71 @@ index 53388eb8e..28644236f 100644
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,6 +739,64 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
return desiredEndpoints
|
||||
}
|
||||
|
||||
+// getAllVMIEndpoints returns endpoints for all VMIs in the infra namespace.
|
||||
+// This is used as a fallback when tenant endpoints don't have NodeName specified,
|
||||
+// similar to ExternalTrafficPolicy=Cluster behavior where traffic is distributed to all nodes.
|
||||
+func (c *Controller) getAllVMIEndpoints() []*discovery.Endpoint {
|
||||
+ var endpoints []*discovery.Endpoint
|
||||
+
|
||||
+ // List all VMIs in the infra namespace
|
||||
+ vmiList, err := c.infraDynamic.
|
||||
+ Resource(kubevirtv1.VirtualMachineInstanceGroupVersionKind.GroupVersion().WithResource("virtualmachineinstances")).
|
||||
+ Namespace(c.infraNamespace).
|
||||
+ List(context.TODO(), metav1.ListOptions{})
|
||||
+ if err != nil {
|
||||
+ klog.Errorf("Failed to list VMIs in namespace %q: %v", c.infraNamespace, err)
|
||||
+ return endpoints
|
||||
+ }
|
||||
+
|
||||
+ for _, obj := range vmiList.Items {
|
||||
+ vmi := &kubevirtv1.VirtualMachineInstance{}
|
||||
+ err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, vmi)
|
||||
+ if err != nil {
|
||||
+ klog.Errorf("Failed to convert Unstructured to VirtualMachineInstance: %v", err)
|
||||
+ continue
|
||||
+ }
|
||||
+
|
||||
+ if vmi.Status.NodeName == "" {
|
||||
+ klog.Warningf("Skipping VMI %s/%s: NodeName is empty", vmi.Namespace, vmi.Name)
|
||||
+ continue
|
||||
+ }
|
||||
+ nodeNamePtr := &vmi.Status.NodeName
|
||||
+
|
||||
+ ready := vmi.Status.Phase == kubevirtv1.Running
|
||||
+ serving := vmi.Status.Phase == kubevirtv1.Running
|
||||
+ terminating := vmi.Status.Phase == kubevirtv1.Failed || vmi.Status.Phase == kubevirtv1.Succeeded
|
||||
+
|
||||
+ for _, i := range vmi.Status.Interfaces {
|
||||
+ if i.Name == "default" {
|
||||
+ if i.IP == "" {
|
||||
+ klog.Warningf("VMI %s/%s interface %q has no IP, skipping", vmi.Namespace, vmi.Name, i.Name)
|
||||
+ continue
|
||||
+ }
|
||||
+ endpoints = append(endpoints, &discovery.Endpoint{
|
||||
+ Addresses: []string{i.IP},
|
||||
+ Conditions: discovery.EndpointConditions{
|
||||
+ Ready: &ready,
|
||||
+ Serving: &serving,
|
||||
+ Terminating: &terminating,
|
||||
+ },
|
||||
+ NodeName: nodeNamePtr,
|
||||
+ })
|
||||
+ break
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ klog.Infof("Fallback: created %d endpoints from all VMIs in namespace %s", len(endpoints), c.infraNamespace)
|
||||
+ return endpoints
|
||||
+}
|
||||
+
|
||||
func (c *Controller) ensureEndpointSliceLabels(slice *discovery.EndpointSlice, svc *v1.Service) (map[string]string, bool) {
|
||||
labels := make(map[string]string)
|
||||
labelsChanged := false
|
||||
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller_test.go b/pkg/controller/kubevirteps/kubevirteps_controller_test.go
|
||||
index 1c97035b4..d205d0bed 100644
|
||||
--- a/pkg/controller/kubevirteps/kubevirteps_controller_test.go
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0b208ed506dd8c453426761d93ec3d42c9d1b791ba6c91b01c6386dcb1b02442
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a09724a7f95283f9130b3da2a89d81c4c6051c6edf0392a81b6fc90f404b76b6
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:71a74ca30f75967bae309be2758f19aa3d37c60b19426b9b622ff1c33a80362f
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $etcd := index $myNS.metadata.annotations "namespace.cozystack.io/etcd" }}
|
||||
{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }}
|
||||
{{- $host := index $myNS.metadata.annotations "namespace.cozystack.io/host" }}
|
||||
{{- $etcd := .Values._namespace.etcd }}
|
||||
{{- $ingress := .Values._namespace.ingress }}
|
||||
{{- $host := .Values._namespace.host }}
|
||||
{{- $kubevirtmachinetemplateNames := list }}
|
||||
{{- define "kubevirtmachinetemplate" -}}
|
||||
spec:
|
||||
@@ -31,9 +30,8 @@ spec:
|
||||
{{- end }}
|
||||
cluster.x-k8s.io/deployment-name: {{ $.Release.Name }}-{{ .groupName }}
|
||||
spec:
|
||||
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }}
|
||||
{{- if $configMap }}
|
||||
{{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }}
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
{{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }}
|
||||
{{- if $rawConstraints }}
|
||||
{{- $rawConstraints | fromYaml | toYaml | nindent 10 }}
|
||||
labelSelector:
|
||||
@@ -294,6 +292,12 @@ metadata:
|
||||
{{- end }}
|
||||
spec:
|
||||
clusterName: {{ $.Release.Name }}
|
||||
replicas: 2
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: {{ $group.maxReplicas }}
|
||||
maxUnavailable: 1
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
cluster.x-k8s.io/cluster-name: {{ $.Release.Name }}
|
||||
@@ -328,6 +332,7 @@ metadata:
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
clusterName: {{ $.Release.Name }}
|
||||
maxUnhealthy: 0
|
||||
nodeStartupTimeout: 10m
|
||||
selector:
|
||||
matchLabels:
|
||||
|
||||
@@ -5,8 +5,8 @@ metadata:
|
||||
annotations:
|
||||
"helm.sh/hook": pre-delete
|
||||
"helm.sh/hook-weight": "10"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
"helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation
|
||||
name: {{ .Release.Name }}-pre-cleanup
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
@@ -65,7 +65,6 @@ spec:
|
||||
|
||||
echo "Cleanup completed successfully"
|
||||
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
@@ -73,7 +72,7 @@ metadata:
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-failed,hook-succeeded
|
||||
helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation,hook-failed
|
||||
helm.sh/hook-weight: "0"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
||||
@@ -45,8 +45,6 @@ spec:
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if .Values.addons.certManager.valuesOverride }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -37,6 +37,8 @@ spec:
|
||||
dependsOn:
|
||||
- name: {{ .Release.Name }}-vsnap-crd
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-metrics-server
|
||||
labels:
|
||||
cozystack.io/repository: system
|
||||
cozystack.io/target-cluster-name: {{ .Release.Name }}
|
||||
spec:
|
||||
releaseName: metrics-server
|
||||
chart:
|
||||
spec:
|
||||
chart: cozy-metrics-server
|
||||
reconcileStrategy: Revision
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cozystack-system
|
||||
namespace: cozy-system
|
||||
version: '>= 0.0.0-0'
|
||||
kubeConfig:
|
||||
secretRef:
|
||||
name: {{ .Release.Name }}-admin-kubeconfig
|
||||
key: super-admin.svc
|
||||
targetNamespace: cozy-monitoring
|
||||
storageNamespace: cozy-monitoring
|
||||
interval: 5m
|
||||
timeout: 10m
|
||||
install:
|
||||
createNamespace: true
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
dependsOn:
|
||||
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- name: {{ .Release.Name }}-prometheus-operator-crds
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }}
|
||||
{{- $targetTenant := .Values._namespace.monitoring }}
|
||||
{{- if .Values.addons.monitoringAgents.enabled }}
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
@@ -44,6 +43,10 @@ spec:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- name: {{ .Release.Name }}-vertical-pod-autoscaler-crds
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- name: {{ .Release.Name }}-prometheus-operator-crds
|
||||
namespace: {{ .Release.Namespace }}
|
||||
- name: {{ .Release.Name }}-metrics-server
|
||||
namespace: {{ .Release.Namespace }}
|
||||
values:
|
||||
vmagent:
|
||||
externalLabels:
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-prometheus-operator-crds
|
||||
labels:
|
||||
cozystack.io/repository: system
|
||||
cozystack.io/target-cluster-name: {{ .Release.Name }}
|
||||
spec:
|
||||
releaseName: prometheus-operator-crds
|
||||
chart:
|
||||
spec:
|
||||
chart: cozy-prometheus-operator-crds
|
||||
reconcileStrategy: Revision
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cozystack-system
|
||||
namespace: cozy-system
|
||||
version: '>= 0.0.0-0'
|
||||
kubeConfig:
|
||||
secretRef:
|
||||
name: {{ .Release.Name }}-admin-kubeconfig
|
||||
key: super-admin.svc
|
||||
targetNamespace: cozy-victoria-metrics-operator
|
||||
storageNamespace: cozy-victoria-metrics-operator
|
||||
interval: 5m
|
||||
install:
|
||||
createNamespace: true
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
dependsOn:
|
||||
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
@@ -37,6 +37,4 @@ spec:
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
{{- define "cozystack.defaultVPAValues" -}}
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }}
|
||||
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
|
||||
{{- $targetTenant := .Values._namespace.monitoring }}
|
||||
vpaForVPA: false
|
||||
vertical-pod-autoscaler:
|
||||
recommender:
|
||||
|
||||
@@ -37,5 +37,3 @@ spec:
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $ingress := index $myNS.metadata.annotations "namespace.cozystack.io/ingress" }}
|
||||
{{- $ingress := .Values._namespace.ingress }}
|
||||
{{- if and (eq .Values.addons.ingressNginx.exposeMethod "Proxied") .Values.addons.ingressNginx.hosts }}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
|
||||
@@ -287,7 +287,7 @@
|
||||
"resourcesPreset": {
|
||||
"description": "Preset if `resources` omitted.",
|
||||
"type": "string",
|
||||
"default": "medium",
|
||||
"default": "large",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
@@ -492,7 +492,7 @@
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"description": "External hostname for Kubernetes cluster. Defaults to `<cluster-name>.<tenant-host>` if empty.",
|
||||
"description": "External hostname for Kubernetes cluster. Defaults to `\u003ccluster-name\u003e.\u003ctenant-host\u003e` if empty.",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
@@ -615,17 +615,17 @@
|
||||
"default": "replicated"
|
||||
},
|
||||
"version": {
|
||||
"description": "Kubernetes version (vMAJOR.MINOR). Supported: 1.28–1.33.",
|
||||
"description": "Kubernetes major.minor version to deploy",
|
||||
"type": "string",
|
||||
"default": "v1.33",
|
||||
"enum": [
|
||||
"v1.28",
|
||||
"v1.29",
|
||||
"v1.30",
|
||||
"v1.31",
|
||||
"v1.33",
|
||||
"v1.32",
|
||||
"v1.33"
|
||||
"v1.31",
|
||||
"v1.30",
|
||||
"v1.29",
|
||||
"v1.28"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,19 @@ nodeGroups:
|
||||
resources: {}
|
||||
gpus: []
|
||||
|
||||
## @param {string} version - Kubernetes version (vMAJOR.MINOR). Supported: 1.28–1.33.
|
||||
##
|
||||
## @enum {string} Version
|
||||
## @value v1.33
|
||||
## @value v1.32
|
||||
## @value v1.31
|
||||
## @value v1.30
|
||||
## @value v1.29
|
||||
## @value v1.28
|
||||
|
||||
## @param {Version} version - Kubernetes major.minor version to deploy
|
||||
version: "v1.33"
|
||||
|
||||
|
||||
## @param {string} host - External hostname for Kubernetes cluster. Defaults to `<cluster-name>.<tenant-host>` if empty.
|
||||
host: ""
|
||||
|
||||
@@ -143,7 +153,7 @@ addons:
|
||||
|
||||
## @typedef {struct} APIServer - API Server configuration.
|
||||
## @field {Resources} resources - CPU and memory resources for API Server.
|
||||
## @field {ResourcesPreset} resourcesPreset="medium" - Preset if `resources` omitted.
|
||||
## @field {ResourcesPreset} resourcesPreset="large" - Preset if `resources` omitted.
|
||||
|
||||
## @typedef {struct} ControllerManager - Controller Manager configuration.
|
||||
## @field {Resources} resources - CPU and memory resources for Controller Manager.
|
||||
@@ -172,7 +182,7 @@ controlPlane:
|
||||
replicas: 2
|
||||
apiServer:
|
||||
resources: {}
|
||||
resourcesPreset: "medium"
|
||||
resourcesPreset: "large"
|
||||
controllerManager:
|
||||
resources: {}
|
||||
resourcesPreset: "micro"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.helmignore
|
||||
/logos
|
||||
/Makefile
|
||||
/hack
|
||||
|
||||
@@ -7,6 +7,10 @@ generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
update:
|
||||
hack/update-versions.sh
|
||||
make generate
|
||||
|
||||
image:
|
||||
docker buildx build images/mariadb-backup \
|
||||
--tag $(REGISTRY)/mariadb-backup:$(call settag,$(MARIADB_BACKUP_TAG)) \
|
||||
|
||||
@@ -79,6 +79,7 @@ more details:
|
||||
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
|
||||
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
|
||||
| `version` | MariaDB major.minor version to deploy | `string` | `v11.8` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
4
packages/apps/mysql/files/versions.yaml
Normal file
4
packages/apps/mysql/files/versions.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
"v11.8": "11.8.5"
|
||||
"v11.4": "11.4.9"
|
||||
"v10.11": "10.11.15"
|
||||
"v10.6": "10.6.24"
|
||||
151
packages/apps/mysql/hack/update-versions.sh
Executable file
151
packages/apps/mysql/hack/update-versions.sh
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MYSQL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
VALUES_FILE="${MYSQL_DIR}/values.yaml"
|
||||
VERSIONS_FILE="${MYSQL_DIR}/files/versions.yaml"
|
||||
MARIADB_API_URL="https://downloads.mariadb.org/rest-api/mariadb/"
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is not installed. Please install jq and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get LTS versions from MariaDB REST API
|
||||
echo "Fetching LTS versions from MariaDB REST API..."
|
||||
LTS_VERSIONS_JSON=$(curl -sSL "${MARIADB_API_URL}")
|
||||
|
||||
if [ -z "$LTS_VERSIONS_JSON" ]; then
|
||||
echo "Error: Could not fetch versions from MariaDB REST API" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract LTS stable major versions
|
||||
LTS_MAJOR_VERSIONS=$(echo "$LTS_VERSIONS_JSON" | jq -r '.major_releases[] | select(.release_support_type == "Long Term Support") | select(.release_status == "Stable") | .release_id' | sort -V -r)
|
||||
|
||||
if [ -z "$LTS_MAJOR_VERSIONS" ]; then
|
||||
echo "Error: Could not find any LTS stable versions" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found LTS major versions: $(echo "$LTS_MAJOR_VERSIONS" | tr '\n' ' ')"
|
||||
|
||||
# Build versions map: major version -> latest patch version
|
||||
declare -A VERSION_MAP
|
||||
MAJOR_VERSIONS=()
|
||||
|
||||
for major_version in $LTS_MAJOR_VERSIONS; do
|
||||
echo "Fetching patch versions for ${major_version}..."
|
||||
|
||||
# Get patch versions for this major version
|
||||
PATCH_VERSIONS_JSON=$(curl -sSL "${MARIADB_API_URL}${major_version}")
|
||||
|
||||
if [ -z "$PATCH_VERSIONS_JSON" ]; then
|
||||
echo "Warning: Could not fetch patch versions for ${major_version}, skipping..." >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract all stable patch version IDs (format: MAJOR.MINOR.PATCH)
|
||||
# Filter only Stable releases
|
||||
PATCH_VERSIONS=$(echo "$PATCH_VERSIONS_JSON" | jq -r --arg major "$major_version" '.releases | to_entries[] | select(.key | startswith($major + ".")) | select(.value.release_status == "Stable") | .key' | sort -V)
|
||||
|
||||
# If no stable releases found, try to get any releases (for backwards compatibility)
|
||||
if [ -z "$PATCH_VERSIONS" ]; then
|
||||
PATCH_VERSIONS=$(echo "$PATCH_VERSIONS_JSON" | jq -r '.releases | keys[]' | grep -E "^${major_version}\." | sort -V)
|
||||
fi
|
||||
|
||||
if [ -z "$PATCH_VERSIONS" ]; then
|
||||
echo "Warning: Could not find any patch versions for ${major_version}, skipping..." >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get the latest patch version
|
||||
LATEST_PATCH=$(echo "$PATCH_VERSIONS" | tail -n1)
|
||||
|
||||
# major_version already has format MAJOR.MINOR (e.g., "11.8")
|
||||
VERSION_MAP["v${major_version}"]="${LATEST_PATCH}"
|
||||
MAJOR_VERSIONS+=("v${major_version}")
|
||||
echo "Found version: v${major_version} -> ${LATEST_PATCH}"
|
||||
done
|
||||
|
||||
if [ ${#MAJOR_VERSIONS[@]} -eq 0 ]; then
|
||||
echo "Error: No matching versions found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sort major versions in descending order (newest first)
|
||||
IFS=$'\n' MAJOR_VERSIONS=($(printf '%s\n' "${MAJOR_VERSIONS[@]}" | sort -V -r))
|
||||
unset IFS
|
||||
|
||||
echo "Major versions to add: ${MAJOR_VERSIONS[*]}"
|
||||
|
||||
# Create/update versions.yaml file
|
||||
echo "Updating $VERSIONS_FILE..."
|
||||
{
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
echo "\"${major_ver}\": \"${VERSION_MAP[$major_ver]}\""
|
||||
done
|
||||
} > "$VERSIONS_FILE"
|
||||
|
||||
echo "Successfully updated $VERSIONS_FILE"
|
||||
|
||||
# Update values.yaml - enum with major.minor versions only
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
# Build new version section
|
||||
NEW_VERSION_SECTION="## @enum {string} Version"
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
## @value $major_ver"
|
||||
done
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
|
||||
## @param {Version} version - MariaDB major.minor version to deploy
|
||||
version: ${MAJOR_VERSIONS[0]}"
|
||||
|
||||
# Check if version section already exists
|
||||
if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then
|
||||
# Version section exists, update it using awk
|
||||
echo "Updating existing version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to replace the section from "## @enum {string} Version" to "version: " (inclusive)
|
||||
# Delete the old section and insert the new one
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @enum {string} Version/ {
|
||||
in_section = 1
|
||||
print new_section
|
||||
next
|
||||
}
|
||||
in_section && /^version: / {
|
||||
in_section = 0
|
||||
next
|
||||
}
|
||||
in_section {
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
else
|
||||
# Version section doesn't exist, insert it before Application-specific parameters section
|
||||
echo "Inserting new version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to insert before "## @section Application-specific parameters"
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @section Application-specific parameters/ {
|
||||
print new_section
|
||||
print ""
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
fi
|
||||
|
||||
echo "Successfully updated $VALUES_FILE with major.minor versions: ${MAJOR_VERSIONS[*]}"
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:1c0beb1b23a109b0e13727b4c73d2c74830e11cede92858ab20101b66f45a858
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570
|
||||
|
||||
8
packages/apps/mysql/templates/_versions.tpl
Normal file
8
packages/apps/mysql/templates/_versions.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{{- define "mysql.versionMap" }}
|
||||
{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }}
|
||||
{{- if not (hasKey $versionMap .Values.version) }}
|
||||
{{- printf `MariaDB version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }}
|
||||
{{- end }}
|
||||
{{- index $versionMap .Values.version }}
|
||||
{{- end }}
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
name: {{ .Release.Name }}-credentials
|
||||
key: root
|
||||
|
||||
image: "mariadb:11.0.2"
|
||||
image: "mariadb:{{ include "mysql.versionMap" $ }}"
|
||||
|
||||
port: 3306
|
||||
|
||||
|
||||
@@ -186,6 +186,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"description": "MariaDB major.minor version to deploy",
|
||||
"type": "string",
|
||||
"default": "v11.8",
|
||||
"enum": [
|
||||
"v11.8",
|
||||
"v11.4",
|
||||
"v10.11",
|
||||
"v10.6"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,16 @@ storageClass: ""
|
||||
## @param {bool} external - Enable external access from outside the cluster.
|
||||
external: false
|
||||
|
||||
##
|
||||
## @enum {string} Version
|
||||
## @value v11.8
|
||||
## @value v11.4
|
||||
## @value v10.11
|
||||
## @value v10.6
|
||||
|
||||
## @param {Version} version - MariaDB major.minor version to deploy
|
||||
version: v11.8
|
||||
|
||||
##
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }}
|
||||
{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace (printf "%s-credentials" .Release.Name) }}
|
||||
{{- $passwords := dict }}
|
||||
|
||||
@@ -53,6 +52,9 @@ spec:
|
||||
force: true
|
||||
remediation:
|
||||
retries: -1
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
name: cozystack-values
|
||||
values:
|
||||
nats:
|
||||
container:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.helmignore
|
||||
/logos
|
||||
/Makefile
|
||||
/hack
|
||||
|
||||
@@ -3,3 +3,7 @@ include ../../../scripts/package.mk
|
||||
generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
update:
|
||||
hack/update-versions.sh
|
||||
make generate
|
||||
|
||||
@@ -76,6 +76,7 @@ See:
|
||||
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
|
||||
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
|
||||
| `version` | PostgreSQL major version to deploy | `string` | `v18` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
6
packages/apps/postgres/files/versions.yaml
Normal file
6
packages/apps/postgres/files/versions.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
"v18": "v18.1"
|
||||
"v17": "v17.7"
|
||||
"v16": "v16.11"
|
||||
"v15": "v15.15"
|
||||
"v14": "v14.20"
|
||||
"v13": "v13.22"
|
||||
138
packages/apps/postgres/hack/update-versions.sh
Executable file
138
packages/apps/postgres/hack/update-versions.sh
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
POSTGRES_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
VALUES_FILE="${POSTGRES_DIR}/values.yaml"
|
||||
VERSIONS_FILE="${POSTGRES_DIR}/files/versions.yaml"
|
||||
|
||||
# Get supported major versions from GitHub README
|
||||
echo "Fetching supported major versions from GitHub..."
|
||||
SUPPORTED_MAJOR_VERSIONS=$(curl -sSL 'https://raw.githubusercontent.com/cloudnative-pg/postgres-containers/refs/heads/main/README.md' | sed -n '/# CNPG PostgreSQL Container Images/,/#/p' | awk -F' +| +' '$4 ~ /[0-9]+\-[0-9]+\-[0-9]+/ && $6 ~ /[0-9]+\-[0-9]+\-[0-9]+/ {print $2}' | sort -u | xargs)
|
||||
|
||||
if [ -z "$SUPPORTED_MAJOR_VERSIONS" ]; then
|
||||
echo "Error: Could not fetch supported major versions" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Supported major versions: $SUPPORTED_MAJOR_VERSIONS"
|
||||
|
||||
# Check if skopeo is installed
|
||||
if ! command -v skopeo &> /dev/null; then
|
||||
echo "Error: skopeo is not installed. Please install skopeo and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is not installed. Please install jq and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get available image tags
|
||||
echo "Fetching available image tags from registry..."
|
||||
AVAILABLE_TAGS=$(skopeo list-tags docker://ghcr.io/cloudnative-pg/postgresql | jq -r '.Tags[] | select(test("^[0-9]+\\.[0-9]+$"))' | sort -V)
|
||||
|
||||
if [ -z "$AVAILABLE_TAGS" ]; then
|
||||
echo "Error: Could not fetch available image tags" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build versions map: major version -> latest minor version
|
||||
declare -A VERSION_MAP
|
||||
MAJOR_VERSIONS=()
|
||||
|
||||
for major_version in $SUPPORTED_MAJOR_VERSIONS; do
|
||||
# Extract major version number (e.g., "18" from "18.1")
|
||||
major_num=$(echo "$major_version" | cut -d. -f1)
|
||||
|
||||
# Find all tags that match this major version
|
||||
matching_tags=$(echo "$AVAILABLE_TAGS" | grep "^${major_num}\\.")
|
||||
|
||||
if [ -n "$matching_tags" ]; then
|
||||
# Get the latest minor version for this major version
|
||||
latest_tag=$(echo "$matching_tags" | tail -n1)
|
||||
VERSION_MAP["v${major_num}"]="v${latest_tag}"
|
||||
MAJOR_VERSIONS+=("v${major_num}")
|
||||
echo "Found version: v${major_num} -> v${latest_tag}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#MAJOR_VERSIONS[@]} -eq 0 ]; then
|
||||
echo "Error: No matching versions found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sort major versions in descending order (newest first)
|
||||
IFS=$'\n' MAJOR_VERSIONS=($(printf '%s\n' "${MAJOR_VERSIONS[@]}" | sort -V -r))
|
||||
unset IFS
|
||||
|
||||
echo "Major versions to add: ${MAJOR_VERSIONS[*]}"
|
||||
|
||||
# Create/update versions.yaml file
|
||||
echo "Updating $VERSIONS_FILE..."
|
||||
{
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
echo "\"${major_ver}\": \"${VERSION_MAP[$major_ver]}\""
|
||||
done
|
||||
} > "$VERSIONS_FILE"
|
||||
|
||||
echo "Successfully updated $VERSIONS_FILE"
|
||||
|
||||
# Update values.yaml - enum with major versions only
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
# Build new version section
|
||||
NEW_VERSION_SECTION="## @enum {string} Version"
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
## @value $major_ver"
|
||||
done
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
|
||||
## @param {Version} version - PostgreSQL major version to deploy
|
||||
version: ${MAJOR_VERSIONS[0]}"
|
||||
|
||||
# Check if version section already exists
|
||||
if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then
|
||||
# Version section exists, update it using awk
|
||||
echo "Updating existing version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to replace the section from "## @enum {string} Version" to "version: " (inclusive)
|
||||
# Delete the old section and insert the new one
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @enum {string} Version/ {
|
||||
in_section = 1
|
||||
print new_section
|
||||
next
|
||||
}
|
||||
in_section && /^version: / {
|
||||
in_section = 0
|
||||
next
|
||||
}
|
||||
in_section {
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
else
|
||||
# Version section doesn't exist, insert it before Application-specific parameters section
|
||||
echo "Inserting new version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to insert before "## @section Application-specific parameters"
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @section Application-specific parameters/ {
|
||||
print new_section
|
||||
print ""
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
fi
|
||||
|
||||
echo "Successfully updated $VALUES_FILE with major versions: ${MAJOR_VERSIONS[*]}"
|
||||
8
packages/apps/postgres/templates/_versions.tpl
Normal file
8
packages/apps/postgres/templates/_versions.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{{- define "postgres.versionMap" }}
|
||||
{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }}
|
||||
{{- if not (hasKey $versionMap .Values.version) }}
|
||||
{{- printf `PostgreSQL version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }}
|
||||
{{- end }}
|
||||
{{- index $versionMap .Values.version }}
|
||||
{{- end }}
|
||||
|
||||
@@ -44,10 +44,10 @@ spec:
|
||||
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 4 }}
|
||||
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:{{ include "postgres.versionMap" $ | trimPrefix "v" }}
|
||||
enableSuperuserAccess: true
|
||||
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }}
|
||||
{{- if $configMap }}
|
||||
{{- $rawConstraints := get $configMap.data "globalAppTopologySpreadConstraints" }}
|
||||
{{- if .Values._cluster.scheduling }}
|
||||
{{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }}
|
||||
{{- if $rawConstraints }}
|
||||
{{- $rawConstraints | fromYaml | toYaml | nindent 2 }}
|
||||
labelSelector:
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: postgres
|
||||
image: ghcr.io/cloudnative-pg/postgresql:15.3
|
||||
image: ghcr.io/cloudnative-pg/postgresql:{{ include "postgres.versionMap" $ | trimPrefix "v" }}
|
||||
command:
|
||||
- bash
|
||||
- /scripts/init.sh
|
||||
|
||||
@@ -243,6 +243,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"description": "PostgreSQL major version to deploy",
|
||||
"type": "string",
|
||||
"default": "v18",
|
||||
"enum": [
|
||||
"v18",
|
||||
"v17",
|
||||
"v16",
|
||||
"v15",
|
||||
"v14",
|
||||
"v13"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,17 @@ storageClass: ""
|
||||
external: false
|
||||
|
||||
##
|
||||
## @enum {string} Version
|
||||
## @value v18
|
||||
## @value v17
|
||||
## @value v16
|
||||
## @value v15
|
||||
## @value v14
|
||||
## @value v13
|
||||
|
||||
## @param {Version} version - PostgreSQL major version to deploy
|
||||
version: v18
|
||||
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.helmignore
|
||||
/logos
|
||||
/Makefile
|
||||
/hack
|
||||
|
||||
@@ -3,3 +3,7 @@ include ../../../scripts/package.mk
|
||||
generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
update:
|
||||
hack/update-versions.sh
|
||||
make generate
|
||||
|
||||
@@ -23,6 +23,7 @@ Service utilizes the Spotahome Redis Operator for efficient management and orche
|
||||
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `1Gi` |
|
||||
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
|
||||
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
|
||||
| `version` | Redis major version to deploy | `string` | `v8` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
2
packages/apps/redis/files/versions.yaml
Normal file
2
packages/apps/redis/files/versions.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
"v8": "8.4.0"
|
||||
"v7": "7.4.7"
|
||||
161
packages/apps/redis/hack/update-versions.sh
Executable file
161
packages/apps/redis/hack/update-versions.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REDIS_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
VALUES_FILE="${REDIS_DIR}/values.yaml"
|
||||
VERSIONS_FILE="${REDIS_DIR}/files/versions.yaml"
|
||||
REDIS_IMAGE="docker://docker.io/redis"
|
||||
|
||||
# Check if skopeo is installed
|
||||
if ! command -v skopeo &> /dev/null; then
|
||||
echo "Error: skopeo is not installed. Please install skopeo and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if jq is installed
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "Error: jq is not installed. Please install jq and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get available image tags
|
||||
echo "Fetching available image tags from registry..."
|
||||
AVAILABLE_TAGS=$(skopeo list-tags "${REDIS_IMAGE}" | jq -r '.Tags[] | select(test("^[0-9]+\\.[0-9]+\\.[0-9]+$"))' | sort -V)
|
||||
|
||||
if [ -z "$AVAILABLE_TAGS" ]; then
|
||||
echo "Error: Could not fetch available image tags" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get all unique major versions and find Latest and Previous
|
||||
echo "Finding Latest and Previous major versions..."
|
||||
ALL_MAJOR_VERSIONS=$(echo "$AVAILABLE_TAGS" | cut -d. -f1 | sort -u -n -r)
|
||||
MAJOR_VERSIONS_ARRAY=($ALL_MAJOR_VERSIONS)
|
||||
|
||||
if [ ${#MAJOR_VERSIONS_ARRAY[@]} -lt 1 ]; then
|
||||
echo "Error: Could not find any major versions" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get Latest and Previous major versions
|
||||
LATEST_MAJOR=${MAJOR_VERSIONS_ARRAY[0]}
|
||||
PREVIOUS_MAJOR=""
|
||||
|
||||
if [ ${#MAJOR_VERSIONS_ARRAY[@]} -ge 2 ]; then
|
||||
PREVIOUS_MAJOR=${MAJOR_VERSIONS_ARRAY[1]}
|
||||
fi
|
||||
|
||||
if [ -z "$PREVIOUS_MAJOR" ]; then
|
||||
echo "Warning: Only one major version found (${LATEST_MAJOR}), using it as both Latest and Previous"
|
||||
PREVIOUS_MAJOR=$LATEST_MAJOR
|
||||
fi
|
||||
|
||||
echo "Latest major version: ${LATEST_MAJOR}"
|
||||
echo "Previous major version: ${PREVIOUS_MAJOR}"
|
||||
|
||||
# Build versions map: major version -> latest patch version
|
||||
declare -A VERSION_MAP
|
||||
MAJOR_VERSIONS=()
|
||||
PROCESSED_MAJORS=()
|
||||
|
||||
for major_version in "$LATEST_MAJOR" "$PREVIOUS_MAJOR"; do
|
||||
# Skip if we already processed this major version
|
||||
if [[ " ${PROCESSED_MAJORS[@]} " =~ " ${major_version} " ]]; then
|
||||
continue
|
||||
fi
|
||||
PROCESSED_MAJORS+=("${major_version}")
|
||||
|
||||
# Find all tags that match this major version
|
||||
matching_tags=$(echo "$AVAILABLE_TAGS" | grep "^${major_version}\\.")
|
||||
|
||||
if [ -n "$matching_tags" ]; then
|
||||
# Get the latest patch version for this major version
|
||||
latest_tag=$(echo "$matching_tags" | tail -n1)
|
||||
VERSION_MAP["v${major_version}"]="${latest_tag}"
|
||||
MAJOR_VERSIONS+=("v${major_version}")
|
||||
echo "Found version: v${major_version} -> ${latest_tag}"
|
||||
else
|
||||
echo "Warning: Could not find any patch versions for ${major_version}, skipping..." >&2
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#MAJOR_VERSIONS[@]} -eq 0 ]; then
|
||||
echo "Error: No matching versions found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sort major versions in descending order (newest first)
|
||||
IFS=$'\n' MAJOR_VERSIONS=($(printf '%s\n' "${MAJOR_VERSIONS[@]}" | sort -V -r))
|
||||
unset IFS
|
||||
|
||||
echo "Major versions to add: ${MAJOR_VERSIONS[*]}"
|
||||
|
||||
# Create/update versions.yaml file
|
||||
echo "Updating $VERSIONS_FILE..."
|
||||
{
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
echo "\"${major_ver}\": \"${VERSION_MAP[$major_ver]}\""
|
||||
done
|
||||
} > "$VERSIONS_FILE"
|
||||
|
||||
echo "Successfully updated $VERSIONS_FILE"
|
||||
|
||||
# Update values.yaml - enum with major versions only
|
||||
TEMP_FILE=$(mktemp)
|
||||
trap "rm -f $TEMP_FILE" EXIT
|
||||
|
||||
# Build new version section
|
||||
NEW_VERSION_SECTION="## @enum {string} Version"
|
||||
for major_ver in "${MAJOR_VERSIONS[@]}"; do
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
## @value $major_ver"
|
||||
done
|
||||
NEW_VERSION_SECTION="${NEW_VERSION_SECTION}
|
||||
|
||||
## @param {Version} version - Redis major version to deploy
|
||||
version: ${MAJOR_VERSIONS[0]}"
|
||||
|
||||
# Check if version section already exists
|
||||
if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then
|
||||
# Version section exists, update it using awk
|
||||
echo "Updating existing version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to replace the section from "## @enum {string} Version" to "version: " (inclusive)
|
||||
# Delete the old section and insert the new one
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @enum {string} Version/ {
|
||||
in_section = 1
|
||||
print new_section
|
||||
next
|
||||
}
|
||||
in_section && /^version: / {
|
||||
in_section = 0
|
||||
next
|
||||
}
|
||||
in_section {
|
||||
next
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
else
|
||||
# Version section doesn't exist, insert it before Application-specific parameters section
|
||||
echo "Inserting new version section in $VALUES_FILE..."
|
||||
|
||||
# Use awk to insert before "## @section Application-specific parameters"
|
||||
awk -v new_section="$NEW_VERSION_SECTION" '
|
||||
/^## @section Application-specific parameters/ {
|
||||
print new_section
|
||||
print ""
|
||||
}
|
||||
{ print }
|
||||
' "$VALUES_FILE" > "$TEMP_FILE.tmp"
|
||||
mv "$TEMP_FILE.tmp" "$VALUES_FILE"
|
||||
fi
|
||||
|
||||
echo "Successfully updated $VALUES_FILE with major versions: ${MAJOR_VERSIONS[*]}"
|
||||
|
||||
8
packages/apps/redis/templates/_versions.tpl
Normal file
8
packages/apps/redis/templates/_versions.tpl
Normal file
@@ -0,0 +1,8 @@
|
||||
{{- define "redis.versionMap" }}
|
||||
{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }}
|
||||
{{- if not (hasKey $versionMap .Values.version) }}
|
||||
{{- printf `Redis version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }}
|
||||
{{- end }}
|
||||
{{- index $versionMap .Values.version }}
|
||||
{{- end }}
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
replicas: 3
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }}
|
||||
redis:
|
||||
image: "redis:8.2.0"
|
||||
image: "redis:{{ include "redis.versionMap" $ }}"
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }}
|
||||
replicas: {{ .Values.replicas }}
|
||||
{{- with .Values.size }}
|
||||
|
||||
@@ -82,6 +82,15 @@
|
||||
"description": "StorageClass used to store the data.",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"version": {
|
||||
"description": "Redis major version to deploy",
|
||||
"type": "string",
|
||||
"default": "v8",
|
||||
"enum": [
|
||||
"v8",
|
||||
"v7"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,13 @@ storageClass: ""
|
||||
## @param {bool} external - Enable external access from outside the cluster.
|
||||
external: false
|
||||
|
||||
## @enum {string} Version
|
||||
## @value v8
|
||||
## @value v7
|
||||
|
||||
## @param {Version} version - Redis major version to deploy
|
||||
version: v8
|
||||
|
||||
##
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
@@ -39,13 +39,13 @@ roleRef:
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
|
||||
@@ -9,6 +9,9 @@ metadata:
|
||||
internal.cozystack.io/tenantmodule: "true"
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
apps.cozystack.io/application.kind: Etcd
|
||||
apps.cozystack.io/application.group: apps.cozystack.io
|
||||
apps.cozystack.io/application.name: etcd
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
@@ -28,4 +31,7 @@ spec:
|
||||
force: true
|
||||
remediation:
|
||||
retries: -1
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
name: cozystack-values
|
||||
{{- end }}
|
||||
|
||||
@@ -8,6 +8,9 @@ metadata:
|
||||
internal.cozystack.io/tenantmodule: "true"
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
apps.cozystack.io/application.kind: Info
|
||||
apps.cozystack.io/application.group: apps.cozystack.io
|
||||
apps.cozystack.io/application.name: info
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
@@ -27,3 +30,6 @@ spec:
|
||||
force: true
|
||||
remediation:
|
||||
retries: -1
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
name: cozystack-values
|
||||
|
||||
@@ -9,6 +9,9 @@ metadata:
|
||||
internal.cozystack.io/tenantmodule: "true"
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
apps.cozystack.io/application.kind: Ingress
|
||||
apps.cozystack.io/application.group: apps.cozystack.io
|
||||
apps.cozystack.io/application.name: ingress
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
@@ -28,4 +31,7 @@ spec:
|
||||
force: true
|
||||
remediation:
|
||||
retries: -1
|
||||
valuesFrom:
|
||||
- kind: Secret
|
||||
name: cozystack-values
|
||||
{{- end }}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }}
|
||||
{{- $oidcEnabled := index .Values._cluster "oidc-enabled" }}
|
||||
{{- if eq $oidcEnabled "true" }}
|
||||
{{- if .Capabilities.APIVersions.Has "v1.edp.epam.com/v1" }}
|
||||
apiVersion: v1.edp.epam.com/v1
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user