mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-03 13:38:56 +00:00
Compare commits
214 Commits
release-0.
...
lineage-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca61c71084 | ||
|
|
65a734bb65 | ||
|
|
07384c3605 | ||
|
|
87b2316194 | ||
|
|
585569f285 | ||
|
|
dbe1df8d27 | ||
|
|
17eb1e0ba3 | ||
|
|
b55c9f616d | ||
|
|
f025845a94 | ||
|
|
e54fc63af4 | ||
|
|
9352861051 | ||
|
|
b9eec3f261 | ||
|
|
f2cfb4f870 | ||
|
|
2291d0f7f2 | ||
|
|
15c100d262 | ||
|
|
2c9864bc09 | ||
|
|
bb1e8805dc | ||
|
|
08b5217b72 | ||
|
|
08d2d61f1a | ||
|
|
356fea6a37 | ||
|
|
e1b97e3727 | ||
|
|
ea27dc9497 | ||
|
|
f06c5d996d | ||
|
|
87c5540ad3 | ||
|
|
03e18ee02f | ||
|
|
382a9787f4 | ||
|
|
2bca6b932c | ||
|
|
601f6bd3c9 | ||
|
|
1243a960e3 | ||
|
|
4dd062d9cd | ||
|
|
3e03b1bd86 | ||
|
|
8f1975d1da | ||
|
|
e15ff2a4d0 | ||
|
|
272185a2df | ||
|
|
be8495dd06 | ||
|
|
7f477eec96 | ||
|
|
cc4b7ea28c | ||
|
|
8335347dc3 | ||
|
|
49d69a5896 | ||
|
|
89a74f653a | ||
|
|
9f2b98d364 | ||
|
|
7090b8adf1 | ||
|
|
c5b46fc79c | ||
|
|
a291badbd4 | ||
|
|
52d749d46a | ||
|
|
9f89ef36bb | ||
|
|
f59d072ef1 | ||
|
|
c0d5e52e65 | ||
|
|
034f71cc9d | ||
|
|
fdd4f167c6 | ||
|
|
8fbebd4e47 | ||
|
|
389ec27b19 | ||
|
|
29df1fdc1e | ||
|
|
c4e048b315 | ||
|
|
ce5fd9d292 | ||
|
|
8e906be9df | ||
|
|
99bfd4884f | ||
|
|
15b213b38b | ||
|
|
8ca8817000 | ||
|
|
9f8c79f5d1 | ||
|
|
ce21299280 | ||
|
|
403d1f9944 | ||
|
|
138e5fbe15 | ||
|
|
fe869b97fd | ||
|
|
a4aeeca2d3 | ||
|
|
33691c2d3a | ||
|
|
08f1bda1aa | ||
|
|
58f65abefd | ||
|
|
9c1563adb7 | ||
|
|
cbbb50b194 | ||
|
|
6684117a00 | ||
|
|
6b9b700177 | ||
|
|
89c80a8178 | ||
|
|
6b5af37e1a | ||
|
|
6cd5e746c8 | ||
|
|
ffa28d0dc0 | ||
|
|
c10f6240b1 | ||
|
|
1ce2df9bc4 | ||
|
|
7690bc6e8a | ||
|
|
a227825336 | ||
|
|
f09fd0b574 | ||
|
|
39042fa04d | ||
|
|
909f55c74e | ||
|
|
32a857fbf2 | ||
|
|
d3bce65081 | ||
|
|
868148709c | ||
|
|
a2134ecce7 | ||
|
|
a1bc9178e3 | ||
|
|
8b49e74a31 | ||
|
|
60965df051 | ||
|
|
4d7992b55a | ||
|
|
c5b64af7e0 | ||
|
|
46c2ee3c31 | ||
|
|
ba6460ea10 | ||
|
|
40b83cab79 | ||
|
|
1743b5d2b3 | ||
|
|
d360c179d1 | ||
|
|
90f6169bad | ||
|
|
64a8a158c3 | ||
|
|
e3a4e284de | ||
|
|
2ef11ff513 | ||
|
|
066571a11e | ||
|
|
41c0c6d829 | ||
|
|
9629ee7298 | ||
|
|
d430048ba3 | ||
|
|
992162f507 | ||
|
|
fbc2c45e7f | ||
|
|
7acd8a2a80 | ||
|
|
21d6c69f73 | ||
|
|
c02a381819 | ||
|
|
c032a4ad49 | ||
|
|
c0f742595f | ||
|
|
168a24ffdf | ||
|
|
f864b40a85 | ||
|
|
39fb4ec8ab | ||
|
|
92f206cb93 | ||
|
|
634b77edad | ||
|
|
e091fa580f | ||
|
|
b1afaf71ca | ||
|
|
70b03ad61a | ||
|
|
a32de78c7c | ||
|
|
330103cc2b | ||
|
|
8b1e55dec2 | ||
|
|
da3f133d89 | ||
|
|
19baa7b14f | ||
|
|
502d31fe8d | ||
|
|
5359c6d991 | ||
|
|
8d4a12e14f | ||
|
|
771fbc817f | ||
|
|
bc22b22341 | ||
|
|
cffff6c49e | ||
|
|
39adc16015 | ||
|
|
896209a004 | ||
|
|
d48b5cfa2f | ||
|
|
c6bceff54b | ||
|
|
ff3305f43c | ||
|
|
58def95f67 | ||
|
|
9bc3b636a2 | ||
|
|
895597eecb | ||
|
|
a91e829cc9 | ||
|
|
be31370540 | ||
|
|
b26dc63b01 | ||
|
|
fafa859660 | ||
|
|
6e119ba940 | ||
|
|
754d5a976d | ||
|
|
c4a2bef4c9 | ||
|
|
cd80a73446 | ||
|
|
299d006d20 | ||
|
|
85063cf624 | ||
|
|
c74df866e6 | ||
|
|
080289fa00 | ||
|
|
98f86269f3 | ||
|
|
44fabd4abc | ||
|
|
8ddbe32ea1 | ||
|
|
432ddf6abc | ||
|
|
9d184a098f | ||
|
|
1c2cc0fa28 | ||
|
|
24807cb679 | ||
|
|
cd8e8bee0a | ||
|
|
856720004f | ||
|
|
d1ad5ff222 | ||
|
|
c81c9d255a | ||
|
|
f057d92a4d | ||
|
|
1ab63187c9 | ||
|
|
2fa56fc1e1 | ||
|
|
36ccfb9509 | ||
|
|
cb3cb99d06 | ||
|
|
8704767ac5 | ||
|
|
03c4bf904f | ||
|
|
dca2eb7ae8 | ||
|
|
1d9465d662 | ||
|
|
53241efe63 | ||
|
|
940b0b18b0 | ||
|
|
824c72318a | ||
|
|
0d7e856186 | ||
|
|
2897813dda | ||
|
|
e3a61b23af | ||
|
|
7918e282bf | ||
|
|
0e428810fd | ||
|
|
fa4fff2292 | ||
|
|
0e875b17d1 | ||
|
|
efb2c632e2 | ||
|
|
8951bc13d7 | ||
|
|
830ec252b9 | ||
|
|
730584bd15 | ||
|
|
0e47e1e8ac | ||
|
|
9617071ada | ||
|
|
3b32bfe149 | ||
|
|
d9a5e9d628 | ||
|
|
0feeaadb9c | ||
|
|
8fac3bfcb1 | ||
|
|
b1e4ebeafc | ||
|
|
2f61798fa8 | ||
|
|
02436f312f | ||
|
|
68a47097c1 | ||
|
|
917a6f354d | ||
|
|
847a834920 | ||
|
|
3088e987e3 | ||
|
|
fddeea03f0 | ||
|
|
2fefafd061 | ||
|
|
084be87618 | ||
|
|
6598213b58 | ||
|
|
4079a69335 | ||
|
|
553c2d5482 | ||
|
|
0c9ab17a12 | ||
|
|
5e8f6e0503 | ||
|
|
f04cd55f2a | ||
|
|
53d9cf365d | ||
|
|
94e2fd0ff9 | ||
|
|
0618446b95 | ||
|
|
640d0f10ac | ||
|
|
a03530a72f | ||
|
|
3612bbd8ca | ||
|
|
028bb365ff |
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/readme-generator-for-helm/releases/download/v1.0.0/readme-generator-for-helm-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ readme-generator-for-helm
|
||||
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v0.8.5/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
|
||||
|
||||
- name: Run pre-commit hooks
|
||||
run: |
|
||||
|
||||
18
.github/workflows/pull-requests.yaml
vendored
18
.github/workflows/pull-requests.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Pull Request
|
||||
|
||||
env:
|
||||
REGISTRY: ${{ secrets.OCIR_REPO }}
|
||||
REGISTRY: ${{ vars.OCIR_REPO }}
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
@@ -32,7 +32,14 @@ jobs:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Set up Docker config
|
||||
run: |
|
||||
if [ -d ~/.docker ]; then
|
||||
cp -r ~/.docker "${{ runner.temp }}/.docker"
|
||||
fi
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.OCIR_USER}}
|
||||
@@ -254,6 +261,11 @@ jobs:
|
||||
done
|
||||
echo "✅ The task completed successfully after $attempt attempts."
|
||||
|
||||
- name: Run OpenAPI tests
|
||||
run: |
|
||||
cd /tmp/$SANDBOX_NAME
|
||||
make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-openapi
|
||||
|
||||
detect_test_matrix:
|
||||
name: "Detect e2e test matrix"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -264,12 +276,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- id: set
|
||||
run: |
|
||||
apps=$(find hack/e2e-apps -maxdepth 1 -mindepth 1 -name '*.bats' | \
|
||||
awk -F/ '{sub(/\..+/, "", $NF); print $NF}' | jq -R . | jq -cs .)
|
||||
apps=$(ls hack/e2e-apps/*.bats | cut -f3 -d/ | cut -f1 -d. | jq -R | jq -cs)
|
||||
echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
test_apps:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.detect_test_matrix.outputs.matrix) }}
|
||||
name: Test ${{ matrix.app }}
|
||||
runs-on: [self-hosted]
|
||||
|
||||
23
.github/workflows/tags.yaml
vendored
23
.github/workflows/tags.yaml
vendored
@@ -149,36 +149,35 @@ jobs:
|
||||
version: ${{ steps.tag.outputs.tag }} # A
|
||||
compare-to: ${{ steps.latest_release.outputs.tag }} # B
|
||||
|
||||
# Create or reuse DRAFT GitHub Release
|
||||
# Create or reuse draft release
|
||||
- name: Create / reuse draft release
|
||||
if: steps.check_release.outputs.skip == 'false'
|
||||
id: release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const tag = '${{ steps.tag.outputs.tag }}';
|
||||
const isRc = ${{ steps.tag.outputs.is_rc }};
|
||||
const outdated = '${{ steps.semver.outputs.comparison-result }}' === '<';
|
||||
const makeLatest = outdated ? false : 'legacy';
|
||||
const releases = await github.rest.repos.listReleases({
|
||||
const tag = '${{ steps.tag.outputs.tag }}';
|
||||
const isRc = ${{ steps.tag.outputs.is_rc }};
|
||||
const releases = await github.rest.repos.listReleases({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
let rel = releases.data.find(r => r.tag_name === tag);
|
||||
|
||||
let rel = releases.data.find(r => r.tag_name === tag);
|
||||
if (!rel) {
|
||||
rel = await github.rest.repos.createRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag_name: tag,
|
||||
name: tag,
|
||||
draft: true,
|
||||
prerelease: isRc,
|
||||
make_latest: makeLatest
|
||||
tag_name: tag,
|
||||
name: tag,
|
||||
draft: true,
|
||||
prerelease: isRc // no make_latest for drafts
|
||||
});
|
||||
console.log(`Draft release created for ${tag}`);
|
||||
} else {
|
||||
console.log(`Re-using existing release ${tag}`);
|
||||
}
|
||||
|
||||
core.setOutput('upload_url', rel.upload_url);
|
||||
|
||||
# Build + upload assets (optional)
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -77,3 +77,5 @@ fabric.properties
|
||||
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
|
||||
tmp/
|
||||
|
||||
2
Makefile
2
Makefile
@@ -18,10 +18,12 @@ build: build-deps
|
||||
make -C packages/system/cilium image
|
||||
make -C packages/system/kubeovn image
|
||||
make -C packages/system/kubeovn-webhook image
|
||||
make -C packages/system/kubeovn-plunger image
|
||||
make -C packages/system/dashboard image
|
||||
make -C packages/system/metallb image
|
||||
make -C packages/system/kamaji image
|
||||
make -C packages/system/bucket image
|
||||
make -C packages/system/objectstorage-controller image
|
||||
make -C packages/core/testing image
|
||||
make -C packages/core/installer image
|
||||
make manifests
|
||||
|
||||
89
api/v1alpha1/cozystackresourcedefinitions_types.go
Normal file
89
api/v1alpha1/cozystackresourcedefinitions_types.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API
|
||||
type CozystackResourceDefinition struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec CozystackResourceDefinitionSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// CozystackResourceDefinitionList contains a list of CozystackResourceDefinition
|
||||
type CozystackResourceDefinitionList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []CozystackResourceDefinition `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&CozystackResourceDefinition{}, &CozystackResourceDefinitionList{})
|
||||
}
|
||||
|
||||
type CozystackResourceDefinitionSpec struct {
|
||||
// Application configuration
|
||||
Application CozystackResourceDefinitionApplication `json:"application"`
|
||||
// Release configuration
|
||||
Release CozystackResourceDefinitionRelease `json:"release"`
|
||||
}
|
||||
|
||||
type CozystackResourceDefinitionChart struct {
|
||||
// Name of the Helm chart
|
||||
Name string `json:"name"`
|
||||
// Source reference for the Helm chart
|
||||
SourceRef SourceRef `json:"sourceRef"`
|
||||
}
|
||||
|
||||
type SourceRef struct {
|
||||
// Kind of the source reference
|
||||
// +kubebuilder:default:="HelmRepository"
|
||||
Kind string `json:"kind"`
|
||||
// Name of the source reference
|
||||
Name string `json:"name"`
|
||||
// Namespace of the source reference
|
||||
// +kubebuilder:default:="cozy-public"
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
type CozystackResourceDefinitionApplication struct {
|
||||
// Kind of the application, used for UI and API
|
||||
Kind string `json:"kind"`
|
||||
// OpenAPI schema for the application, used for API validation
|
||||
OpenAPISchema string `json:"openAPISchema"`
|
||||
// Plural name of the application, used for UI and API
|
||||
Plural string `json:"plural"`
|
||||
// Singular name of the application, used for UI and API
|
||||
Singular string `json:"singular"`
|
||||
}
|
||||
|
||||
type CozystackResourceDefinitionRelease struct {
|
||||
// Helm chart configuration
|
||||
Chart CozystackResourceDefinitionChart `json:"chart"`
|
||||
// Labels for the release
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
// Prefix for the release name
|
||||
Prefix string `json:"prefix"`
|
||||
}
|
||||
@@ -25,6 +25,135 @@ import (
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinition.
|
||||
func (in *CozystackResourceDefinition) DeepCopy() *CozystackResourceDefinition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *CozystackResourceDefinition) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinitionApplication) DeepCopyInto(out *CozystackResourceDefinitionApplication) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionApplication.
|
||||
func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceDefinitionApplication {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinitionApplication)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinitionChart) DeepCopyInto(out *CozystackResourceDefinitionChart) {
|
||||
*out = *in
|
||||
out.SourceRef = in.SourceRef
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionChart.
|
||||
func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinitionChart {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinitionChart)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]CozystackResourceDefinition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionList.
|
||||
func (in *CozystackResourceDefinitionList) DeepCopy() *CozystackResourceDefinitionList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinitionList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) {
|
||||
*out = *in
|
||||
out.Chart = in.Chart
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionRelease.
|
||||
func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefinitionRelease {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinitionRelease)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) {
|
||||
*out = *in
|
||||
out.Application = in.Application
|
||||
in.Release.DeepCopyInto(&out.Release)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.
|
||||
func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefinitionSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CozystackResourceDefinitionSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in Selector) DeepCopyInto(out *Selector) {
|
||||
{
|
||||
@@ -46,6 +175,21 @@ func (in Selector) DeepCopy() Selector {
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SourceRef) DeepCopyInto(out *SourceRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceRef.
|
||||
func (in *SourceRef) DeepCopy() *SourceRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SourceRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Workload) DeepCopyInto(out *Workload) {
|
||||
*out = *in
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
|
||||
cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/controller"
|
||||
"github.com/cozystack/cozystack/internal/controller/lineagelabeler"
|
||||
"github.com/cozystack/cozystack/internal/telemetry"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
@@ -67,6 +68,7 @@ func main() {
|
||||
var telemetryEndpoint string
|
||||
var telemetryInterval string
|
||||
var cozystackVersion string
|
||||
var watchResources string
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
@@ -86,6 +88,9 @@ func main() {
|
||||
"Interval between telemetry data collection (e.g. 15m, 1h)")
|
||||
flag.StringVar(&cozystackVersion, "cozystack-version", "unknown",
|
||||
"Version of Cozystack")
|
||||
flag.StringVar(&watchResources, "watch-resources",
|
||||
"v1/Pod,v1/Service,v1/Secret,v1/PersistentVolumeClaim",
|
||||
"Comma-separated list of resources to watch in the form 'group/version/Kind'.")
|
||||
opts := zap.Options{
|
||||
Development: false,
|
||||
}
|
||||
@@ -206,6 +211,23 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.CozystackResourceDefinitionReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := (&lineagelabeler.LineageLabelerReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
WatchResourceCSV: watchResources,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "LineageLabeler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
176
cmd/kubeovn-plunger/main.go
Normal file
176
cmd/kubeovn-plunger/main.go
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/controller/kubeovnplunger"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
var enableLeaderElection bool
|
||||
var probeAddr string
|
||||
var kubeOVNNamespace string
|
||||
var ovnCentralName string
|
||||
var secureMetrics bool
|
||||
var enableHTTP2 bool
|
||||
var disableTelemetry bool
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.StringVar(&kubeOVNNamespace, "kube-ovn-namespace", "cozy-kubeovn", "Namespace where kube-OVN is deployed.")
|
||||
flag.StringVar(&ovnCentralName, "ovn-central-name", "ovn-central", "Ovn-central deployment name.")
|
||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.BoolVar(&secureMetrics, "metrics-secure", true,
|
||||
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
|
||||
flag.BoolVar(&enableHTTP2, "enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
flag.BoolVar(&disableTelemetry, "disable-telemetry", false,
|
||||
"Disable telemetry collection")
|
||||
opts := zap.Options{
|
||||
Development: false,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
// due to its vulnerabilities. More specifically, disabling http/2 will
|
||||
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
|
||||
// Rapid Reset CVEs. For more information see:
|
||||
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
|
||||
// - https://github.com/advisories/GHSA-4374-p667-p6c8
|
||||
disableHTTP2 := func(c *tls.Config) {
|
||||
setupLog.Info("disabling http/2")
|
||||
c.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
if !enableHTTP2 {
|
||||
tlsOpts = append(tlsOpts, disableHTTP2)
|
||||
}
|
||||
|
||||
webhookServer := webhook.NewServer(webhook.Options{
|
||||
TLSOpts: tlsOpts,
|
||||
})
|
||||
|
||||
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
|
||||
// More info:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server
|
||||
// - https://book.kubebuilder.io/reference/metrics.html
|
||||
metricsServerOptions := metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
SecureServing: secureMetrics,
|
||||
TLSOpts: tlsOpts,
|
||||
}
|
||||
|
||||
if secureMetrics {
|
||||
// FilterProvider is used to protect the metrics endpoint with authn/authz.
|
||||
// These configurations ensure that only authorized users and service accounts
|
||||
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
|
||||
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization
|
||||
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
|
||||
|
||||
// TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically
|
||||
// generate self-signed certificates for the metrics server. While convenient for development and testing,
|
||||
// this setup is not recommended for production.
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsServerOptions,
|
||||
WebhookServer: webhookServer,
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "29a0338b.cozystack.io",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&kubeovnplunger.KubeOVNPlunger{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Registry: metrics.Registry,
|
||||
}).SetupWithManager(mgr, kubeOVNNamespace, ovnCentralName); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "KubeOVNPlunger")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
3359
dashboards/seaweedfs/seaweedfs.json
Normal file
3359
dashboards/seaweedfs/seaweedfs.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,8 @@
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0..
|
||||
-->
|
||||
|
||||
## Major Features and Improvements
|
||||
|
||||
## Security
|
||||
@@ -9,3 +14,7 @@
|
||||
## Documentation
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: **Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0
|
||||
|
||||
@@ -129,7 +129,7 @@ For more information, read the [Cozystack Release Workflow](https://github.com/c
|
||||
* [platform] Reduce requested CPU and RAM for the `kamaji` provider. (@klinch0 in https://github.com/cozystack/cozystack/pull/825)
|
||||
* [platform] Improve the reconciliation loop for the Cozystack system HelmReleases logic. (@klinch0 in https://github.com/cozystack/cozystack/pull/809 and https://github.com/cozystack/cozystack/pull/810, @kvaps in https://github.com/cozystack/cozystack/pull/811)
|
||||
* [platform] Remove extra dependencies for the Piraeus operator. (@klinch0 in https://github.com/cozystack/cozystack/pull/856)
|
||||
* [platform] Refactor dashboard values. (@kvaps in https://github.com/cozystack/cozystack/pull/928, patched by @llamnyp in https://github.com/cozystack/cozystack/pull/952)
|
||||
* [platform] Refactor dashboard values. (@kvaps in https://github.com/cozystack/cozystack/pull/928, patched by @lllamnyp in https://github.com/cozystack/cozystack/pull/952)
|
||||
* [platform] Make FluxCD artifact disabled by default. (@klinch0 in https://github.com/cozystack/cozystack/pull/964)
|
||||
* [kubernetes] Update garbage collection of HelmReleases in tenant Kubernetes clusters. (@kvaps in https://github.com/cozystack/cozystack/pull/835)
|
||||
* [kubernetes] Fix merging `valuesOverride` for tenant clusters. (@kvaps in https://github.com/cozystack/cozystack/pull/879)
|
||||
|
||||
87
docs/changelogs/v0.34.0.md
Normal file
87
docs/changelogs/v0.34.0.md
Normal file
@@ -0,0 +1,87 @@
|
||||
Cozystack v0.34.0 is a stable release.
|
||||
It focuses on cluster reliability, virtualization capabilities, and enhancements to the Cozystack API.
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.0
|
||||
-->
|
||||
|
||||
> [!WARNING]
|
||||
> A regression was found in this release and fixed in patch [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
> When upgrading Cozystack, it's recommended to skip this version and upgrade directly to [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
|
||||
|
||||
## Major Features and Improvements
|
||||
|
||||
* [kubernetes] Enable users to select Kubernetes versions in tenant clusters. Supported versions range from 1.28 to 1.33, updated to the latest patches. (@lllamnyp and @IvanHunters in https://github.com/cozystack/cozystack/pull/1202)
|
||||
* [kubernetes] Enable PVC snapshot capability in tenant Kubernetes clusters. (@klinch0 in https://github.com/cozystack/cozystack/pull/1203)
|
||||
* [vpa] Implement autoscaling for the Vertical Pod Autoscaler itself, ensuring that VPA has sufficient resources and reducing the number of configuration parameters that platform administrators have to manage. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1198)
|
||||
* [vm-instance] Enable running [Windows](https://cozystack.io/docs/operations/virtualization/windows/) and [MikroTik RouterOS](https://cozystack.io/docs/operations/virtualization/mikrotik/) in Cozystack. Add `bus` option and always specify `bootOrder` for all disks. (@kvaps in https://github.com/cozystack/cozystack/pull/1168)
|
||||
* [cozystack-api] Specify OpenAPI schema for apps. (@kvaps in https://github.com/cozystack/cozystack/pull/1174)
|
||||
* [cozystack-api] Refactor OpenAPI Schema and support reading it from config. (@kvaps in https://github.com/cozystack/cozystack/pull/1173)
|
||||
* [cozystack-api] Enable using singular resource names in Cozystack API. For example, `kubectl get tenant` is now a valid command, in addition to `kubectl get tenants`. (@kvaps in https://github.com/cozystack/cozystack/pull/1169)
|
||||
* [postgres] Explain how to back up and restore PostgreSQL using Velero backups. (@klinch0 and @NickVolynkin in https://github.com/cozystack/cozystack/pull/1141)
|
||||
* [seaweedfs] Support multi-zone configuration for S3 storage. (@kvaps in https://github.com/cozystack/cozystack/pull/1194)
|
||||
* [dashboard] Put YAML editor first when deploying and upgrading applications, as a more powerful option. Fix handling multiline strings. (@kvaps in https://github.com/cozystack/cozystack/pull/1227)
|
||||
|
||||
## Security
|
||||
|
||||
* [seaweedfs] Ensure that JWT signing keys in the SeaweedFS security configuration remain consistent across Helm upgrades. Resolve an upstream issue. (@kvaps in https://github.com/cozystack/cozystack/pull/1193 and https://github.com/seaweedfs/seaweedfs/pull/6967)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [cozystack-controller] Fix stale workloads not being deleted when marked for deletion. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1210, @kvaps in https://github.com/cozystack/cozystack/pull/1229)
|
||||
* [cozystack-controller] Improve reliability when updating HelmRelease objects to prevent unintended changes during reconciliation. (@klinch0 in https://github.com/cozystack/cozystack/pull/1205)
|
||||
* [kubevirt-csi] Fix a regression by updating the role of the CSI controller. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1165)
|
||||
* [virtual-machine,vm-instance] Adjusted RBAC role to let users read the service associated with the VMs they create. Consequently, users can now see details of the service in the dashboard and therefore read the IP address of the VM. (@klinch0 in https://github.com/cozystack/cozystack/pull/1161)
|
||||
* [virtual-machine] Fix cloudInit and sshKeys processing. (@kvaps in https://github.com/cozystack/cozystack/pull/1175 and https://github.com/cozystack/cozystack/commit/da3ee5d0ea9e87529c8adc4fcccffabe8782292e)
|
||||
* [cozystack-api] Fix an error with `resourceVersion` which resulted in message 'failed to update HelmRelease: helmreleases.helm.toolkit.fluxcd.io "xxx" is invalid...'. (@kvaps in https://github.com/cozystack/cozystack/pull/1170)
|
||||
* [cozystack-api] Fix an error in updating lists in Cozystack objects, which resulted in message "Warning: resource ... is missing the kubectl.kubernetes.io/last-applied-configuration annotation". (@kvaps in https://github.com/cozystack/cozystack/pull/1171)
|
||||
* [cozystack-api] Disable `strategic-json-patch` support. (@kvaps in https://github.com/cozystack/cozystack/pull/1179)
|
||||
* [cozystack-api] Fix non-existing OpenAPI references. (@kvaps in https://github.com/cozystack/cozystack/pull/1208)
|
||||
* [dashboard] Fix the code for removing dashboard comments which used to mistakenly remove shebang from `cloudInit` scripts. (@kvaps in https://github.com/cozystack/cozystack/pull/1175).
|
||||
* [applications] Reorder configuration values in application README's for better readability. (@NickVolynkin in https://github.com/cozystack/cozystack/pull/1214)
|
||||
* [applications] Disallow selecting `resourcePreset = none` in the visual editor when deploying and upgrading applications. (@NickVolynkin in https://github.com/cozystack/cozystack/pull/1196)
|
||||
* [applications] Fix a typo in preset resource tables in the built-in documentation of managed applications. (@NickVolynkin in https://github.com/cozystack/cozystack/pull/1172)
|
||||
* [kubernetes] Enable deleting Velero component from a tenant Kubernetes cluster. (@klinch0 in https://github.com/cozystack/cozystack/pull/1176)
|
||||
* [kubernetes] Explicitly mention available K8s versions for tenant clusters in the README. (@NickVolynkin in https://github.com/cozystack/cozystack/pull/1212)
|
||||
* [oidc] Enable deleting Keycloak service. (@klinch0 in https://github.com/cozystack/cozystack/pull/1178)
|
||||
* [tenant] Enable deleting extra applications from a tenant. (@klinch0 and @kvaps and in https://github.com/cozystack/cozystack/pull/1162)
|
||||
* [nats] Fix a typo in the application template. (@klinch0 in https://github.com/cozystack/cozystack/pull/1195)
|
||||
* [postgres] Resolve an issue with the visibility of PostgreSQL load balancer on the dashboard. (@klinch0 https://github.com/cozystack/cozystack/pull/1204)
|
||||
* [objectstorage] Update COSI controller and sidecar, including fixes from upstream. (@kvaps in https://github.com/cozystack/cozystack/pull/1209, https://github.com/kubernetes-sigs/container-object-storage-interface/pull/89, and https://github.com/kubernetes-sigs/container-object-storage-interface/pull/90)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Update FerretDB from v1 to v2.4.0.<br>**Breaking change:** before upgrading FerretDB instances, back up and restore the data following the [migration guide](https://docs.ferretdb.io/migration/migrating-from-v1/). (@kvaps in https://github.com/cozystack/cozystack/pull/1206)
|
||||
* Update Talos Linux to v1.10.5. (@kvaps in https://github.com/cozystack/cozystack/pull/1186)
|
||||
* Update LINSTOR to v1.31.2. (@kvaps in https://github.com/cozystack/cozystack/pull/1180)
|
||||
* Update KubeVirt to v1.5.2. (@kvaps in https://github.com/cozystack/cozystack/pull/1183)
|
||||
* Update CDI to v1.62.0. (@kvaps in https://github.com/cozystack/cozystack/pull/1183)
|
||||
* Update Flux Operator to 0.24.0. (@kingdonb in https://github.com/cozystack/cozystack/pull/1167)
|
||||
* Update Kamaji to edge-25.7.1. (@kvaps in https://github.com/cozystack/cozystack/pull/1184)
|
||||
* Update Kube-OVN to v1.13.14. (@kvaps in https://github.com/cozystack/cozystack/pull/1182)
|
||||
* Update Cilium to v1.17.5. (@kvaps in https://github.com/cozystack/cozystack/pull/1181)
|
||||
* Update MariaDB Operator to v0.38.1. (@kvaps in https://github.com/cozystack/cozystack/pull/1188)
|
||||
* Update SeaweedFS to v3.94. (@kvaps in https://github.com/cozystack/cozystack/pull/1194)
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Updated Cozystack Roadmap and Backlog for 2024-2026](https://cozystack.io/docs/roadmap/). (@tym83 and @kvapsova in https://github.com/cozystack/website/pull/249)
|
||||
* [Running Windows VMs](https://cozystack.io/docs/operations/virtualization/windows/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/246)
|
||||
* [Running MikroTik RouterOS VMs](https://cozystack.io/docs/operations/virtualization/mikrotik/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/247)
|
||||
* [Public-network Kubernetes Deployment](https://cozystack.io/docs/operations/faq/#public-network-kubernetes-deployment). (@klinch0 and @NickVolynkin in https://github.com/cozystack/website/pull/242)
|
||||
* [How to allocate space on system disk for user storage](https://cozystack.io/docs/operations/faq/#how-to-allocate-space-on-system-disk-for-user-storage). (@klinch0 and @NickVolynkin in https://github.com/cozystack/website/pull/242)
|
||||
* [Resource Management in Cozystack](https://cozystack.io/docs/guides/resource-management/). (@NickVolynkin in https://github.com/cozystack/website/pull/233)
|
||||
* [Key Concepts of Cozystack](https://cozystack.io/docs/guides/concepts/). (@NickVolynkin in https://github.com/cozystack/website/pull/254)
|
||||
* [Cozystack Architecture and Platform Stack](https://cozystack.io/docs/guides/platform-stack/). (@NickVolynkin in https://github.com/cozystack/website/pull/252)
|
||||
* Fixed a parameter in Kubespan: `cluster.discovery.enabled = true`. (@lb0o in https://github.com/cozystack/website/pull/241)
|
||||
* Updated the Linux Foundation trademark text on the Cozystack website. (@krook in https://github.com/cozystack/website/pull/251)
|
||||
* Auto-update the managed applications reference pages. (@NickVolynkin in https://github.com/cozystack/website/pull/243 and https://github.com/cozystack/website/pull/245)
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
* [ci] Improve workflow for contributors submitting PRs from forks. Use Oracle Cloud Infrastructure Registry for non-release PRs, bypassing restrictions preventing pushing to ghcr.io with default GitHub token. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1226)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.33.0...v0.34.0
|
||||
15
docs/changelogs/v0.34.1.md
Normal file
15
docs/changelogs/v0.34.1.md
Normal file
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.1
|
||||
-->
|
||||
|
||||
> [!WARNING]
|
||||
> A regression was found in this release and fixed in patch [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
> When upgrading Cozystack, it's recommended to skip this version and upgrade directly to [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
* [kubernetes] Fix regression in `volumesnapshotclass` installation from https://github.com/cozystack/cozystack/pull/1203. (@kvaps in https://github.com/cozystack/cozystack/pull/1238)
|
||||
* [objectstorage] Fix building objectstorage images. (@kvaps in https://github.com/cozystack/cozystack/commit/a9e9dfca1fadde1bf2b4e100753e0731bbcfe923)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.34.1
|
||||
14
docs/changelogs/v0.34.2.md
Normal file
14
docs/changelogs/v0.34.2.md
Normal file
@@ -0,0 +1,14 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.2
|
||||
-->
|
||||
|
||||
> [!WARNING]
|
||||
> A regression was found in this release and fixed in patch [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
> When upgrading Cozystack, it's recommended to skip this version and upgrade directly to [0.34.3](https://github.com/cozystack/cozystack/releases/tag/v0.34.3).
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
* [objectstorage] Fix recording image in objectstorage. (@kvaps in https://github.com/cozystack/cozystack/commit/4d9a8389d6bc7e86d63dd976ec853b374a91a637)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.1...v0.34.2
|
||||
13
docs/changelogs/v0.34.3.md
Normal file
13
docs/changelogs/v0.34.3.md
Normal file
@@ -0,0 +1,13 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.3
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [tenant] Fix tenant network policy to allow traffic to additional tenant-related services across namespace hierarchies. (@klinch0 in https://github.com/cozystack/cozystack/pull/1232, backported in https://github.com/cozystack/cozystack/pull/1272)
|
||||
* [kubernetes] Add dependency for snapshot CRD and migration to latest version. (@kvaps in https://github.com/cozystack/cozystack/pull/1275, backported in https://github.com/cozystack/cozystack/pull/1279)
|
||||
* [seaweedfs] Add support for whitelisting and exporting via nginx-ingress. Update cosi-driver. (@kvaps in https://github.com/cozystack/cozystack/pull/1277)
|
||||
* [kubevirt] Fix building Kubevirt CCM (@kvaps in 3c7e256906e1dbb0f957dc3a205fa77a147d419d)
|
||||
* [virtual-machine] Fix a regression with field `optional=true`. (@kvaps in https://github.com/cozystack/cozystack/commit/01053f7c3180d1bd045d7c5fb949984c2bdaf19d)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.2...v0.34.3
|
||||
21
docs/changelogs/v0.34.4.md
Normal file
21
docs/changelogs/v0.34.4.md
Normal file
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.4
|
||||
-->
|
||||
|
||||
## Security
|
||||
|
||||
* [keycloak] Store administrative passwords in the management cluster's secrets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1286)
|
||||
* [keycloak] Update Keycloak client redirect URI to use HTTPS instead of HTTP. Enable `cookie-secure`. (@klinch0 in https://github.com/cozystack/cozystack/pull/1287, backported in https://github.com/cozystack/cozystack/pull/1291)
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
* [kubernetes] Resolve problems with pod names exceeding allowed length by shortening the name of volume snapshot CRD from `*-volumesnapshot-crd-for-tenant-k8s` to `*-vsnap-crd`. To apply this change, update each affected tenant Kubernetes cluster after updating Cozystack. (@klinch0 in https://github.com/cozystack/cozystack/pull/1284)
|
||||
* [cozystack-api] Show correct `kind` values of `ApplicationList`. (@kvaps in https://github.com/cozystack/cozystack/pull/1290, backported in https://github.com/cozystack/cozystack/pull/1293)
|
||||
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
* [tests] Add tests for S3 buckets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1283, backported in https://github.com/cozystack/cozystack/pull/1292)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.3...v0.34.4
|
||||
11
docs/changelogs/v0.34.5.md
Normal file
11
docs/changelogs/v0.34.5.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.5
|
||||
-->
|
||||
|
||||
|
||||
## Fixes
|
||||
|
||||
* [virtual-machine] Enable using custom `instanceType` values in `virtual-machine` and `vm-instance` by disabling field validation. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1300, backported in https://github.com/cozystack/cozystack/pull/1303)
|
||||
* [kubernetes] Disable VPA for VPA in tenant Kubernetes clusters. Tenant clusters have no need for this feature, and it was not designed to work in a tenant cluster, but was enabled by mistake. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1301, backported in https://github.com/cozystack/cozystack/pull/1305)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.4...v0.34.5
|
||||
9
docs/changelogs/v0.34.6.md
Normal file
9
docs/changelogs/v0.34.6.md
Normal file
@@ -0,0 +1,9 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.6
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [dashboard] Fix filling multiline values in the visual editor. (@kvaps in https://github.com/cozystack/cozystack/commit/56fca9bd75efeca25f9483f6c514b6fec26d5d22 and https://github.com/cozystack/kubeapps/commit/4926bc68fabb0914afab574006643c85a597b371)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.5...v0.34.6
|
||||
11
docs/changelogs/v0.34.7.md
Normal file
11
docs/changelogs/v0.34.7.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.7
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [seaweedfs] Disable proxy buffering and proxy request buffering for ingress. (@kvaps in https://github.com/cozystack/cozystack/pull/1330, backported in https://github.com/cozystack/cozystack/commit/96d462e911d4458704b596533d3f10e4b5e80862)
|
||||
* [linstor] Update LINSTOR monitoring configuration to use label `controller_node` instead of `node`. (@kvaps in https://github.com/cozystack/cozystack/pull/1326, backported in https://github.com/cozystack/cozystack/pull/1327)
|
||||
* [kubernetes] Disable VPA for VPA in tenant Kubernetes clusters, patched a fix from v0.34.5. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1318, backported in https://github.com/cozystack/cozystack/pull/1319)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.6...v0.34.7
|
||||
11
docs/changelogs/v0.34.8.md
Normal file
11
docs/changelogs/v0.34.8.md
Normal file
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.34.8
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [etcd] Fix `topologySpreadConstraints`. (@klinch0 in https://github.com/cozystack/cozystack/pull/1331, backported in https://github.com/cozystack/cozystack/pull/1332)
|
||||
* [linstor] Update LINSTOR monitoring configuration: switch labels on `linstor-satellite` and `linstor-controller`. (@kvaps in https://github.com/cozystack/cozystack/pull/1335, backported in https://github.com/cozystack/cozystack/pull/1336)
|
||||
* [kamaji] Fix broken migration jobs originating from missing environment variables in the in-tree build. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1338, backported in https://github.com/cozystack/cozystack/pull/1340)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.7...v0.34.8
|
||||
138
docs/changelogs/v0.35.0.md
Normal file
138
docs/changelogs/v0.35.0.md
Normal file
@@ -0,0 +1,138 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.0
|
||||
-->
|
||||
|
||||
## Feature Highlights
|
||||
|
||||
### External Application Sources in Cozystack
|
||||
|
||||
Cozystack now supports adding external application packages to the platform's application catalog.
|
||||
Platform administrators can include custom or third-party applications alongside built-in ones, using the Cozystack API.
|
||||
|
||||
Adding an application requires making an application package, similar to the ones included in Cozystack
|
||||
under [`packages/apps`](https://github.com/cozystack/cozystack/tree/main/packages/apps).
|
||||
Using external packages is enabled by a new CustomResourceDefinition (CRD) called `CozystackResourceDefinition` and
|
||||
a corresponding controller (reconciler) that watches for these resources.
|
||||
|
||||
Add your own managed application using the [documentation](https://cozystack.io/docs/applications/external/)
|
||||
and an example at [github.com/cozystack/external-apps-example](https://github.com/cozystack/external-apps-example).
|
||||
|
||||
<!--
|
||||
* [platform] Enable using external application packages by adding a `CozystackResourceDefinition` reconciler. Read the documentation on [adding external applications to Cozystack](https://cozystack.io/docs/applications/external/) to learn more. (@klinch0 in https://github.com/cozystack/cozystack/pull/1313)
|
||||
* [cozystack-api] Provide an API for administrators to define custom managed applications alongside existing managed apps. (@klinch in https://github.com/cozystack/cozystack/pull/1230)
|
||||
-->
|
||||
|
||||
|
||||
### Cozystack API Improvements
|
||||
|
||||
This release brings significant improvements to the OpenAPI specs for all managed applications in Cozystack,
|
||||
including databases, tenant Kubernetes, virtual machines, monitoring, and others.
|
||||
These changes include more precise type definitions for fields that were previously defined only as generic objects,
|
||||
and many fields now have value constraints.
|
||||
Now many possible misconfigurations are detected immediately upon API request, and not later, with a failed deployment.
|
||||
|
||||
The Cozystack API now also displays default values for the application resources.
|
||||
Most other fields now have sane default values when such values are possible.
|
||||
|
||||
All these changes pave the road for the new Cozystack UI, which is currently under development.
|
||||
|
||||
### Hetzner RobotLB Support
|
||||
|
||||
MetalLB, the default load balancer included in Cozystack, is built for bare metal and self-hosted VMs,
|
||||
but is not supported on most cloud providers.
|
||||
For example, Hetzner provides its own RobotLB service, which Cozystack now supports as an optional component.
|
||||
|
||||
Read the updated guide on [deploying Cozystack on Hetzner.com](https://cozystack.io/docs/install/providers/hetzner/)
|
||||
to learn more and deploy your own Cozystack cluster on Hetzner.
|
||||
|
||||
### S3 Service: Dedicated Clusters and Monitoring
|
||||
|
||||
You can now deploy dedicated Cozystack clusters to run the S3 service, powered by SeaweedFS.
|
||||
Thanks to the support for [integration with remote filer endpoints](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/),
|
||||
you can connect your primary Cozystack cluster to use S3 storage in a dedicated cluster.
|
||||
|
||||
For security, platform administrators can now configure the SeaweedFS application with
|
||||
a list of IP addresses or CIDR ranges that are allowed to access the filer service.
|
||||
|
||||
SeaweedFS has also been integrated into the monitoring stack and now has its own Grafana dashboard.
|
||||
Together, these enhancements help Cozystack users build a more reliable, scalable, and observable S3 service.
|
||||
|
||||
### ClickHouse Keeper
|
||||
|
||||
The ClickHouse application now includes a ClickHouse Keeper service to improve cluster reliability and availability.
|
||||
This component is deployed by default with every ClickHouse cluster.
|
||||
|
||||
Learn more in the [ClickHouse configuration reference](https://cozystack.io/docs/applications/clickhouse/#clickhouse-keeper-parameters).
|
||||
|
||||
## Major Features and Improvements
|
||||
|
||||
* [platform] Enable using external application packages by adding a `CozystackResourceDefinition` reconciler. Read the documentation on [adding external applications to Cozystack](https://cozystack.io/docs/applications/external/) to learn more. (@klinch0 in https://github.com/cozystack/cozystack/pull/1313)
|
||||
* [cozystack-api, apps] Add default values, clear type definitions, value constraints and other improvements to the OpenAPI specs and READMEs by migrating to [cozyvalue-gen](https://github.com/cozystack/cozyvalues-gen). (@kvaps and @NickVolynkin in https://github.com/cozystack/cozystack/pull/1216, https://github.com/cozystack/cozystack/pull/1314, https://github.com/cozystack/cozystack/pull/1316, https://github.com/cozystack/cozystack/pull/1321, and https://github.com/cozystack/cozystack/pull/1333)
|
||||
* [cozystack-api] Show default values from the OpenAPI spec in the application resources. (@kvaps in https://github.com/cozystack/cozystack/pull/1241)
|
||||
* [cozystack-api] Provide an API for administrators to define custom managed applications alongside existing managed apps. (@klinch in https://github.com/cozystack/cozystack/pull/1230)
|
||||
* [robotlb] Introduce the Hetzner RobotLB balancer. (@IvanHunters and @gwynbleidd2106 in https://github.com/cozystack/cozystack/pull/1233)
|
||||
* [platform, robotlb] Autodetect if node ports should be assigned to load balancer services. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1271)
|
||||
* [seaweedfs] Enable [integration with remote filer endpoints](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/) by adding new `Client` topology. (@kvaps in https://github.com/cozystack/cozystack/pull/1239)
|
||||
* [seaweedfs] Add support for whitelisting and exporting via nginx-ingress. Update cosi-driver. (@kvaps in https://github.com/cozystack/cozystack/pull/1277)
|
||||
* [monitoring, seaweedfs] Add monitoring and Grafana dashboard for SeaweedFS. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1285)
|
||||
* [clickhouse] Add the ClickHouse Keeper component. (@klinch0 in https://github.com/cozystack/cozystack/pull/1298 and https://github.com/cozystack/cozystack/pull/1320)
|
||||
|
||||
## Security
|
||||
|
||||
* [keycloak] Store administrative passwords in the management cluster's secrets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1286)
|
||||
* [keycloak] Update Keycloak client redirect URI to use HTTPS instead of HTTP. Enable `cookie-secure`. (@klinch0 in https://github.com/cozystack/cozystack/pull/1287)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [platform] Introduce a fixed 2-second delay at the start of reconciliation for system and tenant Helm operations. (@klinch0 in https://github.com/cozystack/cozystack/pull/1343)
|
||||
* [kubernetes] Add dependency for snapshot CRD and migration to the latest version. (@kvaps in https://github.com/cozystack/cozystack/pull/1275)
|
||||
* [kubernetes] Fix regression in `volumesnapshotclass` installation from https://github.com/cozystack/cozystack/pull/1203. (@kvaps in https://github.com/cozystack/cozystack/pull/1238)
|
||||
* [kubernetes] Resolve problems with pod names exceeding allowed length by shortening the name of volume snapshot CRD from `*-volumesnapshot-crd-for-tenant-k8s` to `*-vsnap-crd`. To apply this change, update each affected tenant Kubernetes cluster after updating Cozystack. (@klinch0 in https://github.com/cozystack/cozystack/pull/1284)
|
||||
* [kubernetes] Disable VPA for VPA in tenant Kubernetes clusters. Tenant clusters have no need for this feature, and it was not designed to work in a tenant cluster, but was enabled by mistake. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1301 and https://github.com/cozystack/cozystack/pull/1318)
|
||||
* [kamaji] Fix broken migration jobs originating from missing environment variables in the in-tree build. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1338)
|
||||
* [etcd] Fix the `topologySpreadConstraints` for etcd. (@klinch0 in https://github.com/cozystack/cozystack/pull/1331)
|
||||
* [tenant] Fix tenant network policy to allow traffic to additional tenant-related services across namespace hierarchies. (@klinch0 in https://github.com/cozystack/cozystack/pull/1232)
|
||||
* [tenant, monitoring] Improve the reliability of tenant monitoring by increasing the timeout and number of retries. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1294)
|
||||
* [kubevirt] Fix building KubeVirt CCM image. (@kvaps in https://github.com/cozystack/cozystack/commit/3c7e256906e1dbb0f957dc3a205fa77a147d419d)
|
||||
* [virtual-machine] Fix a regression with `optional=true` field. (@kvaps in https://github.com/cozystack/cozystack/commit/01053f7c3180d1bd045d7c5fb949984c2bdaf19d)
|
||||
* [virtual-machine] Enable using custom `instanceType` values in `virtual-machine` and `vm-instance` by disabling field validation. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1300, backported in https://github.com/cozystack/cozystack/pull/1303)
|
||||
* [cozystack-api] Show correct `kind` values of `ApplicationList`. (@kvaps in https://github.com/cozystack/cozystack/pull/1290)
|
||||
* [cozystack-api] Add missing roles to allow cozystack-controller to read Kubernetes deployments. (@klinch0 in https://github.com/cozystack/cozystack/pull/1342)
|
||||
* [linstor] Update LINSTOR monitoring configuration to use label `controller_node` instead of `node`. (@kvaps in https://github.com/cozystack/cozystack/pull/1326 and https://github.com/cozystack/cozystack/pull/1335)
|
||||
* [seaweedfs] Fix SeaweedFS volume configuration. Increase the volume size limit from 100MB to 30,000MB. (@kvaps in https://github.com/cozystack/cozystack/pull/1328)
|
||||
* [seaweedfs] Disable proxy buffering and proxy request buffering for ingress. (@kvaps in https://github.com/cozystack/cozystack/pull/1330)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Update flux-operator to 0.28.0. (@kingdonb in https://github.com/cozystack/cozystack/pull/1315 and https://github.com/cozystack/cozystack/pull/1344)
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Reimplement Cozystack Roadmap as a GitHub project](https://github.com/orgs/cozystack/projects/1). (@cozystack team)
|
||||
* [SeaweedFS Multi-DC Configuration](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/272)
|
||||
* [Troubleshooting Kube-OVN](https://cozystack.io/docs/operations/troubleshooting/#kube-ovn-crash). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/273)
|
||||
* [Removing failed nodes from Cozystack cluster](https://cozystack.io/docs/operations/troubleshooting/#remove-a-failed-node-from-the-cluster). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/273)
|
||||
* [Installing Talos with `kexec`](https://cozystack.io/docs/talos/install/kexec/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/268)
|
||||
* [Rewrite Cozystack tutorial](https://cozystack.io/docs/getting-started/). (@NickVolynkin in https://github.com/cozystack/website/pull/262 and https://github.com/cozystack/website/pull/268)
|
||||
* [How to install Cozystack in Hetzner](https://cozystack.io/docs/install/providers/hetzner/). (@NickVolynkin and @IvanHunters in https://github.com/cozystack/website/pull/280)
|
||||
* [Adding External Applications to Cozystack Catalog](https://cozystack.io/docs/applications/external/). (@klinch0 and @NickVolynkin in https://github.com/cozystack/website/pull/283)
|
||||
* [Creating and Using Named VM Images (Golden Images)](https://cozystack.io/docs/virtualization/vm-image/) (@NickVolynkin and @kvaps in https://github.com/cozystack/website/pull/276)
|
||||
* [Creating Encrypted Storage on LINSTOR](https://cozystack.io/docs/operations/storage/disk-encryption/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/282)
|
||||
* [Adding and removing components on Cozystack installation using `bundle-enable` and `bundle-disable`](https://cozystack.io/docs/operations/bundles/#how-to-enable-and-disable-bundle-components) (@NickVolynkin in https://github.com/cozystack/website/pull/281)
|
||||
* Restructure Cozystack documentation. Bring [managed Kubernetes](https://cozystack.io/docs/kubernetes/), [managed applications](https://cozystack.io/docs/applications/), [virtualization](https://cozystack.io/docs/virtualization/), and [networking](https://cozystack.io/docs/networking/) guides to the top level. (@NickVolynkin in https://github.com/cozystack/website/pull/266)
|
||||
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
* [tests] Add tests for S3 buckets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1283)
|
||||
* [tests, ci] Simplify test discovery logic; run two k8s tests as separate jobs; delete Clickhouse application after a successful test. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1236)
|
||||
* [dx] When running `make` commands with `BUILDER` value specified, `PLATFORM` is optional. (@kvaps in https://github.com/cozystack/cozystack/pull/1288)
|
||||
* [tests] Fix resource specification in virtual machine tests. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1308)
|
||||
* [tests] Increase available space for e2e tests. (@kvaps in https://github.com/cozystack/cozystack/commit/168a24ffdf1202b3bf2e7d2b5ef54b72b7403baf)
|
||||
* [tests, ci] Continue application tests after one of them fails. (@NickVolynkin in https://github.com/cozystack/cozystack/commit/634b77edad6c32c101f3e5daea6a5ffc0c83d904)
|
||||
* [ci] Use a subdomain of aenix.org for Nexus service in CI. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1322)
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0
|
||||
10
docs/changelogs/v0.35.1.md
Normal file
10
docs/changelogs/v0.35.1.md
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.1
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [cozy-lib] Fix malformed retrieval of `cozyConfig` in the cozy-lib template. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1348)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.0...v0.35.1
|
||||
22
docs/changelogs/v0.35.2.md
Normal file
22
docs/changelogs/v0.35.2.md
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.2
|
||||
-->
|
||||
|
||||
## Features and Improvements
|
||||
|
||||
* [talos] Add LLDPD (`ghcr.io/siderolabs/lldpd`) as a built-in system extension, enabling LLDP-based neighbor discovery out of the box. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1351 and https://github.com/cozystack/cozystack/pull/1360)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [cozystack-api] Sanitize the OpenAPI v2 schema. (@kvaps in https://github.com/cozystack/cozystack/pull/1353)
|
||||
* [seaweedfs] Fix a problem where S3 gateway would be moved to an external pod, resulting in authentication failure. (@kvaps in https://github.com/cozystack/cozystack/pull/1361)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Update LINSTOR to v1.31.3. (@kvaps in https://github.com/cozystack/cozystack/pull/1358)
|
||||
* Update SeaweedFS to v3.96. (@kvaps in https://github.com/cozystack/cozystack/pull/1361)
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.1...v0.35.2
|
||||
3
go.mod
3
go.mod
@@ -59,6 +59,7 @@ require (
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // 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
|
||||
@@ -66,9 +67,11 @@ require (
|
||||
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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // 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
|
||||
|
||||
6
go.sum
6
go.sum
@@ -2,6 +2,8 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
|
||||
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=
|
||||
@@ -115,6 +117,8 @@ 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/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=
|
||||
@@ -122,6 +126,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/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=
|
||||
|
||||
@@ -48,7 +48,7 @@ kubectl get ns --no-headers | awk '$2 != "Active"' |
|
||||
|
||||
echo "Collecting helmreleases..."
|
||||
kubectl get hr -A > $REPORT_DIR/kubernetes/helmreleases.txt 2>&1
|
||||
kubectl get hr -A | awk '$4 != "True"' | \
|
||||
kubectl get hr -A --no-headers | awk '$4 != "True"' | \
|
||||
while read NAMESPACE NAME _; do
|
||||
DIR=$REPORT_DIR/kubernetes/helmreleases/$NAMESPACE/$NAME
|
||||
mkdir -p $DIR
|
||||
@@ -105,7 +105,7 @@ kubectl get svc -A --no-headers | awk '$4 == "<pending>"' |
|
||||
|
||||
echo "Collecting pvcs..."
|
||||
kubectl get pvc -A > $REPORT_DIR/kubernetes/pvcs.txt 2>&1
|
||||
kubectl get pvc -A | awk '$3 != "Bound"' |
|
||||
kubectl get pvc -A --no-headers | awk '$3 != "Bound"' |
|
||||
while read NAMESPACE NAME _; do
|
||||
DIR=$REPORT_DIR/kubernetes/pvc/$NAMESPACE/$NAME
|
||||
mkdir -p $DIR
|
||||
|
||||
@@ -24,7 +24,7 @@ run_one() {
|
||||
|
||||
echo "╭ » Run test: $title"
|
||||
START=$(date +%s)
|
||||
skip_next="+ $fn" # первую строку трассировки с именем функции пропустим
|
||||
skip_next="+ $fn"
|
||||
|
||||
{
|
||||
(
|
||||
@@ -83,11 +83,11 @@ awk '
|
||||
}
|
||||
printf("### %s\n", title)
|
||||
printf("%s() {\n", fname)
|
||||
print " set -e" # ошибка → падение теста
|
||||
print " set -e"
|
||||
next
|
||||
}
|
||||
/^}$/ {
|
||||
print " return 0" # если автор не сделал exit 1 — тест ОК
|
||||
print " return 0"
|
||||
print "}"
|
||||
next
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//main/capacity-p
|
||||
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//flux/flux-control-plane.json
|
||||
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//flux/flux-stats.json
|
||||
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//kafka/strimzi-kafka.json
|
||||
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//seaweedfs/seaweedfs.json
|
||||
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//goldpinger/goldpinger.json
|
||||
EOT
|
||||
|
||||
|
||||
47
hack/e2e-apps/bucket.bats
Normal file
47
hack/e2e-apps/bucket.bats
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "Create and Verify Seeweedfs Bucket" {
|
||||
# Create the bucket resource
|
||||
name='test'
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: apps.cozystack.io/v1alpha1
|
||||
kind: Bucket
|
||||
metadata:
|
||||
name: ${name}
|
||||
namespace: tenant-test
|
||||
spec: {}
|
||||
EOF
|
||||
|
||||
# Wait for the bucket to be ready
|
||||
kubectl -n tenant-test wait hr bucket-${name} --timeout=100s --for=condition=ready
|
||||
kubectl -n tenant-test wait bucketclaims.objectstorage.k8s.io bucket-${name} --timeout=300s --for=jsonpath='{.status.bucketReady}'
|
||||
kubectl -n tenant-test wait bucketaccesses.objectstorage.k8s.io bucket-${name} --timeout=300s --for=jsonpath='{.status.accessGranted}'
|
||||
|
||||
# Get and decode credentials
|
||||
kubectl -n tenant-test get secret bucket-${name} -ojsonpath='{.data.BucketInfo}' | base64 -d > bucket-test-credentials.json
|
||||
|
||||
# Get credentials from the secret
|
||||
ACCESS_KEY=$(jq -r '.spec.secretS3.accessKeyID' bucket-test-credentials.json)
|
||||
SECRET_KEY=$(jq -r '.spec.secretS3.accessSecretKey' bucket-test-credentials.json)
|
||||
BUCKET_NAME=$(jq -r '.spec.bucketName' bucket-test-credentials.json)
|
||||
|
||||
# Start port-forwarding
|
||||
bash -c 'timeout 100s kubectl port-forward service/seaweedfs-s3 -n tenant-root 8333:8333 > /dev/null 2>&1 &'
|
||||
|
||||
# Wait for port-forward to be ready
|
||||
timeout 30 sh -ec 'until nc -z localhost 8333; do sleep 1; done'
|
||||
|
||||
# Set up MinIO alias with error handling
|
||||
mc alias set local https://localhost:8333 $ACCESS_KEY $SECRET_KEY --insecure
|
||||
|
||||
# Upload file to bucket
|
||||
mc cp bucket-test-credentials.json $BUCKET_NAME/bucket-test-credentials.json
|
||||
|
||||
# Verify file was uploaded
|
||||
mc ls $BUCKET_NAME/bucket-test-credentials.json
|
||||
|
||||
# Clean up uploaded file
|
||||
mc rm $BUCKET_NAME/bucket-test-credentials.json
|
||||
|
||||
kubectl -n tenant-test delete bucket.apps.cozystack.io ${name}
|
||||
}
|
||||
@@ -27,6 +27,10 @@ spec:
|
||||
s3AccessKey: oobaiRus9pah8PhohL1ThaeTa4UVa7gu
|
||||
s3SecretKey: ju3eum4dekeich9ahM1te8waeGai0oog
|
||||
resticPassword: ChaXoveekoh6eigh4siesheeda2quai0
|
||||
clickhouseKeeper:
|
||||
enabled: true
|
||||
resourcesPreset: "micro"
|
||||
size: "1Gi"
|
||||
resources: {}
|
||||
resourcesPreset: "nano"
|
||||
EOF
|
||||
@@ -38,4 +42,5 @@ EOF
|
||||
timeout 100 sh -ec "until kubectl -n tenant-test get svc chi-clickhouse-$name-clickhouse-0-0 -o jsonpath='{.spec.ports[*].port}' | grep -q '9000 8123 9009'; do sleep 10; done"
|
||||
timeout 80 sh -ec "until kubectl -n tenant-test get sts chi-clickhouse-$name-clickhouse-0-1 ; do sleep 10; done"
|
||||
kubectl -n tenant-test wait statefulset.apps/chi-clickhouse-$name-clickhouse-0-1 --timeout=140s --for=jsonpath='{.status.replicas}'=1
|
||||
kubectl -n tenant-test delete clickhouse $name
|
||||
}
|
||||
|
||||
6
hack/e2e-apps/kubernetes-latest.bats
Normal file
6
hack/e2e-apps/kubernetes-latest.bats
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "Create a tenant Kubernetes control plane with latest version" {
|
||||
. hack/e2e-apps/run-kubernetes.sh
|
||||
run_kubernetes_test 'keys | sort_by(.) | .[-1]' 'test-latest-version' '59991'
|
||||
}
|
||||
6
hack/e2e-apps/kubernetes-previous.bats
Normal file
6
hack/e2e-apps/kubernetes-previous.bats
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
@test "Create a tenant Kubernetes control plane with previous version" {
|
||||
. hack/e2e-apps/run-kubernetes.sh
|
||||
run_kubernetes_test 'keys | sort_by(.) | .[-2]' 'test-previous-version' '59992'
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
run_kubernetes_test() {
|
||||
local version_expr="$1"
|
||||
local test_name="$2"
|
||||
@@ -59,9 +57,6 @@ spec:
|
||||
instanceType: u1.medium
|
||||
maxReplicas: 10
|
||||
minReplicas: 0
|
||||
resources:
|
||||
cpu: ""
|
||||
memory: ""
|
||||
roles:
|
||||
- ingress-nginx
|
||||
storageClass: replicated
|
||||
@@ -104,10 +99,3 @@ EOF
|
||||
kubectl -n tenant-test delete kuberneteses.apps.cozystack.io $test_name
|
||||
|
||||
}
|
||||
|
||||
@test "Create a tenant Kubernetes control plane with latest version" {
|
||||
run_kubernetes_test 'keys | sort_by(.) | .[-1]' 'test-latest-version' '59991'
|
||||
}
|
||||
@test "Create a tenant Kubernetes control plane with previous version" {
|
||||
run_kubernetes_test 'keys | sort_by(.) | .[-2]' 'test-previous-version' '59992'
|
||||
}
|
||||
@@ -20,9 +20,7 @@ spec:
|
||||
storage: 5Gi
|
||||
storageClass: replicated
|
||||
gpus: []
|
||||
resources:
|
||||
cpu: ""
|
||||
memory: ""
|
||||
resources: {}
|
||||
sshKeys:
|
||||
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPht0dPk5qQ+54g1hSX7A6AUxXJW5T6n/3d7Ga2F8gTF
|
||||
test@test
|
||||
|
||||
@@ -18,8 +18,8 @@ spec:
|
||||
EOF
|
||||
sleep 5
|
||||
kubectl -n tenant-test wait hr vm-disk-$name --timeout=5s --for=condition=ready
|
||||
kubectl -n tenant-test wait dv vm-disk-$name --timeout=150s --for=condition=ready
|
||||
kubectl -n tenant-test wait pvc vm-disk-$name --timeout=100s --for=jsonpath='{.status.phase}'=Bound
|
||||
kubectl -n tenant-test wait dv vm-disk-$name --timeout=250s --for=condition=ready
|
||||
kubectl -n tenant-test wait pvc vm-disk-$name --timeout=200s --for=jsonpath='{.status.phase}'=Bound
|
||||
}
|
||||
|
||||
@test "Create a VM Instance" {
|
||||
@@ -42,9 +42,6 @@ spec:
|
||||
disks:
|
||||
- name: $diskName
|
||||
gpus: []
|
||||
resources:
|
||||
cpu: ""
|
||||
memory: ""
|
||||
sshKeys:
|
||||
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPht0dPk5qQ+54g1hSX7A6AUxXJW5T6n/3d7Ga2F8gTF
|
||||
test@test
|
||||
|
||||
@@ -123,16 +123,24 @@ EOF
|
||||
|
||||
@test "Configure Tenant and wait for applications" {
|
||||
# Patch root tenant and wait for its releases
|
||||
kubectl patch tenants/root -n tenant-root --type merge -p '{"spec":{"host":"example.org","ingress":true,"monitoring":true,"etcd":true,"isolated":true}}'
|
||||
|
||||
timeout 60 sh -ec 'until kubectl get hr -n tenant-root etcd ingress monitoring tenant-root >/dev/null 2>&1; do sleep 1; done'
|
||||
kubectl wait hr/etcd hr/ingress hr/tenant-root -n tenant-root --timeout=2m --for=condition=ready
|
||||
kubectl patch tenants/root -n tenant-root --type merge -p '{"spec":{"host":"example.org","ingress":true,"monitoring":true,"etcd":true,"isolated":true, "seaweedfs": true}}'
|
||||
|
||||
timeout 60 sh -ec 'until kubectl get hr -n tenant-root etcd ingress monitoring seaweedfs tenant-root >/dev/null 2>&1; do sleep 1; done'
|
||||
kubectl wait hr/etcd hr/ingress hr/tenant-root hr/seaweedfs -n tenant-root --timeout=4m --for=condition=ready
|
||||
|
||||
# TODO: Workaround ingress unvailability issue
|
||||
if ! kubectl wait hr/monitoring -n tenant-root --timeout=2m --for=condition=ready; then
|
||||
flux reconcile hr monitoring -n tenant-root --force
|
||||
kubectl wait hr/monitoring -n tenant-root --timeout=2m --for=condition=ready
|
||||
fi
|
||||
|
||||
if ! kubectl wait hr/seaweedfs-system -n tenant-root --timeout=2m --for=condition=ready; then
|
||||
flux reconcile hr seaweedfs-system -n tenant-root --force
|
||||
kubectl wait hr/seaweedfs-system -n tenant-root --timeout=2m --for=condition=ready
|
||||
fi
|
||||
|
||||
|
||||
# Expose Cozystack services through ingress
|
||||
kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"expose-services":"api,dashboard,cdi-uploadproxy,vm-exportproxy,keycloak"}}'
|
||||
|
||||
@@ -144,7 +152,7 @@ EOF
|
||||
kubectl wait sts/etcd -n tenant-root --for=jsonpath='{.status.readyReplicas}'=3 --timeout=5m
|
||||
|
||||
# VictoriaMetrics components
|
||||
kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
|
||||
kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=15m
|
||||
kubectl wait vlogs/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
|
||||
kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.clusterStatus}'=operational --timeout=5m
|
||||
|
||||
@@ -181,9 +189,22 @@ spec:
|
||||
ingress: false
|
||||
isolated: true
|
||||
monitoring: false
|
||||
resourceQuotas: {}
|
||||
resourceQuotas:
|
||||
cpu: "60"
|
||||
memory: "128Gi"
|
||||
storage: "100Gi"
|
||||
seaweedfs: false
|
||||
EOF
|
||||
kubectl wait hr/tenant-test -n tenant-root --timeout=1m --for=condition=ready
|
||||
kubectl wait namespace tenant-test --timeout=20s --for=jsonpath='{.status.phase}'=Active
|
||||
# Wait for ResourceQuota to appear and assert values
|
||||
timeout 60 sh -ec 'until [ "$(kubectl get quota -n tenant-test --no-headers 2>/dev/null | wc -l)" -ge 1 ]; do sleep 1; done'
|
||||
kubectl get quota -n tenant-test \
|
||||
-o jsonpath='{range .items[*]}{.spec.hard.requests\.memory}{" "}{.spec.hard.requests\.storage}{"\n"}{end}' \
|
||||
| grep -qx '137438953472 100Gi'
|
||||
|
||||
# Assert LimitRange defaults for containers
|
||||
kubectl get limitrange -n tenant-test \
|
||||
-o jsonpath='{range .items[*].spec.limits[*]}{.default.cpu}{" "}{.default.memory}{" "}{.defaultRequest.cpu}{" "}{.defaultRequest.memory}{"\n"}{end}' \
|
||||
| grep -qx '250m 128Mi 25m 128Mi'
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ EOF
|
||||
for i in 1 2 3; do
|
||||
cp nocloud-amd64.raw srv${i}/system.img
|
||||
qemu-img resize srv${i}/system.img 50G
|
||||
qemu-img create srv${i}/data.img 100G
|
||||
qemu-img create srv${i}/data.img 200G
|
||||
done
|
||||
}
|
||||
|
||||
@@ -132,29 +132,30 @@ machine:
|
||||
- usermode_helper=disabled
|
||||
- name: zfs
|
||||
- name: spl
|
||||
- name: lldpd
|
||||
registries:
|
||||
mirrors:
|
||||
docker.io:
|
||||
endpoints:
|
||||
- https://dockerio.nexus.lllamnyp.su
|
||||
- https://dockerio.nexus.aenix.org
|
||||
cr.fluentbit.io:
|
||||
endpoints:
|
||||
- https://fluentbit.nexus.lllamnyp.su
|
||||
- https://fluentbit.nexus.aenix.org
|
||||
docker-registry3.mariadb.com:
|
||||
endpoints:
|
||||
- https://mariadb.nexus.lllamnyp.su
|
||||
- https://mariadb.nexus.aenix.org
|
||||
gcr.io:
|
||||
endpoints:
|
||||
- https://gcr.nexus.lllamnyp.su
|
||||
- https://gcr.nexus.aenix.org
|
||||
ghcr.io:
|
||||
endpoints:
|
||||
- https://ghcr.nexus.lllamnyp.su
|
||||
- https://ghcr.nexus.aenix.org
|
||||
quay.io:
|
||||
endpoints:
|
||||
- https://quay.nexus.lllamnyp.su
|
||||
- https://quay.nexus.aenix.org
|
||||
registry.k8s.io:
|
||||
endpoints:
|
||||
- https://k8s.nexus.lllamnyp.su
|
||||
- https://k8s.nexus.aenix.org
|
||||
files:
|
||||
- content: |
|
||||
[plugins]
|
||||
|
||||
20
hack/e2e-test-openapi.bats
Normal file
20
hack/e2e-test-openapi.bats
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bats
|
||||
# -----------------------------------------------------------------------------
|
||||
# Test OpenAPI endpoints in a Kubernetes cluster
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@test "Test OpenAPI v2 endpoint" {
|
||||
kubectl get -v7 --raw '/openapi/v2?timeout=32s' > /dev/null
|
||||
}
|
||||
|
||||
@test "Test OpenAPI v3 endpoint" {
|
||||
kubectl get -v7 --raw '/openapi/v3/apis/apps.cozystack.io/v1alpha1' > /dev/null
|
||||
}
|
||||
|
||||
@test "Test OpenAPI v2 endpoint (protobuf)" {
|
||||
(
|
||||
kubectl proxy --port=21234 & sleep 0.5
|
||||
trap "kill $!" EXIT
|
||||
curl -sS --fail 'http://localhost:21234/openapi/v2?timeout=32s' -H 'Accept: application/com.github.proto-openapi.spec.v2@v1.0+protobuf' > /dev/null
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,10 @@ kube::codegen::gen_helpers \
|
||||
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
|
||||
"${SCRIPT_ROOT}/pkg/apis"
|
||||
|
||||
kube::codegen::gen_helpers \
|
||||
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
|
||||
"${SCRIPT_ROOT}/api"
|
||||
|
||||
if [[ -n "${API_KNOWN_VIOLATIONS_DIR:-}" ]]; then
|
||||
report_filename="${API_KNOWN_VIOLATIONS_DIR}/cozystack_api_violation_exceptions.list"
|
||||
if [[ "${UPDATE_API_KNOWN_VIOLATIONS:-}" == "true" ]]; then
|
||||
|
||||
183
internal/controller/cozystackresource_controller.go
Normal file
183
internal/controller/cozystackresource_controller.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/shared/crdmem"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
type CozystackResourceDefinitionReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
Debounce time.Duration
|
||||
|
||||
mu sync.Mutex
|
||||
lastEvent time.Time
|
||||
lastHandled time.Time
|
||||
|
||||
mem *crdmem.Memory
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
crd := &cozyv1alpha1.CozystackResourceDefinition{}
|
||||
err := r.Get(ctx, types.NamespacedName{Name: req.Name}, crd)
|
||||
if err == nil {
|
||||
if r.mem != nil {
|
||||
r.mem.Upsert(crd)
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.lastEvent = time.Now()
|
||||
r.mu.Unlock()
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if apierrors.IsNotFound(err) && r.mem != nil {
|
||||
r.mem.Delete(req.Name)
|
||||
}
|
||||
if req.Namespace == "cozy-system" && req.Name == "cozystack-api" {
|
||||
return r.debouncedRestart(ctx, logger)
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
if r.Debounce == 0 {
|
||||
r.Debounce = 5 * time.Second
|
||||
}
|
||||
if r.mem == nil {
|
||||
r.mem = crdmem.Global()
|
||||
}
|
||||
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("cozystackresource-controller").
|
||||
For(&cozyv1alpha1.CozystackResourceDefinition{}, builder.WithPredicates()).
|
||||
Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
r.mu.Lock()
|
||||
r.lastEvent = time.Now()
|
||||
r.mu.Unlock()
|
||||
return []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Namespace: "cozy-system",
|
||||
Name: "cozystack-api",
|
||||
},
|
||||
}}
|
||||
}),
|
||||
).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
type crdHashView struct {
|
||||
Name string `json:"name"`
|
||||
Spec cozyv1alpha1.CozystackResourceDefinitionSpec `json:"spec"`
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) computeConfigHash() (string, error) {
|
||||
if r.mem == nil {
|
||||
return "", nil
|
||||
}
|
||||
snapshot := r.mem.Snapshot()
|
||||
sort.Slice(snapshot, func(i, j int) bool { return snapshot[i].Name < snapshot[j].Name })
|
||||
views := make([]crdHashView, 0, len(snapshot))
|
||||
for i := range snapshot {
|
||||
views = append(views, crdHashView{
|
||||
Name: snapshot[i].Name,
|
||||
Spec: snapshot[i].Spec,
|
||||
})
|
||||
}
|
||||
b, err := json.Marshal(views)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sum := sha256.Sum256(b)
|
||||
return hex.EncodeToString(sum[:]), nil
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Context, logger logr.Logger) (ctrl.Result, error) {
|
||||
r.mu.Lock()
|
||||
le := r.lastEvent
|
||||
lh := r.lastHandled
|
||||
debounce := r.Debounce
|
||||
r.mu.Unlock()
|
||||
|
||||
if debounce <= 0 {
|
||||
debounce = 5 * time.Second
|
||||
}
|
||||
if le.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
if d := time.Since(le); d < debounce {
|
||||
return ctrl.Result{RequeueAfter: debounce - d}, nil
|
||||
}
|
||||
if !lh.Before(le) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
newHash, err := r.computeConfigHash()
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if deploy.Spec.Template.Annotations == nil {
|
||||
deploy.Spec.Template.Annotations = map[string]string{}
|
||||
}
|
||||
oldHash := deploy.Spec.Template.Annotations["cozystack.io/config-hash"]
|
||||
|
||||
if oldHash == newHash && oldHash != "" {
|
||||
r.mu.Lock()
|
||||
r.lastHandled = le
|
||||
r.mu.Unlock()
|
||||
logger.Info("No changes in CRD config; skipping restart", "hash", newHash)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(deploy.DeepCopy())
|
||||
deploy.Spec.Template.Annotations["cozystack.io/config-hash"] = newHash
|
||||
|
||||
if err := r.Patch(ctx, deploy, patch); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.lastHandled = le
|
||||
r.mu.Unlock()
|
||||
|
||||
logger.Info("Updated cozystack-api podTemplate config-hash; rollout triggered",
|
||||
"old", oldHash, "new", newHash)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
280
internal/controller/kubeovnplunger/kubeovn_plunger.go
Normal file
280
internal/controller/kubeovnplunger/kubeovn_plunger.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/sse"
|
||||
"github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
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/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var (
|
||||
srv *sse.Server
|
||||
)
|
||||
|
||||
const (
|
||||
rescanInterval = 1 * time.Minute
|
||||
)
|
||||
|
||||
// KubeOVNPlunger watches the ovn-central cluster members
|
||||
type KubeOVNPlunger struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
ClientSet kubernetes.Interface
|
||||
REST *rest.Config
|
||||
Registry prometheus.Registerer
|
||||
metrics metrics
|
||||
lastLeader map[string]string
|
||||
seenCIDs map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
// Reconcile runs the checks on the ovn-central members to see if their views of the cluster are consistent
|
||||
func (r *KubeOVNPlunger) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := r.Get(ctx, req.NamespacedName, deploy); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
iphints := map[string]string{}
|
||||
for _, env := range deploy.Spec.Template.Spec.Containers[0].Env {
|
||||
if env.Name != "NODE_IPS" {
|
||||
continue
|
||||
}
|
||||
for _, ip := range strings.Split(env.Value, ",") {
|
||||
iphints[ip] = ""
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(iphints) == 0 {
|
||||
l.Info("WARNING: running without IP hints, some error conditions cannot be detected")
|
||||
}
|
||||
pods := &corev1.PodList{}
|
||||
|
||||
if err := r.List(ctx, pods, client.InNamespace(req.Namespace), client.MatchingLabels(map[string]string{"app": req.Name})); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("list ovn-central pods: %w", err)
|
||||
}
|
||||
|
||||
nbmv := make([]ovnstatus.MemberView, 0, len(pods.Items))
|
||||
sbmv := make([]ovnstatus.MemberView, 0, len(pods.Items))
|
||||
nbSnaps := make([]ovnstatus.HealthSnapshot, 0, len(pods.Items))
|
||||
sbSnaps := make([]ovnstatus.HealthSnapshot, 0, len(pods.Items))
|
||||
// TODO: get real iphints
|
||||
for i := range pods.Items {
|
||||
o := ovnstatus.OVNClient{}
|
||||
o.ApplyDefaults()
|
||||
o.Runner = func(ctx context.Context, bin string, args ...string) (string, error) {
|
||||
cmd := append([]string{bin}, args...)
|
||||
eo := ExecOptions{
|
||||
Namespace: req.Namespace,
|
||||
Pod: pods.Items[i].Name,
|
||||
Container: pods.Items[i].Spec.Containers[0].Name,
|
||||
Command: cmd,
|
||||
}
|
||||
res, err := r.ExecPod(ctx, eo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res.Stdout, nil
|
||||
}
|
||||
nb, sb, err1, err2 := o.HealthBoth(ctx)
|
||||
if err1 != nil || err2 != nil {
|
||||
l.Error(fmt.Errorf("health check failed: nb=%w, sb=%w", err1, err2), "pod", pods.Items[i].Name)
|
||||
continue
|
||||
}
|
||||
nbSnaps = append(nbSnaps, nb)
|
||||
sbSnaps = append(sbSnaps, sb)
|
||||
nbmv = append(nbmv, ovnstatus.BuildMemberView(nb))
|
||||
sbmv = append(sbmv, ovnstatus.BuildMemberView(sb))
|
||||
}
|
||||
r.recordAndPruneCIDs("nb", cidFromSnaps(nbSnaps))
|
||||
r.recordAndPruneCIDs("sb", cidFromSnaps(sbSnaps))
|
||||
nbmv = ovnstatus.NormalizeViews(nbmv)
|
||||
sbmv = ovnstatus.NormalizeViews(sbmv)
|
||||
nbecv := ovnstatus.AnalyzeConsensusWithIPHints(nbmv, &ovnstatus.Hints{ExpectedIPs: iphints})
|
||||
sbecv := ovnstatus.AnalyzeConsensusWithIPHints(sbmv, &ovnstatus.Hints{ExpectedIPs: iphints})
|
||||
expected := len(iphints)
|
||||
r.WriteClusterMetrics("nb", nbSnaps, nbecv, expected)
|
||||
r.WriteClusterMetrics("sb", sbSnaps, sbecv, expected)
|
||||
r.WriteMemberMetrics("nb", nbSnaps, nbmv, nbecv)
|
||||
r.WriteMemberMetrics("sb", sbSnaps, sbmv, sbecv)
|
||||
srv.Publish(nbecv.PrettyString() + sbecv.PrettyString())
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager attaches a generic ticker to trigger a reconcile every <interval> seconds
|
||||
func (r *KubeOVNPlunger) SetupWithManager(mgr ctrl.Manager, kubeOVNNamespace, appName string) error {
|
||||
r.REST = rest.CopyConfig(mgr.GetConfig())
|
||||
cs, err := kubernetes.NewForConfig(r.REST)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build clientset: %w", err)
|
||||
}
|
||||
r.ClientSet = cs
|
||||
ch := make(chan event.GenericEvent, 10)
|
||||
mapFunc := func(context.Context, client.Object) []reconcile.Request {
|
||||
return []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: kubeOVNNamespace, Name: appName},
|
||||
}}
|
||||
}
|
||||
mapper := handler.EnqueueRequestsFromMapFunc(mapFunc)
|
||||
srv = sse.New(sse.Options{
|
||||
Addr: ":18080",
|
||||
AllowCORS: true,
|
||||
})
|
||||
r.initMetrics()
|
||||
r.lastLeader = make(map[string]string)
|
||||
r.seenCIDs = map[string]map[string]struct{}{"nb": {}, "sb": {}}
|
||||
if err := ctrl.NewControllerManagedBy(mgr).
|
||||
Named("kubeovnplunger").
|
||||
WatchesRawSource(source.Channel(ch, mapper)).
|
||||
Complete(r); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
|
||||
go srv.ListenAndServe()
|
||||
<-ctx.Done()
|
||||
_ = srv.Shutdown(context.Background())
|
||||
return nil
|
||||
}))
|
||||
return mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
|
||||
ticker := time.NewTicker(rescanInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
ch <- event.GenericEvent{
|
||||
Object: &metav1.PartialObjectMetadata{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: kubeOVNNamespace,
|
||||
Name: appName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
type ExecOptions struct {
|
||||
Namespace string
|
||||
Pod string
|
||||
Container string
|
||||
Command []string // e.g. []string{"sh", "-c", "echo hi"}
|
||||
Stdin io.Reader // optional
|
||||
TTY bool // if true, stderr is merged into stdout
|
||||
Timeout time.Duration // optional overall timeout
|
||||
}
|
||||
|
||||
type ExecResult struct {
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitCode *int // nil if not determinable
|
||||
}
|
||||
|
||||
// ExecPod runs a command in a pod and returns stdout/stderr/exit code.
|
||||
func (r *KubeOVNPlunger) ExecPod(ctx context.Context, opts ExecOptions) (*ExecResult, error) {
|
||||
if opts.Namespace == "" || opts.Pod == "" || opts.Container == "" {
|
||||
return nil, fmt.Errorf("namespace, pod, and container are required")
|
||||
}
|
||||
|
||||
req := r.ClientSet.CoreV1().RESTClient().
|
||||
Post().
|
||||
Resource("pods").
|
||||
Namespace(opts.Namespace).
|
||||
Name(opts.Pod).
|
||||
SubResource("exec").
|
||||
VersionedParams(&corev1.PodExecOptions{
|
||||
Container: opts.Container,
|
||||
Command: opts.Command,
|
||||
Stdin: opts.Stdin != nil,
|
||||
Stdout: true,
|
||||
Stderr: !opts.TTY,
|
||||
TTY: opts.TTY,
|
||||
}, scheme.ParameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(r.REST, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("spdy executor: %w", err)
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
streamCtx := ctx
|
||||
if opts.Timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
streamCtx, cancel = context.WithTimeout(ctx, opts.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
streamErr := exec.StreamWithContext(streamCtx, remotecommand.StreamOptions{
|
||||
Stdin: opts.Stdin,
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
Tty: opts.TTY,
|
||||
})
|
||||
|
||||
res := &ExecResult{Stdout: stdout.String(), Stderr: stderr.String()}
|
||||
if streamErr != nil {
|
||||
// Try to surface exit code instead of treating all failures as transport errors
|
||||
type exitCoder interface{ ExitStatus() int }
|
||||
if ec, ok := streamErr.(exitCoder); ok {
|
||||
code := ec.ExitStatus()
|
||||
res.ExitCode = &code
|
||||
return res, nil
|
||||
}
|
||||
return res, fmt.Errorf("exec stream: %w", streamErr)
|
||||
}
|
||||
zero := 0
|
||||
res.ExitCode = &zero
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) recordAndPruneCIDs(db, currentCID string) {
|
||||
|
||||
// Mark current as seen
|
||||
if r.seenCIDs[db] == nil {
|
||||
r.seenCIDs[db] = map[string]struct{}{}
|
||||
}
|
||||
if currentCID != "" {
|
||||
r.seenCIDs[db][currentCID] = struct{}{}
|
||||
}
|
||||
|
||||
// Build a set of "still active" CIDs this cycle (could be none if you failed to collect)
|
||||
active := map[string]struct{}{}
|
||||
if currentCID != "" {
|
||||
active[currentCID] = struct{}{}
|
||||
}
|
||||
|
||||
// Any seen CID that isn't active now is stale -> delete all its series
|
||||
for cid := range r.seenCIDs[db] {
|
||||
if _, ok := active[cid]; ok {
|
||||
continue
|
||||
}
|
||||
r.deleteAllFor(db, cid)
|
||||
delete(r.seenCIDs[db], cid)
|
||||
}
|
||||
}
|
||||
34
internal/controller/kubeovnplunger/kubeovn_plunger_test.go
Normal file
34
internal/controller/kubeovnplunger/kubeovn_plunger_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var testPlunger *KubeOVNPlunger
|
||||
|
||||
func init() {
|
||||
scheme := runtime.NewScheme()
|
||||
cfg := config.GetConfigOrDie()
|
||||
c, _ := client.New(cfg, client.Options{})
|
||||
cs, _ := kubernetes.NewForConfig(cfg)
|
||||
testPlunger = &KubeOVNPlunger{
|
||||
Client: c,
|
||||
Scheme: scheme,
|
||||
ClientSet: cs,
|
||||
REST: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlungerGetsStatuses(t *testing.T) {
|
||||
_, err := testPlunger.Reconcile(context.Background(), ctrl.Request{})
|
||||
if err != nil {
|
||||
t.Errorf("error should be nil but it's %s", err)
|
||||
}
|
||||
}
|
||||
423
internal/controller/kubeovnplunger/metrics.go
Normal file
423
internal/controller/kubeovnplunger/metrics.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
type metrics struct {
|
||||
// --- Core cluster health (per DB/cid) ---
|
||||
clusterQuorum *prometheus.GaugeVec // 1/0
|
||||
allAgree *prometheus.GaugeVec // 1/0
|
||||
membersExpected *prometheus.GaugeVec
|
||||
membersObserved *prometheus.GaugeVec
|
||||
ipsExpected *prometheus.GaugeVec
|
||||
ipsObserved *prometheus.GaugeVec
|
||||
excessMembers *prometheus.GaugeVec
|
||||
missingMembers *prometheus.GaugeVec
|
||||
unexpectedIPsCount *prometheus.GaugeVec
|
||||
missingExpectedIPsCount *prometheus.GaugeVec
|
||||
ipConflictsCount *prometheus.GaugeVec
|
||||
sidAddrDisagreements *prometheus.GaugeVec
|
||||
|
||||
// --- Consensus summary (per DB/cid) ---
|
||||
consensusMajoritySize *prometheus.GaugeVec
|
||||
consensusMinoritySize *prometheus.GaugeVec
|
||||
consensusDiffsTotal *prometheus.GaugeVec
|
||||
|
||||
// --- Detail exports (sparse, keyed by IP/SID) ---
|
||||
unexpectedIPGauge *prometheus.GaugeVec // {db,cid,ip} -> 1
|
||||
missingExpectedIPGauge *prometheus.GaugeVec // {db,cid,ip} -> 1
|
||||
ipConflictGauge *prometheus.GaugeVec // {db,cid,ip} -> count(sids)
|
||||
suspectStaleGauge *prometheus.GaugeVec // {db,cid,sid} -> 1
|
||||
|
||||
// --- Per-member liveness/freshness (per DB/cid/sid[/ip]) ---
|
||||
memberConnected *prometheus.GaugeVec // {db,cid,sid,ip}
|
||||
memberLeader *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberLastMsgMs *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberIndex *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberIndexGap *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberReporter *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberMissingReporter *prometheus.GaugeVec // {db,cid,sid}
|
||||
|
||||
// --- Ops/housekeeping ---
|
||||
leaderTransitionsTotal *prometheus.CounterVec // {db,cid}
|
||||
collectErrorsTotal *prometheus.CounterVec // {db,cid}
|
||||
publishEventsTotal *prometheus.CounterVec // {db,cid}
|
||||
snapshotTimestampSec *prometheus.GaugeVec // {db,cid}
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) initMetrics() {
|
||||
p := promauto.With(r.Registry)
|
||||
|
||||
ns := "ovn"
|
||||
|
||||
// --- Core cluster health ---
|
||||
r.metrics.clusterQuorum = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "quorum",
|
||||
Help: "1 if cluster has quorum, else 0",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.allAgree = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "all_agree",
|
||||
Help: "1 if all members report identical membership",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.membersExpected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "members_expected",
|
||||
Help: "Expected cluster size (replicas)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.membersObserved = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "members_observed",
|
||||
Help: "Observed members (distinct SIDs across views)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipsExpected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ips_expected",
|
||||
Help: "Expected distinct member IPs (from k8s hints)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipsObserved = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ips_observed",
|
||||
Help: "Observed distinct member IPs (from OVN views)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.excessMembers = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "excess_members",
|
||||
Help: "Members over expected (>=0)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.missingMembers = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "missing_members",
|
||||
Help: "Members short of expected (>=0)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.unexpectedIPsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "unexpected_ips",
|
||||
Help: "Count of IPs in OVN not present in k8s expected set",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.missingExpectedIPsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "missing_expected_ips",
|
||||
Help: "Count of expected IPs not found in OVN",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipConflictsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ip_conflicts",
|
||||
Help: "Number of IPs claimed by multiple SIDs",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.sidAddrDisagreements = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "sid_address_disagreements",
|
||||
Help: "Number of SIDs seen with >1 distinct addresses",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
// --- Consensus summary ---
|
||||
r.metrics.consensusMajoritySize = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "majority_size",
|
||||
Help: "Majority group size (0 if none)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.consensusMinoritySize = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "minority_size",
|
||||
Help: "Minority group size",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.consensusDiffsTotal = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "diffs_total",
|
||||
Help: "Total per-reporter differences vs truth (missing + extra + mismatches)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
// --- Detail exports (sparse) ---
|
||||
r.metrics.unexpectedIPGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "unexpected_ip",
|
||||
Help: "Unexpected IP present in OVN; value fixed at 1",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.missingExpectedIPGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "missing_expected_ip",
|
||||
Help: "Expected IP missing from OVN; value fixed at 1",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.ipConflictGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "ip_conflict",
|
||||
Help: "Number of SIDs claiming the same IP for this key",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.suspectStaleGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "suspect_stale",
|
||||
Help: "Suspected stale SID candidate for kick; value fixed at 1 (emit only when remediation is warranted)",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
// --- Per-member liveness/freshness ---
|
||||
r.metrics.memberConnected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "connected",
|
||||
Help: "1 if local server reports connected/quorum, else 0",
|
||||
}, []string{"db", "cid", "sid", "ip"})
|
||||
|
||||
r.metrics.memberLeader = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "leader",
|
||||
Help: "1 if this member is leader, else 0",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberLastMsgMs = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "last_msg_ms",
|
||||
Help: "Follower->leader 'last msg' age in ms (legacy heuristic). NaN/omit if unknown",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberIndex = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "index",
|
||||
Help: "Local Raft log index",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberIndexGap = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "index_gap",
|
||||
Help: "Leader index minus local index (>=0)",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberReporter = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "reporter",
|
||||
Help: "1 if a self-view from this SID was collected in the scrape cycle",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberMissingReporter = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "missing_reporter",
|
||||
Help: "1 if SID appears in union but produced no self-view",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
// --- Ops/housekeeping ---
|
||||
r.metrics.leaderTransitionsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "leader_transitions_total",
|
||||
Help: "Count of observed leader SID changes",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.collectErrorsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "collect_errors_total",
|
||||
Help: "Count of errors during health collection/analysis",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.publishEventsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "publish_events_total",
|
||||
Help: "Count of SSE publish events (optional)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.snapshotTimestampSec = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "snapshot_timestamp_seconds",
|
||||
Help: "Unix timestamp of the last successful consensus snapshot",
|
||||
}, []string{"db", "cid"})
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) WriteClusterMetrics(db string, snaps []ovnstatus.HealthSnapshot, ecv ovnstatus.ExtendedConsensusResult, expectedReplicas int) {
|
||||
cid := cidFromSnaps(snaps)
|
||||
|
||||
// Core cluster health
|
||||
r.metrics.clusterQuorum.WithLabelValues(db, cid).Set(b2f(ecv.HasMajority))
|
||||
r.metrics.allAgree.WithLabelValues(db, cid).Set(b2f(ecv.AllAgree))
|
||||
r.metrics.membersExpected.WithLabelValues(db, cid).Set(float64(expectedReplicas))
|
||||
r.metrics.membersObserved.WithLabelValues(db, cid).Set(float64(ecv.MembersCount))
|
||||
r.metrics.ipsExpected.WithLabelValues(db, cid).Set(float64(len(ecv.ConsensusResult.TruthView.Members))) // optional; or len(hints.ExpectedIPs)
|
||||
r.metrics.ipsObserved.WithLabelValues(db, cid).Set(float64(ecv.DistinctIPCount))
|
||||
r.metrics.excessMembers.WithLabelValues(db, cid).Set(float64(ecv.ExpectedExcess))
|
||||
r.metrics.missingMembers.WithLabelValues(db, cid).Set(float64(ecv.ExpectedShortfall))
|
||||
r.metrics.unexpectedIPsCount.WithLabelValues(db, cid).Set(float64(len(ecv.UnexpectedIPs)))
|
||||
r.metrics.missingExpectedIPsCount.WithLabelValues(db, cid).Set(float64(len(ecv.MissingExpectedIPs)))
|
||||
r.metrics.ipConflictsCount.WithLabelValues(db, cid).Set(float64(len(ecv.IPConflicts)))
|
||||
|
||||
// Count SIDs with >1 distinct addresses
|
||||
disagree := 0
|
||||
for _, n := range ecv.SIDAddressDisagreements {
|
||||
if n > 1 {
|
||||
disagree++
|
||||
}
|
||||
}
|
||||
r.metrics.sidAddrDisagreements.WithLabelValues(db, cid).Set(float64(disagree))
|
||||
|
||||
// Consensus summary
|
||||
r.metrics.consensusMajoritySize.WithLabelValues(db, cid).Set(float64(len(ecv.MajorityMembers)))
|
||||
r.metrics.consensusMinoritySize.WithLabelValues(db, cid).Set(float64(len(ecv.MinorityMembers)))
|
||||
|
||||
// Sum diffs across reporters (missing + extra + mismatches)
|
||||
totalDiffs := 0
|
||||
for _, d := range ecv.Diffs {
|
||||
totalDiffs += len(d.MissingSIDs) + len(d.ExtraSIDs) + len(d.AddressMismatches)
|
||||
}
|
||||
r.metrics.consensusDiffsTotal.WithLabelValues(db, cid).Set(float64(totalDiffs))
|
||||
|
||||
// Sparse per-key exports (reset then re-emit for this {db,cid})
|
||||
r.metrics.unexpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for _, ip := range ecv.UnexpectedIPs {
|
||||
r.metrics.unexpectedIPGauge.WithLabelValues(db, cid, ip).Set(1)
|
||||
}
|
||||
|
||||
r.metrics.missingExpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for _, ip := range ecv.MissingExpectedIPs {
|
||||
r.metrics.missingExpectedIPGauge.WithLabelValues(db, cid, ip).Set(1)
|
||||
}
|
||||
|
||||
r.metrics.ipConflictGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for ip, sids := range ecv.IPConflicts {
|
||||
r.metrics.ipConflictGauge.WithLabelValues(db, cid, ip).Set(float64(len(sids)))
|
||||
}
|
||||
|
||||
// Only emit suspects when remediation is warranted (e.g., TooManyMembers / unexpected IPs / conflicts)
|
||||
r.metrics.suspectStaleGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
if ecv.TooManyMembers || len(ecv.UnexpectedIPs) > 0 || len(ecv.IPConflicts) > 0 {
|
||||
for _, sid := range ecv.SuspectStaleSIDs {
|
||||
r.metrics.suspectStaleGauge.WithLabelValues(db, cid, sid).Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot timestamp
|
||||
r.metrics.snapshotTimestampSec.WithLabelValues(db, cid).Set(float64(time.Now().Unix()))
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) WriteMemberMetrics(db string, snaps []ovnstatus.HealthSnapshot, views []ovnstatus.MemberView, ecv ovnstatus.ExtendedConsensusResult) {
|
||||
cid := cidFromSnaps(snaps)
|
||||
|
||||
// Figure out current leader SID (prefer local view from any leader snapshot)
|
||||
curLeader := ""
|
||||
for _, s := range snaps {
|
||||
if s.Local.Leader {
|
||||
curLeader = s.Local.SID
|
||||
break
|
||||
}
|
||||
}
|
||||
// Leader transitions
|
||||
key := db + "|" + cid
|
||||
if prev, ok := r.lastLeader[key]; ok && prev != "" && curLeader != "" && prev != curLeader {
|
||||
r.metrics.leaderTransitionsTotal.WithLabelValues(db, cid).Inc()
|
||||
}
|
||||
if curLeader != "" {
|
||||
r.lastLeader[key] = curLeader
|
||||
}
|
||||
|
||||
// Build quick maps for reporter set & IP per SID (best-effort)
|
||||
reporter := map[string]struct{}{}
|
||||
for _, v := range views {
|
||||
if v.FromSID != "" {
|
||||
reporter[v.FromSID] = struct{}{}
|
||||
}
|
||||
}
|
||||
sidToIP := map[string]string{}
|
||||
for _, v := range views {
|
||||
for sid, addr := range v.Members {
|
||||
if sidToIP[sid] == "" && addr != "" {
|
||||
sidToIP[sid] = ovnstatus.AddrToIP(addr) // expose addrToIP or wrap here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset member vectors for this {db,cid} (avoid stale series)
|
||||
r.metrics.memberConnected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLeader.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLastMsgMs.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndex.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndexGap.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberMissingReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Leader index (to compute gaps)
|
||||
lIdx := leaderIndex(snaps, curLeader)
|
||||
|
||||
// Emit one series per snapshot (self view)
|
||||
for _, s := range snaps {
|
||||
sid := s.Local.SID
|
||||
ip := sidToIP[sid]
|
||||
if ip == "" {
|
||||
ip = "unknown"
|
||||
}
|
||||
|
||||
r.metrics.memberConnected.WithLabelValues(db, cid, sid, ip).Set(b2f(s.Local.Connected))
|
||||
r.metrics.memberLeader.WithLabelValues(db, cid, sid).Set(b2f(s.Local.Leader))
|
||||
r.metrics.memberIndex.WithLabelValues(db, cid, sid).Set(float64(s.Local.Index))
|
||||
|
||||
if lIdx != nil && s.Local.Index >= 0 {
|
||||
gap := *lIdx - s.Local.Index
|
||||
if gap < 0 {
|
||||
gap = 0
|
||||
}
|
||||
r.metrics.memberIndexGap.WithLabelValues(db, cid, sid).Set(float64(gap))
|
||||
}
|
||||
|
||||
// Reporter presence
|
||||
_, isReporter := reporter[sid]
|
||||
r.metrics.memberReporter.WithLabelValues(db, cid, sid).Set(b2f(isReporter))
|
||||
}
|
||||
|
||||
// “Missing reporter” SIDs = union − reporters (from ecv)
|
||||
reporterSet := map[string]struct{}{}
|
||||
for sid := range reporter {
|
||||
reporterSet[sid] = struct{}{}
|
||||
}
|
||||
unionSet := map[string]struct{}{}
|
||||
for _, sid := range ecv.UnionMembers {
|
||||
unionSet[sid] = struct{}{}
|
||||
}
|
||||
for sid := range unionSet {
|
||||
if _, ok := reporterSet[sid]; !ok {
|
||||
r.metrics.memberMissingReporter.WithLabelValues(db, cid, sid).Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy follower freshness (if you kept LastMsgMs in servers parsing)
|
||||
// We only know LastMsgMs from the Full.Servers in each snapshot; pick the freshest per SID.
|
||||
lastMsg := map[string]int64{}
|
||||
for _, s := range snaps {
|
||||
for _, srv := range s.Full.Servers {
|
||||
if srv.LastMsgMs != nil {
|
||||
cur, ok := lastMsg[srv.SID]
|
||||
if !ok || *srv.LastMsgMs < cur {
|
||||
lastMsg[srv.SID] = *srv.LastMsgMs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for sid, ms := range lastMsg {
|
||||
r.metrics.memberLastMsgMs.WithLabelValues(db, cid, sid).Set(float64(ms))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) deleteAllFor(db, cid string) {
|
||||
// Cluster-level vecs (db,cid)
|
||||
r.metrics.clusterQuorum.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.allAgree.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.membersExpected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.membersObserved.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipsExpected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipsObserved.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.excessMembers.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingMembers.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.unexpectedIPsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingExpectedIPsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipConflictsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.sidAddrDisagreements.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
r.metrics.consensusMajoritySize.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.consensusMinoritySize.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.consensusDiffsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Sparse detail vecs (db,cid,*)
|
||||
r.metrics.unexpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingExpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipConflictGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.suspectStaleGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Per-member vecs (db,cid,*)
|
||||
r.metrics.memberConnected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLeader.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLastMsgMs.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndex.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndexGap.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberMissingReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Ops vecs (db,cid)
|
||||
r.metrics.leaderTransitionsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.collectErrorsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.publishEventsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.snapshotTimestampSec.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
}
|
||||
31
internal/controller/kubeovnplunger/util.go
Normal file
31
internal/controller/kubeovnplunger/util.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import "github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
|
||||
func b2f(b bool) float64 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Pull a cluster UUID (cid) from any snapshots’ Local.CID (falls back to "")
|
||||
func cidFromSnaps(snaps []ovnstatus.HealthSnapshot) string {
|
||||
for _, s := range snaps {
|
||||
if s.Local.CID != "" {
|
||||
return s.Local.CID
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Map SID -> last local index to compute gaps (optional)
|
||||
func leaderIndex(snaps []ovnstatus.HealthSnapshot, leaderSID string) (idx *int64) {
|
||||
for _, s := range snaps {
|
||||
if s.Local.SID == leaderSID && s.Local.Index > 0 {
|
||||
v := s.Local.Index
|
||||
return &v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
@@ -0,0 +1,367 @@
|
||||
package lineagelabeler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/shared/crdmem"
|
||||
"github.com/cozystack/cozystack/pkg/lineage"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var ErrNoAncestors = errors.New("no ancestors")
|
||||
|
||||
type LineageLabelerReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
WatchResourceCSV string
|
||||
|
||||
dynClient dynamic.Interface
|
||||
mapper meta.RESTMapper
|
||||
|
||||
appMap atomic.Value
|
||||
once sync.Once
|
||||
|
||||
mem *crdmem.Memory
|
||||
}
|
||||
|
||||
type chartRef struct{ repo, chart string }
|
||||
type appRef struct{ groupVersion, kind, prefix string }
|
||||
|
||||
func (r *LineageLabelerReconciler) initMapping() {
|
||||
r.once.Do(func() {
|
||||
r.appMap.Store(make(map[chartRef]appRef))
|
||||
})
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) currentMap() map[chartRef]appRef {
|
||||
val := r.appMap.Load()
|
||||
if val == nil {
|
||||
return map[chartRef]appRef{}
|
||||
}
|
||||
return val.(map[chartRef]appRef)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
cfg := r.currentMap()
|
||||
s := hr.Spec.Chart.Spec
|
||||
key := chartRef{s.SourceRef.Name, s.Chart}
|
||||
if v, ok := cfg[key]; ok {
|
||||
return v.groupVersion, v.kind, v.prefix, nil
|
||||
}
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
|
||||
func parseGVKList(csv string) ([]schema.GroupVersionKind, error) {
|
||||
csv = strings.TrimSpace(csv)
|
||||
if csv == "" {
|
||||
return nil, fmt.Errorf("watch resource list is empty")
|
||||
}
|
||||
parts := strings.Split(csv, ",")
|
||||
out := make([]schema.GroupVersionKind, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
s := strings.Split(p, "/")
|
||||
if len(s) == 2 {
|
||||
out = append(out, schema.GroupVersionKind{Group: "", Version: s[0], Kind: s[1]})
|
||||
continue
|
||||
}
|
||||
if len(s) == 3 {
|
||||
out = append(out, schema.GroupVersionKind{Group: s[0], Version: s[1], Kind: s[2]})
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("invalid resource token %q, expected 'group/version/Kind' or 'v1/Kind'", p)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
r.initMapping()
|
||||
|
||||
cfg := rest.CopyConfig(mgr.GetConfig())
|
||||
dc, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached := memory.NewMemCacheClient(disco)
|
||||
r.dynClient = dc
|
||||
r.mapper = restmapper.NewDeferredDiscoveryRESTMapper(cached)
|
||||
|
||||
if r.mem == nil {
|
||||
r.mem = crdmem.Global()
|
||||
}
|
||||
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(gvks) == 0 {
|
||||
return fmt.Errorf("no resources to watch")
|
||||
}
|
||||
|
||||
b := ctrl.NewControllerManagedBy(mgr).Named("lineage-labeler")
|
||||
|
||||
nsPred := predicate.NewPredicateFuncs(func(obj client.Object) bool {
|
||||
ns := obj.GetNamespace()
|
||||
return ns != "" && strings.HasPrefix(ns, "tenant-")
|
||||
})
|
||||
|
||||
primary := gvks[0]
|
||||
primaryObj := &unstructured.Unstructured{}
|
||||
primaryObj.SetGroupVersionKind(primary)
|
||||
b = b.For(primaryObj,
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for _, gvk := range gvks[1:] {
|
||||
u := &unstructured.Unstructured{}
|
||||
u.SetGroupVersionKind(gvk)
|
||||
b = b.Watches(u,
|
||||
&handler.EnqueueRequestForObject{},
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
b = b.Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
_ = r.refreshAppMap(context.Background())
|
||||
|
||||
return b.Complete(r)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) refreshAppMap(ctx context.Context) error {
|
||||
var items []cozyv1alpha1.CozystackResourceDefinition
|
||||
var err error
|
||||
if r.mem != nil {
|
||||
items, err = r.mem.ListFromCacheOrAPI(ctx, r.Client)
|
||||
} else {
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
err = r.Client.List(ctx, &list)
|
||||
items = list.Items
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newMap := make(map[chartRef]appRef, len(items))
|
||||
for _, crd := range items {
|
||||
k := chartRef{
|
||||
repo: crd.Spec.Release.Chart.SourceRef.Name,
|
||||
chart: crd.Spec.Release.Chart.Name,
|
||||
}
|
||||
v := appRef{
|
||||
groupVersion: "apps.cozystack.io/v1alpha1",
|
||||
kind: crd.Spec.Application.Kind,
|
||||
prefix: crd.Spec.Release.Prefix,
|
||||
}
|
||||
if _, exists := newMap[k]; exists {
|
||||
continue
|
||||
}
|
||||
newMap[k] = v
|
||||
}
|
||||
r.appMap.Store(newMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
if req.Namespace == "" || !strings.HasPrefix(req.Namespace, "tenant-") {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if len(r.currentMap()) == 0 {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
if len(r.currentMap()) == 0 {
|
||||
return ctrl.Result{RequeueAfter: 2 * time.Second}, nil
|
||||
}
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
var obj *unstructured.Unstructured
|
||||
found := false
|
||||
|
||||
for _, gvk := range gvks {
|
||||
mapping, mErr := r.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if mErr != nil {
|
||||
continue
|
||||
}
|
||||
ns := req.Namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
ns = ""
|
||||
}
|
||||
res, gErr := r.dynClient.Resource(mapping.Resource).Namespace(ns).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if gErr != nil {
|
||||
if apierrors.IsNotFound(gErr) {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
obj = res
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
if !found || obj == nil {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
existing := obj.GetLabels()
|
||||
if existing == nil {
|
||||
existing = map[string]string{}
|
||||
}
|
||||
|
||||
keys := []string{
|
||||
"apps.cozystack.io/application.group",
|
||||
"apps.cozystack.io/application.kind",
|
||||
"apps.cozystack.io/application.name",
|
||||
}
|
||||
allPresent := true
|
||||
for _, k := range keys {
|
||||
if _, ok := existing[k]; !ok {
|
||||
allPresent = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allPresent {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
labels, warn, err := r.computeLabels(ctx, obj)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoAncestors) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
if warn != "" {
|
||||
l.V(1).Info("lineage ambiguous; using first ancestor", "name", req.NamespacedName)
|
||||
}
|
||||
|
||||
for k, v := range labels {
|
||||
existing[k] = v
|
||||
}
|
||||
obj.SetLabels(existing)
|
||||
|
||||
// Server-Side Apply: claim ownership of our label keys
|
||||
gvk := obj.GroupVersionKind()
|
||||
patch := &unstructured.Unstructured{}
|
||||
patch.SetGroupVersionKind(gvk)
|
||||
patch.SetNamespace(obj.GetNamespace())
|
||||
patch.SetName(obj.GetName())
|
||||
patch.SetLabels(map[string]string{
|
||||
"apps.cozystack.io/application.group": existing["apps.cozystack.io/application.group"],
|
||||
"apps.cozystack.io/application.kind": existing["apps.cozystack.io/application.kind"],
|
||||
"apps.cozystack.io/application.name": existing["apps.cozystack.io/application.name"],
|
||||
})
|
||||
|
||||
// Use controller-runtime client with Apply patch type and field owner
|
||||
if err := r.Patch(ctx, patch,
|
||||
client.Apply,
|
||||
client.FieldOwner("cozystack/lineage"),
|
||||
client.ForceOwnership(false),
|
||||
); err != nil {
|
||||
if apierrors.IsConflict(err) {
|
||||
return ctrl.Result{RequeueAfter: 500 * time.Millisecond}, nil
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) computeLabels(ctx context.Context, o *unstructured.Unstructured) (map[string]string, string, error) {
|
||||
owners := lineage.WalkOwnershipGraph(ctx, r.dynClient, r.mapper, r, o)
|
||||
if len(owners) == 0 {
|
||||
return nil, "", ErrNoAncestors
|
||||
}
|
||||
obj, err := owners[0].GetUnstructured(ctx, r.dynClient, r.mapper)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(obj.GetAPIVersion())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid APIVersion %s: %w", obj.GetAPIVersion(), err)
|
||||
}
|
||||
var warn string
|
||||
if len(owners) > 1 {
|
||||
warn = "ambiguous"
|
||||
}
|
||||
group := gv.Group
|
||||
if len(group) > 63 {
|
||||
group = trimDNSLabel(group[:63])
|
||||
}
|
||||
return map[string]string{
|
||||
"apps.cozystack.io/application.group": group,
|
||||
"apps.cozystack.io/application.kind": obj.GetKind(),
|
||||
"apps.cozystack.io/application.name": obj.GetName(),
|
||||
}, warn, nil
|
||||
}
|
||||
|
||||
func trimDNSLabel(s string) string {
|
||||
for len(s) > 0 {
|
||||
b := s[len(s)-1]
|
||||
if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
|
||||
return s
|
||||
}
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -33,6 +33,7 @@ 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 {
|
||||
|
||||
@@ -26,6 +26,7 @@ type TenantHelmReconciler struct {
|
||||
|
||||
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 {
|
||||
|
||||
99
internal/shared/crdmem/memory.go
Normal file
99
internal/shared/crdmem/memory.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package crdmem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type Memory struct {
|
||||
mu sync.RWMutex
|
||||
data map[string]cozyv1alpha1.CozystackResourceDefinition
|
||||
primed bool
|
||||
primeOnce sync.Once
|
||||
}
|
||||
|
||||
func New() *Memory {
|
||||
return &Memory{data: make(map[string]cozyv1alpha1.CozystackResourceDefinition)}
|
||||
}
|
||||
|
||||
var (
|
||||
global *Memory
|
||||
globalOnce sync.Once
|
||||
)
|
||||
|
||||
func Global() *Memory {
|
||||
globalOnce.Do(func() { global = New() })
|
||||
return global
|
||||
}
|
||||
|
||||
func (m *Memory) Upsert(obj *cozyv1alpha1.CozystackResourceDefinition) {
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.data[obj.Name] = *obj.DeepCopy()
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Memory) Delete(name string) {
|
||||
m.mu.Lock()
|
||||
delete(m.data, name)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Memory) Snapshot() []cozyv1alpha1.CozystackResourceDefinition {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
out := make([]cozyv1alpha1.CozystackResourceDefinition, 0, len(m.data))
|
||||
for _, v := range m.data {
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *Memory) IsPrimed() bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.primed
|
||||
}
|
||||
|
||||
type runnable func(context.Context) error
|
||||
|
||||
func (r runnable) Start(ctx context.Context) error { return r(ctx) }
|
||||
|
||||
func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error {
|
||||
var errOut error
|
||||
m.primeOnce.Do(func() {
|
||||
errOut = mgr.Add(runnable(func(ctx context.Context) error {
|
||||
if ok := mgr.GetCache().WaitForCacheSync(ctx); !ok {
|
||||
return nil
|
||||
}
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := mgr.GetClient().List(ctx, &list); err == nil {
|
||||
for i := range list.Items {
|
||||
m.Upsert(&list.Items[i])
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.primed = true
|
||||
m.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
})
|
||||
return errOut
|
||||
}
|
||||
|
||||
func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.CozystackResourceDefinition, error) {
|
||||
if m.IsPrimed() {
|
||||
return m.Snapshot(), nil
|
||||
}
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := c.List(ctx, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list.Items, nil
|
||||
}
|
||||
293
internal/sse/server.go
Normal file
293
internal/sse/server.go
Normal file
@@ -0,0 +1,293 @@
|
||||
// Package sse provides a tiny Server-Sent Events server with pluggable routes.
|
||||
// No external deps; safe for quick demos and small dashboards.
|
||||
package sse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Options configures the SSE server.
|
||||
type Options struct {
|
||||
// Addr is the listening address, e.g. ":8080" or "127.0.0.1:0".
|
||||
Addr string
|
||||
|
||||
// IndexPath is the path serving a minimal live HTML page ("" to disable).
|
||||
// e.g. "/" or "/status"
|
||||
IndexPath string
|
||||
|
||||
// StreamPath is the SSE endpoint path, e.g. "/stream".
|
||||
StreamPath string
|
||||
|
||||
// Title for the index page (cosmetic).
|
||||
Title string
|
||||
|
||||
// AllowCORS, if true, sets Access-Control-Allow-Origin: * for /stream.
|
||||
AllowCORS bool
|
||||
|
||||
// ClientBuf is the per-client buffered message queue size.
|
||||
// If 0, defaults to 16. When full, new messages are dropped for that client.
|
||||
ClientBuf int
|
||||
|
||||
// Heartbeat sends a comment line every interval to keep connections alive.
|
||||
// If 0, defaults to 25s.
|
||||
Heartbeat time.Duration
|
||||
|
||||
// Logger (optional). If nil, log.Printf is used.
|
||||
Logger *log.Logger
|
||||
}
|
||||
|
||||
// Server is a simple SSE broadcaster.
|
||||
type Server struct {
|
||||
opts Options
|
||||
mux *http.ServeMux
|
||||
http *http.Server
|
||||
|
||||
clientsMu sync.RWMutex
|
||||
clients map[*client]struct{}
|
||||
|
||||
// latest holds the most recent payload (sent to new clients on connect).
|
||||
latestMu sync.RWMutex
|
||||
latest string
|
||||
}
|
||||
|
||||
type client struct {
|
||||
ch chan string
|
||||
closeCh chan struct{}
|
||||
flusher http.Flusher
|
||||
w http.ResponseWriter
|
||||
req *http.Request
|
||||
logf func(string, ...any)
|
||||
heartbeat time.Duration
|
||||
}
|
||||
|
||||
func New(opts Options) *Server {
|
||||
if opts.ClientBuf <= 0 {
|
||||
opts.ClientBuf = 16
|
||||
}
|
||||
if opts.Heartbeat <= 0 {
|
||||
opts.Heartbeat = 25 * time.Second
|
||||
}
|
||||
if opts.Addr == "" {
|
||||
opts.Addr = ":8080"
|
||||
}
|
||||
if opts.StreamPath == "" {
|
||||
opts.StreamPath = "/stream"
|
||||
}
|
||||
if opts.IndexPath == "" {
|
||||
opts.IndexPath = "/"
|
||||
}
|
||||
s := &Server{
|
||||
opts: opts,
|
||||
mux: http.NewServeMux(),
|
||||
clients: make(map[*client]struct{}),
|
||||
}
|
||||
s.routes()
|
||||
s.http = &http.Server{
|
||||
Addr: opts.Addr,
|
||||
Handler: s.mux,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) routes() {
|
||||
if s.opts.IndexPath != "" {
|
||||
s.mux.HandleFunc(s.opts.IndexPath, s.handleIndex)
|
||||
}
|
||||
s.mux.HandleFunc(s.opts.StreamPath, s.handleStream)
|
||||
}
|
||||
|
||||
func (s *Server) logf(format string, args ...any) {
|
||||
if s.opts.Logger != nil {
|
||||
s.opts.Logger.Printf(format, args...)
|
||||
} else {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServe starts the HTTP server (blocking).
|
||||
func (s *Server) ListenAndServe() error {
|
||||
s.logf("sse: listening on http://%s (index=%s, stream=%s)", s.http.Addr, s.opts.IndexPath, s.opts.StreamPath)
|
||||
return s.http.ListenAndServe()
|
||||
}
|
||||
|
||||
// Shutdown gracefully stops the server.
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
s.clientsMu.Lock()
|
||||
for c := range s.clients {
|
||||
close(c.closeCh)
|
||||
}
|
||||
s.clientsMu.Unlock()
|
||||
return s.http.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Publish broadcasts a new payload to all clients and stores it as latest.
|
||||
func (s *Server) Publish(payload string) {
|
||||
// Store latest
|
||||
s.latestMu.Lock()
|
||||
s.latest = payload
|
||||
s.latestMu.Unlock()
|
||||
|
||||
// Broadcast
|
||||
s.clientsMu.RLock()
|
||||
defer s.clientsMu.RUnlock()
|
||||
for c := range s.clients {
|
||||
select {
|
||||
case c.ch <- payload:
|
||||
default:
|
||||
// Drop if client is slow (buffer full)
|
||||
if s.opts.Logger != nil {
|
||||
s.opts.Logger.Printf("sse: dropping message to slow client %p", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
page := indexTemplate(s.opts.Title, s.opts.StreamPath)
|
||||
_, _ = w.Write([]byte(page))
|
||||
}
|
||||
|
||||
func (s *Server) handleStream(w http.ResponseWriter, r *http.Request) {
|
||||
// Required SSE headers
|
||||
if s.opts.AllowCORS {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ch: make(chan string, s.opts.ClientBuf),
|
||||
closeCh: make(chan struct{}),
|
||||
flusher: flusher,
|
||||
w: w,
|
||||
req: r,
|
||||
logf: s.logf,
|
||||
heartbeat: s.opts.Heartbeat,
|
||||
}
|
||||
|
||||
// Register client
|
||||
s.clientsMu.Lock()
|
||||
s.clients[c] = struct{}{}
|
||||
s.clientsMu.Unlock()
|
||||
|
||||
// Initial comment to open the stream for some proxies
|
||||
fmt.Fprintf(w, ": connected %s\n\n", time.Now().Format(time.RFC3339))
|
||||
flusher.Flush()
|
||||
|
||||
// Send latest if any
|
||||
s.latestMu.RLock()
|
||||
latest := s.latest
|
||||
s.latestMu.RUnlock()
|
||||
if latest != "" {
|
||||
writeSSE(w, latest)
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Start pump
|
||||
go c.pump()
|
||||
|
||||
// Block until client disconnects
|
||||
<-r.Context().Done()
|
||||
|
||||
// Unregister client
|
||||
close(c.closeCh)
|
||||
s.clientsMu.Lock()
|
||||
delete(s.clients, c)
|
||||
s.clientsMu.Unlock()
|
||||
}
|
||||
|
||||
func (c *client) pump() {
|
||||
t := time.NewTicker(c.heartbeat)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-c.closeCh:
|
||||
return
|
||||
case msg := <-c.ch:
|
||||
writeSSE(c.w, msg)
|
||||
c.flusher.Flush()
|
||||
case <-t.C:
|
||||
// heartbeat comment (keeps connections alive through proxies)
|
||||
fmt.Fprint(c.w, ": hb\n\n")
|
||||
c.flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSSE(w http.ResponseWriter, msg string) {
|
||||
// Split on lines; each needs its own "data:" field per the SSE spec
|
||||
lines := strings.Split(strings.TrimRight(msg, "\n"), "\n")
|
||||
for _, ln := range lines {
|
||||
fmt.Fprintf(w, "data: %s\n", ln)
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
|
||||
// Minimal index page with live updates
|
||||
func indexTemplate(title, streamPath string) string {
|
||||
if title == "" {
|
||||
title = "SSE Stream"
|
||||
}
|
||||
if streamPath == "" {
|
||||
streamPath = "/stream"
|
||||
}
|
||||
const tpl = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{.Title}}</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
||||
pre { background:#111; color:#eee; padding:1rem; border-radius:12px; white-space:pre-wrap;}
|
||||
.status { margin-bottom: 1rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Title}}</h1>
|
||||
<div class="status">Connecting…</div>
|
||||
<pre id="out"></pre>
|
||||
<script>
|
||||
const statusEl = document.querySelector('.status');
|
||||
const out = document.getElementById('out');
|
||||
const es = new EventSource('{{.Stream}}');
|
||||
es.onmessage = (e) => {
|
||||
// Replace content with the latest full snapshot
|
||||
if (e.data === "") return;
|
||||
// We accumulate until a blank 'data:' terminator; simpler approach: reset on first line.
|
||||
// For this demo, server always sends full content in one event, so just overwrite.
|
||||
out.textContent = (out._acc ?? "") + e.data + "\n";
|
||||
};
|
||||
es.addEventListener('open', () => { statusEl.textContent = "Connected"; out._acc = ""; });
|
||||
es.addEventListener('error', () => { statusEl.textContent = "Disconnected (browser will retry)…"; out._acc = ""; });
|
||||
// Optional: keep the latest only per message
|
||||
es.onmessage = (e) => {
|
||||
out.textContent = e.data + "\n";
|
||||
statusEl.textContent = "Connected";
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
page, _ := template.New("idx").Parse(tpl)
|
||||
var b strings.Builder
|
||||
_ = page.Execute(&b, map[string]any{
|
||||
"Title": title,
|
||||
"Stream": streamPath,
|
||||
})
|
||||
return b.String()
|
||||
}
|
||||
@@ -4,6 +4,5 @@
|
||||
cd packages/core/installer
|
||||
make image-cozystack REGISTRY=YOUR_CUSTOM_REGISTRY
|
||||
make apply
|
||||
kubectl delete pod dashboard-redis-master-0 -n cozy-dashboard
|
||||
kubectl delete po -l app=source-controller -n cozy-fluxcd
|
||||
```
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
yq -o json -i '.properties = {}' values.schema.json
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"properties": {},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.11.1
|
||||
version: 0.13.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
CLICKHOUSE_BACKUP_TAG = $(shell awk '$$0 ~ /^version:/ {print $$2}' Chart.yaml)
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
yq -i -o json --indent 4 '.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
|
||||
image:
|
||||
docker buildx build images/clickhouse-backup \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/clickhouse-backup:$(call settag,$(CLICKHOUSE_BACKUP_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/clickhouse-backup:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/clickhouse-backup.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/clickhouse-backup:$(call settag,$(CLICKHOUSE_BACKUP_TAG))@$$(yq e '."containerimage.digest"' images/clickhouse-backup.json -o json -r)" \
|
||||
> images/clickhouse-backup.tag
|
||||
rm -f images/clickhouse-backup.json
|
||||
|
||||
@@ -23,35 +23,54 @@ For more details, read [Restic: Effective Backup from Stdin](https://blog.aenix.
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `replicas` | Number of Clickhouse replicas | `2` |
|
||||
| `shards` | Number of Clickhouse shards | `1` |
|
||||
| `resources` | Explicit CPU and memory configuration for each ClickHouse replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `small` |
|
||||
| `size` | Persistent Volume Claim size, available for application data | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the application data | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of Clickhouse replicas | `int` | `2` |
|
||||
| `shards` | Number of Clickhouse shards | `int` | `1` |
|
||||
| `resources` | Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `size` | Persistent Volume Claim size, available for application data | `quantity` | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `string` | `""` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ---------------- | -------------------------------------------------------- | ----- |
|
||||
| `logStorageSize` | Size of Persistent Volume for logs | `2Gi` |
|
||||
| `logTTL` | TTL (expiration time) for query_log and query_thread_log | `15` |
|
||||
| `users` | Users configuration | `{}` |
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------- | ------------------------------------------------------------ | ------------------- | ------- |
|
||||
| `logStorageSize` | Size of Persistent Volume for logs | `quantity` | `2Gi` |
|
||||
| `logTTL` | TTL (expiration time) for `query_log` and `query_thread_log` | `int` | `15` |
|
||||
| `users` | Users configuration | `map[string]object` | `{...}` |
|
||||
| `users[name].password` | Password for the user | `*string` | `null` |
|
||||
| `users[name].readonly` | User is `readonly`, default is `false`. | `*bool` | `null` |
|
||||
|
||||
|
||||
### Backup parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | ---------------------------------------------- | ------------------------------------------------------ |
|
||||
| `backup.enabled` | Enable periodic backups | `false` |
|
||||
| `backup.s3Region` | AWS S3 region where backups are stored | `us-east-1` |
|
||||
| `backup.s3Bucket` | S3 bucket used for storing backups | `s3.example.org/clickhouse-backups` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `0 2 * * *` |
|
||||
| `backup.cleanupStrategy` | Retention strategy for cleaning up old backups | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
|
||||
| `backup.s3AccessKey` | Access key for S3, used for authentication | `oobaiRus9pah8PhohL1ThaeTa4UVa7gu` |
|
||||
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `ju3eum4dekeich9ahM1te8waeGai0oog` |
|
||||
| `backup.resticPassword` | Password for Restic backup encryption | `ChaXoveekoh6eigh4siesheeda2quai0` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | ---------------------------------------------- | -------- | ------------------------------------------------------ |
|
||||
| `backup` | Backup configuration | `object` | `{}` |
|
||||
| `backup.enabled` | Enable regular backups, default is `false` | `bool` | `false` |
|
||||
| `backup.s3Region` | AWS S3 region where backups are stored | `string` | `us-east-1` |
|
||||
| `backup.s3Bucket` | S3 bucket used for storing backups | `string` | `s3.example.org/clickhouse-backups` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `string` | `0 2 * * *` |
|
||||
| `backup.cleanupStrategy` | Retention strategy for cleaning up old backups | `string` | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
|
||||
| `backup.s3AccessKey` | Access key for S3, used for authentication | `string` | `<your-access-key>` |
|
||||
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `string` | `<your-secret-key>` |
|
||||
| `backup.resticPassword` | Password for Restic backup encryption | `string` | `<password>` |
|
||||
|
||||
|
||||
### Clickhouse Keeper parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `clickhouseKeeper` | Clickhouse Keeper configuration | `*object` | `{}` |
|
||||
| `clickhouseKeeper.enabled` | Deploy ClickHouse Keeper for cluster coordination | `*bool` | `true` |
|
||||
| `clickhouseKeeper.size` | Persistent Volume Claim size, available for application data | `*quantity` | `1Gi` |
|
||||
| `clickhouseKeeper.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
| `clickhouseKeeper.replicas` | Number of Keeper replicas | `*int` | `3` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/clickhouse-backup:0.11.1@sha256:3faf7a4cebf390b9053763107482de175aa0fdb88c1e77424fd81100b1c3a205
|
||||
ghcr.io/cozystack/cozystack/clickhouse-backup:0.13.0@sha256:3faf7a4cebf390b9053763107482de175aa0fdb88c1e77424fd81100b1c3a205
|
||||
|
||||
96
packages/apps/clickhouse/templates/chkeeper.yaml
Normal file
96
packages/apps/clickhouse/templates/chkeeper.yaml
Normal file
@@ -0,0 +1,96 @@
|
||||
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
apiVersion: "clickhouse-keeper.altinity.com/v1"
|
||||
kind: "ClickHouseKeeperInstallation"
|
||||
metadata:
|
||||
name: "{{ .Release.Name }}-keeper"
|
||||
annotations:
|
||||
prometheus.io/port: "7000"
|
||||
prometheus.io/scrape: "true"
|
||||
spec:
|
||||
namespaceDomainPattern: "%s.svc.{{ $clusterDomain }}"
|
||||
configuration:
|
||||
clusters:
|
||||
- name: "cluster1"
|
||||
layout:
|
||||
replicasCount: {{ .Values.clickhouseKeeper.replicas }}
|
||||
settings:
|
||||
logger/level: "trace"
|
||||
logger/console: "true"
|
||||
listen_host: "0.0.0.0"
|
||||
keeper_server/four_letter_word_white_list: "*"
|
||||
keeper_server/coordination_settings/raft_logs_level: "information"
|
||||
prometheus/endpoint: "/metrics"
|
||||
prometheus/port: "7000"
|
||||
prometheus/metrics: "true"
|
||||
prometheus/events: "true"
|
||||
prometheus/asynchronous_metrics: "true"
|
||||
prometheus/status_info: "false"
|
||||
|
||||
defaults:
|
||||
templates:
|
||||
# Templates are specified as default for all clusters
|
||||
podTemplate: default
|
||||
dataVolumeClaimTemplate: default
|
||||
|
||||
templates:
|
||||
podTemplates:
|
||||
- name: default
|
||||
metadata:
|
||||
labels:
|
||||
app: "{{ .Release.Name }}-keeper"
|
||||
annotations:
|
||||
prometheus.io/port: "7000"
|
||||
prometheus.io/scrape: "true"
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
- labelSelector:
|
||||
matchExpressions:
|
||||
- key: "app"
|
||||
operator: In
|
||||
values:
|
||||
- "{{ .Release.Name }}-keeper"
|
||||
topologyKey: "kubernetes.io/hostname"
|
||||
containers:
|
||||
- name: clickhouse-keeper
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: clickhouse/clickhouse-keeper:24.9.2.42
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.clickhouseKeeper.resourcesPreset .Values.resources $) | nindent 20 }}
|
||||
securityContext:
|
||||
fsGroup: 101
|
||||
|
||||
volumeClaimTemplates:
|
||||
- name: default
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: "{{ .Values.clickhouseKeeper.size }}"
|
||||
---
|
||||
apiVersion: operator.victoriametrics.com/v1beta1
|
||||
kind: VMPodScrape
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-keeper
|
||||
namespace: {{ .Release.Namespace }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}-keeper
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- {{ .Release.Namespace }}
|
||||
podMetricsEndpoints:
|
||||
- port: metrics
|
||||
path: /metrics
|
||||
interval: 30s
|
||||
scheme: http
|
||||
relabelConfigs:
|
||||
- action: replace
|
||||
sourceLabels: [__meta_kubernetes_pod_node_name]
|
||||
targetLabel: instance
|
||||
{{- end }}
|
||||
@@ -91,6 +91,18 @@ spec:
|
||||
layout:
|
||||
shardsCount: {{ .Values.shards }}
|
||||
replicasCount: {{ .Values.replicas }}
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
zookeeper:
|
||||
nodes:
|
||||
{{- $replicas := int .Values.clickhouseKeeper.replicas }}
|
||||
{{- $release := .Release.Name }}
|
||||
{{- $namespace := .Release.Namespace }}
|
||||
{{- $clusterDomain := .Values.clusterDomain }}
|
||||
{{- range $i := until $replicas }}
|
||||
- host: "chk-{{ $release }}-keeper-cluster1-0-{{ $i }}.{{ $namespace }}.svc.{{ $clusterDomain }}"
|
||||
port: 2181
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
templates:
|
||||
volumeClaimTemplates:
|
||||
- name: data-volume-template
|
||||
|
||||
@@ -23,6 +23,9 @@ rules:
|
||||
- workloadmonitors
|
||||
resourceNames:
|
||||
- {{ .Release.Name }}
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
- {{ .Release.Name }}-keeper
|
||||
{{- end }}
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
kind: RoleBinding
|
||||
|
||||
@@ -11,3 +11,18 @@ spec:
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-keeper
|
||||
spec:
|
||||
replicas: {{ .Values.clickhouseKeeper.replicas }}
|
||||
minReplicas: 1
|
||||
kind: clickhouse
|
||||
type: clickhouse
|
||||
selector:
|
||||
app: {{ $.Release.Name }}-keeper
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,100 +1,237 @@
|
||||
{
|
||||
"properties": {
|
||||
"backup": {
|
||||
"properties": {
|
||||
"cleanupStrategy": {
|
||||
"default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"description": "Retention strategy for cleaning up old backups",
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable periodic backups",
|
||||
"type": "boolean"
|
||||
},
|
||||
"resticPassword": {
|
||||
"default": "ChaXoveekoh6eigh4siesheeda2quai0",
|
||||
"description": "Password for Restic backup encryption",
|
||||
"type": "string"
|
||||
},
|
||||
"s3AccessKey": {
|
||||
"default": "oobaiRus9pah8PhohL1ThaeTa4UVa7gu",
|
||||
"description": "Access key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"s3Bucket": {
|
||||
"default": "s3.example.org/clickhouse-backups",
|
||||
"description": "S3 bucket used for storing backups",
|
||||
"type": "string"
|
||||
},
|
||||
"s3Region": {
|
||||
"default": "us-east-1",
|
||||
"description": "AWS S3 region where backups are stored",
|
||||
"type": "string"
|
||||
},
|
||||
"s3SecretKey": {
|
||||
"default": "ju3eum4dekeich9ahM1te8waeGai0oog",
|
||||
"description": "Secret key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"schedule": {
|
||||
"default": "0 2 * * *",
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"cleanupStrategy": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"enabled": false,
|
||||
"resticPassword": "\u003cpassword\u003e",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3Bucket": "s3.example.org/clickhouse-backups",
|
||||
"s3Region": "us-east-1",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * *"
|
||||
},
|
||||
"required": [
|
||||
"cleanupStrategy",
|
||||
"enabled",
|
||||
"resticPassword",
|
||||
"s3AccessKey",
|
||||
"s3Bucket",
|
||||
"s3Region",
|
||||
"s3SecretKey",
|
||||
"schedule"
|
||||
],
|
||||
"properties": {
|
||||
"cleanupStrategy": {
|
||||
"description": "Retention strategy for cleaning up old backups",
|
||||
"type": "string",
|
||||
"default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"
|
||||
},
|
||||
"logStorageSize": {
|
||||
"default": "2Gi",
|
||||
"description": "Size of Persistent Volume for logs",
|
||||
"type": "string"
|
||||
"enabled": {
|
||||
"description": "Enable regular backups, default is `false`",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"logTTL": {
|
||||
"default": 15,
|
||||
"description": "TTL (expiration time) for query_log and query_thread_log",
|
||||
"type": "number"
|
||||
"resticPassword": {
|
||||
"description": "Password for Restic backup encryption",
|
||||
"type": "string",
|
||||
"default": "\u003cpassword\u003e"
|
||||
},
|
||||
"s3AccessKey": {
|
||||
"description": "Access key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-access-key\u003e"
|
||||
},
|
||||
"s3Bucket": {
|
||||
"description": "S3 bucket used for storing backups",
|
||||
"type": "string",
|
||||
"default": "s3.example.org/clickhouse-backups"
|
||||
},
|
||||
"s3Region": {
|
||||
"description": "AWS S3 region where backups are stored",
|
||||
"type": "string",
|
||||
"default": "us-east-1"
|
||||
},
|
||||
"s3SecretKey": {
|
||||
"description": "Secret key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-secret-key\u003e"
|
||||
},
|
||||
"schedule": {
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string",
|
||||
"default": "0 2 * * *"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clickhouseKeeper": {
|
||||
"description": "Clickhouse Keeper configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": true,
|
||||
"replicas": 3,
|
||||
"resourcesPreset": "micro",
|
||||
"size": "1Gi"
|
||||
},
|
||||
"required": [
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Deploy ClickHouse Keeper for cluster coordination",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of Clickhouse replicas",
|
||||
"type": "number"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each ClickHouse replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"description": "Number of Keeper replicas",
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "small",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"shards": {
|
||||
"default": 1,
|
||||
"description": "Number of Clickhouse shards",
|
||||
"type": "number"
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"default": "10Gi",
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"type": "string"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the application data",
|
||||
"type": "string"
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"default": "1Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"logStorageSize": {
|
||||
"description": "Size of Persistent Volume for logs",
|
||||
"default": "2Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"logTTL": {
|
||||
"description": "TTL (expiration time) for `query_log` and `query_thread_log`",
|
||||
"type": "integer",
|
||||
"default": 15
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of Clickhouse replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"shards": {
|
||||
"description": "Number of Clickhouse shards",
|
||||
"type": "integer",
|
||||
"default": 1
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
},
|
||||
"users": {
|
||||
"description": "Users configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"description": "Password for the user",
|
||||
"type": "string"
|
||||
},
|
||||
"readonly": {
|
||||
"description": "User is `readonly`, default is `false`.",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,36 @@
|
||||
## @section Common parameters
|
||||
##
|
||||
## @param replicas Number of Clickhouse replicas
|
||||
## @param replicas {int} Number of Clickhouse replicas
|
||||
replicas: 2
|
||||
## @param shards Number of Clickhouse shards
|
||||
## @param shards {int} Number of Clickhouse shards
|
||||
shards: 1
|
||||
## @param resources Explicit CPU and memory configuration for each ClickHouse replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @param resources {*resources} Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources.cpu {*quantity} CPU available to each replica
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each replica
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
resources: {}
|
||||
|
||||
## @param resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
|
||||
|
||||
## @param resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "small"
|
||||
## @param size Persistent Volume Claim size, available for application data
|
||||
## @param size {quantity} Persistent Volume Claim size, available for application data
|
||||
size: 10Gi
|
||||
## @param storageClass StorageClass used to store the application data
|
||||
## @param storageClass {string} StorageClass used to store the data
|
||||
storageClass: ""
|
||||
|
||||
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
## @param logStorageSize Size of Persistent Volume for logs
|
||||
## @param logStorageSize {quantity} Size of Persistent Volume for logs
|
||||
logStorageSize: 2Gi
|
||||
## @param logTTL TTL (expiration time) for query_log and query_thread_log
|
||||
## @param logTTL {int} TTL (expiration time) for `query_log` and `query_thread_log`
|
||||
logTTL: 15
|
||||
## @param users [object] Users configuration
|
||||
## @param users {map[string]user} Users configuration
|
||||
## @field user.password {*string} Password for the user
|
||||
## @field user.readonly {*bool} User is `readonly`, default is `false`.
|
||||
## Example:
|
||||
## users:
|
||||
## user1:
|
||||
@@ -38,21 +44,34 @@ users: {}
|
||||
|
||||
## @section Backup parameters
|
||||
|
||||
## @param backup.enabled Enable periodic backups
|
||||
## @param backup.s3Region AWS S3 region where backups are stored
|
||||
## @param backup.s3Bucket S3 bucket used for storing backups
|
||||
## @param backup.schedule Cron schedule for automated backups
|
||||
## @param backup.cleanupStrategy Retention strategy for cleaning up old backups
|
||||
## @param backup.s3AccessKey Access key for S3, used for authentication
|
||||
## @param backup.s3SecretKey Secret key for S3, used for authentication
|
||||
## @param backup.resticPassword Password for Restic backup encryption
|
||||
## @param backup {backup} Backup configuration
|
||||
## @field backup.enabled {bool} Enable regular backups, default is `false`
|
||||
## @field backup.s3Region {string} AWS S3 region where backups are stored
|
||||
## @field backup.s3Bucket {string} S3 bucket used for storing backups
|
||||
## @field backup.schedule {string} Cron schedule for automated backups
|
||||
## @field backup.cleanupStrategy {string} Retention strategy for cleaning up old backups
|
||||
## @field backup.s3AccessKey {string} Access key for S3, used for authentication
|
||||
## @field backup.s3SecretKey {string} Secret key for S3, used for authentication
|
||||
## @field backup.resticPassword {string} Password for Restic backup encryption
|
||||
backup:
|
||||
enabled: false
|
||||
s3Region: us-east-1
|
||||
s3Bucket: s3.example.org/clickhouse-backups
|
||||
s3Bucket: "s3.example.org/clickhouse-backups"
|
||||
schedule: "0 2 * * *"
|
||||
cleanupStrategy: "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"
|
||||
s3AccessKey: oobaiRus9pah8PhohL1ThaeTa4UVa7gu
|
||||
s3SecretKey: ju3eum4dekeich9ahM1te8waeGai0oog
|
||||
resticPassword: ChaXoveekoh6eigh4siesheeda2quai0
|
||||
s3AccessKey: "<your-access-key>"
|
||||
s3SecretKey: "<your-secret-key>"
|
||||
resticPassword: "<password>"
|
||||
|
||||
|
||||
## @section Clickhouse Keeper parameters
|
||||
## @param clickhouseKeeper {*clickhouseKeeper} Clickhouse Keeper configuration
|
||||
## @field clickhouseKeeper.enabled {*bool} Deploy ClickHouse Keeper for cluster coordination
|
||||
## @field clickhouseKeeper.size {*quantity} Persistent Volume Claim size, available for application data
|
||||
## @field clickhouseKeeper.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
## @field clickhouseKeeper.replicas {*int} Number of Keeper replicas
|
||||
clickhouseKeeper:
|
||||
enabled: true
|
||||
size: 1Gi
|
||||
resourcesPreset: micro
|
||||
replicas: 3
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 1.0.0
|
||||
version: 1.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
include ../../../scripts/package.mk
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
yq -i -o json --indent 4 '.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
|
||||
update:
|
||||
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/FerretDB/FerretDB | awk -F'[/^]' '{sub("^v", "", $$3)} END{print $$3}') && \
|
||||
|
||||
@@ -8,42 +8,52 @@ Internally, FerretDB service is backed by Postgres.
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `replicas` | Number of replicas | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge. | `micro` |
|
||||
| `size` | Persistent Volume size | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `""` |
|
||||
| `external` | Enable external access from outside the cluster | `false` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
| `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` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ----- |
|
||||
| `quorum.minSyncReplicas` | Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed | `0` |
|
||||
| `quorum.maxSyncReplicas` | Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas) | `0` |
|
||||
| `users` | Users configuration | `{}` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `quorum` | Configuration for the quorum-based synchronous replication | `object` | `{}` |
|
||||
| `quorum.minSyncReplicas` | Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed | `int` | `0` |
|
||||
| `quorum.maxSyncReplicas` | Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas) | `int` | `0` |
|
||||
| `users` | Users configuration | `map[string]object` | `{...}` |
|
||||
| `users[name].password` | Password for the user | `*string` | `null` |
|
||||
|
||||
|
||||
### Backup parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | ---------------------------------------------------------- | ----------------------------------- |
|
||||
| `backup.enabled` | Enable regular backups | `false` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `0 2 * * * *` |
|
||||
| `backup.retentionPolicy` | Retention policy | `30d` |
|
||||
| `backup.destinationPath` | Path to store the backup (i.e. s3://bucket/path/to/folder) | `s3://bucket/path/to/folder/` |
|
||||
| `backup.endpointURL` | S3 Endpoint used to upload data to the cloud | `http://minio-gateway-service:9000` |
|
||||
| `backup.s3AccessKey` | Access key for S3, used for authentication | `oobaiRus9pah8PhohL1ThaeTa4UVa7gu` |
|
||||
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `ju3eum4dekeich9ahM1te8waeGai0oog` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | ---------------------------------------------------------- | -------- | ----------------------------------- |
|
||||
| `backup` | Backup configuration | `object` | `{}` |
|
||||
| `backup.enabled` | Enable regular backups, default is `false`. | `bool` | `false` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `string` | `0 2 * * * *` |
|
||||
| `backup.retentionPolicy` | Retention policy | `string` | `30d` |
|
||||
| `backup.endpointURL` | S3 Endpoint used to upload data to the cloud | `string` | `http://minio-gateway-service:9000` |
|
||||
| `backup.destinationPath` | Path to store the backup (i.e. s3://bucket/path/to/folder) | `string` | `s3://bucket/path/to/folder/` |
|
||||
| `backup.s3AccessKey` | Access key for S3, used for authentication | `string` | `<your-access-key>` |
|
||||
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `string` | `<your-secret-key>` |
|
||||
|
||||
|
||||
### Bootstrap (recovery) parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `bootstrap.enabled` | Restore database cluster from a backup | `false` |
|
||||
| `bootstrap.recoveryTime` | Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest | `""` |
|
||||
| `bootstrap.oldName` | Name of database cluster before deleting | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
|
||||
| `bootstrap` | Bootstrap (recovery) configuration | `object` | `{}` |
|
||||
| `bootstrap.enabled` | Restore database cluster from a backup | `*bool` | `false` |
|
||||
| `bootstrap.recoveryTime` | Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest. | `*string` | `""` |
|
||||
| `bootstrap.oldName` | Name of database cluster before deleting | `*string` | `""` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
{{- if .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: ferretdb
|
||||
|
||||
@@ -1,120 +1,202 @@
|
||||
{
|
||||
"properties": {
|
||||
"backup": {
|
||||
"properties": {
|
||||
"destinationPath": {
|
||||
"default": "s3://bucket/path/to/folder/",
|
||||
"description": "Path to store the backup (i.e. s3://bucket/path/to/folder)",
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable regular backups",
|
||||
"type": "boolean"
|
||||
},
|
||||
"endpointURL": {
|
||||
"default": "http://minio-gateway-service:9000",
|
||||
"description": "S3 Endpoint used to upload data to the cloud",
|
||||
"type": "string"
|
||||
},
|
||||
"retentionPolicy": {
|
||||
"default": "30d",
|
||||
"description": "Retention policy",
|
||||
"type": "string"
|
||||
},
|
||||
"s3AccessKey": {
|
||||
"default": "oobaiRus9pah8PhohL1ThaeTa4UVa7gu",
|
||||
"description": "Access key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"s3SecretKey": {
|
||||
"default": "ju3eum4dekeich9ahM1te8waeGai0oog",
|
||||
"description": "Secret key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"schedule": {
|
||||
"default": "0 2 * * * *",
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"destinationPath": "s3://bucket/path/to/folder/",
|
||||
"enabled": false,
|
||||
"endpointURL": "http://minio-gateway-service:9000",
|
||||
"retentionPolicy": "30d",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * * *"
|
||||
},
|
||||
"required": [
|
||||
"destinationPath",
|
||||
"enabled",
|
||||
"endpointURL",
|
||||
"retentionPolicy",
|
||||
"s3AccessKey",
|
||||
"s3SecretKey",
|
||||
"schedule"
|
||||
],
|
||||
"properties": {
|
||||
"destinationPath": {
|
||||
"description": "Path to store the backup (i.e. s3://bucket/path/to/folder)",
|
||||
"type": "string",
|
||||
"default": "s3://bucket/path/to/folder/"
|
||||
},
|
||||
"bootstrap": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Restore database cluster from a backup",
|
||||
"type": "boolean"
|
||||
},
|
||||
"oldName": {
|
||||
"default": "",
|
||||
"description": "Name of database cluster before deleting",
|
||||
"type": "string"
|
||||
},
|
||||
"recoveryTime": {
|
||||
"default": "",
|
||||
"description": "Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"enabled": {
|
||||
"description": "Enable regular backups, default is `false`.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"external": {
|
||||
"default": false,
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean"
|
||||
"endpointURL": {
|
||||
"description": "S3 Endpoint used to upload data to the cloud",
|
||||
"type": "string",
|
||||
"default": "http://minio-gateway-service:9000"
|
||||
},
|
||||
"quorum": {
|
||||
"properties": {
|
||||
"maxSyncReplicas": {
|
||||
"default": 0,
|
||||
"description": "Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)",
|
||||
"type": "number"
|
||||
},
|
||||
"minSyncReplicas": {
|
||||
"default": 0,
|
||||
"description": "Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"retentionPolicy": {
|
||||
"description": "Retention policy",
|
||||
"type": "string",
|
||||
"default": "30d"
|
||||
},
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of replicas",
|
||||
"type": "number"
|
||||
"s3AccessKey": {
|
||||
"description": "Access key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-access-key\u003e"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"s3SecretKey": {
|
||||
"description": "Secret key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-secret-key\u003e"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "micro",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"default": "10Gi",
|
||||
"description": "Persistent Volume size",
|
||||
"type": "string"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
"schedule": {
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string",
|
||||
"default": "0 2 * * * *"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"bootstrap": {
|
||||
"description": "Bootstrap (recovery) configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"oldName": "",
|
||||
"recoveryTime": ""
|
||||
},
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Restore database cluster from a backup",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"oldName": {
|
||||
"description": "Name of database cluster before deleting",
|
||||
"type": "string"
|
||||
},
|
||||
"recoveryTime": {
|
||||
"description": "Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"quorum": {
|
||||
"description": "Configuration for the quorum-based synchronous replication",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"maxSyncReplicas": 0,
|
||||
"minSyncReplicas": 0
|
||||
},
|
||||
"required": [
|
||||
"maxSyncReplicas",
|
||||
"minSyncReplicas"
|
||||
],
|
||||
"properties": {
|
||||
"maxSyncReplicas": {
|
||||
"description": "Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)",
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
},
|
||||
"minSyncReplicas": {
|
||||
"description": "Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed",
|
||||
"type": "integer",
|
||||
"default": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
},
|
||||
"users": {
|
||||
"description": "Users configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"description": "Password for the user",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,35 @@
|
||||
## @section Common parameters
|
||||
##
|
||||
## @param replicas Number of replicas
|
||||
## @param replicas {int} Number of replicas
|
||||
replicas: 2
|
||||
## @param resources Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @param resources {*resources} Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources.cpu {*quantity} CPU available to each replica
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each replica
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
## @param resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
resourcesPreset: "micro"
|
||||
## @param size Persistent Volume size
|
||||
size: 10Gi
|
||||
## @param storageClass StorageClass used to store the data
|
||||
storageClass: ""
|
||||
## @param external Enable external access from outside the cluster
|
||||
external: false
|
||||
resources: {}
|
||||
|
||||
## @param resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "micro"
|
||||
## @param size {quantity} Persistent Volume Claim size, available for application data
|
||||
size: 10Gi
|
||||
## @param storageClass {string} StorageClass used to store the data
|
||||
storageClass: ""
|
||||
## @param external {bool} Enable external access from outside the cluster
|
||||
external: false
|
||||
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
## Configuration for the quorum-based synchronous replication
|
||||
## @param quorum.minSyncReplicas Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed
|
||||
## @param quorum.maxSyncReplicas Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)
|
||||
## @param quorum {quorum} Configuration for the quorum-based synchronous replication
|
||||
## @field quorum.minSyncReplicas {int} Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed
|
||||
## @field quorum.maxSyncReplicas {int} Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)
|
||||
quorum:
|
||||
minSyncReplicas: 0
|
||||
maxSyncReplicas: 0
|
||||
## @param users [object] Users configuration
|
||||
|
||||
## @param users {map[string]user} Users configuration
|
||||
## @field user.password {*string} Password for the user
|
||||
## Example:
|
||||
## users:
|
||||
## user1:
|
||||
@@ -36,30 +40,34 @@ quorum:
|
||||
users: {}
|
||||
|
||||
|
||||
|
||||
## @section Backup parameters
|
||||
##
|
||||
## @param backup.enabled Enable regular backups
|
||||
## @param backup.schedule Cron schedule for automated backups
|
||||
## @param backup.retentionPolicy Retention policy
|
||||
## @param backup.destinationPath Path to store the backup (i.e. s3://bucket/path/to/folder)
|
||||
## @param backup.endpointURL S3 Endpoint used to upload data to the cloud
|
||||
## @param backup.s3AccessKey Access key for S3, used for authentication
|
||||
## @param backup.s3SecretKey Secret key for S3, used for authentication
|
||||
|
||||
## @param backup {backup} Backup configuration
|
||||
## @field backup.enabled {bool} Enable regular backups, default is `false`.
|
||||
## @field backup.schedule {string} Cron schedule for automated backups
|
||||
## @field backup.retentionPolicy {string} Retention policy
|
||||
## @field backup.endpointURL {string} S3 Endpoint used to upload data to the cloud
|
||||
## @field backup.destinationPath {string} Path to store the backup (i.e. s3://bucket/path/to/folder)
|
||||
## @field backup.s3AccessKey {string} Access key for S3, used for authentication
|
||||
## @field backup.s3SecretKey {string} Secret key for S3, used for authentication
|
||||
backup:
|
||||
enabled: false
|
||||
retentionPolicy: 30d
|
||||
destinationPath: s3://bucket/path/to/folder/
|
||||
endpointURL: http://minio-gateway-service:9000
|
||||
schedule: "0 2 * * * *"
|
||||
s3AccessKey: oobaiRus9pah8PhohL1ThaeTa4UVa7gu
|
||||
s3SecretKey: ju3eum4dekeich9ahM1te8waeGai0oog
|
||||
retentionPolicy: 30d
|
||||
endpointURL: http://minio-gateway-service:9000
|
||||
destinationPath: s3://bucket/path/to/folder/
|
||||
s3AccessKey: "<your-access-key>"
|
||||
s3SecretKey: "<your-secret-key>"
|
||||
|
||||
|
||||
## @section Bootstrap (recovery) parameters
|
||||
##
|
||||
## @param bootstrap.enabled Restore database cluster from a backup
|
||||
## @param bootstrap.recoveryTime Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest
|
||||
## @param bootstrap.oldName Name of database cluster before deleting
|
||||
## @param bootstrap {bootstrap} Bootstrap (recovery) configuration
|
||||
## @field bootstrap.enabled {*bool} Restore database cluster from a backup
|
||||
## @field bootstrap.recoveryTime {*string} Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest.
|
||||
## @field bootstrap.oldName {*string} Name of database cluster before deleting
|
||||
##
|
||||
bootstrap:
|
||||
enabled: false
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.6.1
|
||||
version: 0.7.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
NGINX_CACHE_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml)
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
@@ -8,24 +7,17 @@ image: image-nginx
|
||||
|
||||
image-nginx:
|
||||
docker buildx build images/nginx-cache \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/nginx-cache:$(call settag,$(NGINX_CACHE_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/nginx-cache:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/nginx-cache.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/nginx-cache:$(call settag,$(NGINX_CACHE_TAG))@$$(yq e '."containerimage.digest"' images/nginx-cache.json -o json -r)" \
|
||||
> images/nginx-cache.tag
|
||||
rm -f images/nginx-cache.json
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
yq -i -o json --indent 4 '.properties.haproxy.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
yq -i -o json --indent 4 '.properties.nginx.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
|
||||
update:
|
||||
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/chrislim2888/IP2Location-C-Library | awk -F'[/^]' 'END{print $$3}') && \
|
||||
|
||||
@@ -60,33 +60,43 @@ The deployment architecture is illustrated in the diagram below:
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------- | ----------------------------------------------- | ------- |
|
||||
| `size` | Persistent Volume size | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `""` |
|
||||
| `external` | Enable external access from outside the cluster | `false` |
|
||||
| Name | Description | Type | Value |
|
||||
| -------------- | ------------------------------------------------------------ | ---------- | ------- |
|
||||
| `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` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------- | ----------------------- | ----- |
|
||||
| `endpoints` | Endpoints configuration | `[]` |
|
||||
| Name | Description | Type | Value |
|
||||
| ----------- | ----------------------------------------------- | ---------- | ----- |
|
||||
| `endpoints` | Endpoints configuration, as a list of <ip:port> | `[]string` | `[]` |
|
||||
|
||||
|
||||
### HAProxy parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------ |
|
||||
| `haproxy.replicas` | Number of HAProxy replicas | `2` |
|
||||
| `haproxy.resources` | Explicit CPU and memory configuration for each HAProxy replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `haproxy.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `nano` |
|
||||
| Name | Description | Type | Value |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------ |
|
||||
| `haproxy` | HAProxy configuration | `object` | `{}` |
|
||||
| `haproxy.replicas` | Number of HAProxy replicas | `int` | `2` |
|
||||
| `haproxy.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `haproxy.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `haproxy.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `haproxy.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
|
||||
### Nginx parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------ |
|
||||
| `nginx.replicas` | Number of Nginx replicas | `2` |
|
||||
| `nginx.resources` | Explicit CPU and memory configuration for each nginx replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `nginx.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `nano` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------ |
|
||||
| `nginx` | Nginx configuration | `object` | `{}` |
|
||||
| `nginx.replicas` | Number of Nginx replicas | `int` | `2` |
|
||||
| `nginx.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `nginx.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `nginx.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `nginx.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.6.1@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.7.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
|
||||
|
||||
@@ -10,7 +10,9 @@ spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
{{- if .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
app: {{ .Release.Name }}-haproxy
|
||||
|
||||
@@ -1,85 +1,172 @@
|
||||
{
|
||||
"properties": {
|
||||
"endpoints": {
|
||||
"default": [],
|
||||
"description": "Endpoints configuration",
|
||||
"items": {},
|
||||
"type": "array"
|
||||
},
|
||||
"external": {
|
||||
"default": false,
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean"
|
||||
},
|
||||
"haproxy": {
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of HAProxy replicas",
|
||||
"type": "number"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each HAProxy replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "nano",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"nginx": {
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of Nginx replicas",
|
||||
"type": "number"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each nginx replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "nano",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"size": {
|
||||
"default": "10Gi",
|
||||
"description": "Persistent Volume size",
|
||||
"type": "string"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
}
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"endpoints": {
|
||||
"description": "Endpoints configuration, as a list of \u003cip:port\u003e",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"external": {
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"haproxy": {
|
||||
"description": "HAProxy configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "nano"
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of HAProxy replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "nano",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"nginx": {
|
||||
"description": "Nginx configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "nano"
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of Nginx replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "nano",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
## @section Common parameters
|
||||
##
|
||||
## @param size Persistent Volume size
|
||||
## @param size {quantity} Persistent Volume Claim size, available for application data
|
||||
size: 10Gi
|
||||
## @param storageClass StorageClass used to store the data
|
||||
## @param storageClass {string} StorageClass used to store the data
|
||||
storageClass: ""
|
||||
## @param external Enable external access from outside the cluster
|
||||
## @param external {bool} Enable external access from outside the cluster
|
||||
external: false
|
||||
## @section Application-specific parameters
|
||||
|
||||
## @param endpoints Endpoints configuration
|
||||
## @param endpoints {[]string} Endpoints configuration, as a list of <ip:port>
|
||||
## Example:
|
||||
## endpoints:
|
||||
## - 10.100.3.1:80
|
||||
@@ -20,28 +20,34 @@ external: false
|
||||
##
|
||||
endpoints: []
|
||||
|
||||
|
||||
## @section HAProxy parameters
|
||||
##
|
||||
## @param haproxy {haproxy} HAProxy configuration
|
||||
haproxy:
|
||||
## @param haproxy.replicas Number of HAProxy replicas
|
||||
## @field haproxy.replicas {int} Number of HAProxy replicas
|
||||
replicas: 2
|
||||
## @param haproxy.resources Explicit CPU and memory configuration for each HAProxy replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field haproxy.resources {resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources.cpu {*quantity} CPU available to each replica
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each replica
|
||||
resources: {}
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
|
||||
## @param haproxy.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field haproxy.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "nano"
|
||||
|
||||
## @section Nginx parameters
|
||||
##
|
||||
## @param nginx {nginx} Nginx configuration
|
||||
nginx:
|
||||
## @param nginx.replicas Number of Nginx replicas
|
||||
## @field nginx.replicas {int} Number of Nginx replicas
|
||||
replicas: 2
|
||||
## @param nginx.resources Explicit CPU and memory configuration for each nginx replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @field nginx.resources {*resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
resources: {}
|
||||
|
||||
## @param nginx.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field nginx.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "nano"
|
||||
|
||||
@@ -2,6 +2,4 @@ include ../../../scripts/package.mk
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
yq -i -o json --indent 4 '.properties.kafka.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
yq -i -o json --indent 4 '.properties.zookeeper.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
|
||||
@@ -4,35 +4,49 @@
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ---------- | ----------------------------------------------- | ------- |
|
||||
| `external` | Enable external access from outside the cluster | `false` |
|
||||
| Name | Description | Type | Value |
|
||||
| ---------- | ----------------------------------------------- | ------ | ------- |
|
||||
| `external` | Enable external access from outside the cluster | `bool` | `false` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------- | ---------------------------------- | ----- |
|
||||
| `topics` | Topics configuration (see example) | `[]` |
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------- | -------------------- | ---------- | ----- |
|
||||
| `topics` | Topics configuration | `[]object` | `[]` |
|
||||
| `topics[i].name` | Topic name | `string` | `""` |
|
||||
| `topics[i].partitions` | Number of partitions | `int` | `0` |
|
||||
| `topics[i].replicas` | Number of replicas | `int` | `0` |
|
||||
| `topics[i].config` | Topic configuration | `object` | `{}` |
|
||||
|
||||
|
||||
### Kafka configuration
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `kafka.replicas` | Number of Kafka replicas | `3` |
|
||||
| `kafka.resources` | Explicit CPU and memory configuration for each Kafka replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `kafka.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `small` |
|
||||
| `kafka.size` | Persistent Volume size for Kafka | `10Gi` |
|
||||
| `kafka.storageClass` | StorageClass used to store the Kafka data | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `kafka` | Kafka configuration | `object` | `{}` |
|
||||
| `kafka.replicas` | Number of Kafka replicas | `int` | `3` |
|
||||
| `kafka.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `kafka.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `kafka.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `kafka.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `kafka.size` | Persistent Volume size for Kafka | `quantity` | `10Gi` |
|
||||
| `kafka.storageClass` | StorageClass used to store the Kafka data | `string` | `""` |
|
||||
|
||||
|
||||
### Zookeeper configuration
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `zookeeper.replicas` | Number of ZooKeeper replicas | `3` |
|
||||
| `zookeeper.resources` | Explicit CPU and memory configuration for each Zookeeper replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `zookeeper.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `small` |
|
||||
| `zookeeper.size` | Persistent Volume size for ZooKeeper | `5Gi` |
|
||||
| `zookeeper.storageClass` | StorageClass used to store the ZooKeeper data | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `zookeeper` | Zookeeper configuration | `object` | `{}` |
|
||||
| `zookeeper.replicas` | Number of ZooKeeper replicas | `int` | `3` |
|
||||
| `zookeeper.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
|
||||
| `zookeeper.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `zookeeper.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `zookeeper.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `zookeeper.size` | Persistent Volume size for ZooKeeper | `quantity` | `5Gi` |
|
||||
| `zookeeper.storageClass` | StorageClass used to store the ZooKeeper data | `string` | `""` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -1,95 +1,222 @@
|
||||
{
|
||||
"properties": {
|
||||
"external": {
|
||||
"default": false,
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean"
|
||||
},
|
||||
"kafka": {
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"default": 3,
|
||||
"description": "Number of Kafka replicas",
|
||||
"type": "number"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each Kafka replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "small",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"default": "10Gi",
|
||||
"description": "Persistent Volume size for Kafka",
|
||||
"type": "string"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the Kafka data",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"topics": {
|
||||
"default": [],
|
||||
"description": "Topics configuration (see example)",
|
||||
"items": {},
|
||||
"type": "array"
|
||||
},
|
||||
"zookeeper": {
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"default": 3,
|
||||
"description": "Number of ZooKeeper replicas",
|
||||
"type": "number"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each Zookeeper replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "small",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"default": "5Gi",
|
||||
"description": "Persistent Volume size for ZooKeeper",
|
||||
"type": "string"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the ZooKeeper data",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"external": {
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"kafka": {
|
||||
"description": "Kafka configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset",
|
||||
"size",
|
||||
"storageClass"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of Kafka replicas",
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume size for Kafka",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the Kafka data",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"topics": {
|
||||
"description": "Topics configuration",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"config",
|
||||
"name",
|
||||
"partitions",
|
||||
"replicas"
|
||||
],
|
||||
"properties": {
|
||||
"config": {
|
||||
"description": "Topic configuration",
|
||||
"type": "object",
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
},
|
||||
"name": {
|
||||
"description": "Topic name",
|
||||
"type": "string"
|
||||
},
|
||||
"partitions": {
|
||||
"description": "Number of partitions",
|
||||
"type": "integer"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of replicas",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zookeeper": {
|
||||
"description": "Zookeeper configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "5Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"replicas",
|
||||
"resourcesPreset",
|
||||
"size",
|
||||
"storageClass"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of ZooKeeper replicas",
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume size for ZooKeeper",
|
||||
"default": "5Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the ZooKeeper data",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
## @section Common parameters
|
||||
##
|
||||
## @param external Enable external access from outside the cluster
|
||||
## @param external {bool} Enable external access from outside the cluster
|
||||
external: false
|
||||
|
||||
|
||||
## @section Application-specific parameters
|
||||
##
|
||||
## @param topics Topics configuration (see example)
|
||||
## @param topics {[]topic} Topics configuration
|
||||
## @field topic {topic} Topic
|
||||
## @field topic.name {string} Topic name
|
||||
## @field topic.partitions {int} Number of partitions
|
||||
## @field topic.replicas {int} Number of replicas
|
||||
## @field topic.config {object} Topic configuration
|
||||
## Example:
|
||||
## topics:
|
||||
## - name: Results
|
||||
@@ -27,38 +32,41 @@ topics: []
|
||||
|
||||
## @section Kafka configuration
|
||||
##
|
||||
## @param kafka {kafka} Kafka configuration
|
||||
kafka:
|
||||
## @param kafka.replicas Number of Kafka replicas
|
||||
## @field kafka.replicas {int} Number of Kafka replicas
|
||||
replicas: 3
|
||||
## @param kafka.resources Explicit CPU and memory configuration for each Kafka replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @field kafka.resources {*resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources.cpu {*quantity} CPU available to each replica
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each replica
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
## @param kafka.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
resources: {}
|
||||
## @field kafka.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "small"
|
||||
## @param kafka.size Persistent Volume size for Kafka
|
||||
## @field kafka.size {quantity} Persistent Volume size for Kafka
|
||||
size: 10Gi
|
||||
## @param kafka.storageClass StorageClass used to store the Kafka data
|
||||
## @field kafka.storageClass {string} StorageClass used to store the Kafka data
|
||||
storageClass: ""
|
||||
|
||||
|
||||
## @section Zookeeper configuration
|
||||
##
|
||||
## @param zookeeper {zookeeper} Zookeeper configuration
|
||||
zookeeper:
|
||||
## @param zookeeper.replicas Number of ZooKeeper replicas
|
||||
## @field zookeeper.replicas {int} Number of ZooKeeper replicas
|
||||
replicas: 3
|
||||
## @param zookeeper.resources Explicit CPU and memory configuration for each Zookeeper replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @field zookeeper.resources {*resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
# resources:
|
||||
# cpu: 4000m
|
||||
# memory: 4Gi
|
||||
## @param zookeeper.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
resources: {}
|
||||
|
||||
## @field zookeeper.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "small"
|
||||
## @param zookeeper.size Persistent Volume size for ZooKeeper
|
||||
## @field zookeeper.size {quantity} Persistent Volume size for ZooKeeper
|
||||
size: 5Gi
|
||||
## @param zookeeper.storageClass StorageClass used to store the ZooKeeper data
|
||||
## @field zookeeper.storageClass {string} StorageClass used to store the ZooKeeper data
|
||||
storageClass: ""
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.26.0
|
||||
version: 0.29.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,69 +1,48 @@
|
||||
KUBERNETES_VERSION = v1.32
|
||||
KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml)
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
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
|
||||
yq -o json -i '.properties.addons.properties.ingressNginx.properties.exposeMethod.enum = ["Proxied","LoadBalancer"]' values.schema.json
|
||||
yq -o json -i '.properties.controlPlane.properties.apiServer.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
yq -o json -i '.properties.controlPlane.properties.controllerManager.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
yq -o json -i '.properties.controlPlane.properties.scheduler.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
yq -o json -i '.properties.controlPlane.properties.konnectivity.properties.server.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
|
||||
image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-csi-driver image-cluster-autoscaler
|
||||
|
||||
image-ubuntu-container-disk:
|
||||
docker buildx build images/ubuntu-container-disk \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--build-arg KUBERNETES_VERSION=${KUBERNETES_VERSION} \
|
||||
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)) \
|
||||
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/ubuntu-container-disk.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk.json -o json -r)" \
|
||||
> images/ubuntu-container-disk.tag
|
||||
rm -f images/ubuntu-container-disk.json
|
||||
|
||||
image-kubevirt-cloud-provider:
|
||||
docker buildx build images/kubevirt-cloud-provider \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/kubevirt-cloud-provider:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/kubevirt-cloud-provider.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/kubevirt-cloud-provider.json -o json -r)" \
|
||||
> images/kubevirt-cloud-provider.tag
|
||||
rm -f images/kubevirt-cloud-provider.json
|
||||
|
||||
image-kubevirt-csi-driver:
|
||||
docker buildx build images/kubevirt-csi-driver \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/kubevirt-csi-driver:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/kubevirt-csi-driver.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/kubevirt-csi-driver.json -o json -r)" \
|
||||
> images/kubevirt-csi-driver.tag
|
||||
IMAGE=$$(cat images/kubevirt-csi-driver.tag) \
|
||||
@@ -73,17 +52,12 @@ image-kubevirt-csi-driver:
|
||||
|
||||
image-cluster-autoscaler:
|
||||
docker buildx build images/cluster-autoscaler \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/cluster-autoscaler:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/cluster-autoscaler.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/cluster-autoscaler.json -o json -r)" \
|
||||
> images/cluster-autoscaler.tag
|
||||
rm -f images/cluster-autoscaler.json
|
||||
|
||||
@@ -84,53 +84,93 @@ See the reference for components utilized in this service:
|
||||
|
||||
### Common Parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------- | ------------------------------------- | ------------ |
|
||||
| `storageClass` | StorageClass used to store user data. | `replicated` |
|
||||
| Name | Description | Type | Value |
|
||||
| -------------- | ----------------------------------- | -------- | ------------ |
|
||||
| `storageClass` | StorageClass used to store the data | `string` | `replicated` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------ | ----------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `v1.32` |
|
||||
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `""` |
|
||||
| `nodeGroups` | Worker nodes configuration (see example) | `{}` |
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.33` |
|
||||
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `string` | `""` |
|
||||
| `nodeGroups` | Worker nodes configuration | `map[string]object` | `{...}` |
|
||||
| `nodeGroups[name].minReplicas` | Minimum amount of replicas | `int` | `0` |
|
||||
| `nodeGroups[name].maxReplicas` | Maximum amount of replicas | `int` | `0` |
|
||||
| `nodeGroups[name].instanceType` | Virtual machine instance type | `string` | `""` |
|
||||
| `nodeGroups[name].ephemeralStorage` | Ephemeral storage size | `quantity` | `""` |
|
||||
| `nodeGroups[name].roles` | List of node's roles | `[]string` | `[]` |
|
||||
| `nodeGroups[name].resources` | Resources available to each worker node | `object` | `{}` |
|
||||
| `nodeGroups[name].resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `nodeGroups[name].gpus` | List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM) | `[]object` | `[]` |
|
||||
| `nodeGroups[name].gpus.name` | Name of GPU, such as "nvidia.com/AD102GL_L40S" | `string` | `""` |
|
||||
|
||||
|
||||
### Cluster Addons
|
||||
|
||||
| Name | Description | Value |
|
||||
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
|
||||
| `addons.certManager.enabled` | Enable cert-manager, which automatically creates and manages SSL/TLS certificates. | `false` |
|
||||
| `addons.certManager.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.cilium.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.gatewayAPI.enabled` | Enable the Gateway API | `false` |
|
||||
| `addons.ingressNginx.enabled` | Enable the Ingress-NGINX controller (requires nodes labeled with the 'ingress-nginx' role). | `false` |
|
||||
| `addons.ingressNginx.exposeMethod` | Method to expose the Ingress-NGINX controller. (allowed values: Proxied, LoadBalancer) | `Proxied` |
|
||||
| `addons.ingressNginx.hosts` | List of domain names that the parent cluster should route to this tenant cluster. Taken into account only when `exposeMethod` is set to `Proxied`. | `[]` |
|
||||
| `addons.ingressNginx.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.gpuOperator.enabled` | Enable the GPU-operator | `false` |
|
||||
| `addons.gpuOperator.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.fluxcd.enabled` | Enable FluxCD | `false` |
|
||||
| `addons.fluxcd.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.monitoringAgents.enabled` | Enable monitoring agents (Fluent Bit and VMAgents) to send logs and metrics. If tenant monitoring is enabled, data is sent to tenant storage; otherwise, it goes to root storage. | `false` |
|
||||
| `addons.monitoringAgents.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.verticalPodAutoscaler.valuesOverride` | Custom values to override | `{}` |
|
||||
| `addons.velero.enabled` | Enable velero for backup and restore k8s cluster. | `false` |
|
||||
| `addons.velero.valuesOverride` | Custom values to override | `{}` |
|
||||
| Name | Description | Type | Value |
|
||||
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | --------- |
|
||||
| `addons` | Cluster addons configuration | `object` | `{}` |
|
||||
| `addons.certManager` | Cert-manager: automatically creates and manages SSL/TLS certificate | `object` | `{}` |
|
||||
| `addons.certManager.enabled` | Enable cert-manager, which automatically creates and manages SSL/TLS certificates. | `bool` | `false` |
|
||||
| `addons.certManager.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.cilium` | Cilium CNI plugin | `object` | `{}` |
|
||||
| `addons.cilium.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.gatewayAPI` | Gateway API | `object` | `{}` |
|
||||
| `addons.gatewayAPI.enabled` | Enable the Gateway API | `bool` | `false` |
|
||||
| `addons.ingressNginx` | Ingress-NGINX Controller | `object` | `{}` |
|
||||
| `addons.ingressNginx.enabled` | Enable the Ingress-NGINX controller (requires nodes labeled with the 'ingress-nginx' role). | `bool` | `false` |
|
||||
| `addons.ingressNginx.exposeMethod` | Method to expose the Ingress-NGINX controller. Allowed values: `Proxied`, `LoadBalancer`. | `string` | `Proxied` |
|
||||
| `addons.ingressNginx.hosts` | List of domain names that the parent cluster should route to this tenant cluster. Taken into account only when `exposeMethod` is set to `Proxied`. | `[]string` | `[]` |
|
||||
| `addons.ingressNginx.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.gpuOperator` | GPU-operator: NVIDIA GPU Operator | `object` | `{}` |
|
||||
| `addons.gpuOperator.enabled` | Enable the GPU-operator | `bool` | `false` |
|
||||
| `addons.gpuOperator.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.fluxcd` | Flux CD | `object` | `{}` |
|
||||
| `addons.fluxcd.enabled` | Enable FluxCD | `bool` | `false` |
|
||||
| `addons.fluxcd.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.monitoringAgents` | MonitoringAgents | `object` | `{}` |
|
||||
| `addons.monitoringAgents.enabled` | Enable monitoring agents (Fluent Bit and VMAgents) to send logs and metrics. If tenant monitoring is enabled, data is sent to tenant storage; otherwise, it goes to root storage. | `bool` | `false` |
|
||||
| `addons.monitoringAgents.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.verticalPodAutoscaler` | VerticalPodAutoscaler | `object` | `{}` |
|
||||
| `addons.verticalPodAutoscaler.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.velero` | Velero | `object` | `{}` |
|
||||
| `addons.velero.enabled` | Enable Velero for backup and recovery of a tenant Kubernetes cluster. | `bool` | `false` |
|
||||
| `addons.velero.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.coredns` | Coredns | `object` | `{}` |
|
||||
| `addons.coredns.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
|
||||
|
||||
### Kubernetes Control Plane Configuration
|
||||
|
||||
| Name | Description | Value |
|
||||
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- |
|
||||
| `controlPlane.replicas` | Number of replicas for Kubernetes control-plane components. | `2` |
|
||||
| `controlPlane.apiServer.resources` | Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `controlPlane.apiServer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `medium` |
|
||||
| `controlPlane.controllerManager.resources` | Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `controlPlane.controllerManager.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `micro` |
|
||||
| `controlPlane.scheduler.resources` | Explicit CPU and memory configuration for the Scheduler. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `controlPlane.scheduler.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `micro` |
|
||||
| `controlPlane.konnectivity.server.resources` | Explicit CPU and memory configuration for Konnectivity. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `controlPlane.konnectivity.server.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `micro` |
|
||||
| Name | Description | Type | Value |
|
||||
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------- |
|
||||
| `controlPlane` | Control Plane Configuration | `object` | `{}` |
|
||||
| `controlPlane.replicas` | Number of replicas for Kubernetes control plane components. | `int` | `2` |
|
||||
| `controlPlane.apiServer` | Control plane API server configuration. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources` | Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `controlPlane.apiServer.resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.apiServer.resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.apiServer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `medium` |
|
||||
| `controlPlane.controllerManager` | Controller Manager configuration. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources` | Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `controlPlane.controllerManager.resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.controllerManager.resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.controllerManager.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
| `controlPlane.scheduler` | Scheduler configuration. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources` | Explicit CPU and memory configuration for the Scheduler. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `controlPlane.scheduler.resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.scheduler.resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.scheduler.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
| `controlPlane.konnectivity` | Konnectivity configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server` | Konnectivity server configuration. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources` | Explicit CPU and memory configuration for Konnectivity. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `controlPlane.konnectivity.server.resources.cpu` | CPU available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.konnectivity.server.resources.memory` | Memory (RAM) available to each worker node | `*quantity` | `null` |
|
||||
| `controlPlane.konnectivity.server.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.26.0@sha256:3a8170433e1632e5cc2b6d9db34d0605e8e6c63c158282c38450415e700e932e
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.26.0@sha256:49843a0b670eab061627e48df338b2b8bc9f577dc2cfd4c2ed4071e02e64b424
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.29.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
|
||||
@@ -21,6 +21,6 @@ RUN go mod vendor
|
||||
|
||||
RUN CGO_ENABLED=0 go build -mod=vendor -ldflags="-s -w" -o bin/kubevirt-cloud-controller-manager ./cmd/kubevirt-cloud-controller-manager
|
||||
|
||||
FROM registry.access.redhat.com/ubi9/ubi-micro
|
||||
FROM scratch
|
||||
COPY --from=builder /go/src/kubevirt.io/cloud-provider-kubevirt/bin/kubevirt-cloud-controller-manager /bin/kubevirt-cloud-controller-manager
|
||||
ENTRYPOINT [ "/bin/kubevirt-cloud-controller-manager" ]
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.26.0@sha256:445c2727b04ac68595b43c988ff17b3d69a7b22b0644fde3b10c65b47a7bc036
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.0@sha256:3a3bc912f70ccba1e9f92a0754179dbdc4c01f24073467b6d1406c77da794863
|
||||
|
||||
@@ -127,9 +127,6 @@ spec:
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.controlPlane.scheduler.resourcesPreset .Values.controlPlane.scheduler.resources $) | nindent 6 }}
|
||||
dataStoreName: "{{ $etcd }}"
|
||||
addons:
|
||||
coreDNS:
|
||||
dnsServiceIPs:
|
||||
- 10.95.0.10
|
||||
konnectivity:
|
||||
server:
|
||||
port: 8132
|
||||
|
||||
47
packages/apps/kubernetes/templates/helmreleases/coredns.yaml
Normal file
47
packages/apps/kubernetes/templates/helmreleases/coredns.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
{{- define "cozystack.defaultCoreDNSValues" -}}
|
||||
coredns:
|
||||
service:
|
||||
clusterIP: "10.95.0.10"
|
||||
{{- end }}
|
||||
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-coredns
|
||||
labels:
|
||||
cozystack.io/repository: system
|
||||
cozystack.io/target-cluster-name: {{ .Release.Name }}
|
||||
spec:
|
||||
interval: 5m
|
||||
releaseName: coredns
|
||||
chart:
|
||||
spec:
|
||||
chart: cozy-coredns
|
||||
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: kube-system
|
||||
storageNamespace: kube-system
|
||||
install:
|
||||
createNamespace: true
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
values:
|
||||
{{- toYaml (deepCopy .Values.addons.coredns.valuesOverride | mergeOverwrite (fromYaml (include "cozystack.defaultCoreDNSValues" .))) | nindent 4 }}
|
||||
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 }}
|
||||
@@ -35,6 +35,8 @@ spec:
|
||||
storageClass: "{{ . }}"
|
||||
{{- end }}
|
||||
dependsOn:
|
||||
- name: {{ .Release.Name }}-vsnap-crd
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
@@ -41,6 +41,7 @@ spec:
|
||||
{{ .Release.Name }}-fluxcd
|
||||
{{ .Release.Name }}-gpu-operator
|
||||
{{ .Release.Name }}-velero
|
||||
{{ .Release.Name }}-coredns
|
||||
-p '{"spec": {"suspend": true}}'
|
||||
--type=merge --field-manager=flux-client-side-apply || true
|
||||
---
|
||||
@@ -81,6 +82,7 @@ rules:
|
||||
- {{ .Release.Name }}-fluxcd
|
||||
- {{ .Release.Name }}-gpu-operator
|
||||
- {{ .Release.Name }}-velero
|
||||
- {{ .Release.Name }}-coredns
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
|
||||
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
|
||||
{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }}
|
||||
vpaForVPA: false
|
||||
vertical-pod-autoscaler:
|
||||
recommender:
|
||||
extraArgs:
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-volumesnapshot-crd-for-tenant-k8s
|
||||
name: {{ .Release.Name }}-vsnap-crd
|
||||
labels:
|
||||
cozystack.io/repository: system
|
||||
cozystack.io/target-cluster-name: {{ .Release.Name }}
|
||||
spec:
|
||||
interval: 5m
|
||||
releaseName: volumesnapshot-crd-for-tenant-k8s
|
||||
releaseName: vsnap-crd
|
||||
chart:
|
||||
spec:
|
||||
chart: cozy-volumesnapshot-crd-for-tenant-k8s
|
||||
chart: cozy-vsnap-crd
|
||||
reconcileStrategy: Revision
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
@@ -21,8 +21,8 @@ spec:
|
||||
secretRef:
|
||||
name: {{ .Release.Name }}-admin-kubeconfig
|
||||
key: super-admin.svc
|
||||
targetNamespace: cozy-volumesnapshot-crd-for-tenant-k8s
|
||||
storageNamespace: cozy-volumesnapshot-crd-for-tenant-k8s
|
||||
targetNamespace: cozy-vsnap-crd
|
||||
storageNamespace: cozy-vsnap-crd
|
||||
install:
|
||||
createNamespace: true
|
||||
remediation:
|
||||
|
||||
@@ -1,182 +1,387 @@
|
||||
{
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"addons": {
|
||||
"properties": {
|
||||
"description": "Cluster addons configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"certManager": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable cert-manager, which automatically creates and manages SSL/TLS certificates.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"cilium": {
|
||||
"properties": {
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"coredns": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"fluxcd": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable FluxCD",
|
||||
"type": "boolean"
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"gatewayAPI": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable the Gateway API",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"enabled": false
|
||||
},
|
||||
"gpuOperator": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable the GPU-operator",
|
||||
"type": "boolean"
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"ingressNginx": {
|
||||
"enabled": false,
|
||||
"exposeMethod": "Proxied",
|
||||
"hosts": {},
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"monitoringAgents": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"velero": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"verticalPodAutoscaler": {
|
||||
"valuesOverride": {}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"certManager",
|
||||
"cilium",
|
||||
"coredns",
|
||||
"fluxcd",
|
||||
"gatewayAPI",
|
||||
"gpuOperator",
|
||||
"ingressNginx",
|
||||
"monitoringAgents",
|
||||
"velero",
|
||||
"verticalPodAutoscaler"
|
||||
],
|
||||
"properties": {
|
||||
"certManager": {
|
||||
"description": "Cert-manager: automatically creates and manages SSL/TLS certificate",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable cert-manager, which automatically creates and manages SSL/TLS certificates.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"cilium": {
|
||||
"description": "Cilium CNI plugin",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"coredns": {
|
||||
"description": "Coredns",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"fluxcd": {
|
||||
"description": "Flux CD",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable FluxCD",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"gatewayAPI": {
|
||||
"description": "Gateway API",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false
|
||||
},
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the Gateway API",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"gpuOperator": {
|
||||
"description": "GPU-operator: NVIDIA GPU Operator",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"description": "Enable the GPU-operator",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ingressNginx": {
|
||||
"description": "Ingress-NGINX Controller",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"exposeMethod": "Proxied",
|
||||
"hosts": {},
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"exposeMethod",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable the Ingress-NGINX controller (requires nodes labeled with the 'ingress-nginx' role).",
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"exposeMethod": {
|
||||
"default": "Proxied",
|
||||
"description": "Method to expose the Ingress-NGINX controller. (allowed values: Proxied, LoadBalancer)",
|
||||
"description": "Method to expose the Ingress-NGINX controller. Allowed values: `Proxied`, `LoadBalancer`.",
|
||||
"type": "string",
|
||||
"default": "Proxied",
|
||||
"enum": [
|
||||
"Proxied",
|
||||
"LoadBalancer"
|
||||
]
|
||||
},
|
||||
"hosts": {
|
||||
"default": [],
|
||||
"description": "List of domain names that the parent cluster should route to this tenant cluster. Taken into account only when `exposeMethod` is set to `Proxied`.",
|
||||
"items": {},
|
||||
"type": "array"
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"monitoringAgents": {
|
||||
"description": "MonitoringAgents",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable monitoring agents (Fluent Bit and VMAgents) to send logs and metrics. If tenant monitoring is enabled, data is sent to tenant storage; otherwise, it goes to root storage.",
|
||||
"type": "boolean"
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"velero": {
|
||||
"description": "Velero",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable velero for backup and restore k8s cluster.",
|
||||
"type": "boolean"
|
||||
"description": "Enable Velero for backup and recovery of a tenant Kubernetes cluster.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"verticalPodAutoscaler": {
|
||||
"description": "VerticalPodAutoscaler",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"valuesOverride": {
|
||||
"default": {},
|
||||
"description": "Custom values to override",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"controlPlane": {
|
||||
"properties": {
|
||||
"description": "Control Plane Configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"apiServer": {
|
||||
"properties": {
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "medium",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"resources": {},
|
||||
"resourcesPreset": "medium"
|
||||
},
|
||||
"controllerManager": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"konnectivity": {
|
||||
"server": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"replicas": 2,
|
||||
"scheduler": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiServer",
|
||||
"controllerManager",
|
||||
"konnectivity",
|
||||
"replicas",
|
||||
"scheduler"
|
||||
],
|
||||
"properties": {
|
||||
"apiServer": {
|
||||
"description": "Control plane API server configuration.",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "medium"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "micro",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "medium",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
@@ -187,22 +392,131 @@
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"controllerManager": {
|
||||
"description": "Controller Manager configuration.",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"type": "object"
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"konnectivity": {
|
||||
"description": "Konnectivity configuration.",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"server": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"server"
|
||||
],
|
||||
"properties": {
|
||||
"server": {
|
||||
"description": "Konnectivity server configuration.",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for Konnectivity. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "micro",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
@@ -213,28 +527,64 @@
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of replicas for Kubernetes control-plane components.",
|
||||
"type": "number"
|
||||
"description": "Number of replicas for Kubernetes control plane components.",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"scheduler": {
|
||||
"description": "Scheduler configuration.",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"resources": {},
|
||||
"resourcesPreset": "micro"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for the Scheduler. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "micro",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
@@ -245,26 +595,132 @@
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"host": {
|
||||
"default": "",
|
||||
"description": "Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.",
|
||||
"type": "string"
|
||||
},
|
||||
"nodeGroups": {
|
||||
"description": "Worker nodes configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"md0": {
|
||||
"ephemeralStorage": "20Gi",
|
||||
"gpus": {},
|
||||
"instanceType": "u1.medium",
|
||||
"maxReplicas": 10,
|
||||
"minReplicas": 0,
|
||||
"resources": {},
|
||||
"roles": [
|
||||
"ingress-nginx"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ephemeralStorage",
|
||||
"instanceType",
|
||||
"maxReplicas",
|
||||
"minReplicas",
|
||||
"resources"
|
||||
],
|
||||
"properties": {
|
||||
"ephemeralStorage": {
|
||||
"description": "Ephemeral storage size",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"gpus": {
|
||||
"description": "List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name of GPU, such as \"nvidia.com/AD102GL_L40S\"",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"instanceType": {
|
||||
"description": "Virtual machine instance type",
|
||||
"type": "string"
|
||||
},
|
||||
"maxReplicas": {
|
||||
"description": "Maximum amount of replicas",
|
||||
"type": "integer"
|
||||
},
|
||||
"minReplicas": {
|
||||
"description": "Minimum amount of replicas",
|
||||
"type": "integer"
|
||||
},
|
||||
"resources": {
|
||||
"description": "Resources available to each worker node",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each worker node",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"roles": {
|
||||
"description": "List of node's roles",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "replicated",
|
||||
"description": "StorageClass used to store user data.",
|
||||
"type": "string"
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string",
|
||||
"default": "replicated"
|
||||
},
|
||||
"version": {
|
||||
"default": "v1.32",
|
||||
"description": "Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.",
|
||||
"type": "string",
|
||||
"default": "v1.33",
|
||||
"enum": [
|
||||
"v1.28",
|
||||
"v1.29",
|
||||
@@ -274,7 +730,5 @@
|
||||
"v1.33"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
## @section Common Parameters
|
||||
|
||||
## @param storageClass StorageClass used to store user data.
|
||||
## @param storageClass {string} StorageClass used to store the data
|
||||
storageClass: replicated
|
||||
|
||||
## @section Application-specific parameters
|
||||
## @param version Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.
|
||||
version: "v1.32"
|
||||
## @param host Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
|
||||
## @param version {string} Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.
|
||||
version: "v1.33"
|
||||
## @param host {string} Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
|
||||
host: ""
|
||||
## @param nodeGroups [object] Worker nodes configuration (see example)
|
||||
|
||||
## @param nodeGroups {map[string]node} Worker nodes configuration
|
||||
## @field node {node} Node configuration
|
||||
## @field node.minReplicas {int} Minimum amount of replicas
|
||||
## @field node.maxReplicas {int} Maximum amount of replicas
|
||||
## @field node.instanceType {string} Virtual machine instance type
|
||||
## @field node.ephemeralStorage {quantity} Ephemeral storage size
|
||||
## @field node.roles {[]string} List of node's roles
|
||||
## @field node.resources {resources} Resources available to each worker node
|
||||
## @field resources.cpu {*quantity} CPU available to each worker node
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each worker node
|
||||
## @field node.gpus {[]gpu} List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)
|
||||
## @field gpu.name {string} Name of GPU, such as "nvidia.com/AD102GL_L40S"
|
||||
##
|
||||
nodeGroups:
|
||||
md0:
|
||||
@@ -18,11 +30,7 @@ nodeGroups:
|
||||
ephemeralStorage: 20Gi
|
||||
roles:
|
||||
- ingress-nginx
|
||||
|
||||
resources:
|
||||
cpu: ""
|
||||
memory: ""
|
||||
|
||||
resources: {}
|
||||
## List of GPUs to attach (WARN: NVIDIA driver requires at least 4 GiB of RAM)
|
||||
## e.g:
|
||||
## instanceType: "u1.xlarge"
|
||||
@@ -33,117 +41,131 @@ nodeGroups:
|
||||
|
||||
## @section Cluster Addons
|
||||
##
|
||||
## @param addons {addons} Cluster addons configuration
|
||||
addons:
|
||||
## Cert-manager: automatically creates and manages SSL/TLS certificate
|
||||
## @field addons.certManager {certManager} Cert-manager: automatically creates and manages SSL/TLS certificate
|
||||
##
|
||||
certManager:
|
||||
## @param addons.certManager.enabled Enable cert-manager, which automatically creates and manages SSL/TLS certificates.
|
||||
## @field certManager.enabled {bool} Enable cert-manager, which automatically creates and manages SSL/TLS certificates.
|
||||
enabled: false
|
||||
## @param addons.certManager.valuesOverride Custom values to override
|
||||
## @field certManager.valuesOverride {object} Custom values to override
|
||||
valuesOverride: {}
|
||||
|
||||
## Cilium CNI plugin
|
||||
## @field addons.cilium {cilium} Cilium CNI plugin
|
||||
##
|
||||
cilium:
|
||||
## @param addons.cilium.valuesOverride Custom values to override
|
||||
## @field cilium.valuesOverride {object} Custom values to override
|
||||
valuesOverride: {}
|
||||
|
||||
## Gateway API
|
||||
## @field addons.gatewayAPI {gatewayAPI} Gateway API
|
||||
##
|
||||
gatewayAPI:
|
||||
## @param addons.gatewayAPI.enabled Enable the Gateway API
|
||||
## @field gatewayAPI.enabled {bool} Enable the Gateway API
|
||||
enabled: false
|
||||
|
||||
## Ingress-NGINX Controller
|
||||
## @field addons.ingressNginx {ingressNginx} Ingress-NGINX Controller
|
||||
##
|
||||
ingressNginx:
|
||||
## @param addons.ingressNginx.enabled Enable the Ingress-NGINX controller (requires nodes labeled with the 'ingress-nginx' role).
|
||||
## @field ingressNginx.enabled {bool} Enable the Ingress-NGINX controller (requires nodes labeled with the 'ingress-nginx' role).
|
||||
enabled: false
|
||||
## @param addons.ingressNginx.exposeMethod Method to expose the Ingress-NGINX controller. (allowed values: Proxied, LoadBalancer)
|
||||
## @field ingressNginx.exposeMethod {string enum:"Proxied,LoadBalancer"} Method to expose the Ingress-NGINX controller. Allowed values: `Proxied`, `LoadBalancer`.
|
||||
exposeMethod: Proxied
|
||||
## @param addons.ingressNginx.hosts List of domain names that the parent cluster should route to this tenant cluster. Taken into account only when `exposeMethod` is set to `Proxied`.
|
||||
## @field ingressNginx.hosts {[]string} List of domain names that the parent cluster should route to this tenant cluster. Taken into account only when `exposeMethod` is set to `Proxied`.
|
||||
## e.g:
|
||||
## hosts:
|
||||
## - example.org
|
||||
## - foo.example.net
|
||||
##
|
||||
hosts: []
|
||||
## @param addons.ingressNginx.valuesOverride Custom values to override
|
||||
## @field ingressNginx.valuesOverride {object} Custom values to override
|
||||
valuesOverride: {}
|
||||
|
||||
## GPU-operator: NVIDIA GPU Operator
|
||||
## @field addons.gpuOperator {gpuOperator} GPU-operator: NVIDIA GPU Operator
|
||||
##
|
||||
gpuOperator:
|
||||
## @param addons.gpuOperator.enabled Enable the GPU-operator
|
||||
## @param addons.gpuOperator.valuesOverride Custom values to override
|
||||
## @field gpuOperator.enabled {bool} Enable the GPU-operator
|
||||
## @field gpuOperator.valuesOverride {object} Custom values to override
|
||||
enabled: false
|
||||
valuesOverride: {}
|
||||
|
||||
## Flux CD
|
||||
## @field addons.fluxcd {fluxcd} Flux CD
|
||||
##
|
||||
fluxcd:
|
||||
## @param addons.fluxcd.enabled Enable FluxCD
|
||||
## @param addons.fluxcd.valuesOverride Custom values to override
|
||||
## @field fluxcd.enabled {bool} Enable FluxCD
|
||||
## @field fluxcd.valuesOverride {object} Custom values to override
|
||||
##
|
||||
enabled: false
|
||||
valuesOverride: {}
|
||||
|
||||
## MonitoringAgents
|
||||
## @field addons.monitoringAgents {monitoringAgents} MonitoringAgents
|
||||
##
|
||||
monitoringAgents:
|
||||
## @param addons.monitoringAgents.enabled Enable monitoring agents (Fluent Bit and VMAgents) to send logs and metrics. If tenant monitoring is enabled, data is sent to tenant storage; otherwise, it goes to root storage.
|
||||
## @param addons.monitoringAgents.valuesOverride Custom values to override
|
||||
## @field monitoringAgents.enabled {bool} Enable monitoring agents (Fluent Bit and VMAgents) to send logs and metrics. If tenant monitoring is enabled, data is sent to tenant storage; otherwise, it goes to root storage.
|
||||
## @field monitoringAgents.valuesOverride {object} Custom values to override
|
||||
##
|
||||
enabled: false
|
||||
valuesOverride: {}
|
||||
|
||||
## VerticalPodAutoscaler
|
||||
## @field addons.verticalPodAutoscaler {verticalPodAutoscaler} VerticalPodAutoscaler
|
||||
##
|
||||
verticalPodAutoscaler:
|
||||
## @param addons.verticalPodAutoscaler.valuesOverride Custom values to override
|
||||
## @field verticalPodAutoscaler.valuesOverride {object} Custom values to override
|
||||
##
|
||||
valuesOverride: {}
|
||||
|
||||
## Velero
|
||||
## @field addons.velero {velero} Velero
|
||||
##
|
||||
velero:
|
||||
## @param addons.velero.enabled Enable velero for backup and restore k8s cluster.
|
||||
## @param addons.velero.valuesOverride Custom values to override
|
||||
## @field velero.enabled {bool} Enable Velero for backup and recovery of a tenant Kubernetes cluster.
|
||||
## @field velero.valuesOverride {object} Custom values to override
|
||||
##
|
||||
enabled: false
|
||||
valuesOverride: {}
|
||||
|
||||
## @field addons.coredns {coredns} Coredns
|
||||
##
|
||||
coredns:
|
||||
## @field coredns.valuesOverride {object} Custom values to override
|
||||
##
|
||||
valuesOverride: {}
|
||||
|
||||
## @section Kubernetes Control Plane Configuration
|
||||
##
|
||||
## @param controlPlane {controlPlane} Control Plane Configuration
|
||||
controlPlane:
|
||||
## @param controlPlane.replicas Number of replicas for Kubernetes control-plane components.
|
||||
## @field controlPlane.replicas {int} Number of replicas for Kubernetes control plane components.
|
||||
replicas: 2
|
||||
## @field controlPlane.apiServer {apiServer} Control plane API server configuration.
|
||||
apiServer:
|
||||
## @param controlPlane.apiServer.resources Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
resources: {}
|
||||
## @param controlPlane.apiServer.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field apiServer.resources {resources} Explicit CPU and memory configuration for the API Server. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## e.g:
|
||||
## resources:
|
||||
## cpu: 4000m
|
||||
## memory: 4Gi
|
||||
##
|
||||
resources: {}
|
||||
## @field apiServer.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "medium"
|
||||
|
||||
## @field controlPlane.controllerManager {controllerManager} Controller Manager configuration.
|
||||
controllerManager:
|
||||
## @param controlPlane.controllerManager.resources Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @param controlPlane.controllerManager.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field controllerManager.resources {resources} Explicit CPU and memory configuration for the Controller Manager. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field controllerManager.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "micro"
|
||||
resources: {}
|
||||
|
||||
## @field controlPlane.scheduler {scheduler} Scheduler configuration.
|
||||
scheduler:
|
||||
## @param controlPlane.scheduler.resources Explicit CPU and memory configuration for the Scheduler. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @param controlPlane.scheduler.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field scheduler.resources {resources} Explicit CPU and memory configuration for the Scheduler. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field scheduler.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "micro"
|
||||
resources: {}
|
||||
|
||||
## @field controlPlane.konnectivity {konnectivity} Konnectivity configuration.
|
||||
konnectivity:
|
||||
## @field konnectivity.server {konnectivityServer} Konnectivity server configuration.
|
||||
server:
|
||||
## @param controlPlane.konnectivity.server.resources Explicit CPU and memory configuration for Konnectivity. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @param controlPlane.konnectivity.server.resourcesPreset Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.
|
||||
## @field konnectivityServer.resources {resources} Explicit CPU and memory configuration for Konnectivity. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field konnectivityServer.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "micro"
|
||||
resources: {}
|
||||
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.9.1
|
||||
version: 0.10.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
MARIADB_BACKUP_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml)
|
||||
PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"]
|
||||
|
||||
include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
readme-generator-for-helm -v values.yaml -s values.schema.json -r README.md
|
||||
yq -i -o json --indent 4 '.properties.resourcesPreset.enum = $(PRESET_ENUM)' values.schema.json
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
|
||||
|
||||
image:
|
||||
docker buildx build images/mariadb-backup \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/mariadb-backup:$(call settag,$(MARIADB_BACKUP_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/mariadb-backup:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/mariadb-backup.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/mariadb-backup:$(call settag,$(MARIADB_BACKUP_TAG))@$$(yq e '."containerimage.digest"' images/mariadb-backup.json -o json -r)" \
|
||||
> images/mariadb-backup.tag
|
||||
rm -f images/mariadb-backup.json
|
||||
|
||||
@@ -14,12 +14,12 @@ This managed service is controlled by mariadb-operator, ensuring efficient manag
|
||||
|
||||
### How to switch master/slave replica
|
||||
|
||||
```
|
||||
```bash
|
||||
kubectl edit mariadb <instnace>
|
||||
```
|
||||
update:
|
||||
|
||||
```
|
||||
```bash
|
||||
spec:
|
||||
replication:
|
||||
primary:
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
|
||||
check status:
|
||||
|
||||
```
|
||||
```bash
|
||||
NAME READY STATUS PRIMARY POD AGE
|
||||
<instance> True Running app-db1-1 41d
|
||||
```
|
||||
@@ -36,13 +36,13 @@ NAME READY STATUS PRIMARY POD AGE
|
||||
### How to restore backup:
|
||||
|
||||
find snapshot:
|
||||
```
|
||||
```bash
|
||||
restic -r s3:s3.example.org/mariadb-backups/database_name snapshots
|
||||
```
|
||||
|
||||
|
||||
restore:
|
||||
```
|
||||
```bash
|
||||
restic -r s3:s3.example.org/mariadb-backups/database_name restore latest --target /tmp/
|
||||
```
|
||||
|
||||
@@ -51,15 +51,16 @@ more details:
|
||||
|
||||
### Known issues
|
||||
|
||||
- **Replication can't not be finished with various errors**
|
||||
- **Replication can't be finised in case if binlog purged**
|
||||
Until mariadbbackup is not used to bootstrap a node by mariadb-operator (this feature is not inmplemented yet), follow these manual steps to fix it:
|
||||
- **Replication can't be finished with various errors**
|
||||
- **Replication can't be finished in case if `binlog` purged**
|
||||
|
||||
Until `mariadbbackup` is not used to bootstrap a node by mariadb-operator (this feature is not inmplemented yet), follow these manual steps to fix it:
|
||||
https://github.com/mariadb-operator/mariadb-operator/issues/141#issuecomment-1804760231
|
||||
|
||||
- **Corrupted indicies**
|
||||
Sometimes some indecies can be corrupted on master replica, you can recover them from slave:
|
||||
|
||||
```
|
||||
```bash
|
||||
mysqldump -h <slave> -P 3306 -u<user> -p<password> --column-statistics=0 <database> <table> ~/tmp/fix-table.sql
|
||||
mysql -h <master> -P 3306 -u<user> -p<password> <database> < ~/tmp/fix-table.sql
|
||||
```
|
||||
@@ -68,34 +69,45 @@ more details:
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------- |
|
||||
| `replicas` | Number of MariaDB replicas | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `{}` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge. | `nano` |
|
||||
| `size` | Persistent Volume size | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `""` |
|
||||
| `external` | Enable external access from outside the cluster | `false` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of MariaDB replicas | `int` | `2` |
|
||||
| `resources` | Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
|
||||
| `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` |
|
||||
|
||||
|
||||
### Application-specific parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------- | ----------------------- | ----- |
|
||||
| `users` | Users configuration | `{}` |
|
||||
| `databases` | Databases configuration | `{}` |
|
||||
| Name | Description | Type | Value |
|
||||
| -------------------------------- | --------------------------------------- | ------------------- | ------- |
|
||||
| `users` | Users configuration | `map[string]object` | `{...}` |
|
||||
| `users[name].password` | Password for the user | `string` | `""` |
|
||||
| `users[name].maxUserConnections` | Maximum amount of connections | `int` | `0` |
|
||||
| `databases` | Databases configuration | `map[string]object` | `{...}` |
|
||||
| `databases[name].roles` | Roles for the database | `*object` | `null` |
|
||||
| `databases[name].roles.admin` | List of users with admin privileges | `[]string` | `[]` |
|
||||
| `databases[name].roles.readonly` | List of users with read-only privileges | `[]string` | `[]` |
|
||||
|
||||
|
||||
### Backup parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ------------------------ | ---------------------------------------------- | ------------------------------------------------------ |
|
||||
| `backup.enabled` | Enable periodic backups | `false` |
|
||||
| `backup.s3Region` | The AWS S3 region where backups are stored | `us-east-1` |
|
||||
| `backup.s3Bucket` | The S3 bucket used for storing backups | `s3.example.org/postgres-backups` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `0 2 * * *` |
|
||||
| `backup.cleanupStrategy` | The strategy for cleaning up old backups | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
|
||||
| `backup.s3AccessKey` | The access key for S3, used for authentication | `oobaiRus9pah8PhohL1ThaeTa4UVa7gu` |
|
||||
| `backup.s3SecretKey` | The secret key for S3, used for authentication | `ju3eum4dekeich9ahM1te8waeGai0oog` |
|
||||
| `backup.resticPassword` | The password for Restic backup encryption | `ChaXoveekoh6eigh4siesheeda2quai0` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------ | ---------------------------------------------- | -------- | ------------------------------------------------------ |
|
||||
| `backup` | Backup configuration | `object` | `{}` |
|
||||
| `backup.enabled` | Enable regular backups, default is `false`. | `bool` | `false` |
|
||||
| `backup.s3Region` | AWS S3 region where backups are stored | `string` | `us-east-1` |
|
||||
| `backup.s3Bucket` | S3 bucket used for storing backups | `string` | `s3.example.org/mysql-backups` |
|
||||
| `backup.schedule` | Cron schedule for automated backups | `string` | `0 2 * * *` |
|
||||
| `backup.cleanupStrategy` | Retention strategy for cleaning up old backups | `string` | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
|
||||
| `backup.s3AccessKey` | Access key for S3, used for authentication | `string` | `<your-access-key>` |
|
||||
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `string` | `<your-secret-key>` |
|
||||
| `backup.resticPassword` | Password for Restic backup encryption | `string` | `<password>` |
|
||||
|
||||
|
||||
## Parameter examples and reference
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.9.1@sha256:a3789db9e9e065ff60cbac70771b4a8aa1460db3194307cf5ca5d4fe1b412b6b
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.10.0@sha256:a3789db9e9e065ff60cbac70771b4a8aa1460db3194307cf5ca5d4fe1b412b6b
|
||||
|
||||
@@ -1,90 +1,199 @@
|
||||
{
|
||||
"properties": {
|
||||
"backup": {
|
||||
"properties": {
|
||||
"cleanupStrategy": {
|
||||
"default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"description": "The strategy for cleaning up old backups",
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"description": "Enable periodic backups",
|
||||
"type": "boolean"
|
||||
},
|
||||
"resticPassword": {
|
||||
"default": "ChaXoveekoh6eigh4siesheeda2quai0",
|
||||
"description": "The password for Restic backup encryption",
|
||||
"type": "string"
|
||||
},
|
||||
"s3AccessKey": {
|
||||
"default": "oobaiRus9pah8PhohL1ThaeTa4UVa7gu",
|
||||
"description": "The access key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"s3Bucket": {
|
||||
"default": "s3.example.org/postgres-backups",
|
||||
"description": "The S3 bucket used for storing backups",
|
||||
"type": "string"
|
||||
},
|
||||
"s3Region": {
|
||||
"default": "us-east-1",
|
||||
"description": "The AWS S3 region where backups are stored",
|
||||
"type": "string"
|
||||
},
|
||||
"s3SecretKey": {
|
||||
"default": "ju3eum4dekeich9ahM1te8waeGai0oog",
|
||||
"description": "The secret key for S3, used for authentication",
|
||||
"type": "string"
|
||||
},
|
||||
"schedule": {
|
||||
"default": "0 2 * * *",
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"backup": {
|
||||
"description": "Backup configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"cleanupStrategy": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
|
||||
"enabled": false,
|
||||
"resticPassword": "\u003cpassword\u003e",
|
||||
"s3AccessKey": "\u003cyour-access-key\u003e",
|
||||
"s3Bucket": "s3.example.org/mysql-backups",
|
||||
"s3Region": "us-east-1",
|
||||
"s3SecretKey": "\u003cyour-secret-key\u003e",
|
||||
"schedule": "0 2 * * *"
|
||||
},
|
||||
"required": [
|
||||
"cleanupStrategy",
|
||||
"enabled",
|
||||
"resticPassword",
|
||||
"s3AccessKey",
|
||||
"s3Bucket",
|
||||
"s3Region",
|
||||
"s3SecretKey",
|
||||
"schedule"
|
||||
],
|
||||
"properties": {
|
||||
"cleanupStrategy": {
|
||||
"description": "Retention strategy for cleaning up old backups",
|
||||
"type": "string",
|
||||
"default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"
|
||||
},
|
||||
"external": {
|
||||
"default": false,
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean"
|
||||
"enabled": {
|
||||
"description": "Enable regular backups, default is `false`.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"replicas": {
|
||||
"default": 2,
|
||||
"description": "Number of MariaDB replicas",
|
||||
"type": "number"
|
||||
"resticPassword": {
|
||||
"description": "Password for Restic backup encryption",
|
||||
"type": "string",
|
||||
"default": "\u003cpassword\u003e"
|
||||
},
|
||||
"resources": {
|
||||
"default": {},
|
||||
"description": "Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object"
|
||||
"s3AccessKey": {
|
||||
"description": "Access key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-access-key\u003e"
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"default": "nano",
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: nano, micro, small, medium, large, xlarge, 2xlarge.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
"s3Bucket": {
|
||||
"description": "S3 bucket used for storing backups",
|
||||
"type": "string",
|
||||
"default": "s3.example.org/mysql-backups"
|
||||
},
|
||||
"size": {
|
||||
"default": "10Gi",
|
||||
"description": "Persistent Volume size",
|
||||
"type": "string"
|
||||
"s3Region": {
|
||||
"description": "AWS S3 region where backups are stored",
|
||||
"type": "string",
|
||||
"default": "us-east-1"
|
||||
},
|
||||
"storageClass": {
|
||||
"default": "",
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
"s3SecretKey": {
|
||||
"description": "Secret key for S3, used for authentication",
|
||||
"type": "string",
|
||||
"default": "\u003cyour-secret-key\u003e"
|
||||
},
|
||||
"schedule": {
|
||||
"description": "Cron schedule for automated backups",
|
||||
"type": "string",
|
||||
"default": "0 2 * * *"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Chart Values",
|
||||
"type": "object"
|
||||
}
|
||||
"databases": {
|
||||
"description": "Databases configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roles": {
|
||||
"description": "Roles for the database",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"admin": {
|
||||
"description": "List of users with admin privileges",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"readonly": {
|
||||
"description": "List of users with read-only privileges",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
"description": "Enable external access from outside the cluster",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of MariaDB replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each MariaDB replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "nano",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume Claim size, available for application data",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
},
|
||||
"users": {
|
||||
"description": "Users configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"maxUserConnections",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"maxUserConnections": {
|
||||
"description": "Maximum amount of connections",
|
||||
"type": "integer"
|
||||
},
|
||||
"password": {
|
||||
"description": "Password for the user",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user