Compare commits

...

75 Commits

Author SHA1 Message Date
Andrei Kvapil
ca61c71084 Replace Ancestor tracking webhook with controller
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-17 12:48:17 +02:00
Timofei Larkin
65a734bb65 [ci] Get REGISTRY from vars, not secrets (#1423)
## What this PR does

This patch sources the REGISTRY env var from GitHub actions variables
instead of secrets, so pull requests from forked repos work correctly.

### Release note

```release-note
[ci] Source the REGISTRY env var from actions' variables, not secrets,
so external pull requests can work.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated CI configuration to source the container registry setting from
organization variables instead of secrets, improving maintainability and
visibility of build settings.
* No impact to application features, functionality, or performance;
builds and deployments continue to operate as before.
* No action required from users or admins; this is an internal workflow
refinement.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-16 15:05:25 +04:00
Timofei Larkin
07384c3605 [ci] Get REGISTRY from vars, not secrets
This patch sources the REGISTRY env var from GitHub actions variables
instead of secrets, so pull requests from forked repos work correctly.

```release-note
[ci] Source the REGISTRY env var from actions' variables, not secrets,
so external pull requests can work.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-16 11:36:00 +03:00
Andrei Kvapil
87b2316194 Release v0.36.0-beta.4 (#1422)
This PR prepares the release `v0.36.0-beta.4`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
- Upgraded multiple components to v0.36.0-beta.4 (installer, API,
controller, dashboard, Kamaji, objectstorage controller, matchbox, e2e
sandbox, objectstorage-sidecar).
- Refreshed image digests to latest for kubevirt CSI driver,
nginx-cache, kubeovn, and s3manager.
  - Updated dashboard app version and related API images.
  - Pinned kubeovn-plunger to a stable version instead of latest.
- General stability, compatibility, and maintenance improvements with no
functional changes to user workflows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-16 02:04:15 +02:00
cozystack-bot
585569f285 Prepare release v0.36.0-beta.4
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-15 23:22:50 +00:00
Andrei Kvapil
dbe1df8d27 [seaweedfs] Remove VerticalPodAutoscaler (#1421)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

It does not work well anyway

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- Breaking Changes
- Vertical Pod Autoscalers for SeaweedFS components (filer, master,
volume) are no longer deployed. Resource autoscaling via VPA is disabled
for new installs and upgrades.
- On upgrade, previously created VPAs may be removed; ensure resource
requests/limits are configured or manage autoscaling via HPA or external
tooling.

- Chores
- Deployment simplified by removing built-in VPA resources for SeaweedFS
components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-15 21:51:38 +02:00
Andrei Kvapil
17eb1e0ba3 [seaweedfs] Remove VerticalPodAutoscaler
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-15 20:54:51 +02:00
Andrei Kvapil
b55c9f616d [kube-ovn] fix plunger: flag provided but not defined: -kube-ovn-namespace (#1418)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

This PR fixes the error:

```
flag provided but not defined: -kube-ovn-namespace
Usage of /kubeovn-plunger:
  -disable-telemetry
        Disable telemetry collection
  -enable-http2
        If set, HTTP/2 will be enabled for the metrics and webhook servers
  -health-probe-bind-address string
        The address the probe endpoint binds to. (default ":8081")
  -kubeconfig string
        Paths to a kubeconfig. Only required if out-of-cluster.
  -leader-elect
        Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
  -metrics-bind-address string
        The address the metrics endpoint binds to. Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service. (default "0")
  -metrics-secure
        If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead. (default true)
  -zap-devel
        Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
  -zap-encoder value
        Zap log encoding (one of 'json' or 'console')
  -zap-log-level value
        Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity
  -zap-stacktrace-level value
        Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').
  -zap-time-encoding value
        Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.
```

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
  - None.

- Bug Fixes
- Improved kube-ovn-plunger reliability by removing a redundant
namespace configuration, allowing automatic detection and reducing
potential misconfiguration.
- Preserved existing logging and metrics behavior with no changes
required by users.

- Chores
- Simplified deployment configuration for kube-ovn-plunger by
eliminating an unnecessary parameter, reducing maintenance overhead.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-15 18:43:50 +02:00
Andrei Kvapil
f025845a94 [ingress] make nginx resources configurable (#1416)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Added per-replica CPU and memory configuration for the ingress
controller.
- Introduced resource presets (nano, micro, small, medium, large,
xlarge, 2xlarge) with a default of micro.
- Documentation
- Updated parameters guide to document new resource settings and
presets.
- Chores
  - Bumped ingress chart version to 1.9.0.
- Updated version mapping to include the new chart version and pin the
previous one.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-15 18:42:17 +02:00
Andrei Kvapil
e54fc63af4 [seaweedfs] Refactor config; add resources (#1415)
Co-authored-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
- Component-based configuration (master, volume with zones, filer, db,
s3) with per-service replicas and resource presets.
  - Per-zone volume monitoring plus new DB and S3 monitors.
- Database replicas/size/storageClass now configurable; S3 defaults to 2
replicas.
- Documentation
  - README updated to the new component-based schema.
- Refactor
- Configuration reorganized from flat to nested; standardized resource
settings.
- Chores
  - Chart version bumped to 0.7.0.
- Automated migration to upgrade releases and relocate existing values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-15 18:42:02 +02:00
Timofei Larkin
9352861051 [cozystack-controller] Clusterwide read perms (#1419)
## What this PR does

In an earlier patch the Cozystack controller now reads arbitrary objects
in the cluster to establish the lineage of any created pod, service,
pvc, or secret. These objects may be created by various other
controllers, so in general, the controller now requires read permissions
on arbitrary objects in the cluster.

### Release note

```release-note
[cozystack-controler] Fix an RBAC error that prevented the workload
labelling feature from working.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-15 20:37:14 +04:00
Andrei Kvapil
b9eec3f261 [installer] Fix: add jq and git to installer image (#1417)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```
2025-09-15 17:51:26 +02:00
Timofei Larkin
f2cfb4f870 [cozystack-controller] Clusterwide read perms
In an earlier patch the Cozystack controller now reads arbitrary objects
in the cluster to establish the lineage of any created pod, service,
pvc, or secret. These objects may be created by various other
controllers, so in general, the controller now requires read permissions
on arbitrary objects in the cluster.

```release-note
[cozystack-controler] Fix an RBAC error that prevented the workload
labelling feature from working.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-15 18:49:37 +03:00
Andrei Kvapil
2291d0f7f2 [kube-ovn] fix plunger: flag provided but not defined: -kube-ovn-namespace
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-15 17:27:18 +02:00
Andrei Kvapil
15c100d262 [installer] Fix: add jq and git to installer image
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-15 16:53:48 +02:00
kklinch0
2c9864bc09 [ingress] make nginx resources configurable
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-09-15 16:48:52 +02:00
Andrei Kvapil
bb1e8805dc [seaweedfs] Refactor config; add resources
Co-authored-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-15 16:45:40 +02:00
Andrei Kvapil
08b5217b72 [kubeovn] Fix service scrape for plunger (#1414)
## What this PR does

This patch delivers changes to the monitoring config of Kube-OVN
plunger, which were accidentally omitted in its release, leading to a
duplicate service, broken monitoring agents' helm release and not
actually scraping the plunger.

### Release note

```release-note
[kubeovn-plunger] Fix the VMServiceScrape object for collecting the
plunger's metrics.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Enable metrics scraping for Kube-OVN Plunger, integrating it into the
monitoring stack.

- Chores
- Migrated scraping to a VictoriaMetrics configuration and moved
resources to the monitoring namespace.
- Updated selectors to target the Kube-OVN Plunger workload in the
appropriate namespace.
- Adjusted metric relabeling: node label removed and some label names
simplified; series may appear under kubeovn-plunger instead of kube-dns.
- Standardized scrape port naming to “metrics,” aligning with current
service conventions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-15 10:49:38 +02:00
Timofei Larkin
08d2d61f1a [kubeovn] Fix service scrape for plunger
This patch delivers changes to the monitoring config of Kube-OVN
plunger, which were accidentally omitted in its release, leading to a
duplicate service, broken monitoring agents' helm release and not
actually scraping the plunger.

```release-note
[kubeovn-plunger] Fix the VMServiceScrape object for collecting the
plunger's metrics.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-15 10:50:16 +03:00
Timofei Larkin
356fea6a37 [cozystack-controller] Ancestor tracking webhook (#1400)
## What this PR does

Many resources created as part of managed apps in cozystack (pods,
secrets, etc) do not carry predictable labels that unambiguously
indicate which app originally triggered their creation. Some resources
are managed by controllers and other custom resources and this
indirection can lead to loss of information. Other controllers sometimes
simply do not allow setting labels on controlled resources and the
latter do not inherit labels from the owner. This patch implements a
webhook that sidesteps this problem with a universal solution. On
creation of a pod/secret/PVC etc it walks through the owner references
until a HelmRelease is found that can be matched with a managed app
dynamically registered in the Cozystack API server. The pod is mutated
with labels identifying the managed app.

### Release note

```release-note
[cozystack-controller] Add a mutating webhook to identify the Cozystack
managed app that ultimately owns low-level resources created in the
cluster and label these resources with a reference to said app.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Adds an admission webhook that injects application lineage labels on
resource create/update for improved observability and ownership tracing.
- Adds a runtime-updatable mapping for resolving HelmRelease →
application, and registers both the lineage controller and webhook
during startup.
- Adds Deployment, Service, and cert-manager templates to enable and
secure the webhook (in-cluster TLS, service routing).

- **Tests**
- Adds a test to exercise lineage traversal and validate ownership-graph
resolution and labeling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-12 11:44:12 +04:00
Timofei Larkin
e1b97e3727 [cozystack-controller] Ancestor tracking webhook
Many resources created as part of managed apps in cozystack (pods,
secrets, etc) do not carry predictable labels that unambiguously
indicate which app originally triggered their creation. Some resources
are managed by controllers and other custom resources and this
indirection can lead to loss of information. Other controllers sometimes
simply do not allow setting labels on controlled resources and the
latter do not inherit labels from the owner. This patch implements a
webhook that sidesteps this problem with a universal solution. On
creation of a pod/secret/PVC etc it walks through the owner references
until a HelmRelease is found that can be matched with a managed app
dynamically registered in the Cozystack API server. The pod is mutated
with labels identifying the managed app.

```release-note
[cozystack-controller] Add a mutating webhook to identify the Cozystack
managed app that ultimately owns low-level resources created in the
cluster and label these resources with a reference to said app.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-11 20:55:33 +03:00
Andrei Kvapil
ea27dc9497 [kubernetes] Change settings coredns replicas and image (#1410)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Exposed configuration for CoreDNS: you can now set the image
repository and replica count via values.
- Changes
- CoreDNS now deploys in the kube-system namespace for better alignment
with cluster services.
- Default CoreDNS replica count increased to 2 to improve availability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-11 15:20:06 +02:00
Andrei Kvapil
f06c5d996d [kubernetes] Change settings coredns replicas and image
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-11 15:11:49 +02:00
Timofei Larkin
87c5540ad3 [kubeovn] Implement the KubeOVN plunger (#1380)
## What this PR does

This patch implements external monitoring of the Kube-OVN cluster. A new
reconciler timed to run its reconcile loop at a fixed interval execs
into the ovn-central pods and collects their cluster info. If the
members' opinions about the cluster disagree, an alert is raised. Other
issues with the distributed consensus are also highlighted.

### Release note

```release-note
[kubeovn,cozystack-controller] Implement the KubeOVN plunger, an
external monitoring agent for the ovn-central cluster.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-11 14:17:03 +04:00
Andrei Kvapil
03e18ee02f feature make force upgrade for ingress controller chart (#1404)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
  - Bumped tenant chart version to 1.14.0; no user-visible changes.
- Updated deployment configuration to force ingress upgrades (no impact
on app behavior).
  - Refreshed version mappings to reflect the new release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-11 12:15:25 +02:00
Timofei Larkin
382a9787f4 [kubeovn] Implement the KubeOVN plunger
This patch implements external monitoring of the Kube-OVN cluster. A new
reconciler timed to run its reconcile loop at a fixed interval execs
into the ovn-central pods and collects their cluster info. If the
members' opinions about the cluster disagree, an alert is raised. Other
issues with the distributed consensus are also highlighted.

```release-note
[kubeovn,cozystack-controller] Implement the KubeOVN plunger, an
external monitoring agent for the ovn-central cluster.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-11 02:11:58 +03:00
Andrei Kvapil
2bca6b932c [etcd] Fix Global TopologySpreadConstarints (#1405)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[etcd] Fix Global TopologySpreadConstarints
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Ensures topology spread constraints consistently target etcd pods when
raw constraints are used by adding an explicit label selector, improving
scheduling consistency and reducing uneven distribution risks.

* **Chores**
  * Bumped etcd chart version to 2.10.1.
  * Updated version mapping to reference the latest release.
  * No other functional changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-10 16:19:02 +02:00
Andrei Kvapil
601f6bd3c9 [etcd] Fix Global TopologySpreadConstarints
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-10 15:10:47 +02:00
kklinch0
1243a960e3 feature make force upgrade for ingress controller chart
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-09-10 09:34:37 +03:00
Andrei Kvapil
4dd062d9cd Feat/tests with resource quota (#1389)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
Feat/tests with resource quota
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Tenant resource quotas now accept explicit cpu, memory, and storage
values per namespace.
- Default container limits and requests added via a LimitRange (CPU,
memory, ephemeral storage).

- **Behavior Changes**
- Resource quota output simplified: quotas emitted at the root and
storage limit entries omitted from flattened output.

- **Tests**
- Increased timeouts for VM disk readiness and PVC binding; added
runtime checks validating ResourceQuota and LimitRange defaults.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-09 19:25:19 +02:00
IvanHunters
3e03b1bd86 add resource quota for testing ns
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-09-09 17:42:52 +03:00
Andrei Kvapil
8f1975d1da Release v0.36.0-beta.3 (#1401)
This PR prepares the release `v0.36.0-beta.3`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
- Upgraded core/system components to v0.36.0-beta.3 (installer,
controller, API, dashboard, kamaji, objectstorage-controller, seaweedfs
sidecar, bootbox/matchbox).
- Bumped dashboard appVersion and image tags; updated kubeapps APIs
digest.
- Updated Kubernetes components: cluster-autoscaler to 0.29.0, kubevirt
cloud provider to 0.29.0, kubevirt CSI driver/node to 0.29.0.
- Refreshed image digests for nginx-cache, s3manager, kubeovn, and
kubeovn-webhook.
  - Adjusted kamaji migrate-image to v0.36.0-beta.3.
  - Updated testing e2e image to v0.36.0-beta.3.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-09 09:10:19 +02:00
cozystack-bot
e15ff2a4d0 Prepare release v0.36.0-beta.3
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-08 20:23:57 +00:00
Andrei Kvapil
272185a2df k8s change coredns ns (#1395)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
- k8s change coredns ns
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
  * Updated Kubernetes app chart to 0.29.0.
* Bumped default Kubernetes minor version from v1.32 to v1.33 for new
deployments.
* CoreDNS release now installs and stores state in the kube-system
namespace.
  * Refreshed versions mapping to include the new chart version.
* **Documentation**
  * README and schema defaults updated to show v1.33.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-08 15:53:33 +02:00
kklinch0
be8495dd06 k8s change coredns ns
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-09-06 23:37:32 +03:00
Andrei Kvapil
7f477eec96 Release v0.36.0-beta.2 (#1393)
This PR prepares the release `v0.36.0-beta.2`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
  - None.
- Bug Fixes
  - None.
- Chores
- Upgraded core components to v0.36.0-beta.2: Installer, E2E Sandbox,
CozyStack API, CozyStack Controller (version updated), Dashboard
(appVersion and images), Kamaji (image and migrate-image), ObjectStorage
Controller, SeaweedFS sidecar, Bootbox Matchbox.
- Refreshed pinned image digests for Nginx Cache, KubeVirt CSI Driver
(including node), Kube-OVN, and S3 Manager.
- No user-facing functionality changes; versions and image references
updated for consistency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 14:25:58 +02:00
Andrei Kvapil
cc4b7ea28c [ci] use host buildx config (#1015)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
- Improved Docker configuration handling during pull-request builds by
adding a setup step to preserve runner Docker credentials when present.
- Restricted container registry login to non-fork pull requests to avoid
using protected credentials for forked contributions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 14:25:12 +02:00
cozystack-bot
8335347dc3 Prepare release v0.36.0-beta.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-05 11:02:16 +00:00
Andrei Kvapil
49d69a5896 [dx] Remove BUILDER and PLATFORM autodetection logic (#1391)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

It does not work well anyway.

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[dx] Remove BUILDER and PLATFORM autodetection logic
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Refactor
- Consolidated Docker Buildx flags into a single configurable argument
across all image build targets, keeping tagging, caching, and metadata
behavior unchanged.
- Chores
- Added configurable environment variables for builds (e.g., builder,
platform, extra args, tag) to standardize and simplify configuration.
- Removed automatic builder/platform detection; these can now be
explicitly set when needed, making builds more predictable and
customizable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 12:53:52 +02:00
Andrei Kvapil
89a74f653a [ci] use host buildx config
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-05 11:56:51 +02:00
Andrei Kvapil
9f2b98d364 [dx] Remove BUILDER and PLATFORM autodetection logic
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-05 11:48:19 +02:00
Andrei Kvapil
7090b8adf1 [seaweedfs] Fix connectivity issues for SeaweedFS (#1386)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[seaweedfs] Fix connectivity issues for SeaweedFS
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Increased Nginx Ingress timeouts for the SeaweedFS S3 endpoint
(read/send: 3600s, client body: 3600s, client header: 120s). This
enhances stability for long-running S3 operations, reducing premature
disconnects and timeout errors.
* Users should experience more reliable large uploads/downloads and
fewer interruptions, especially over slower or inconsistent networks.
* No other behavior changes; existing S3 access and routing remain the
same.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-05 10:18:56 +02:00
Andrei Kvapil
c5b46fc79c [platform] Fix boolean override bug in Helm merge — ConfigMap values now correctly take precedence over bundle defaults (#1385)
## What this PR does

Fixes a bug where boolean values from bundle files could not be properly
overridden by `values-<component>` entries in ConfigMaps.

The root cause is a Helm merge function limitation: when merging boolean
values, `true` from the first map is not overwritten by `false` from the
second map. This caused ConfigMap overrides to be ignored in certain
cases.

This PR switches to `mergeOverwrite`, ensuring that ConfigMap values
always take precedence over bundle defaults, as intended.

### Example
- **Bundle:** `autoDirectNodeRoutes: true`  
- **ConfigMap (values-cilium):** `autoDirectNodeRoutes: false`  
- **Before:** result = `true` (incorrect)  
- **After:** result = `false` (correct)  

With this change, users can reliably override any component
configuration using the `values-<component>` pattern in the Cozystack
ConfigMap.

---

## Release note

```release-note
[platform] Fix boolean override bug in Helm merge — ConfigMap values now correctly take precedence over bundle defaults


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- Bug Fixes
  - Fixed per-release value merging so release-specific settings reliably take precedence over accumulated defaults.
  - Resolved cases where release overrides were ignored or only partially applied during deploys and upgrades.
  - Made merge behavior deterministic and predictable across environments, reducing configuration surprises.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 17:30:07 +02:00
Andrei Kvapil
a291badbd4 [seaweedfs] Fix connectivity issues for SeaweedFS
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-04 17:21:57 +02:00
Denis Yudin
52d749d46a fix: use mergeOverwrite to properly override ConfigMap values
Fixes an issue where boolean values from bundle files were not being
properly overridden by values-<component> ConfigMap entries.

The Helm merge function has a bug when merging boolean values where
true from the first dict doesn't get overwritten by false from the
second dict. Using mergeOverwrite ensures ConfigMap values take
precedence over bundle values as intended.

Example:
- Bundle: autoDirectNodeRoutes: true
- ConfigMap values-cilium: autoDirectNodeRoutes: false
- Before: result was true (incorrect)
- After: result is false (correct)

This fix ensures that users can properly override any component
configuration using the values-<component> pattern in the cozystack
ConfigMap.

Signed-off-by: Denis Yudin <dyudin@intermedia.com>
2025-09-04 12:29:05 +01:00
Timofei Larkin
9f89ef36bb [ci] Fix garbage output in cozyreport (#1383)
## What this PR does

Some "while read NAMESPACE NAME _" steps in the cozyreport script that
collects debug info weren't omitting the headers in `kubectl get` output
and trying to get objects named NAME in namespace NAMESPACE. This patch
adds `--no-header` to some places where it was forgotten.

### Release note

```release-note
[ci] Fix an error in cozyreport that tried to parse non-existent objects
and generated garbage output in CI debug logs
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-04 15:04:01 +04:00
Timofei Larkin
f59d072ef1 [ci] Fix garbage output in cozyreport
Some "while read NAMESPACE NAME _" steps in the cozyreport script that
collects debug info weren't omitting the headers in `kubectl get` output
and trying to get objects named NAME in namespace NAMESPACE. This patch
adds `--no-header` to some places where it was forgotten.

```release-note
[ci] Fix an error in cozyreport that tried to parse non-existent objects
and generated garbage output in CI debug logs
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-04 11:03:08 +03:00
Andrei Kvapil
c0d5e52e65 Release v0.36.0-beta.1 (#1379)
This PR prepares the release `v0.36.0-beta.1`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
- Upgraded platform container images to v0.36.0-beta.1 across the stack,
including the core installer, controllers and API, networking webhook,
dashboard components, control plane manager, object storage services
(controller, sidecar, S3 manager), ancillary services, and e2e testing
sandbox. Image digests updated accordingly.
  - Dashboard app version updated to v0.36.0-beta.1.
- No user-facing behavior changes expected; updates align component
versions and ensure consistency across deployments.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-02 22:38:16 +02:00
cozystack-bot
034f71cc9d Prepare release v0.36.0-beta.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-02 11:36:20 +00:00
Andrei Kvapil
fdd4f167c6 [virtual-machine] fix versions migration (#1378)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```
2025-09-02 13:31:39 +02:00
Andrei Kvapil
8fbebd4e47 [virtual-machine] fix versions migration
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-02 13:30:40 +02:00
Andrei Kvapil
389ec27b19 Release v0.36.0-alpha.2 (#1370)
This PR prepares the release `v0.36.0-alpha.2`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- Chores
- Upgraded component images to v0.36.0-alpha.2: installer, API,
controller, dashboard, Kamaji, KubeOVN webhook, objectstorage
controller, SeaweedFS sidecar, Bootbox matchbox, and testing sandbox.
- Updated dashboard config appVersion to v0.36.0-alpha.2; refreshed
dashboard and kubeapps-apis image tags/digest.
- Updated Kamaji migrate-image argument and cozystackVersion to
v0.36.0-alpha.2.
  - Refreshed image digests for nginx-cache and s3manager.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 20:28:34 +02:00
cozystack-bot
29df1fdc1e Prepare release v0.36.0-alpha.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-01 18:00:05 +00:00
Andrei Kvapil
c4e048b315 fix race conditions for seaweedfs and fix tests preparing (#1371)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
fix race conditions for seaweedfs and fix tests preparing
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* Chores
* Increased deployment timeouts to 10 minutes and set install/upgrade
remediation to unlimited retries for SeaweedFS, ingress, and monitoring
components to improve deployment resilience.
* Tests
* Extended end-to-end readiness waits for alerting components from 5 to
15 minutes for more stable test runs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 19:46:46 +02:00
Andrei Kvapil
ce5fd9d292 [virtual-machine] Fix vm update hook (#1376)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

Fix regression introduced by
https://github.com/cozystack/cozystack/pull/1169, now we have correct
singular names for virtualmachines which are conflictiing with KubeVirt
ones.

Solution: explicitly specify apiversion

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[virtual-machine] Fix vm update hook
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved reliability of VM update hooks by targeting the correct API
resource, preventing occasional patch failures when updating
instancetype and preference.
* Ensures VM updates apply consistently across environments without
changing existing behavior.

* **Chores**
* Aligned resource references with fully qualified API names to enhance
compatibility with current cluster configurations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 19:46:14 +02:00
IvanHunters
8e906be9df fix race conditions for seaweedfs and fix tests preparing
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-09-01 16:32:49 +03:00
Andrei Kvapil
99bfd4884f Get rid of bitnami images (#1374)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

This PR removes bitnami images from all charts. Bitnami has deprecated
their free images, see details here:
- https://github.com/bitnami/charts/issues/35164

Also dashboard has moved helper images to `bitnamilegacy`, we will fully
replace it by our new dashboard soon:
- https://github.com/cozystack/cozystack/pull/1269

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
Get rid of bitnami images
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* New Features
* Added configurable image overrides for Kubeapps components (frontend,
auth proxy, Redis, kubectl).
  * Introduced image settings for Velero’s kubectl helper.
  * Added image configuration for Vertical Pod Autoscaler components.
  * Added a configurable resize hook image for SeaweedFS volumes.

* Chores
* Standardized kubectl-related images to alpine/k8s:1.33.4 across
multiple operational hooks (VM update, PVC resize, etcd maintenance,
SeaweedFS pre-upgrade), with no behavioral changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 14:36:03 +02:00
Andrei Kvapil
15b213b38b Fix vm update hook
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-01 14:33:56 +02:00
Andrei Kvapil
8ca8817000 Fix missing cozy-lib.resources.flatten template (#1372)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[tenant] Fix missing cozy-lib.resources.flatten template
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Added support to output resource quotas as a flattened key-value map
using dot-notation (e.g., limits.cpu, requests.memory) for easier
reading and overrides.
- Outputs are grouped under a top-level resourceQuotas section, ready
for YAML-based configuration and tooling.
- Backward compatible: this is an additive capability and does not
change existing behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 13:09:49 +02:00
Andrei Kvapil
9f8c79f5d1 Update SeaweedFS to v3.97 to enable SSE support (#1373)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[seaweedfs] Update SeaweedFS to v3.97 to enable SSE support
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* Chores
  * Updated SeaweedFS chart to version 4.0.397 and app version to 3.97.
* Changed the image used for volume resize operations to
alpine/k8s:1.28.4, replacing bitnami/kubectl.
* This affects the resize hook used to patch Kubernetes resources during
capacity changes for PVC-based deployments.
  * No other functional changes included in this update.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 13:09:35 +02:00
Andrei Kvapil
ce21299280 Get rid of bitnami images
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-01 13:05:56 +02:00
Andrei Kvapil
403d1f9944 Update SeaweedFS to v3.97 to enable SSE support
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-01 12:25:30 +02:00
Andrei Kvapil
138e5fbe15 [virtual-machine] Use external IP for egress traffic for PortList method too (#1349)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[virtual-machine] Use external IP for egress traffic for PortList method too
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Default network policies for Virtual Machine and VM Instance: ingress
from cluster/world, egress to world, optional port-based ingress when
using a port list.
  - Services now always include whole-IP annotation.
- VM workloads default to blocking external communication via
annotation.
- Tenant network policy now applies only to workloads explicitly labeled
to allow external communication.

- **Chores**
- Version bumps: Tenant 1.13.0, Virtual Machine 0.14.0, VM Instance
0.12.0.
- Updated versions map and added a migration script to advance cluster
component versions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-01 12:15:47 +02:00
Andrei Kvapil
fe869b97fd Fix missing cozy-lib.resources.flatten template
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-01 12:13:23 +02:00
Andrei Kvapil
a4aeeca2d3 [virtual-machine] Use external IP for egress traffic for PortList method too
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-29 19:49:45 +02:00
Andrei Kvapil
33691c2d3a [docs] Changelogs for release series v0.35.x (#1347)
- **[docs] Changelogs for v0.34.***
- **[docs] Changelogs for v0.35.0-alpha.1**
- **[docs] Changelogs for v0.35.0-alpha.2**
- **[docs] Changelogs for v0.35.0-alpha.3**
- **[docs] Changelogs for v0.35.0-beta.1**


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Added v0.35.0 changelog covering Major Features (external app
reconciler, RobotLB autodetect, SeaweedFS S3 & monitoring, API
improvements, ClickHouse Keeper), Security, Fixes, Dependencies
(flux-operator 0.28.0), and CI/CD.
  * Added v0.35.1 changelog noting a cozy-lib retrieval fix.
* Added v0.35.2 changelog (LLDPD built-in, SeaweedFS & API fixes,
dependency bumps).
* Updated changelog template: removed placeholder top line and added a
prominent "Full Changelog" link.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 19:15:42 +02:00
Andrei Kvapil
08f1bda1aa fix seaweedfs s3 liveness probe scheme (#1368)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
fix seaweedfs s3 liveness probe scheme
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Added a liveness check for the SeaweedFS S3 endpoint (HTTPS). This
improves health monitoring and enables automatic recovery if the service
becomes unresponsive, enhancing stability and uptime while reducing
manual intervention. Readiness behavior remains unchanged. No user
action required.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-29 10:57:45 +02:00
IvanHunters
58f65abefd fix seaweedfs s3 liveness probe scheme
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-08-29 10:32:34 +03:00
Andrei Kvapil
9c1563adb7 Release v0.36.0-alpha.1 (#1365)
This PR prepares the release `v0.36.0-alpha.1`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Chores
- Upgraded platform components to v0.36.0-alpha.1 (installer,
controller, API, dashboard, Kamaji, objectstorage controller, SeaweedFS
sidecar, Matchbox, e2e sandbox).
- Updated Kubernetes add-ons: cluster-autoscaler 0.28.0; KubeVirt cloud
provider and CSI driver 0.28.0; Kube-OVN 1.14.5.
- Refreshed image digests for nginx-cache and s3manager to latest
builds.
- Updated dashboard app/version and Kubeapps images, including new API
image digest for improved compatibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-28 23:26:23 +02:00
cozystack-bot
cbbb50b194 Prepare release v0.36.0-alpha.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-08-28 19:01:36 +00:00
Timofei Larkin
6684117a00 [kube-ovn] Update and patch Kube-OVN (#1363)
## What this PR does

This patch updates Kube-OVN to 1.14.5 and patches the northd leader
check to test against all northd endpoints instead of just the first one
marked as ready.

### Release note

```release-note
[kube-ovn, fix] Update Kube-OVN and improve northd leader detection.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-08-28 21:22:07 +04:00
Timofei Larkin
6b9b700177 [kube-ovn] Update and patch Kube-OVN
This patch updates Kube-OVN to 1.14.5 and patches the northd leader
check to test again all northd endpoints instead of just the first one
marked as ready.

```release-note
[kube-ovn, fix] Update Kube-OVN and improve northd leader detection.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-08-28 19:04:09 +03:00
klinch0
89c80a8178 [tenant-k8s] change coredns (#1362)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
- tenant-k8s change coredns
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added a configurable CoreDNS addon with valuesOverride, packaged
chart, and managed deployment (metrics, autoscaling, HPA, customizable
Service).
  - Sets CoreDNS service cluster IP to 10.95.0.10 by default.

- **Documentation**
- Updated Kubernetes Addons docs to include CoreDNS configuration
options and examples.

- **Tests**
- Added unit tests for CoreDNS deployment, RBAC, Service, autoscaler,
HPA, and monitoring manifests.

- **Chores**
- Bumped Kubernetes app chart version to 0.28.0 and updated version
mappings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-28 13:22:16 +03:00
kklinch0
6b5af37e1a [tenant-k8s] change coredns
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-08-28 12:06:46 +03:00
Nick Volynkin
c10f6240b1 [docs] Changelogs for v0.35.*
Signed-off-by: Nick Volynkin <nick.volynkin@gmail.com>
2025-08-26 20:59:17 +03:00
184 changed files with 8337 additions and 800 deletions

View File

@@ -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}}

View File

@@ -18,6 +18,7 @@ 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

View File

@@ -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,
}
@@ -214,6 +219,15 @@ func main() {
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
View 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)
}
}

View File

@@ -1,4 +1,3 @@
Release description.
<!--
https://github.com/cozystack/cozystack/releases/tag/v0..
@@ -15,3 +14,7 @@ https://github.com/cozystack/cozystack/releases/tag/v0..
## Documentation
## Development, Testing, and CI/CD
---
**Full Changelog**: **Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0

138
docs/changelogs/v0.35.0.md Normal file
View 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

View 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

View 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
View File

@@ -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
View File

@@ -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=

View File

@@ -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

View File

@@ -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" {

View File

@@ -123,6 +123,7 @@ 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, "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'
@@ -139,6 +140,7 @@ EOF
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"}}'
@@ -150,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
@@ -187,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'
}

View File

@@ -2,14 +2,25 @@ 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"
@@ -20,85 +31,55 @@ type CozystackResourceDefinitionReconciler struct {
client.Client
Scheme *runtime.Scheme
// Configurable debounce duration
Debounce time.Duration
// Internal state for debouncing
mu sync.Mutex
lastEvent time.Time // Time of last CRUD event on CozystackResourceDefinition
lastHandled time.Time // Last time the Deployment was actually restarted
lastEvent time.Time
lastHandled time.Time
mem *crdmem.Memory
}
// Reconcile handles the logic to restart the target Deployment only once,
// even if multiple events occur close together
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
logger := log.FromContext(ctx)
// Only respond to our target deployment
if req.Namespace != "cozy-system" || req.Name != "cozystack-api" {
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
}
r.mu.Lock()
le := r.lastEvent
lh := r.lastHandled
debounce := r.Debounce
r.mu.Unlock()
if debounce <= 0 {
debounce = 5 * time.Second
}
// No events received yet — nothing to do
if le.IsZero() {
return ctrl.Result{}, nil
}
// Wait until the debounce duration has passed since the last event
if d := time.Since(le); d < debounce {
return ctrl.Result{RequeueAfter: debounce - d}, nil
}
// Already handled this event — skip restart
if !lh.Before(le) {
return ctrl.Result{}, nil
}
// Perform the restart by patching the deployment annotation
deploy := &appsv1.Deployment{}
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
log.Error(err, "Failed to get Deployment cozy-system/cozystack-api")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
patch := client.MergeFrom(deploy.DeepCopy())
if deploy.Spec.Template.Annotations == nil {
deploy.Spec.Template.Annotations = make(map[string]string)
}
deploy.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
if err := r.Patch(ctx, deploy, patch); err != nil {
log.Error(err, "Failed to patch Deployment annotation")
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
// Mark this event as handled
r.mu.Lock()
r.lastHandled = le
r.mu.Unlock()
log.Info("Deployment cozy-system/cozystack-api successfully restarted")
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
}
// SetupWithManager configures how the controller listens to events
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("cozystack-restart-controller").
Named("cozystackresource-controller").
For(&cozyv1alpha1.CozystackResourceDefinition{}, builder.WithPredicates()).
Watches(
&cozyv1alpha1.CozystackResourceDefinition{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
@@ -115,3 +96,88 @@ func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manage
).
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
}

View 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)
}
}

View 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)
}
}

View 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})
}

View 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
}

View 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
}

View 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
View 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()
}

View File

@@ -8,16 +8,11 @@ generate:
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

View File

@@ -7,16 +7,11 @@ 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

View File

@@ -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.27.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

View File

@@ -12,52 +12,37 @@ image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-
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) \
@@ -67,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

View File

@@ -93,7 +93,7 @@ See the reference for components utilized in this service:
| Name | Description | Type | Value |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.32` |
| `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` |
@@ -139,6 +139,8 @@ See the reference for components utilized in this service:
| `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

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.27.0@sha256:e4fbb7d2043f25b90cc8840468d0880e9d3d72ae8b1c8801bf8c35f944cc485d
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.27.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.29.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.27.0@sha256:c35987e8b37ad3b34a9a32fe6e80eee77b4c57b99090ca5cdbc3d16c25edb3b9
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.0@sha256:3a3bc912f70ccba1e9f92a0754179dbdc4c01f24073467b6d1406c77da794863

View File

@@ -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

View 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 }}

View File

@@ -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

View File

@@ -13,6 +13,9 @@
"cilium": {
"valuesOverride": {}
},
"coredns": {
"valuesOverride": {}
},
"fluxcd": {
"enabled": false,
"valuesOverride": {}
@@ -45,6 +48,7 @@
"required": [
"certManager",
"cilium",
"coredns",
"fluxcd",
"gatewayAPI",
"gpuOperator",
@@ -97,6 +101,24 @@
}
}
},
"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",
@@ -698,7 +720,7 @@
"version": {
"description": "Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.",
"type": "string",
"default": "v1.32",
"default": "v1.33",
"enum": [
"v1.28",
"v1.29",

View File

@@ -5,7 +5,7 @@ storageClass: replicated
## @section Application-specific parameters
## @param version {string} Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.
version: "v1.32"
version: "v1.33"
## @param host {string} Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
host: ""
@@ -122,6 +122,13 @@ addons:
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

View File

@@ -8,16 +8,11 @@ generate:
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

View File

@@ -4,4 +4,4 @@ description: Separated tenant namespace
icon: /logos/tenant.svg
type: application
version: 1.12.0
version: 1.14.0

View File

@@ -20,5 +20,7 @@ spec:
version: "*"
interval: 1m0s
timeout: 5m0s
upgrade:
force: true
values: {}
{{- end }}

View File

@@ -19,10 +19,10 @@ spec:
namespace: cozy-public
install:
remediation:
retries: 10
retries: -1
upgrade:
remediation:
retries: 10
retries: -1
interval: 1m0s
timeout: 10m0s
{{- end }}

View File

@@ -20,7 +20,11 @@ metadata:
name: allow-external-communication
namespace: {{ include "tenant.name" . }}
spec:
endpointSelector: {}
endpointSelector:
matchExpressions:
- key: policy.cozystack.io/allow-external-communication
operator: NotIn
values: ["false"]
ingress:
- fromEntities:
- world

View File

@@ -7,4 +7,21 @@ metadata:
spec:
hard:
{{- include "cozy-lib.resources.flatten" (list .Values.resourceQuotas $) | nindent 6 }}
---
apiVersion: v1
kind: LimitRange
metadata:
name: tenant-range-limits
namespace: {{ include "tenant.name" . }}
spec:
limits:
- default:
cpu: "250m"
memory: "128Mi"
ephemeral-storage: "2Gi"
defaultRequest:
cpu: "25m"
memory: "128Mi"
ephemeral-storage: "50Mi"
type: Container
{{- end }}

View File

@@ -17,6 +17,12 @@ spec:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
install:
remediation:
retries: -1
upgrade:
remediation:
retries: -1
interval: 1m0s
timeout: 5m0s
timeout: 10m0s
{{- end }}

View File

@@ -69,7 +69,9 @@ kubernetes 0.26.0 9584e5f5
kubernetes 0.26.1 0e47e1e8
kubernetes 0.26.2 8ddbe32e
kubernetes 0.26.3 c02a3818
kubernetes 0.27.0 HEAD
kubernetes 0.27.0 6cd5e746
kubernetes 0.28.0 7f477eec
kubernetes 0.29.0 HEAD
mysql 0.1.0 263e47be
mysql 0.2.0 c24a103f
mysql 0.3.0 53f2365e
@@ -163,55 +165,16 @@ tcp-balancer 0.4.2 4369b031
tcp-balancer 0.5.0 08cb7c0f
tcp-balancer 0.5.1 c02a3818
tcp-balancer 0.6.0 HEAD
tenant 1.10.0 4369b031
tenant 1.11.0 08cb7c0f
tenant 1.11.1 28c9fcd6
tenant 1.11.2 c02a3818
tenant 1.12.0 HEAD
virtual-machine 0.1.4 f2015d65
virtual-machine 0.1.5 263e47be
virtual-machine 0.2.0 c0685f43
virtual-machine 0.3.0 6c5cf5bf
virtual-machine 0.4.0 b8e33d19
virtual-machine 0.5.0 1ec10165
virtual-machine 0.6.0 4e68e65c
virtual-machine 0.7.0 e23286a3
virtual-machine 0.7.1 0ab39f20
virtual-machine 0.8.0 3fa4dd3a
virtual-machine 0.8.1 93c46161
virtual-machine 0.8.2 de19450f
virtual-machine 0.9.0 721c12a7
virtual-machine 0.9.1 93bdf411
virtual-machine 0.10.0 6130f43d
virtual-machine 0.10.2 632224a3
virtual-machine 0.11.0 4369b031
virtual-machine 0.12.0 acd4663a
virtual-machine 0.12.1 909208ba
virtual-machine 0.12.2 8ddbe32e
virtual-machine 0.12.3 c02a3818
virtual-machine 0.13.0 HEAD
tenant 1.13.0 8f1975d1
tenant 1.14.0 HEAD
virtual-machine 0.14.0 HEAD
vm-disk 0.1.0 d971f2ff
vm-disk 0.1.1 6130f43d
vm-disk 0.1.2 632224a3
vm-disk 0.2.0 4369b031
vm-disk 0.3.0 c02a3818
vm-disk 0.4.0 HEAD
vm-instance 0.1.0 1ec10165
vm-instance 0.2.0 84f3ccc0
vm-instance 0.3.0 4e68e65c
vm-instance 0.4.0 e23286a3
vm-instance 0.4.1 0ab39f20
vm-instance 0.5.0 3fa4dd3a
vm-instance 0.5.1 de19450f
vm-instance 0.6.0 721c12a7
vm-instance 0.7.0 6130f43d
vm-instance 0.7.2 632224a3
vm-instance 0.8.0 4369b031
vm-instance 0.9.0 acd4663a
vm-instance 0.10.0 909208ba
vm-instance 0.10.1 8ddbe32e
vm-instance 0.10.2 c02a3818
vm-instance 0.11.0 HEAD
vm-instance 0.12.0 HEAD
vpn 0.1.0 263e47be
vpn 0.2.0 53f2365e
vpn 0.3.0 6c5cf5bf

View File

@@ -17,10 +17,10 @@ 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.13.0
version: 0.14.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
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: 0.13.0
appVersion: 0.14.0

View File

@@ -6,14 +6,12 @@ metadata:
name: {{ include "virtual-machine.fullname" . }}
labels:
{{- include "virtual-machine.labels" . | nindent 4 }}
{{- if eq .Values.externalMethod "WholeIP" }}
annotations:
networking.cozystack.io/wholeIP: "true"
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
selector:
@@ -29,3 +27,27 @@ spec:
{{- end }}
{{- end }}
{{- end }}
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: {{ include "virtual-machine.fullname" . }}
spec:
endpointSelector:
matchLabels:
{{- include "virtual-machine.selectorLabels" . | nindent 6 }}
ingress:
- fromEntities:
- cluster
- fromEntities:
- world
{{- if eq .Values.externalMethod "PortList" }}
toPorts:
- ports:
{{- range .Values.externalPorts }}
- port: {{ quote . }}
{{- end }}
{{- end }}
egress:
- toEntities:
- world

View File

@@ -51,7 +51,7 @@ spec:
restartPolicy: Never
containers:
- name: update-resources
image: bitnami/kubectl:latest
image: docker.io/alpine/k8s:1.33.4
resources:
requests:
memory: "16Mi"
@@ -64,14 +64,14 @@ spec:
- |
{{- if $needUpdateType }}
echo "Patching VirtualMachine for instancetype update..."
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
--type merge \
-p '{"spec":{"instancetype":{"name": "{{ $instanceType }}", "revisionName": null}}}'
{{- end }}
{{- if $needUpdateProfile }}
echo "Patching VirtualMachine for preference update..."
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
--type merge \
-p '{"spec":{"preference":{"name": "{{ $instanceProfile }}", "revisionName": null}}}'
{{- end }}

View File

@@ -62,6 +62,7 @@ spec:
template:
metadata:
annotations:
policy.cozystack.io/allow-external-communication: "false"
kubevirt.io/allow-pod-bridge-network-live-migration: "true"
labels:
{{- include "virtual-machine.labels" . | nindent 8 }}

View File

@@ -19,7 +19,7 @@ spec:
backoffLimit: 1
containers:
- name: resize
image: bitnami/kubectl
image: docker.io/alpine/k8s:1.33.4
command: ["sh", "-xec"]
args:
- |

View File

@@ -17,10 +17,10 @@ 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.0
version: 0.12.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
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: 0.11.0
appVersion: 0.12.0

View File

@@ -6,14 +6,12 @@ metadata:
name: {{ include "virtual-machine.fullname" . }}
labels:
{{- include "virtual-machine.labels" . | nindent 4 }}
{{- if eq .Values.externalMethod "WholeIP" }}
annotations:
networking.cozystack.io/wholeIP: "true"
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
selector:
@@ -29,3 +27,27 @@ spec:
{{- end }}
{{- end }}
{{- end }}
---
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: {{ include "virtual-machine.fullname" . }}
spec:
endpointSelector:
matchLabels:
{{- include "virtual-machine.selectorLabels" . | nindent 6 }}
ingress:
- fromEntities:
- cluster
- fromEntities:
- world
{{- if eq .Values.externalMethod "PortList" }}
toPorts:
- ports:
{{- range .Values.externalPorts }}
- port: {{ quote . }}
{{- end }}
{{- end }}
egress:
- toEntities:
- world

View File

@@ -41,7 +41,7 @@ spec:
restartPolicy: Never
containers:
- name: update-resources
image: bitnami/kubectl:latest
image: docker.io/alpine/k8s:1.33.4
resources:
requests:
memory: "16Mi"
@@ -54,14 +54,14 @@ spec:
- |
{{- if $needUpdateType }}
echo "Patching VirtualMachine for instancetype update..."
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
--type merge \
-p '{"spec":{"instancetype":{"name": "{{ $instanceType }}", "revisionName": null}}}'
{{- end }}
{{- if $needUpdateProfile }}
echo "Patching VirtualMachine for preference update..."
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
--type merge \
-p '{"spec":{"preference":{"name": "{{ $instanceProfile }}", "revisionName": null}}}'
{{- end }}

View File

@@ -26,6 +26,7 @@ spec:
template:
metadata:
annotations:
policy.cozystack.io/allow-external-communication: "false"
kubevirt.io/allow-pod-bridge-network-live-migration: "true"
labels:
{{- include "virtual-machine.labels" . | nindent 8 }}

View File

@@ -24,15 +24,11 @@ image: pre-checks image-matchbox image-cozystack image-talos
image-cozystack:
docker buildx build -f images/cozystack/Dockerfile ../../.. \
--provenance false \
--tag $(REGISTRY)/installer:$(call settag,$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/installer:latest \
--platform linux/amd64 \
--cache-to type=inline \
--metadata-file images/installer.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
IMAGE="$(REGISTRY)/installer:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/installer.json -o json -r)" \
yq -i '.cozystack.image = strenv(IMAGE)' values.yaml
rm -f images/installer.json
@@ -45,15 +41,12 @@ image-matchbox:
test -f ../../../_out/assets/kernel-amd64 || make talos-kernel
test -f ../../../_out/assets/initramfs-metal-amd64.xz || make talos-initramfs
docker buildx build -f images/matchbox/Dockerfile ../../.. \
--provenance false \
--tag $(REGISTRY)/matchbox:$(call settag,$(TAG)) \
--tag $(REGISTRY)/matchbox:$(call settag,$(TALOS_VERSION)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/matchbox:latest \
--cache-to type=inline \
--metadata-file images/matchbox.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
echo "$(REGISTRY)/matchbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/matchbox.json -o json -r)" \
> ../../extra/bootbox/images/matchbox.tag
rm -f images/matchbox.json

View File

@@ -34,7 +34,7 @@ FROM alpine:3.22
RUN wget -O- https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.1.0
RUN apk add --no-cache make kubectl coreutils
RUN apk add --no-cache make kubectl coreutils git jq
COPY --from=builder /src/scripts /cozystack/scripts
COPY --from=builder /src/packages/core /cozystack/packages/core

View File

@@ -1,2 +1,2 @@
cozystack:
image: ghcr.io/cozystack/cozystack/installer:v0.35.2@sha256:1f1caa8b79869848e45b514c1bf29f96a196aa6cd439fa3c7212c30b0271638d
image: ghcr.io/cozystack/cozystack/installer:v0.36.0-beta.4@sha256:51cb9828af3bdceac289de3b1161625065db22535a961958530e9c751880ee96

View File

@@ -70,6 +70,12 @@ releases:
privileged: true
dependsOn: [cilium,kubeovn,cert-manager]
- name: kubeovn-plunger
releaseName: kubeovn-plunger
chart: cozy-kubeovn-plunger
namespace: cozy-kubeovn
dependsOn: [cilium,kubeovn]
- name: cozy-proxy
releaseName: cozystack
chart: cozy-cozy-proxy

View File

@@ -77,7 +77,7 @@ spec:
{{- $values = merge . $values }}
{{- end }}
{{- with index $cozyConfig.data (printf "values-%s" $x.name) }}
{{- $values = merge (fromYaml .) $values }}
{{- $values = mergeOverwrite $values (fromYaml .) }}
{{- end }}
{{- with $values }}
values:

View File

@@ -16,16 +16,11 @@ image: image-e2e-sandbox
image-e2e-sandbox:
docker buildx build -f images/e2e-sandbox/Dockerfile images/e2e-sandbox \
--provenance false \
--builder=$(BUILDER) \
--platform=$(PLATFORM) \
--tag $(REGISTRY)/e2e-sandbox:$(call settag,$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/e2e-sandbox:latest \
--cache-to type=inline \
--metadata-file images/e2e-sandbox.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
IMAGE="$(REGISTRY)/e2e-sandbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/e2e-sandbox.json -o json -r)" \
yq -i '.e2e.image = strenv(IMAGE)' values.yaml
rm -f images/e2e-sandbox.json

View File

@@ -1,2 +1,2 @@
e2e:
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.35.2@sha256:aef4672d4d6c3f28b2fcab966ff76dd6dca1917d5c22bf23aff2f37d4d906245
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.36.0-beta.4@sha256:c70ad3321fc14ca831c84bf6e7e6e5409bfe8130703173c277ca51db740c6cb3

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/matchbox:v0.35.2@sha256:83ec3d69d9e45bc964b803e6d52dc3c6902ebb1573efbc3fa1bd9adfdadf968b
ghcr.io/cozystack/cozystack/matchbox:v0.36.0-beta.4@sha256:764c547a352c9c1d0442e43cdfd0ef50b216bd7f6e5514c777b1d90c4d95da92

View File

@@ -3,4 +3,4 @@ name: etcd
description: Storage for Kubernetes clusters
icon: /logos/etcd.svg
type: application
version: 2.10.0
version: 2.10.1

View File

@@ -56,6 +56,9 @@ spec:
{{- end }}
{{- if $rawConstraints }}
{{- $rawConstraints | fromYaml | toYaml | nindent 6 }}
labelSelector:
matchLabels:
app.kubernetes.io/instance: etcd
{{- else }}
topologySpreadConstraints:
- maxSkew: 1

View File

@@ -25,7 +25,7 @@ spec:
serviceAccountName: etcd-hook
containers:
- name: kubectl
image: bitnami/kubectl:latest
image: docker.io/alpine/k8s:1.33.4
command:
- sh
args:

View File

@@ -3,4 +3,4 @@ name: ingress
description: NGINX Ingress Controller
icon: /logos/ingress-nginx.svg
type: application
version: 1.8.0
version: 1.9.0

View File

@@ -4,9 +4,13 @@
### Common parameters
| Name | Description | Type | Value |
| ---------------- | ----------------------------------------------------------------- | ----------- | ------- |
| `replicas` | Number of ingress-nginx replicas | `int` | `2` |
| `whitelist` | List of client networks | `[]*string` | `[]` |
| `clouflareProxy` | Restoring original visitor IPs when Cloudflare proxied is enabled | `bool` | `false` |
| Name | Description | Type | Value |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ------- |
| `replicas` | Number of ingress-nginx replicas | `int` | `2` |
| `whitelist` | List of client networks | `[]*string` | `[]` |
| `cloudflareProxy` | Restoring original visitor IPs when Cloudflare proxied is enabled | `bool` | `false` |
| `resources` | Explicit CPU and memory configuration for each ingress-nginx 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` |

View File

@@ -15,14 +15,21 @@ spec:
name: cozystack-system
namespace: cozy-system
version: '>= 0.0.0-0'
install:
remediation:
retries: -1
upgrade:
remediation:
retries: -1
interval: 1m0s
timeout: 5m0s
timeout: 10m0s
values:
ingress-nginx:
fullnameOverride: {{ trimPrefix "tenant-" .Release.Namespace }}-ingress
controller:
replicaCount: {{ .Values.replicas }}
ingressClass: {{ .Release.Namespace }}
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 10 }}
ingressClassResource:
name: {{ .Release.Namespace }}
controllerValue: k8s.io/ingress-nginx-{{ .Release.Namespace }}
@@ -43,12 +50,12 @@ spec:
type: LoadBalancer
externalTrafficPolicy: Local
{{- end }}
{{- if or .Values.whitelist .Values.clouflareProxy }}
{{- if or .Values.whitelist .Values.cloudflareProxy }}
config:
{{- with .Values.whitelist }}
whitelist-source-range: "{{ join "," . }}"
{{- end }}
{{- if .Values.clouflareProxy }}
{{- if .Values.cloudflareProxy }}
set_real_ip_from: "{{ include "ingress.cloudflare-ips" . }}"
use-forwarded-headers: "true"
server-snippet: "real_ip_header CF-Connecting-IP;"

View File

@@ -2,7 +2,7 @@
"title": "Chart Values",
"type": "object",
"properties": {
"clouflareProxy": {
"cloudflareProxy": {
"description": "Restoring original visitor IPs when Cloudflare proxied is enabled",
"type": "boolean",
"default": false
@@ -12,6 +12,53 @@
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for each ingress-nginx 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"
]
},
"whitelist": {
"description": "List of client networks",
"type": "array",

View File

@@ -11,5 +11,16 @@ replicas: 2
## - "10.100.0.0/16"
whitelist: []
## @param clouflareProxy {bool} Restoring original visitor IPs when Cloudflare proxied is enabled
clouflareProxy: false
## @param cloudflareProxy {bool} Restoring original visitor IPs when Cloudflare proxied is enabled
cloudflareProxy: false
## @param resources {*resources} Explicit CPU and memory configuration for each ingress-nginx 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
## Example:
## resources:
## cpu: 4000m
## memory: 4Gi
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"

View File

@@ -10,16 +10,11 @@ generate:
image:
docker buildx build images/grafana \
--provenance false \
--builder=$(BUILDER) \
--platform=$(PLATFORM) \
--tag $(REGISTRY)/grafana:$(call settag,$(GRAFANA_TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/grafana:latest \
--cache-to type=inline \
--metadata-file images/grafana.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
echo "$(REGISTRY)/grafana:$(call settag,$(GRAFANA_TAG))@$$(yq e '."containerimage.digest"' images/grafana.json -o json -r)" \
> images/grafana.tag
rm -f images/grafana.json

View File

@@ -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.0
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

View File

@@ -4,19 +4,55 @@
### Common parameters
| Name | Description | Type | Value |
| ---------------------- | ------------------------------------------------------------------------------------------------------ | ------------------- | -------- |
| `host` | The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host). | `*string` | `""` |
| `topology` | The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client) | `string` | `Simple` |
| `replicationFactor` | Replication factor: number of replicas for each volume in the SeaweedFS cluster. | `int` | `2` |
| `replicas` | Number of replicas | `int` | `2` |
| `size` | Persistent Volume size | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data | `*string` | `""` |
| `zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{...}` |
| `zones[name].replicas` | Number of replicas in the zone | `int` | `0` |
| `zones[name].size` | Zone storage size | `quantity` | `""` |
| `filer` | Filer service configuration | `*object` | `{}` |
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
| Name | Description | Type | Value |
| ------------------- | ------------------------------------------------------------------------------------------------------ | --------- | -------- |
| `host` | The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host). | `*string` | `""` |
| `topology` | The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client) | `string` | `Simple` |
| `replicationFactor` | Replication factor: number of replicas for each volume in the SeaweedFS cluster. | `int` | `2` |
### SeaweedFS Components Configuration
| Name | Description | Type | Value |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
| `db` | Database Configuration | `object` | `{}` |
| `db.replicas` | Number of database replicas | `*int` | `2` |
| `db.size` | Persistent Volume size | `*quantity` | `10Gi` |
| `db.storageClass` | StorageClass used to store the data | `*string` | `""` |
| `db.resources` | Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `db.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
| `db.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
| `master` | Master service configuration | `*object` | `{}` |
| `master.replicas` | Number of master replicas | `*int` | `3` |
| `master.resources` | Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `master.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
| `master.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
| `filer` | Filer service configuration | `*object` | `{}` |
| `filer.replicas` | Number of filer replicas | `*int` | `2` |
| `filer.resources` | Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `filer.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
| `filer.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
| `volume` | Volume service configuration | `*object` | `{}` |
| `volume.replicas` | Number of volume replicas | `*int` | `2` |
| `volume.size` | Persistent Volume size | `*quantity` | `10Gi` |
| `volume.storageClass` | StorageClass used to store the data | `*string` | `""` |
| `volume.resources` | Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `volume.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
| `volume.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` |
| `volume.zones.replicas` | Number of replicas in the zone | `*int` | `null` |
| `volume.zones.size` | Zone storage size | `*quantity` | `null` |
| `s3` | S3 service configuration | `*object` | `{}` |
| `s3.replicas` | Number of s3 replicas | `*int` | `2` |
| `s3.resources` | Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `s3.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
| `s3.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.35.2@sha256:93ffb888755b2914fbbfc402a1daf450d628bced71629c53627b1b759740df72
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.0-beta.4@sha256:24451989b15b6801b33ad355a5507307d0333bf9afd240f1db0aca9c92f6b2ad

View File

@@ -9,11 +9,11 @@
{{- fail "Invalid value for .Values.replicationFactor. Must be at least 1." }}
{{- end }}
{{- if eq .Values.topology "MultiZone" }}
{{- if (eq (len .Values.zones) 0) }}
{{- if (eq (len .Values.volume.zones) 0) }}
{{- fail "Zones must be defined for MultiZone topology." }}
{{- end }}
{{- if and (hasKey .Values "zones") (gt (int .Values.replicationFactor) (len .Values.zones)) }}
{{- fail "replicationFactor must be less than or equal to the number of zones defined in .Values.zones." }}
{{- if and (hasKey .Values.volume "zones") (gt (int .Values.replicationFactor) (len .Values.volume.zones)) }}
{{- fail "replicationFactor must be less than or equal to the number of zones defined in .Values.volume.zones." }}
{{- end }}
{{- end }}
@@ -51,11 +51,24 @@ spec:
name: cozystack-system
namespace: cozy-system
version: '>= 0.0.0-0'
install:
remediation:
retries: -1
upgrade:
remediation:
retries: -1
interval: 1m0s
timeout: 5m0s
timeout: 10m0s
values:
global:
serviceAccountName: "{{ .Release.Namespace }}-seaweedfs"
db:
replicas: {{ .Values.db.replicas }}
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.db.resourcesPreset .Values.db.resources $) | nindent 8 }}
size: {{ .Values.db.size }}
{{- with .Values.db.storageClass }}
storageClass: {{ . }}
{{- end }}
seaweedfs:
master:
{{ if eq .Values.topology "Simple" }}
@@ -63,36 +76,25 @@ spec:
{{- else if eq .Values.topology "MultiZone" }}
defaultReplicaPlacement: "{{ sub .Values.replicationFactor 1 }}00"
{{- end }}
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
replicas: {{ .Values.master.replicas }}
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.master.resourcesPreset .Values.master.resources $) | nindent 10 }}
volume:
{{ if eq .Values.topology "MultiZone" }}
enabled: false
{{- end }}
replicas: {{ .Values.replicas }}
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
replicas: {{ .Values.volume.replicas }}
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.volume.resourcesPreset .Values.volume.resources $) | nindent 10 }}
dataDirs:
- name: data1
type: "persistentVolumeClaim"
size: "{{ .Values.size }}"
{{- with .Values.storageClass }}
size: "{{ .Values.volume.size }}"
{{- with .Values.volume.storageClass }}
storageClass: {{ . }}
{{- end }}
maxVolumes: 0
{{ if eq .Values.topology "MultiZone" }}
volumes:
{{- range $zoneName, $zone := .Values.zones }}
{{- range $zoneName, $zone := .Values.volume.zones }}
{{ $zoneName }}:
{{ with $zone.replicas }}
replicas: {{ . }}
@@ -107,8 +109,8 @@ spec:
{{- end }}
{{- if $zone.storageClass }}
storageClass: {{ $zone.storageClass }}
{{- else if $.Values.storageClass }}
storageClass: {{ $.Values.storageClass }}
{{- else if $.Values.volume.storageClass }}
storageClass: {{ $.Values.volume.storageClass }}
{{- end }}
maxVolumes: 0
nodeSelector: |
@@ -124,13 +126,7 @@ spec:
{{- end }}
s3:
domainName: {{ .Values.host | default (printf "s3.%s" $host) }}
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.filer.resourcesPreset .Values.filer.resources $) | nindent 10 }}
s3:
ingress:
className: {{ $ingress }}
@@ -144,6 +140,7 @@ spec:
- hosts:
- {{ .Values.host | default (printf "s3.%s" $host) }}
secretName: {{ .Release.Name }}-s3-ingress-tls
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.s3.resourcesPreset .Values.s3.resources $) | nindent 10 }}
cosi:
driverName: "{{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io"
bucketClassName: "{{ .Release.Namespace }}"
@@ -160,8 +157,8 @@ kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-master
spec:
replicas: 3
minReplicas: 2
replicas: {{ .Values.master.replicas }}
minReplicas: {{ div .Values.master.replicas 2 | add1 }}
kind: seaweedfs
type: master
selector:
@@ -174,7 +171,7 @@ kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-filer
spec:
replicas: 2
replicas: {{ .Values.filer.replicas }}
minReplicas: 1
kind: seaweedfs
type: filer
@@ -182,27 +179,47 @@ spec:
app.kubernetes.io/component: filer
app.kubernetes.io/name: seaweedfs
version: {{ $.Chart.Version }}
{{ if eq .Values.topology "Simple" }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-volume
spec:
replicas: {{ .Values.replicas }}
minReplicas: {{ div .Values.replicas 2 | add1 }}
replicas: {{ .Values.volume.replicas }}
minReplicas: 1
kind: seaweedfs
type: volume
selector:
app.kubernetes.io/component: volume
app.kubernetes.io/name: seaweedfs
version: {{ $.Chart.Version }}
{{- else if eq .Values.topology "MultiZone" }}
{{- range $zoneName, $zoneSpec := .Values.volume.zones }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-volume-{{ $zoneName }}
spec:
replicas: {{ default $.Values.volume.replicas $zoneSpec.replicas }}
minReplicas: 1
kind: seaweedfs
type: volume
selector:
app.kubernetes.io/component: volume-{{ $zoneName }}
app.kubernetes.io/name: seaweedfs
version: {{ $.Chart.Version }}
{{- end }}
{{- end }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-db
spec:
replicas: 2
replicas: {{ .Values.db.replicas }}
minReplicas: 1
kind: seaweedfs
type: postgres
@@ -210,4 +227,18 @@ spec:
cnpg.io/cluster: seaweedfs-db
cnpg.io/podRole: instance
version: {{ $.Chart.Version }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-s3
spec:
replicas: {{ .Values.s3.replicas }}
minReplicas: 1
kind: seaweedfs
type: s3
selector:
app.kubernetes.io/component: s3
app.kubernetes.io/name: seaweedfs
version: {{ $.Chart.Version }}
{{- end }}

View File

@@ -1,68 +0,0 @@
{{- if not (eq .Values.topology "Client") }}
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: {{ .Release.Name }}-filer
spec:
targetRef:
apiVersion: apps/v1
kind: StatefulSet
name: {{ .Release.Name }}-filer
updatePolicy:
updateMode: Auto
resourcePolicy:
containerPolicies:
- containerName: seaweedfs
minAllowed:
cpu: 25m
memory: 64Mi
maxAllowed:
cpu: "1"
memory: 2048Mi
---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: {{ .Release.Name }}-master
spec:
targetRef:
apiVersion: apps/v1
kind: StatefulSet
name: {{ .Release.Name }}-master
updatePolicy:
updateMode: Auto
resourcePolicy:
containerPolicies:
- containerName: seaweedfs
minAllowed:
cpu: 25m
memory: 64Mi
maxAllowed:
cpu: "1"
memory: 2048Mi
---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: {{ .Release.Name }}-volume
spec:
targetRef:
apiVersion: apps/v1
kind: StatefulSet
name: {{ .Release.Name }}-volume
updatePolicy:
updateMode: Auto
resourcePolicy:
containerPolicies:
- containerName: seaweedfs
minAllowed:
cpu: 25m
memory: 64Mi
maxAllowed:
cpu: "1"
memory: 2048Mi
{{- end }}

View File

@@ -2,14 +2,108 @@
"title": "Chart Values",
"type": "object",
"properties": {
"db": {
"description": "Database Configuration",
"type": "object",
"default": {
"replicas": 2,
"resources": {},
"resourcesPreset": "small",
"size": "10Gi",
"storageClass": ""
},
"required": [
"resources",
"resourcesPreset"
],
"properties": {
"replicas": {
"description": "Number of database replicas",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "The number of CPU cores allocated",
"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": "The amount of memory allocated",
"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",
"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"
}
}
},
"filer": {
"description": "Filer service configuration",
"type": "object",
"default": {
"grpcHost": "",
"grpcPort": 443,
"replicas": 2,
"resources": {},
"resourcesPreset": "small",
"whitelist": {}
},
"required": [
"resources",
"resourcesPreset"
],
"properties": {
"grpcHost": {
"description": "The hostname used to expose or access the filer service externally.",
@@ -20,6 +114,58 @@
"type": "integer",
"default": 443
},
"replicas": {
"description": "Number of filer replicas",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "The number of CPU cores allocated",
"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": "The amount of memory allocated",
"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"
]
},
"whitelist": {
"description": "A list of IP addresses or CIDR ranges that are allowed to access the filer service.",
"type": "array",
@@ -34,33 +180,144 @@
"description": "The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host).",
"type": "string"
},
"replicas": {
"description": "Number of replicas",
"type": "integer",
"default": 2
"master": {
"description": "Master service configuration",
"type": "object",
"default": {
"replicas": 3,
"resources": {},
"resourcesPreset": "small"
},
"required": [
"resources",
"resourcesPreset"
],
"properties": {
"replicas": {
"description": "Number of master replicas",
"type": "integer",
"default": 3
},
"resources": {
"description": "Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "The number of CPU cores allocated",
"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": "The amount of memory allocated",
"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"
]
}
}
},
"replicationFactor": {
"description": "Replication factor: number of replicas for each volume in the SeaweedFS cluster.",
"type": "integer",
"default": 2
},
"size": {
"description": "Persistent Volume size",
"default": "10Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
"s3": {
"description": "S3 service configuration",
"type": "object",
"default": {
"replicas": 2,
"resources": {},
"resourcesPreset": "small"
},
"required": [
"resources",
"resourcesPreset"
],
"x-kubernetes-int-or-string": true
},
"storageClass": {
"description": "StorageClass used to store the data",
"type": "string"
"properties": {
"replicas": {
"description": "Number of s3 replicas",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "The number of CPU cores allocated",
"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": "The amount of memory allocated",
"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"
]
}
}
},
"topology": {
"description": "The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client)",
@@ -72,33 +329,117 @@
"Client"
]
},
"zones": {
"description": "A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.",
"volume": {
"description": "Volume service configuration",
"type": "object",
"default": {},
"additionalProperties": {
"type": "object",
"required": [
"replicas",
"size"
],
"properties": {
"replicas": {
"description": "Number of replicas in the zone",
"type": "integer"
},
"size": {
"description": "Zone storage size",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
"default": {
"replicas": 2,
"resources": {},
"resourcesPreset": "small",
"size": "10Gi",
"storageClass": "",
"zones": {}
},
"required": [
"resources",
"resourcesPreset"
],
"properties": {
"replicas": {
"description": "Number of volume replicas",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "The number of CPU cores allocated",
"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": "The amount of memory allocated",
"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",
"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"
},
"zones": {
"description": "A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.",
"type": "object",
"default": {},
"additionalProperties": {
"type": "object",
"properties": {
"replicas": {
"description": "Number of replicas in the zone",
"type": "integer"
},
{
"type": "string"
"size": {
"description": "Zone 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
}
],
"x-kubernetes-int-or-string": true
}
}
}
}

View File

@@ -4,42 +4,93 @@
host: ""
## @param topology {string enum:"Simple,MultiZone,Client"} The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client)
##
topology: Simple
## @param replicationFactor {int} Replication factor: number of replicas for each volume in the SeaweedFS cluster.
replicationFactor: 2
## @param replicas {int} Number of replicas
## @section SeaweedFS Components Configuration
##
replicas: 2
## @param size {quantity} Persistent Volume size
size: 10Gi
## @param storageClass {*string} StorageClass used to store the data
storageClass: ""
## @param db {db} Database Configuration
db:
## @field db.replicas {*int} Number of database replicas
## @field db.size {*quantity} Persistent Volume size
## @field db.storageClass {*string} StorageClass used to store the data
replicas: 2
size: "10Gi"
storageClass: ""
## @field db.resources {resources} Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied.
## @field resources {*resources} Resource configuration for etcd
## @field resources.cpu {*quantity} The number of CPU cores allocated
## @field resources.memory {*quantity} The amount of memory allocated
## e.g:
## resources:
## cpu: 4000m
## memory: 4Gi
##
## @field db.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`.
resources: {}
resourcesPreset: "small"
## @param zones {map[string]zone} A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.
## @field zone.replicas {int} Number of replicas in the zone
## @field zone.size {quantity} Zone storage size
## Example:
## zones:
## dc1:
## replicas: 2
## size: 10Gi
## dc2:
## replicas: 2
## size: 10Gi
## dc3:
## replicas: 2
## size: 10Gi
zones: {}
## @param master {*master} Master service configuration
master:
## @field master.replicas {*int} Number of master replicas
## @field master.resources {resources} Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied.
## @field master.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`.
replicas: 3
resources: {}
resourcesPreset: "small"
## @param filer {*filer} Filer service configuration
## @field filer.grpcHost {*string} The hostname used to expose or access the filer service externally.
## @field filer.grpcPort {*int} The port used to access the filer service externally.
## TODO: select a more appropriate type after resolving https://github.com/cozystack/cozyvalues-gen/issues/4
## @field filer.whitelist {[]*string} A list of IP addresses or CIDR ranges that are allowed to access the filer service.
filer:
## @field filer.replicas {*int} Number of filer replicas
## @field filer.resources {resources} Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied.
## @field filer.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`.
replicas: 2
resources: {}
resourcesPreset: "small"
## @field filer.grpcHost {*string} The hostname used to expose or access the filer service externally.
## @field filer.grpcPort {*int} The port used to access the filer service externally.
## @field filer.whitelist {[]*string} A list of IP addresses or CIDR ranges that are allowed to access the filer service.
grpcHost: ""
grpcPort: 443
whitelist: []
## @param volume {*volume} Volume service configuration
volume:
## @field volume.replicas {*int} Number of volume replicas
## @field volume.size {*quantity} Persistent Volume size
## @field volume.storageClass {*string} StorageClass used to store the data
## @field volume.resources {resources} Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied.
## @field volume.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`.
replicas: 2
size: 10Gi
storageClass: ""
resources: {}
resourcesPreset: "small"
## @field volume.zones {map[string]zone} A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.
## @field zone.replicas {*int} Number of replicas in the zone
## @field zone.size {*quantity} Zone storage size
## Example:
## zones:
## dc1:
## replicas: 2
## size: 10Gi
## dc2:
## replicas: 2
## size: 10Gi
## dc3:
## replicas: 2
## size: 10Gi
zones: {}
## @param s3 {*s3} S3 service configuration
s3:
## @field s3.replicas {*int} Number of s3 replicas
## @field s3.resources {resources} Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied.
## @field s3.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`.
replicas: 2
resources: {}
resourcesPreset: "small"

View File

@@ -16,7 +16,8 @@ etcd 2.7.0 632224a3
etcd 2.8.0 4369b031
etcd 2.9.0 8ddbe32e
etcd 2.9.1 c02a3818
etcd 2.10.0 HEAD
etcd 2.10.0 7f477eec
etcd 2.10.1 HEAD
info 1.0.0 93bdf411
info 1.0.1 632224a3
info 1.1.0 c02a3818
@@ -29,7 +30,8 @@ ingress 1.4.0 fd240701
ingress 1.5.0 93bdf411
ingress 1.6.0 632224a3
ingress 1.7.0 c02a3818
ingress 1.8.0 HEAD
ingress 1.8.0 8f1975d1
ingress 1.9.0 HEAD
monitoring 1.0.0 d7cfa53c
monitoring 1.1.0 25221fdc
monitoring 1.2.0 f81be075
@@ -62,4 +64,5 @@ seaweedfs 0.3.0 45a7416c
seaweedfs 0.4.0 632224a3
seaweedfs 0.4.1 8c86905b
seaweedfs 0.5.0 9584e5f5
seaweedfs 0.6.0 HEAD
seaweedfs 0.6.0 8f1975d1
seaweedfs 0.7.0 HEAD

View File

@@ -172,3 +172,17 @@
{{- $xmsMi := min (div $memoryLimitInt 4194304) (div $memoryRequestInt 1048576) }}
{{- printf `-Xms%dm -Xmx%dm` $xmsMi $xmxMi }}
{{- end }}
{{- define "cozy-lib.resources.flatten" -}}
{{- $out := dict -}}
{{- $res := include "cozy-lib.resources.sanitize" . | fromYaml -}}
{{- range $section, $values := $res }}
{{- range $k, $v := $values }}
{{- $key := printf "%s.%s" $section $k }}
{{- if ne $key "limits.storage" }}
{{- $_ := set $out $key $v }}
{{- end }}
{{- end }}
{{- end }}
{{- $out | toYaml }}
{{- end }}

View File

@@ -12,16 +12,11 @@ image: image-s3manager
image-s3manager:
docker buildx build images/s3manager \
--provenance false \
--builder=$(BUILDER) \
--platform=$(PLATFORM) \
--tag $(REGISTRY)/s3manager:$(call settag,$(S3MANAGER_TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/s3manager:latest \
--cache-to type=inline \
--metadata-file images/s3manager.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
echo "$(REGISTRY)/s3manager:$(call settag,$(S3MANAGER_TAG))@$$(yq e '."containerimage.digest"' images/s3manager.json -o json -r)" \
> images/s3manager.tag
rm -f images/s3manager.json

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:084c6e1665762cae38bac235c79c00d4ec61b5e73ad46ebf7eed596b2c5fcd0a
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:b9a401defb90a822087e50e7ab6afd9b4db7e71728030f92c7d320ac46889053

View File

@@ -17,17 +17,12 @@ update:
image:
docker buildx build images/cilium \
--provenance false \
--builder=$(BUILDER) \
--platform=$(PLATFORM) \
--tag $(REGISTRY)/cilium:$(call settag,$(CILIUM_TAG)) \
--tag $(REGISTRY)/cilium:$(call settag,$(CILIUM_TAG)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/cilium:latest \
--cache-to type=inline \
--metadata-file images/cilium.json \
--push=$(PUSH) \
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
--load=$(LOAD)
$(BUILDX_ARGS)
REPOSITORY="$(REGISTRY)/cilium" \
yq -i '.cilium.image.repository = strenv(REPOSITORY)' values.yaml
TAG=$(call settag,$(CILIUM_TAG)) \

View File

@@ -0,0 +1,3 @@
apiVersion: v2
name: cozy-coredns
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

View File

@@ -0,0 +1,10 @@
export NAME=coredns
export NAMESPACE=kube-system
include ../../../scripts/package.mk
update:
rm -rf charts
helm repo add cozy-coredns https://coredns.github.io/helm
helm repo update cozy-coredns
helm pull cozy-coredns/coredns --untar --untardir charts

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
OWNERS
*.tests

View File

@@ -0,0 +1,24 @@
annotations:
artifacthub.io/changes: |
- kind: changed
description: Bump to CoreDNS 1.12.3
apiVersion: v2
appVersion: 1.12.3
description: CoreDNS is a DNS server that chains plugins and provides Kubernetes DNS
Services
home: https://coredns.io
icon: https://coredns.io/images/CoreDNS_Colour_Horizontal.png
keywords:
- coredns
- dns
- kubedns
maintainers:
- name: mrueg
- name: haad
- name: hagaibarel
- name: shubham-cmyk
name: coredns
sources:
- https://github.com/coredns/coredns
type: application
version: 1.43.2

View File

@@ -0,0 +1,316 @@
# CoreDNS
[CoreDNS](https://coredns.io/) is a DNS server that chains plugins and provides DNS Services
# TL;DR;
```console
$ helm repo add coredns https://coredns.github.io/helm
$ helm --namespace=kube-system install coredns coredns/coredns
```
## Introduction
This chart bootstraps a [CoreDNS](https://github.com/coredns/coredns) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. This chart will provide DNS Services and can be deployed in multiple configuration to support various scenarios listed below:
- CoreDNS as a cluster dns service and a drop-in replacement for Kube/SkyDNS. This is the default mode and CoreDNS is deployed as cluster-service in kube-system namespace. This mode is chosen by setting `isClusterService` to true.
- CoreDNS as an external dns service. In this mode CoreDNS is deployed as any kubernetes app in user specified namespace. The CoreDNS service can be exposed outside the cluster by using using either the NodePort or LoadBalancer type of service. This mode is chosen by setting `isClusterService` to false.
- CoreDNS as an external dns provider for kubernetes federation. This is a sub case of 'external dns service' which uses etcd plugin for CoreDNS backend. This deployment mode as a dependency on `etcd-operator` chart, which needs to be pre-installed.
## Prerequisites
- Kubernetes 1.10 or later
## Installing the Chart
The chart can be installed as follows:
```console
$ helm repo add coredns https://coredns.github.io/helm
$ helm --namespace=kube-system install coredns coredns/coredns
```
The command deploys CoreDNS on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists various ways to override default configuration during deployment.
> **Tip**: List all releases using `helm list --all-namespaces`
## OCI installing
The chart can also be installed using the following:
```console
$ helm --namespace=kube-system install coredns oci://ghcr.io/coredns/charts/coredns --version 1.38.0
```
The command deploys the `1.38.0` version of CoreDNS on the Kubernetes cluster in the default configuration.
## Helm Unit Testing & Debugging Guide
This document explains how to write, run, and debug Helm unit tests for this chart using [helm-unittest](https://github.com/helm-unittest/helm-unittest).
---
### Prerequisites
Install the Helm unittest plugin:
```bash
helm plugin install https://github.com/helm-unittest/helm-unittest
```
### Running Unit Tests
Run all unit tests in the chart folder (e.g., `./coredns`):
```bash
helm unittest ./coredns
```
To output results in **JUnit XML** format:
```bash
mkdir -p test-results
helm unittest --strict \
--output-type JUnit \
--output-file test-results/helm-unittest-report.xml \
./coredns
```
---
### Debugging Helm Charts
Render the chart templates with real values to debug:
```bash
helm template ./coredns --debug
```
## YAML Intellisense in VS Code
Add this line at the top of your unit test YAML files for schema validation and autocompletion in VS Code:
```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
```
This improves YAML editing and error highlighting for test definitions.
## Uninstalling the Chart
To uninstall/delete the `coredns` deployment:
```console
$ helm uninstall coredns
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
| Parameter | Description | Default |
| :--------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- |
| `image.repository` | The image repository to pull from | coredns/coredns |
| `image.tag` | The image tag to pull from (derived from Chart.yaml) | `` |
| `image.pullPolicy` | Image pull policy | IfNotPresent |
| `image.pullSecrets` | Specify container image pull secrets | `[]` |
| `replicaCount` | Number of replicas | 1 |
| `resources.limits.cpu` | Container maximum CPU | `100m` |
| `resources.limits.memory` | Container maximum memory | `128Mi` |
| `resources.requests.cpu` | Container requested CPU | `100m` |
| `resources.requests.memory` | Container requested memory | `128Mi` |
| `serviceType` | Kubernetes Service type | `ClusterIP` |
| `prometheus.service.enabled` | Set this to `true` to create Service for Prometheus metrics | `false` |
| `prometheus.service.annotations` | Annotations to add to the metrics Service | `{prometheus.io/scrape: "true", prometheus.io/port: "9153"}` |
| `prometheus.service.selector` | Pod selector | `{}` |
| `prometheus.monitor.enabled` | Set this to `true` to create ServiceMonitor for Prometheus operator | `false` |
| `prometheus.monitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | {} |
| `prometheus.monitor.namespace` | Selector to select which namespaces the Endpoints objects are discovered from. | `""` |
| `prometheus.monitor.interval` | Scrape interval for polling the metrics endpoint. (E.g. "30s") | `""` |
| `prometheus.monitor.selector` | Service selector | `{}` |
| `service.clusterIP` | IP address to assign to service | `""` |
| `service.clusterIPs` | IP addresses to assign to service | `[]` |
| `service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` |
| `service.externalIPs` | External IP addresses | [] |
| `service.externalTrafficPolicy` | Enable client source IP preservation | [] |
| `service.ipFamilyPolicy` | Service dual-stack policy | `""` |
| `service.annotations` | Annotations to add to service | {} |
| `service.selector` | Pod selector | `{}` |
| `service.trafficDistribution` | Service traffic routing strategy | |
| `serviceAccount.create` | If true, create & use serviceAccount | false |
| `serviceAccount.name` | If not set & create is true, use template fullname | |
| `rbac.create` | If true, create & use RBAC resources | true |
| `rbac.pspEnable` | Specifies whether a PodSecurityPolicy should be created. | `false` |
| `isClusterService` | Specifies whether chart should be deployed as cluster-service or normal k8s app. | true |
| `priorityClassName` | Name of Priority Class to assign pods | `""` |
| `securityContext` | securityContext definition for pods | capabilities.add.NET_BIND_SERVICE |
| `servers` | Configuration for CoreDNS and plugins | See values.yml |
| `livenessProbe.enabled` | Enable/disable the Liveness probe | `true` |
| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `60` |
| `livenessProbe.periodSeconds` | How often to perform the probe | `10` |
| `livenessProbe.timeoutSeconds` | When the probe times out | `5` |
| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` |
| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
| `readinessProbe.enabled` | Enable/disable the Readiness probe | `true` |
| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | `30` |
| `readinessProbe.periodSeconds` | How often to perform the probe | `10` |
| `readinessProbe.timeoutSeconds` | When the probe times out | `5` |
| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` |
| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
| `affinity` | Affinity settings for pod assignment | {} |
| `nodeSelector` | Node labels for pod assignment | {} |
| `tolerations` | Tolerations for pod assignment | [] |
| `zoneFiles` | Configure custom Zone files | [] |
| `extraContainers` | Optional array of sidecar containers | [] |
| `extraVolumes` | Optional array of volumes to create | [] |
| `extraVolumeMounts` | Optional array of volumes to mount inside the CoreDNS container | [] |
| `extraSecrets` | Optional array of secrets to mount inside the CoreDNS container | [] |
| `env` | Optional array of environment variables for CoreDNS container | [] |
| `customLabels` | Optional labels for Deployment(s), Pod, Service, ServiceMonitor objects | {} |
| `customAnnotations` | Optional annotations for Deployment(s), Pod, Service, ServiceMonitor objects |
| `rollingUpdate.maxUnavailable` | Maximum number of unavailable replicas during rolling update | `1` |
| `rollingUpdate.maxSurge` | Maximum number of pods created above desired number of pods | `25%` |
| `podDisruptionBudget` | Optional PodDisruptionBudget | {} |
| `podAnnotations` | Optional Pod only Annotations | {} |
| `terminationGracePeriodSeconds` | Optional duration in seconds the pod needs to terminate gracefully. | 30 |
| `hpa.enabled` | Enable Hpa autoscaler instead of proportional one | `false` |
| `hpa.minReplicas` | Hpa minimum number of CoreDNS replicas | `1` |
| `hpa.maxReplicas` | Hpa maximum number of CoreDNS replicas | `2` |
| `hpa.metrics` | Metrics definitions used by Hpa to scale up and down | {} |
| `autoscaler.enabled` | Optionally enabled a cluster-proportional-autoscaler for CoreDNS | `false` |
| `autoscaler.coresPerReplica` | Number of cores in the cluster per CoreDNS replica | `256` |
| `autoscaler.nodesPerReplica` | Number of nodes in the cluster per CoreDNS replica | `16` |
| `autoscaler.min` | Min size of replicaCount | 0 |
| `autoscaler.max` | Max size of replicaCount | 0 (aka no max) |
| `autoscaler.includeUnschedulableNodes` | Should the replicas scale based on the total number or only schedulable nodes | `false` |
| `autoscaler.preventSinglePointFailure` | If true does not allow single points of failure to form | `true` |
| `autoscaler.customFlags` | A list of custom flags to pass into cluster-proportional-autoscaler | (no args) |
| `autoscaler.image.repository` | The image repository to pull autoscaler from | registry.k8s.io/cpa/cluster-proportional-autoscaler |
| `autoscaler.image.tag` | The image tag to pull autoscaler from | `1.8.5` |
| `autoscaler.image.pullPolicy` | Image pull policy for the autoscaler | IfNotPresent |
| `autoscaler.image.pullSecrets` | Specify container image pull secrets | `[]` |
| `autoscaler.priorityClassName` | Optional priority class for the autoscaler pod. `priorityClassName` used if not set. | `""` |
| `autoscaler.affinity` | Affinity settings for pod assignment for autoscaler | {} |
| `autoscaler.nodeSelector` | Node labels for pod assignment for autoscaler | {} |
| `autoscaler.tolerations` | Tolerations for pod assignment for autoscaler | [] |
| `autoscaler.resources.limits.cpu` | Container maximum CPU for cluster-proportional-autoscaler | `20m` |
| `autoscaler.resources.limits.memory` | Container maximum memory for cluster-proportional-autoscaler | `10Mi` |
| `autoscaler.resources.requests.cpu` | Container requested CPU for cluster-proportional-autoscaler | `20m` |
| `autoscaler.resources.requests.memory` | Container requested memory for cluster-proportional-autoscaler | `10Mi` |
| `autoscaler.configmap.annotations` | Annotations to add to autoscaler config map. For example to stop CI renaming them | {} |
| `autoscaler.livenessProbe.enabled` | Enable/disable the Liveness probe | `true` |
| `autoscaler.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `10` |
| `autoscaler.livenessProbe.periodSeconds` | How often to perform the probe | `5` |
| `autoscaler.livenessProbe.timeoutSeconds` | When the probe times out | `5` |
| `autoscaler.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `3` |
| `autoscaler.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
| `autoscaler.extraContainers` | Optional array of sidecar containers | [] |
| `deployment.enabled` | Optionally disable the main deployment and its respective resources. | `true` |
| `deployment.name` | Name of the deployment if `deployment.enabled` is true. Otherwise the name of an existing deployment for the autoscaler or HPA to target. | `""` |
| `deployment.annotations` | Annotations to add to the main deployment | `{}` |
| `deployment.selector` | Pod selector | `{}` |
| `clusterRole.nameOverride` | ClusterRole name override | |
See `values.yaml` for configuration notes. Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
```console
$ helm install coredns \
coredns/coredns \
--set rbac.create=false
```
The above command disables automatic creation of RBAC rules.
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
```console
$ helm install coredns coredns/coredns -f values.yaml
```
> **Tip**: You can use the default [values.yaml](/charts/coredns/values.yaml)
## Caveats
The chart will automatically determine which protocols to listen on based on
the protocols you define in your zones. This means that you could potentially
use both "TCP" and "UDP" on a single port.
Some cloud environments like "GCE" or "Azure container service" cannot
create external loadbalancers with both "TCP" and "UDP" protocols. So
When deploying CoreDNS with `serviceType="LoadBalancer"` on such cloud
environments, make sure you do not attempt to use both protocols at the same
time.
## Autoscaling
By setting `autoscaler.enabled = true` a
[cluster-proportional-autoscaler](https://github.com/kubernetes-incubator/cluster-proportional-autoscaler)
will be deployed. This will default to a coredns replica for every 256 cores, or
16 nodes in the cluster. These can be changed with `autoscaler.coresPerReplica`
and `autoscaler.nodesPerReplica`. When cluster is using large nodes (with more
cores), `coresPerReplica` should dominate. If using small nodes,
`nodesPerReplica` should dominate.
This also creates a ServiceAccount, ClusterRole, and ClusterRoleBinding for
the autoscaler deployment.
`replicaCount` is ignored if this is enabled.
By setting `hpa.enabled = true` a [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)
is enabled for Coredns deployment. This can scale number of replicas based on meitrics
like CpuUtilization, MemoryUtilization or Custom ones.
## Adopting existing CoreDNS resources
If you do not want to delete the existing CoreDNS resources in your cluster, you can adopt the resources into a release as of Helm 3.2.0.
You will also need to annotate and label your existing resources to allow Helm to assume control of them. See: https://github.com/helm/helm/pull/7649
```
annotations:
meta.helm.sh/release-name: your-release-name
meta.helm.sh/release-namespace: your-release-namespace
labels:
app.kubernetes.io/managed-by: Helm
```
Once you have annotated and labeled all the resources this chart specifies, you may need to locally template the chart and compare against existing manifest to ensure there are no changes/diffs.s If
you have been careful this should not diff and leave all the resources unmodified and now under management of helm.
Some values to investigate to help adopt your existing manifests to the Helm release are:
- k8sAppLabelOverride
- service.name
- customLabels
In some cases, you will need to orphan delete your existing deployment since selector labels are immutable.
```
kubectl delete deployment coredns --cascade=orphan
```
This will delete the deployment and leave the replicaset to ensure no downtime in the cluster. You will need to manually delete the replicaset AFTER Helm has released a new deployment.
Here is an example script to modify the annotations and labels of existing resources:
WARNING: Substitute YOUR_HELM_RELEASE_NAME_HERE with the name of your helm release.
```
#!/usr/bin/env bash
set -euo pipefail
for kind in config service serviceAccount; do
echo "setting annotations and labels on $kind/coredns"
kubectl -n kube-system annotate --overwrite $kind coredns meta.helm.sh/release-name=YOUR_HELM_RELEASE_NAME_HERE
kubectl -n kube-system annotate --overwrite $kind coredns meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label --overwrite $kind coredns app.kubernetes.io/managed-by=Helm
done
```
NOTE: Sometimes, previous deployments of kube-dns that have been migrated to CoreDNS still use kube-dns for the service name as well.
```
echo "setting annotations and labels on service/kube-dns"
kubectl -n kube-system annotate --overwrite service kube-dns meta.helm.sh/release-name=YOUR_HELM_RELEASE_NAME_HERE
kubectl -n kube-system annotate --overwrite service kube-dns meta.helm.sh/release-namespace=kube-system
kubectl -n kube-system label --overwrite service kube-dns app.kubernetes.io/managed-by=Helm
```

View File

@@ -0,0 +1,30 @@
{{- if .Values.isClusterService }}
CoreDNS is now running in the cluster as a cluster-service.
{{- else }}
CoreDNS is now running in the cluster.
It can be accessed using the below endpoint
{{- if contains "NodePort" .Values.serviceType }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "coredns.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo "$NODE_IP:$NODE_PORT"
{{- else if contains "LoadBalancer" .Values.serviceType }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl get svc -w {{ template "coredns.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "coredns.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SERVICE_IP
{{- else if contains "ClusterIP" .Values.serviceType }}
"{{ template "coredns.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local"
from within the cluster
{{- end }}
{{- end }}
It can be tested with the following:
1. Launch a Pod with DNS tools:
kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
2. Query the DNS server:
/ # host kubernetes

View File

@@ -0,0 +1,236 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "coredns.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "coredns.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "coredns.labels" -}}
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
{{- end -}}
{{/*
Common labels with autoscaler
*/}}
{{- define "coredns.labels.autoscaler" -}}
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
{{- end -}}
{{/*
Allow k8s-app label to be overridden
*/}}
{{- define "coredns.k8sapplabel" -}}
{{- default .Chart.Name .Values.k8sAppLabelOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Generate the list of ports automatically from the server definitions
*/}}
{{- define "coredns.servicePorts" -}}
{{/* Set ports to be an empty dict */}}
{{- $ports := dict -}}
{{/* Iterate through each of the server blocks */}}
{{- range .Values.servers -}}
{{/* Capture port to avoid scoping awkwardness */}}
{{- $port := toString .port -}}
{{- $serviceport := default .port .servicePort -}}
{{/* If none of the server blocks has mentioned this port yet take note of it */}}
{{- if not (hasKey $ports $port) -}}
{{- $ports := set $ports $port (dict "istcp" false "isudp" false "serviceport" $serviceport) -}}
{{- end -}}
{{/* Retrieve the inner dict that holds the protocols for a given port */}}
{{- $innerdict := index $ports $port -}}
{{/*
Look at each of the zones and check which protocol they serve
At the moment the following are supported by CoreDNS:
UDP: dns://
TCP: tls://, grpc://, https://
*/}}
{{- range .zones -}}
{{- if has (default "" .scheme) (list "dns://" "") -}}
{{/* Optionally enable tcp for this service as well */}}
{{- if eq (default false .use_tcp) true }}
{{- $innerdict := set $innerdict "istcp" true -}}
{{- end }}
{{- $innerdict := set $innerdict "isudp" true -}}
{{- end -}}
{{- if has (default "" .scheme) (list "tls://" "grpc://" "https://") -}}
{{- $innerdict := set $innerdict "istcp" true -}}
{{- end -}}
{{- end -}}
{{/* If none of the zones specify scheme, default to dns:// udp */}}
{{- if and (not (index $innerdict "istcp")) (not (index $innerdict "isudp")) -}}
{{- $innerdict := set $innerdict "isudp" true -}}
{{- end -}}
{{- if .nodePort -}}
{{- $innerdict := set $innerdict "nodePort" .nodePort -}}
{{- end -}}
{{/* Write the dict back into the outer dict */}}
{{- $ports := set $ports $port $innerdict -}}
{{- end -}}
{{/* Write out the ports according to the info collected above */}}
{{- range $port, $innerdict := $ports -}}
{{- $portList := list -}}
{{- if index $innerdict "isudp" -}}
{{- $portList = append $portList (dict "port" (get $innerdict "serviceport") "protocol" "UDP" "name" (printf "udp-%s" $port) "targetPort" ($port | int)) -}}
{{- end -}}
{{- if index $innerdict "istcp" -}}
{{- $portList = append $portList (dict "port" (get $innerdict "serviceport") "protocol" "TCP" "name" (printf "tcp-%s" $port) "targetPort" ($port | int)) -}}
{{- end -}}
{{- range $portDict := $portList -}}
{{- if index $innerdict "nodePort" -}}
{{- $portDict := set $portDict "nodePort" (get $innerdict "nodePort" | int) -}}
{{- end -}}
{{- printf "- %s\n" (toJson $portDict) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Generate the list of ports automatically from the server definitions
*/}}
{{- define "coredns.containerPorts" -}}
{{/* Set ports to be an empty dict */}}
{{- $ports := dict -}}
{{/* Iterate through each of the server blocks */}}
{{- range .Values.servers -}}
{{/* Capture port to avoid scoping awkwardness */}}
{{- $port := toString .port -}}
{{/* If none of the server blocks has mentioned this port yet take note of it */}}
{{- if not (hasKey $ports $port) -}}
{{- $ports := set $ports $port (dict "istcp" false "isudp" false) -}}
{{- end -}}
{{/* Retrieve the inner dict that holds the protocols for a given port */}}
{{- $innerdict := index $ports $port -}}
{{/*
Look at each of the zones and check which protocol they serve
At the moment the following are supported by CoreDNS:
UDP: dns://
TCP: tls://, grpc://, https://
*/}}
{{- range .zones -}}
{{- if has (default "" .scheme) (list "dns://" "") -}}
{{/* Optionally enable tcp for this service as well */}}
{{- if eq (default false .use_tcp) true }}
{{- $innerdict := set $innerdict "istcp" true -}}
{{- end }}
{{- $innerdict := set $innerdict "isudp" true -}}
{{- end -}}
{{- if has (default "" .scheme) (list "tls://" "grpc://" "https://") -}}
{{- $innerdict := set $innerdict "istcp" true -}}
{{- end -}}
{{- end -}}
{{/* If none of the zones specify scheme, default to dns:// udp */}}
{{- if and (not (index $innerdict "istcp")) (not (index $innerdict "isudp")) -}}
{{- $innerdict := set $innerdict "isudp" true -}}
{{- end -}}
{{- if .hostPort -}}
{{- $innerdict := set $innerdict "hostPort" .hostPort -}}
{{- end -}}
{{/* Write the dict back into the outer dict */}}
{{- $ports := set $ports $port $innerdict -}}
{{/* Fetch port from the configuration if the prometheus section exists */}}
{{- range .plugins -}}
{{- if eq .name "prometheus" -}}
{{- $prometheus_addr := toString .parameters -}}
{{- $prometheus_addr_list := regexSplit ":" $prometheus_addr -1 -}}
{{- $prometheus_port := index $prometheus_addr_list 1 -}}
{{- $ports := set $ports $prometheus_port (dict "istcp" true "isudp" false) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Write out the ports according to the info collected above */}}
{{- range $port, $innerdict := $ports -}}
{{- $portList := list -}}
{{- if index $innerdict "isudp" -}}
{{- $portList = append $portList (dict "containerPort" ($port | int) "protocol" "UDP" "name" (printf "udp-%s" $port)) -}}
{{- end -}}
{{- if index $innerdict "istcp" -}}
{{- $portList = append $portList (dict "containerPort" ($port | int) "protocol" "TCP" "name" (printf "tcp-%s" $port)) -}}
{{- end -}}
{{- range $portDict := $portList -}}
{{- if index $innerdict "hostPort" -}}
{{- $portDict := set $portDict "hostPort" (get $innerdict "hostPort" | int) -}}
{{- end -}}
{{- printf "- %s\n" (toJson $portDict) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "coredns.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "coredns.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "coredns.clusterRoleName" -}}
{{- if and .Values.clusterRole .Values.clusterRole.nameOverride -}}
{{ .Values.clusterRole.nameOverride }}
{{- else -}}
{{ template "coredns.fullname" . }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,30 @@
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "coredns.fullname" . }}-autoscaler
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list","watch"]
- apiGroups: [""]
resources: ["replicationcontrollers/scale"]
verbs: ["get", "update"]
- apiGroups: ["extensions", "apps"]
resources: ["deployments/scale", "replicasets/scale"]
verbs: ["get", "update"]
# Remove the configmaps rule once below issue is fixed:
# kubernetes-incubator/cluster-proportional-autoscaler#16
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "create"]
{{- end }}

View File

@@ -0,0 +1,36 @@
{{- if and .Values.deployment.enabled .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ template "coredns.clusterRoleName" . }}
labels: {{- include "coredns.labels" . | nindent 4 }}
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
{{- if .Values.rbac.pspEnable }}
- apiGroups:
- policy
- extensions
resources:
- podsecuritypolicies
verbs:
- use
resourceNames:
- {{ template "coredns.fullname" . }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,23 @@
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "coredns.fullname" . }}-autoscaler
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "coredns.fullname" . }}-autoscaler
subjects:
- kind: ServiceAccount
name: {{ template "coredns.fullname" . }}-autoscaler
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -0,0 +1,15 @@
{{- if and .Values.deployment.enabled .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ template "coredns.clusterRoleName" . }}
labels: {{- include "coredns.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ template "coredns.clusterRoleName" . }}
subjects:
- kind: ServiceAccount
name: {{ template "coredns.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -0,0 +1,33 @@
{{- if .Values.autoscaler.enabled }}
---
kind: ConfigMap
apiVersion: v1
metadata:
name: {{ template "coredns.fullname" . }}-autoscaler
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
{{- if .Values.customLabels }}
{{- toYaml .Values.customLabels | nindent 4 }}
{{- end }}
{{- if or .Values.autoscaler.configmap.annotations .Values.customAnnotations }}
annotations:
{{- if .Values.customAnnotations }}
{{- toYaml .Values.customAnnotations | nindent 4 }}
{{- end }}
{{- if .Values.autoscaler.configmap.annotations -}}
{{ toYaml .Values.autoscaler.configmap.annotations | nindent 4 }}
{{- end }}
{{- end }}
data:
# When cluster is using large nodes(with more cores), "coresPerReplica" should dominate.
# If using small nodes, "nodesPerReplica" should dominate.
linear: |-
{
"coresPerReplica": {{ .Values.autoscaler.coresPerReplica | float64 }},
"nodesPerReplica": {{ .Values.autoscaler.nodesPerReplica | float64 }},
"preventSinglePointFailure": {{ .Values.autoscaler.preventSinglePointFailure }},
"min": {{ .Values.autoscaler.min | int }},
"max": {{ .Values.autoscaler.max | int }},
"includeUnschedulableNodes": {{ .Values.autoscaler.includeUnschedulableNodes }}
}
{{- end }}

View File

@@ -0,0 +1,37 @@
{{- if .Values.deployment.enabled }}
{{- if not .Values.deployment.skipConfig }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "coredns.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
data:
Corefile: |-
{{- range $name, $conf := .Values.extraConfig }}
{{ $name }}{{ if $conf.parameters }} {{ $conf.parameters }}{{ end }}
{{- end }}
{{ range .Values.servers }}
{{- range $idx, $zone := .zones }}{{ if $idx }} {{ else }}{{ end }}{{ default "" $zone.scheme }}{{ default "." $zone.zone }}{{ else }}.{{ end -}}
{{- if .port }}:{{ .port }} {{ end -}}
{
{{- range .plugins }}
{{ .name }}{{ if .parameters }} {{ .parameters }}{{ end }}{{ if .configBlock }} {
{{ .configBlock | indent 12 }}
}{{ end }}
{{- end }}
}
{{ end }}
{{- range .Values.zoneFiles }}
{{ .filename }}: {{ toYaml .contents | indent 4 }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,98 @@
{{- if and (.Values.autoscaler.enabled) (not .Values.hpa.enabled) }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "coredns.fullname" . }}-autoscaler
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
template:
metadata:
labels:
{{- if .Values.isClusterService }}
{{- if not (hasKey .Values.customLabels "k8s-app")}}
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
{{- end }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | nindent 8 }}
{{- end }}
annotations:
checksum/configmap: {{ include (print $.Template.BasePath "/configmap-autoscaler.yaml") . | sha256sum }}
{{- if .Values.isClusterService }}
scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
{{- end }}
{{- with .Values.autoscaler.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ template "coredns.fullname" . }}-autoscaler
{{- $priorityClassName := default .Values.priorityClassName .Values.autoscaler.priorityClassName }}
{{- if $priorityClassName }}
priorityClassName: {{ $priorityClassName | quote }}
{{- end }}
{{- if .Values.autoscaler.affinity }}
affinity:
{{ toYaml .Values.autoscaler.affinity | indent 8 }}
{{- end }}
{{- if .Values.autoscaler.tolerations }}
tolerations:
{{ toYaml .Values.autoscaler.tolerations | indent 8 }}
{{- end }}
{{- if .Values.autoscaler.nodeSelector }}
nodeSelector:
{{ toYaml .Values.autoscaler.nodeSelector | indent 8 }}
{{- end }}
{{- if .Values.autoscaler.image.pullSecrets }}
imagePullSecrets:
{{ toYaml .Values.autoscaler.image.pullSecrets | indent 8 }}
{{- end }}
containers:
- name: autoscaler
image: "{{ .Values.autoscaler.image.repository }}:{{ .Values.autoscaler.image.tag }}"
imagePullPolicy: {{ .Values.autoscaler.image.pullPolicy }}
resources:
{{ toYaml .Values.autoscaler.resources | indent 10 }}
{{- if .Values.autoscaler.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: {{ .Values.autoscaler.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.autoscaler.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.autoscaler.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.autoscaler.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.autoscaler.livenessProbe.failureThreshold }}
{{- end }}
command:
- /cluster-proportional-autoscaler
- --namespace={{ .Release.Namespace }}
- --configmap={{ template "coredns.fullname" . }}-autoscaler
- --target=Deployment/{{ default (include "coredns.fullname" .) .Values.deployment.name }}
- --logtostderr=true
- --v=2
{{- if .Values.autoscaler.customFlags }}
{{ toYaml .Values.autoscaler.customFlags | indent 10 }}
{{- end }}
{{- if .Values.autoscaler.extraContainers }}
{{ toYaml .Values.autoscaler.extraContainers | indent 6 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,174 @@
{{- if .Values.deployment.enabled }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ default (include "coredns.fullname" .) .Values.deployment.name }}
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | replace ":" "-" | replace "@" "_" | trunc 63 | trimSuffix "-" | quote }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- if or .Values.deployment.annotations .Values.customAnnotations }}
annotations:
{{- if .Values.customAnnotations }}
{{- toYaml .Values.customAnnotations | nindent 4 }}
{{- end }}
{{- if .Values.deployment.annotations }}
{{- toYaml .Values.deployment.annotations | nindent 4 }}
{{- end }}
{{- end }}
spec:
{{- if and (not .Values.autoscaler.enabled) (not .Values.hpa.enabled) }}
replicas: {{ .Values.replicaCount }}
{{- end }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: {{ .Values.rollingUpdate.maxUnavailable }}
maxSurge: {{ .Values.rollingUpdate.maxSurge }}
selector:
{{- if .Values.deployment.selector }}
{{- toYaml .Values.deployment.selector | nindent 4 }}
{{- else }}
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
{{- end }}
template:
metadata:
labels:
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- if .Values.isClusterService }}
scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
{{- end }}
{{- if .Values.podAnnotations }}
{{ toYaml .Values.podAnnotations | indent 8 }}
{{- end }}
spec:
{{- if .Values.podSecurityContext }}
securityContext: {{ toYaml .Values.podSecurityContext | nindent 8 }}
{{- end }}
{{- if .Values.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
{{- end }}
serviceAccountName: {{ template "coredns.serviceAccountName" . }}
{{- if .Values.priorityClassName }}
priorityClassName: {{ .Values.priorityClassName | quote }}
{{- end }}
{{- if .Values.isClusterService }}
dnsPolicy: Default
{{- end }}
{{- if .Values.affinity }}
affinity:
{{ toYaml .Values.affinity | indent 8 }}
{{- end }}
{{- if .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{ tpl (toYaml .Values.topologySpreadConstraints) $ | indent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
{{- if .Values.image.pullSecrets }}
imagePullSecrets:
{{ toYaml .Values.image.pullSecrets | indent 8 }}
{{- end }}
{{- if .Values.initContainers }}
initContainers:
{{ toYaml .Values.initContainers | indent 8 }}
{{- end }}
containers:
- name: "coredns"
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
{{- range .Values.extraSecrets }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
readOnly: true
{{- end }}
{{- if .Values.extraVolumeMounts }}
{{- toYaml .Values.extraVolumeMounts | nindent 8}}
{{- end }}
{{- if .Values.env }}
env:
{{- toYaml .Values.env | nindent 10}}
{{- end }}
resources:
{{ toYaml .Values.resources | indent 10 }}
ports:
{{ include "coredns.containerPorts" . | indent 8 }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
{{- end }}
{{- if .Values.securityContext }}
securityContext:
{{- toYaml .Values.securityContext | nindent 10 }}
{{- end }}
{{- if .Values.extraContainers }}
{{ toYaml .Values.extraContainers | indent 6 }}
{{- end }}
volumes:
- name: config-volume
configMap:
name: {{ template "coredns.fullname" . }}
items:
- key: Corefile
path: Corefile
{{ range .Values.zoneFiles }}
- key: {{ .filename }}
path: {{ .filename }}
{{ end }}
{{- range .Values.extraSecrets }}
- name: {{ .name }}
secret:
secretName: {{ .name }}
defaultMode: {{ default 400 .defaultMode }}
{{- end }}
{{- if .Values.extraVolumes }}
{{ toYaml .Values.extraVolumes | indent 8 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,33 @@
{{- if and (.Values.hpa.enabled) (not .Values.autoscaler.enabled) }}
---
{{- if .Capabilities.APIVersions.Has "autoscaling/v2" }}
apiVersion: autoscaling/v2
{{- else }}
apiVersion: autoscaling/v2beta2
{{- end }}
kind: HorizontalPodAutoscaler
metadata:
name: {{ template "coredns.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ default (include "coredns.fullname" .) .Values.deployment.name }}
minReplicas: {{ .Values.hpa.minReplicas }}
maxReplicas: {{ .Values.hpa.maxReplicas }}
metrics:
{{ toYaml .Values.hpa.metrics | indent 4 }}
{{- if .Values.hpa.behavior }}
behavior:
{{ toYaml .Values.hpa.behavior | indent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,26 @@
{{- if and .Values.deployment.enabled .Values.podDisruptionBudget -}}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ template "coredns.fullname" . }}
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if not .Values.podDisruptionBudget.selector }}
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
{{- end }}
{{ toYaml .Values.podDisruptionBudget | indent 2 }}
{{- end }}

View File

@@ -0,0 +1,39 @@
{{- if and .Values.deployment.enabled .Values.prometheus.service.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ template "coredns.fullname" . }}-metrics
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
app.kubernetes.io/component: metrics
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- if or .Values.prometheus.service.annotations .Values.service.annotations .Values.customAnnotations }}
annotations:
{{- if .Values.prometheus.service.annotations }}
{{- toYaml .Values.prometheus.service.annotations | nindent 4 }}
{{- end }}
{{- if .Values.service.annotations }}
{{- toYaml .Values.service.annotations | nindent 4 }}
{{- end }}
{{- if .Values.customAnnotations }}
{{- toYaml .Values.customAnnotations | nindent 4 }}
{{- end }}
{{- end }}
spec:
selector:
{{- if .Values.prometheus.service.selector }}
{{- toYaml .Values.prometheus.service.selector | nindent 4 }}
{{- else }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
{{- end }}
ports:
- name: metrics
port: 9153
targetPort: 9153
{{- end }}

View File

@@ -0,0 +1,61 @@
{{- if .Values.deployment.enabled }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ default (include "coredns.fullname" .) .Values.service.name }}
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- if or .Values.service.annotations .Values.customAnnotations }}
annotations:
{{- if .Values.service.annotations }}
{{- toYaml .Values.service.annotations | nindent 4 }}
{{- end }}
{{- if .Values.customAnnotations }}
{{- toYaml .Values.customAnnotations | nindent 4 }}
{{- end }}
{{- end }}
spec:
selector:
{{- if .Values.service.selector }}
{{- toYaml .Values.service.selector | nindent 4 }}
{{- else }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
{{- if .Values.isClusterService }}
k8s-app: {{ template "coredns.k8sapplabel" . }}
{{- end }}
app.kubernetes.io/name: {{ template "coredns.name" . }}
{{- end }}
{{- if .Values.service.clusterIP }}
clusterIP: {{ .Values.service.clusterIP }}
{{- end }}
{{- if .Values.service.clusterIPs }}
clusterIPs:
{{ toYaml .Values.service.clusterIPs | nindent 4 }}
{{- end }}
{{- if .Values.service.externalIPs }}
externalIPs:
{{- toYaml .Values.service.externalIPs | nindent 4 }}
{{- end }}
{{- if .Values.service.externalTrafficPolicy }}
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
{{- end }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
{{- if .Values.service.loadBalancerClass }}
loadBalancerClass: {{ .Values.service.loadBalancerClass }}
{{- end }}
ports:
{{ include "coredns.servicePorts" . | indent 2 -}}
type: {{ default "ClusterIP" .Values.serviceType }}
{{- if .Values.service.ipFamilyPolicy }}
ipFamilyPolicy: {{ .Values.service.ipFamilyPolicy }}
{{- end }}
{{- end }}
{{- if .Values.service.trafficDistribution }}
trafficDistribution: {{ .Values.service.trafficDistribution }}
{{- end }}

View File

@@ -0,0 +1,20 @@
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "coredns.fullname" . }}-autoscaler
namespace: {{ .Release.Namespace }}
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
{{- if .Values.customLabels }}
{{ toYaml .Values.customLabels | indent 4 }}
{{- end }}
{{- with .Values.customAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if .Values.autoscaler.image.pullSecrets }}
imagePullSecrets:
{{ toYaml .Values.autoscaler.image.pullSecrets | indent 2 }}
{{- end }}
{{- end }}

Some files were not shown because too many files have changed in this diff Show More