Compare commits

..

295 Commits

Author SHA1 Message Date
Andrei Kvapil
156c1e8524 PoC: Move workloadmonitors to controller
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-04 19:31:31 +01:00
Andrei Kvapil
19ed058897 [dashboard-controller] Move bages generation logic to internal dashboard component (#1567)
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
[dashboard-controller] Move bages generation logic to internal dashboard component
```

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

* **Refactors**
* Dashboard badges now render with a unified ResourceBadge component
across columns and headers.
* Explicit badge size options removed; badges use simplified default
sizing.
* Badge payload simplified to a single value-driven field; abbreviation
and rendering are handled by the badge component.
* Stable per-kind color hashing removed; badges use streamlined,
consistent styling with optional color overrides.
* Column and header labels expanded to full descriptive names for
clearer navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 17:36:33 +01:00
Andrei Kvapil
6438ce98b1 Add QOSI to ADOPTERS.md (#1589)
```release-note
[adopters] Add QOSI to ADOPTERS.md
```

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

* **Documentation**
* Added a new adopter entry for QOSI (dated 2025-10-04) with a
descriptive use-case.
  * Removed an erroneous stray dash that preceded the new entry.
* Cleaned up formatting (trailing newline added); existing adopter list
otherwise unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 17:33:22 +01:00
Timofei Larkin
523d8ea638 [vpc] Give predictable name to subnet configmap (#1590)
## What this PR does

The new VPC feature creates a configmap, containing the list of subnets
under the VPC. However, the configmap has the VPC ID in its name, rather
than the name of the VPC, making it harder to target inside the
dashboard, as the helper functions from Helm (e.g. sha256sum) may not be
available in the dashboard's custom resources. This patch renames the
configmap to use the VPC's name.

### Release note

```release-note
[vpc] Change the subnet configmap name to a human-friendly value
(matching the VPC name), instead of being derived via a sha256sum,
making it easier to reference in the dashboard.
```
2025-11-04 20:27:57 +04:00
Andrei Kvapil
e89896fdba [flux] Close Flux Operator ports to external access (#1581)
This patch updates the Flux Operator Deployment to remove hostPort and
hostNetwork, ensuring that ports 8080 and 8081 are only accessible
within the cluster. This prevents external exposure and improves
security.

```release-note
[flux] Close Flux Operator ports (8080/8081) to external access for improved security.
```

<!-- 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.
-->


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

* **Security**
* Added a cluster-wide network policy for the Flux Operator to block
external access to internal service ports (notably TCP 8080 and 8081)
while preserving intra-cluster communication.
* **Chores**
* Update process now applies the new network policy as part of Flux
Operator deployments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 17:13:43 +01:00
Andrei Kvapil
ab5101a713 [dashboard] Migrate patches to upstream project (#1569)
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
[dashboard] Migrate patches to upstream project
[dashboard] Fix nested lists in addtiionalProperties
```

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

## Summary by CodeRabbit

* **New Features**
* Enhanced form generation with improved type inference and nested
property resolution for dynamic form fields.

* **Bug Fixes**
* Fixed stream data handling issues and improved form field value
normalization.
* Better support for array item initialization with sensible defaults
based on field types.

* **Chores**
  * Updated API endpoints for namespace resource management.
  * Updated container images and configurations.
  * Improved tenant branding configuration structure.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 17:11:57 +01:00
Andrei Kvapil
af460f1c41 [dashboard-controller] Move bages generation logic to internal dashboard
component

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-04 17:11:31 +01:00
Timofei Larkin
634649f9ec [nats] Fixes for NATS App Helm chart, fix template issues with config.merge (#1583)
[nats] Fixes for NATS App Helm chart, fix template issues with
config.merge value

## What this PR does

This PR fixes two critical bugs in the NATS application chart
(`packages/apps/nats`) that prevented successful deployment when using
user authentication with custom configuration:

**Bug #1: YAML Parse Error with Empty config.merge**
- When users were defined but `config.merge` was empty (`{}`), the Helm
template would fail with YAML parse errors
- Fixed by adding safe existence checks before accessing
`.Values.config.merge` and preventing rendering of empty merge blocks

**Bug #2: Incorrect Config Nesting**
- When both `users` and `config.merge` were defined, custom NATS config
values (e.g., `max_payload`, `max_connections`) were incorrectly nested
as children of the `accounts` object instead of being placed at the root
NATS configuration level
- This caused NATS pods to crash with "Expected map entries for
accounts" errors
- Fixed by correcting the indentation from `nindent 12` to `nindent 10`
in the template

The related issue is #1354 

### Changes Made

**File**: `packages/apps/nats/templates/nats.yaml`

1. Line 55: Added safe existence checks for `.Values.config.merge`
2. Line 66: Added length validation to prevent rendering empty merge
objects
3. Line 67: Fixed indentation to ensure config values merge at root
level

### Impact

- Enables NATS deployments with user authentication and custom
configuration
- Fixes Stalwart mail server clustering scenarios that require NATS with
authentication
- Resolves HelmRelease failures in multi-tenant environments

### Testing

Can be tested with:
```yaml
apiVersion: apps.cozystack.io/v1alpha1
kind: NATS
metadata:
  name: test-nats
spec:
  replicas: 2
  users:
    testuser: {}
  config:
    merge:
      max_payload: 2097152
      max_connections: 500
```

Expected result: HelmRelease succeeds, NATS pods start successfully, and
configuration is valid.

---

### Release note

```release-note
[nats] Fix NATS application chart template bugs that prevented deployments with user authentication and custom configuration.
```


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

## Summary by CodeRabbit

* **Bug Fixes**
* Enhanced configuration validation with improved guard conditions to
ensure proper handling of merge configurations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 20:00:17 +04:00
Timofei Larkin
df782fec9c [vpc] Give predictable name to subnet configmap
## What this PR does

The new VPC feature creates a configmap, containing the list of subnets
under the VPC. However, the configmap has the VPC ID in its name, rather
than the name of the VPC, making it harder to target inside the
dashboard, as the helper functions from Helm (e.g. sha256sum) may not be
available in the dashboard's custom resources. This patch renames the
configmap to use the VPC's name.

### Release note

```release-note
[vpc] Change the subnet configmap name to a human-friendly value
(matching the VPC name), instead of being derived via a sha256sum,
making it easier to reference in the dashboard.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-11-04 18:54:48 +03:00
Timofei Larkin
172774b6cd [nats] Terser checks using with
This patch makes the fixes from `b1ebc9cc` by @insignia96 terser by
making use of Helm's `with` blocks.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-11-04 18:22:47 +03:00
Timofei Larkin
62119eb761 [vpc] Install Multus by default (#1587)
## What this PR does

The recent patch introducing VPCs in Cozystack did not include enabling
Multus, which is a dependency for this feature. This patch enables
Multus by default in the paas-full bundle.

### Release-note

```release-note
[vpc] Enable Multus by default as a necessary dependency for VPCs.
```
2025-11-04 19:14:34 +04:00
IvanHunters
48c6e23ca0 add rule for success installing
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 17:56:49 +03:00
Timofei Larkin
9064a72c92 [vpc] Install Multus by default
## What this PR does

The recent patch introducing VPCs in Cozystack did not include enabling
Multus, which is a dependency for this feature. This patch enables
Multus by default in the paas-full bundles.

### Release-note

```release-note
[vpc] Enable Multus by default as a necessary dependency for VPCs.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-11-04 17:02:55 +03:00
Tamerlan Abu
dc06b16d11 add qosikz as adopters
Signed-off-by: Tamerlan Abu <tamerlanabu@gmail.com>
2025-11-04 17:52:52 +05:00
Andrei Kvapil
739a74dc28 [kubevirt] Fix: kubevirt metrics rule (#1584)
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
[kubevirt] Fix: kubevirt metrics rule
```

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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed Prometheus alert rule expressions for virtual machine
monitoring. Corrected status and phase condition comparisons to
accurately identify when virtual machines are not running, ensuring
alerts trigger reliably in such scenarios. These improvements enhance
the accuracy of monitoring notifications.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 13:34:51 +01:00
Andrei Kvapil
723eefea66 [dashboard] Migrate patches to upstream project
[dashboard] Fix nested lists in addtiionalProperties

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-04 13:33:21 +01:00
Nikita
1d10907168 [core] rm talos lldp extension (#1586)
<!-- 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
Removes Talos lldp extension. Please build a custom talos image with factory.talos.dev if you need it.

### 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
Talos lldp extension removed.
```

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

## Summary by CodeRabbit

* **Chores**
* Removed LLDPD (Link Layer Discovery Protocol Daemon) system extension
from cluster configuration. This eliminates the LLDPD kernel module from
cluster setups, removes LLDPD references from build processes, and
updates installation profiles across all supported deployment methods
including bare metal, cloud environments, and ISO installations,
resulting in a reduced system footprint.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 13:52:33 +03:00
nbykov0
c19cddf08e [core] rm talos lldp extension
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-11-04 12:21:23 +03:00
Andrei Kvapil
4c08caafe1 [ingress] Enforce HTTPS-only for API (#1582)
This patch updates the default API Ingress to add the
nginx.ingress.kubernetes.io/force-ssl-redirect annotation, ensuring all
HTTP traffic (port 80) is redirected to HTTPS (port 443). This prevents
unencrypted external access and improves security.

```release-note
[ingress] Force HTTPS access for api.dev3.infra.aenix.org and block direct HTTP.
```

<!-- 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.
-->


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

## Summary by CodeRabbit

* **New Features**
* Enforced SSL/TLS redirect for API ingress connections to enhance
security.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 10:18:37 +01:00
Andrei Kvapil
be58047aba [redis] Bump Redis image version for security fixes (#1580)
This patch updates the RedisFailover Helm template to use a newer,
secure Redis version (8.2.0). This addresses known security issues in
the previous Redis version and ensures safer deployments.

```release-note
[redis] Upgrade Redis to a secure version (8.2.0) to fix security vulnerabilities.
```

<!-- 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.
-->


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

* **New Features**
* Redis deployments can now specify the container image via a new
configurable value (default: redis:8.2.0), allowing easy override of the
Redis image used.
* **Schema**
* Values schema and resource definition schemas updated to include and
validate the new image setting.
* **Documentation**
  * README updated to document the new image parameter.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-04 10:17:51 +01:00
IvanHunters
f60e2555c9 add patch
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 12:14:43 +03:00
Andrei Kvapil
6443a1264e [kubevirt] Fix: kubevirt metrics rule
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-04 10:13:33 +01:00
IvanHunters
52a23eacfc close metrics port for external
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 12:00:30 +03:00
IvanHunters
2634b01465 revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:32:18 +03:00
IvanHunters
15a3636d5f revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:29:55 +03:00
IvanHunters
ef43ef6753 revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:26:56 +03:00
IvanHunters
ba804b7c52 revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:24:38 +03:00
IvanHunters
9c5abf49ca revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:23:16 +03:00
IvanHunters
10e79651ef revert redis values and static image in the chart
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:21:10 +03:00
IvanHunters
965818efd4 fix crd
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 11:06:49 +03:00
Isaiah Olson
b1ebc9cc85 Fixes for NATS App Helm chart, fix template issues with config.merge value
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-11-03 23:59:12 -06:00
IvanHunters
667c778f27 [ingress] Enforce HTTPS-only for API
This patch updates the default API Ingress to add the
nginx.ingress.kubernetes.io/force-ssl-redirect annotation,
ensuring all HTTP traffic (port 80) is redirected to HTTPS (port 443).
This prevents unencrypted external access and improves security.

```release-note
[ingress] Force HTTPS access for api.dev3.infra.aenix.org and block direct HTTP.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 02:29:24 +03:00
IvanHunters
77d95e3b91 fix generator scheme for redis image
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 02:12:06 +03:00
IvanHunters
a8d3cbce82 Fix values.schema.json for values.yaml by security fix
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 02:04:50 +03:00
IvanHunters
eea685065a [flux] Close Flux Operator ports to external access
This patch updates the Flux Operator Deployment to remove hostPort and hostNetwork,
ensuring that ports 8080 and 8081 are only accessible within the cluster.
This prevents external exposure and improves security.

```release-note
[flux] Close Flux Operator ports (8080/8081) to external access for improved security.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 01:59:16 +03:00
IvanHunters
480f8027d7 [redis] Bump Redis image version for security fixes
This patch updates the RedisFailover Helm template to use a newer,
secure Redis version (8.2.0). This addresses known security issues
in the previous Redis version and ensures safer deployments.

```release-note
[redis] Upgrade Redis to a secure version (8.2.0) to fix security vulnerabilities.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-04 01:50:11 +03:00
Timofei Larkin
19b56414a6 [api] Delete previous instance when changing type (#1579)
## What this PR does

It was observed during upgrades to the `cozystack-api` Helm release that
when enabling the local endpoint for the traffic locality feature, hence
switching from a deployment to a daemonset, the deployment may remain
unpruned and the pods of the deployment will continue to run
indefinitely. This patch adds a post-upgrade hook that explicitly
deletes the deployment in case it exists and was not pruned.

### Release-note

```release-note
[api] Delete the cozystack-api deployment in a post-upgrade hook when
migrating to a daemonset and vice-versa.
```

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

* **New Features**
* Automatic post-upgrade cleanup that removes outdated cluster resources
when the local Kubernetes API endpoint is disabled.
* Cleanup runs in the release namespace during upgrades and includes
necessary permissions for the cleanup job to complete.

* **Configuration**
* New release value toggles the local Kubernetes API endpoint to enable
or skip the cleanup behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-03 19:27:01 +04:00
Timofei Larkin
0f9806e9b0 [api] Delete previous instance when changing type
## What this PR does

It was observed during upgrades to the `cozystack-api` Helm release that
when enabling the local endpoint for the traffic locality feature, hence
switching from a deployment to a daemonset, the deployment may remain
unpruned and the pods of the deployment will continue to run
indefinitely. This patch adds a post-upgrade hook that explicitly deletes
the deployment in case it exists and was not pruned.

### Release-note

```release-note
[api] Delete the cozystack-api deployment in a post-upgrade hook when
migrating to a daemonset and vice-versa.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-11-03 17:22:38 +03:00
Andrei Kvapil
177073596c [tenant] Allow listing workloads (#1576)
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] Allow listing workload
```
2025-11-03 11:57:12 +01:00
Andrei Kvapil
93a9241899 [tenant] Allow listing workloads
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-03 11:56:00 +01:00
Andrei Kvapil
5401ae9734 [seaweedfs] Fix migration to v3.99 (#1572)
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 migration to v3.99
```

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

## Summary by CodeRabbit

* **Chores**
  * Upgraded seaweedfs configuration to version 3.
* Updated pre-upgrade hook execution conditions to ensure proper upgrade
procedures.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-03 11:33:16 +01:00
Andrei Kvapil
b78d97f374 [nats] Merge container spec, not podTemplate (#1571)
## What this PR does

The NATS chart incorrectly used podTemplate+merge instead of
container+merge to add resource requests and limits to the NATS
container in the statefulset, but as a result it just completely wiped
out the default container spec. By moving the overrides under the
container key, the upstream chart now correctly merges the resource
requests, instead of overwriting the container spec.

### Release note

```release-note
[nats] Fix incorrect path to container resources in the NATS chart.
```

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

* **Chores**
* Restructured NATS deployment configuration while maintaining existing
functionality and resource settings; templates were reorganized to
streamline how container and resource definitions are represented. No
functional or behavioral changes are expected for deployments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-03 11:33:03 +01:00
Timofei Larkin
8b95db06ee [nats] Merge container spec, not podTemplate
## What this PR does

The NATS chart incorrectly used podTemplate+merge instead of
container+merge to add resource requests and limits to the NATS
container in the statefulset, but as a result it just completely wiped
out the default container spec. By moving the overrides under the
container key, the upstream chart now correctly merges the resource
requests, instead of overwriting the container spec.

### Release note

```release-note
[nats] Fix incorrect path to container resources in the NATS chart.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-11-03 12:50:24 +03:00
Andrei Kvapil
5a2d4d7e66 [e2e] Increase Kubernetes connection timeouts (#1570)
This patch increases the connection and request timeouts used in the E2E
tests when communicating with the Kubernetes API. The change improves
test stability under high load and slow cluster response conditions.

```release-note
[e2e] Increase connection and request timeouts for Kubernetes API calls in E2E tests to improve stability.
```

<!-- 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.
-->


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

## Summary by CodeRabbit

* **Chores**
* Optimized infrastructure timeout and polling configurations to improve
deployment reliability and test execution efficiency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-03 10:48:10 +01:00
Andrei Kvapil
42e6f0e3f2 [seaweedfs] Fix migration to v3.99
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-11-03 10:41:39 +01:00
IvanHunters
e2eb1e267b [e2e] Increase Kubernetes connection timeouts
This patch increases the connection and request timeouts used in the
E2E tests when communicating with the Kubernetes API. The change improves
test stability under high load and slow cluster response conditions.

```release-note
[e2e] Increase connection and request timeouts for Kubernetes API calls in E2E tests to improve stability.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-11-03 10:53:47 +03:00
Andrei Kvapil
2ac533f2f6 Update LINSTOR v1.32.3 (#1565)
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
Update LINSTOR v1.32.3
```
2025-11-01 01:21:49 +05:00
Andrei Kvapil
ae9f9c57b1 Update LINSTOR v1.32.3
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-31 21:19:47 +01:00
Andrei Kvapil
18f253f77a [dashboard] Update openapi-ui v1.0.3 + fixes (#1564)
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

- Update openapi-ui to v1.0.3
- Show YAML editor as readonly in YAML tab
- Remove inside link from user menu
- fix editing for tenantmodules, fixes
https://github.com/cozystack/cozystack/issues/1550
- fix editing valuesOverride, fixes
https://github.com/cozystack/cozystack/issues/1560

### 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
[dashboard] Update openapi-ui v1.0.3 + fixes
```

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

* **Bug Fixes**
* Made YAML editor read-only to prevent accidental modifications in
details view
  * Fixed API request header handling to prevent stream abort issues
  * Updated resource API endpoint paths for correct data retrieval
  * Removed menu navigation item from user interface
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-31 22:09:36 +05:00
Andrei Kvapil
bd9dcb52a3 [dashboard] Add new patches
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-31 18:06:44 +01:00
Andrei Kvapil
be473a12be [dashboard] Update openapi-ui v1.0.3
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-31 18:05:59 +01:00
Timofei Larkin
8f5adcccf5 [system] Add VPC (#1543)
<!-- 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
Add VPC support

### 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
Added VPC support
```

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

* **New Features**
- Added Virtual Private Cloud (VPC) support with configurable subnets,
per-subnet network attachments, and generated subnet resources.
- Enabled subnet support for Virtual Machine and VM Instance: additional
interfaces, Multus networks, and conditional cloud-init/network secret
wiring for supported images.

* **Documentation**
- Added/updated docs and examples for VPC, Virtual Machine, and VM
Instance showing subnet parameters.

* **Chores**
- Expanded admin role permissions to manage Virtual Private Cloud
resources.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-31 19:40:50 +04:00
Andrei Kvapil
08bd918a10 [seaweedfs] Update SeaweedFS v3.99 and deploy S3 as stacked service (#1562)
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**
* Helm values now control ingress paths; computed cluster endpoint env
vars are injected.
  * Optional container securityContext for volume init containers added.
  * Node architecture-specific targeting disabled by default.

* **Refactor**
* Image configuration reorganized with separate registry field;
container image build simplified.

* **Bug Fixes / Behavior**
* S3-related authorization and signature handling changed; S3 gateway
toggled.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-31 20:13:09 +05:00
nbykov0
023276ebab [apps] tenant: add vpcs to tenant roles
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-31 17:48:36 +03:00
nbykov0
19c4674ebb [apps] vm-instance: add vpc support
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-31 17:48:36 +03:00
nbykov0
202da193c0 [apps] virtual-machine: add vpc support
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-31 17:48:36 +03:00
nbykov0
cc9687707c [apps] Add VPC app
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-31 17:48:17 +03:00
Andrei Kvapil
ac10e35272 [seaweedfs] Update SeaweedFS v3.99 and deploy S3 as stacked service
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-31 13:58:03 +01:00
Andrei Kvapil
fc7d5ee71f [seaweedfs] Allow users to discover their buckets (#1528)
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 enables building of `seaweedfs` image.
Also backports patch from upstream
https://github.com/seaweedfs/seaweedfs/pull/7335

### 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] Allow users to discover their buckets
```

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

* **Bug Fixes**
* S3 signature handling adjusted so signature verification focuses on
authentication; permission checks are evaluated afterward.

* **Chores**
* Build process now discovers and uses remote release versions
dynamically.
* Introduced an optimized multi-stage container build with improved
tagging and registry caching.
* Added configurable image settings (global image name and image tag)
for deployment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-31 15:13:37 +05:00
Timofei Larkin
9d90503fb7 [dashboard] Revert reconciler removal (#1559)
## What this PR does

In a previous patch (#1555) the reconciliation loop for the OpenAPI UI
resources was accidentally removed. This patch reintroduces a separate
controller, which handles updates to CozystackResourceDefinitions and
creates, updates, or deletes the dashboard's custom resources.

### Release note

```release-note
[dashboard] Reintroduce the accidentally removed reconciler that
autoconfigures custom dashboard resources for the OpenAPI UI.
```
2025-10-31 12:59:13 +04:00
Andrei Kvapil
4be1c257d6 [mariadb-operator] Add post-delete job to remove PVCs (#1553)
<!-- 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

[mariadb-operator] Add post-delete job to remove PVCs
This patch adds a Helm post-delete hook job that removes
PersistentVolumeClaims
left behind after Helm release deletion. The MariaDB Operator currently
does not
handle PVC cleanup, so this job ensures proper resource removal.

### 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
[mariadb-operator] Add a post-delete hook job to clean up PVCs left after Helm release deletion.
```

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

* **New Features**
* Persistent storage volumes are now automatically cleaned up when the
MySQL application is deleted, preventing orphaned storage resources from
accumulating in your cluster.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-30 23:21:48 +05:00
Timofei Larkin
f3ba8eca8e [dashboard] Revert reconciler removal
## What this PR does

In a previous patch (#1555) the reconciliation loop for the OpenAPI UI
resources was accidentally removed. This patch reintroduces a separate
controller, which handles updates to CozystackResourceDefinitions and
creates, updates, or deletes the dashboard's custom resources.

### Release note

```release-note
[dashboard] Reintroduce the accidentally removed reconciler that
autoconfigures custom dashboard resources for the OpenAPI UI.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-30 19:37:20 +03:00
IvanHunters
0f286ee7ba [mariadb-operator] Add post-delete job to remove PVCs
This patch adds a Helm post-delete hook job that removes PersistentVolumeClaims
left behind after Helm release deletion. The MariaDB Operator currently does not
handle PVC cleanup, so this job ensures proper resource removal.

```release-note
[mariadb-operator] Add a post-delete hook job to clean up PVCs left after Helm release deletion.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-29 22:57:11 +03:00
Timofei Larkin
5acf62824a [api,lineage] Ensure node-local traffic (#1554)
## What this PR does

Since 0.37, many requests to the k8s API now go through a mutating
webhook (lineage-controller-webhook). Since the lineage webhook makes
multiple requests to the k8s API and, indirectly, to the Cozystack API
server, each request for, e.g., creating a secret now causes a lot of
chatter between the webhook, the k8s API, and the Cozystack API. When
this happens cross-node or, worse yet, cross-zone, this can blow up the
latency for simple requests.

### BREAKING CHANGES

This patch changes the Cozystack API to a DaemonSet targetting
controlplane nodes, configures its service for an `Local` internal
traffic policy and adds environment variables indicating that the k8s
API server is to be found at \<hostIP\>:6443, **not only for the
Cozystack API, but also for the lineage-controller-webhook.** This is a
valid configuration in most scenarios, including the default
installation method on top of Talos Linux in Cozystack, however, if this
is not valid in your environment, you must now set the values
`.lineageControllerWebhook.localK8sAPIEndpoint.enabled` and
`.cozystackAPI.localK8sAPIEndpoint.enabled` to `false` in the respective
system Helm releases.

### Release note

```release-note
[api,lineage] Configure all chatter between the Lineage webhook, the
Cozystack API server and the Kubernetes API server to be confined to a
single controlplane node, improving k8s API latency.
```

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

* **New Features**
* Optional local Kubernetes API endpoint mode with configurable topology
(DaemonSet vs Deployment), replica setting, service behavior, and node
scheduling.
* Certificate lifecycle managed via cert-manager with namespace-scoped
issuers and certificates; secret-backed TLS assets with restricted
permissions.
* Controller runtime flag to select API workload kind; webhook can
optionally target local API host/port.

* **Security**
* Enforced TLS verification using cert-manager CA injection; removed
insecure TLS-skip behavior.
* **Permissions**
  * Controller role expanded to allow daemonset management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-29 18:17:14 +04:00
Timofei Larkin
93e33a0921 [kubernetes] Use controlPlane.replicas field (#1556)
## What this PR does

The managed Kubernetes app accepts a .controPlane.replicas field, but
this value was never used, instead being hardcoded in the
KamajiControlPlane template to 2. This patch fixes this.

### Release note

```release-note
[kubernetes] Pass the .controlPlane.replicas field into the
KamajiControlPlane template, making the replica count of the
controlplane pods user-configurable.
```

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

## Summary by CodeRabbit

* **New Features**
* Control plane replica count is now configurable via Helm values,
allowing flexible deployment scaling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-29 16:15:19 +04:00
Timofei Larkin
c4fa795491 [kubernetes] Use controlPlane.replicas field
## What this PR does

The managed Kubernetes app accepts a .controPlane.replicas field, but
this value was never used, instead being hardcoded in the
KamajiControlPlane template to 2. This patch fixes this.

### Release note

```release-note
[kubernetes] Pass the .controlPlane.replicas field into the
KamajiControlPlane template, making the replica count of the
controlplane pods user-configurable.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-29 13:36:59 +03:00
Timofei Larkin
f93042499b [controller] Remove crdmem, handle DaemonSet (#1555)
## What this PR does

This patch drops the custom caching of the Cozystack resource
definitions in favor of the informer cache and adds a flag to the
Cozystack controller to select, whether it restarts the cozystack-api
deployment or the cozystack-api daemonset.

### BREAKING CHANGES

As with the new default behavior of using a local endpoint for the k8s
API by the lineage webhook and the Cozystack API, the Cozystack
controller now also defaults to restarting a Cozystack API DaemonSet
instead of a Deployment. To revert to the old behavior, disable the
local k8s API endpoint on the webhook and cozystack API and set the
`cozystackController.cozystackAPIKind` value in the Cozystack controller
system Helm chart to "Deployment".

### Release note

```release-note
[controller] Use informer cache instead of the older bespoke
implementation and add support for running the Cozystack API as a
DaemonSet.
```
2025-10-29 14:22:54 +04:00
Timofei Larkin
7cbe564ff1 [controller] Remove crdmem, handle DaemonSet
This patch drops the custom caching of the Cozystack resource
definitions in favor of the informer cache and adds a flag to the
Cozystack controller to select, whether it restarts the cozystack-api
deployment or the cozystack-api daemonset.

As with the new default behavior of using a local endpoint for the k8s
API by the lineage webhook and the Cozystack API, the Cozystack
controller now also defaults to restarting a Cozystack API DaemonSet
instead of a Deployment. To revert to the old behavior, disable the
local k8s API endpoint on the webhook and cozystack API and set the
`cozystackController.cozystackAPIKind` value in the Cozystack controller
system Helm chart to "Deployment".

```release-note
[controller] Use informer cache instead of the older bespoke
implementation and add support for running the Cozystack API as a
DaemonSet.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-29 12:45:57 +03:00
Timofei Larkin
62ff0c0b39 [api,lineage] Ensure node-local traffic
Since 0.37, many requests to the k8s API now go through a mutating
webhook (lineage-controller-webhook). Since the lineage webhook makes
multiple requests to the k8s API and, indirectly, to the Cozystack API
server, each request for, e.g., creating a secret now causes a lot of
chatter between the webhook, the k8s API, and the Cozystack API. When
this happens cross-node or, worse yet, cross-zone, this can blow up the
latency for simple requests.

This patch changes the Cozystack API to a DaemonSet targetting
controlplane nodes, configures its service for an `Local` internal
traffic policy and adds environment variables indicating that the k8s
API server is to be found at <hostIP>:6443, **not only for the Cozystack
API, but also for the lineage-controller-webhook.** This is a valid
configuration in most scenarios, including the default installation
method on top of Talos Linux in Cozystack, however, if this is not valid
in your environment, you must now set the values
`.lineageControllerWebhook.localK8sAPIEndpoint.enabled` and
`.cozystackAPI.localK8sAPIEndpoint.enabled` to `false` in the respective
system Helm releases.

```release-note
[api,lineage] Configure all chatter between the Lineage webhook, the
Cozystack API server and the Kubernetes API server to be confined to a
single controlplane node, improving k8s API latency.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-29 09:17:27 +03:00
Andrei Kvapil
198b30887a [dx] JSDoc compatible syntax for values.yaml (#1536)
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

depends on https://github.com/cozystack/cozyvalues-gen/pull/16

### 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] JSDoc compatible syntax for values.yaml
```

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

* **Documentation**
* Standardized and tightened wording, punctuation and examples across
many charts, READMEs and schemas for clearer parameter docs and
defaults.

* **Refactor**
* Replaced many inline parameter blocks with consistent typedefs/enums
and typed maps (resources, presets, components, addons, storage,
sources, etc.) to unify configuration surfaces.

* **Chores**
* Workflow: updated pre-commit generate step to a newer generator
release.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-29 09:40:58 +05:00
Andrei Kvapil
9632772337 [dx] JSDoc compatible syntax for values.yaml
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-29 08:57:26 +05:00
Timofei Larkin
992c7d54fe Flux Operator v0.30.0 (#1482)
With this release, the new Flux 2.7 version becomes generally available

The Flux 2.7 upgrade may require some API bumps. (This PR only upgrades
Flux Operator.)

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

## Summary by CodeRabbit

- New Features
- Added support for an optional “source-watcher” component in Flux
installations, selectable via chart configuration.

- Documentation
- Updated READMEs to reflect v0.30.0 and clarified that charts can
install, configure, and automatically upgrade Flux. Version badges
refreshed.

- Chores
- Bumped chart and app versions to 0.30.0 across Flux Operator and Flux
Instance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-27 19:13:28 +04:00
Timofei Larkin
4e3c8eafa1 Merge remote-tracking branch 'origin/main' into pr/1482-flux-kingdonb 2025-10-27 17:33:08 +03:00
Timofei Larkin
05cd1a1c82 [system] kube-ovn: turn off enableLb (#1548)
<!-- 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
Turns off kubeovn enableLb, kube-proxy implementation of kube-ovn.

### 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
[system] kube-ovn: turn off kube-proxy implementation
```

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

## Summary by CodeRabbit

* **Chores**
* Added a new load balancing configuration option to system settings
(disabled by default).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-27 18:25:41 +04:00
Timofei Larkin
ee1c83ec85 [redis-operator] Build patched operator in-tree (#1547)
## What this PR does

This patch moves the build of the Redis operator into the Cozystack
organization and patches it to prevent overwriting third-party labels on
owned resources.

### Release note

```release-note
[redis-operator] Move operator into tree and patch it to retain
third-party labels on owned resources, reducing noisy traffic to the API
server.
```

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

## Summary by CodeRabbit

* **Chores**
* Implemented automated Docker image build pipeline with version
tracking and caching.
* Updated image configuration to include repository reference and digest
for reproducibility.

* **Bug Fixes**
* Improved label and annotation handling to preserve existing Kubernetes
resource metadata instead of overwriting it.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-27 18:22:29 +04:00
Timofei Larkin
1f784db3f7 [api] Use shared informer cache (#1539)
## What this PR does

This patch changes all clients in the Cozystack API server to typed ones
from the controller runtime. This should improve the performance of the
API server and simplifies the code by removing work with unstructured
objects and dynamic clients.

### Release note

```release-note
[api] Use typed and cache-backed k8s clients in the Cozystack API to
improve performance. Get rid of operations on unstructured objects and
use of dynamic clients.
```

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

* **Refactor**
* Backend migrated to a controller-runtime manager with typed clients
for Kubernetes resources, improving watch reliability and cache sync.
* Storage paths for applications, tenant modules, namespaces, and
secrets now use strongly-typed resource handling for more consistent
behavior.

* **Chores**
  * Cluster role expanded to include services in core API permissions.

* **Notes**
  * No user-facing API schema changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-27 18:00:20 +04:00
Timofei Larkin
f4e0145c1c [api] Use shared informer cache
This patch changes all clients in the Cozystack API server to typed
ones from the controller runtime. This should improve the performance of
the API server and simplifies the code by removing work with
unstructured objects and dynamic clients.

```release-note
[api] Use typed and cache-backed k8s clients in the Cozystack API to
improve performance. Get rid of operations on unstructured objects and
use of dynamic clients.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-27 16:21:23 +03:00
Timofei Larkin
efd96877ab [docs] Changelogs for the v0.36.* series (#1399)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- Documentation
- Added changelogs v0.35.3–v0.35.5 documenting fixes (SeaweedFS S3
liveness/timeouts, VM update-hook regression, Helm merge precedence,
Makefile autodetect removal, etcd topology constraints, test quotas)
with comparison links.
- Published v0.36.0 release notes (feature highlights, major
improvements, dependencies, fixes, CI/dev updates).
- Added v0.36.1 and v0.36.2 release notes and a changelog
template/formatting tweak.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-24 10:53:43 +04:00
Timofei Larkin
5a20693d67 Update docs/changelogs/v0.36.2.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-24 09:52:16 +03:00
Timofei Larkin
448fc61570 [monitoring] add settings alert for slack (#1545)
**What this PR does**
This PR adds configuration for sending alerts from Alerta to Slack.

**Key changes**
Added Slack integration configuration in Alerta settings.
2025-10-24 10:33:08 +04:00
nbykov0
dc0eebd81e [system] kube-ovn: enableLb -> false
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-23 18:51:46 +03:00
Timofei Larkin
a545ff3781 [redis-operator] Build patched operator in-tree
This patch moves the build of the Redis operator into the Cozystack
organization and patches it to prevent overwriting third-party labels on
owned resources.

```release-note
[redis-operator] Move operator into tree and patch it to retain
third-party labels on owned resources, reducing noisy traffic to the API
server.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-23 16:57:27 +03:00
Alexey Artamonov
82cebe3ad7 add settings alert for slack
Signed-off-by: Alexey Artamonov <scooby18@yandex.ru>
2025-10-22 17:22:18 +03:00
Timofei Larkin
184441d82f [ferretdb] Fix tests (#1540)
## What this PR does

Previous test for FerretDB referenced Postgres init jobs, likely copied from other test cases. Removed these references to make tests pass.

### Release note

```release-note
[ferretdb] Remove an erroneous reference to Postgres in the FerretDB test.
```
2025-10-22 11:04:49 +04:00
IvanHunters
ebbc76582c fix tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-22 02:03:33 +03:00
IvanHunters
8e57ac487e fix tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-21 23:57:02 +03:00
IvanHunters
766f6e9a9e fix tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-21 21:38:43 +03:00
Andrei Kvapil
d1fa0e6586 [velero] Set defaultItemOperationTimeout=24h (#1542)
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 changes default timeout for Velero to copy single item. Default
value 4h is not enough for copying large block volumes of virtual
machines.

### 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
[velero] Set defaultItemOperationTimeout=24h
```

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

## Summary by CodeRabbit

* **Chores**
* Extended default operation timeout to 24 hours to provide increased
time for operations to complete.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-21 17:44:50 +02:00
Andrei Kvapil
3e41504b2d Update Kube-OVN v1.14.11 (#1514)
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
- Added configurable OVN IPsec key directory and updated deployments to
mount keys from this path.
- Tightened CRD validation with explicit numeric min/max bounds to
prevent invalid configurations.

- Chores
- Updated kube-ovn chart and container image to v1.14.11 (build
environment updated).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-21 17:42:45 +02:00
Andrei Kvapil
06f68d28d9 [velero] Set defaultItemOperationTimeout=24h
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-21 11:56:22 +02:00
Andrei Kvapil
21de4f7584 Update CODEOWNERS (#1537)
- klinch0
+ nbykov0

<!-- 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
CODEOWNERS updated

### 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
CODEOWNERS updated
```

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

## Summary by CodeRabbit

* **Chores**
  * Updated repository maintenance configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-20 13:57:41 +02:00
Nikita
840c264e86 Update CODEOWNERS
- klinch0
+ nbykov0

Signed-off-by: Nikita <166552198+nbykov0@users.noreply.github.com>
2025-10-20 14:46:10 +03:00
Nikita
bbb92ba497 [system] Add multus (#1526)
<!-- 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
Add multus to system components.


### 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
Added multus to system components.
```

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

* **New Features**
* Added Multus CNI integration enabling multi-network support for
Kubernetes clusters.
* Provided a Helm chart and packaged deployment for Multus, including
required CRD, RBAC, service account, ConfigMap, and DaemonSet to
provision and run the Multus daemon.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-20 14:41:38 +03:00
nbykov0
b163a5913f packages/system: add multus
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2025-10-20 12:54:32 +03:00
Timofei Larkin
ba8a9cc1f7 [kamaji] Respect 3rd party labels (#1531)
## What this PR does

The Kamaji controller overwrites labels on many of the resources it owns
(clastix/kamaji#991). This change applies PR clastix/kamaji#992 to
Cozystack's build of Kamaji, so the lineage webhook doesn't fight the
Kamaji controller, causing a non-stop reconciliation loop.

### Release note

```release-note
[kamaji] Do not clobber third party labels on resources controlled by
Kamaji.
```
2025-10-17 19:49:04 +04:00
Timofei Larkin
b858745cdd [kamaji] Respect 3rd party labels
The Kamaji controller overwrites labels on many of the resources it owns
(clastix/kamaji#991). This change applies PR clastix/kamaji#992 to
Cozystack's build of Kamaji, so the lineage webhook doesn't fight the
Kamaji controller, causing a non-stop reconciliation loop.

```release-note
[kamaji] Do not clobber third party labels on resources controlled by
Kamaji.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-17 15:17:00 +03:00
Andrei Kvapil
d9d6fa1a5a [ci] Fix build from external forks (#1530)
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
[ci] Fix build from external forks
```

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

## Summary by CodeRabbit

* **Chores**
  * Updated continuous integration workflow configuration.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-17 10:40:03 +02:00
Andrei Kvapil
9e635fcc3f [feature] add ferretdb tests (#1464)
<!-- 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
[feature] add ferretdb tests
```

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

## Summary by CodeRabbit

- Tests
- Added an end-to-end test that provisions a FerretDB instance on
Kubernetes, waits for readiness and init completion, verifies
connectivity to read/write Postgres services (including endpoint
convergence), accounts for known RO delays, and performs full cleanup.
Uses timeouts and retries for stability.
- Chores
  - No user-facing product changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-17 08:43:55 +02:00
IvanHunters
2791e3e96a add ferretdb tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-17 09:13:27 +03:00
Andrei Kvapil
1a977bd4b4 [ci] Fix build from external forks
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-16 20:17:28 +02:00
Andrei Kvapil
10516c38ec Update Talos Linux v1.11.3 (#1527)
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
Update Talos Linux v1.11.3
```

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

## Summary by CodeRabbit

* **Chores**
  * Updated version to v1.11.3 across installer profiles.
* Refreshed system extension images to latest available builds with
updated firmware and driver versions.
  * Enhanced output format configuration for improved image generation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-16 19:34:15 +02:00
Andrei Kvapil
1e36722ab8 [seaweedfs] Allow users to discover their buckets
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-16 19:31:51 +02:00
Timofei Larkin
d57f9acc7a [lineage] Check for nil chart in HelmRelease (#1525)
## What this PR does

Some HelmReleases use `chartRef` instead of `chart`. If the lineage
webhook finds such a HelmRelease, a nil pointer dereference happens.
This patch adds a nil check to guard against this.

### Release note

```release-note
[lineage] Add a nil check to guard against HelmReleases with a nil
.spec.chart field when traversing the ownership tree.
```
2025-10-16 18:06:14 +04:00
Timofei Larkin
466f0fed52 [dashboard] Show service LB IP (#1524)
Fix an incorrect JSON path that prevented Service LoadBalancer IPs from
rendering in the table view.
2025-10-16 17:35:16 +04:00
Timofei Larkin
c1edc5d711 [apps] Make VM service user facing (#1523) 2025-10-16 16:58:47 +04:00
Timofei Larkin
354507a4ea [lineage] Check for nil chart in HelmRelease
Some HelmReleases use `chartRef` instead of `chart`. If the
lineage webhook finds such a HelmRelease, a nil pointer dereference
happens. This patch adds a nil check to guard against this.

```release-note
[lineage] Add a nil check to guard against HelmReleases with a nil
.spec.chart field when traversing the ownership tree.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-16 15:52:14 +03:00
Timofei Larkin
a9c2bfb33b [apps] Make VM service user facing
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-16 15:05:23 +03:00
Timofei Larkin
85c9da58de [dashboard] Show service LB IP
Fix an incorrect JSON path that prevented Service LoadBalancer IPs from
rendering in the table view.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-16 14:43:54 +03:00
Andrei Kvapil
b1d5de1006 Update Talos Linux v1.11.3
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-15 18:16:44 +02:00
Andrei Kvapil
af96e3da94 [platform] Better migration for 0.36.2->0.37.2+ (#1521)
## What this PR does

For users upgrading from 0.36.2 directly to 0.37.2+, where the
lineage-controller-webhook is broken out of the Cozystack controller
into a separate daemonset, the existing migration script of 0.36->0.37.0
is insufficient. This patch ensures the presence of the new version of
the lineage webhook and fixes a bug in the migration script where the
readiness of the webhook was not appropriately verified.

### Release note

```release-note
[platform] Improved migration script when skipping versions 0.37.0 and
0.37.1 during upgrades.
```

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

* **New Features**
* Automatically installs the lineage-controller webhook via Helm during
setup, aligning its deployment with existing controller components.

* **Chores**
* Adds a temporary namespace for preflight validation of the webhook
service to avoid cluster-wide side effects.
* Replaces cluster-scoped dry-run checks with namespace-scoped dry-run
and ensures cleanup and timeout-based waits for predictable
installation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-15 12:49:51 +02:00
Timofei Larkin
bf1ece5f7c [platform] Better migration for 0.36.2->0.37.2+
For users upgrading from 0.36.2 directly to 0.37.2+, where the
lineage-controller-webhook is broken out of the Cozystack controller
into a separate daemonset, the existing migration script of 0.36->0.37.0
is insufficient. This patch ensures the presence of the new version of
the lineage webhook and fixes a bug in the migration script where the
readiness of the webhook was not appropriately verified.

```release-note
[platform] Improved migration script when skipping versions 0.37.0 and
0.37.1 during upgrades.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-15 13:28:29 +03:00
Andrei Kvapil
c3f70abc99 Update Kube-OVN v1.14.11
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-15 10:15:20 +02:00
Andrei Kvapil
ea85856b1a [docs] Changelog for v0.37 (#1516)
## What this PR does

Adds summary of changes since the release of v0.36.0 up to v0.37.0.

### Release note

```release-note
[docs] Changelog for v0.37
```

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

* **Documentation**
* Added the public-facing v0.37.0 changelog: “OpenAPI Dashboard &
Lineage Everywhere.”
* Details highlights, new features (Dashboard, Webhook/Lineage,
API/Platform, Monitoring & Ops, Storage & Backups, Kubernetes/Tooling,
UI/Icons), minor improvements, and bug fixes.
* Includes dependency/version notes, refactors/chores, governance,
breaking changes with upgrade guidance, and security/stability
information.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-15 10:09:54 +02:00
Andrei Kvapil
e9bdbcf60d [api] Fix listing tenantnamespaces for non-oidc users (#1517)
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
[api] Fix listing tenantnamespaces for non-oidc users
```

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

* **New Features**
* Namespace access now recognizes group, user, and service-account
subjects when evaluating RBAC bindings, granting access for matching
identities.
* Service accounts are properly recognized and allowed when their
fully-qualified identity matches bindings.

* **Improvements**
* Simplified and more reliable RBAC subject evaluation to reduce missed
eligible namespaces.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-15 10:09:26 +02:00
Andrei Kvapil
43ec8f0877 [bucket] Expose bucket name in secrets (#1518)
## What this PR does

The object storage controller creates secrets with bucket credentials
that have no reference to the parent BucketAccess object. Because of
this they cannot be linked to the managing app
(buckets.apps.cozystack.io) and are not displayed in the new dashboard.
This change patches the auxiliary helm release <bucket_name>-system to
include the bucket name in __its__ secret, so that the necessary secret
values is still presented to the user.

### Release note

```release-note
[bucket] Expose bucket name in tenant secret.
```
2025-10-15 10:02:59 +02:00
Timofei Larkin
1f0edc5f79 [docs] Changelog for v0.37
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-14 23:44:06 +03:00
Timofei Larkin
fe8ec75ac7 [bucket] Expose bucket name in secrets.
The object storage controller creates secrets with bucket credentials
that have no reference to the parent BucketAccess object. Because of
this they cannot be linked to the managing app
(buckets.apps.cozystack.io) and are not displayed in the new dashboard.
This change patches the auxiliary helm release <bucket_name>-system to
include the bucket name in __its__ secret, so that the necessary secret
values is still presented to the user.

```release-note
[bucket] Expose bucket name in tenant secret.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-14 21:54:56 +03:00
Andrei Kvapil
671e13df70 [api] Fix listing tenantnamespaces for non-oidc users
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-14 13:56:24 +02:00
Timofei Larkin
36df9bda05 [lineage] Separate webhook from cozy controller (#1515)
## What this PR does

The lineage-controller-webhook makes a lot of outgoing API calls for
every event it handles, contributing to a high API server latency,
increasing the number of in-flight requests and generally degrading
performance. This patch remedies this by separating the lineage
component from the cozystack-controller and deploying it as a separate
component on all control-plane nodes. Additionally, a new internal label
is introduced to track if a resource has already been handled by the
webhook. This label is used to exclude such resources from
consideration. Addresses #1513.

### Release note

```release-note
[lineage] Break webhook out into a separate daemonset. Reduce
unnecessary webhook calls by marking handled resources and excluding
them from consideration by the webhook's object selector.
```

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

- **New Features**
- Standalone Lineage Controller Webhook deployed as its own DaemonSet
with a dedicated Helm chart and image build targets.
  - Dedicated TLS provisioning for the webhook via chart-managed certs.

- **Changes**
  - Main controller no longer hosts webhook endpoints or certificates.
- Webhook now excludes already-managed resources to reduce unnecessary
invocations.
  - Platform bundles updated to include the new webhook release.

- **Documentation**
  - Changelog updated to reflect the separation and optimization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-14 12:30:43 +04:00
Timofei Larkin
2a82273902 [lineage] Separate webhook from cozy controller
The lineage-controller-webhook makes a lot of outgoing API calls for
every event it handles, contributing to a high API server latency,
increasing the number of in-flight requests and generally degrading
performance. This patch remedies this by separating the lineage
component from the cozystack-controller and deploying it as a separate
component on all control-plane nodes. Additionally, a new internal label
is introduced to track if a resource has already been handled by the
webhook. This label is used to exclude such resources from
consideration. Addresses #1513.

```release-note
[lineage] Break webhook out into a separate daemonset. Reduce
unnecessary webhook calls by marking handled resources and excluding
them from consideration by the webhook's object selector.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-14 10:50:28 +03:00
Andrei Kvapil
bb8f2047bf [api] Fix RBAC for listing of TenantNamespaces and handle system:masters (#1511)
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/1507

### 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
[api] Fix RBAC for listing of TenantNamespaces and handle system:masters
```

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

## Summary by CodeRabbit

- New Features
- System-wide administrators now see all tenant namespaces without
filtering.
- Expanded read access for role bindings to improve visibility of access
configurations.

- Bug Fixes
- Resolved cases where some authorized admins could not view all tenant
namespaces due to RBAC filtering.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 15:20:16 +02:00
Andrei Kvapil
2a508c4f29 [api] Fix RBAC for listing of TenantNamespaces and handle system:masters
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-13 15:16:11 +02:00
Andrei Kvapil
a4cbc7341d [dashboard] Fix logout (#1510)
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
[dashboard] Fix logout
```

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

## Summary by CodeRabbit

* **New Features**
* Enhanced OIDC logout flow: backend logout is now supported, improving
reliability of signing out across services.
* Whitelisted the identity provider domain to enable seamless redirects
during authentication and logout journeys.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 15:00:18 +02:00
Andrei Kvapil
fdc2b8f3ec Add addtional check to wait for lineage-webhook (#1506)
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

* **Chores**
* Added a timeout-based step that repeatedly attempts server-side
dry-run creation of a Kubernetes Service (headless) between controller
upgrade and subsequent waits.
* Inserts this validation step without altering existing flow or other
behaviors.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 15:00:00 +02:00
Andrei Kvapil
e6070210f6 [api] Efficient listing of TenantNamespaces (#1507)
## What this PR does

The Cozystack API server lists TenantNamespaces by running a
SubjectAccessReview against every single requested namespace to see if
the user can create a WorkloadMonitor there. Will this is robust in
terms of permissions, delegating the authorization decision to the k8s
API, this is incredibly inefficient and has caused high latency to the
API. This patch simplifies the logic by instead getting the user's
groups and checking if the namespace contains a rolebinding for that
group. That way listing TenantNamespaces is reduced to a list call to
the k8s API for namespaces and another list call for rolebindings across
all namespaces, while authorization is done on the Cozystack API server
instead of making further calls to the k8s API.

### Release note

```release-note
[api] Optimize listing of TenantNamespaces, fixes a bug causing very
high latency to the k8s API.
```

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

- Bug Fixes
- TenantNamespace visibility now consistently reflects RBAC role
bindings. Cluster administrators see all namespaces; users only see
namespaces they’re permitted to access.

- Refactor
- Access evaluation simplified to rely on role/rolebinding membership,
removing per-namespace authorization calls and improving listing
performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 14:59:44 +02:00
Andrei Kvapil
e1e9eef63b The Cozystack Kubernetes tests are now POSIX-compatible (#1509)
This patch replaces bash-specific [[ ... ]] expressions in the
run_kubernetes_test function with POSIX-compliant case and test
constructs. It ensures that the Kubernetes version on each worker node
is verified correctly and that required components (CoreDNS, Cilium,
ingress-nginx, vsnap-crd) are ready before proceeding. Now the tests
work reliably even when executed with /bin/sh, such as in Bats.

```release-note
[tests] Make Kubernetes tests POSIX-compliant and more reliable:
verify worker node versions and ensure required releases (CoreDNS,
Cilium, ingress-nginx, vsnap-crd) are installed and ready.
```

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

* **Bug Fixes**
* Improved Kubernetes version detection to correctly handle 1.32
variants.
* Made node readiness checks more reliable to reduce false failures
during runs.

* **Refactor**
* Streamlined version matching logic for clearer, more predictable
behavior across releases.

* **Style**
  * Minor formatting cleanups with no functional impact.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 14:59:15 +02:00
Andrei Kvapil
f2a8e2d45d [dashboard] Fix logout
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-13 14:50:01 +02:00
IvanHunters
1a49cbef2d The Cozystack Kubernetes tests are now POSIX-compatible and more robust.
This patch replaces bash-specific [[ ... ]] expressions in the
run_kubernetes_test function with POSIX-compliant case and test
constructs. It ensures that the Kubernetes version on each worker node
is verified correctly and that required components (CoreDNS, Cilium,
ingress-nginx, vsnap-crd) are ready before proceeding. Now the tests
work reliably even when executed with /bin/sh, such as in Bats.

```release-note
[tests] Make Kubernetes tests POSIX-compliant and more reliable:
verify worker node versions and ensure required releases (CoreDNS,
Cilium, ingress-nginx, vsnap-crd) are installed and ready.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-13 14:21:59 +03:00
Timofei Larkin
346dce83d4 [api] Efficient listing of TenantNamespaces
The Cozystack API server lists TenantNamespaces by running a
SubjectAccessReview against every single requested namespace to see if
the user can create a WorkloadMonitor there. Will this is robust in
terms of permissions, delegating the authorization decision to the k8s
API, this is incredibly inefficient and has caused high latency to the
API. This patch simplifies the logic by instead getting the user's
groups and checking if the namespace contains a rolebinding for that
group. That way listing TenantNamespaces is reduced to a list call to
the k8s API for namespaces and another list call for rolebindings across
all namespaces, while authorization is done on the Cozystack API server
instead of making further calls to the k8s API.

```release-note
[api] Optimize listing of TenantNamespaces, fixes a bug causing very
high latency to the k8s API.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-13 13:02:16 +03:00
Andrei Kvapil
4d8dca8049 Add addtional check to wait for lineage-webhook
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-13 10:39:41 +02:00
Timofei Larkin
7a19215f0a [foundationdb] Upgrade FDB app for latest Cozy (#1505)
## What this PR does

Since this contribution was made, the layout of the repository changed
significantly. This patch addresses these updates and brings the
FoundationDB managed app into harmony with the new structure.

### Release note

```release-note
[foundationdb, maintenance] Harmonize FoundationDB repo layout with
v0.37.0 repository structure.
```

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

## Summary by CodeRabbit

* **New Features**
* Introduced a standalone FoundationDB resource with dashboard metadata,
enabling configuration of backups, cluster sizing, security, and storage
via Cozystack.

* **Documentation**
  * Updated README to clarify the default for resources is now null.

* **Refactor**
* Simplified FoundationDB schema by removing prefilled defaults; many
sections now default to empty objects, requiring explicit user
configuration.

* **Chores**
* Retired legacy template references and centralized version mappings;
FoundationDB is now provided through the new resource catalog structure.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 15:24:24 +04:00
Timofei Larkin
2d9dc9fe01 [foundationdb] Upgrade FDB app for latest Cozy
Since this contribution was made, the layout of the repository changed
significantly. This patch addresses these updates and brings the
FoundationDB managed app into harmony with the new structure.

```release-note
[foundationdb, maintenance] Harmonize FoundationDB repo layout with
v0.37.0 repository structure.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-10 12:50:22 +03:00
Timofei Larkin
d86742eb36 [apps] [foundationdb] Initial support for FoundationDB operator (#1397)
<!-- 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 adds initial support to begin testing managed FoundationDB
instances in Cozystack. Addresses #824

### 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
[apps] Add FoundationDB as a managed app for tenants
[foundationdb] Add fdb-kubernetes-operator with v2.13.0
```

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

- **New Features**
- Added managed FoundationDB app and operator with Helm charts to deploy
configurable clusters (storage, resources, backups, monitoring, workload
monitor).

- **Documentation**
- Added comprehensive README, default values, and a JSON Schema for
chart configuration and validation.

- **Tests**
- Added end-to-end test provisioning a FoundationDB cluster, validating
rollout, health, monitoring artifacts, security context, storage claims,
and cleanup.

- **Chores**
- Added packaging targets, operator charts and CRDs, API schema
registration, versions map entry, and bundle release entries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 13:01:32 +04:00
Timofei Larkin
857416d1d2 [maintenance] Resolve merge conflicts
Resolve conflicts for FoundationDB feature by @insignia96

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-10 11:58:01 +03:00
Timofei Larkin
072aa9ebc0 Release v0.37.0 (#1504)
This PR prepares the release `v0.37.0`.
2025-10-10 12:27:31 +04:00
cozystack-bot
aff8b0c30a Prepare release v0.37.0
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-10-10 07:46:34 +00:00
Andrei Kvapil
51883cfc69 Release v0.37.0-beta.2 (#1496)
This PR prepares the release `v0.37.0-beta.2`.

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

- Chores
- Upgraded many platform components and container images to
v0.37.0-beta.2 (installer, controllers, API, dashboard services,
networking, storage, MySQL backup, KubeVirt CSI, NGINX cache, and
related sidecars). Image digests/tags updated only; no user-facing
configuration or behavior changes.
- Style
  - Dashboard tenant version now shown as v0.37.0-beta.2.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 02:54:15 +02:00
Andrei Kvapil
29a6cdec05 Update MAINTAINERS.md (#1491)
Exclude Andrei Gumilev

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

* **Documentation**
* Updated the Maintainers documentation to reflect the current team by
removing an outdated entry.
* Improves accuracy of ownership and contact information for project
stewardship.

* **Chores**
* Performed repository housekeeping to keep governance information
current.
  * No changes to product functionality; no user-facing impact.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 02:29:30 +02:00
Andrei Kvapil
929dae8e24 Update CONTRIBUTOR_LADDER.md (#1492)
<!-- 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

* **Documentation**
* Updated the Contributor Ladder guide title for clarity and consistency
across the documentation.
* Adjusted in-page navigation to point to the correct section, ensuring
links align with the updated heading.
* Standardized section anchors to improve reliability of internal
navigation.
* Improved readability and structure without affecting product
functionality or user workflows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 02:28:56 +02:00
cozystack-bot
a50f53de2e Prepare release v0.37.0-beta.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-10-10 00:21:12 +00:00
Andrei Kvapil
484211f7a0 [kubernetes] fix: spec.selector: Required value (#1502)
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

- **Bug Fixes**
- Corrected MachineDeployment label selectors to match existing template
labels, ensuring resources are properly targeted and managed.
- Improves reliability of scaling and rolling updates by preventing
orphaned or unmanaged machines/pods.
- Aligns selectors with cluster and deployment labels, enabling
consistent behavior across environments.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-10 02:15:36 +02:00
Andrei Kvapil
b6eefe4453 [dashboard] Remove Tenant resource from Marketplace; fix field override when typing (#1503)
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-10-10 02:14:19 +02:00
Andrei Kvapil
3b9fa33240 [dashboard] Remove Tenant resource from Marketplace; fix field override when typing
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-10 02:13:12 +02:00
Andrei Kvapil
9184450b39 [kubernetes] fix: spec.selector: Required value
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-10 02:01:41 +02:00
Andrei Kvapil
59f42de1db [dashboard] Fix listing modules (#1501)
<img width="2620" height="1970" alt="image"
src="https://github.com/user-attachments/assets/a8d0417b-214f-4c6c-8cab-2539043c62e8"
/>

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-10-10 01:25:38 +02:00
Andrei Kvapil
2ae926d04e [dashboard] Fix listing modules
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-10 01:24:10 +02:00
Andrei Kvapil
0ba4d4494e [dashboard] Add filter for tenantresources (#1500)
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-10-09 23:42:59 +02:00
Andrei Kvapil
19c91071d8 [dashboard] Add filter for tenantresources
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-09 23:42:08 +02:00
Andrei Kvapil
9ce3f8e53f [dashboard] fix yaml highlighting and handle x-preserve-unknown-fields (#1499)
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

* **Chores**
* Updated the OpenAPI UI container build to use a newer underlying
toolkit revision, refreshing dependencies and ensuring alignment with
upstream.
* Improves build reliability and maintainability with routine
configuration maintenance.
  * No user-facing features or behavior changes are expected.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-09 23:41:45 +02:00
Andrei Kvapil
7eb701d846 [dashboard] fix yaml highlighting and handle x-preserve-unknown-fields
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-09 23:33:10 +02:00
Andrei Kvapil
40a3ec1e70 [installer] Even more rigorous migration (#1498)
## What this PR does

Due to a deficiency of cozypkg (--with-source reconciles the HelmChart,
but not the HelmRepository), we have to use workarounds to bulletproof
the latest migration, by applying directly from the assets server.

### Release note

```release-note
[installer] Run 20th migration using helm charts directly from the
assets server instead of relying on cozypkg to reconcile its resources
properly.
```

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

- **Bug Fixes**
- Mutating webhook now excludes both the default and kube-system
namespaces to avoid unintended mutations of core workloads.

- **Chores**
- Hardened migration sequence: ordered release removals with waits,
switched to packaged apply steps with short pauses, added readiness
checks, removed obsolete webhook upgrade/reconciliation, and
standardized RFC3339(nano) migration stamping.
- Removed bundled resource-definition CRD and adjusted CRD
generation/output handling.
- Installer image now includes Helm as a runtime/build-time dependency.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-09 23:31:00 +02:00
Timofei Larkin
43c222decf [installer] Even more rigorous migration
Due to a deficiency of cozypkg (--with-source reconciles the HelmChart,
but not the HelmRepository), we have to use workarounds to bulletproof
the latest migration, by applying directly from the assets server.

```release-note
[installer] Run 20th migration using helm charts directly from the
assets server instead of relying on cozypkg to reconcile its resources
properly.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-09 21:34:14 +03:00
Timofei Larkin
13dccea84b [lineage] Use an auto-refreshing RESTMapper (#1497)
## What this PR does

Since the Cozystack extension API can now change dynamically while there
are live clients (the lineage webhook) querying this API, the REST
mapper of the client should "expect" that things may change and refresh
their discovery information when they get a cache miss to see if new
kinds have been registered.

### Release note

```release-note
[lineage] Use an auto-refreshing RESTMapper in the webhook's API client
that tries to update its API discovery info when it fails to GET a
resource kind that was previously not registered in its schema.
```

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

- Refactor
- Streamlined webhook initialization by removing redundant
discovery/cache components, reducing startup complexity and overhead.
- Improved error handling during webhook setup for clearer diagnostics
on manager startup.
- Reduced runtime dependencies to improve reliability across diverse
cluster environments.
- Minor import and initialization cleanups to align with current
controller-runtime practices.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-09 15:42:30 +04:00
Timofei Larkin
e2b4cd8bd0 [lineage] Use an auto-refreshing RESTMapper
Since the Cozystack extension API can now change dynamically while there
are live clients (the lineage webhook) querying this API, the REST
mapper of the client should "expect" that things may change and refresh
their discovery information when they get a cache miss to see if new
kinds have been registered.

```release-note
[lineage] Use an auto-refreshing RESTMapper in the webhook's API client
that tries to update its API discovery info when it fails to GET a
resource kind that was previously not registered in its schema.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-09 13:12:06 +03:00
Andrei Kvapil
ad2858e113 Update CODE_OF_CONDUCT.md (#1493)
<!-- 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

* **Documentation**
* Expanded the Code of Conduct with a Vendor Neutrality Manifesto
outlining commitments, principles, and expectations for interactions
with vendors and community members.
* Added an affirmation and signature section to reinforce accountability
and clarity.
* Clarifies standards for fair, transparent collaboration and community
engagement.
* No product or UI changes; this update improves guidance for
contributors and partners.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-09 09:22:42 +02:00
Andrei Kvapil
c6e9131f60 [oidc] Check APIVersions before deploying (#1495)
## What this PR does

When enabling OIDC, the Tenant applications may try to deploy
KeycloakRealmGroups before the Keycloak operator is live. This may lead
to a race where neither HelmRelease is able to progress. This patch
addresses this.

### Release note

```release-note
[oidc] Do not deploy KeycloakRealmGroup resources as part of the Tenant
application if the v1.edp.epam.com API is not yet available.
```

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improves deployment reliability by conditionally creating the initial
Keycloak realm group only when the required API version is available.
This prevents install/upgrade failures in environments lacking the
corresponding CRD.
* Other Keycloak realm groups continue to be created as before, ensuring
no change to existing group provisioning where supported.
* Enhances cross-environment compatibility for tenant deployments
without impacting users on fully supported clusters.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-08 22:32:09 +02:00
Timofei Larkin
45036ff249 [oidc] Check APIVersions before deploying
When enabling OIDC, the Tenant applications may try to deploy
KeycloakRealmGroups before the Keycloak operator is live. This may
lead to a race where neither HelmRelease is able to progress. This patch
addresses this.

```release-note
[oidc] Do not deploy KeycloakRealmGroup resources as part of the Tenant
application if the v1.edp.epam.com API is not yet available.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-08 20:37:21 +03:00
Timofei Larkin
6dd08947ae [api,platform] Decouple CozyRDs from API HR (#1494)
## What this PR does

This commit patches the Cozystack API server to tolerate an absence of
Cozystack Resource Definitions either registered as CRDs on the k8s API
or simply as an absence of CozyRDs persisted to etcd. This decouples the
upgrade of the CozyRD CRD from the upgrade of the Cozystack API.

### Release note

```release-note
[api,platform] Decouple the Cozystack API from the Cozystack Resource
Definitions, allowing independent upgrades of either one and a more
reliable migration from 0.36 to 0.37.
```

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

## Summary by CodeRabbit

- New Features
- Introduced Cozystack Resource Definition CRD and charts, now included
in hosted and full bundles to provision CRDs before dependent
components.

- Bug Fixes
- Improved startup reliability by retrying resource discovery with
exponential backoff, reducing failures on slow cluster readiness.
- OpenAPI generation no longer errors when no kinds are present,
preventing unnecessary startup failures.

- Chores
  - Added packaging scaffolding and default values for new charts.
  - Updated internal script paths for CRD generation outputs.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-08 18:35:37 +04:00
Timofei Larkin
a1fd97f2d7 Update issue templates (#1408)
Add an issue template for bug reports.
2025-10-08 18:31:55 +04:00
Timofei Larkin
8076f120d8 Update .github/ISSUE_TEMPLATE/bug_report.md
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-08 17:30:56 +03:00
Timofei Larkin
4e766ed82e [api,platform] Decouple CozyRDs from API HR
This commit patches the Cozystack API server to tolerate an absence of
Cozystack Resource Definitions either registered as CRDs on the k8s API
or simply as an absence of CozyRDs persisted to etcd. This decouples the
upgrade of the CozyRD CRD from the upgrade of the Cozystack API.

```release-note
[api,platform] Decouple the Cozystack API from the Cozystack Resource
Definitions, allowing independent upgrades of either one and a more
reliable migration from 0.36 to 0.37.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-08 16:18:47 +03:00
Timur Tukaev
d42f4b1097 Update CODE_OF_CONDUCT.md
Signed-off-by: Timur Tukaev <90071493+tym83@users.noreply.github.com>
2025-10-08 09:43:34 +05:00
Timur Tukaev
6b6cee8103 Update CONTRIBUTOR_LADDER.md
Signed-off-by: Timur Tukaev <90071493+tym83@users.noreply.github.com>
2025-10-08 09:28:27 +05:00
Timur Tukaev
7f62e14e86 Update MAINTAINERS.md
Exclude Andrei Gumilev

Signed-off-by: Timur Tukaev <90071493+tym83@users.noreply.github.com>
2025-10-08 09:16:26 +05:00
Timur Tukaev
a369171a20 Create CONTRIBUTOR_LADDER.md (#1224)
Contributor ladder is an important tool for community participants who
are loyal to project and would like to take more responsibility in
project. Besides, it's needed for CNCF Incubated applications

<!-- 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
Adding description on how community member might become a contributor
and a project maintainer.

### Release note
v0.1

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

## Summary by CodeRabbit

* **Documentation**
* Added a contributor ladder document outlining roles, responsibilities,
and progression paths for project contributors, including policies on
advancement, inactivity, and removal. Links to related resources and
contact information are also provided.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-08 09:15:11 +05:00
Andrei Kvapil
dfcdf19554 Release v0.37.0-beta.1 (#1490)
This PR prepares the release `v0.37.0-beta.1`.

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

## Summary by CodeRabbit

- Chores
- Pinned multiple container images from “latest” to specific versions
and refreshed digests for improved stability and reproducibility.
- Upgraded core components from v0.37.0-alpha.2 to v0.37.0-beta.1 across
installer, API, controller, dashboard services, Kamaji, kubeovn tools,
and object storage sidecar/controller.
- Updated Cilium to 1.17.8 and refreshed digests for KubeOVN, MetalLB,
Grafana, and related apps.
- Documentation
  - Dashboard branding text updated to display v0.37.0-beta.1.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 23:02:48 +02:00
cozystack-bot
458ca63729 Prepare release v0.37.0-beta.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-10-06 20:18:29 +00:00
Timofei Larkin
1ee3d00128 [etcd] Add VPA for etcd (#1489)
## What this PR does

The etcd tenant module deploys by default with a large resource
limit/request and these values are not exposed at deploy time. This
patch lowers the default resources and adds a VPA to autoconfigure them
according to the real needs.

### Release note

```release-note
[etcd] Attach VPA to etcd and lower initial default resource requests.
```

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

- New Features
- Enabled automatic resource autoscaling for etcd with a Vertical Pod
Autoscaler (VPA).

- Chores
- Updated default etcd resource requests to CPU 1000m and memory 512Mi
(previously 4 and 1Gi), reflected across chart values and API schema.
  - Changed the output location for generated CRDs.

- Documentation
- Revised README to document the new default CPU and memory values for
etcd.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 23:43:40 +04:00
Andrei Kvapil
00199a788a Upd Velero v1.17.0 (#1484)
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

- enables nodeAgent by default
- fixes https://github.com/cozystack/cozystack/issues/1442

### 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
- Per-repository maintenance via ConfigMap with global and repo-specific
settings.
- PodVolumeBackup/Restore: cancel requests, progress reporting,
node/uploader visibility, expanded phases.
  - New volumeGroupSnapshotLabelKey on Backups and Schedules.
  - DataUpload: specify CSI driver.
  - Metrics Service: ipFamilyPolicy and ipFamilies support.
  - Optional container resizePolicy.

- Changes
  - Upgraded to Velero 1.17.0; Helm chart v11.0.0.
  - Deployment name standardized to “velero”.
  - Node agent enabled by default.
  - Templates now block deprecated options with clear error messages.

- Documentation
- Expanded README on repository maintenance, deprecations, and upgrade
guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 21:00:45 +02:00
Andrei Kvapil
dfb0838a1e feat/impruvement-kubernetes-tests (#1485)
<!-- 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
Improved tests for verifying installed kubernetes client clusters
```

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

* New Features
* Added node readiness checks (expected node count, detailed node
display) and kubelet version validation with compatibility handling.

* Improvements
* Increased API port-forward timeout and extended rollout/machine
deployment waits for more reliable rollouts.
  * Added per-component readiness waits for core cluster services.

* Chores
  * Bumped default Kubernetes version to v1.33.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 20:59:59 +02:00
Timofei Larkin
42c9d65c7c [etcd] Add VPA for etcd
The etcd tenant module deploys by default with a large resource
limit/request and these values are not exposed at deploy time. This
patch lowers the default resources and adds a VPA to autoconfigure them
according to the real needs.

```release-note
[etcd] Attach VPA to etcd and lower initial default resource requests.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-06 21:57:33 +03:00
Andrei Kvapil
4afda63440 Upd Velero v1.17.0
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-06 17:59:48 +02:00
Andrei Kvapil
50b8dda38e Add me to MAINTAINERS.md (#1487)
Add me to MAINTAINERS

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

## Summary by CodeRabbit

* **Documentation**
* Added Nikita Bykov to the public maintainers list, including name,
GitHub handle, company, and area of responsibility.
* Ensures the maintainer roster is current and transparent for
contributors and users seeking points of contact.
  * No product functionality, UI, or API behavior changes.
  * Helps improve project governance visibility and support routing.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 12:46:26 +02:00
Andrei Kvapil
e3cebeab47 [kafka] Disable noisy alerts (#1488)
## What this PR does

The alerts deployed with the Kafka Strimzi operator are noisy and not
useful, when a given namespace does not deploy any kafka clusters. This
patch removes them.

### Release note

```release-note
[kafka] Disable useless alerts for Kafka which fire when not called for,
e.g. when Kafka isn't deployed.
```

fixes https://github.com/cozystack/cozystack/issues/790

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

## Summary by CodeRabbit

* **Chores**
* Temporarily disabled rendering of monitoring alert snippets for the
Kafka Operator, resulting in no alerts being generated from this
component.
* Keeps existing deployments unaffected beyond the absence of these
alerts; no configuration changes required by users.
* Preserves previous alert definitions internally for potential
reactivation in a future update.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 12:45:53 +02:00
Timofei Larkin
e986e7c16a [controller, api] Select ingresses and services (#1486)
## What this PR does

This patch extends the resource-selecting function of the webhook to
also apply selectors to ingresses and services, like has been already
done for secrets. The Cozystack resource definitions have been upgraded
to contain two more fields: `ingresses` and `services` and populated
with counterparts of the legacy selectors from the dashboard roles.

### Release note

```release-note
[controller, api] Enable marking ingresses and services as user-facing
and implement selectors for existing CozystackResourceDefinitions.
```

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

* **New Features**
* CRD and API now support selecting Services and Ingresses alongside
Secrets.
* Lineage/labeling logic updated to evaluate Services and Ingresses when
computing tenant/resource labels.
* System resource definitions updated to expose Service/Ingress
selectors across many system apps (Bucket, Bootbox, ClickHouse, etcd,
Ferretdb, Ingress, Kafka, Kubernetes, Monitoring, MySQL, NATS, Postgres,
RabbitMQ, Redis, SeaweedFS, VM Instance, VPN).
* VM service templates add apps.cozystack.io/user-service: "true" label.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 13:41:17 +04:00
Timofei Larkin
1f158fa909 [kafka] Disable noisy alerts
The alerts deployed with the Kafka Strimzi operator are noisy and not
useful, when a given namespace does not deploy any kafka clusters. This
patch removes them.

```release-note
[kafka] Disable useless alerts for Kafka which fire when not called for,
e.g. when Kafka isn't deployed.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-06 12:23:08 +03:00
Nikita
13dba1b8b4 Add me to MAINTAINERS.md
Signed-off-by: Nikita <166552198+nbykov0@users.noreply.github.com>
2025-10-06 12:17:48 +03:00
Timofei Larkin
9b0f919052 [controller, api] Select ingresses and services
This patch extends the resource-selecting function of the webhook to
also apply selectors to ingresses and services, like has been already
done for secrets. The Cozystack resource definitions have been upgraded
to contain two more fields: `ingresses` and `services` and populated
with counterparts of the legacy selectors from the dashboard roles.

```release-note
[controller, api] Enable marking ingresses and services as user-facing
and implement selectors for existing CozystackResourceDefinitions.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-06 11:31:09 +03:00
Andrei Kvapil
da0eb7a829 Update cilium v1.17.8 (#1473)
<!-- 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
- Added hooks to inject extra volumes/volumeMounts and a configurable
dnsPolicy for cilium-agent.
  - Introduced podSecurityContext.seccompProfile (type: Unconfined).

- Bug Fixes
- Refined kubeProxyReplacement-driven settings (healthz bind,
hostPort/nodePort) and broadened Hubble IPv6 preference logic.
  - Removed externalIPs configuration.

- Documentation
- Updated README to reflect new versions, image digests, security
context, and removed externalIPs references.

- Chores
- Bumped Cilium and related images to v1.17.8, Hubble UI to v0.13.3,
Envoy to v1.33.9; refreshed image digests and Dockerfile default
version.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-03 14:46:44 +02:00
IvanHunters
012906cd59 feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 10:54:09 +03:00
IvanHunters
f2cd585b45 feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 10:42:28 +03:00
IvanHunters
6937b8e2b6 feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 10:33:28 +03:00
IvanHunters
a8562f03d1 feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 10:25:34 +03:00
IvanHunters
2383bc9f13 feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 09:30:58 +03:00
IvanHunters
670341f6bd feat/impruvement-kubernetes-tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-10-03 09:07:14 +03:00
Andrei Kvapil
945887f30d [seaweedfs] Fix timeout while uploading hude files (#1483)
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

related to https://github.com/seaweedfs/seaweedfs/pull/7294

### 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**
* Allow supplying extra S3 server startup arguments via configuration,
enabling custom runtime flags for the S3 service.

* **Chores**
* Set default S3 idle timeout to 60 seconds for improved default
connection handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-02 18:46:26 +02:00
Andrei Kvapil
408b8dde3a [seaweedfs] Fix timeout while uploading hude files
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-10-02 13:00:07 +02:00
Timofei Larkin
295fdf1b8e [lineage, controller] Implement name selectors (#1477)
## What this PR does

This patch implements name-based selectors for
`CozystackResourceDefinitions.spec.secrets`. Application developers may
now specify secrets that should or should not be visible to end users by
specifying a `resourceNames` field with a string slice of acceptable
names. This will, for instance, let developers exclude a secret like
`postgres-dbname-superuser` that has a predictable name even if it does
not have predictable labels. Simple templates are supported, so
`postgres-{{ .name }}-superuser` is also a valid entry under
`resourceNames`.

### Release note

```release-note
[lineage, controller] Let application developers determine resource
visibility for end users by name, as well as by labels.
```

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

- **New Features**
- Resource selectors now support exact resource-name filtering (with
templating); include/exclude selectors operate at the resource level and
require both label and name matches.

- **Chores**
- Many service templates switched from label-based default exclusions to
empty excludes with explicit name-based includes.
- Updated several component image tags to latest and refreshed CRD
packaging/templates.
- Standardized tenant-resource label keys/values and adjusted secret
labels in manifests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-01 18:35:05 +04:00
Kingdon B
6bd7d3add5 Flux Operator v0.30.0
Signed-off-by: Kingdon B <kingdon@urmanac.com>
2025-10-01 10:12:38 -04:00
Timofei Larkin
8d50dfb73f [controller,api] Specify visible secrets
This patch carries the selectors for secrets to be shown to end users
over from the legacy dashboard-resourcemap roles into the new
CozystackResourceDefinition selectors. Also a {{ .namespace }} template
variable is added to the variables supported in the `resourceNames`
field in the selector.

```release-note
[controller,api] Support {{ .namespace }} in `resourceNames` resource
selectors, add whitelist of secrets to show to end-users.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-01 16:56:52 +03:00
Timofei Larkin
c16e37e079 [controller,api] Refactor tenant resource label
This patch refactors the secret selectors to use the
`internal.cozystack.io/tenantresource` label for managing secret
visibility and removes any selectors based on it or the previous
`apps.cozystack.io/tenantresource` label, the idea being that this label
will only ever be set by the controller.

```
[controller,api] Refactor labels for the secret selector.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-01 13:24:40 +03:00
Timofei Larkin
66004c83e2 [lineage, controller] Implement name selectors
This patch implements name-based selectors for
`CozystackResourceDefinitions.spec.secrets`. Application developers may
now specify secrets that should or should not be visible to end users by
specifying a `resourceNames` field with a string slice of acceptable
names. This will, for instance, let developers exclude a secret like
`postgres-dbname-superuser` that has a predictable name even if it does
not have predictable labels. Simple templates are supported, so
`postgres-{{ .name }}-superuser` is also a valid entry under
`resourceNames`.

```release-note
[lineage, controller] Let application developers determine resource
visibility for end users by name, as well as by labels.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-10-01 12:29:04 +03:00
Andrei Kvapil
86d6706ee1 Release v0.37.0-alpha.2 (#1481)
This PR prepares the release `v0.37.0-alpha.2`.

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

## Summary by CodeRabbit

- Chores
- Bumped platform version to v0.37.0-alpha.2 across core and system
components.
- Updated images for installer, e2e sandbox, API, controller, dashboard
(OpenAPI UI, K8s BFF, token proxy), Kamaji, kube-ovn (webhook and core),
object storage controller, SeaweedFS sidecar, S3 manager, and nginx
cache.
- Synchronized version string displayed in the dashboard to
v0.37.0-alpha.2.
- No functional behavior changes; updates focus on image/tag refreshes
and digests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-30 20:28:03 +02:00
cozystack-bot
6de14d679d Prepare release v0.37.0-alpha.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-30 17:46:53 +00:00
Andrei Kvapil
da13a6a2e5 [dashboard] fix: showing secrets with empty values (#1480)
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
[dashboard] fix: showing secrets with empty values
```

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

* **Bug Fixes**
* Fixed base64 extraction for secret data in the dashboard so secret
values are parsed and shown correctly.
* Addresses cases where secret fields could appear blank or incorrect
due to parsing issues.
* Improves consistency and reliability of secret-related columns across
dashboard views, reducing confusion and manual checks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-30 19:34:51 +02:00
Andrei Kvapil
82926a8b2a [dashboard] fix: showing secrets with empty values
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-30 19:04:40 +02:00
Andrei Kvapil
cbc7070269 feature/make info app unconditionally (#1474)
<!-- 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
- make info app unconditionally
```

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

- New Features
- Dashboard resource mapping now adapts to the OIDC setting, switching
resource names and RBAC subjects accordingly for OIDC and non-OIDC
environments.
- Bug Fixes
- Helm release is now consistently deployed without being gated by the
OIDC flag, ensuring reliable rollout across environments.
- Refactor
- Introduced configuration-driven branching for resource names and
access subjects in the dashboard, improving alignment with environment
settings and reducing manual adjustments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-30 12:13:05 +02:00
Andrei Kvapil
94375f3161 [seaweedfs] Fix setting size for multi-dc volumes (#1476)
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 setting size for multi-dc volumes
```

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

* **Bug Fixes**
* Per-zone data directory size now falls back to the global volume size
value when a zone doesn’t specify one, fixing incorrect fallback
behavior.
* Users relying on the previous fallback may need to set zone-specific
sizes to preserve prior behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-30 12:11:28 +02:00
Andrei Kvapil
0bdc801d9a Fix migration to v0.37.0 (#1475)
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

* **Bug Fixes**
* Prevented unintended deletion of platform resource definitions during
migrations.
* Made timestamp generation resilient to environment differences to
avoid script failures.
* Made annotation steps tolerate failures so migrations continue if
overwrite fails.

* **Chores**
* Re-enabled automatic chart update path and added periodic
reconciliation to keep platform components up to date.

* **Refactor**
* Switched VM cloud-init to use native Kubernetes Secret for improved
compatibility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-30 12:11:17 +02:00
Timofei Larkin
4e618adf0a [monitoring] Add whitelist for labels in cadvisor/kubelet metrics
This patch introduces a whitelist-based label filtering mechanism in
cadvisor/kubelet metrics collection. By explicitly keeping only the
desired labels, we avoid noisy and high-cardinality dimensions while
retaining meaningful CPU metrics for analysis.

This improves the stability of the metrics pipeline and ensures
consistent visibility into application workloads.

```release-note
[monitoring] Introduce whitelist label filtering for cadvisor/kubelet
metrics to reduce noise and improve CPU metric reliability.
```
2025-09-30 13:21:38 +04:00
IvanHunters
8601299a91 [platform] Add whitelist for labels in cadvisor/kubelet metrics
This patch introduces a whitelist-based label filtering mechanism in
cadvisor/kubelet metrics collection. By explicitly keeping only the
desired labels, we avoid noisy and high-cardinality dimensions while
retaining meaningful CPU metrics for analysis.

This improves the stability of the metrics pipeline and ensures
consistent visibility into application workloads.

```release-note
[platform] Introduce whitelist label filtering for cadvisor/kubelet
metrics to reduce noise and improve CPU metric reliability.
```

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-09-30 11:04:19 +03:00
kklinch0
65bee1a8dc feature/make info app unconditionally
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-09-30 00:40:28 +03:00
Andrei Kvapil
ffb1b89d2e [seaweedfs] Fix setting size for multi-dc volumes
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-29 16:57:16 +02:00
Andrei Kvapil
bb9db7fcaf Fix migration to v0.37.0
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-29 08:44:29 +02:00
Andrei Kvapil
1753df590e Update Cilium v1.17.8
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-26 15:08:37 +02:00
Andrei Kvapil
7c1e103197 Release v0.37.0-alpha.1 (#1467)
This PR prepares the release `v0.37.0-alpha.1`.

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

- New Features
  - None
- Bug Fixes
  - None
- Chores
- Pinned many container images to explicit versioned tags and digests
for stability and reproducibility.
- Upgraded core components to v0.37.0-alpha.1 (installer, API,
controller, dashboard services, Kamaji, object storage, sidecars).
- Updated third-party images: Cilium 1.17.5, Kube-OVN v1.14.5, MetalLB
digests, s3manager v0.5.0, Ubuntu container disk v1.32, Grafana 0.0.0.
- Style
  - Dashboard branding updated to show v0.37.0-alpha.1.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-26 12:11:03 +02:00
cozystack-bot
93ddc4e2c4 Prepare release v0.37.0-alpha.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-26 08:34:15 +00:00
Andrei Kvapil
ded6a9fd69 Flux Operator 0.29.0 (#1466)
Release tag:

*
https://github.com/controlplaneio-fluxcd/flux-operator/releases/tag/v0.29.0

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

## Summary by CodeRabbit

- New Features
- Added multitenancy workload identity support, including enablement
toggle and default workload identity service account.
- FluxInstance gains fields for multitenant workload identity and
default service accounts, plus schema validations for safer configs.
- ResourceSet introduces input strategy (Flatten/Permute) and enhanced
input provider references and validations.

- Documentation
- Updated README to reflect new multitenancy settings and version
badges.

- Chores
- Bumped Helm chart versions/appVersions to 0.29.0 across related
charts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-26 10:25:01 +02:00
Andrei Kvapil
bff8a5b8c7 [kubernetes] Fix coredns tag (#1469)
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

fixes https://github.com/cozystack/cozystack/issues/1468

### 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**
* Pinned CoreDNS image to registry.k8s.io/coredns/coredns:v1.12.4 for
consistent, reproducible deployments.
  * Confirmed replica count remains at 2 (no scaling changes).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-26 10:23:57 +02:00
Andrei Kvapil
8be9ac48ba [kubernetes] Fix coredns tag
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-26 09:47:51 +02:00
Kingdon B
fcab75177e Flux Operator 0.29.0
Signed-off-by: Kingdon B <kingdon@urmanac.com>
2025-09-25 20:40:44 -04:00
Andrei Kvapil
b13ce92024 [dashboard] Cumulative fixes (#1465)
- **Exclude bootbox from marketplace**
- **[dashboard] fix: disable auto-expanding**

<!-- 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-25 23:38:47 +02:00
Andrei Kvapil
ab11b8e4dd [dashboard] fix: disable auto-expanding
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 23:36:46 +02:00
Andrei Kvapil
e9403425a7 Exclude bootbox from marketplace
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 23:36:38 +02:00
Andrei Kvapil
28cd5bebf2 [dashboard] Cumulative fixes (#1463)
- **[dashboard] Fix API group for the applications**
- **fix sidebars**
- **Introduce module parameter**
- **fix keysAndTags for info**
- **always prefill name in dashboard**
- **Add factory for ingress resources**
- **Add formated tables for tenantnamespaces**

<!-- 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
- Module-aware dashboards: module resources are grouped under “Modules”
in the sidebar and shown as “Tenant Modules” in breadcrumbs.
- New Kubernetes details views for Services, Secrets, and Ingresses with
enriched tabs and ingress rules.

- Improvements
  - Marketplace hides module resources to reduce clutter.
  - Consistent navigation and links aligned to tenant namespaces.
- Sidebars expanded with additional built-in Kubernetes entries and
per-resource detail sidebars.
- Custom forms now always prefill the name field for smoother creation
flows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 20:10:10 +02:00
Andrei Kvapil
364cba3100 Add formated tables for tenantnamespaces
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:01:07 +02:00
Andrei Kvapil
dd76166e44 Add factory for ingress resources
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:01:07 +02:00
Andrei Kvapil
ef7dcabe64 always prefill name in dashboard
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:01:06 +02:00
Andrei Kvapil
b4c9ca36a9 fix keysAndTags for info
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:01:06 +02:00
Andrei Kvapil
37f9065d55 Introduce module parameter
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:01:06 +02:00
Andrei Kvapil
f130895b30 fix sidebars
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 19:00:39 +02:00
Andrei Kvapil
907dcb5e8b [dashboard] Fix API group for the applications
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 17:14:35 +02:00
Andrei Kvapil
d52a2fbe94 [dashboard] Fix /docs redirect (#1462)
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-25 17:04:21 +02:00
Andrei Kvapil
f41ab0d251 [dashboard] Fix /docs redirect
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 17:03:55 +02:00
Andrei Kvapil
58b7a6456c [dashboard] Add branding config (#1460)
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
- Introduces dynamic branding support. Tenant name, footer text, title,
logo text, logo SVG, and icon SVG can now be customized via cluster
configuration.
- Branding values are pulled automatically at runtime, enabling
per-tenant look and feel without app redeploys.
- Ensures consistent, centralized control over visual identity across
the dashboard.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 17:01:49 +02:00
Andrei Kvapil
772d663bc1 [cozystack-api] Specify tenantmodules labels (#1461)
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
- Tenant components are now consistently tagged as modules, enabling
clearer grouping, filtering, and management in dashboards and APIs.
- Improves discoverability and automation by making module scope
explicit across tenant services.

- Chores
- Standardized an internal module label across tenant releases and
system resource definitions (etcd, ingress, monitoring, SeaweedFS, info)
for consistency.
  - Metadata-only update with no runtime behavior changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 17:01:34 +02:00
Andrei Kvapil
e5c1cf97bd [cozystack-api] Specify tenantmodules labels
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 16:37:19 +02:00
Andrei Kvapil
7605df5f29 [dashboard] Add branding config
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 16:17:58 +02:00
Andrei Kvapil
df89117fa1 [dashboard] refactor dashboard configuration (#1457)
- Refactor code for dashboard resources creation
- Move dashboard-config helm chart to dynamic dashboard controller

<!-- 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**
* Static dashboard resources (breadcrumbs, factories, forms, marketplace
panels, table mappings) are initialized at startup and materialized
automatically.

* **Improvements**
* Unified UI construction with consistent badges, headers and
deterministic IDs.
  * Automatic cleanup of stale/orphaned dashboard resources.
  * Increased controller client throughput for faster operations.

* **Refactor**
* Consolidated static dashboard resource generation into a unified,
config-driven flow.

* **Chores**
* Removed legacy dashboard-config templates; updated controller and
dashboard image digests.
  * Added dashboard ConfigMap and wired UI env vars to it.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 16:16:50 +02:00
Andrei Kvapil
9873011ebf [dashboard] refactor dashboard configuration
- Refactor code for dashboard resources creation
- Move dashboard-config helm chart to dynamic dashboard controller
- Move white-label configuration to separate configmap

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 14:57:33 +02:00
Andrei Kvapil
b25aa10243 [dashboard] fix listing namespaces as unprivileged user (#1456)
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

* **Improvements**
* Updated namespace data source to a new API, ensuring tenant namespaces
display correctly and stay in sync.

* **Bug Fixes**
* Improved reliability of streamed requests by removing problematic
headers, preventing errors during form-based operations.

* **Chores**
* Adjusted image build process to apply patches during build, enabling
quicker delivery of fixes without altering runtime behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 14:30:58 +02:00
Andrei Kvapil
f3b317ceea Update Cozystack screenshot (#1459)
ref https://github.com/cozystack/website/pull/335

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

* **Documentation**
* Updated README screenshot to use the dark-theme image, improving
visual consistency for viewers using dark mode.
* Purely presentational change — no impact on application behavior or
public interfaces.
* Clarifies repository appearance for users browsing in different
themes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-25 14:30:21 +02:00
Andrei Kvapil
16496e238a Update Cozystack screenshot
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-25 14:28:22 +02:00
Timofei Larkin
c282d03330 [platform] Update lineage labels at upgrade (#1452)
## What this PR does

1. Since the VictoriaMetrics operator aggressively manages the metadata on
all owned components, the addition of labels by the lineage webhook
causes non-stop updates sent to the k8s API server. We mitigate this by
modifying the Monitoring Helm chart to set the `managedMetadata` field
on all VictoriaMetrics custom resources, where applicable.

2. This patch adds a migration script, that adds an annotation to all
resources that may be of interest, triggering an update event on the
lineage webhook. This will analyze the ancestor tree of these resources
and add labels to them, referencing their managing application.

3. This patch makes sure that migration #20 really uses the very latest
chart versions by forcing a reconcile with cozypkg, instead of
annotating the underlying HelmRelease.

### Release note

```release-note
[monitoring] Explicitly set lineage labels on VictoriaMetrics' resources
known not to play nice when something modifies their owned resources in
flight.
[platform] Add migration script to update pre-existing resources with
lineage labels.
[installer] Update cozypkg in installer and use it to bulletproof the
20th migration script by reconciling the HelmReleases with the
--with-source flag.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-25 15:55:32 +04:00
Timofei Larkin
0f8a9ac9ef [installer] Update cozypkg and improve migration 20
This patch makes sure that migration #20 really uses the very latest
chart versions by forcing a reconcile with cozypkg, instead of
annotating the underlying HelmRelease.

```release-note
[installer] Update cozypkg in installer and use it to bulletproof the
20th migration script by reconciling the HelmReleases with the
--with-source flag.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-25 12:03:22 +03:00
Timofei Larkin
21ca1349c4 [monitoring] Add lineage labels to VM components
Since the VictoriaMetrics operator aggressively manages the metadata on
all owned components, the addition of labels by the lineage webhook
causes non-stop updates sent to the k8s API server. We mitigate this by
modifying the Monitoring Helm chart to set the `managedMetadata` field
on all VictoriaMetrics custom resources, where applicable.

```release-note
[monitoring] Explicitly set lineage labels on VictoriaMetrics' resources
known not to play nice when something modifies their owned resources in
flight.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-25 10:53:34 +03:00
Andrei Kvapil
23e59ea654 New dashboard based on OpenAPI schema (#1269)
A new dashboard based on https://github.com/PRO-Robotech/openapi-ui
project

<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-01-00
OpenAPI UI"
src="https://github.com/user-attachments/assets/7ae04789-24ec-4e4b-830b-6f16e96513eb"
/>
<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-01-14
OpenAPI UI"
src="https://github.com/user-attachments/assets/ca5aa85d-43f0-4b5b-b87a-3bc237834f10"
/>
<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-02-05
OpenAPI UI"
src="https://github.com/user-attachments/assets/ebee7bfa-c3ac-4fe6-b5e1-43e9e7042c6a"
/>

<!-- 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.
-->

<!--  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
[cozystack-api] Implement TenantNamespace, TenantModules, TenantSecret and TenantSecretsTable resources
[cozystack-controller] Introduce new dashboard-controller
[dashboard] Introduce new dashboard based on openapi-ui
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-25 00:07:07 +03:00
Andrei Kvapil
c81a1aa2b0 [dashboard] fix listing namespaces as unprivileged user
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-24 21:38:12 +02:00
Andrei Kvapil
bb653e5a87 New dashboard based on OpenAPI schema (#1269)
A new dashboard based on https://github.com/PRO-Robotech/openapi-ui
project

<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-01-00
OpenAPI UI"
src="https://github.com/user-attachments/assets/7ae04789-24ec-4e4b-830b-6f16e96513eb"
/>
<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-01-14
OpenAPI UI"
src="https://github.com/user-attachments/assets/ca5aa85d-43f0-4b5b-b87a-3bc237834f10"
/>
<img width="1720" height="1373" alt="Screenshot 2025-08-01 at 09-02-05
OpenAPI UI"
src="https://github.com/user-attachments/assets/ebee7bfa-c3ac-4fe6-b5e1-43e9e7042c6a"
/>




<!-- 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
[cozystack-api] Implement TenantNamespace, TenantModules, TenantSecret and TenantSecretsTable resources
[cozystack-controller] Introduce new dashboard-controller
[dashboard] Introduce new dashboard based on openapi-ui
```
2025-09-24 20:11:41 +02:00
Andrei Kvapil
0afc3c1e86 [cozystack-api] Implement TenantNamespace, TenantModules, TenantSecret and TenantSecretsTable resources
[cozystack-controller] Introduce new dashboard-controller
[dashboard] Introduce new dashboard based on openapi-ui

Co-authored-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: kklinch0 <kklinch0@gmail.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-24 18:27:54 +02:00
Andrei Kvapil
789666d53b Remove versions_map logic (#1453)
TBD: How can we ensure that migrations were completed **before**
updating user-charts

## What this PR does

This PR removes logic for user apps versioning.
It is not needed anymore for new dashboard and does not make sence for
cozystack-api server, which always validates values accourding to the
latest spec from CozystackResourceDefinition.

### 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
- Removed legacy version maps and packaging scripts (including
gen_versions_map and package_chart); pre-commit hook for versions
removed.
- Makefiles updated to unified chart discovery and shared env includes;
logo copy step removed and installer image no longer bundles logos.
- Many charts’ version fields replaced with build-time placeholders
(0.0.0); appVersion metadata added.

- Refactor
  - Added standardized fix-charts and repo targets for packaging.
- HelmRelease defaults tightened: explicit version constraints, longer
intervals/timeouts, remediation retries, and upgrade.force.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-24 17:33:13 +02:00
Andrei Kvapil
152ab20a17 Update linage webhook configuration (#1454)
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**
* Expanded lineage webhook coverage to include WorkloadMonitor
resources, enabling automatic mutation and consistent metadata across
these workloads.
* Added mutation support for Ingresses, helping propagate lineage
metadata across HTTP entrypoints for improved traceability and
governance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-24 17:20:38 +02:00
Andrei Kvapil
9f9d8f8530 Allign timeouts for HelmReleases
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-24 16:54:17 +02:00
Andrei Kvapil
97f1b29975 Update linage webhook configuration
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-24 14:58:16 +02:00
Andrei Kvapil
f871fbdb1e Remove versions_map logic
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-24 12:32:37 +02:00
Andrei Kvapil
5d76e6b626 Feat/webhook workload monitors (#1448)
## 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. This resubmission of the PR now
includes semantics to compare secrets to label selectors in
CozystackResourceDefinitions to determine, whether they should be marked
as user-facing or not.

### 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 a lineage mutating webhook that auto-applies ancestry labels to
core resources and VMCluster.
- Introduces secret include/exclude selectors in resource definitions
for fine-grained tenant secret visibility.
- Deploys webhook service with TLS via cert-manager (issuers,
certificates) and updates deployment to expose webhook port.

- Chores
- Updates numerous container images to latest tags and digests across
system and app components (controller, dashboard, kubeovn, cilium,
kamaji, storage, etc.).

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-24 11:10:19 +02:00
Timofei Larkin
4620f7dfa1 [platform] Add secret selectors to CozyRDs
This patch populates existing CozystackResourceDefinitions with minimal
working examples of secret selectors to take advantage of the newest
revision of the ancestor tracking webhook.

```release-note
[platform] Specify secret selectors for existing managed apps in their
respective CozystackResourceDefinitions, which provides the last bit of
information necessary for the lineage webhook to correctly mark secrets
as user-facing or not.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-24 12:07:21 +03:00
Timofei Larkin
562145e69b [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-24 12:07:18 +03:00
Timofei Larkin
0e1f73999b [platform] Add secret selectors to app definitions
This patch expands the CozystackResourceDefinitions with new label
selector fields to include and exclude secrets by their labelsets.
This will enable application developers to selectively show or hide
application secrets to and from end-users.

```release-note
[platform] Add selectors for application secrets, offering developers
an API to control secret visibility for end users.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-24 12:06:41 +03:00
Andrei Kvapil
744a0f3ca6 [platform] Mark some secrets as non-user-facing (#1446)
## What this PR does

Some k8s secrets created when deploying managed applications are
unhelpful to the end user or are outright not meant to be shown, because
they contain internal credentials not meant to be presented to the user.
This patch adds an `apps.cozystack.io/tenantresource=false` label to
such resources which will be later used to filter out such secrets in
the web UI.

### Release note

```release-note
[platform] Mark non-user-facing secrets as such to avoid clutter in the
dashboard and leaking internal credentials.
```

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

* **New Features**
* Automatic creation of a ServiceAccount token Secret via the Info
add-on.

* **Improvements**
  * VPN TLS Secret CA field standardized to ca.crt for consistency.

* **Removals**
* Removed the explicit ServiceAccount token Secret from the Tenant app
(token now managed by Info).

* **Chores**
  * Added non-functional metadata labels to several Secrets.
  * Bumped chart/package metadata versions and updated version mappings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-24 11:04:54 +02:00
klinch0
3ac83ac48c [k8s] add expanding persistent volumes in tenant clusters (#1438)
<!-- 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
- add expanding persistent volumes in tenant clusters
```

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

## Summary by CodeRabbit

- New Features
- Enabled PersistentVolumeClaim expansion in the KubeVirt CSI
StorageClass.
- Added CSI resizer sidecar to the controller for online volume
resizing.
- Introduced cluster-scoped RBAC to allow required access to
PersistentVolumes.

- Chores
- Updated Kubernetes app chart to 0.29.2 and set app version to 1.32.6.
  - Upgraded KubeVirt CSI driver image to 0.37.0.
  - Refreshed versions map entries for the new release.
- Simplified CoreDNS configuration to use the default image repository.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-23 19:58:54 +03:00
Andrei Kvapil
d991c49254 [platform] Add secret selectors to app definitions (#1447)
## What this PR does

This patch expands the CozystackResourceDefinitions with new label
selector fields to include and exclude secrets by their labelsets. This
will enable application developers to selectively show or hide
application secrets to and from end-users.

### Release note

```release-note
[platform] Add selectors for application secrets, offering developers
an API to control secret visibility for end users.
```

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

## Summary by CodeRabbit

* **New Features**
* Added support for configuring secret visibility on resource
definitions using include/exclude label selectors. This lets you
precisely control which secrets are considered without affecting
existing setups.
* The configuration is optional; if not set, behavior remains unchanged.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-23 18:29:56 +02:00
Timofei Larkin
48919c0cfe [platform] Add secret selectors to app definitions
This patch expands the CozystackResourceDefinitions with new label
selector fields to include and exclude secrets by their labelsets.
This will enable application developers to selectively show or hide
application secrets to and from end-users.

```release-note
[platform] Add selectors for application secrets, offering developers
an API to control secret visibility for end users.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-23 17:37:26 +03:00
Timofei Larkin
7e4883dfcc [platform] Mark some secrets as non-user-facing
Some k8s secrets created when deploying managed applications are
unhelpful to the end user or are outright not meant to be shown, because
they contain internal credentials not meant to be presented to the user.
This patch adds an `apps.cozystack.io/tenantresource=false` label to
such resources which will be later used to filter out such secrets in
the web UI.

```release-note
[platform] Mark non-user-facing secrets as such to avoid clutter in the
dashboard and leaking internal credentials.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-23 15:09:18 +03:00
Andrei Kvapil
66b53cb1ae [vm-disk] New SVG icon (#1435)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Co-authored-by: Viktoriia Kvapil
<159528100+kvapsova@users.noreply.github.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-22 16:29:58 +02:00
Andrei Kvapil
6005b76e96 [dashboard] Fix FerretDB spec (#1440)
## What this PR does

Due to a typo in the spec, the dashboard couldn't deploy or display
instances of FerretDB. This patch fixes the typo.

### Release note

```release-note
[dashboard] Fix FerretDB management in the web UI.
```

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

## Summary by CodeRabbit

- Bug Fixes
- Corrected FerretDB resource pluralization to “ferretdbs,” aligning
with Kubernetes conventions. This ensures resources display and behave
correctly in the dashboard, preventing discovery issues and errors in
listing, navigation, and management.
- Improves reliability of installs and upgrades with Flux/Helm workflows
by matching expected resource names. No other FerretDB settings were
changed, maintaining backward compatibility for existing configurations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-22 16:29:17 +02:00
Timofei Larkin
e34d9613c7 [dashboard] Fix FerretDB spec
Due to a typo in the spec, the dashboard couldn't deploy or display
instances of FerretDB. This patch fixes the typo.

```release-note
[dashboard] Fix FerretDB management in the web UI.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-09-22 16:15:18 +03:00
Nick Volynkin
2fcf975e6a [docs] Changelogs for v0.36.*
Signed-off-by: Nick Volynkin <nick.volynkin@gmail.com>
2025-09-22 09:46:16 +03:00
kklinch0
ca19529c7d [k8s] add expanding persistent volumes in tenant clusters
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-09-20 14:10:00 +03:00
Andrei Kvapil
b3be1f4e1e [vm-disk] New SVG icon
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
Co-authored-by: Viktoriia Kvapil <159528100+kvapsova@users.noreply.github.com>
2025-09-18 16:27:38 +02:00
Nick Volynkin
b7bebecb64 [docs] Changelogs for v0.35.*
Signed-off-by: Nick Volynkin <nick.volynkin@gmail.com>
2025-09-18 09:15:03 +03:00
Andrei Kvapil
53fbe7c2ee Release v0.36.1 (#1434)
This PR prepares the release `v0.36.1`.
2025-09-18 05:26:48 +02:00
cozystack-bot
18ff789256 Prepare release v0.36.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-18 02:58:44 +00:00
Andrei Kvapil
3d02fbfba4 [cozystack-api] Update defaulting API schemas (#1433)
## 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
[cozystack-api] Update defaulting API schemas
```
2025-09-18 04:54:07 +02:00
Andrei Kvapil
8c6fc68367 [cozystack-api] Update defaulting API schemas
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-18 04:49:50 +02:00
Andrei Kvapil
9d2fe2605f [cozystack-api] Implement Kubernetes-like defaulting (#1432)
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**
- Application specs now get recursive, Kubernetes-like defaulting:
missing fields in nested objects and arrays are auto-populated safely
without mutating shared defaults.
- No changes to public APIs; existing manifests remain compatible while
gaining broader defaulting.

- **Tests**
- Added unit tests validating defaulting behavior, per-item defaults,
and non-creation of absent keys.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-18 03:01:39 +02:00
Andrei Kvapil
edb3e92585 [cozystack-api] Implement Kubernetes-like defaulting
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-18 02:32:16 +02:00
Andrei Kvapil
7118232490 Update ADOPTERS.md (#1429)
<!-- 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

* **Documentation**
* Added Hidora to the Adopters list, including contact
(@matthieu-robin), date (2025-09-17), and a description highlighting
Hikube’s Swiss-based, multi-datacenter, sovereign cloud capabilities.
Users can reference these details for real-world usage context.
* Updated the table with a spacer row to maintain formatting
consistency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 22:59:54 +02:00
Andrei Kvapil
19f81a2d32 [seaweedfs] fix seaweedfs migration (#1430)
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
  - None
- Bug Fixes
- Prevented duplicate or incorrect chart version updates during
migrations.
- Avoided failures when configuration values are missing or not
correctly structured.
- Preserved existing volume settings (size and storage class) during
updates.
- Refactor
- Reorganized migration steps to validate and create parent
configuration before modifying nested fields.
- Made the migration process more defensive and order-aware for smoother
upgrades.
- Chores
- Improved migration scripts for reliability during version and
configuration updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 22:59:42 +02:00
Andrei Kvapil
b93fe65992 [seaweedfs] fix seaweedfs migration
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-17 22:58:43 +02:00
Andrei Kvapil
541347d321 [dashboard] Fix bitnami dependencies (#1431)
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

- Chores
- Updated container base images for the dashboard and APIs to maintained
legacy variants to improve build stability and align with security
patching.
- No user-facing changes: functionality, performance, and UI remain
unchanged.
- Runtime versions are consistent with previous releases; deployment
artifacts are equivalent.
- Existing workflows and configurations continue to work as before; no
action required from users.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 22:58:03 +02:00
Andrei Kvapil
1827d29412 [dashboard] Fix bitnami dependencies
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-17 22:34:31 +02:00
Andrei Kvapil
a1a107a90b Release v0.36.0 (#1428)
This PR prepares the release `v0.36.0`.

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

## Summary by CodeRabbit

- Chores
- Promoted images from v0.36.0-beta.4 to stable v0.36.0 across the
platform (installer, controller, API, dashboard, Kamaji,
kubeovn-webhook, kubeovn-plunger, object storage components, SeaweedFS
sidecar, matchbox, e2e).
- Updated image digests for multiple components to latest builds (nginx
cache, KubeVirt CSI driver, S3 manager, KubeOVN).
- Style
  - Dashboard now displays app version v0.36.0.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 16:45:35 +02:00
Matthieu ROBIN
6cd0a3409e Update ADOPTERS.md
Signed-off-by: Matthieu ROBIN <info@matthieurobin.com>
2025-09-17 14:26:55 +02:00
cozystack-bot
f5c575d12f Prepare release v0.36.0
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-09-17 11:45:04 +00:00
Andrei Kvapil
d10b3635cc [cozystack-controller] Implement cache for CozystackResourceDefinitions (#1427)
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 introduces shared cache for CozystackResourceDefinitions and
warbs it up before making decidion on restart cozystack-api server.

Reastart logic was also updated to trigger restart only if consistent
hash from the configuration has been changed.

### 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
[cozystack-controller] Implement cache for CozystackResourceDefinitions
```

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

## Summary by CodeRabbit

- New Features
- Smarter, hash-based restarts for the API component, triggered only
when configuration truly changes.
- Debounced restart behavior to avoid rapid, repeated restarts during
bursts of updates.

- Performance
- Introduces an internal in-memory configuration cache to speed up
evaluations and reduce API calls.
  - Cache is primed at startup for faster, more responsive operations.

- Bug Fixes
- Eliminates unnecessary restarts when there are no effective config
changes, improving stability and reducing disruption.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 13:40:07 +02:00
Andrei Kvapil
cdf53e89e9 Replace Ancestor tracking webhook with controller
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-09-17 12:55:43 +02:00
Andrei Kvapil
37720b9609 Revert "[cozystack-controller] Ancestor tracking webhook" (#1425)
Reverts cozystack/cozystack#1400

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

## Summary by CodeRabbit

* New Features
  * None
* Refactor
* Removed the lineage mutating admission webhook and its controller
logic; objects are no longer auto-labeled/mutated.
* Deployment now targets the cozy-system namespace and no longer exposes
a webhook port or mounts webhook certs.
* Chores
* Removed Service and cert-manager resources previously used for webhook
TLS; cert-manager is no longer required.
* Tests
  * Removed lineage-related tests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-17 12:49:30 +02:00
Andrei Kvapil
ce522284c4 Revert "[cozystack-controller] Ancestor tracking webhook" 2025-09-17 12:46:00 +02:00
Isaiah Olson
edc12e3f7e Add FoundationDB configuration values for storage engine and redundancy mode, update tests, and fix workload monitor
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:19 -05:00
Isaiah Olson
27b06f4fbd Update FoundationDB values to properly set cluster size
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:19 -05:00
Isaiah Olson
3f6888a470 Add FoundationDB instances to dashboard
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:18 -05:00
Isaiah Olson
1a4e979e63 Fix value for image type to reflect the deprecated status of the split images and use unified by default
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:17 -05:00
Isaiah Olson
a3be02132d Update Foundation DB tests and update chart to make sure they pass
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:16 -05:00
Isaiah Olson
c6ec3168f7 Fix default UID of root in FoundationDB app chart
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:15 -05:00
Isaiah Olson
5b58ec5cdd Update FoundationDB to use latest stable version supported by the operator by default
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:14 -05:00
Isaiah Olson
7e622181ed Add FoundationDB operator to the bundles
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:13 -05:00
Isaiah Olson
bf38316163 Restructure FoundationDB values and flatten them
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:12 -05:00
Isaiah Olson
1475196437 Update values schema for FoundationDB app chart
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:11 -05:00
Isaiah Olson
076d69a10b Add OpenAPI schema and resource definitions for FoundationDB
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:10 -05:00
Isaiah Olson
5654ac4e3d Initial support for FoundationDB operator
Signed-off-by: Isaiah Olson <isaiah@olson-network.com>
2025-09-17 01:16:09 -05:00
Marian Koreniuk
16a700dabf Fix bug_report.md
Signed-off-by: Marian Koreniuk <moriarti@cp.if.ua>
2025-09-11 16:26:20 +02:00
Marian Koreniuk
7f8b673dbc Update bug_report.md
Signed-off-by: Marian Koreniuk <moriarti@cp.if.ua>
2025-09-10 22:01:37 +02:00
Marian Koreniuk
24482d958b Update issue templates 2025-09-10 21:55:24 +02:00
Timur Tukaev
14aba9edb2 Create CONTRIBUTOR_LADDER.md
Contributor ladder is an important tool for community participants who are loyal to project and would like to take more responsibility in project. Besides, it's needed for CNCF Incubated  applications

Signed-off-by: Timur Tukaev <90071493+tym83@users.noreply.github.com>
2025-07-20 15:56:25 +05:00
701 changed files with 33426 additions and 29605 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1 +1 @@
* @kvaps @lllamnyp @klinch0
* @kvaps @lllamnyp @nbykov0

50
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,50 @@
---
name: Bug report
about: Create a report to help us improve
labels: 'bug'
assignees: ''
---
<!--
Thank you for submitting a bug report!
Please fill in the fields below to help us investigate the problem.
-->
**Describe the bug**
A clear and concise description of what the bug is.
**Environment**
- Cozystack version
- Provider: on-prem, Hetzner, and so on
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behaviour**
When taking the steps to reproduce, what should have happened differently?
**Actual behaviour**
A clear and concise description of what happens when the bug occurs. Explain how the system currently behaves, including error messages, unexpected results, or incorrect functionality observed during execution.
**Logs**
```
Paste any relevant logs here. Please redact tokens, passwords, private keys.
```
**Screenshots**
If applicable, add screenshots to help explain the problem.
**Additional context**
Add any other context about the problem here.
**Checklist**
- [ ] I have checked the documentation
- [ ] I have searched for similar issues
- [ ] I have included all required information
- [ ] I have provided clear steps to reproduce
- [ ] I have included relevant logs

View File

@@ -28,7 +28,7 @@ jobs:
- name: Install generate
run: |
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v0.8.5/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.0.5/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen
- name: Run pre-commit hooks
run: |

View File

@@ -1,7 +1,8 @@
name: Pull Request
env:
REGISTRY: ${{ vars.OCIR_REPO }}
# TODO: unhardcode this
REGISTRY: iad.ocir.io/idyksih5sir9/cozystack
on:
pull_request:
types: [opened, synchronize, reopened]

View File

@@ -1,18 +1,11 @@
repos:
- repo: local
hooks:
- id: gen-versions-map
name: Generate versions map and check for changes
entry: sh -c 'make -C packages/apps check-version-map && make -C packages/extra check-version-map'
language: system
types: [file]
pass_filenames: false
description: Run the script and fail if it generates changes
- id: run-make-generate
name: Run 'make generate' in all app directories
entry: |
flock -x .git/pre-commit.lock sh -c '
for dir in ./packages/apps/*/ ./packages/extra/*/ ./packages/system/cozystack-api/; do
for dir in ./packages/apps/*/ ./packages/extra/*/; do
if [ -d "$dir" ]; then
echo "Running make generate in $dir"
make generate -C "$dir" || exit $?

View File

@@ -30,3 +30,6 @@ This list is sorted in chronological order, based on the submission date.
| [Bootstack](https://bootstack.app/) | @mrkhachaturov | 2024-08-01| At Bootstack, we utilize a Kubernetes operator specifically designed to simplify and streamline cloud infrastructure creation.|
| [gohost](https://gohost.kz/) | @karabass_off | 2024-02-01 | Our company has been working in the market of Kazakhstan for more than 15 years, providing clients with a standard set of services: VPS/VDC, IaaS, shared hosting, etc. Now we are expanding the lineup by introducing Bare Metal Kubenetes cluster under Cozystack management. |
| [Urmanac](https://urmanac.com) | @kingdonb | 2024-12-04 | Urmanac is the future home of a hosting platform for the knowledge base of a community of personal server enthusiasts. We use Cozystack to provide support services for web sites hosted using both conventional deployments and on SpinKube, with WASM. |
| [Hidora](https://hikube.cloud) | @matthieu-robin | 2025-09-17 | Hidora is a Swiss cloud provider delivering managed services and infrastructure solutions through datacenters located in Switzerland, ensuring data sovereignty and reliability. Its sovereign cloud platform, Hikube, is designed to run workloads with high availability across multiple datacenters, providing enterprises with a secure and scalable foundation for their applications based on Cozystack. |
| [QOSI](https://qosi.kz) | @tabu-a | 2025-10-04 | QOSI is a non-profit organization driving open-source adoption and digital sovereignty across Kazakhstan and Central Asia. We use Cozystack as a platform for deploying sovereign, GPU-enabled clouds and educational environments under the National AI Program. Our goal is to accelerate the regions transition toward open, self-hosted cloud-native technologies |
|

View File

@@ -1,3 +1,22 @@
# Code of Conduct
Cozystack follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
# Cozystack Vendor Neutrality Manifesto
Cozystack exists for the cloud-native community. We are committed to a project culture where no single company, product, or commercial agenda directs our roadmap, governance, brand, or releases. Our North Star is user value, technical excellence, and open collaboration under the CNCF umbrella.
## Our Commitments
- **Community-first:** Decisions prioritize the broader community over any vendor interest.
- **Open collaboration:** Ideas, discussions, and outcomes happen in public spaces; contributions are welcomed from all.
- **Merit over affiliation:** Proposals are evaluated on technical merit and user impact, not on who submits them.
- **Inclusive stewardship:** Leadership and maintenance are open to contributors who demonstrate sustained, constructive impact.
- **Technology choice:** We prefer open, pluggable designs that interoperate with multiple ecosystems and providers.
- **Neutral brand & voice:** Our name, logo, website, and documentation do not imply endorsement or preference for any vendor.
- **Transparent practices:** Funding acknowledgments, partnerships, and potential conflicts are communicated openly.
- **User trust:** Security handling, releases, and communications aim to be timely, transparent, and fair to all users.
By contributing to Cozystack, we affirm these principles and work together to keep the project open, welcoming, and vendor-neutral.
*— The Cozystack community*

151
CONTRIBUTOR_LADDER.md Normal file
View File

@@ -0,0 +1,151 @@
# Contributor Ladder
* [Contributor Ladder](#contributor-ladder)
* [Community Participant](#community-participant)
* [Contributor](#contributor)
* [Reviewer](#reviewer)
* [Maintainer](#maintainer)
* [Inactivity](#inactivity)
* [Involuntary Removal](#involuntary-removal-or-demotion)
* [Stepping Down/Emeritus Process](#stepping-downemeritus-process)
* [Contact](#contact)
## Contributor Ladder
Hello! We are excited that you want to learn more about our project contributor ladder! This contributor ladder outlines the different contributor roles within the project, along with the responsibilities and privileges that come with them. Community members generally start at the first levels of the "ladder" and advance up it as their involvement in the project grows. Our project members are happy to help you advance along the contributor ladder.
Each of the contributor roles below is organized into lists of three types of things. "Responsibilities" are things that a contributor is expected to do. "Requirements" are qualifications a person needs to meet to be in that role, and "Privileges" are things contributors on that level are entitled to.
### Community Participant
Description: A Community Participant engages with the project and its community, contributing their time, thoughts, etc. Community participants are usually users who have stopped being anonymous and started being active in project discussions.
* Responsibilities:
* Must follow the [CNCF CoC](https://github.com/cncf/foundation/blob/main/code-of-conduct.md)
* How users can get involved with the community:
* Participating in community discussions
* Helping other users
* Submitting bug reports
* Commenting on issues
* Trying out new releases
* Attending community events
### Contributor
Description: A Contributor contributes directly to the project and adds value to it. Contributions need not be code. People at the Contributor level may be new contributors, or they may only contribute occasionally.
* Responsibilities include:
* Follow the [CNCF CoC](https://github.com/cncf/foundation/blob/main/code-of-conduct.md)
* Follow the project [contributing guide] (https://github.com/cozystack/cozystack/blob/main/CONTRIBUTING.md)
* Requirements (one or several of the below):
* Report and sometimes resolve issues
* Occasionally submit PRs
* Contribute to the documentation
* Show up at meetings, takes notes
* Answer questions from other community members
* Submit feedback on issues and PRs
* Test releases and patches and submit reviews
* Run or helps run events
* Promote the project in public
* Help run the project infrastructure
* Privileges:
* Invitations to contributor events
* Eligible to become a Maintainer
### Reviewer
Description: A Reviewer has responsibility for specific code, documentation, test, or other project areas. They are collectively responsible, with other Reviewers, for reviewing all changes to those areas and indicating whether those changes are ready to merge. They have a track record of contribution and review in the project.
Reviewers are responsible for a "specific area." This can be a specific code directory, driver, chapter of the docs, test job, event, or other clearly-defined project component that is smaller than an entire repository or subproject. Most often it is one or a set of directories in one or more Git repositories. The "specific area" below refers to this area of responsibility.
Reviewers have all the rights and responsibilities of a Contributor, plus:
* Responsibilities include:
* Continues to contribute regularly, as demonstrated by having at least 15 PRs a year, as demonstrated by [Cozystack devstats](https://cozystack.devstats.cncf.io).
* Following the reviewing guide
* Reviewing most Pull Requests against their specific areas of responsibility
* Reviewing at least 40 PRs per year
* Helping other contributors become reviewers
* Requirements:
* Must have successful contributions to the project, including at least one of the following:
* 10 accepted PRs,
* Reviewed 20 PRs,
* Resolved and closed 20 Issues,
* Become responsible for a key project management area,
* Or some equivalent combination or contribution
* Must have been contributing for at least 6 months
* Must be actively contributing to at least one project area
* Must have two sponsors who are also Reviewers or Maintainers, at least one of whom does not work for the same employer
* Has reviewed, or helped review, at least 20 Pull Requests
* Has analyzed and resolved test failures in their specific area
* Has demonstrated an in-depth knowledge of the specific area
* Commits to being responsible for that specific area
* Is supportive of new and occasional contributors and helps get useful PRs in shape to commit
* Additional privileges:
* Has GitHub or CI/CD rights to approve pull requests in specific directories
* Can recommend and review other contributors to become Reviewers
* May be assigned Issues and Reviews
* May give commands to CI/CD automation
* Can recommend other contributors to become Reviewers
The process of becoming a Reviewer is:
1. The contributor is nominated by opening a PR against the appropriate repository, which adds their GitHub username to the OWNERS file for one or more directories.
2. At least two members of the team that owns that repository or main directory, who are already Approvers, approve the PR.
### Maintainer
Description: Maintainers are very established contributors who are responsible for the entire project. As such, they have the ability to approve PRs against any area of the project, and are expected to participate in making decisions about the strategy and priorities of the project.
A Maintainer must meet the responsibilities and requirements of a Reviewer, plus:
* Responsibilities include:
* Reviewing at least 40 PRs per year, especially PRs that involve multiple parts of the project
* Mentoring new Reviewers
* Writing refactoring PRs
* Participating in CNCF maintainer activities
* Determining strategy and policy for the project
* Participating in, and leading, community meetings
* Requirements
* Experience as a Reviewer for at least 6 months
* Demonstrates a broad knowledge of the project across multiple areas
* Is able to exercise judgment for the good of the project, independent of their employer, friends, or team
* Mentors other contributors
* Can commit to spending at least 10 hours per month working on the project
* Additional privileges:
* Approve PRs to any area of the project
* Represent the project in public as a Maintainer
* Communicate with the CNCF on behalf of the project
* Have a vote in Maintainer decision-making meetings
Process of becoming a maintainer:
1. Any current Maintainer may nominate a current Reviewer to become a new Maintainer, by opening a PR against the root of the cozystack repository adding the nominee as an Approver in the [MAINTAINERS](https://github.com/cozystack/cozystack/blob/main/MAINTAINERS.md) file.
2. The nominee will add a comment to the PR testifying that they agree to all requirements of becoming a Maintainer.
3. A majority of the current Maintainers must then approve the PR.
## Inactivity
It is important for contributors to be and stay active to set an example and show commitment to the project. Inactivity is harmful to the project as it may lead to unexpected delays, contributor attrition, and a lost of trust in the project.
* Inactivity is measured by:
* Periods of no contributions for longer than 6 months
* Periods of no communication for longer than 3 months
* Consequences of being inactive include:
* Involuntary removal or demotion
* Being asked to move to Emeritus status
## Involuntary Removal or Demotion
Involuntary removal/demotion of a contributor happens when responsibilities and requirements aren't being met. This may include repeated patterns of inactivity, extended period of inactivity, a period of failing to meet the requirements of your role, and/or a violation of the Code of Conduct. This process is important because it protects the community and its deliverables while also opens up opportunities for new contributors to step in.
Involuntary removal or demotion is handled through a vote by a majority of the current Maintainers.
## Stepping Down/Emeritus Process
If and when contributors' commitment levels change, contributors can consider stepping down (moving down the contributor ladder) vs moving to emeritus status (completely stepping away from the project).
Contact the Maintainers about changing to Emeritus status, or reducing your contributor level.
## Contact
* For inquiries, please reach out to: @kvaps, @tym83

View File

@@ -7,6 +7,6 @@
| Kingdon Barrett | [@kingdonb](https://github.com/kingdonb) | Urmanac | FluxCD and flux-operator |
| Timofei Larkin | [@lllamnyp](https://github.com/lllamnyp) | 3commas | Etcd-operator Lead |
| Artem Bortnikov | [@aobort](https://github.com/aobort) | Timescale | Etcd-operator Lead |
| Andrei Gumilev | [@chumkaska](https://github.com/chumkaska) | Ænix | Platform Documentation |
| Timur Tukaev | [@tym83](https://github.com/tym83) | Ænix | Cozystack Website, Marketing, Community Management |
| Kirill Klinchenkov | [@klinch0](https://github.com/klinch0) | Ænix | Core Maintainer |
| Nikita Bykov | [@nbykov0](https://github.com/nbykov0) | Ænix | Maintainer of ARM and stuff |

View File

@@ -15,6 +15,7 @@ build: build-deps
make -C packages/extra/monitoring image
make -C packages/system/cozystack-api image
make -C packages/system/cozystack-controller image
make -C packages/system/lineage-controller-webhook image
make -C packages/system/cilium image
make -C packages/system/kubeovn image
make -C packages/system/kubeovn-webhook image
@@ -30,14 +31,9 @@ build: build-deps
repos:
rm -rf _out
make -C packages/apps check-version-map
make -C packages/extra check-version-map
make -C packages/system repo
make -C packages/apps repo
make -C packages/extra repo
mkdir -p _out/logos
cp ./packages/apps/*/logos/*.svg ./packages/extra/*/logos/*.svg _out/logos/
manifests:
mkdir -p _out/assets

View File

@@ -19,7 +19,7 @@ Database-as-a-Service, virtual machines, load balancers, HTTP caching services,
Use Cozystack to build your own cloud or provide a cost-effective development environment.
![Cozystack user interface](https://cozystack.io/img/screenshot.png)
![Cozystack user interface](https://cozystack.io/img/screenshot-dark.png)
## Use-Cases

View File

@@ -1,4 +1,5 @@
API rule violation: list_type_missing,github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1,ApplicationStatus,Conditions
API rule violation: list_type_missing,github.com/cozystack/cozystack/pkg/apis/core/v1alpha1,TenantModuleStatus,Conditions
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Ref
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Schema
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,XEmbeddedResource

View File

@@ -0,0 +1,255 @@
// SPDX-License-Identifier: Apache-2.0
// Package v1alpha1 defines front.in-cloud.io API types.
//
// Group: dashboard.cozystack.io
// Version: v1alpha1
package v1alpha1
import (
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// -----------------------------------------------------------------------------
// Shared shapes
// -----------------------------------------------------------------------------
// CommonStatus is a generic Status block with Kubernetes conditions.
type CommonStatus struct {
// ObservedGeneration reflects the most recent generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions represent the latest available observations of an object's state.
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
// ArbitrarySpec holds schemaless user data and preserves unknown fields.
// We map the entire .spec to a single JSON payload to mirror the CRDs you provided.
// NOTE: Using apiextensionsv1.JSON avoids losing arbitrary structure during round-trips.
type ArbitrarySpec struct {
// +kubebuilder:validation:XPreserveUnknownFields
// +kubebuilder:pruning:PreserveUnknownFields
v1.JSON `json:",inline"`
}
// -----------------------------------------------------------------------------
// Sidebar
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=sidebars,scope=Cluster
// +kubebuilder:subresource:status
type Sidebar struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type SidebarList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Sidebar `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomFormsPrefill (shortName: cfp)
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customformsprefills,scope=Cluster,shortName=cfp
// +kubebuilder:subresource:status
type CustomFormsPrefill struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomFormsPrefillList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomFormsPrefill `json:"items"`
}
// -----------------------------------------------------------------------------
// BreadcrumbInside
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=breadcrumbsinside,scope=Cluster
// +kubebuilder:subresource:status
type BreadcrumbInside struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type BreadcrumbInsideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BreadcrumbInside `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomFormsOverride (shortName: cfo)
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customformsoverrides,scope=Cluster,shortName=cfo
// +kubebuilder:subresource:status
type CustomFormsOverride struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomFormsOverrideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomFormsOverride `json:"items"`
}
// -----------------------------------------------------------------------------
// TableUriMapping
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=tableurimappings,scope=Cluster
// +kubebuilder:subresource:status
type TableUriMapping struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type TableUriMappingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TableUriMapping `json:"items"`
}
// -----------------------------------------------------------------------------
// Breadcrumb
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=breadcrumbs,scope=Cluster
// +kubebuilder:subresource:status
type Breadcrumb struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type BreadcrumbList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Breadcrumb `json:"items"`
}
// -----------------------------------------------------------------------------
// MarketplacePanel
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=marketplacepanels,scope=Cluster
// +kubebuilder:subresource:status
type MarketplacePanel struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type MarketplacePanelList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []MarketplacePanel `json:"items"`
}
// -----------------------------------------------------------------------------
// Navigation
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=navigations,scope=Cluster
// +kubebuilder:subresource:status
type Navigation struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type NavigationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Navigation `json:"items"`
}
// -----------------------------------------------------------------------------
// CustomColumnsOverride
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=customcolumnsoverrides,scope=Cluster
// +kubebuilder:subresource:status
type CustomColumnsOverride struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type CustomColumnsOverrideList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CustomColumnsOverride `json:"items"`
}
// -----------------------------------------------------------------------------
// Factory
// -----------------------------------------------------------------------------
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=factories,scope=Cluster
// +kubebuilder:subresource:status
type Factory struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ArbitrarySpec `json:"spec"`
Status CommonStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
type FactoryList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Factory `json:"items"`
}

View File

@@ -0,0 +1,75 @@
/*
Copyright 2025.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1alpha1 contains API Schema definitions for the v1alpha1 API group.
// +kubebuilder:object:generate=true
// +groupName=dashboard.cozystack.io
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "dashboard.cozystack.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
GroupVersion,
&Sidebar{},
&SidebarList{},
&CustomFormsPrefill{},
&CustomFormsPrefillList{},
&BreadcrumbInside{},
&BreadcrumbInsideList{},
&CustomFormsOverride{},
&CustomFormsOverrideList{},
&TableUriMapping{},
&TableUriMappingList{},
&Breadcrumb{},
&BreadcrumbList{},
&MarketplacePanel{},
&MarketplacePanelList{},
&Navigation{},
&NavigationList{},
&CustomColumnsOverride{},
&CustomColumnsOverrideList{},
&Factory{},
&FactoryList{},
)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil
}

View File

@@ -0,0 +1,654 @@
//go:build !ignore_autogenerated
/*
Copyright 2025 The Cozystack Authors.
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.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ArbitrarySpec) DeepCopyInto(out *ArbitrarySpec) {
*out = *in
in.JSON.DeepCopyInto(&out.JSON)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArbitrarySpec.
func (in *ArbitrarySpec) DeepCopy() *ArbitrarySpec {
if in == nil {
return nil
}
out := new(ArbitrarySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Breadcrumb) DeepCopyInto(out *Breadcrumb) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Breadcrumb.
func (in *Breadcrumb) DeepCopy() *Breadcrumb {
if in == nil {
return nil
}
out := new(Breadcrumb)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Breadcrumb) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BreadcrumbInside) DeepCopyInto(out *BreadcrumbInside) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BreadcrumbInside.
func (in *BreadcrumbInside) DeepCopy() *BreadcrumbInside {
if in == nil {
return nil
}
out := new(BreadcrumbInside)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbInside) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BreadcrumbInsideList) DeepCopyInto(out *BreadcrumbInsideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BreadcrumbInside, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BreadcrumbInsideList.
func (in *BreadcrumbInsideList) DeepCopy() *BreadcrumbInsideList {
if in == nil {
return nil
}
out := new(BreadcrumbInsideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbInsideList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BreadcrumbList) DeepCopyInto(out *BreadcrumbList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Breadcrumb, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BreadcrumbList.
func (in *BreadcrumbList) DeepCopy() *BreadcrumbList {
if in == nil {
return nil
}
out := new(BreadcrumbList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BreadcrumbList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CommonStatus) DeepCopyInto(out *CommonStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonStatus.
func (in *CommonStatus) DeepCopy() *CommonStatus {
if in == nil {
return nil
}
out := new(CommonStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomColumnsOverride) DeepCopyInto(out *CustomColumnsOverride) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomColumnsOverride.
func (in *CustomColumnsOverride) DeepCopy() *CustomColumnsOverride {
if in == nil {
return nil
}
out := new(CustomColumnsOverride)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomColumnsOverride) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomColumnsOverrideList) DeepCopyInto(out *CustomColumnsOverrideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomColumnsOverride, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomColumnsOverrideList.
func (in *CustomColumnsOverrideList) DeepCopy() *CustomColumnsOverrideList {
if in == nil {
return nil
}
out := new(CustomColumnsOverrideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomColumnsOverrideList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomFormsOverride) DeepCopyInto(out *CustomFormsOverride) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsOverride.
func (in *CustomFormsOverride) DeepCopy() *CustomFormsOverride {
if in == nil {
return nil
}
out := new(CustomFormsOverride)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsOverride) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomFormsOverrideList) DeepCopyInto(out *CustomFormsOverrideList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomFormsOverride, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsOverrideList.
func (in *CustomFormsOverrideList) DeepCopy() *CustomFormsOverrideList {
if in == nil {
return nil
}
out := new(CustomFormsOverrideList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsOverrideList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomFormsPrefill) DeepCopyInto(out *CustomFormsPrefill) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsPrefill.
func (in *CustomFormsPrefill) DeepCopy() *CustomFormsPrefill {
if in == nil {
return nil
}
out := new(CustomFormsPrefill)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsPrefill) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CustomFormsPrefillList) DeepCopyInto(out *CustomFormsPrefillList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CustomFormsPrefill, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomFormsPrefillList.
func (in *CustomFormsPrefillList) DeepCopy() *CustomFormsPrefillList {
if in == nil {
return nil
}
out := new(CustomFormsPrefillList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CustomFormsPrefillList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Factory) DeepCopyInto(out *Factory) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Factory.
func (in *Factory) DeepCopy() *Factory {
if in == nil {
return nil
}
out := new(Factory)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Factory) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FactoryList) DeepCopyInto(out *FactoryList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Factory, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FactoryList.
func (in *FactoryList) DeepCopy() *FactoryList {
if in == nil {
return nil
}
out := new(FactoryList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *FactoryList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MarketplacePanel) DeepCopyInto(out *MarketplacePanel) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MarketplacePanel.
func (in *MarketplacePanel) DeepCopy() *MarketplacePanel {
if in == nil {
return nil
}
out := new(MarketplacePanel)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MarketplacePanel) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MarketplacePanelList) DeepCopyInto(out *MarketplacePanelList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MarketplacePanel, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MarketplacePanelList.
func (in *MarketplacePanelList) DeepCopy() *MarketplacePanelList {
if in == nil {
return nil
}
out := new(MarketplacePanelList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MarketplacePanelList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Navigation) DeepCopyInto(out *Navigation) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Navigation.
func (in *Navigation) DeepCopy() *Navigation {
if in == nil {
return nil
}
out := new(Navigation)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Navigation) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NavigationList) DeepCopyInto(out *NavigationList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Navigation, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NavigationList.
func (in *NavigationList) DeepCopy() *NavigationList {
if in == nil {
return nil
}
out := new(NavigationList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NavigationList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Sidebar) DeepCopyInto(out *Sidebar) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidebar.
func (in *Sidebar) DeepCopy() *Sidebar {
if in == nil {
return nil
}
out := new(Sidebar)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Sidebar) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SidebarList) DeepCopyInto(out *SidebarList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Sidebar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidebarList.
func (in *SidebarList) DeepCopy() *SidebarList {
if in == nil {
return nil
}
out := new(SidebarList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SidebarList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TableUriMapping) DeepCopyInto(out *TableUriMapping) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TableUriMapping.
func (in *TableUriMapping) DeepCopy() *TableUriMapping {
if in == nil {
return nil
}
out := new(TableUriMapping)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TableUriMapping) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TableUriMappingList) DeepCopyInto(out *TableUriMappingList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TableUriMapping, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TableUriMappingList.
func (in *TableUriMappingList) DeepCopy() *TableUriMappingList {
if in == nil {
return nil
}
out := new(TableUriMappingList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TableUriMappingList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
)
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API
type CozystackResourceDefinition struct {
@@ -32,7 +33,7 @@ type CozystackResourceDefinition struct {
// +kubebuilder:object:root=true
// CozystackResourceDefinitionList contains a list of CozystackResourceDefinition
// CozystackResourceDefinitionList contains a list of CozystackResourceDefinitions
type CozystackResourceDefinitionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
@@ -48,6 +49,20 @@ type CozystackResourceDefinitionSpec struct {
Application CozystackResourceDefinitionApplication `json:"application"`
// Release configuration
Release CozystackResourceDefinitionRelease `json:"release"`
// Secret selectors
Secrets CozystackResourceDefinitionResources `json:"secrets,omitempty"`
// Service selectors
Services CozystackResourceDefinitionResources `json:"services,omitempty"`
// Ingress selectors
Ingresses CozystackResourceDefinitionResources `json:"ingresses,omitempty"`
// Dashboard configuration for this resource
Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"`
// WorkloadMonitors configuration for this resource
// List of WorkloadMonitor templates to be created for each application instance
WorkloadMonitors []WorkloadMonitorTemplate `json:"workloadMonitors,omitempty"`
}
type CozystackResourceDefinitionChart struct {
@@ -87,3 +102,141 @@ type CozystackResourceDefinitionRelease struct {
// Prefix for the release name
Prefix string `json:"prefix"`
}
// CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.
// A resource matches this selector only if it satisfies ALL criteria:
// - Label selector conditions (matchExpressions and matchLabels)
// - AND has a name that matches one of the names in resourceNames (if specified)
//
// The resourceNames field supports Go templates with the following variables available:
// - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name)
// - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind)
// - {{ .namespace }}: The namespace of the resource being processed
//
// Example YAML:
//
// secrets:
// include:
// - matchExpressions:
// - key: badlabel
// operator: DoesNotExist
// matchLabels:
// goodlabel: goodvalue
// resourceNames:
// - "{{ .name }}-secret"
// - "{{ .kind }}-{{ .name }}-tls"
// - "specificname"
type CozystackResourceDefinitionResourceSelector struct {
metav1.LabelSelector `json:",inline"`
// ResourceNames is a list of resource names to match
// If specified, the resource must have one of these exact names to match the selector
// +optional
ResourceNames []string `json:"resourceNames,omitempty"`
}
type CozystackResourceDefinitionResources struct {
// Exclude contains an array of resource selectors that target resources.
// If a resource matches the selector in any of the elements in the array, it is
// hidden from the user, regardless of the matches in the include array.
Exclude []*CozystackResourceDefinitionResourceSelector `json:"exclude,omitempty"`
// Include contains an array of resource selectors that target resources.
// If a resource matches the selector in any of the elements in the array, and
// matches none of the selectors in the exclude array that resource is marked
// as a tenant resource and is visible to users.
Include []*CozystackResourceDefinitionResourceSelector `json:"include,omitempty"`
}
// ---- Dashboard types ----
// DashboardTab enumerates allowed UI tabs.
// +kubebuilder:validation:Enum=workloads;ingresses;services;secrets;yaml
type DashboardTab string
const (
DashboardTabWorkloads DashboardTab = "workloads"
DashboardTabIngresses DashboardTab = "ingresses"
DashboardTabServices DashboardTab = "services"
DashboardTabSecrets DashboardTab = "secrets"
DashboardTabYAML DashboardTab = "yaml"
)
// CozystackResourceDefinitionDashboard describes how this resource appears in the UI.
type CozystackResourceDefinitionDashboard struct {
// Human-readable name shown in the UI (e.g., "Bucket")
Singular string `json:"singular"`
// Plural human-readable name (e.g., "Buckets")
Plural string `json:"plural"`
// Hard-coded name used in the UI (e.g., "bucket")
// +optional
Name string `json:"name,omitempty"`
// Whether this resource is singular (not a collection) in the UI
// +optional
SingularResource bool `json:"singularResource,omitempty"`
// Order weight for sorting resources in the UI (lower first)
// +optional
Weight int `json:"weight,omitempty"`
// Short description shown in catalogs or headers (e.g., "S3 compatible storage")
// +optional
Description string `json:"description,omitempty"`
// Icon encoded as a string (e.g., inline SVG, base64, or data URI)
// +optional
Icon string `json:"icon,omitempty"`
// Category used to group resources in the UI (e.g., "Storage", "Networking")
Category string `json:"category"`
// Free-form tags for search and filtering
// +optional
Tags []string `json:"tags,omitempty"`
// Which tabs to show for this resource
// +optional
Tabs []DashboardTab `json:"tabs,omitempty"`
// Order of keys in the YAML view
// +optional
KeysOrder [][]string `json:"keysOrder,omitempty"`
// Whether this resource is a module (tenant module)
// +optional
Module bool `json:"module,omitempty"`
}
// ---- WorkloadMonitor types ----
// WorkloadMonitorTemplate defines a template for creating WorkloadMonitor resources
// for application instances. Fields support Go template syntax with the following variables:
// - {{ .Release.Name }}: The name of the Helm release
// - {{ .Release.Namespace }}: The namespace of the Helm release
// - {{ .Chart.Version }}: The version of the Helm chart
// - {{ .Values.<path> }}: Any value from the Helm values
type WorkloadMonitorTemplate struct {
// Name is the name of the WorkloadMonitor.
// Supports Go template syntax (e.g., "{{ .Release.Name }}-keeper")
// +required
Name string `json:"name"`
// Kind specifies the kind of the workload (e.g., "postgres", "kafka")
// +required
Kind string `json:"kind"`
// Type specifies the type of the workload (e.g., "postgres", "zookeeper")
// +required
Type string `json:"type"`
// Selector is a map of label key-value pairs for matching workloads.
// Supports Go template syntax in values (e.g., "app.kubernetes.io/instance: {{ .Release.Name }}")
// +required
Selector map[string]string `json:"selector"`
// Replicas is a Go template expression that evaluates to the desired number of replicas.
// Example: "{{ .Values.replicas }}" or "{{ .Values.clickhouseKeeper.replicas }}"
// +optional
Replicas string `json:"replicas,omitempty"`
// MinReplicas is a Go template expression that evaluates to the minimum number of replicas.
// Example: "1" or "{{ div .Values.replicas 2 | add1 }}"
// +optional
MinReplicas string `json:"minReplicas,omitempty"`
// Condition is a Go template expression that must evaluate to "true" for the monitor to be created.
// Example: "{{ .Values.clickhouseKeeper.enabled }}"
// If empty, the monitor is always created.
// +optional
Condition string `json:"condition,omitempty"`
}

View File

@@ -82,6 +82,42 @@ func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinit
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) {
*out = *in
if in.Tags != nil {
in, out := &in.Tags, &out.Tags
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Tabs != nil {
in, out := &in.Tabs, &out.Tabs
*out = make([]DashboardTab, len(*in))
copy(*out, *in)
}
if in.KeysOrder != nil {
in, out := &in.KeysOrder, &out.KeysOrder
*out = make([][]string, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = make([]string, len(*in))
copy(*out, *in)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionDashboard.
func (in *CozystackResourceDefinitionDashboard) DeepCopy() *CozystackResourceDefinitionDashboard {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionDashboard)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) {
*out = *in
@@ -137,11 +173,84 @@ func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefin
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionResourceSelector) DeepCopyInto(out *CozystackResourceDefinitionResourceSelector) {
*out = *in
in.LabelSelector.DeepCopyInto(&out.LabelSelector)
if in.ResourceNames != nil {
in, out := &in.ResourceNames, &out.ResourceNames
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResourceSelector.
func (in *CozystackResourceDefinitionResourceSelector) DeepCopy() *CozystackResourceDefinitionResourceSelector {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionResourceSelector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionResources) DeepCopyInto(out *CozystackResourceDefinitionResources) {
*out = *in
if in.Exclude != nil {
in, out := &in.Exclude, &out.Exclude
*out = make([]*CozystackResourceDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(CozystackResourceDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
if in.Include != nil {
in, out := &in.Include, &out.Include
*out = make([]*CozystackResourceDefinitionResourceSelector, len(*in))
for i := range *in {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(CozystackResourceDefinitionResourceSelector)
(*in).DeepCopyInto(*out)
}
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResources.
func (in *CozystackResourceDefinitionResources) DeepCopy() *CozystackResourceDefinitionResources {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionResources)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) {
*out = *in
out.Application = in.Application
in.Release.DeepCopyInto(&out.Release)
in.Secrets.DeepCopyInto(&out.Secrets)
in.Services.DeepCopyInto(&out.Services)
in.Ingresses.DeepCopyInto(&out.Ingresses)
if in.Dashboard != nil {
in, out := &in.Dashboard, &out.Dashboard
*out = new(CozystackResourceDefinitionDashboard)
(*in).DeepCopyInto(*out)
}
if in.WorkloadMonitors != nil {
in, out := &in.WorkloadMonitors, &out.WorkloadMonitors
*out = make([]WorkloadMonitorTemplate, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.
@@ -359,6 +468,28 @@ func (in *WorkloadMonitorStatus) DeepCopy() *WorkloadMonitorStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadMonitorTemplate) DeepCopyInto(out *WorkloadMonitorTemplate) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadMonitorTemplate.
func (in *WorkloadMonitorTemplate) DeepCopy() *WorkloadMonitorTemplate {
if in == nil {
return nil
}
out := new(WorkloadMonitorTemplate)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadStatus) DeepCopyInto(out *WorkloadStatus) {
*out = *in

View File

@@ -26,8 +26,8 @@ import (
func main() {
ctx := genericapiserver.SetupSignalContext()
options := server.NewAppsServerOptions(os.Stdout, os.Stderr)
cmd := server.NewCommandStartAppsServer(ctx, options)
options := server.NewCozyServerOptions(os.Stdout, os.Stderr)
cmd := server.NewCommandStartCozyServer(ctx, options)
code := cli.Run(cmd)
os.Exit(code)
}

View File

@@ -38,7 +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/controller/dashboard"
"github.com/cozystack/cozystack/internal/telemetry"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
@@ -54,6 +54,7 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(cozystackiov1alpha1.AddToScheme(scheme))
utilruntime.Must(dashboard.AddToScheme(scheme))
utilruntime.Must(helmv2.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
@@ -68,7 +69,7 @@ func main() {
var telemetryEndpoint string
var telemetryInterval string
var cozystackVersion string
var watchResources string
var reconcileDeployment 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.")
@@ -88,9 +89,8 @@ 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'.")
flag.BoolVar(&reconcileDeployment, "reconcile-deployment", false,
"If set, the Cozystack API server is assumed to run as a Deployment, else as a DaemonSet.")
opts := zap.Options{
Development: false,
}
@@ -155,7 +155,12 @@ func main() {
// this setup is not recommended for production.
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
// Configure rate limiting for the Kubernetes client
config := ctrl.GetConfigOrDie()
config.QPS = 50.0 // Increased from default 5.0
config.Burst = 100 // Increased from default 10
mgr, err := ctrl.NewManager(config, ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
@@ -187,6 +192,14 @@ func main() {
os.Exit(1)
}
if err = (&controller.WorkloadMonitorFromCRDReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "WorkloadMonitorFromCRD")
os.Exit(1)
}
if err = (&controller.WorkloadReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
@@ -211,20 +224,25 @@ func main() {
os.Exit(1)
}
cozyAPIKind := "DaemonSet"
if reconcileDeployment {
cozyAPIKind = "Deployment"
}
if err = (&controller.CozystackResourceDefinitionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
CozystackAPIKind: cozyAPIKind,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler")
os.Exit(1)
}
if err := (&lineagelabeler.LineageLabelerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
WatchResourceCSV: watchResources,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "LineageLabeler")
dashboardManager := &dashboard.Manager{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}
if err = dashboardManager.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "DashboardReconciler")
os.Exit(1)
}
@@ -253,7 +271,9 @@ func main() {
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
ctx := ctrl.SetupSignalHandler()
dashboardManager.InitializeStaticResources(ctx)
if err := mgr.Start(ctx); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}

View File

@@ -0,0 +1,179 @@
/*
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/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
lcw "github.com/cozystack/cozystack/internal/lineagecontrollerwebhook"
// +kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(cozystackiov1alpha1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var secureMetrics bool
var enableHTTP2 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.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")
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.
}
// Configure rate limiting for the Kubernetes client
config := ctrl.GetConfigOrDie()
config.QPS = 50.0 // Increased from default 5.0
config.Burst = 100 // Increased from default 10
mgr, err := ctrl.NewManager(config, ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "8796f12d.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 start manager")
os.Exit(1)
}
lineageControllerWebhook := &lcw.LineageControllerWebhook{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}
if err := lineageControllerWebhook.SetupWithManagerAsController(mgr); err != nil {
setupLog.Error(err, "unable to setup controller", "controller", "LineageController")
os.Exit(1)
}
if err := lineageControllerWebhook.SetupWithManagerAsWebhook(mgr); err != nil {
setupLog.Error(err, "unable to setup webhook", "webhook", "LineageWebhook")
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

@@ -0,0 +1,18 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0..
-->
## Features and Improvements
## Security
## Fixes
## Dependencies
## Development, Testing, and CI/CD
---
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.36.0...main

View File

@@ -17,4 +17,4 @@ https://github.com/cozystack/cozystack/releases/tag/v0..
---
**Full Changelog**: **Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0

View File

@@ -0,0 +1,3 @@
# Changes after v0.37.0
* [lineage] Break webhook out into a separate daemonset. Reduce unnecessary webhook calls by marking handled resources and excluding them from consideration by the webhook's object selector (@lllamnyp in #1515).

View File

@@ -0,0 +1,10 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.35.3
-->
## Fixes
* [seaweedfs] Add a liveness check for the SeaweedFS S3 endpoint to improve health monitoring and enable automatic recovery. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1368)
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.2...v0.35.3

View File

@@ -0,0 +1,14 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.35.4
-->
## Fixes
* [virtual-machine] Fix the regression in VM update hook introduced in https://github.com/cozystack/cozystack/pull/1169 by targeting the correct API resource and avoiding conflicts with KubeVirt resources. (@kvaps in https://github.com/cozystack/cozystack/pull/1376, backported in https://github.com/cozystack/cozystack/pull/1377)
* [cozy-lib] Add the missing template `cozy-lib.resources.flatten`. (@kvaps in https://github.com/cozystack/cozystack/pull/1372, backported in https://github.com/cozystack/cozystack/pull/1375)
* [platform] Fix a boolean override bug in Helm merge. ConfigMap values now correctly take precedence over bundle defaults. (@dyudin0821 in https://github.com/cozystack/cozystack/pull/1385, backported in https://github.com/cozystack/cozystack/pull/1388)
* [seaweedfs] Resolve connectivity issues in SeaweedFS. Increase Nginx ingress timeouts for SeaweedFS S3 endpoint. (@kvaps in https://github.com/cozystack/cozystack/pull/1386, backported in https://github.com/cozystack/cozystack/pull/1390)
* [dx] Remove the BUILDER and PLATFORM autodetect logic in Makefiles. (@kvaps in https://github.com/cozystack/cozystack/pull/1391, backported in https://github.com/cozystack/cozystack/pull/1392)
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.3...v0.35.4

View File

@@ -0,0 +1,11 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.35.5
-->
## Fixes
* [etcd] Ensure that TopologySpreadConstraints consistently target etcd pods. (@kvaps in https://github.com/cozystack/cozystack/pull/1405, backported in https://github.com/cozystack/cozystack/pull/1406)
* [tests] Add resource quota for testing namespaces. (@IvanHunters in https://github.com/cozystack/cozystack/commit/4982cdf5024c8bb9aa794b91d55545ea6b105d17)
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.4...v0.35.5

117
docs/changelogs/v0.36.0.md Normal file
View File

@@ -0,0 +1,117 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.36.0
-->
## Feature Highlights
Release v0.36.0 focuses on the stability, observability, and flexible configuration of managed applications.
### Per-Namespace Resource Limits for Tenants
Resource management for Cozystack tenants has received a final patch and is now graduated to a stable feature.
Platform administrators can define explicit CPU, memory, and storage limits for each tenant's namespace
via the tenant specification.
This prevents any single tenant from consuming more than their share of cluster resources,
ensuring cluster stability and a guaranteed service level for each tenant.
### Kube-OVN Cluster Health Monitor
A new component called the Kube-OVN Plunger continuously monitors the health of the Kube-OVN network's central control cluster.
This external agent gathers OVN cluster status and consensus information, exposing Prometheus metrics and live events stream via SSE.
As a result, it provides much better visibility of the virtual network layer and helps maintain a reliable and observable network in Cozystack.
This change opens the road to automated Kube-OVN database operations and recovery in specific corner cases.
### Configurable CoreDNS Addon for Kubernetes
Cozystack introduces a dedicated CoreDNS addon for managing cluster DNS with greater flexibility.
CoreDNS is now deployed via a Helm chart and can be tuned through custom values in the cluster specification,
including autoscaling, replica count, and adjusting service IP.
CoreDNS can now be configured in the dashboard and using Cozystack API.
### Granular SeaweedFS Service Configuration
The SeaweedFS S3 storage service in Cozystack is now far more configurable at a component level.
The Helm chart for SeaweedFS now includes independent configuration for each component and its resources.
It includes the master nodes, volume servers with support for multiple zones, filers, the backing database, and the S3 gateway.
Administrators can set per-component parameters such as the number of replicas, available CPU, memory, and storage size.
### Server-side Encryption for S3
Cozystack v0.36.0 includes SeaweedFS 3.97, bringing support for server-side encryption of S3 buckets (SSE-C, SSE-KMS, and SSE-S3).
**Breaking change:** upon updating Cozystack, SeaweedFS will be updated to a newer version, and the services specification
will be converted to the new format.
### Custom Resource Profiles for Ingress Controller
NGINX controller is now configurable on a per-replica basis.
Configurations include the ingress controller pods' CPU and memory requests/limits, either with direct values or using one of the available presets.
### Cozystack REST API Documentation
[Cozystack REST API reference](https://cozystack.io/docs/cozystack-api/rest/) is now published on the website.
It includes endpoints and methods for listing, creating, updating, and removing each managed application, defined as Cozystack CRD.
### Built-in LLDP-Based Neighbor Discovery in Talos
Cozystack now includes the LLDPD extension in its Talos OS image, enabling Link Layer Discovery Protocol (LLDP) out of the box.
This means each node can automatically discover and advertise its network neighbors and topology without any manual setup.
### Use external IP for Egress Traffic in VMs
When a virtual machine has an external IP assigned to it, it will now always use it for egress traffic, independently of the external method used.
## Major 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)
* [kubernetes] Add a configurable CoreDNS addon with valuesOverride, packaged chart, and managed deployment (metrics, autoscaling, HPA, customizable Service). (@klinch0 in https://github.com/cozystack/cozystack/pull/1362)
* [kube-ovn] Implement the Kube-OVN plunger, an external monitoring agent for the ovn-central cluster. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1380, patched in https://github.com/cozystack/cozystack/pull/1414 and https://github.com/cozystack/cozystack/pull/1418)
* [tenant] Enable per-namespace resource quota settings in tenants, with explicit cpu, memory, and storage values. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1389)
* [seaweedfs] Add detailed resource configuration for each component of the SeaweedFS service. (@klinch0 and @kvaps in https://github.com/cozystack/cozystack/pull/1415)
* [ingress] Enable per-replica resource configuration to the ingress controller. (@kvaps in https://github.com/cozystack/cozystack/pull/1416)
* [virtual-machine] Use external IP for egress traffic with `PortList` method. (@kvaps in https://github.com/cozystack/cozystack/pull/1349)
## Fixes
* [cozy-lib] Fix malformed retrieval of `cozyConfig` in the cozy-lib template. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1348)
* [cozy-lib] Add the missing template `cozy-lib.resources.flatten`. (@kvaps in https://github.com/cozystack/cozystack/pull/1372)
* [cozystack-api] Sanitize the OpenAPI v2 schema. (@kvaps in https://github.com/cozystack/cozystack/pull/1353)
* [kube-ovn] Improve northd leader detection. Patch the northd leader check to test against all endpoints instead of just the first one marked as ready. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1363)
* [seaweedfs] Add a liveness check for the SeaweedFS S3 endpoint to improve health monitoring and enable automatic recovery. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1368)
* [seaweedfs] Resolve race conditions in SeaweedFS. Increase deployment timeouts and set install/upgrade remediation to unlimited retries to improve deployment resilience. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1371)
* [seaweedfs] Resolve connectivity issues in SeaweedFS. Increase Nginx ingress timeouts for SeaweedFS S3 endpoint. (@kvaps in https://github.com/cozystack/cozystack/pull/1386)
* [virtual-machine] Fix the reg ression in VM update hook introduced in https://github.com/cozystack/cozystack/pull/1169. Target the correct API resource and avoid conflicts with KubeVirt resources. (@kvaps in https://github.com/cozystack/cozystack/pull/1376)
* [virtual-machine] Correct app version references in `virtual-machine` and `vm-instance`, ensuring accurate versioning during migrations. (@kvaps in https://github.com/cozystack/cozystack/pull/1378).
* [cozyreport] Fix an error where cozyreport tried to parse non-existent objects and generated garbage output in CI debug logs. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1383)
* [platform] Fix a boolean override bug in Helm merge. ConfigMap values now correctly take precedence over bundle defaults. (@dyudin0821 in https://github.com/cozystack/cozystack/pull/1385)
* [kubernetes] CoreDNS release now installs and stores state in the `kube-system` namespace. (@kvaps in https://github.com/cozystack/cozystack/pull/1395)
* [kubernetes] Expose configuration for CoreDNS, enabling setting the image repository and replica count via `values.yaml`. (@kvaps in https://github.com/cozystack/cozystack/pull/1410)
* [etcd] Ensure that TopologySpreadConstraints consistently target etcd pods. (@kvaps in https://github.com/cozystack/cozystack/pull/1405)
* [tenant] Use force-upgrade for ingress controller charts. (@klinch0 in https://github.com/cozystack/cozystack/pull/1404)
* [cozystack-controller] Fix an RBAC error that prevented the workload labelling feature from working. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1419)
* [seaweedfs] Remove VerticalPodAutoscaler for SeaweedFS. (@kvaps in https://github.com/cozystack/cozystack/pull/1421)
## Dependencies
* Update LINSTOR to v1.31.3. (@kvaps in https://github.com/cozystack/cozystack/pull/1358)
* Update SeaweedFS to v3.97. (@kvaps in https://github.com/cozystack/cozystack/pull/1361 and https://github.com/cozystack/cozystack/pull/1373)
* Update Kube-OVN to 1.14.5. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1363)
* Replace Bitnami images with alternatives in all charts. (@kvaps in https://github.com/cozystack/cozystack/pull/1374)
## Documentation
## Development, Testing, and CI/CD
* [dx] Remove the BUILDER and PLATFORM autodetect logic in Makefiles. (@kvaps in https://github.com/cozystack/cozystack/pull/1391)
* [ci] Use the host buildx config in CI. (@kvaps in https://github.com/cozystack/cozystack/pull/1015)
* [ci] Add `jq` and `git` to the installer image. (@kvaps in https://github.com/cozystack/cozystack/pull/1417)
* [ci] Source the `REGISTRY` environment variable from actions' variables, not secrets, so external pull requests can work. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1423)
---
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.0...v0.36.0

View File

@@ -0,0 +1,22 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.36.1
-->
## Major Features and Improvements
* [cozystack-api] Implement recursive, Kubernetes-like defaulting for applications: missing fields in nested objects and arrays are auto-populated safely without mutating shared defaults. (@kvaps in https://github.com/cozystack/cozystack/pull/1432)
## Fixes
* [cozystack-api] Update defaulting API schemas. (@kvaps in https://github.com/cozystack/cozystack/pull/1433)
* [dashboard] Fix Bitnami dependencies. (@kvaps in https://github.com/cozystack/cozystack/pull/1431)
* [seaweedfs] Fix SeaweedFS migration. (@kvaps in https://github.com/cozystack/cozystack/pull/1430)
## Development, Testing, and CI/CD
* [adopters] Add [Hidora](https://hikube.cloud) to the Cozystack adopters list. (@matthieu-robin in https://github.com/cozystack/cozystack/pull/1429)
---
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.36.0...v0.36.1

View File

@@ -0,0 +1,18 @@
<!--
https://github.com/cozystack/cozystack/releases/tag/v0.36.2
-->
## Features and Improvements
## Security
## Fixes
## Dependencies
## Development, Testing, and CI/CD
---
**Full Changelog**: [v0.36.1...v0.36.2](https://github.com/cozystack/cozystack/compare/v0.36.1...v0.36.2)

117
docs/changelogs/v0.37.0.md Normal file
View File

@@ -0,0 +1,117 @@
# Cozystack v0.37 — “OpenAPI Dashboard & Lineage Everywhere”
Weve shipped a big usability push this cycle: a brand-new **OpenAPI-driven dashboard**, lineage labeling across core resource types, and several reliability improvements to smooth upgrades from 0.36→ 0.37. Below are the highlights and the full categorized lists.
## Highlights
* **New OpenAPI-based Dashboard** replaces the old UI, adds module-aware navigation, dynamic branding, and richer Kubernetes resource views ([**@kvaps**](https://github.com/kvaps) in #1269, #1463, #1460).
* **Lineage Webhook** tags Pods, PVCs, Services, Ingresses, and Secrets, adding labels referencing the managing Cozystack application ([**@lllamnyp**](https://github.com/lllamnyp) in #1448, #1452, #1477, #1486, #1497; [**@kvaps**](https://github.com/kvaps) in #1454).
* **Smoother upgrades** with installer and migration hardening, decoupled CRDs vs. API server ([**@lllamnyp**](https://github.com/lllamnyp) in #1494, #1498; [**@kvaps**](https://github.com/kvaps) in #1506).
* **Operations quality**: Kubernetes tests with smarter waits/readiness checks ([**@IvanHunters**](https://github.com/IvanHunters) in #1485).
---
## New features
### Dashboard
* Introduce the OpenAPI-based dashboard and controller; implement TenantNamespace, TenantModules, TenantSecret/SecretsTable resources ([**@kvaps**](https://github.com/kvaps) in #1269).
* Module-aware navigation, richer detail views (Services/Secrets/Ingresses), improved sidebars; “Tenant Modules” grouping ([**@kvaps**](https://github.com/kvaps) in #1463).
* Dynamic branding via cluster config (tenant name, footer/title, logo/icon SVGs) ([**@kvaps**](https://github.com/kvaps) in #1460).
* Dashboard: fix namespace listing for unprivileged users and stabilize streamed requests; build-time patching ([**@kvaps**](https://github.com/kvaps) in #1456).
* Dashboard UX set: marketplace hides module resources; consistent navigation/links; prefill “name” in forms; ingress factory; formatted TenantNamespaces tables ([**@kvaps**](https://github.com/kvaps) in #1463).
* **Dashboard**: list modules reliably; remove Tenant from Marketplace; fix field override while typing ([**@kvaps**](https://github.com/kvaps) in #1501, #1503).
* **Dashboard**: correct API group for applications; sidebars; disable auto-expand; fix `/docs` redirect ([**@kvaps**](https://github.com/kvaps) in #1463, #1465, #1462).
* **Dashboard**: show Secrets with empty values correctly ([**@kvaps**](https://github.com/kvaps) in #1480).
* Dashboard configuration refactor: generate static resources at startup; auto-cleanup stale objects; higher controller client throughput ([**@kvaps**](https://github.com/kvaps) in #1457).
### Migration to v0.37
* **Installer/Migrations**: prevent unintended deletion of platform resource definitions; resilient timestamping; tolerant annotations; stronger migrate-then-reconcile flow ([**@kvaps**](https://github.com/kvaps) in #1475; Andrei Kvapil & [**@lllamnyp**](https://github.com/lllamnyp) in #1498).
* Installer hardening for **migration #20**: packaged apply, ordered waits/readiness checks, RFC3339(nano) stamping; Helm in installer image (Andrei Kvapil & [**@lllamnyp**](https://github.com/lllamnyp) in #1498).
* **Decoupled API & CozyRDs**: You can now upgrade the Cozystack API server independently of CRDs/CozyRD instances, easing 0.36 → 0.37 migrations ([**@lllamnyp**](https://github.com/lllamnyp) in #1494).
* **Migration #20**: The installer runs migration from packaged Helm charts with ordered waits/readiness checks; annotations are tolerant; timestamps are environment-robust (Andrei Kvapil & [**@lllamnyp**](https://github.com/lllamnyp) in #1498; [**@kvaps**](https://github.com/kvaps) in #1475).
### Webhook / Lineage
* Add a lineage mutating webhook to auto-label Pods/Secrets/PVCs/Ingresses/WorkloadMonitors with owning app ([**@lllamnyp**](https://github.com/lllamnyp) in #1448, #1497, [**@kvaps**](https://github.com/kvaps) in #1454).
* **Name-based** selectors for Secret visibility (templates supported) ([**@lllamnyp**](https://github.com/lllamnyp) in #1477).
* Select **Services** and **Ingresses** in CRDs/API; treat them as user-facing when configured ([**@lllamnyp**](https://github.com/lllamnyp) in #1486).
* **VictoriaMetrics integration**: Lineage labels are explicitly set on VM resources; `managedMetadata` is configured to avoid controller “fights” over labels ([**@lllamnyp**](https://github.com/lllamnyp) in #1452).
* Webhook **excludes** `default` and `kube-system` to avoid unintended mutations (part of the installer/migration hardening by Andrei Kvapil & [**@lllamnyp**](https://github.com/lllamnyp) in #1498).
### API / Platform
* Decouple the Cozystack API from Cozystack Resource Definitions to allow independent upgrades ([**@lllamnyp**](https://github.com/lllamnyp) in #1494).
* Add **label selectors** to app definitions for Secret include/exclude ([**@lllamnyp**](https://github.com/lllamnyp) in #1447).
### Monitoring & Ops
* Reduce node labelsets in target relabeling configs on cadvisor/kubelet metrics to reduce cardinality while keeping useful CPU metrics ([**@IvanHunters**](https://github.com/IvanHunters) in #1455).
### Storage & Backups
* PVC expansion in tenant clusters via KubeVirt CSI resizer; RBAC updates (Klinch0 in #1438).
* Velero upgraded to **v1.17.0**; node agent enabled by default and a raft of usability features ([**@kvaps**](https://github.com/kvaps) in #1484).
### Kubernetes/tests & Tooling
* Smarter Kubernetes test flows: node readiness checks, kubelet version validation, longer rollout waits, per-component readiness ([**@IvanHunters**](https://github.com/IvanHunters) in #1485).
### UI/Icons
* New **VM-Disk** SVG icon ([**@kvapsova**](https://github.com/kvapsova) in #1435).
---
## Improvements (minor)
* Make the **Info** app deploy irrespective of OIDC settings ([**klinch0**](https://github.com/klinch0) in #1474).
* Move SA token Secret creation to **Info** app ([**@lllamnyp**](https://github.com/lllamnyp) in #1446).
* Explicitly set lineage labels for VictoriaMetrics resources ([**@lllamnyp**](https://github.com/lllamnyp) in #1452).
---
## Bug fixes
* **Kubernetes**: fix MachineDeployment `spec.selector` mismatch to ensure proper targeting ([**@kvaps**](https://github.com/kvaps) in #1502).
* **Old dashboard**: FerretDB spec typo prevented deploy/display ([**@lllamnyp**](https://github.com/lllamnyp) in #1440).
* **SeaweedFS**: fix per-zone size fallback for multi-DC volumes; make migrations more robust ([**@kvaps**](https://github.com/kvaps) in #1476, #1430).
* **CoreDNS**: pin tag to v1.12.4 ([**@kvaps**](https://github.com/kvaps) in #1469).
* **OIDC**: avoid creating KeycloakRealmGroup before operator API is available ([**@lllamnyp**](https://github.com/lllamnyp) in #1495).
* **Kafka**: disable noisy alerts when Kafka isnt deployed ([**@lllamnyp**](https://github.com/lllamnyp) in #1488).
---
## Dependency & version updates
* **Velero → v1.17.0**; Helm chart v11; node agent default-on ([**@kvaps**](https://github.com/kvaps) in #1484).
* **Cilium → v1.17.8** ([**@kvaps**](https://github.com/kvaps) in #1473).
* **Flux Operator → v0.29.0** (Kingdon Barrett in #1466).
---
## Refactors & chores
* Remove legacy `versions_map`; unify packaging targets; tighten HelmRelease defaults; replace many chart versions with build-time placeholders ([**@kvaps**](https://github.com/kvaps) in #1453).
* Pin CoreDNS image and refresh numerous images ([**@kvaps**](https://github.com/kvaps) in #1469; related image refreshes across #1448 work).
---
## Documentation & governance
* **Contributor Ladder** created and later updated (Timur Tukaev in #1224; Andrei Kvapil & Timur Tukaev in #1492).
* **Code of Conduct** updated with a Vendor Neutrality Manifesto (Timur Tukaev in #1493).
* **Adopters**: add Hidora (Matthieu Robin in #1429).
* **MAINTAINERS**: add/remove entries (Nikita Bykov in #1487; Timur Tukaev in #1491).
* **Issue templates**: new bug-report template and tweaks (Moriarti).
* **README**: updated dark-theme screenshot ([**@kvaps**](https://github.com/kvaps) in #1459).
---
## Breaking changes & upgrade notes
---
## Security & stability

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bats
@test "Create DB FerretDB" {
name='test'
kubectl apply -f - <<EOF
apiVersion: apps.cozystack.io/v1alpha1
kind: FerretDB
metadata:
name: $name
namespace: tenant-test
spec:
backup:
destinationPath: "s3://bucket/path/to/folder/"
enabled: false
endpointURL: "http://minio-gateway-service:9000"
retentionPolicy: "30d"
s3AccessKey: "<your-access-key>"
s3SecretKey: "<your-secret-key>"
schedule: "0 2 * * * *"
bootstrap:
enabled: false
external: false
quorum:
maxSyncReplicas: 0
minSyncReplicas: 0
replicas: 2
resources: {}
resourcesPreset: "micro"
size: "10Gi"
users:
testuser:
password: xai7Wepo
EOF
sleep 5
kubectl -n tenant-test wait hr ferretdb-$name --timeout=100s --for=condition=ready
timeout 40 sh -ec "until kubectl -n tenant-test get svc ferretdb-$name-postgres-r -o jsonpath='{.spec.ports[0].port}' | grep -q '5432'; do sleep 10; done"
timeout 40 sh -ec "until kubectl -n tenant-test get svc ferretdb-$name-postgres-ro -o jsonpath='{.spec.ports[0].port}' | grep -q '5432'; do sleep 10; done"
timeout 40 sh -ec "until kubectl -n tenant-test get svc ferretdb-$name-postgres-rw -o jsonpath='{.spec.ports[0].port}' | grep -q '5432'; do sleep 10; done"
timeout 120 sh -ec "until kubectl -n tenant-test get endpoints ferretdb-$name-postgres-r -o jsonpath='{.subsets[*].addresses[*].ip}' | grep -q '[0-9]'; do sleep 10; done"
# for some reason it takes longer for the read-only endpoint to be ready
#timeout 120 sh -ec "until kubectl -n tenant-test get endpoints ferretdb-$name-postgres-ro -o jsonpath='{.subsets[*].addresses[*].ip}' | grep -q '[0-9]'; do sleep 10; done"
timeout 120 sh -ec "until kubectl -n tenant-test get endpoints ferretdb-$name-postgres-rw -o jsonpath='{.subsets[*].addresses[*].ip}' | grep -q '[0-9]'; do sleep 10; done"
kubectl -n tenant-test delete ferretdb.apps.cozystack.io $name
}

View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bats
@test "Create DB FoundationDB" {
name='test'
kubectl apply -f - <<EOF
apiVersion: apps.cozystack.io/v1alpha1
kind: FoundationDB
metadata:
name: $name
namespace: tenant-test
spec:
cluster:
version: "7.3.63"
processCounts:
storage: 3
stateless: -1
cluster_controller: 1
redundancyMode: "double"
storageEngine: "ssd-2"
faultDomain:
key: "foundationdb.org/none"
valueFrom: "\$FDB_ZONE_ID"
storage:
size: "1Gi"
storageClass: ""
resourcesPreset: "small"
backup:
enabled: false
s3:
bucket: ""
endpoint: ""
region: ""
credentials:
accessKeyId: ""
secretAccessKey: ""
retentionPolicy: "7d"
monitoring:
enabled: true
customParameters:
- "knob_disable_posix_kernel_aio=1"
imageType: "unified"
automaticReplacements: true
EOF
sleep 15
# Wait for HelmRelease to be ready
kubectl -n tenant-test wait hr foundationdb-$name --timeout=300s --for=condition=ready
# Wait for FoundationDBCluster to be created (name has foundationdb- prefix)
timeout 300 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name; do sleep 15; done"
# Wait for cluster to become available (initial reconciliation takes time - allow 5 minutes)
timeout 300 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.usable_regions}' | grep -q '1'; do sleep 30; done"
# Check that storage processes are running
timeout 300 sh -ec "until [ \$(kubectl -n tenant-test get pods -l foundationdb.org/fdb-cluster-name=foundationdb-$name,foundationdb.org/fdb-process-class=storage --field-selector=status.phase=Running --no-headers | wc -l) -eq 3 ]; do sleep 15; done"
# Check that log processes are running (these are the stateless processes)
timeout 300 sh -ec "until [ \$(kubectl -n tenant-test get pods -l foundationdb.org/fdb-cluster-name=foundationdb-$name,foundationdb.org/fdb-process-class=log --field-selector=status.phase=Running --no-headers | wc -l) -ge 1 ]; do sleep 15; done"
# Check that cluster controller is running
timeout 300 sh -ec "until [ \$(kubectl -n tenant-test get pods -l foundationdb.org/fdb-cluster-name=foundationdb-$name,foundationdb.org/fdb-process-class=cluster_controller --field-selector=status.phase=Running --no-headers | wc -l) -eq 1 ]; do sleep 15; done"
# Check WorkloadMonitor is created and configured
timeout 120 sh -ec "until kubectl -n tenant-test get workloadmonitor foundationdb-$name; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get workloadmonitor foundationdb-$name -o jsonpath='{.spec.replicas}' | grep -q '3'; do sleep 5; done"
# Check dashboard resource map is created
kubectl -n tenant-test get configmap foundationdb-$name-resourcemap
# Verify cluster is healthy (check cluster status) - allow extra time for initial setup
timeout 300 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.health.available}' | grep -q 'true'; do sleep 20; done"
# Validate status.configured field
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.configured}' | grep -q 'true'; do sleep 10; done"
# Validate status.connectionString field exists and contains expected format
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.connectionString}' | grep -q '@.*\.svc\.cozy\.local'; do sleep 10; done"
# Validate comprehensive status.databaseConfiguration fields
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.logs}' | grep -q '3'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.proxies}' | grep -q '3'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.redundancy_mode}' | grep -q 'double'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.resolvers}' | grep -q '1'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.storage_engine}' | grep -q 'ssd-2'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.databaseConfiguration.usable_regions}' | grep -q '1'; do sleep 10; done"
# Validate status.desiredProcessGroups field
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.desiredProcessGroups}' | grep -q '^[0-9][0-9]*$'; do sleep 10; done"
# Validate status.generations.reconciled field
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.generations.reconciled}' | grep -q '^[0-9][0-9]*$'; do sleep 10; done"
# Validate status.hasListenIPsForAllPods field
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.hasListenIPsForAllPods}' | grep -q 'true'; do sleep 10; done"
# Validate comprehensive status.health fields
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.health.fullReplication}' | grep -q 'true'; do sleep 10; done"
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.status.health.healthy}' | grep -q 'true'; do sleep 10; done"
# Verify security context is applied correctly (non-root user)
storage_pod=$(kubectl -n tenant-test get pods -l foundationdb.org/fdb-cluster-name=foundationdb-$name,foundationdb.org/fdb-process-class=storage --no-headers | head -n1 | awk '{print $1}')
kubectl -n tenant-test get pod "$storage_pod" -o jsonpath='{.spec.containers[0].securityContext.runAsUser}' | grep -q '4059'
kubectl -n tenant-test get pod "$storage_pod" -o jsonpath='{.spec.containers[0].securityContext.runAsGroup}' | grep -q '4059'
# Verify volumeClaimTemplate is properly configured in FoundationDBCluster CRD
timeout 60 sh -ec "until kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name -o jsonpath='{.spec.processes.general.volumeClaimTemplate.spec.resources.requests.storage}' | grep -q '1Gi'; do sleep 10; done"
# Verify PVCs are created with correct storage size (1Gi as specified in test)
timeout 120 sh -ec "until [ \$(kubectl -n tenant-test get pvc -l foundationdb.org/fdb-cluster-name=foundationdb-$name --no-headers | wc -l) -ge 3 ]; do sleep 10; done"
kubectl -n tenant-test get pvc -l foundationdb.org/fdb-cluster-name=foundationdb-$name -o jsonpath='{.items[*].spec.resources.requests.storage}' | grep -q '1Gi'
# Verify actual PVC storage capacity matches requested size
kubectl -n tenant-test get pvc -l foundationdb.org/fdb-cluster-name=foundationdb-$name -o jsonpath='{.items[*].status.capacity.storage}' | grep -q '1Gi'
# Clean up
kubectl -n tenant-test delete foundationdb $name
# Wait for cleanup to complete
timeout 120 sh -ec "while kubectl -n tenant-test get foundationdbclusters.apps.foundationdb.org foundationdb-$name 2>/dev/null; do sleep 10; done"
}

View File

@@ -64,37 +64,90 @@ spec:
EOF
# Wait for the tenant-test namespace to be active
kubectl wait namespace tenant-test --timeout=20s --for=jsonpath='{.status.phase}'=Active
# Wait for the Kamaji control plane to be created (retry for up to 10 seconds)
timeout 10 sh -ec 'until kubectl get kamajicontrolplane -n tenant-test kubernetes-'"${test_name}"'; do sleep 1; done'
# Wait for the tenant control plane to be fully created (timeout after 4 minutes)
kubectl wait --for=condition=TenantControlPlaneCreated kamajicontrolplane -n tenant-test kubernetes-${test_name} --timeout=4m
# Wait for Kubernetes resources to be ready (timeout after 2 minutes)
kubectl wait tcp -n tenant-test kubernetes-${test_name} --timeout=2m --for=jsonpath='{.status.kubernetesResources.version.status}'=Ready
# Wait for all required deployments to be available (timeout after 4 minutes)
kubectl wait deploy --timeout=4m --for=condition=available -n tenant-test kubernetes-${test_name} kubernetes-${test_name}-cluster-autoscaler kubernetes-${test_name}-kccm kubernetes-${test_name}-kcsi-controller
# Wait for the machine deployment to scale to 2 replicas (timeout after 1 minute)
kubectl wait machinedeployment kubernetes-${test_name}-md0 -n tenant-test --timeout=1m --for=jsonpath='{.status.replicas}'=2
# Get the admin kubeconfig and save it to a file
kubectl get secret kubernetes-${test_name}-admin-kubeconfig -ojsonpath='{.data.super-admin\.conf}' -n tenant-test | base64 -d > tenantkubeconfig
# Update the kubeconfig to use localhost for the API server
yq -i ".clusters[0].cluster.server = \"https://localhost:${port}\"" tenantkubeconfig
# Set up port forwarding to the Kubernetes API server for a 40 second timeout
bash -c 'timeout 40s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
# Set up port forwarding to the Kubernetes API server for a 200 second timeout
bash -c 'timeout 300s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
# Verify the Kubernetes version matches what we expect (retry for up to 20 seconds)
timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 5; done'
# Wait for the nodes to be ready (timeout after 2 minutes)
timeout 3m bash -c '
until [ "$(kubectl --kubeconfig tenantkubeconfig get nodes -o jsonpath="{.items[*].metadata.name}" | wc -w)" -eq 2 ]; do
sleep 2
done
'
# Verify the nodes are ready
kubectl --kubeconfig tenantkubeconfig wait node --all --timeout=2m --for=condition=Ready
kubectl --kubeconfig tenantkubeconfig get nodes -o wide
# Verify the kubelet version matches what we expect
versions=$(kubectl --kubeconfig tenantkubeconfig get nodes -o jsonpath='{.items[*].status.nodeInfo.kubeletVersion}')
node_ok=true
case "$k8s_version" in
v1.32*)
echo "⚠️ TODO: Temporary stub — allowing nodes with v1.33 while k8s_version is v1.32"
;;
esac
for v in $versions; do
case "$k8s_version" in
v1.32|v1.32.*)
case "$v" in
v1.32 | v1.32.* | v1.32-* | v1.33 | v1.33.* | v1.33-*)
;;
*)
node_ok=false
break
;;
esac
;;
*)
case "$v" in
"${k8s_version}" | "${k8s_version}".* | "${k8s_version}"-*)
;;
*)
node_ok=false
break
;;
esac
;;
esac
done
if [ "$node_ok" != true ]; then
echo "Kubelet versions did not match expected ${k8s_version}" >&2
exit 1
fi
# Wait for all machine deployment replicas to be ready (timeout after 10 minutes)
kubectl wait machinedeployment kubernetes-${test_name}-md0 -n tenant-test --timeout=10m --for=jsonpath='{.status.v1beta2.readyReplicas}'=2
for component in cilium coredns csi ingress-nginx vsnap-crd; do
kubectl wait hr kubernetes-${test_name}-${component} -n tenant-test --timeout=1m --for=condition=ready
done
# Clean up by deleting the Kubernetes resource
kubectl -n tenant-test delete kuberneteses.apps.cozystack.io $test_name

View File

@@ -132,7 +132,6 @@ machine:
- usermode_helper=disabled
- name: zfs
- name: spl
- name: lldpd
registries:
mirrors:
docker.io:

View File

@@ -1,64 +0,0 @@
#!/bin/sh
set -e
file=versions_map
charts=$(find . -mindepth 2 -maxdepth 2 -name Chart.yaml | awk 'sub("/Chart.yaml", "")')
new_map=$(
for chart in $charts; do
awk '/^name:/ {chart=$2} /^version:/ {version=$2} END{printf "%s %s %s\n", chart, version, "HEAD"}' "$chart/Chart.yaml"
done
)
if [ ! -f "$file" ] || [ ! -s "$file" ]; then
echo "$new_map" > "$file"
exit 0
fi
miss_map=$(mktemp)
trap 'rm -f "$miss_map"' EXIT
echo -n "$new_map" | awk 'NR==FNR { nm[$1 " " $2] = $3; next } { if (!($1 " " $2 in nm)) print $1, $2, $3}' - "$file" > $miss_map
# search accross all tags sorted by version
search_commits=$(git ls-remote --tags origin | awk -F/ '$3 ~ /v[0-9]+.[0-9]+.[0-9]+/ {print}' | sort -k2,2 -rV | awk '{print $1}')
resolved_miss_map=$(
while read -r chart version commit; do
# if version is found in HEAD, it's HEAD
if [ "$(awk '$1 == "version:" {print $2}' ./${chart}/Chart.yaml)" = "${version}" ]; then
echo "$chart $version HEAD"
continue
fi
# if commit is not HEAD, check if it's valid
if [ "$commit" != "HEAD" ]; then
if [ "$(git show "${commit}:./${chart}/Chart.yaml" | awk '$1 == "version:" {print $2}')" != "${version}" ]; then
echo "Commit $commit for $chart $version is not valid" >&2
exit 1
fi
commit=$(git rev-parse --short "$commit")
echo "$chart $version $commit"
continue
fi
# if commit is HEAD, but version is not found in HEAD, check all tags
found_tag=""
for tag in $search_commits; do
if [ "$(git show "${tag}:./${chart}/Chart.yaml" | awk '$1 == "version:" {print $2}')" = "${version}" ]; then
found_tag=$(git rev-parse --short "${tag}")
break
fi
done
if [ -z "$found_tag" ]; then
echo "Can't find $chart $version in any version tag, removing it" >&2
continue
fi
echo "$chart $version $found_tag"
done < $miss_map
)
printf "%s\n" "$new_map" "$resolved_miss_map" | sort -k1,1 -k2,2 -V | awk '$1' > "$file"

View File

@@ -1,65 +0,0 @@
#!/bin/sh
set -e
usage() {
printf "%s\n" "Usage:" >&2 ;
printf -- "%s\n" '---' >&2 ;
printf "%s %s\n" "$0" "INPUT_DIR OUTPUT_DIR TMP_DIR [DEPENDENCY_DIR]" >&2 ;
printf -- "%s\n" '---' >&2 ;
printf "%s\n" "Takes a helm repository from INPUT_DIR, with an optional library repository in" >&2 ;
printf "%s\n" "DEPENDENCY_DIR, prepares a view of the git archive at select points in history" >&2 ;
printf "%s\n" "in TMP_DIR and packages helm charts, outputting the tarballs to OUTPUT_DIR" >&2 ;
}
if [ "x$(basename $PWD)" != "xpackages" ]
then
echo "Error: This script must run from the ./packages/ directory" >&2
echo >&2
usage
exit 1
fi
if [ "x$#" != "x3" ] && [ "x$#" != "x4" ]
then
echo "Error: This script takes 3 or 4 arguments" >&2
echo "Got $# arguments:" "$@" >&2
echo >&2
usage
exit 1
fi
input_dir=$1
output_dir=$2
tmp_dir=$3
if [ "x$#" = "x4" ]
then
dependency_dir=$4
fi
rm -rf "${output_dir:?}"
mkdir -p "${output_dir}"
while read package _ commit
do
# this lets devs build the packages from a dirty repo for quick local testing
if [ "x$commit" = "xHEAD" ]
then
helm package "${input_dir}/${package}" -d "${output_dir}"
continue
fi
git archive --format tar "${commit}" "${input_dir}/${package}" | tar -xf- -C "${tmp_dir}/"
# the library chart is not present in older commits and git archive doesn't fail gracefully if the path is not found
if [ "x${dependency_dir}" != "x" ] && git ls-tree --name-only "${commit}" "${dependency_dir}" | grep -qx "${dependency_dir}"
then
git archive --format tar "${commit}" "${dependency_dir}" | tar -xf- -C "${tmp_dir}/"
fi
helm package "${tmp_dir}/${input_dir}/${package}" -d "${output_dir}"
rm -rf "${tmp_dir:?}/${input_dir:?}/${package:?}"
if [ "x${dependency_dir}" != "x" ]
then
rm -rf "${tmp_dir:?}/${dependency_dir:?}"
fi
done < "${input_dir}/versions_map"
helm repo index "${output_dir}"

View File

@@ -53,4 +53,6 @@ kube::codegen::gen_openapi \
"${SCRIPT_ROOT}/pkg/apis"
$CONTROLLER_GEN object:headerFile="hack/boilerplate.go.txt" paths="./api/..."
$CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/..." output:crd:artifacts:config=packages/system/cozystack-controller/templates/crds
$CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/..." output:crd:artifacts:config=packages/system/cozystack-controller/crds
mv packages/system/cozystack-controller/crds/cozystack.io_cozystackresourcedefinitions.yaml \
packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml

139
hack/update-crd.sh Executable file
View File

@@ -0,0 +1,139 @@
#!/usr/bin/env bash
set -euo pipefail
# Requirements: yq (v4), jq, base64
need() { command -v "$1" >/dev/null 2>&1 || { echo "need $1"; exit 1; }; }
need yq; need jq; need base64
CHART_YAML="${CHART_YAML:-Chart.yaml}"
VALUES_YAML="${VALUES_YAML:-values.yaml}"
SCHEMA_JSON="${SCHEMA_JSON:-values.schema.json}"
CRD_DIR="../../system/cozystack-resource-definitions/cozyrds"
[[ -f "$CHART_YAML" ]] || { echo "No $CHART_YAML found"; exit 1; }
[[ -f "$SCHEMA_JSON" ]] || { echo "No $SCHEMA_JSON found"; exit 1; }
# Read basics from Chart.yaml
NAME="$(yq -r '.name // ""' "$CHART_YAML")"
DESC="$(yq -r '.description // ""' "$CHART_YAML")"
ICON_PATH_RAW="$(yq -r '.icon // ""' "$CHART_YAML")"
if [[ -z "$NAME" ]]; then
echo "Chart.yaml: .name is empty"; exit 1
fi
# Resolve icon path
# Accepts:
# /logos/foo.svg -> ./logos/foo.svg
# logos/foo.svg -> logos/foo.svg
# ./logos/foo.svg -> ./logos/foo.svg
# Fallback: ./logos/${NAME}.svg
resolve_icon_path() {
local p="$1"
if [[ -z "$p" || "$p" == "null" ]]; then
echo "./logos/${NAME}.svg"; return
fi
if [[ "$p" == /* ]]; then
echo ".${p}"
else
echo "$p"
fi
}
ICON_PATH="$(resolve_icon_path "$ICON_PATH_RAW")"
if [[ ! -f "$ICON_PATH" ]]; then
# try fallback
ALT="./logos/${NAME}.svg"
if [[ -f "$ALT" ]]; then
ICON_PATH="$ALT"
else
echo "Icon not found: $ICON_PATH"; exit 1
fi
fi
# Base64 (portable: no -w / -b options)
ICON_B64="$(base64 < "$ICON_PATH" | tr -d '\n' | tr -d '\r')"
# Decide which HelmRepository name to use based on path
# .../apps/... -> cozystack-apps
# .../extra/... -> cozystack-extra
# default: cozystack-apps
SOURCE_NAME="cozystack-apps"
case "$PWD" in
*"/apps/"*) SOURCE_NAME="cozystack-apps" ;;
*"/extra/"*) SOURCE_NAME="cozystack-extra" ;;
esac
# If file doesn't exist, create a minimal skeleton
OUT="${OUT:-$CRD_DIR/$NAME.yaml}"
if [[ ! -f "$OUT" ]]; then
cat >"$OUT" <<EOF
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ${NAME}
spec: {}
EOF
fi
# Export vars for yq env()
export RES_NAME="$NAME"
export PREFIX="$NAME-"
if [ "$SOURCE_NAME" == "cozystack-extra" ]; then
export PREFIX=""
fi
export DESCRIPTION="$DESC"
export ICON_B64="$ICON_B64"
export SOURCE_NAME="$SOURCE_NAME"
export SCHEMA_JSON_MIN="$(jq -c . "$SCHEMA_JSON")"
# Generate keysOrder from values.yaml
export KEYS_ORDER="$(
yq -o=json '.' "$VALUES_YAML" | jq -c '
def get_paths_recursive(obj; path):
obj | to_entries | map(
.key as $key |
.value as $value |
if $value | type == "object" then
[path + [$key]] + get_paths_recursive($value; path + [$key])
else
[path + [$key]]
end
) | flatten(1)
;
(
[ ["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata","name"] ]
)
+
(
get_paths_recursive(.; []) # get all paths in order
| map(select(length>0)) # drop root
| map(map(select(type != "number"))) # drop array indices
| map(["spec"] + .) # prepend "spec"
)
'
)"
# Update only necessary fields in-place
# - openAPISchema is loaded from file as a multi-line string (block scalar)
# - labels ensure cozystack.io/ui: "true"
# - prefix = "<name>-"
# - sourceRef derived from directory (apps|extra)
yq -i '
.apiVersion = (.apiVersion // "cozystack.io/v1alpha1") |
.kind = (.kind // "CozystackResourceDefinition") |
.metadata.name = strenv(RES_NAME) |
.spec.application.openAPISchema = strenv(SCHEMA_JSON_MIN) |
(.spec.application.openAPISchema style="literal") |
.spec.release.prefix = (strenv(PREFIX)) |
.spec.release.labels."cozystack.io/ui" = "true" |
.spec.release.chart.name = strenv(RES_NAME) |
.spec.release.chart.sourceRef.kind = "HelmRepository" |
.spec.release.chart.sourceRef.name = strenv(SOURCE_NAME) |
.spec.release.chart.sourceRef.namespace = "cozy-public" |
.spec.dashboard.description = strenv(DESCRIPTION) |
.spec.dashboard.icon = strenv(ICON_B64) |
.spec.dashboard.keysOrder = env(KEYS_ORDER)
' "$OUT"
echo "Updated $OUT"

View File

@@ -5,22 +5,18 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"sort"
"slices"
"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"
corev1 "k8s.io/api/core/v1"
"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"
@@ -37,49 +33,20 @@ type CozystackResourceDefinitionReconciler struct {
lastEvent time.Time
lastHandled time.Time
mem *crdmem.Memory
CozystackAPIKind string
}
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
crd := &cozyv1alpha1.CozystackResourceDefinition{}
err := r.Get(ctx, types.NamespacedName{Name: req.Name}, crd)
if err == nil {
if r.mem != nil {
r.mem.Upsert(crd)
}
r.mu.Lock()
r.lastEvent = time.Now()
r.mu.Unlock()
return ctrl.Result{}, nil
}
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
if apierrors.IsNotFound(err) && r.mem != nil {
r.mem.Delete(req.Name)
}
if req.Namespace == "cozy-system" && req.Name == "cozystack-api" {
return r.debouncedRestart(ctx, logger)
}
return ctrl.Result{}, nil
return r.debouncedRestart(ctx)
}
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
if r.Debounce == 0 {
r.Debounce = 5 * time.Second
}
if r.mem == nil {
r.mem = crdmem.Global()
}
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
return err
}
return ctrl.NewControllerManagedBy(mgr).
Named("cozystackresource-controller").
For(&cozyv1alpha1.CozystackResourceDefinition{}, builder.WithPredicates()).
Watches(
&cozyv1alpha1.CozystackResourceDefinition{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
@@ -102,17 +69,19 @@ type crdHashView struct {
Spec cozyv1alpha1.CozystackResourceDefinitionSpec `json:"spec"`
}
func (r *CozystackResourceDefinitionReconciler) computeConfigHash() (string, error) {
if r.mem == nil {
return "", nil
func (r *CozystackResourceDefinitionReconciler) computeConfigHash(ctx context.Context) (string, error) {
list := &cozyv1alpha1.CozystackResourceDefinitionList{}
if err := r.List(ctx, list); err != nil {
return "", err
}
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 {
slices.SortFunc(list.Items, sortCozyRDs)
views := make([]crdHashView, 0, len(list.Items))
for i := range list.Items {
views = append(views, crdHashView{
Name: snapshot[i].Name,
Spec: snapshot[i].Spec,
Name: list.Items[i].Name,
Spec: list.Items[i].Spec,
})
}
b, err := json.Marshal(views)
@@ -123,7 +92,9 @@ func (r *CozystackResourceDefinitionReconciler) computeConfigHash() (string, err
return hex.EncodeToString(sum[:]), nil
}
func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Context, logger logr.Logger) (ctrl.Result, error) {
func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Context) (ctrl.Result, error) {
logger := log.FromContext(ctx)
r.mu.Lock()
le := r.lastEvent
lh := r.lastHandled
@@ -143,20 +114,17 @@ func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Con
return ctrl.Result{}, nil
}
newHash, err := r.computeConfigHash()
newHash, err := r.computeConfigHash(ctx)
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 {
tpl, obj, patch, err := r.getWorkload(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"})
if 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"]
oldHash := tpl.Annotations["cozystack.io/config-hash"]
if oldHash == newHash && oldHash != "" {
r.mu.Lock()
@@ -166,10 +134,9 @@ func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Con
return ctrl.Result{}, nil
}
patch := client.MergeFrom(deploy.DeepCopy())
deploy.Spec.Template.Annotations["cozystack.io/config-hash"] = newHash
tpl.Annotations["cozystack.io/config-hash"] = newHash
if err := r.Patch(ctx, deploy, patch); err != nil {
if err := r.Patch(ctx, obj, patch); err != nil {
return ctrl.Result{}, err
}
@@ -181,3 +148,40 @@ func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Con
"old", oldHash, "new", newHash)
return ctrl.Result{}, nil
}
func (r *CozystackResourceDefinitionReconciler) getWorkload(
ctx context.Context,
key types.NamespacedName,
) (tpl *corev1.PodTemplateSpec, obj client.Object, patch client.Patch, err error) {
if r.CozystackAPIKind == "Deployment" {
dep := &appsv1.Deployment{}
if err := r.Get(ctx, key, dep); err != nil {
return nil, nil, nil, err
}
obj = dep
tpl = &dep.Spec.Template
patch = client.MergeFrom(dep.DeepCopy())
} else {
ds := &appsv1.DaemonSet{}
if err := r.Get(ctx, key, ds); err != nil {
return nil, nil, nil, err
}
obj = ds
tpl = &ds.Spec.Template
patch = client.MergeFrom(ds.DeepCopy())
}
if tpl.Annotations == nil {
tpl.Annotations = make(map[string]string)
}
return tpl, obj, patch, nil
}
func sortCozyRDs(a, b cozyv1alpha1.CozystackResourceDefinition) int {
if a.Name == b.Name {
return 0
}
if a.Name < b.Name {
return -1
}
return 1
}

View File

@@ -0,0 +1,80 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureBreadcrumb creates or updates a Breadcrumb resource for the given CRD
func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
group, version, kind := pickGVK(crd)
lowerKind := strings.ToLower(kind)
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
obj := &dashv1alpha1.Breadcrumb{}
obj.SetName(detailID)
plural := pickPlural(kind, crd)
// Prefer dashboard.Plural for UI label if provided
labelPlural := titleFromKindPlural(kind, plural)
if crd != nil && crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Plural != "" {
labelPlural = crd.Spec.Dashboard.Plural
}
key := plural // e.g., "virtualmachines"
label := labelPlural
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", strings.ToLower(group), strings.ToLower(version), plural)
// If this is a module, change the first breadcrumb item to "Tenant Modules"
if crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Module {
key = "tenantmodules"
label = "Tenant Modules"
link = "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
items := []any{
map[string]any{
"key": key,
"label": label,
"link": link,
},
map[string]any{
"key": strings.ToLower(kind), // "etcd"
"label": "{6}", // literal, as in your example
},
}
spec := map[string]any{
"id": detailID,
"breadcrumbItems": items,
}
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
}

View File

@@ -0,0 +1,150 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureCustomColumnsOverride creates or updates a CustomColumnsOverride that
// renders a header row with a colored badge and resource name link, plus a few
// useful columns (Ready, Created, Version).
//
// Naming convention mirrors your example:
//
// metadata.name: stock-namespace-<group>.<version>.<plural>
// spec.id: stock-namespace-/<group>/<version>/<plural>
func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (controllerutil.OperationResult, error) {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
// Details page segment uses lowercase kind, mirroring your example
detailsSegment := strings.ToLower(kind) + "-details"
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
id := fmt.Sprintf("stock-namespace-/%s/%s/%s", g, v, plural)
obj := &dashv1alpha1.CustomColumnsOverride{}
obj.SetName(name)
href := fmt.Sprintf("/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/%s/{reqsJsonPath[0]['.metadata.name']['-']}", detailsSegment)
if g == "apps.cozystack.io" && kind == "Tenant" && plural == "tenants" {
href = "/openapi-ui/{2}/{reqsJsonPath[0]['.status.namespace']['-']}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
desired := map[string]any{
"spec": map[string]any{
"id": id,
"additionalPrinterColumns": []any{
map[string]any{
"name": "Name",
"type": "factory",
"jsonPath": ".metadata.name",
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "header-row",
"align": "center",
"gap": 6,
},
"children": []any{
map[string]any{
"type": "ResourceBadge",
"data": map[string]any{
"id": "header-badge",
"value": kind,
// abbreviation auto-generated by ResourceBadge from value
},
},
map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": "name-link",
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
"href": href,
},
},
},
},
},
},
},
map[string]any{
"name": "Ready",
"type": "Boolean",
"jsonPath": `.status.conditions[?(@.type=="Ready")].status`,
},
map[string]any{
"name": "Created",
"type": "factory",
"jsonPath": ".metadata.creationTimestamp",
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "time-block",
"align": "center",
"gap": 6,
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "time-icon",
"text": "🌐",
},
},
map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": "time-value",
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
"formatter": "timestamp",
},
},
},
},
},
},
},
map[string]any{
"name": "Version",
"type": "string",
"jsonPath": ".status.version",
},
},
},
}
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(desired["spec"])
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
// Return OperationResultCreated/Updated is not available here with unstructured; we can mimic Updated when no error.
return controllerutil.OperationResultNone, err
}

View File

@@ -0,0 +1,75 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureCustomFormsOverride creates or updates a CustomFormsOverride resource for the given CRD
func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", g, v, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", g, v, plural)
obj := &dashv1alpha1.CustomFormsOverride{}
obj.SetName(name)
// Replicates your Helm includes (system metadata + api + status).
hidden := []any{}
hidden = append(hidden, hiddenMetadataSystem()...)
hidden = append(hidden, hiddenMetadataAPI()...)
hidden = append(hidden, hiddenStatus()...)
// If Name is set, hide metadata
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
hidden = append([]interface{}{
[]any{"metadata"},
}, hidden...)
}
var sort []any
if crd.Spec.Dashboard != nil && len(crd.Spec.Dashboard.KeysOrder) > 0 {
sort = make([]any, len(crd.Spec.Dashboard.KeysOrder))
for i, v := range crd.Spec.Dashboard.KeysOrder {
sort[i] = v
}
}
spec := map[string]any{
"customizationId": customizationID,
"hidden": hidden,
"sort": sort,
"schema": map[string]any{}, // {}
"strategy": "merge",
}
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
}

View File

@@ -0,0 +1,81 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// ensureCustomFormsPrefill creates or updates a CustomFormsPrefill resource for the given CRD
func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
app := crd.Spec.Application
group, version, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", group, version, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", group, version, plural)
values, err := buildPrefillValues(app.OpenAPISchema)
if err != nil {
return reconcile.Result{}, err
}
// Always prefill metadata.name (empty string if not specified in CRD)
var nameValue string
if crd.Spec.Dashboard != nil {
nameValue = strings.TrimSpace(crd.Spec.Dashboard.Name)
}
values = append([]interface{}{
map[string]interface{}{
"path": toIfaceSlice([]string{"metadata", "name"}),
"value": nameValue,
},
}, values...)
cfp := &dashv1alpha1.CustomFormsPrefill{}
cfp.Name = name // cluster-scoped
specMap := map[string]any{
"customizationId": customizationID,
"values": values,
}
// Use json.Marshal with sorted keys to ensure consistent output
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
_, err = controllerutil.CreateOrUpdate(ctx, m.Client, cfp, func() error {
if err := controllerutil.SetOwnerReference(crd, cfp, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(cfp, crd, ResourceTypeDynamic)
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
if !compareArbitrarySpecs(cfp.Spec, newSpec) {
cfp.Spec = newSpec
}
return nil
})
if err != nil {
return reconcile.Result{}, err
}
logger.Info("Applied CustomFormsPrefill", "name", cfp.Name)
return reconcile.Result{}, nil
}

View File

@@ -0,0 +1,515 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureFactory creates or updates a Factory resource for the given CRD
func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
lowerKind := strings.ToLower(kind)
factoryName := fmt.Sprintf("%s-details", lowerKind)
resourceFetch := fmt.Sprintf("/api/clusters/{2}/k8s/apis/%s/%s/namespaces/{3}/%s/{6}", g, v, plural)
flags := factoryFeatureFlags(crd)
var keysOrder [][]string
if crd.Spec.Dashboard != nil {
keysOrder = crd.Spec.Dashboard.KeysOrder
}
tabs := []any{
detailsTab(kind, resourceFetch, crd.Spec.Application.OpenAPISchema, keysOrder),
}
if flags.Workloads {
tabs = append(tabs, workloadsTab(kind))
}
if flags.Ingresses {
tabs = append(tabs, ingressesTab(kind))
}
if flags.Services {
tabs = append(tabs, servicesTab(kind))
}
if flags.Secrets {
tabs = append(tabs, secretsTab(kind))
}
tabs = append(tabs, yamlTab(plural))
// Use unified factory creation
config := UnifiedResourceConfig{
Name: factoryName,
ResourceType: "factory",
Kind: kind,
Plural: plural,
Title: strings.ToLower(plural),
}
spec := createUnifiedFactory(config, tabs, []any{resourceFetch})
obj := &dashv1alpha1.Factory{}
obj.SetName(factoryName)
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
}
// ---------------- Tabs builders ----------------
func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[string]any {
paramsBlocks := buildOpenAPIParamsBlocks(schemaJSON, keysOrder)
paramsList := map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "params-list",
"vertical": true,
"gap": float64(24),
},
"children": paramsBlocks,
}
leftColStack := []any{
antdText("details-title", true, kind, map[string]any{
"fontSize": float64(20),
"marginBottom": float64(12),
}),
antdFlexVertical("meta-name-block", 4, []any{
antdText("meta-name-label", true, "Name", nil),
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
}),
antdFlexVertical("meta-namespace-block", 8, []any{
antdText("meta-namespace-label", true, "Namespace", nil),
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "namespace-row",
"align": "center",
"gap": float64(6),
},
"children": []any{
createUnifiedBadgeFromKind("ns-badge", "Namespace"),
antdLink("namespace-link",
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
"/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
),
},
},
}),
antdFlexVertical("meta-created-block", 4, []any{
antdText("time-label", true, "Created", nil),
antdFlex("time-block", 6, []any{
antdText("time-icon", false, "🌐", nil),
parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"),
}),
}),
antdFlexVertical("meta-version-block", 4, []any{
antdText("version-label", true, "Version", nil),
parsedText("version-value", "{reqsJsonPath[0]['.status.version']['-']}", nil),
}),
antdFlexVertical("meta-released-block", 4, []any{
antdText("released-label", true, "Released", nil),
parsedText("released-value", "{reqsJsonPath[0]['.status.conditions[?(@.type==\"Released\")].status']['-']}", nil),
}),
antdFlexVertical("meta-ready-block", 4, []any{
antdText("ready-label", true, "Ready", nil),
parsedText("ready-value", "{reqsJsonPath[0]['.status.conditions[?(@.type==\"Ready\")].status']['-']}", nil),
}),
}
rightColStack := []any{
antdText("params-title", true, "Parameters", map[string]any{
"fontSize": float64(20),
"marginBottom": float64(12),
}),
paramsList,
}
return map[string]any{
"key": "details",
"label": "Details",
"children": []any{
contentCard("details-card", map[string]any{"marginBottom": float64(24)}, []any{
map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": "details-grid",
"gutter": []any{float64(48), float64(12)},
},
"children": []any{
map[string]any{
"type": "antdCol",
"data": map[string]any{"id": "col-left", "span": float64(12)},
"children": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{"id": "col-left-stack", "vertical": true, "gap": float64(24)},
"children": leftColStack,
},
},
},
map[string]any{
"type": "antdCol",
"data": map[string]any{"id": "col-right", "span": float64(12)},
"children": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{"id": "col-right-stack", "vertical": true, "gap": float64(24)},
"children": rightColStack,
},
},
},
},
},
spacer("conditions-top-spacer", float64(16)),
antdText("conditions-title", true, "Conditions", map[string]any{"fontSize": float64(20)}),
spacer("conditions-spacer", float64(8)),
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "conditions-table",
"fetchUrl": endpoint,
"clusterNamePartOfUrl": "{2}",
"customizationId": "factory-status-conditions",
"baseprefix": "/openapi-ui",
"withoutControls": true,
"pathToItems": []any{"status", "conditions"},
},
},
}),
},
}
}
func workloadsTab(kind string) map[string]any {
return map[string]any{
"key": "workloads",
"label": "Workloads",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "workloads-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.cozystack.io.workloadmonitors",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func servicesTab(kind string) map[string]any {
return map[string]any{
"key": "services",
"label": "Services",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "services-table",
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1.services",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
"internal.cozystack.io/tenantresource": "true",
},
},
},
},
}
}
func ingressesTab(kind string) map[string]any {
return map[string]any{
"key": "ingresses",
"label": "Ingresses",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "ingresses-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-networking.k8s.io.v1.ingresses",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
"internal.cozystack.io/tenantresource": "true",
},
},
},
},
}
}
func secretsTab(kind string) map[string]any {
return map[string]any{
"key": "secrets",
"label": "Secrets",
"children": []any{
map[string]any{
"type": "EnrichedTable",
"data": map[string]any{
"id": "secrets-table",
"fetchUrl": "/api/clusters/{2}/k8s/apis/core.cozystack.io/v1alpha1/namespaces/{3}/tenantsecretstables",
"clusterNamePartOfUrl": "{2}",
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.core.cozystack.io.tenantsecretstables",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
},
},
},
},
}
}
func yamlTab(plural string) map[string]any {
return map[string]any{
"key": "yaml",
"label": "YAML",
"children": []any{
map[string]any{
"type": "YamlEditorSingleton",
"data": map[string]any{
"id": "yaml-editor",
"cluster": "{2}",
"isNameSpaced": true,
"type": "builtin",
"typeName": plural,
"prefillValuesRequestIndex": float64(0),
"readOnly": true,
"substractHeight": float64(400),
},
},
},
}
}
// ---------------- OpenAPI → Right column ----------------
func buildOpenAPIParamsBlocks(schemaJSON string, keysOrder [][]string) []any {
var blocks []any
fields := collectOpenAPILeafFields(schemaJSON, 2, 20)
// Sort fields according to keysOrder if provided
if len(keysOrder) > 0 {
fields = sortFieldsByKeysOrder(fields, keysOrder)
}
for idx, f := range fields {
id := fmt.Sprintf("param-%d", idx)
blocks = append(blocks,
antdFlexVertical(id, 4, []any{
antdText(id+"-label", true, f.Label, nil),
parsedText(id+"-value", fmt.Sprintf("{reqsJsonPath[0]['.spec.%s']['-']}", f.JSONPathSpec), nil),
}),
)
}
if len(fields) == 0 {
blocks = append(blocks,
antdText("params-empty", false, "No scalar parameters detected in schema (see YAML tab for full spec).", map[string]any{"opacity": float64(0.7)}),
)
}
return blocks
}
// sortFieldsByKeysOrder sorts fields according to the provided keysOrder
func sortFieldsByKeysOrder(fields []fieldInfo, keysOrder [][]string) []fieldInfo {
// Create a map for quick lookup of field positions
orderMap := make(map[string]int)
for i, path := range keysOrder {
// Convert path to dot notation (e.g., ["spec", "systemDisk", "image"] -> "systemDisk.image")
if len(path) > 1 && path[0] == "spec" {
dotPath := strings.Join(path[1:], ".")
orderMap[dotPath] = i
}
}
// Sort fields based on their position in keysOrder
sort.Slice(fields, func(i, j int) bool {
posI, existsI := orderMap[fields[i].JSONPathSpec]
posJ, existsJ := orderMap[fields[j].JSONPathSpec]
// If both exist in orderMap, sort by position
if existsI && existsJ {
return posI < posJ
}
// If only one exists, prioritize the one that exists
if existsI {
return true
}
if existsJ {
return false
}
// If neither exists, maintain original order (stable sort)
return i < j
})
return fields
}
func collectOpenAPILeafFields(schemaJSON string, maxDepth, maxFields int) []fieldInfo {
type node = map[string]any
if strings.TrimSpace(schemaJSON) == "" {
return nil
}
var root any
if err := json.Unmarshal([]byte(schemaJSON), &root); err != nil {
// invalid JSON — skip
return nil
}
props := map[string]any{}
if m, ok := root.(node); ok {
if p, ok := m["properties"].(node); ok {
props = p
}
}
if len(props) == 0 {
return nil
}
var out []fieldInfo
var visit func(prefix []string, n node, depth int)
addField := func(path []string, schema node) {
// Skip excluded paths (backup/bootstrap/password)
if shouldExcludeParamPath(path) {
return
}
// build label "Foo Bar / Baz"
label := humanizePath(path)
desc := getString(schema, "description")
out = append(out, fieldInfo{
JSONPathSpec: strings.Join(path, "."),
Label: label,
Description: desc,
})
}
visit = func(prefix []string, n node, depth int) {
if len(out) >= maxFields {
return
}
// Scalar?
if isScalarType(n) || isIntOrString(n) || hasEnum(n) {
addField(prefix, n)
return
}
// Object with properties
if props, ok := n["properties"].(node); ok {
if depth >= maxDepth {
// too deep — stop
return
}
// deterministic ordering
keys := make([]string, 0, len(props))
for k := range props {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
child, _ := props[k].(node)
visit(append(prefix, k), child, depth+1)
if len(out) >= maxFields {
return
}
}
return
}
// Arrays: try to render item if it's scalar and depth limit allows
if n["type"] == "array" {
if items, ok := n["items"].(node); ok && (isScalarType(items) || isIntOrString(items) || hasEnum(items)) {
addField(prefix, items)
}
return
}
// Otherwise skip (unknown/complex)
}
// top-level: iterate properties
keys := make([]string, 0, len(props))
for k := range props {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
if child, ok := props[k].(node); ok {
visit([]string{k}, child, 1)
if len(out) >= maxFields {
break
}
}
}
return out
}
// ---------------- Feature flags ----------------
type factoryFlags struct {
Workloads bool
Ingresses bool
Services bool
Secrets bool
}
// factoryFeatureFlags tries several conventional locations so you can evolve the API
// without breaking the controller. Defaults are false (hidden).
func factoryFeatureFlags(crd *cozyv1alpha1.CozystackResourceDefinition) factoryFlags {
var f factoryFlags
f.Workloads = true
f.Ingresses = true
f.Services = true
f.Secrets = true
return f
}

View File

@@ -0,0 +1,442 @@
package dashboard
import (
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
)
// ---------------- Types used by OpenAPI parsing ----------------
type fieldInfo struct {
JSONPathSpec string // dotted path under .spec (e.g., "systemDisk.image")
Label string // "System Disk / Image" or "systemDisk.image"
Description string
}
// ---------------- Public entry: ensure Factory ------------------
// pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section,
// falling back to other likely fields if your schema differs.
func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) {
// Best guess based on your examples:
if crd.Spec.Application.Kind != "" {
kind = crd.Spec.Application.Kind
}
// For applications, always use apps.cozystack.io group, not the CRD's own group
group = "apps.cozystack.io"
version = "v1alpha1"
// Reasonable fallbacks if any are empty:
if kind == "" {
kind = "Resource"
}
return
}
// pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s".
func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string {
// If you have crd.Spec.Application.Plural, prefer it. Example:
if crd.Spec.Application.Plural != "" {
return crd.Spec.Application.Plural
}
// naive pluralization
k := strings.ToLower(kind)
if strings.HasSuffix(k, "s") {
return k
}
return k + "s"
}
// ----------------------- Helpers (OpenAPI → values) -----------------------
// defaultOrZero returns the schema default if present; otherwise a reasonable zero value.
func defaultOrZero(sub map[string]interface{}) interface{} {
if v, ok := sub["default"]; ok {
return v
}
typ, _ := sub["type"].(string)
switch typ {
case "string":
return ""
case "boolean":
return false
case "array":
return []interface{}{}
case "integer", "number":
return 0
case "object":
return map[string]interface{}{}
default:
return nil
}
}
// toIfaceSlice converts []string -> []interface{}.
func toIfaceSlice(ss []string) []interface{} {
out := make([]interface{}, len(ss))
for i, s := range ss {
out[i] = s
}
return out
}
// buildPrefillValues converts an OpenAPI schema (JSON string) into a []interface{} "values" list
// suitable for CustomFormsPrefill.spec.values.
// Rules:
// - For top-level primitive/array fields: emit an entry, using default if present, otherwise zero.
// - For top-level objects: recursively process nested objects and emit entries for all default values
// found at any nesting level.
func buildPrefillValues(openAPISchema string) ([]interface{}, error) {
var root map[string]interface{}
if err := json.Unmarshal([]byte(openAPISchema), &root); err != nil {
return nil, fmt.Errorf("cannot parse openAPISchema: %w", err)
}
props, _ := root["properties"].(map[string]interface{})
if props == nil {
return []interface{}{}, nil
}
var values []interface{}
processSchemaProperties(props, []string{"spec"}, &values, true)
return values, nil
}
// processSchemaProperties recursively processes OpenAPI schema properties and extracts default values
func processSchemaProperties(props map[string]interface{}, path []string, values *[]interface{}, topLevel bool) {
for pname, raw := range props {
sub, _ := raw.(map[string]interface{})
if sub == nil {
continue
}
typ, _ := sub["type"].(string)
currentPath := append(path, pname)
switch typ {
case "object":
// Check if this object has a default value
if objDefault, ok := sub["default"].(map[string]interface{}); ok {
// Process the default object recursively
processDefaultObject(objDefault, currentPath, values)
}
// Also process child properties for their individual defaults
if childProps, ok := sub["properties"].(map[string]interface{}); ok {
processSchemaProperties(childProps, currentPath, values, false)
}
default:
// For primitive types, use default if present, otherwise zero value
val := defaultOrZero(sub)
// Only emit zero-value entries when at top level
if val != nil || topLevel {
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": val,
}
*values = append(*values, entry)
}
}
}
}
// processDefaultObject recursively processes a default object and creates entries for all nested values
func processDefaultObject(obj map[string]interface{}, path []string, values *[]interface{}) {
for key, value := range obj {
currentPath := append(path, key)
// If the value is a map, process it recursively
if nestedObj, ok := value.(map[string]interface{}); ok {
processDefaultObject(nestedObj, currentPath, values)
} else {
// For primitive values, create an entry
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": value,
}
*values = append(*values, entry)
}
}
}
// normalizeJSON makes maps/slices JSON-safe for k8s Unstructured:
// - converts all int/int32/... to float64
// - leaves strings, bools, nil as-is
func normalizeJSON(v any) any {
switch t := v.(type) {
case map[string]any:
out := make(map[string]any, len(t))
for k, val := range t {
out[k] = normalizeJSON(val)
}
return out
case []any:
out := make([]any, len(t))
for i := range t {
out[i] = normalizeJSON(t[i])
}
return out
case int:
return float64(t)
case int8:
return float64(t)
case int16:
return float64(t)
case int32:
return float64(t)
case int64:
return float64(t)
case uint, uint8, uint16, uint32, uint64:
return float64(reflect.ValueOf(t).Convert(reflect.TypeOf(uint64(0))).Uint())
case float32:
return float64(t)
default:
return v
}
}
// --- helpers for schema inspection ---
func isScalarType(n map[string]any) bool {
switch getString(n, "type") {
case "string", "integer", "number", "boolean":
return true
default:
return false
}
}
func isIntOrString(n map[string]any) bool {
// Kubernetes extension: x-kubernetes-int-or-string: true
if v, ok := n["x-kubernetes-int-or-string"]; ok {
if b, ok := v.(bool); ok && b {
return true
}
}
// anyOf: integer|string
if anyOf, ok := n["anyOf"].([]any); ok {
hasInt := false
hasStr := false
for _, it := range anyOf {
if m, ok := it.(map[string]any); ok {
switch getString(m, "type") {
case "integer":
hasInt = true
case "string":
hasStr = true
}
}
}
return hasInt && hasStr
}
return false
}
func hasEnum(n map[string]any) bool {
_, ok := n["enum"]
return ok
}
func getString(n map[string]any, key string) string {
if v, ok := n[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// shouldExcludeParamPath returns true if any part of the path contains
// backup / bootstrap / password (case-insensitive)
func shouldExcludeParamPath(parts []string) bool {
for _, p := range parts {
lp := strings.ToLower(p)
if strings.Contains(lp, "backup") || strings.Contains(lp, "bootstrap") || strings.Contains(lp, "password") || strings.Contains(lp, "cloudinit") {
return true
}
}
return false
}
func humanizePath(parts []string) string {
// "systemDisk.image" -> "System Disk / Image"
return strings.Join(parts, " / ")
}
// titleFromKindPlural returns a presentable plural label, e.g.:
// kind="VirtualMachine", plural="virtualmachines" => "VirtualMachines"
func titleFromKindPlural(kind, plural string) string {
return kind + "s"
}
// The hidden lists below mirror the Helm templates you shared.
// Each entry is a path as nested string array, e.g. ["metadata","creationTimestamp"].
func hiddenMetadataSystem() []any {
return []any{
[]any{"metadata", "annotations"},
[]any{"metadata", "labels"},
[]any{"metadata", "namespace"},
[]any{"metadata", "creationTimestamp"},
[]any{"metadata", "deletionGracePeriodSeconds"},
[]any{"metadata", "deletionTimestamp"},
[]any{"metadata", "finalizers"},
[]any{"metadata", "generateName"},
[]any{"metadata", "generation"},
[]any{"metadata", "managedFields"},
[]any{"metadata", "ownerReferences"},
[]any{"metadata", "resourceVersion"},
[]any{"metadata", "selfLink"},
[]any{"metadata", "uid"},
}
}
func hiddenMetadataAPI() []any {
return []any{
[]any{"kind"},
[]any{"apiVersion"},
[]any{"appVersion"},
}
}
func hiddenStatus() []any {
return []any{
[]any{"status"},
}
}
// compareArbitrarySpecs compares two ArbitrarySpec objects by comparing their JSON content
func compareArbitrarySpecs(spec1, spec2 dashv1alpha1.ArbitrarySpec) bool {
// If both are empty, they're equal
if len(spec1.JSON.Raw) == 0 && len(spec2.JSON.Raw) == 0 {
return true
}
// If one is empty and the other is not, they're different
if len(spec1.JSON.Raw) == 0 || len(spec2.JSON.Raw) == 0 {
return false
}
// Parse and normalize both specs
norm1, err := normalizeJSONForComparison(spec1.JSON.Raw)
if err != nil {
return false
}
norm2, err := normalizeJSONForComparison(spec2.JSON.Raw)
if err != nil {
return false
}
// Compare normalized JSON
equal := string(norm1) == string(norm2)
return equal
}
// normalizeJSONForComparison normalizes JSON by sorting arrays and objects
func normalizeJSONForComparison(data []byte) ([]byte, error) {
var obj interface{}
if err := json.Unmarshal(data, &obj); err != nil {
return nil, err
}
// Recursively normalize the object
normalized := normalizeObject(obj)
// Re-marshal to get normalized JSON
return json.Marshal(normalized)
}
// normalizeObject recursively normalizes objects and arrays
func normalizeObject(obj interface{}) interface{} {
switch v := obj.(type) {
case map[string]interface{}:
// For maps, we don't need to sort keys as json.Marshal handles that
result := make(map[string]interface{})
for k, val := range v {
result[k] = normalizeObject(val)
}
return result
case []interface{}:
// For arrays, we need to sort them if they contain objects with comparable fields
if len(v) == 0 {
return v
}
// Check if this is an array of objects that can be sorted
if canSortArray(v) {
// Sort the array
sorted := make([]interface{}, len(v))
copy(sorted, v)
sortArray(sorted)
return sorted
}
// If we can't sort, just normalize each element
result := make([]interface{}, len(v))
for i, val := range v {
result[i] = normalizeObject(val)
}
return result
default:
return v
}
}
// canSortArray checks if an array can be sorted (contains objects with comparable fields)
func canSortArray(arr []interface{}) bool {
if len(arr) == 0 {
return false
}
// Check if all elements are objects
for _, item := range arr {
if _, ok := item.(map[string]interface{}); !ok {
return false
}
}
// Check if objects have comparable fields (like "path" for CustomFormsPrefill values)
firstObj, ok := arr[0].(map[string]interface{})
if !ok {
return false
}
// Look for "path" field which is used in CustomFormsPrefill values
if _, hasPath := firstObj["path"]; hasPath {
return true
}
return false
}
// sortArray sorts an array of objects by their "path" field
func sortArray(arr []interface{}) {
sort.Slice(arr, func(i, j int) bool {
objI, okI := arr[i].(map[string]interface{})
objJ, okJ := arr[j].(map[string]interface{})
if !okI || !okJ {
return false
}
pathI, hasPathI := objI["path"]
pathJ, hasPathJ := objJ["path"]
if !hasPathI || !hasPathJ {
return false
}
// Convert paths to strings for comparison
pathIStr := fmt.Sprintf("%v", pathI)
pathJStr := fmt.Sprintf("%v", pathJ)
return pathIStr < pathJStr
})
}

View File

@@ -0,0 +1,457 @@
package dashboard
import (
"context"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
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/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
// Label keys for dashboard resource management
LabelManagedBy = "dashboard.cozystack.io/managed-by"
LabelResourceType = "dashboard.cozystack.io/resource-type"
LabelCRDName = "dashboard.cozystack.io/crd-name"
LabelCRDGroup = "dashboard.cozystack.io/crd-group"
LabelCRDVersion = "dashboard.cozystack.io/crd-version"
LabelCRDKind = "dashboard.cozystack.io/crd-kind"
LabelCRDPlural = "dashboard.cozystack.io/crd-plural"
// Label values
ManagedByValue = "cozystack-dashboard-controller"
ResourceTypeStatic = "static"
ResourceTypeDynamic = "dynamic"
)
// AddToScheme exposes dashboard types registration for controller setup.
func AddToScheme(s *runtime.Scheme) error {
return dashv1alpha1.AddToScheme(s)
}
// Manager owns logic for creating/updating dashboard resources derived from CRDs.
// Its easy to extend: add new ensure* methods and wire them into EnsureForCRD.
type Manager struct {
client.Client
Scheme *runtime.Scheme
}
// NewManager constructs a dashboard Manager.
func NewManager(c client.Client, scheme *runtime.Scheme) *Manager {
m := &Manager{Client: c, Scheme: scheme}
return m
}
func (m *Manager) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Named("dashboard-reconciler").
For(&cozyv1alpha1.CozystackResourceDefinition{}).
Complete(m)
}
func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
l := log.FromContext(ctx)
crd := &cozyv1alpha1.CozystackResourceDefinition{}
err := m.Get(ctx, types.NamespacedName{Name: req.Name}, crd)
if err != nil {
if apierrors.IsNotFound(err) {
if err := m.CleanupOrphanedResources(ctx); err != nil {
l.Error(err, "Failed to cleanup orphaned dashboard resources")
}
return ctrl.Result{}, nil // no point in requeuing here
}
return ctrl.Result{}, err
}
return m.EnsureForCRD(ctx, crd)
}
// EnsureForCRD is the single entry-point used by the controller.
// Add more ensure* calls here as you implement support for other resources:
//
// - ensureBreadcrumb (implemented)
// - ensureCustomColumnsOverride (implemented)
// - ensureCustomFormsOverride (implemented)
// - ensureCustomFormsPrefill (implemented)
// - ensureFactory
// - ensureMarketplacePanel (implemented)
// - ensureSidebar (implemented)
// - ensureTableUriMapping (implemented)
func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
// Early return if crd.Spec.Dashboard is nil to prevent oscillation
if crd.Spec.Dashboard == nil {
return reconcile.Result{}, nil
}
// MarketplacePanel
if res, err := m.ensureMarketplacePanel(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomFormsPrefill
if res, err := m.ensureCustomFormsPrefill(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomColumnsOverride
if _, err := m.ensureCustomColumnsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureTableUriMapping(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureBreadcrumb(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureCustomFormsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureSidebar(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureFactory(ctx, crd); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
// InitializeStaticResources creates all static dashboard resources once during controller startup
func (m *Manager) InitializeStaticResources(ctx context.Context) error {
return m.ensureStaticResources(ctx)
}
// addDashboardLabels adds standard dashboard management labels to a resource
func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.CozystackResourceDefinition, resourceType string) {
labels := obj.GetLabels()
if labels == nil {
labels = make(map[string]string)
}
labels[LabelManagedBy] = ManagedByValue
labels[LabelResourceType] = resourceType
if crd != nil {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
labels[LabelCRDName] = crd.Name
labels[LabelCRDGroup] = g
labels[LabelCRDVersion] = v
labels[LabelCRDKind] = kind
labels[LabelCRDPlural] = plural
}
obj.SetLabels(labels)
}
// getDashboardResourceSelector returns a label selector for dashboard-managed resources
func (m *Manager) getDashboardResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
}
}
// getDynamicResourceSelector returns a label selector for dynamic dashboard resources
func (m *Manager) getDynamicResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
LabelResourceType: ResourceTypeDynamic,
}
}
// getStaticResourceSelector returns a label selector for static dashboard resources
func (m *Manager) getStaticResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
LabelResourceType: ResourceTypeStatic,
}
}
// CleanupOrphanedResources removes dashboard resources that are no longer needed
// This should be called after cache warming to ensure all current resources are known
func (m *Manager) CleanupOrphanedResources(ctx context.Context) error {
var crdList cozyv1alpha1.CozystackResourceDefinitionList
if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil {
return err
}
allCRDs := crdList.Items
// Build a set of expected resource names for each type
expectedResources := m.buildExpectedResourceSet(allCRDs)
// Clean up each resource type
resourceTypes := []client.Object{
&dashv1alpha1.CustomColumnsOverride{},
&dashv1alpha1.CustomFormsOverride{},
&dashv1alpha1.CustomFormsPrefill{},
&dashv1alpha1.MarketplacePanel{},
&dashv1alpha1.Sidebar{},
&dashv1alpha1.TableUriMapping{},
&dashv1alpha1.Breadcrumb{},
&dashv1alpha1.Factory{},
}
for _, resourceType := range resourceTypes {
if err := m.cleanupResourceType(ctx, resourceType, expectedResources); err != nil {
return err
}
}
return nil
}
// buildExpectedResourceSet creates a map of expected resource names by type
func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResourceDefinition) map[string]map[string]bool {
expected := make(map[string]map[string]bool)
// Initialize maps for each resource type
resourceTypes := []string{
"CustomColumnsOverride",
"CustomFormsOverride",
"CustomFormsPrefill",
"MarketplacePanel",
"Sidebar",
"TableUriMapping",
"Breadcrumb",
"Factory",
}
for _, rt := range resourceTypes {
expected[rt] = make(map[string]bool)
}
// Add static resources (these should always exist)
staticResources := CreateAllStaticResources()
for _, resource := range staticResources {
resourceType := resource.GetObjectKind().GroupVersionKind().Kind
if expected[resourceType] != nil {
expected[resourceType][resource.GetName()] = true
}
}
// Add dynamic resources based on current CRDs
for _, crd := range crds {
if crd.Spec.Dashboard == nil {
continue
}
// Note: We include ALL resources with dashboard config, regardless of module flag
// because ensureFactory and ensureBreadcrumb create resources for all CRDs with dashboard config
g, v, kind := pickGVK(&crd)
plural := pickPlural(kind, &crd)
// CustomColumnsOverride - created for ALL CRDs with dashboard config
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
expected["CustomColumnsOverride"][name] = true
// CustomFormsOverride - created for ALL CRDs with dashboard config
name = fmt.Sprintf("%s.%s.%s", g, v, plural)
expected["CustomFormsOverride"][name] = true
// CustomFormsPrefill - created for ALL CRDs with dashboard config
expected["CustomFormsPrefill"][name] = true
// MarketplacePanel - only created for non-module CRDs
if !crd.Spec.Dashboard.Module {
expected["MarketplacePanel"][crd.Name] = true
}
// Sidebar resources - created for ALL CRDs with dashboard config
lowerKind := strings.ToLower(kind)
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
expected["Sidebar"][detailsID] = true
// Add other stock sidebars that are created for each CRD
stockSidebars := []string{
"stock-instance-api-form",
"stock-instance-api-table",
"stock-instance-builtin-form",
"stock-instance-builtin-table",
"stock-project-factory-marketplace",
"stock-project-factory-workloadmonitor-details",
"stock-project-api-form",
"stock-project-api-table",
"stock-project-builtin-form",
"stock-project-builtin-table",
"stock-project-crd-form",
"stock-project-crd-table",
}
for _, sidebarID := range stockSidebars {
expected["Sidebar"][sidebarID] = true
}
// TableUriMapping - created for ALL CRDs with dashboard config
name = fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
expected["TableUriMapping"][name] = true
// Breadcrumb - created for ALL CRDs with dashboard config
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
expected["Breadcrumb"][detailID] = true
// Factory - created for ALL CRDs with dashboard config
factoryName := fmt.Sprintf("%s-details", lowerKind)
expected["Factory"][factoryName] = true
}
return expected
}
// cleanupResourceType removes orphaned resources of a specific type
func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.Object, expectedResources map[string]map[string]bool) error {
var (
list client.ObjectList
resourceKind string
)
switch resourceType.(type) {
case *dashv1alpha1.CustomColumnsOverride:
list = &dashv1alpha1.CustomColumnsOverrideList{}
resourceKind = "CustomColumnsOverride"
case *dashv1alpha1.CustomFormsOverride:
list = &dashv1alpha1.CustomFormsOverrideList{}
resourceKind = "CustomFormsOverride"
case *dashv1alpha1.CustomFormsPrefill:
list = &dashv1alpha1.CustomFormsPrefillList{}
resourceKind = "CustomFormsPrefill"
case *dashv1alpha1.MarketplacePanel:
list = &dashv1alpha1.MarketplacePanelList{}
resourceKind = "MarketplacePanel"
case *dashv1alpha1.Sidebar:
list = &dashv1alpha1.SidebarList{}
resourceKind = "Sidebar"
case *dashv1alpha1.TableUriMapping:
list = &dashv1alpha1.TableUriMappingList{}
resourceKind = "TableUriMapping"
case *dashv1alpha1.Breadcrumb:
list = &dashv1alpha1.BreadcrumbList{}
resourceKind = "Breadcrumb"
case *dashv1alpha1.Factory:
list = &dashv1alpha1.FactoryList{}
resourceKind = "Factory"
default:
return nil // Unknown type
}
expected := expectedResources[resourceKind]
if expected == nil {
return nil // No expected resources for this type
}
// List with dashboard labels
if err := m.List(ctx, list, m.getDashboardResourceSelector()); err != nil {
return err
}
// Delete resources that are not in the expected set
switch l := list.(type) {
case *dashv1alpha1.CustomColumnsOverrideList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.CustomFormsOverrideList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.CustomFormsPrefillList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.MarketplacePanelList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.SidebarList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.TableUriMappingList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.BreadcrumbList:
for _, item := range l.Items {
if !expected[item.Name] {
logger := log.FromContext(ctx)
logger.Info("Deleting orphaned Breadcrumb resource", "name", item.Name)
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
}
}
}
case *dashv1alpha1.FactoryList:
for _, item := range l.Items {
if !expected[item.Name] {
logger := log.FromContext(ctx)
logger.Info("Deleting orphaned Factory resource", "name", item.Name)
if err := m.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
}
}
}
}
return nil
}

View File

@@ -0,0 +1,111 @@
package dashboard
import (
"context"
"encoding/json"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// ensureMarketplacePanel creates or updates a MarketplacePanel resource for the given CRD
func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
mp := &dashv1alpha1.MarketplacePanel{}
mp.Name = crd.Name // cluster-scoped resource, name mirrors CRD name
// If dashboard is not set, delete the panel if it exists.
if crd.Spec.Dashboard == nil {
err := m.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because dashboard is not set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Skip module and tenant resources (they don't need MarketplacePanel)
if crd.Spec.Dashboard.Module || crd.Spec.Application.Kind == "Tenant" {
err := m.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because resource is a module", "name", mp.Name)
return reconcile.Result{}, nil
}
// Build desired spec from CRD fields
d := crd.Spec.Dashboard
app := crd.Spec.Application
displayName := d.Singular
if displayName == "" {
displayName = app.Kind
}
tags := make([]any, len(d.Tags))
for i, t := range d.Tags {
tags[i] = t
}
specMap := map[string]any{
"description": d.Description,
"name": displayName,
"type": "nonCrd",
"apiGroup": "apps.cozystack.io",
"apiVersion": "v1alpha1",
"typeName": app.Plural, // e.g., "buckets"
"disabled": false,
"hidden": false,
"tags": tags,
"icon": d.Icon,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
_, err = controllerutil.CreateOrUpdate(ctx, m.Client, mp, func() error {
if err := controllerutil.SetOwnerReference(crd, mp, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(mp, crd, ResourceTypeDynamic)
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
if !compareArbitrarySpecs(mp.Spec, newSpec) {
mp.Spec = newSpec
}
return nil
})
if err != nil {
return reconcile.Result{}, err
}
logger.Info("Applied MarketplacePanel", "name", mp.Name)
return reconcile.Result{}, nil
}

View File

@@ -0,0 +1,360 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureSidebar creates/updates multiple Sidebar resources that share the same menu:
// - The "details" sidebar tied to the current kind (stock-project-factory-<kind>-details)
// - The stock-instance sidebars: api-form, api-table, builtin-form, builtin-table
// - The stock-project sidebars: api-form, api-table, builtin-form, builtin-table, crd-form, crd-table
//
// Menu rules:
// - The first section is "Marketplace" with two hardcoded entries:
// - Marketplace (/openapi-ui/{clusterName}/{namespace}/factory/marketplace)
// - Tenant Info (/openapi-ui/{clusterName}/{namespace}/factory/info-details/info)
// - All other sections are built from CRDs where spec.dashboard != nil.
// - Categories are ordered strictly as:
// Marketplace, IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration
// - Items within each category: sort by Weight (desc), then Label (A→Z).
func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Build the full menu once.
// 1) Fetch all CRDs
var all []cozyv1alpha1.CozystackResourceDefinition
var crdList cozyv1alpha1.CozystackResourceDefinitionList
if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil {
return err
}
all = crdList.Items
// 2) Build category -> []item map (only for CRDs with spec.dashboard != nil)
type item struct {
Key string
Label string
Link string
Weight int
}
categories := map[string][]item{} // category label -> children
keysAndTags := map[string]any{} // plural -> []string{ "<lower(kind)>-sidebar" }
// Collect sidebar names for module resources
var moduleSidebars []any
for i := range all {
def := &all[i]
// Include ONLY when spec.dashboard != nil
if def.Spec.Dashboard == nil {
continue
}
g, v, kind := pickGVK(def)
plural := pickPlural(kind, def)
lowerKind := strings.ToLower(kind)
// Check if this resource is a module
if def.Spec.Dashboard.Module {
// Special case: info should have its own keysAndTags, not be in modules
if lowerKind == "info" {
keysAndTags[plural] = []any{fmt.Sprintf("%s-sidebar", lowerKind)}
} else {
// Add to modules sidebar list
moduleSidebars = append(moduleSidebars, fmt.Sprintf("%s-sidebar", lowerKind))
}
} else {
// Add to keysAndTags for non-module resources
keysAndTags[plural] = []any{fmt.Sprintf("%s-sidebar", lowerKind)}
}
// Only add to menu categories if not a module
if !def.Spec.Dashboard.Module {
cat := safeCategory(def) // falls back to "Resources" if empty
// Label: prefer dashboard.Plural if provided
label := titleFromKindPlural(kind, plural)
if def.Spec.Dashboard.Plural != "" {
label = def.Spec.Dashboard.Plural
}
// Weight (default 0)
weight := def.Spec.Dashboard.Weight
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", g, v, plural)
categories[cat] = append(categories[cat], item{
Key: plural,
Label: label,
Link: link,
Weight: weight,
})
}
}
// Add modules to keysAndTags if we have any module sidebars
if len(moduleSidebars) > 0 {
keysAndTags["modules"] = moduleSidebars
}
// Add sidebars for built-in Kubernetes resources
keysAndTags["services"] = []any{"service-sidebar"}
keysAndTags["secrets"] = []any{"secret-sidebar"}
keysAndTags["ingresses"] = []any{"ingress-sidebar"}
// 3) Sort items within each category by Weight (desc), then Label (A→Z)
for cat := range categories {
sort.Slice(categories[cat], func(i, j int) bool {
if categories[cat][i].Weight != categories[cat][j].Weight {
return categories[cat][i].Weight < categories[cat][j].Weight // lower weight first
}
return strings.ToLower(categories[cat][i].Label) < strings.ToLower(categories[cat][j].Label)
})
}
// 4) Order categories strictly:
// Marketplace (hardcoded), IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration
orderedCats := orderCategoryLabels(categories)
// 5) Build menuItems (hardcode "Marketplace"; then dynamic categories; then hardcode "Administration")
menuItems := []any{
map[string]any{
"key": "marketplace",
"label": "Marketplace",
"children": []any{
map[string]any{
"key": "marketplace",
"label": "Marketplace",
"link": "/openapi-ui/{clusterName}/{namespace}/factory/marketplace",
},
},
},
}
for _, cat := range orderedCats {
// Skip "Marketplace" and "Administration" here since they're hardcoded
if strings.EqualFold(cat, "Marketplace") || strings.EqualFold(cat, "Administration") {
continue
}
children := []any{}
for _, it := range categories[cat] {
children = append(children, map[string]any{
"key": it.Key,
"label": it.Label,
"link": it.Link,
})
}
if len(children) > 0 {
menuItems = append(menuItems, map[string]any{
"key": slugify(cat),
"label": cat,
"children": children,
})
}
}
// Add hardcoded Administration section
menuItems = append(menuItems, map[string]any{
"key": "administration",
"label": "Administration",
"children": []any{
map[string]any{
"key": "info",
"label": "Info",
"link": "/openapi-ui/{clusterName}/{namespace}/factory/info-details/info",
},
map[string]any{
"key": "modules",
"label": "Modules",
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules",
},
map[string]any{
"key": "tenants",
"label": "Tenants",
"link": "/openapi-ui/{clusterName}/{namespace}/api-table/apps.cozystack.io/v1alpha1/tenants",
},
},
})
// 6) Prepare the list of Sidebar IDs to upsert with the SAME content
// Create sidebars for ALL CRDs with dashboard config
targetIDs := []string{
// stock-instance sidebars
"stock-instance-api-form",
"stock-instance-api-table",
"stock-instance-builtin-form",
"stock-instance-builtin-table",
// stock-project sidebars
"stock-project-factory-marketplace",
"stock-project-factory-workloadmonitor-details",
"stock-project-factory-kube-service-details",
"stock-project-factory-kube-secret-details",
"stock-project-factory-kube-ingress-details",
"stock-project-api-form",
"stock-project-api-table",
"stock-project-builtin-form",
"stock-project-builtin-table",
"stock-project-crd-form",
"stock-project-crd-table",
}
// Add details sidebars for all CRDs with dashboard config
for i := range all {
def := &all[i]
if def.Spec.Dashboard == nil {
continue
}
_, _, kind := pickGVK(def)
lowerKind := strings.ToLower(kind)
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
targetIDs = append(targetIDs, detailsID)
}
// 7) Upsert all target sidebars with identical menuItems and keysAndTags
return m.upsertMultipleSidebars(ctx, crd, targetIDs, keysAndTags, menuItems)
}
// upsertMultipleSidebars creates/updates several Sidebar resources with the same menu spec.
func (m *Manager) upsertMultipleSidebars(
ctx context.Context,
crd *cozyv1alpha1.CozystackResourceDefinition,
ids []string,
keysAndTags map[string]any,
menuItems []any,
) error {
for _, id := range ids {
spec := map[string]any{
"id": id,
"keysAndTags": keysAndTags,
"menuItems": menuItems,
}
obj := &dashv1alpha1.Sidebar{}
obj.SetName(id)
if _, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
// Only set owner reference for dynamic sidebars (stock-project-factory-{kind}-details)
// Static sidebars (stock-instance-*, stock-project-*) should not have owner references
if strings.HasPrefix(id, "stock-project-factory-") && strings.HasSuffix(id, "-details") {
// This is a dynamic sidebar, set owner reference only if it matches the current CRD
_, _, kind := pickGVK(crd)
lowerKind := strings.ToLower(kind)
expectedID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
if id == expectedID {
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
} else {
// This is a different CRD's sidebar, don't modify owner references or labels
// Just update the spec
}
} else {
// This is a static sidebar, don't set owner references
// Add static labels
labels := obj.GetLabels()
if labels == nil {
labels = make(map[string]string)
}
labels[LabelManagedBy] = ManagedByValue
labels[LabelResourceType] = ResourceTypeStatic
obj.SetLabels(labels)
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
}); err != nil {
return err
}
}
return nil
}
// orderCategoryLabels returns category labels ordered strictly as:
//
// Marketplace, IaaS, PaaS, NaaS, <others A→Z>, Resources, Administration.
//
// It only returns labels that exist in `cats` (except "Marketplace" which is hardcoded by caller).
func orderCategoryLabels[T any](cats map[string][]T) []string {
if len(cats) == 0 {
return []string{"Marketplace", "IaaS", "PaaS", "NaaS", "Resources", "Administration"}
}
head := []string{"Marketplace", "IaaS", "PaaS", "NaaS"}
tail := []string{"Resources", "Administration"}
present := make(map[string]struct{}, len(cats))
for k := range cats {
present[k] = struct{}{}
}
var result []string
// Add head anchors (keep "Marketplace" in the order signature for the caller)
for _, h := range head {
result = append(result, h)
delete(present, h)
}
// Collect "others": exclude tail
var others []string
for k := range present {
if k == "Resources" || k == "Administration" {
continue
}
others = append(others, k)
}
sort.Slice(others, func(i, j int) bool { return strings.ToLower(others[i]) < strings.ToLower(others[j]) })
// Append others, then tail (always in fixed order)
result = append(result, others...)
result = append(result, tail...)
return result
}
// safeCategory returns spec.dashboard.category or "Resources" if not set.
func safeCategory(def *cozyv1alpha1.CozystackResourceDefinition) string {
if def == nil || def.Spec.Dashboard == nil {
return "Resources"
}
if def.Spec.Dashboard.Category != "" {
return def.Spec.Dashboard.Category
}
return "Resources"
}
// slugify converts a category label to a key-friendly identifier.
// "User Management" -> "usermanagement", "PaaS" -> "paas".
func slugify(s string) string {
s = strings.TrimSpace(strings.ToLower(s))
out := make([]byte, 0, len(s))
for i := 0; i < len(s); i++ {
c := s[i]
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') {
out = append(out, c)
}
}
return string(out)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
package dashboard
import (
"context"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureStaticResources ensures all static dashboard resources are created
func (m *Manager) ensureStaticResources(ctx context.Context) error {
// Use refactored resources from static_refactored.go
// This replaces the old static variables with dynamic creation using helper functions
staticResources := CreateAllStaticResources()
// Create or update each static resource
for _, resource := range staticResources {
if err := m.ensureStaticResource(ctx, resource); err != nil {
return err
}
}
return nil
}
// ensureStaticResource creates or updates a single static resource
func (m *Manager) ensureStaticResource(ctx context.Context, obj client.Object) error {
// Create a copy to avoid modifying the original
resource := obj.DeepCopyObject().(client.Object)
// Add dashboard labels to static resources
m.addDashboardLabels(resource, nil, ResourceTypeStatic)
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, resource, func() error {
// For static resources, we don't need to set owner references
// as they are meant to be persistent across CRD changes
// Copy Spec from the original object to the live object
switch o := obj.(type) {
case *dashv1alpha1.CustomColumnsOverride:
resource.(*dashv1alpha1.CustomColumnsOverride).Spec = o.Spec
case *dashv1alpha1.Breadcrumb:
resource.(*dashv1alpha1.Breadcrumb).Spec = o.Spec
case *dashv1alpha1.CustomFormsOverride:
resource.(*dashv1alpha1.CustomFormsOverride).Spec = o.Spec
case *dashv1alpha1.Factory:
resource.(*dashv1alpha1.Factory).Spec = o.Spec
case *dashv1alpha1.Navigation:
resource.(*dashv1alpha1.Navigation).Spec = o.Spec
case *dashv1alpha1.TableUriMapping:
resource.(*dashv1alpha1.TableUriMapping).Spec = o.Spec
}
// Ensure labels are always set
m.addDashboardLabels(resource, nil, ResourceTypeStatic)
return nil
})
return err
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package dashboard
import (
"context"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
)
// ensureTableUriMapping creates or updates a TableUriMapping resource for the given CRD
func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Links are fully managed by the CustomColumnsOverride.
return nil
}

View File

@@ -0,0 +1,207 @@
package dashboard
// ---------------- UI helpers (use float64 for numeric fields) ----------------
func contentCard(id string, style map[string]any, children []any) map[string]any {
return contentCardWithTitle(id, "", style, children)
}
func contentCardWithTitle(id any, title string, style map[string]any, children []any) map[string]any {
data := map[string]any{
"id": id,
"style": style,
}
if title != "" {
data["title"] = title
}
return map[string]any{
"type": "ContentCard",
"data": data,
"children": children,
}
}
func antdText(id string, strong bool, text string, style map[string]any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "antd")
}
data := map[string]any{
"id": id,
"text": text,
"strong": strong,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "antdText", "data": data}
}
func parsedText(id, text string, style map[string]any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "parsed")
}
data := map[string]any{
"id": id,
"text": text,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "parsedText", "data": data}
}
func parsedTextWithFormatter(id, text, formatter string) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "formatted")
}
return map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": id,
"text": text,
"formatter": formatter,
},
}
}
func spacer(id string, space float64) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "spacer")
}
return map[string]any{
"type": "Spacer",
"data": map[string]any{
"id": id,
"$space": space,
},
}
}
func antdFlex(id string, gap float64, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "flex")
}
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"align": "center",
"gap": gap,
},
"children": children,
}
}
func antdFlexVertical(id string, gap float64, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "flex-vertical")
}
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"vertical": true,
"gap": gap,
},
"children": children,
}
}
func antdRow(id string, gutter []any, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "row")
}
return map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": id,
"gutter": gutter,
},
"children": children,
}
}
func antdCol(id string, span float64, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"span": span,
},
"children": children,
}
}
func antdColWithStyle(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdLink(id, text, href string) map[string]any {
return map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": id,
"text": text,
"href": href,
},
}
}
// ---------------- Badge helpers ----------------
// createBadge creates a badge element with the given text, color, and title
func createBadge(id, text, color, title string) map[string]any {
return map[string]any{
"type": "antdText",
"data": map[string]any{
"id": id,
"text": text,
"title": title,
"style": map[string]any{
"whiteSpace": "nowrap",
"backgroundColor": color,
"fontWeight": 400,
"lineHeight": "24px",
"minWidth": 24,
"textAlign": "center",
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": "15px",
"padding": "0 9px",
},
},
}
}
// createBadgeFromKind creates a badge using the existing badge generation functions
func createBadgeFromKind(id, kind, title string) map[string]any {
return createUnifiedBadgeFromKind(id, kind)
}
// createHeaderBadge creates a badge specifically for headers with consistent styling
func createHeaderBadge(id, kind, plural string) map[string]any {
return createUnifiedBadgeFromKind(id, kind)
}

View File

@@ -0,0 +1,335 @@
package dashboard
import (
"crypto/sha1"
"fmt"
"strings"
)
// ---------------- Unified ID generation helpers ----------------
// generateID creates a unique ID based on the provided components
func generateID(components ...string) string {
if len(components) == 0 {
return ""
}
// Join components with hyphens and convert to lowercase
id := strings.ToLower(strings.Join(components, "-"))
// Remove any special characters that might cause issues
id = strings.ReplaceAll(id, ".", "-")
id = strings.ReplaceAll(id, "/", "-")
id = strings.ReplaceAll(id, " ", "-")
// Remove multiple consecutive hyphens
for strings.Contains(id, "--") {
id = strings.ReplaceAll(id, "--", "-")
}
// Remove leading/trailing hyphens
id = strings.Trim(id, "-")
return id
}
// generateSpecID creates a spec.id from metadata.name and other components
func generateSpecID(metadataName string, components ...string) string {
allComponents := append([]string{metadataName}, components...)
return generateID(allComponents...)
}
// generateMetadataName creates metadata.name from spec.id
func generateMetadataName(specID string) string {
// Convert ID format to metadata.name format
// Replace / with . for metadata.name
name := strings.ReplaceAll(specID, "/", ".")
// Clean up the name to be RFC 1123 compliant
// Remove any leading/trailing dots and ensure it starts/ends with alphanumeric
name = strings.Trim(name, ".")
// Replace multiple consecutive dots with single dot
for strings.Contains(name, "..") {
name = strings.ReplaceAll(name, "..", ".")
}
// Replace any remaining problematic patterns
// Handle cases like "stock-namespace-.v1" -> "stock-namespace-v1"
name = strings.ReplaceAll(name, "-.", "-")
name = strings.ReplaceAll(name, ".-", "-")
// Ensure it starts with alphanumeric character
if len(name) > 0 && !isAlphanumeric(name[0]) {
name = "a" + name
}
// Ensure it ends with alphanumeric character
if len(name) > 0 && !isAlphanumeric(name[len(name)-1]) {
name = name + "a"
}
return name
}
// isAlphanumeric checks if a character is alphanumeric
func isAlphanumeric(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
}
// ---------------- Unified badge generation helpers ----------------
// BadgeConfig holds configuration for badge generation
type BadgeConfig struct {
Kind string // Resource kind in PascalCase (e.g., "VirtualMachine") - used for value and auto-generation
Text string // Optional abbreviation override (if empty, ResourceBadge auto-generates from Kind)
Color string // Optional custom backgroundColor override
}
// createUnifiedBadge creates a badge using the unified BadgeConfig with ResourceBadge component
func createUnifiedBadge(id string, config BadgeConfig) map[string]any {
data := map[string]any{
"id": id,
"value": config.Kind,
}
// Add abbreviation override if specified (otherwise ResourceBadge auto-generates from Kind)
if config.Text != "" {
data["abbreviation"] = config.Text
}
// Add custom color if specified
if config.Color != "" {
data["style"] = map[string]any{
"backgroundColor": config.Color,
}
}
return map[string]any{
"type": "ResourceBadge",
"data": data,
}
}
// createUnifiedBadgeFromKind creates a badge from kind with ResourceBadge component
// Abbreviation is auto-generated by ResourceBadge from kind, but can be customized if needed
func createUnifiedBadgeFromKind(id, kind string) map[string]any {
return map[string]any{
"type": "ResourceBadge",
"data": map[string]any{
"id": id,
"value": kind,
// abbreviation is optional - ResourceBadge auto-generates from value
},
}
}
// ---------------- Resource creation helpers with unified approach ----------------
// ResourceConfig holds configuration for resource creation
type ResourceConfig struct {
SpecID string
MetadataName string
Kind string
Title string
BadgeConfig BadgeConfig
}
// createResourceConfig creates a ResourceConfig from components
func createResourceConfig(components []string, kind, title string) ResourceConfig {
// Generate spec.id from components
specID := generateID(components...)
// Generate metadata.name from spec.id
metadataName := generateMetadataName(specID)
// Generate badge config
badgeConfig := BadgeConfig{
Kind: kind,
}
return ResourceConfig{
SpecID: specID,
MetadataName: metadataName,
Kind: kind,
Title: title,
BadgeConfig: badgeConfig,
}
}
// ---------------- Enhanced color generation ----------------
// ---------------- Automatic ID generation for UI elements ----------------
// generateElementID creates an ID for UI elements based on context and type
func generateElementID(elementType, context string, components ...string) string {
allComponents := append([]string{elementType, context}, components...)
return generateID(allComponents...)
}
// generateBadgeID creates an ID for badge elements
func generateBadgeID(context string, kind string) string {
return generateElementID("badge", context, kind)
}
// generateLinkID creates an ID for link elements
func generateLinkID(context string, linkType string) string {
return generateElementID("link", context, linkType)
}
// generateTextID creates an ID for text elements
func generateTextID(context string, textType string) string {
return generateElementID("text", context, textType)
}
// generateContainerID creates an ID for container elements
func generateContainerID(context string, containerType string) string {
return generateElementID("container", context, containerType)
}
// generateTableID creates an ID for table elements
func generateTableID(context string, tableType string) string {
return generateElementID("table", context, tableType)
}
// ---------------- Enhanced resource creation with automatic IDs ----------------
// createResourceWithAutoID creates a resource with automatically generated IDs
func createResourceWithAutoID(resourceType, name string, spec map[string]any) map[string]any {
// Generate spec.id from name
specID := generateSpecID(name)
// Add the spec.id to the spec
spec["id"] = specID
return spec
}
// ---------------- Unified resource creation helpers ----------------
// UnifiedResourceConfig holds configuration for unified resource creation
type UnifiedResourceConfig struct {
Name string
ResourceType string
Kind string
Plural string
Title string
Color string
BadgeText string
}
// createUnifiedFactory creates a factory using unified approach
func createUnifiedFactory(config UnifiedResourceConfig, tabs []any, urlsToFetch []any) map[string]any {
// Generate spec.id from name
specID := generateSpecID(config.Name)
// Create header with unified badge
badgeConfig := BadgeConfig{
Kind: config.Kind,
Text: config.BadgeText,
Color: config.Color,
}
badge := createUnifiedBadge(generateBadgeID("header", config.Kind), badgeConfig)
nameText := parsedText(generateTextID("header", "name"), "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"lineHeight": "24px",
})
header := antdFlex(generateContainerID("header", "row"), float64(6), []any{
badge,
nameText,
})
// Add marginBottom style to header
if headerData, ok := header["data"].(map[string]any); ok {
if headerData["style"] == nil {
headerData["style"] = map[string]any{}
}
if style, ok := headerData["style"].(map[string]any); ok {
style["marginBottom"] = float64(24)
}
}
return map[string]any{
"key": config.Name,
"id": specID,
"sidebarTags": []any{fmt.Sprintf("%s-sidebar", strings.ToLower(config.Kind))},
"withScrollableMainContentCard": true,
"urlsToFetch": urlsToFetch,
"data": []any{
header,
map[string]any{
"type": "antdTabs",
"data": map[string]any{
"id": generateContainerID("tabs", strings.ToLower(config.Kind)),
"defaultActiveKey": "details",
"items": tabs,
},
},
},
}
}
// createUnifiedCustomColumn creates a custom column using unified approach
func createUnifiedCustomColumn(name, jsonPath, kind, title, href string) map[string]any {
badgeConfig := BadgeConfig{
Kind: kind,
}
badge := createUnifiedBadge(generateBadgeID("column", kind), badgeConfig)
linkID := generateLinkID("column", "name")
if jsonPath == ".metadata.namespace" {
linkID = generateLinkID("column", "namespace")
}
link := antdLink(linkID, "{reqsJsonPath[0]['"+jsonPath+"']['-']}", href)
return map[string]any{
"name": name,
"type": "factory",
"jsonPath": jsonPath,
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": generateContainerID("column", "header"),
"align": "center",
"gap": float64(6),
},
"children": []any{badge, link},
},
},
},
}
}
// ---------------- Utility functions ----------------
// hashString creates a short hash from a string for ID generation
func hashString(s string) string {
hash := sha1.Sum([]byte(s))
return fmt.Sprintf("%x", hash[:4])
}
// sanitizeForID removes characters that shouldn't be in IDs
func sanitizeForID(s string) string {
// Replace problematic characters
s = strings.ReplaceAll(s, ".", "-")
s = strings.ReplaceAll(s, "/", "-")
s = strings.ReplaceAll(s, " ", "-")
s = strings.ReplaceAll(s, "_", "-")
// Remove multiple consecutive hyphens
for strings.Contains(s, "--") {
s = strings.ReplaceAll(s, "--", "-")
}
// Remove leading/trailing hyphens
s = strings.Trim(s, "-")
return strings.ToLower(s)
}

View File

@@ -1,367 +0,0 @@
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,439 @@
package controller
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"text/template"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// WorkloadMonitorFromCRDReconciler reconciles HelmReleases and creates WorkloadMonitors
// based on CozystackResourceDefinition templates
type WorkloadMonitorFromCRDReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=get;list;watch
// +kubebuilder:rbac:groups=cozystack.io,resources=workloadmonitors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch
const (
WorkloadMonitorOwnerLabel = "workloadmonitor.cozystack.io/owned-by-crd"
WorkloadMonitorSourceLabel = "workloadmonitor.cozystack.io/helm-release"
)
// Reconcile processes HelmRelease resources and creates corresponding WorkloadMonitors
func (r *WorkloadMonitorFromCRDReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// Get the HelmRelease
hr := &helmv2.HelmRelease{}
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
if errors.IsNotFound(err) {
// HelmRelease deleted - cleanup will be handled by owner references
return ctrl.Result{}, nil
}
logger.Error(err, "unable to fetch HelmRelease")
return ctrl.Result{}, err
}
// Skip system HelmReleases
if strings.HasPrefix(hr.Name, "tenant-") {
return ctrl.Result{}, nil
}
// Find the matching CozystackResourceDefinition
crd, err := r.findCRDForHelmRelease(ctx, hr)
if err != nil {
if errors.IsNotFound(err) {
// No CRD found for this HelmRelease - skip
logger.V(1).Info("No CozystackResourceDefinition found for HelmRelease", "name", hr.Name)
return ctrl.Result{}, nil
}
logger.Error(err, "unable to find CozystackResourceDefinition")
return ctrl.Result{}, err
}
// If CRD doesn't have WorkloadMonitors, cleanup any existing ones we created
if len(crd.Spec.WorkloadMonitors) == 0 {
if err := r.cleanupWorkloadMonitors(ctx, hr); err != nil {
logger.Error(err, "failed to cleanup WorkloadMonitors")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// Get the HelmRelease values for template rendering
values, err := r.getHelmReleaseValues(ctx, hr)
if err != nil {
logger.Error(err, "unable to get HelmRelease values")
return ctrl.Result{}, err
}
// Create/update WorkloadMonitors based on templates
if err := r.reconcileWorkloadMonitors(ctx, hr, crd, values); err != nil {
logger.Error(err, "failed to reconcile WorkloadMonitors")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// findCRDForHelmRelease finds the CozystackResourceDefinition for a given HelmRelease
func (r *WorkloadMonitorFromCRDReconciler) findCRDForHelmRelease(ctx context.Context, hr *helmv2.HelmRelease) (*cozyv1alpha1.CozystackResourceDefinition, error) {
// List all CozystackResourceDefinitions
var crdList cozyv1alpha1.CozystackResourceDefinitionList
if err := r.List(ctx, &crdList); err != nil {
return nil, err
}
// Match by chart name and prefix
for i := range crdList.Items {
crd := &crdList.Items[i]
if crd.Spec.Release.Chart.Name == hr.Spec.Chart.Spec.Chart {
// Check if HelmRelease name matches the prefix
if strings.HasPrefix(hr.Name, crd.Spec.Release.Prefix) {
return crd, nil
}
}
}
return nil, errors.NewNotFound(schema.GroupResource{Group: "cozystack.io", Resource: "cozystackresourcedefinitions"}, "")
}
// getHelmReleaseValues extracts the values from HelmRelease spec
func (r *WorkloadMonitorFromCRDReconciler) getHelmReleaseValues(ctx context.Context, hr *helmv2.HelmRelease) (map[string]interface{}, error) {
if hr.Spec.Values == nil {
return make(map[string]interface{}), nil
}
// Convert apiextensionsv1.JSON to map
values := make(map[string]interface{})
if err := json.Unmarshal(hr.Spec.Values.Raw, &values); err != nil {
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
}
return values, nil
}
// reconcileWorkloadMonitors creates or updates WorkloadMonitors based on CRD templates
func (r *WorkloadMonitorFromCRDReconciler) reconcileWorkloadMonitors(
ctx context.Context,
hr *helmv2.HelmRelease,
crd *cozyv1alpha1.CozystackResourceDefinition,
values map[string]interface{},
) error {
logger := log.FromContext(ctx)
// Get chart version from HelmRelease
chartVersion := ""
if hr.Status.History != nil && len(hr.Status.History) > 0 {
chartVersion = hr.Status.History[0].ChartVersion
}
// Template context
templateData := map[string]interface{}{
"Release": map[string]interface{}{
"Name": hr.Name,
"Namespace": hr.Namespace,
},
"Chart": map[string]interface{}{
"Version": chartVersion,
},
"Values": values,
}
// Track which monitors we should have
expectedMonitors := make(map[string]bool)
// Process each WorkloadMonitor template
for _, tmpl := range crd.Spec.WorkloadMonitors {
// Check condition
if tmpl.Condition != "" {
shouldCreate, err := evaluateCondition(tmpl.Condition, templateData)
if err != nil {
logger.Error(err, "failed to evaluate condition", "template", tmpl.Name, "condition", tmpl.Condition)
continue
}
if !shouldCreate {
logger.V(1).Info("Skipping WorkloadMonitor due to condition", "template", tmpl.Name)
continue
}
}
// Render monitor name
monitorName, err := renderTemplate(tmpl.Name, templateData)
if err != nil {
logger.Error(err, "failed to render monitor name", "template", tmpl.Name)
continue
}
expectedMonitors[monitorName] = true
// Render selector values
selector := make(map[string]string)
for key, valueTmpl := range tmpl.Selector {
renderedValue, err := renderTemplate(valueTmpl, templateData)
if err != nil {
logger.Error(err, "failed to render selector value", "key", key, "template", valueTmpl)
continue
}
selector[key] = renderedValue
}
// Render replicas
var replicas *int32
if tmpl.Replicas != "" {
replicasStr, err := renderTemplate(tmpl.Replicas, templateData)
if err != nil {
logger.Error(err, "failed to render replicas", "template", tmpl.Replicas)
} else {
if replicasInt, err := strconv.ParseInt(replicasStr, 10, 32); err == nil {
replicas = pointer.Int32(int32(replicasInt))
}
}
}
// Render minReplicas
var minReplicas *int32
if tmpl.MinReplicas != "" {
minReplicasStr, err := renderTemplate(tmpl.MinReplicas, templateData)
if err != nil {
logger.Error(err, "failed to render minReplicas", "template", tmpl.MinReplicas)
} else {
if minReplicasInt, err := strconv.ParseInt(minReplicasStr, 10, 32); err == nil {
minReplicas = pointer.Int32(int32(minReplicasInt))
}
}
}
// Create or update WorkloadMonitor
monitor := &cozyv1alpha1.WorkloadMonitor{
ObjectMeta: metav1.ObjectMeta{
Name: monitorName,
Namespace: hr.Namespace,
},
}
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, monitor, func() error {
// Set labels
if monitor.Labels == nil {
monitor.Labels = make(map[string]string)
}
monitor.Labels[WorkloadMonitorOwnerLabel] = "true"
monitor.Labels[WorkloadMonitorSourceLabel] = hr.Name
// Set owner reference to HelmRelease for automatic cleanup
if err := controllerutil.SetControllerReference(hr, monitor, r.Scheme); err != nil {
return err
}
// Update spec
monitor.Spec.Selector = selector
monitor.Spec.Kind = tmpl.Kind
monitor.Spec.Type = tmpl.Type
monitor.Spec.Version = chartVersion
monitor.Spec.Replicas = replicas
monitor.Spec.MinReplicas = minReplicas
return nil
})
if err != nil {
logger.Error(err, "failed to create/update WorkloadMonitor", "name", monitorName)
continue
}
logger.V(1).Info("WorkloadMonitor reconciled", "name", monitorName)
}
// Cleanup WorkloadMonitors that are no longer in templates
if err := r.cleanupUnexpectedMonitors(ctx, hr, expectedMonitors); err != nil {
logger.Error(err, "failed to cleanup unexpected WorkloadMonitors")
return err
}
return nil
}
// cleanupWorkloadMonitors removes all WorkloadMonitors created for a HelmRelease
func (r *WorkloadMonitorFromCRDReconciler) cleanupWorkloadMonitors(ctx context.Context, hr *helmv2.HelmRelease) error {
return r.cleanupUnexpectedMonitors(ctx, hr, make(map[string]bool))
}
// cleanupUnexpectedMonitors removes WorkloadMonitors that are no longer expected
func (r *WorkloadMonitorFromCRDReconciler) cleanupUnexpectedMonitors(
ctx context.Context,
hr *helmv2.HelmRelease,
expectedMonitors map[string]bool,
) error {
logger := log.FromContext(ctx)
// List all WorkloadMonitors in the namespace that we created
var monitorList cozyv1alpha1.WorkloadMonitorList
labelSelector := labels.SelectorFromSet(labels.Set{
WorkloadMonitorOwnerLabel: "true",
WorkloadMonitorSourceLabel: hr.Name,
})
if err := r.List(ctx, &monitorList,
client.InNamespace(hr.Namespace),
client.MatchingLabelsSelector{Selector: labelSelector},
); err != nil {
return err
}
// Delete monitors that are not expected
for i := range monitorList.Items {
monitor := &monitorList.Items[i]
if !expectedMonitors[monitor.Name] {
logger.Info("Deleting unexpected WorkloadMonitor", "name", monitor.Name)
if err := r.Delete(ctx, monitor); err != nil && !errors.IsNotFound(err) {
logger.Error(err, "failed to delete WorkloadMonitor", "name", monitor.Name)
}
}
}
return nil
}
// renderTemplate renders a Go template string with the given data
func renderTemplate(tmplStr string, data interface{}) (string, error) {
// Check if it's already a simple value (no template markers)
if !strings.Contains(tmplStr, "{{") {
return tmplStr, nil
}
// Add Sprig functions for compatibility with Helm templates
tmpl, err := template.New("").Funcs(getTemplateFuncs()).Parse(tmplStr)
if err != nil {
return "", fmt.Errorf("failed to parse template: %w", err)
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return "", fmt.Errorf("failed to execute template: %w", err)
}
return strings.TrimSpace(buf.String()), nil
}
// evaluateCondition evaluates a template condition (should return "true" or non-empty for true)
func evaluateCondition(condition string, data interface{}) (bool, error) {
result, err := renderTemplate(condition, data)
if err != nil {
return false, err
}
// Check for truthy values
result = strings.TrimSpace(strings.ToLower(result))
return result == "true" || result == "1" || result == "yes", nil
}
// getTemplateFuncs returns template functions compatible with Helm
func getTemplateFuncs() template.FuncMap {
return template.FuncMap{
// Math functions
"add": func(a, b int) int { return a + b },
"sub": func(a, b int) int { return a - b },
"mul": func(a, b int) int { return a * b },
"div": func(a, b int) int {
if b == 0 {
return 0
}
return a / b
},
"add1": func(a int) int { return a + 1 },
"sub1": func(a int) int { return a - 1 },
// String functions
"upper": strings.ToUpper,
"lower": strings.ToLower,
"trim": strings.TrimSpace,
"trimAll": func(cutset, s string) string { return strings.Trim(s, cutset) },
"replace": func(old, new string, n int, s string) string { return strings.Replace(s, old, new, n) },
// Logic functions
"default": func(defaultVal, val interface{}) interface{} {
if val == nil || val == "" {
return defaultVal
}
return val
},
"empty": func(val interface{}) bool {
return val == nil || val == ""
},
"not": func(val bool) bool {
return !val
},
}
}
// SetupWithManager sets up the controller with the Manager
func (r *WorkloadMonitorFromCRDReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Named("workloadmonitor-from-crd-controller").
For(&helmv2.HelmRelease{}).
Owns(&cozyv1alpha1.WorkloadMonitor{}).
Watches(
&cozyv1alpha1.CozystackResourceDefinition{},
handler.EnqueueRequestsFromMapFunc(r.mapCRDToHelmReleases),
).
Complete(r)
}
// mapCRDToHelmReleases maps CRD changes to HelmRelease reconcile requests
func (r *WorkloadMonitorFromCRDReconciler) mapCRDToHelmReleases(ctx context.Context, obj client.Object) []reconcile.Request {
crd, ok := obj.(*cozyv1alpha1.CozystackResourceDefinition)
if !ok {
return nil
}
// List all HelmReleases
var hrList helmv2.HelmReleaseList
if err := r.List(ctx, &hrList); err != nil {
return nil
}
var requests []reconcile.Request
for i := range hrList.Items {
hr := &hrList.Items[i]
// Skip tenant HelmReleases
if strings.HasPrefix(hr.Name, "tenant-") {
continue
}
// Match by chart name and prefix
if crd.Spec.Release.Chart.Name == hr.Spec.Chart.Spec.Chart {
if strings.HasPrefix(hr.Name, crd.Spec.Release.Prefix) {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: hr.Name,
Namespace: hr.Namespace,
},
})
}
}
}
return requests
}

View File

@@ -0,0 +1,50 @@
package lineagecontrollerwebhook
import (
"fmt"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
helmv2 "github.com/fluxcd/helm-controller/api/v2"
)
type chartRef struct {
repo string
chart string
}
type appRef struct {
group string
kind string
}
type runtimeConfig struct {
chartAppMap map[chartRef]*cozyv1alpha1.CozystackResourceDefinition
appCRDMap map[appRef]*cozyv1alpha1.CozystackResourceDefinition
}
func (l *LineageControllerWebhook) initConfig() {
l.initOnce.Do(func() {
if l.config.Load() == nil {
l.config.Store(&runtimeConfig{
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
})
}
})
}
func (l *LineageControllerWebhook) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
cfg, ok := l.config.Load().(*runtimeConfig)
if !ok {
return "", "", "", fmt.Errorf("failed to load chart-app mapping from config")
}
if hr.Spec.Chart == nil {
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
}
s := hr.Spec.Chart.Spec
val, ok := cfg.chartAppMap[chartRef{s.SourceRef.Name, s.Chart}]
if !ok {
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
}
return "apps.cozystack.io/v1alpha1", val.Spec.Application.Kind, val.Spec.Release.Prefix, nil
}

View File

@@ -0,0 +1,54 @@
package lineagecontrollerwebhook
import (
"context"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch;get
func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&cozyv1alpha1.CozystackResourceDefinition{}).
Complete(c)
}
func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
l := log.FromContext(ctx)
crds := &cozyv1alpha1.CozystackResourceDefinitionList{}
if err := c.List(ctx, crds); err != nil {
l.Error(err, "failed reading CozystackResourceDefinitions")
return ctrl.Result{}, err
}
cfg := &runtimeConfig{
chartAppMap: make(map[chartRef]*cozyv1alpha1.CozystackResourceDefinition),
appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition),
}
for _, crd := range crds.Items {
chRef := chartRef{
crd.Spec.Release.Chart.SourceRef.Name,
crd.Spec.Release.Chart.Name,
}
appRef := appRef{
"apps.cozystack.io",
crd.Spec.Application.Kind,
}
newRef := crd
if _, exists := cfg.chartAppMap[chRef]; exists {
l.Info("duplicate chart mapping detected; ignoring subsequent entry", "key", chRef)
} else {
cfg.chartAppMap[chRef] = &newRef
}
if _, exists := cfg.appCRDMap[appRef]; exists {
l.Info("duplicate app mapping detected; ignoring subsequent entry", "key", appRef)
} else {
cfg.appCRDMap[appRef] = &newRef
}
}
c.config.Store(cfg)
return ctrl.Result{}, nil
}

View File

@@ -0,0 +1,73 @@
package lineagecontrollerwebhook
import (
"bytes"
"context"
"text/template"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// matchName checks if the provided name matches any of the resource names in the array.
// Each entry in resourceNames is treated as a Go template that gets rendered using the passed context.
// A nil resourceNames array matches any string.
func matchName(ctx context.Context, name string, templateContext map[string]string, resourceNames []string) bool {
if resourceNames == nil {
return true
}
logger := log.FromContext(ctx)
for _, templateStr := range resourceNames {
tmpl, err := template.New("resourceName").Parse(templateStr)
if err != nil {
logger.Error(err, "failed to parse resource name template", "template", templateStr)
continue
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, templateContext)
if err != nil {
logger.Error(err, "failed to execute resource name template", "template", templateStr, "context", templateContext)
continue
}
if buf.String() == name {
return true
}
}
return false
}
func matchResourceToSelector(ctx context.Context, name string, templateContext, l map[string]string, s *cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool {
sel, err := metav1.LabelSelectorAsSelector(&s.LabelSelector)
if err != nil {
log.FromContext(ctx).Error(err, "failed to convert label selector to selector")
return false
}
labelMatches := sel.Matches(labels.Set(l))
nameMatches := matchName(ctx, name, templateContext, s.ResourceNames)
return labelMatches && nameMatches
}
func matchResourceToSelectorArray(ctx context.Context, name string, templateContext, l map[string]string, ss []*cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool {
for _, s := range ss {
if matchResourceToSelector(ctx, name, templateContext, l, s) {
return true
}
}
return false
}
func matchResourceToExcludeInclude(ctx context.Context, name string, templateContext, l map[string]string, resources *cozyv1alpha1.CozystackResourceDefinitionResources) bool {
if resources == nil {
return false
}
if matchResourceToSelectorArray(ctx, name, templateContext, l, resources.Exclude) {
return false
}
return matchResourceToSelectorArray(ctx, name, templateContext, l, resources.Include)
}

View File

@@ -0,0 +1,23 @@
package lineagecontrollerwebhook
import (
"sync"
"sync/atomic"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
// +kubebuilder:webhook:path=/mutate-lineage,mutating=true,failurePolicy=Fail,sideEffects=None,groups="",resources=pods,secrets,services,persistentvolumeclaims,verbs=create;update,versions=v1,name=mlineage.cozystack.io,admissionReviewVersions={v1}
type LineageControllerWebhook struct {
client.Client
Scheme *runtime.Scheme
decoder admission.Decoder
dynClient dynamic.Interface
mapper meta.RESTMapper
config atomic.Value
initOnce sync.Once
}

View File

@@ -0,0 +1,208 @@
package lineagecontrollerwebhook
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/cozystack/cozystack/pkg/lineage"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
)
var (
NoAncestors = fmt.Errorf("no managed apps found in lineage")
AncestryAmbiguous = fmt.Errorf("object ancestry is ambiguous")
)
const (
ManagedObjectKey = "internal.cozystack.io/managed-by-cozystack"
ManagerGroupKey = "apps.cozystack.io/application.group"
ManagerKindKey = "apps.cozystack.io/application.kind"
ManagerNameKey = "apps.cozystack.io/application.name"
)
// getResourceSelectors returns the appropriate CozystackResourceDefinitionResources for a given GroupKind
func (h *LineageControllerWebhook) getResourceSelectors(gk schema.GroupKind, crd *cozyv1alpha1.CozystackResourceDefinition) *cozyv1alpha1.CozystackResourceDefinitionResources {
switch {
case gk.Group == "" && gk.Kind == "Secret":
return &crd.Spec.Secrets
case gk.Group == "" && gk.Kind == "Service":
return &crd.Spec.Services
case gk.Group == "networking.k8s.io" && gk.Kind == "Ingress":
return &crd.Spec.Ingresses
default:
return nil
}
}
// SetupWithManager registers the handler with the webhook server.
func (h *LineageControllerWebhook) SetupWithManagerAsWebhook(mgr ctrl.Manager) error {
cfg := rest.CopyConfig(mgr.GetConfig())
var err error
h.dynClient, err = dynamic.NewForConfig(cfg)
if err != nil {
return err
}
httpClient, err := rest.HTTPClientFor(cfg)
if err != nil {
return err
}
h.mapper, err = apiutil.NewDynamicRESTMapper(cfg, httpClient)
if err != nil {
return err
}
h.initConfig()
// Register HTTP path -> handler.
mgr.GetWebhookServer().Register("/mutate-lineage", &admission.Webhook{Handler: h})
return nil
}
// InjectDecoder lets controller-runtime give us a decoder for AdmissionReview requests.
func (h *LineageControllerWebhook) InjectDecoder(d admission.Decoder) error {
h.decoder = d
return nil
}
// Handle is called for each AdmissionReview that matches the webhook config.
func (h *LineageControllerWebhook) Handle(ctx context.Context, req admission.Request) admission.Response {
logger := log.FromContext(ctx).WithValues(
"gvk", req.Kind.String(),
"namespace", req.Namespace,
"name", req.Name,
"operation", req.Operation,
)
warn := make(admission.Warnings, 0)
obj := &unstructured.Unstructured{}
if err := h.decodeUnstructured(req, obj); err != nil {
return admission.Errored(400, fmt.Errorf("decode object: %w", err))
}
labels, err := h.computeLabels(ctx, obj)
for {
if err != nil && errors.Is(err, NoAncestors) {
break // not a problem, mark object as unmanaged
}
if err != nil && errors.Is(err, AncestryAmbiguous) {
warn = append(warn, "object ancestry ambiguous, using first ancestor found")
break
}
if err != nil {
logger.Error(err, "error computing lineage labels")
return admission.Errored(500, fmt.Errorf("error computing lineage labels: %w", err))
}
if err == nil {
break
}
}
h.applyLabels(obj, labels)
mutated, err := json.Marshal(obj)
if err != nil {
return admission.Errored(500, fmt.Errorf("marshal mutated pod: %w", err))
}
logger.V(1).Info("mutated pod", "namespace", obj.GetNamespace(), "name", obj.GetName())
return admission.PatchResponseFromRaw(req.Object.Raw, mutated).WithWarnings(warn...)
}
func (h *LineageControllerWebhook) computeLabels(ctx context.Context, o *unstructured.Unstructured) (map[string]string, error) {
owners := lineage.WalkOwnershipGraph(ctx, h.dynClient, h.mapper, h, o)
if len(owners) == 0 {
return map[string]string{ManagedObjectKey: "false"}, NoAncestors
}
obj, err := owners[0].GetUnstructured(ctx, h.dynClient, h.mapper)
if err != nil {
return nil, err
}
gv, err := schema.ParseGroupVersion(obj.GetAPIVersion())
if err != nil {
// should never happen, we got an APIVersion right from the API
return nil, fmt.Errorf("could not parse APIVersion %s to a group and version: %w", obj.GetAPIVersion(), err)
}
if len(owners) > 1 {
err = AncestryAmbiguous
}
labels := map[string]string{
// truncate apigroup to first 63 chars
ManagedObjectKey: "true",
ManagerGroupKey: func(s string) string {
if len(s) < 63 {
return s
}
s = s[:63]
for b := s[62]; !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9')); s = s[:len(s)-1] {
b = s[len(s)-1]
}
return s
}(gv.Group),
ManagerKindKey: obj.GetKind(),
ManagerNameKey: obj.GetName(),
}
templateLabels := map[string]string{
"kind": strings.ToLower(obj.GetKind()),
"name": obj.GetName(),
"namespace": o.GetNamespace(),
}
cfg := h.config.Load().(*runtimeConfig)
crd := cfg.appCRDMap[appRef{gv.Group, obj.GetKind()}]
resourceSelectors := h.getResourceSelectors(o.GroupVersionKind().GroupKind(), crd)
labels[corev1alpha1.TenantResourceLabelKey] = func(b bool) string {
if b {
return corev1alpha1.TenantResourceLabelValue
}
return "false"
}(matchResourceToExcludeInclude(ctx, o.GetName(), templateLabels, o.GetLabels(), resourceSelectors))
return labels, err
}
func (h *LineageControllerWebhook) applyLabels(o *unstructured.Unstructured, labels map[string]string) {
existing := o.GetLabels()
if existing == nil {
existing = make(map[string]string)
}
for k, v := range labels {
existing[k] = v
}
o.SetLabels(existing)
}
func (h *LineageControllerWebhook) decodeUnstructured(req admission.Request, out *unstructured.Unstructured) error {
if h.decoder != nil {
if err := h.decoder.Decode(req, out); err == nil {
return nil
}
if req.Kind.Group != "" || req.Kind.Kind != "" || req.Kind.Version != "" {
out.SetGroupVersionKind(schema.GroupVersionKind{
Group: req.Kind.Group,
Version: req.Kind.Version,
Kind: req.Kind.Kind,
})
if err := h.decoder.Decode(req, out); err == nil {
return nil
}
}
}
if len(req.Object.Raw) == 0 {
return errors.New("empty admission object")
}
return json.Unmarshal(req.Object.Raw, &out.Object)
}

View File

@@ -1,14 +1,12 @@
OUT=../_out/repos/apps
TMP := $(shell mktemp -d)
OUT=../../_out/repos/apps
CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}')
include ../../scripts/common-envs.mk
repo:
cd .. && ../hack/package_chart.sh apps $(OUT) $(TMP) library
rm -rf "$(OUT)"
helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION)
helm repo index "$(OUT)"
fix-chartnames:
find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i "s/^name: .*/name: $$i/" "$$i/Chart.yaml"; done
gen-versions-map: fix-chartnames
../../hack/gen_versions_map.sh
check-version-map: gen-versions-map
git diff --exit-code -- versions_map
fix-charts:
find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}' | while read i; do sed -i -e "s/^name: .*/name: $$i/" -e "s/^version: .*/version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process/g" "$$i/Chart.yaml"; done

View File

@@ -2,24 +2,6 @@ apiVersion: v2
name: bucket
description: S3 compatible storage
icon: /logos/bucket.svg
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
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.2.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.
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
appVersion: "0.2.0"

View File

@@ -2,4 +2,5 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
yq -o json -i '.properties = {}' values.schema.json
yq -o json -i '.properties = {}' values.schema.json
../../../hack/update-crd.sh

View File

@@ -12,7 +12,14 @@ spec:
name: cozystack-system
namespace: cozy-system
version: '>= 0.0.0-0'
interval: 1m0s
timeout: 5m0s
interval: 5m
timeout: 10m
install:
remediation:
retries: -1
upgrade:
force: true
remediation:
retries: -1
values:
bucketName: {{ .Release.Name }}

View File

@@ -2,24 +2,6 @@ apiVersion: v2
name: clickhouse
description: Managed ClickHouse service
icon: /logos/clickhouse.svg
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
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
# 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.
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
appVersion: "24.9.2"

View File

@@ -5,6 +5,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
image:
docker buildx build images/clickhouse-backup \

View File

@@ -23,53 +23,53 @@ For more details, read [Restic: Effective Backup from Stdin](https://blog.aenix.
### Common parameters
| Name | Description | Type | Value |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
| `replicas` | Number of Clickhouse replicas | `int` | `2` |
| `shards` | Number of Clickhouse shards | `int` | `1` |
| `resources` | Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
| `size` | Persistent Volume Claim size, available for application data | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data | `string` | `""` |
| Name | Description | Type | Value |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------- |
| `replicas` | Number of ClickHouse replicas. | `int` | `2` |
| `shards` | Number of ClickHouse shards. | `int` | `1` |
| `resources` | Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `resources.cpu` | CPU available to each replica. | `quantity` | `""` |
| `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `small` |
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
### Application-specific parameters
| Name | Description | Type | Value |
| ---------------------- | ------------------------------------------------------------ | ------------------- | ------- |
| `logStorageSize` | Size of Persistent Volume for logs | `quantity` | `2Gi` |
| `logTTL` | TTL (expiration time) for `query_log` and `query_thread_log` | `int` | `15` |
| `users` | Users configuration | `map[string]object` | `{...}` |
| `users[name].password` | Password for the user | `*string` | `null` |
| `users[name].readonly` | User is `readonly`, default is `false`. | `*bool` | `null` |
| Name | Description | Type | Value |
| ---------------------- | ------------------------------------------------------------- | ------------------- | ------- |
| `logStorageSize` | Size of Persistent Volume for logs. | `quantity` | `2Gi` |
| `logTTL` | TTL (expiration time) for `query_log` and `query_thread_log`. | `int` | `15` |
| `users` | Users configuration map. | `map[string]object` | `{}` |
| `users[name].password` | Password for the user. | `string` | `""` |
| `users[name].readonly` | User is readonly (default: false). | `bool` | `false` |
### Backup parameters
| Name | Description | Type | Value |
| ------------------------ | ---------------------------------------------- | -------- | ------------------------------------------------------ |
| `backup` | Backup configuration | `object` | `{}` |
| `backup.enabled` | Enable regular backups, default is `false` | `bool` | `false` |
| `backup.s3Region` | AWS S3 region where backups are stored | `string` | `us-east-1` |
| `backup.s3Bucket` | S3 bucket used for storing backups | `string` | `s3.example.org/clickhouse-backups` |
| `backup.schedule` | Cron schedule for automated backups | `string` | `0 2 * * *` |
| `backup.cleanupStrategy` | Retention strategy for cleaning up old backups | `string` | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
| `backup.s3AccessKey` | Access key for S3, used for authentication | `string` | `<your-access-key>` |
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `string` | `<your-secret-key>` |
| `backup.resticPassword` | Password for Restic backup encryption | `string` | `<password>` |
| Name | Description | Type | Value |
| ------------------------ | ----------------------------------------------- | -------- | ------------------------------------------------------ |
| `backup` | Backup configuration. | `object` | `{}` |
| `backup.enabled` | Enable regular backups (default: false). | `bool` | `false` |
| `backup.s3Region` | AWS S3 region where backups are stored. | `string` | `us-east-1` |
| `backup.s3Bucket` | S3 bucket used for storing backups. | `string` | `s3.example.org/clickhouse-backups` |
| `backup.schedule` | Cron schedule for automated backups. | `string` | `0 2 * * *` |
| `backup.cleanupStrategy` | Retention strategy for cleaning up old backups. | `string` | `--keep-last=3 --keep-daily=3 --keep-within-weekly=1m` |
| `backup.s3AccessKey` | Access key for S3 authentication. | `string` | `<your-access-key>` |
| `backup.s3SecretKey` | Secret key for S3 authentication. | `string` | `<your-secret-key>` |
| `backup.resticPassword` | Password for Restic backup encryption. | `string` | `<password>` |
### Clickhouse Keeper parameters
### ClickHouse Keeper parameters
| Name | Description | Type | Value |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
| `clickhouseKeeper` | Clickhouse Keeper configuration | `*object` | `{}` |
| `clickhouseKeeper.enabled` | Deploy ClickHouse Keeper for cluster coordination | `*bool` | `true` |
| `clickhouseKeeper.size` | Persistent Volume Claim size, available for application data | `*quantity` | `1Gi` |
| `clickhouseKeeper.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
| `clickhouseKeeper.replicas` | Number of Keeper replicas | `*int` | `3` |
| Name | Description | Type | Value |
| ---------------------------------- | ------------------------------------------------------------ | ---------- | ------- |
| `clickhouseKeeper` | ClickHouse Keeper configuration. | `object` | `{}` |
| `clickhouseKeeper.enabled` | Deploy ClickHouse Keeper for cluster coordination. | `bool` | `true` |
| `clickhouseKeeper.size` | Persistent Volume Claim size available for application data. | `quantity` | `1Gi` |
| `clickhouseKeeper.resourcesPreset` | Default sizing preset. | `string` | `micro` |
| `clickhouseKeeper.replicas` | Number of Keeper replicas. | `int` | `3` |
## Parameter examples and reference

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/clickhouse-backup:0.13.0@sha256:3faf7a4cebf390b9053763107482de175aa0fdb88c1e77424fd81100b1c3a205
ghcr.io/cozystack/cozystack/clickhouse-backup:0.0.0@sha256:3faf7a4cebf390b9053763107482de175aa0fdb88c1e77424fd81100b1c3a205

View File

@@ -1,28 +0,0 @@
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}
spec:
replicas: {{ .Values.replicas }}
minReplicas: 1
kind: clickhouse
type: clickhouse
selector:
app.kubernetes.io/instance: {{ $.Release.Name }}
version: {{ $.Chart.Version }}
{{- if .Values.clickhouseKeeper.enabled }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-keeper
spec:
replicas: {{ .Values.clickhouseKeeper.replicas }}
minReplicas: 1
kind: clickhouse
type: clickhouse
selector:
app: {{ $.Release.Name }}-keeper
version: {{ $.Chart.Version }}
{{- end }}

View File

@@ -3,18 +3,9 @@
"type": "object",
"properties": {
"backup": {
"description": "Backup configuration",
"description": "Backup configuration.",
"type": "object",
"default": {
"cleanupStrategy": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m",
"enabled": false,
"resticPassword": "\u003cpassword\u003e",
"s3AccessKey": "\u003cyour-access-key\u003e",
"s3Bucket": "s3.example.org/clickhouse-backups",
"s3Region": "us-east-1",
"s3SecretKey": "\u003cyour-secret-key\u003e",
"schedule": "0 2 * * *"
},
"default": {},
"required": [
"cleanupStrategy",
"enabled",
@@ -27,72 +18,64 @@
],
"properties": {
"cleanupStrategy": {
"description": "Retention strategy for cleaning up old backups",
"description": "Retention strategy for cleaning up old backups.",
"type": "string",
"default": "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"
},
"enabled": {
"description": "Enable regular backups, default is `false`",
"description": "Enable regular backups (default: false).",
"type": "boolean",
"default": false
},
"resticPassword": {
"description": "Password for Restic backup encryption",
"description": "Password for Restic backup encryption.",
"type": "string",
"default": "\u003cpassword\u003e"
},
"s3AccessKey": {
"description": "Access key for S3, used for authentication",
"description": "Access key for S3 authentication.",
"type": "string",
"default": "\u003cyour-access-key\u003e"
},
"s3Bucket": {
"description": "S3 bucket used for storing backups",
"description": "S3 bucket used for storing backups.",
"type": "string",
"default": "s3.example.org/clickhouse-backups"
},
"s3Region": {
"description": "AWS S3 region where backups are stored",
"description": "AWS S3 region where backups are stored.",
"type": "string",
"default": "us-east-1"
},
"s3SecretKey": {
"description": "Secret key for S3, used for authentication",
"description": "Secret key for S3 authentication.",
"type": "string",
"default": "\u003cyour-secret-key\u003e"
},
"schedule": {
"description": "Cron schedule for automated backups",
"description": "Cron schedule for automated backups.",
"type": "string",
"default": "0 2 * * *"
}
}
},
"clickhouseKeeper": {
"description": "Clickhouse Keeper configuration",
"description": "ClickHouse Keeper configuration.",
"type": "object",
"default": {
"enabled": true,
"replicas": 3,
"resourcesPreset": "micro",
"size": "1Gi"
},
"required": [
"resourcesPreset"
],
"default": {},
"properties": {
"enabled": {
"description": "Deploy ClickHouse Keeper for cluster coordination",
"description": "Deploy ClickHouse Keeper for cluster coordination.",
"type": "boolean",
"default": true
},
"replicas": {
"description": "Number of Keeper replicas",
"description": "Number of Keeper replicas.",
"type": "integer",
"default": 3
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
"description": "Default sizing preset.",
"type": "string",
"default": "micro",
"enum": [
@@ -106,7 +89,7 @@
]
},
"size": {
"description": "Persistent Volume Claim size, available for application data",
"description": "Persistent Volume Claim size available for application data.",
"default": "1Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
@@ -122,7 +105,7 @@
}
},
"logStorageSize": {
"description": "Size of Persistent Volume for logs",
"description": "Size of Persistent Volume for logs.",
"default": "2Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
@@ -136,22 +119,22 @@
"x-kubernetes-int-or-string": true
},
"logTTL": {
"description": "TTL (expiration time) for `query_log` and `query_thread_log`",
"description": "TTL (expiration time) for `query_log` and `query_thread_log`.",
"type": "integer",
"default": 15
},
"replicas": {
"description": "Number of Clickhouse replicas",
"description": "Number of ClickHouse replicas.",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied.",
"description": "Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each replica",
"description": "CPU available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
@@ -164,7 +147,7 @@
"x-kubernetes-int-or-string": true
},
"memory": {
"description": "Memory (RAM) available to each replica",
"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": [
{
@@ -179,7 +162,7 @@
}
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
"description": "Default sizing preset used when `resources` is omitted.",
"type": "string",
"default": "small",
"enum": [
@@ -193,12 +176,12 @@
]
},
"shards": {
"description": "Number of Clickhouse shards",
"description": "Number of ClickHouse shards.",
"type": "integer",
"default": 1
},
"size": {
"description": "Persistent Volume Claim size, available for application data",
"description": "Persistent Volume Claim size available for application data.",
"default": "10Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
@@ -212,22 +195,23 @@
"x-kubernetes-int-or-string": true
},
"storageClass": {
"description": "StorageClass used to store the data",
"type": "string"
"description": "StorageClass used to store the data.",
"type": "string",
"default": ""
},
"users": {
"description": "Users configuration",
"description": "Users configuration map.",
"type": "object",
"default": {},
"additionalProperties": {
"type": "object",
"properties": {
"password": {
"description": "Password for the user",
"description": "Password for the user.",
"type": "string"
},
"readonly": {
"description": "User is `readonly`, default is `false`.",
"description": "User is readonly (default: false).",
"type": "boolean"
}
}

View File

@@ -1,36 +1,54 @@
##
## @section Common parameters
##
## @param replicas {int} Number of Clickhouse replicas
## @typedef {struct} Resources - Explicit CPU and memory configuration for each ClickHouse replica.
## @field {quantity} [cpu] - CPU available to each replica.
## @field {quantity} [memory] - Memory (RAM) available to each replica.
## @enum {string} ResourcesPreset - Default sizing preset.
## @value nano
## @value micro
## @value small
## @value medium
## @value large
## @value xlarge
## @value 2xlarge
## @param {int} replicas - Number of ClickHouse replicas.
replicas: 2
## @param shards {int} Number of Clickhouse shards
## @param {int} shards - Number of ClickHouse shards.
shards: 1
## @param resources {*resources} Explicit CPU and memory configuration for each Clickhouse replica. When left empty, the preset defined in `resourcesPreset` is applied.
## @field resources.cpu {*quantity} CPU available to each replica
## @field resources.memory {*quantity} Memory (RAM) available to each replica
# resources:
# cpu: 4000m
# memory: 4Gi
## @param {Resources} [resources] - Explicit CPU and memory configuration for each ClickHouse replica. When omitted, the preset defined in `resourcesPreset` is applied.
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`.
## @param {ResourcesPreset} resourcesPreset="small" - Default sizing preset used when `resources` is omitted.
resourcesPreset: "small"
## @param size {quantity} Persistent Volume Claim size, available for application data
## @param {quantity} size - Persistent Volume Claim size available for application data.
size: 10Gi
## @param storageClass {string} StorageClass used to store the data
## @param {string} storageClass - StorageClass used to store the data.
storageClass: ""
##
## @section Application-specific parameters
##
## @param logStorageSize {quantity} Size of Persistent Volume for logs
## @param {quantity} logStorageSize - Size of Persistent Volume for logs.
logStorageSize: 2Gi
## @param logTTL {int} TTL (expiration time) for `query_log` and `query_thread_log`
## @param {int} logTTL - TTL (expiration time) for `query_log` and `query_thread_log`.
logTTL: 15
## @param users {map[string]user} Users configuration
## @field user.password {*string} Password for the user
## @field user.readonly {*bool} User is `readonly`, default is `false`.
## @typedef {struct} User - User configuration.
## @field {string} [password] - Password for the user.
## @field {bool} [readonly] - User is readonly (default: false).
## @param {map[string]User} users - Users configuration map.
users: {}
## Example:
## users:
## user1:
@@ -39,20 +57,22 @@ logTTL: 15
## readonly: true
## password: hackme
##
users: {}
##
## @section Backup parameters
##
## @param backup {backup} Backup configuration
## @field backup.enabled {bool} Enable regular backups, default is `false`
## @field backup.s3Region {string} AWS S3 region where backups are stored
## @field backup.s3Bucket {string} S3 bucket used for storing backups
## @field backup.schedule {string} Cron schedule for automated backups
## @field backup.cleanupStrategy {string} Retention strategy for cleaning up old backups
## @field backup.s3AccessKey {string} Access key for S3, used for authentication
## @field backup.s3SecretKey {string} Secret key for S3, used for authentication
## @field backup.resticPassword {string} Password for Restic backup encryption
## @typedef {struct} Backup - Backup configuration.
## @field {bool} enabled - Enable regular backups (default: false).
## @field {string} s3Region - AWS S3 region where backups are stored.
## @field {string} s3Bucket - S3 bucket used for storing backups.
## @field {string} schedule - Cron schedule for automated backups.
## @field {string} cleanupStrategy - Retention strategy for cleaning up old backups.
## @field {string} s3AccessKey - Access key for S3 authentication.
## @field {string} s3SecretKey - Secret key for S3 authentication.
## @field {string} resticPassword - Password for Restic backup encryption.
## @param {Backup} backup - Backup configuration.
backup:
enabled: false
s3Region: us-east-1
@@ -63,13 +83,17 @@ backup:
s3SecretKey: "<your-secret-key>"
resticPassword: "<password>"
##
## @section ClickHouse Keeper parameters
##
## @section Clickhouse Keeper parameters
## @param clickhouseKeeper {*clickhouseKeeper} Clickhouse Keeper configuration
## @field clickhouseKeeper.enabled {*bool} Deploy ClickHouse Keeper for cluster coordination
## @field clickhouseKeeper.size {*quantity} Persistent Volume Claim size, available for application data
## @field clickhouseKeeper.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
## @field clickhouseKeeper.replicas {*int} Number of Keeper replicas
## @typedef {struct} ClickHouseKeeper - ClickHouse Keeper configuration.
## @field {bool} [enabled] - Deploy ClickHouse Keeper for cluster coordination.
## @field {quantity} [size] - Persistent Volume Claim size available for application data.
## @field {ResourcesPreset} [resourcesPreset] - Default sizing preset.
## @field {int} [replicas] - Number of Keeper replicas.
## @param {ClickHouseKeeper} clickhouseKeeper - ClickHouse Keeper configuration.
clickhouseKeeper:
enabled: true
size: 1Gi

View File

@@ -2,24 +2,6 @@ apiVersion: v2
name: ferretdb
description: Managed FerretDB service
icon: /logos/ferretdb.svg
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 1.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
appVersion: 2.4.0

View File

@@ -2,6 +2,7 @@ include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
update:
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/FerretDB/FerretDB | awk -F'[/^]' '{sub("^v", "", $$3)} END{print $$3}') && \

View File

@@ -8,51 +8,51 @@ Internally, FerretDB service is backed by Postgres.
### Common parameters
| Name | Description | Type | Value |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- |
| `replicas` | Number of replicas | `int` | `2` |
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
| `size` | Persistent Volume Claim size, available for application data | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data | `string` | `""` |
| `external` | Enable external access from outside the cluster | `bool` | `false` |
| Name | Description | Type | Value |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
| `replicas` | Number of replicas. | `int` | `2` |
| `resources` | Explicit CPU and memory configuration for each FerretDB replica. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `resources.cpu` | CPU available to each replica. | `quantity` | `""` |
| `resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `micro` |
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
### Application-specific parameters
| Name | Description | Type | Value |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
| `quorum` | Configuration for the quorum-based synchronous replication | `object` | `{}` |
| `quorum.minSyncReplicas` | Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed | `int` | `0` |
| `quorum.maxSyncReplicas` | Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas) | `int` | `0` |
| `users` | Users configuration | `map[string]object` | `{...}` |
| `users[name].password` | Password for the user | `*string` | `null` |
| Name | Description | Type | Value |
| ------------------------ | ---------------------------------------------------------------------------------- | ------------------- | ----- |
| `quorum` | Configuration for quorum-based synchronous replication. | `object` | `{}` |
| `quorum.minSyncReplicas` | Minimum number of synchronous replicas required for commit. | `int` | `0` |
| `quorum.maxSyncReplicas` | Maximum number of synchronous replicas allowed (must be less than total replicas). | `int` | `0` |
| `users` | Users configuration map. | `map[string]object` | `{}` |
| `users[name].password` | Password for the user. | `string` | `""` |
### Backup parameters
| Name | Description | Type | Value |
| ------------------------ | ---------------------------------------------------------- | -------- | ----------------------------------- |
| `backup` | Backup configuration | `object` | `{}` |
| `backup.enabled` | Enable regular backups, default is `false`. | `bool` | `false` |
| `backup.schedule` | Cron schedule for automated backups | `string` | `0 2 * * * *` |
| `backup.retentionPolicy` | Retention policy | `string` | `30d` |
| `backup.endpointURL` | S3 Endpoint used to upload data to the cloud | `string` | `http://minio-gateway-service:9000` |
| `backup.destinationPath` | Path to store the backup (i.e. s3://bucket/path/to/folder) | `string` | `s3://bucket/path/to/folder/` |
| `backup.s3AccessKey` | Access key for S3, used for authentication | `string` | `<your-access-key>` |
| `backup.s3SecretKey` | Secret key for S3, used for authentication | `string` | `<your-secret-key>` |
| Name | Description | Type | Value |
| ------------------------ | ------------------------------------------------------------ | -------- | ----------------------------------- |
| `backup` | Backup configuration. | `object` | `{}` |
| `backup.enabled` | Enable regular backups (default: false). | `bool` | `false` |
| `backup.schedule` | Cron schedule for automated backups. | `string` | `0 2 * * * *` |
| `backup.retentionPolicy` | Retention policy. | `string` | `30d` |
| `backup.endpointURL` | S3 endpoint URL for uploads. | `string` | `http://minio-gateway-service:9000` |
| `backup.destinationPath` | Path to store the backup (e.g. s3://bucket/path/to/folder/). | `string` | `s3://bucket/path/to/folder/` |
| `backup.s3AccessKey` | Access key for S3 authentication. | `string` | `<your-access-key>` |
| `backup.s3SecretKey` | Secret key for S3 authentication. | `string` | `<your-secret-key>` |
### Bootstrap (recovery) parameters
| Name | Description | Type | Value |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
| `bootstrap` | Bootstrap (recovery) configuration | `object` | `{}` |
| `bootstrap.enabled` | Restore database cluster from a backup | `*bool` | `false` |
| `bootstrap.recoveryTime` | Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest. | `*string` | `""` |
| `bootstrap.oldName` | Name of database cluster before deleting | `*string` | `""` |
| Name | Description | Type | Value |
| ------------------------ | ------------------------------------------------------------------- | -------- | ------- |
| `bootstrap` | Bootstrap configuration. | `object` | `{}` |
| `bootstrap.enabled` | Restore database cluster from a backup. | `bool` | `false` |
| `bootstrap.recoveryTime` | Timestamp (RFC3339) for point-in-time recovery; empty means latest. | `string` | `""` |
| `bootstrap.oldName` | Name of database cluster before deletion. | `string` | `""` |
## Parameter examples and reference

View File

@@ -1,13 +0,0 @@
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}
spec:
replicas: {{ .Values.replicas }}
minReplicas: 1
kind: ferretdb
type: ferretdb
selector:
app.kubernetes.io/instance: {{ $.Release.Name }}
version: {{ $.Chart.Version }}

View File

@@ -3,17 +3,9 @@
"type": "object",
"properties": {
"backup": {
"description": "Backup configuration",
"description": "Backup configuration.",
"type": "object",
"default": {
"destinationPath": "s3://bucket/path/to/folder/",
"enabled": false,
"endpointURL": "http://minio-gateway-service:9000",
"retentionPolicy": "30d",
"s3AccessKey": "\u003cyour-access-key\u003e",
"s3SecretKey": "\u003cyour-secret-key\u003e",
"schedule": "0 2 * * * *"
},
"default": {},
"required": [
"destinationPath",
"enabled",
@@ -25,107 +17,102 @@
],
"properties": {
"destinationPath": {
"description": "Path to store the backup (i.e. s3://bucket/path/to/folder)",
"description": "Path to store the backup (e.g. s3://bucket/path/to/folder/).",
"type": "string",
"default": "s3://bucket/path/to/folder/"
},
"enabled": {
"description": "Enable regular backups, default is `false`.",
"description": "Enable regular backups (default: false).",
"type": "boolean",
"default": false
},
"endpointURL": {
"description": "S3 Endpoint used to upload data to the cloud",
"description": "S3 endpoint URL for uploads.",
"type": "string",
"default": "http://minio-gateway-service:9000"
},
"retentionPolicy": {
"description": "Retention policy",
"description": "Retention policy.",
"type": "string",
"default": "30d"
},
"s3AccessKey": {
"description": "Access key for S3, used for authentication",
"description": "Access key for S3 authentication.",
"type": "string",
"default": "\u003cyour-access-key\u003e"
},
"s3SecretKey": {
"description": "Secret key for S3, used for authentication",
"description": "Secret key for S3 authentication.",
"type": "string",
"default": "\u003cyour-secret-key\u003e"
},
"schedule": {
"description": "Cron schedule for automated backups",
"description": "Cron schedule for automated backups.",
"type": "string",
"default": "0 2 * * * *"
}
}
},
"bootstrap": {
"description": "Bootstrap (recovery) configuration",
"description": "Bootstrap configuration.",
"type": "object",
"default": {
"enabled": false,
"oldName": "",
"recoveryTime": ""
},
"default": {},
"properties": {
"enabled": {
"description": "Restore database cluster from a backup",
"description": "Restore database cluster from a backup.",
"type": "boolean",
"default": false
},
"oldName": {
"description": "Name of database cluster before deleting",
"type": "string"
"description": "Name of database cluster before deletion.",
"type": "string",
"default": ""
},
"recoveryTime": {
"description": "Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest.",
"type": "string"
"description": "Timestamp (RFC3339) for point-in-time recovery; empty means latest.",
"type": "string",
"default": ""
}
}
},
"external": {
"description": "Enable external access from outside the cluster",
"description": "Enable external access from outside the cluster.",
"type": "boolean",
"default": false
},
"quorum": {
"description": "Configuration for the quorum-based synchronous replication",
"description": "Configuration for quorum-based synchronous replication.",
"type": "object",
"default": {
"maxSyncReplicas": 0,
"minSyncReplicas": 0
},
"default": {},
"required": [
"maxSyncReplicas",
"minSyncReplicas"
],
"properties": {
"maxSyncReplicas": {
"description": "Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)",
"description": "Maximum number of synchronous replicas allowed (must be less than total replicas).",
"type": "integer",
"default": 0
},
"minSyncReplicas": {
"description": "Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed",
"description": "Minimum number of synchronous replicas required for commit.",
"type": "integer",
"default": 0
}
}
},
"replicas": {
"description": "Number of replicas",
"description": "Number of replicas.",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.",
"description": "Explicit CPU and memory configuration for each FerretDB replica. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each replica",
"description": "CPU available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
@@ -138,7 +125,7 @@
"x-kubernetes-int-or-string": true
},
"memory": {
"description": "Memory (RAM) available to each replica",
"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": [
{
@@ -153,7 +140,7 @@
}
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
"description": "Default sizing preset used when `resources` is omitted.",
"type": "string",
"default": "micro",
"enum": [
@@ -167,7 +154,7 @@
]
},
"size": {
"description": "Persistent Volume Claim size, available for application data",
"description": "Persistent Volume Claim size available for application data.",
"default": "10Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
@@ -181,18 +168,19 @@
"x-kubernetes-int-or-string": true
},
"storageClass": {
"description": "StorageClass used to store the data",
"type": "string"
"description": "StorageClass used to store the data.",
"type": "string",
"default": ""
},
"users": {
"description": "Users configuration",
"description": "Users configuration map.",
"type": "object",
"default": {},
"additionalProperties": {
"type": "object",
"properties": {
"password": {
"description": "Password for the user",
"description": "Password for the user.",
"type": "string"
}
}

View File

@@ -1,35 +1,56 @@
##
## @section Common parameters
##
## @param replicas {int} Number of replicas
## @typedef {struct} Resources - Explicit CPU and memory configuration for each FerretDB replica.
## @field {quantity} [cpu] - CPU available to each replica.
## @field {quantity} [memory] - Memory (RAM) available to each replica.
## @enum {string} ResourcesPreset - Default sizing preset.
## @value nano
## @value micro
## @value small
## @value medium
## @value large
## @value xlarge
## @value 2xlarge
## @param {int} replicas - Number of replicas.
replicas: 2
## @param resources {*resources} Explicit CPU and memory configuration for each FerretDB replica. When left empty, the preset defined in `resourcesPreset` is applied.
## @field resources.cpu {*quantity} CPU available to each replica
## @field resources.memory {*quantity} Memory (RAM) available to each replica
# resources:
# cpu: 4000m
# memory: 4Gi
## @param {Resources} [resources] - Explicit CPU and memory configuration for each FerretDB replica. When omitted, the preset defined in `resourcesPreset` is applied.
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`.
## @param {ResourcesPreset} resourcesPreset="micro" - Default sizing preset used when `resources` is omitted.
resourcesPreset: "micro"
## @param size {quantity} Persistent Volume Claim size, available for application data
## @param {quantity} size - Persistent Volume Claim size available for application data.
size: 10Gi
## @param storageClass {string} StorageClass used to store the data
## @param {string} storageClass - StorageClass used to store the data.
storageClass: ""
## @param external {bool} Enable external access from outside the cluster
## @param {bool} external - Enable external access from outside the cluster.
external: false
##
## @section Application-specific parameters
##
## @param quorum {quorum} Configuration for the quorum-based synchronous replication
## @field quorum.minSyncReplicas {int} Minimum number of synchronous replicas that must acknowledge a transaction before it is considered committed
## @field quorum.maxSyncReplicas {int} Maximum number of synchronous replicas that can acknowledge a transaction (must be lower than the total number of replicas)
## @typedef {struct} Quorum - Configuration for quorum-based synchronous replication.
## @field {int} minSyncReplicas - Minimum number of synchronous replicas required for commit.
## @field {int} maxSyncReplicas - Maximum number of synchronous replicas allowed (must be less than total replicas).
## @param {Quorum} quorum - Configuration for quorum-based synchronous replication.
quorum:
minSyncReplicas: 0
maxSyncReplicas: 0
## @param users {map[string]user} Users configuration
## @field user.password {*string} Password for the user
## @typedef {struct} User - User configuration.
## @field {string} [password] - Password for the user.
## @param {map[string]User} users - Users configuration map.
users: {}
## Example:
## users:
## user1:
@@ -37,21 +58,21 @@ quorum:
## user2:
## password: hackme
##
users: {}
##
## @section Backup parameters
##
## @param backup {backup} Backup configuration
## @field backup.enabled {bool} Enable regular backups, default is `false`.
## @field backup.schedule {string} Cron schedule for automated backups
## @field backup.retentionPolicy {string} Retention policy
## @field backup.endpointURL {string} S3 Endpoint used to upload data to the cloud
## @field backup.destinationPath {string} Path to store the backup (i.e. s3://bucket/path/to/folder)
## @field backup.s3AccessKey {string} Access key for S3, used for authentication
## @field backup.s3SecretKey {string} Secret key for S3, used for authentication
## @typedef {struct} Backup - Backup configuration.
## @field {bool} enabled - Enable regular backups (default: false).
## @field {string} schedule - Cron schedule for automated backups.
## @field {string} retentionPolicy - Retention policy.
## @field {string} endpointURL - S3 endpoint URL for uploads.
## @field {string} destinationPath - Path to store the backup (e.g. s3://bucket/path/to/folder/).
## @field {string} s3AccessKey - Access key for S3 authentication.
## @field {string} s3SecretKey - Secret key for S3 authentication.
## @param {Backup} backup - Backup configuration.
backup:
enabled: false
schedule: "0 2 * * * *"
@@ -61,18 +82,17 @@ backup:
s3AccessKey: "<your-access-key>"
s3SecretKey: "<your-secret-key>"
##
## @section Bootstrap (recovery) parameters
##
## @param bootstrap {bootstrap} Bootstrap (recovery) configuration
## @field bootstrap.enabled {*bool} Restore database cluster from a backup
## @field bootstrap.recoveryTime {*string} Timestamp (PITR) up to which recovery will proceed, expressed in RFC 3339 format. If left empty, will restore latest.
## @field bootstrap.oldName {*string} Name of database cluster before deleting
##
## @typedef {struct} Bootstrap - Bootstrap configuration for restoring a database cluster from a backup.
## @field {bool} [enabled] - Restore database cluster from a backup.
## @field {string} [recoveryTime] - Timestamp (RFC3339) for point-in-time recovery; empty means latest.
## @field {string} [oldName] - Name of database cluster before deletion.
## @param {Bootstrap} bootstrap - Bootstrap configuration.
bootstrap:
enabled: false
# example: 2020-11-26 15:22:00.00000+00
recoveryTime: ""
oldName: ""

View File

@@ -0,0 +1 @@
Makefile

View File

@@ -0,0 +1,25 @@
apiVersion: v2
name: foundationdb
description: Managed FoundationDB service
icon: /logos/foundationdb.svg
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
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.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "7.3.63"

View File

@@ -0,0 +1,4 @@
include ../../../scripts/package.mk
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md

View File

@@ -0,0 +1,195 @@
# FoundationDB
A managed FoundationDB service for Cozystack.
## Overview
FoundationDB is a distributed database designed to handle large volumes of structured data across clusters of commodity servers. It organizes data as an ordered key-value store and employs ACID transactions for all operations.
This package provides a managed FoundationDB cluster deployment using the FoundationDB Kubernetes Operator.
## Features
- **High Availability**: Multi-instance deployment with automatic failover
- **ACID Transactions**: Full ACID transaction support across the cluster
- **Scalable**: Easily scale storage and compute resources
- **Backup Integration**: Optional S3-compatible backup storage
- **Monitoring**: Built-in monitoring and alerting through WorkloadMonitor
- **Flexible Configuration**: Support for custom FoundationDB parameters
## Configuration
### Basic Configuration
```yaml
# Cluster process configuration
cluster:
version: "7.3.63"
processCounts:
storage: 3 # Number of storage processes (determines cluster size)
stateless: -1 # Automatically calculated
cluster_controller: 1
faultDomain:
key: "kubernetes.io/hostname"
valueFrom: "spec.nodeName"
```
### Storage
```yaml
storage:
size: "16Gi" # Storage size per instance
storageClass: "" # Storage class (optional)
```
### Resources
```yaml
# Use preset sizing
resourcesPreset: "medium" # small, medium, large, xlarge, 2xlarge
# Or custom resource configuration
resources:
cpu: "2000m"
memory: "4Gi"
```
### Backup (Optional)
```yaml
backup:
enabled: true
s3:
bucket: "my-fdb-backups"
endpoint: "https://s3.amazonaws.com"
region: "us-east-1"
credentials:
accessKeyId: "AKIA..."
secretAccessKey: "..."
retentionPolicy: "7d"
```
### Advanced Configuration
```yaml
# Custom FoundationDB parameters
customParameters:
- "knob_disable_posix_kernel_aio=1"
# Image type (unified is default and recommended for new deployments)
imageType: "unified"
# Enable automatic pod replacements
automaticReplacements: true
# Security context configuration
securityContext:
runAsUser: 4059
runAsGroup: 4059
```
## Prerequisites
- FoundationDB Operator must be installed in the cluster
- Sufficient storage and compute resources
- For backups: S3-compatible storage credentials
## Deployment
1. Install the FoundationDB operator (system package)
2. Deploy this application package with your desired configuration
3. The cluster will be automatically provisioned and configured
## Monitoring
This package includes WorkloadMonitor integration for cluster health monitoring and resource tracking. Monitoring can be disabled by setting:
```yaml
monitoring:
enabled: false
```
## Security
- All containers run with restricted security contexts
- No privilege escalation allowed
- Read-only root filesystem where possible
- Custom security context configurations supported
## Fault Tolerance
FoundationDB is designed for high availability:
- Automatic failure detection and recovery
- Data replication across instances
- Configurable fault domains for rack/zone awareness
- Transaction log redundancy
The included `WorkloadMonitor` is automatically configured based on the `cluster.redundancyMode` value. It sets the `minReplicas` property on the `WorkloadMonitor` resource to ensure the cluster's health status accurately reflects its fault tolerance level. The number of tolerated failures is as follows:
- `single`: 0 failures
- `double`: 1 failure
- `triple` and datacenter-aware modes: 2 failures
For example, with the default configuration (`redundancyMode: double` and 3 storage pods), `minReplicas` will be set to 2.
## Performance Considerations
- Use SSD storage for better performance
- Consider dedicating nodes for storage processes
- Monitor cluster metrics for optimization opportunities
- Scale storage and stateless processes based on workload
## Support
For issues related to FoundationDB itself, refer to the [FoundationDB documentation](https://apple.github.io/foundationdb/).
For Cozystack-specific issues, consult the Cozystack documentation or support channels.
## Parameters
### Common parameters
| Name | Description | Type | Value |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------ |
| `cluster` | Cluster configuration. | `object` | `{}` |
| `cluster.processCounts` | Process counts for different roles. | `object` | `{}` |
| `cluster.processCounts.stateless` | Number of stateless processes (-1 for automatic). | `int` | `-1` |
| `cluster.processCounts.storage` | Number of storage processes (determines cluster size). | `int` | `3` |
| `cluster.processCounts.cluster_controller` | Number of cluster controller processes. | `int` | `1` |
| `cluster.version` | Version of FoundationDB to use. | `string` | `7.3.63` |
| `cluster.redundancyMode` | Database redundancy mode (single, double, triple, three_datacenter, three_datacenter_fallback). | `string` | `double` |
| `cluster.storageEngine` | Storage engine (ssd-2, ssd-redwood-v1, ssd-rocksdb-v1, memory). | `string` | `ssd-2` |
| `cluster.faultDomain` | Fault domain configuration. | `object` | `{}` |
| `cluster.faultDomain.key` | Fault domain key. | `string` | `kubernetes.io/hostname` |
| `cluster.faultDomain.valueFrom` | Fault domain value source. | `string` | `spec.nodeName` |
| `storage` | Storage configuration. | `object` | `{}` |
| `storage.size` | Size of persistent volumes for each instance. | `quantity` | `16Gi` |
| `storage.storageClass` | Storage class (if not set, uses cluster default). | `string` | `""` |
| `resources` | Explicit CPU and memory configuration for each FoundationDB instance. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `resources.cpu` | CPU available to each instance. | `quantity` | `""` |
| `resources.memory` | Memory (RAM) available to each instance. | `quantity` | `""` |
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `medium` |
| `backup` | Backup configuration. | `object` | `{}` |
| `backup.enabled` | Enable backups. | `bool` | `false` |
| `backup.s3` | S3 configuration for backups. | `object` | `{}` |
| `backup.s3.bucket` | S3 bucket name. | `string` | `""` |
| `backup.s3.endpoint` | S3 endpoint URL. | `string` | `""` |
| `backup.s3.region` | S3 region. | `string` | `us-east-1` |
| `backup.s3.credentials` | S3 credentials. | `object` | `{}` |
| `backup.s3.credentials.accessKeyId` | S3 access key ID. | `string` | `""` |
| `backup.s3.credentials.secretAccessKey` | S3 secret access key. | `string` | `""` |
| `backup.retentionPolicy` | Retention policy for backups. | `string` | `7d` |
| `monitoring` | Monitoring configuration. | `object` | `{}` |
| `monitoring.enabled` | Enable WorkloadMonitor integration. | `bool` | `true` |
### FoundationDB configuration
| Name | Description | Type | Value |
| ---------------------------- | ------------------------------------------ | ---------- | --------- |
| `customParameters` | Custom parameters to pass to FoundationDB. | `[]string` | `[]` |
| `imageType` | Container image deployment type. | `string` | `unified` |
| `securityContext` | Security context for containers. | `object` | `{}` |
| `securityContext.runAsUser` | User ID to run the container. | `int` | `4059` |
| `securityContext.runAsGroup` | Group ID to run the container. | `int` | `4059` |
| `automaticReplacements` | Enable automatic pod replacements. | `bool` | `true` |

View File

@@ -0,0 +1 @@
../../../library/cozy-lib

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="144"
height="144"
viewBox="0 0 144 144"
fill="none"
version="1.1"
id="svg4"
sodipodi:docname="foundationdb.svg"
inkscape:version="1.4.2 (unknown)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview4"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.0902778"
inkscape:cx="72"
inkscape:cy="72.492588"
inkscape:window-width="1920"
inkscape:window-height="1128"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<rect
width="144"
height="144"
rx="24"
fill="url(#paint0_linear_fdb)"
id="rect1"
style="fill:#ffffff" />
<!-- FoundationDB Icon (scaled and positioned) -->
<!-- FoundationDB Text -->
<defs
id="defs4">
<linearGradient
id="paint0_linear_fdb"
x1="140"
y1="130.5"
x2="4"
y2="9.49999"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#047BFE"
id="stop3" />
<stop
offset="1"
stop-color="#3F9AFB"
id="stop4" />
</linearGradient>
</defs>
<g
id="g1134"
transform="matrix(3.132791,0,0,3.132791,-115.98385,6.9294227)">
<g
transform="matrix(0.08541251,0,0,0.08541251,8.7615159,9.5962543)"
id="g10">
<polygon
style="fill:#3f9afb"
class="st0"
points="457.2,150.5 457.2,98.6 561.4,124 561.6,164.8 666.6,150.9 666.3,98.7 845.8,143 846.4,189.9 667.4,165.8 560.6,177.3 457.1,165.4 354.2,177.6 354.1,165.7 "
id="polygon4" />
<path
style="fill:#0b70e0"
inkscape:connector-curvature="0"
class="st1"
d="m 666.6,183.2 179.6,18.6 v 46 H 353.8 l -0.5,-12.2 h 103.5 c 0,0 0,-34.2 0,-52.3 34.8,3.4 103.8,10.2 103.8,10.2 v 40.9 h 106 z"
id="path6" />
<path
style="fill:#9eccfd"
inkscape:connector-curvature="0"
class="st2"
d="m 561.4,109.1 -0.3,-12.6 c 0,0 68.1,-20.4 103.3,-30.8 0,-16.9 0,-33.2 0,-52.9 61.8,24.8 121.2,48.8 181.2,72.9 0,15 0,29.4 0,45.4 -61.5,-16.9 -121.7,-33.5 -180.2,-49.6 -35.6,9.5 -104,27.6 -104,27.6 z"
id="path8" />
</g>
<polygon
transform="matrix(0.08541251,0,0,0.08541251,8.7795597,9.6869671)"
style="fill:#3f9afb"
class="st0"
points="666.6,150.9 666.3,98.7 845.8,143 846.4,189.9 667.4,165.8 560.6,177.3 457.1,165.4 354.2,177.6 354.1,165.7 457.2,150.5 457.2,98.6 561.4,124 561.6,164.8 "
id="polygon856" />
<path
style="fill:#0b70e0;stroke-width:0.0854125"
inkscape:connector-curvature="0"
class="st1"
d="m 65.715539,25.334539 15.340087,1.588673 v 3.928975 h -42.05712 l -0.04271,-1.042033 h 8.840195 c 0,0 0,-2.921107 0,-4.467074 2.972356,0.290403 8.865819,0.871208 8.865819,0.871208 v 3.493371 h 9.053726 z"
id="path858" />
<path
style="fill:#9eccfd;stroke-width:0.0854125"
inkscape:connector-curvature="0"
class="st2"
d="m 56.730143,19.005472 -0.02562,-1.076198 c 0,0 5.816592,-1.742415 8.823112,-2.630705 0,-1.443471 0,-2.835695 0,-4.518322 5.278493,2.11823 10.351997,4.168131 15.476747,6.226572 0,1.281188 0,2.511128 0,3.877728 -5.252869,-1.443471 -10.394702,-2.861319 -15.391334,-4.23646 -3.040686,0.811419 -8.882901,2.357385 -8.882901,2.357385 z"
id="path860" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,47 @@
{{/*
Common resource definitions
*/}}
{{- define "foundationdb.resources" -}}
{{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resources.preset .Values.resources $) }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "foundationdb.labels" -}}
helm.sh/chart: {{ include "foundationdb.chart" . }}
{{ include "foundationdb.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "foundationdb.selectorLabels" -}}
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Chart name and version
*/}}
{{- define "foundationdb.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Calculate minReplicas for WorkloadMonitor based on redundancyMode
*/}}
{{- define "foundationdb.minReplicas" -}}
{{- $replicas := .Values.cluster.processCounts.storage -}}
{{- if or (eq .Values.cluster.redundancyMode "triple") (eq .Values.cluster.redundancyMode "three_data_hall") (eq .Values.cluster.redundancyMode "three_datacenter") (eq .Values.cluster.redundancyMode "three_datacenter_fallback") (eq .Values.cluster.redundancyMode "three_data_hall_fallback") }}
{{- print (max 1 (sub $replicas 2)) -}}
{{- else if eq .Values.cluster.redundancyMode "double" }}
{{- print (max 1 (sub $replicas 1)) -}}
{{- else }}
{{- print $replicas -}}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,65 @@
{{- if .Values.backup.enabled }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-s3-creds
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
type: Opaque
data:
AWS_ACCESS_KEY_ID: {{ .Values.backup.s3.credentials.accessKeyId | b64enc }}
AWS_SECRET_ACCESS_KEY: {{ .Values.backup.s3.credentials.secretAccessKey | b64enc }}
---
apiVersion: apps.foundationdb.org/v1beta2
kind: FoundationDBBackup
metadata:
name: {{ .Release.Name }}-backup
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
clusterName: {{ .Release.Name }}
backupState: Running
backupDeploymentSpec:
podTemplateSpec:
spec:
containers:
- name: foundationdb
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
securityContext:
runAsUser: 0
customParameters:
- backup_agent_snapshot_mode=0
snapshotPeriodSeconds: 3600
blobStoreConfiguration:
accountName: {{ .Values.backup.s3.bucket }}
bucket: {{ .Values.backup.s3.bucket }}
{{- if .Values.backup.s3.endpoint }}
endpoint: {{ .Values.backup.s3.endpoint }}
{{- end }}
credentials:
AWS_ACCESS_KEY_ID:
secretKeyRef:
name: {{ .Release.Name }}-s3-creds
key: AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY:
secretKeyRef:
name: {{ .Release.Name }}-s3-creds
key: AWS_SECRET_ACCESS_KEY
{{- end }}

View File

@@ -0,0 +1,98 @@
{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" | default (dict "data" (dict)) }}
{{- $clusterDomain := index $cozyConfig.data "cluster-domain" | default "cozy.local" }}
---
apiVersion: apps.foundationdb.org/v1beta2
kind: FoundationDBCluster
metadata:
name: {{ .Release.Name }}
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
version: {{ .Values.cluster.version | quote }}
databaseConfiguration:
redundancy_mode: {{ .Values.cluster.redundancyMode }}
storage_engine: {{ .Values.cluster.storageEngine }}
processCounts:
{{- toYaml .Values.cluster.processCounts | nindent 4 }}
automationOptions:
replacements:
enabled: {{ .Values.automaticReplacements }}
faultDomain:
key: {{ .Values.cluster.faultDomain.key }}
{{- if .Values.cluster.faultDomain.valueFrom }}
valueFrom: {{ .Values.cluster.faultDomain.valueFrom }}
{{- end }}
imageType: {{ .Values.imageType }}
labels:
filterOnOwnerReference: false
matchLabels:
foundationdb.org/fdb-cluster-name: {{ .Release.Name }}
processClassLabels:
- foundationdb.org/fdb-process-class
processGroupIDLabels:
- foundationdb.org/fdb-process-group-id
minimumUptimeSecondsForBounce: 60
processes:
general:
{{- if .Values.customParameters }}
customParameters:
{{- range .Values.customParameters }}
- {{ . }}
{{- end }}
{{- end }}
podTemplate:
metadata:
labels:
policy.cozystack.io/allow-to-apiserver: "true"
spec:
serviceAccountName: {{ .Release.Name }}-foundationdb
securityContext:
fsGroup: {{ .Values.securityContext.runAsGroup }}
containers:
- name: foundationdb
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 16 }}
securityContext:
{{- toYaml .Values.securityContext | nindent 16 }}
- name: foundationdb-kubernetes-sidecar
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
securityContext:
{{- toYaml .Values.securityContext | nindent 16 }}
initContainers:
- name: foundationdb-kubernetes-init
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
securityContext:
{{- toYaml .Values.securityContext | nindent 16 }}
volumeClaimTemplate:
spec:
{{- if .Values.storage.storageClass }}
storageClassName: {{ .Values.storage.storageClass }}
{{- end }}
resources:
requests:
storage: {{ .Values.storage.size }}
routing:
dnsDomain: {{ $clusterDomain }}
defineDNSLocalityFields: true
sidecarContainer:
enableLivenessProbe: true
enableReadinessProbe: true

View File

@@ -0,0 +1,22 @@
{{- if .Values.monitoring.enabled }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-resourcemap
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.cozystack.io/type: dashboard-resourcemap
data:
resources: |
- apiVersion: apps.foundationdb.org/v1beta2
kind: FoundationDBCluster
name: {{ .Release.Name }}
{{- if .Values.backup.enabled }}
- apiVersion: apps.foundationdb.org/v1beta2
kind: FoundationDBBackup
name: {{ .Release.Name }}-backup
{{- end }}
{{- end }}

View File

@@ -0,0 +1,22 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-foundationdb
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- watch
- create
- update
- patch
- delete

View File

@@ -0,0 +1,17 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ .Release.Name }}-foundationdb
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ .Release.Name }}-foundationdb
subjects:
- kind: ServiceAccount
name: {{ .Release.Name }}-foundationdb
namespace: {{ .Release.Namespace }}

View File

@@ -0,0 +1,9 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Release.Name }}-foundationdb
labels:
app.kubernetes.io/name: foundationdb
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}

View File

@@ -0,0 +1,287 @@
{
"title": "Chart Values",
"type": "object",
"properties": {
"automaticReplacements": {
"description": "Enable automatic pod replacements.",
"type": "boolean",
"default": true
},
"backup": {
"description": "Backup configuration.",
"type": "object",
"default": {},
"required": [
"enabled",
"retentionPolicy",
"s3"
],
"properties": {
"enabled": {
"description": "Enable backups.",
"type": "boolean",
"default": false
},
"retentionPolicy": {
"description": "Retention policy for backups.",
"type": "string",
"default": "7d"
},
"s3": {
"description": "S3 configuration for backups.",
"type": "object",
"default": {},
"required": [
"bucket",
"credentials",
"endpoint",
"region"
],
"properties": {
"bucket": {
"description": "S3 bucket name.",
"type": "string",
"default": ""
},
"credentials": {
"description": "S3 credentials.",
"type": "object",
"default": {},
"required": [
"accessKeyId",
"secretAccessKey"
],
"properties": {
"accessKeyId": {
"description": "S3 access key ID.",
"type": "string",
"default": ""
},
"secretAccessKey": {
"description": "S3 secret access key.",
"type": "string",
"default": ""
}
}
},
"endpoint": {
"description": "S3 endpoint URL.",
"type": "string",
"default": ""
},
"region": {
"description": "S3 region.",
"type": "string",
"default": "us-east-1"
}
}
}
}
},
"cluster": {
"description": "Cluster configuration.",
"type": "object",
"default": {},
"required": [
"faultDomain",
"processCounts",
"redundancyMode",
"storageEngine",
"version"
],
"properties": {
"faultDomain": {
"description": "Fault domain configuration.",
"type": "object",
"default": {},
"required": [
"key",
"valueFrom"
],
"properties": {
"key": {
"description": "Fault domain key.",
"type": "string",
"default": "kubernetes.io/hostname"
},
"valueFrom": {
"description": "Fault domain value source.",
"type": "string",
"default": "spec.nodeName"
}
}
},
"processCounts": {
"description": "Process counts for different roles.",
"type": "object",
"default": {},
"required": [
"cluster_controller",
"stateless",
"storage"
],
"properties": {
"cluster_controller": {
"description": "Number of cluster controller processes.",
"type": "integer",
"default": 1
},
"stateless": {
"description": "Number of stateless processes (-1 for automatic).",
"type": "integer",
"default": -1
},
"storage": {
"description": "Number of storage processes (determines cluster size).",
"type": "integer",
"default": 3
}
}
},
"redundancyMode": {
"description": "Database redundancy mode (single, double, triple, three_datacenter, three_datacenter_fallback).",
"type": "string",
"default": "double"
},
"storageEngine": {
"description": "Storage engine (ssd-2, ssd-redwood-v1, ssd-rocksdb-v1, memory).",
"type": "string",
"default": "ssd-2"
},
"version": {
"description": "Version of FoundationDB to use.",
"type": "string",
"default": "7.3.63"
}
}
},
"customParameters": {
"description": "Custom parameters to pass to FoundationDB.",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"imageType": {
"description": "Container image deployment type.",
"type": "string",
"default": "unified",
"enum": [
"unified",
"split"
]
},
"monitoring": {
"description": "Monitoring configuration.",
"type": "object",
"default": {},
"required": [
"enabled"
],
"properties": {
"enabled": {
"description": "Enable WorkloadMonitor integration.",
"type": "boolean",
"default": true
}
}
},
"resources": {
"description": "Explicit CPU and memory configuration for each FoundationDB instance. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each instance.",
"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 instance.",
"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.",
"type": "string",
"default": "medium",
"enum": [
"small",
"medium",
"large",
"xlarge",
"2xlarge"
]
},
"securityContext": {
"description": "Security context for containers.",
"type": "object",
"default": {},
"required": [
"runAsGroup",
"runAsUser"
],
"properties": {
"runAsGroup": {
"description": "Group ID to run the container.",
"type": "integer",
"default": 4059
},
"runAsUser": {
"description": "User ID to run the container.",
"type": "integer",
"default": 4059
}
}
},
"storage": {
"description": "Storage configuration.",
"type": "object",
"default": {},
"required": [
"size",
"storageClass"
],
"properties": {
"size": {
"description": "Size of persistent volumes for each instance.",
"default": "16Gi",
"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": "Storage class (if not set, uses cluster default).",
"type": "string",
"default": ""
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
##
## @section Common parameters
##
## @typedef {struct} ClusterProcessCounts - Process counts for different roles.
## @field {int} stateless - Number of stateless processes (-1 for automatic).
## @field {int} storage - Number of storage processes (determines cluster size).
## @field {int} cluster_controller - Number of cluster controller processes.
## @typedef {struct} ClusterFaultDomain - Fault domain configuration.
## @field {string} key - Fault domain key.
## @field {string} valueFrom - Fault domain value source.
## @typedef {struct} Cluster - Cluster configuration.
## @field {ClusterProcessCounts} processCounts - Process counts for different roles.
## @field {string} version - Version of FoundationDB to use.
## @field {string} redundancyMode - Database redundancy mode (single, double, triple, three_datacenter, three_datacenter_fallback).
## @field {string} storageEngine - Storage engine (ssd-2, ssd-redwood-v1, ssd-rocksdb-v1, memory).
## @field {ClusterFaultDomain} faultDomain - Fault domain configuration.
## @param {Cluster} cluster - Cluster configuration.
cluster:
processCounts:
stateless: -1
storage: 3
cluster_controller: 1
version: "7.3.63"
redundancyMode: "double"
storageEngine: "ssd-2"
faultDomain:
key: "kubernetes.io/hostname"
valueFrom: "spec.nodeName"
## @typedef {struct} Storage - Storage configuration.
## @field {quantity} size - Size of persistent volumes for each instance.
## @field {string} storageClass - Storage class (if not set, uses cluster default).
## @param {Storage} storage - Storage configuration.
storage:
size: "16Gi"
storageClass: ""
## @typedef {struct} Resources - Explicit CPU and memory configuration for each FoundationDB instance.
## @field {quantity} [cpu] - CPU available to each instance.
## @field {quantity} [memory] - Memory (RAM) available to each instance.
## @enum {string} ResourcesPreset - Default sizing preset.
## @value small
## @value medium
## @value large
## @value xlarge
## @value 2xlarge
## @param {Resources} [resources] - Explicit CPU and memory configuration for each FoundationDB instance. When omitted, the preset defined in `resourcesPreset` is applied.
resources: {}
## @param {ResourcesPreset} resourcesPreset="medium" - Default sizing preset used when `resources` is omitted.
resourcesPreset: "medium"
## @typedef {struct} BackupS3Credentials - S3 credentials.
## @field {string} accessKeyId - S3 access key ID.
## @field {string} secretAccessKey - S3 secret access key.
## @typedef {struct} BackupS3 - S3 configuration for backups.
## @field {string} bucket - S3 bucket name.
## @field {string} endpoint - S3 endpoint URL.
## @field {string} region - S3 region.
## @field {BackupS3Credentials} credentials - S3 credentials.
## @typedef {struct} Backup - Backup configuration.
## @field {bool} enabled - Enable backups.
## @field {BackupS3} s3 - S3 configuration for backups.
## @field {string} retentionPolicy - Retention policy for backups.
## @param {Backup} backup - Backup configuration.
backup:
enabled: false
s3:
bucket: ""
endpoint: ""
region: "us-east-1"
credentials:
accessKeyId: ""
secretAccessKey: ""
retentionPolicy: "7d"
## @typedef {struct} Monitoring - Monitoring configuration.
## @field {bool} enabled - Enable WorkloadMonitor integration.
## @param {Monitoring} monitoring - Monitoring configuration.
monitoring:
enabled: true
##
## @section FoundationDB configuration
##
## @param {[]string} customParameters - Custom parameters to pass to FoundationDB.
customParameters: []
## @enum {string} ImageType - Container image deployment type.
## @value unified
## @value split
## @param {ImageType} imageType="unified" - Container image deployment type.
imageType: "unified"
## @typedef {struct} SecurityContext - Security context for containers.
## @field {int} runAsUser - User ID to run the container.
## @field {int} runAsGroup - Group ID to run the container.
## @param {SecurityContext} securityContext - Security context for containers.
securityContext:
runAsUser: 4059
runAsGroup: 4059
## @param {bool} automaticReplacements - Enable automatic pod replacements.
automaticReplacements: true

View File

@@ -2,24 +2,6 @@ apiVersion: v2
name: http-cache
description: Layer7 load balancer and caching service
icon: /logos/nginx.svg
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
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.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
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
appVersion: "1.25.3"

View File

@@ -18,6 +18,7 @@ image-nginx:
generate:
cozyvalues-gen -v values.yaml -s values.schema.json -r README.md
../../../hack/update-crd.sh
update:
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/chrislim2888/IP2Location-C-Library | awk -F'[/^]' 'END{print $$3}') && \

View File

@@ -62,40 +62,40 @@ The deployment architecture is illustrated in the diagram below:
| Name | Description | Type | Value |
| -------------- | ------------------------------------------------------------ | ---------- | ------- |
| `size` | Persistent Volume Claim size, available for application data | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data | `string` | `""` |
| `external` | Enable external access from outside the cluster | `bool` | `false` |
| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` |
| `storageClass` | StorageClass used to store the data. | `string` | `""` |
| `external` | Enable external access from outside the cluster. | `bool` | `false` |
### Application-specific parameters
| Name | Description | Type | Value |
| ----------- | ----------------------------------------------- | ---------- | ----- |
| `endpoints` | Endpoints configuration, as a list of <ip:port> | `[]string` | `[]` |
| Name | Description | Type | Value |
| ----------- | ------------------------------------------------ | ---------- | ----- |
| `endpoints` | Endpoints configuration, as a list of <ip:port>. | `[]string` | `[]` |
### HAProxy parameters
| Name | Description | Type | Value |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------ |
| `haproxy` | HAProxy configuration | `object` | `{}` |
| `haproxy.replicas` | Number of HAProxy replicas | `int` | `2` |
| `haproxy.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `haproxy.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
| `haproxy.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
| `haproxy.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
| Name | Description | Type | Value |
| -------------------------- | -------------------------------------------------------------------------------------------------------- | ---------- | ------ |
| `haproxy` | HAProxy configuration. | `object` | `{}` |
| `haproxy.replicas` | Number of HAProxy replicas. | `int` | `2` |
| `haproxy.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `haproxy.resources.cpu` | CPU available to each replica. | `quantity` | `""` |
| `haproxy.resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` |
| `haproxy.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` |
### Nginx parameters
| Name | Description | Type | Value |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------ |
| `nginx` | Nginx configuration | `object` | `{}` |
| `nginx.replicas` | Number of Nginx replicas | `int` | `2` |
| `nginx.resources` | Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `null` |
| `nginx.resources.cpu` | CPU available to each replica | `*quantity` | `null` |
| `nginx.resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
| `nginx.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `nano` |
| Name | Description | Type | Value |
| ------------------------ | -------------------------------------------------------------------------------------------------------- | ---------- | ------ |
| `nginx` | Nginx configuration. | `object` | `{}` |
| `nginx.replicas` | Number of Nginx replicas. | `int` | `2` |
| `nginx.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
| `nginx.resources.cpu` | CPU available to each replica. | `quantity` | `""` |
| `nginx.resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` |
| `nginx.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` |
## Parameter examples and reference

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/nginx-cache:0.7.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:50ac1581e3100bd6c477a71161cb455a341ffaf9e5e2f6086802e4e25271e8af

View File

@@ -1,39 +0,0 @@
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-haproxy
spec:
replicas: {{ .Values.haproxy.replicas }}
minReplicas: 1
kind: http-cache
type: http-cache
selector:
app: {{ $.Release.Name }}-haproxy
version: {{ $.Chart.Version }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}-nginx
spec:
replicas: {{ .Values.nginx.replicas }}
minReplicas: 1
kind: http-cache
type: http-cache
selector:
app: {{ $.Release.Name }}-nginx-cache
version: {{ $.Chart.Version }}
---
apiVersion: cozystack.io/v1alpha1
kind: WorkloadMonitor
metadata:
name: {{ $.Release.Name }}
spec:
replicas: {{ .Values.replicas }}
minReplicas: 1
kind: http-cache
type: http-cache
selector:
app.kubernetes.io/instance: {{ $.Release.Name }}
version: {{ $.Chart.Version }}

View File

@@ -3,7 +3,7 @@
"type": "object",
"properties": {
"endpoints": {
"description": "Endpoints configuration, as a list of \u003cip:port\u003e",
"description": "Endpoints configuration, as a list of \u003cip:port\u003e.",
"type": "array",
"default": [],
"items": {
@@ -11,36 +11,31 @@
}
},
"external": {
"description": "Enable external access from outside the cluster",
"description": "Enable external access from outside the cluster.",
"type": "boolean",
"default": false
},
"haproxy": {
"description": "HAProxy configuration",
"description": "HAProxy configuration.",
"type": "object",
"default": {
"replicas": 2,
"resources": {},
"resourcesPreset": "nano"
},
"default": {},
"required": [
"replicas",
"resources",
"resourcesPreset"
],
"properties": {
"replicas": {
"description": "Number of HAProxy replicas",
"description": "Number of HAProxy replicas.",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
"description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each replica",
"description": "CPU available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
@@ -53,7 +48,7 @@
"x-kubernetes-int-or-string": true
},
"memory": {
"description": "Memory (RAM) available to each replica",
"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": [
{
@@ -68,7 +63,7 @@
}
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
"description": "Default sizing preset used when `resources` is omitted.",
"type": "string",
"default": "nano",
"enum": [
@@ -84,30 +79,26 @@
}
},
"nginx": {
"description": "Nginx configuration",
"description": "Nginx configuration.",
"type": "object",
"default": {
"replicas": 2,
"resources": {},
"resourcesPreset": "nano"
},
"default": {},
"required": [
"replicas",
"resourcesPreset"
],
"properties": {
"replicas": {
"description": "Number of Nginx replicas",
"description": "Number of Nginx replicas.",
"type": "integer",
"default": 2
},
"resources": {
"description": "Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.",
"description": "Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.",
"type": "object",
"default": {},
"properties": {
"cpu": {
"description": "CPU available to each replica",
"description": "CPU available to each replica.",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
{
@@ -120,7 +111,7 @@
"x-kubernetes-int-or-string": true
},
"memory": {
"description": "Memory (RAM) available to each replica",
"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": [
{
@@ -135,7 +126,7 @@
}
},
"resourcesPreset": {
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
"description": "Default sizing preset used when `resources` is omitted.",
"type": "string",
"default": "nano",
"enum": [
@@ -151,7 +142,7 @@
}
},
"size": {
"description": "Persistent Volume Claim size, available for application data",
"description": "Persistent Volume Claim size available for application data.",
"default": "10Gi",
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"anyOf": [
@@ -165,8 +156,9 @@
"x-kubernetes-int-or-string": true
},
"storageClass": {
"description": "StorageClass used to store the data",
"type": "string"
"description": "StorageClass used to store the data.",
"type": "string",
"default": ""
}
}
}

View File

@@ -1,14 +1,22 @@
##
## @section Common parameters
##
## @param size {quantity} Persistent Volume Claim size, available for application data
size: 10Gi
## @param storageClass {string} StorageClass used to store the data
storageClass: ""
## @param external {bool} Enable external access from outside the cluster
external: false
## @section Application-specific parameters
## @param endpoints {[]string} Endpoints configuration, as a list of <ip:port>
## @param {quantity} size - Persistent Volume Claim size available for application data.
size: 10Gi
## @param {string} storageClass - StorageClass used to store the data.
storageClass: ""
## @param {bool} external - Enable external access from outside the cluster.
external: false
##
## @section Application-specific parameters
##
## @param {[]string} endpoints - Endpoints configuration, as a list of <ip:port>.
endpoints: []
## Example:
## endpoints:
## - 10.100.3.1:80
@@ -17,37 +25,46 @@ external: false
## - 10.100.3.12:80
## - 10.100.3.3:80
## - 10.100.3.13:80
##
endpoints: []
## @section HAProxy parameters
##
## @param haproxy {haproxy} HAProxy configuration
## @typedef {struct} Resources - Explicit CPU and memory configuration for each replica.
## @field {quantity} [cpu] - CPU available to each replica.
## @field {quantity} [memory] - Memory (RAM) available to each replica.
## @enum {string} ResourcesPreset - Default sizing preset.
## @value nano
## @value micro
## @value small
## @value medium
## @value large
## @value xlarge
## @value 2xlarge
## @typedef {struct} HAProxy - HAProxy configuration.
## @field {int} replicas - Number of HAProxy replicas.
## @field {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.
## @field {ResourcesPreset} resourcesPreset - Default sizing preset used when `resources` is omitted.
## @param {HAProxy} haproxy - HAProxy configuration.
haproxy:
## @field haproxy.replicas {int} Number of HAProxy replicas
replicas: 2
## @field haproxy.resources {resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
## @field resources.cpu {*quantity} CPU available to each replica
## @field resources.memory {*quantity} Memory (RAM) available to each replica
resources: {}
# resources:
# cpu: 4000m
# memory: 4Gi
## @field haproxy.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
resourcesPreset: "nano"
##
## @section Nginx parameters
##
## @param nginx {nginx} Nginx configuration
nginx:
## @field nginx.replicas {int} Number of Nginx replicas
replicas: 2
## @field nginx.resources {*resources} Explicit CPU and memory configuration for each replica. When left empty, the preset defined in `resourcesPreset` is applied.
# resources:
# cpu: 4000m
# memory: 4Gi
resources: {}
## @field nginx.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
## @typedef {struct} Nginx - Nginx configuration.
## @field {int} replicas - Number of Nginx replicas.
## @field {Resources} [resources] - Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied.
## @field {ResourcesPreset} resourcesPreset - Default sizing preset used when `resources` is omitted.
## @param {Nginx} nginx - Nginx configuration.
nginx:
replicas: 2
resources: {}
resourcesPreset: "nano"

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