Compare commits

..

68 Commits

Author SHA1 Message Date
Andrei Kvapil
8b97d87d90 Refactor and implement TenantSecret
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-05 16:34:36 +02:00
Andrei Kvapil
991e0479b9 filter namespaces by checking access to them
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-05 16:34:27 +02:00
Andrei Kvapil
7c5152963d [cozystack-api] Implement TenantNamespace resource
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-05 16:34:20 +02:00
Andrei Kvapil
cd80a73446 [dashboard] fix diff editor
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-05 12:54:47 +02:00
Timofei Larkin
c74df866e6 [kubernetes] Disable VPA for VPA in tenant k8s (#1301)
## What this PR does

This patch disables the VPA for VPA deployment in tenant kubernetes
clusters. This feature was never designed for deployment in tenant
clusters and causes unexpected errors.

### Release note

```release-note
[kubernetes] Disable VPA for VPA feature for tenant clusters, fixing an unintended regression.
```

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

* **New Features**
* Added a new configuration option to enable or disable vertical pod
autoscaling for the autoscaler itself.

* **Chores**
  * Updated the Kubernetes application chart version to 0.26.3.
  * Updated version mapping for the Kubernetes package.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-04 18:46:37 +04:00
Timofei Larkin
080289fa00 [kubernetes] Disable VPA for VPA in tenant k8s
This patch disables the VPA for VPA deployment in tenant kubernetes
clusters. This feature was never designed for deployment in tenant
clusters and causes unexpected errors.

[kubernetes] Disable VPA for VPA feature for tenant clusters, fixing an
unintended regression.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-08-04 16:47:48 +03:00
Timofei Larkin
98f86269f3 [virtual-machine] Disable instanceType validation (#1300)
## What this PR does

Workaround for #1299. If a Cozystack installation provides custom
instance types for virtual machines, the static validation rules prevent
such instance types from being used, as they are included in the OpenAPI
schema of the Cozystack API server and then once more applied in the
dependent HelmRelease, offering users no easy way to remedy this in
runtime.

### Release note

```release-note
[virtual-machine] Disable instanceType validation to enable using custom instance types.
```





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

* **New Features**
* Updated chart versions for virtual machine and VM instance
applications.
* **Refactor**
* Relaxed restrictions on instance type selection, allowing any string
value.
* **Chores**
* Updated version mappings for virtual machine and VM instance packages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-04 16:45:33 +04:00
Timofei Larkin
44fabd4abc [virtual-machine] Disable instanceType validation
Workaround for #1299. If a Cozystack installation provides custom
instance types for virtual machines, the static validation rules prevent
such instance types from being used, as they are included in the OpenAPI
schema of the Cozystack API server and then once more applied in the
dependent HelmRelease, offering users no easy way to remedy this in
runtime.

[virtual-machine] Disable instanceType validation to enable using custom
instance types.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-08-04 15:16:32 +03:00
Andrei Kvapil
8ddbe32ea1 Release v0.35.0-alpha.3 (#1295)
This PR prepares the release `v0.35.0-alpha.3`.

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

## Summary by CodeRabbit

* **Chores**
* Updated multiple container image tags and digests across various
components to newer versions, including core, monitoring, storage, and
dashboard services.
* Refreshed version references in configuration files to align with the
latest releases.
  * No changes to user-facing features or configuration options.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 19:01:19 +02:00
Andrei Kvapil
432ddf6abc [ci] Fix creating draft release
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-08-01 18:59:15 +02:00
cozystack-bot
9d184a098f Prepare release v0.35.0-alpha.3
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-08-01 13:07:01 +00:00
Andrei Kvapil
24807cb679 [cozystack-api] fix type for ApplicationList (#1290)
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

```
# kubectl get --raw /apis/apps.cozystack.io/v1alpha1/namespaces/tenant-whmcs/vminstances  | jq .
```

was showing:

```
{
  "apiVersion": "apps.cozystack.io/v1alpha1",
  "items": [],
  "kind": "BucketList",
  "metadata": {
    "resourceVersion": "123218712"
  }
}
```

now it shows:

```
{
  "apiVersion": "apps.cozystack.io/v1alpha1",
  "items": [],
  "kind": "VMInstanceList",
  "metadata": {
    "resourceVersion": "123218712"
  }
}
```

### 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] fix type for ApplicationList
```

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

* **Refactor**
* Improved how application lists are constructed and returned, using a
more flexible unstructured format for responses.
* Enhanced data handling to support new list formats for better
compatibility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 14:34:48 +02:00
Andrei Kvapil
cd8e8bee0a [dx] Allow to not specify BUILDER for makefile if PLATFORM specified (#1288)
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
[dx] Allow to not specify BUILDER for makefile if PLATFORM specified
```

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

## Summary by CodeRabbit

* **Refactor**
* Improved handling of the PLATFORM variable to ensure it is only set
when undefined, providing clearer and more predictable behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 12:53:41 +02:00
Andrei Kvapil
856720004f [seaweed] add tests for S3 buckets (#1283)
<!-- 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
Introduced automated end-to-end testing for SeaweedFS bucket creation
and verification in Kubernetes environments.

### 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
[seaweed] add tests for S3 buckets
```

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

## Summary by CodeRabbit

* **New Features**
* Introduced an end-to-end test for SeaweedFS Bucket resources,
including creation, credential verification, file upload, and cleanup.
* **Chores**
* Updated test scripts to include SeaweedFS in tenant configuration and
extended wait times for application readiness.
* Enhanced test environment by adding the MinIO client to the Docker
image for improved S3 compatibility testing.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 12:52:33 +02:00
Andrei Kvapil
d1ad5ff222 [monitoring] add seaweedfs monitoring (#1285)
<!-- 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 seaweedfs monitoring and grafana dashboard

### 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 seaweedfs monitoring and grafana dashboard
```

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

## Summary by CodeRabbit

* **New Features**
* Added a new SeaweedFS monitoring dashboard to the available monitoring
options.
* Enabled global monitoring configuration and enhanced SeaweedFS S3
service settings, including authentication and readiness probe.

* **Bug Fixes**
* Corrected how annotations are applied to the SeaweedFS volume service
monitor to ensure proper configuration inheritance.

* **Chores**
  * Updated monitoring package version to 1.12.1.
  * Adjusted version mapping for the monitoring package.
* Applied patch to fix volume service monitor configuration in SeaweedFS
setup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 12:50:55 +02:00
Andrei Kvapil
c81c9d255a dashboard auth-proxy enable cookie-secure (#1287)
<!-- 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

- dashboard auth-proxy enable cookie-secure

### 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 auth-proxy enable cookie-secure
```

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

* **Chores**
  * Updated Keycloak client redirect URI to use HTTPS instead of HTTP.
* Improved authentication security by adjusting cookie and SSL settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-08-01 12:50:24 +02:00
Andrei Kvapil
f057d92a4d [cozystack-api] fix type for ApplicationList
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-31 22:58:55 +02:00
klinch0
1ab63187c9 Update packages/system/keycloak-configure/templates/configure-kk.yaml
Co-authored-by: Timofei Larkin <lllamnyp@gmail.com>
Signed-off-by: klinch0 <68821526+klinch0@users.noreply.github.com>
2025-07-31 17:13:25 +03:00
klinch0
2fa56fc1e1 k8s make volumesnapshot crd name shorter (#1284)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

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

```release-note
- k8s make volumesnapshot crd name shorter
```

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

* **Chores**
  * Updated chart version for the Kubernetes application.
* Changed Helm chart and namespace references to use a new, shorter
name.
* Updated version mapping to reflect the latest Kubernetes package
version.
* Renamed the Helm chart for volume snapshot resources to a shorter
name.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-31 17:12:57 +03:00
IvanHunters
36ccfb9509 add limits for s3 deployment
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-31 11:30:35 +03:00
klinch0
cb3cb99d06 [keycloak] keep admin password in secret (#1286)
<!-- 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
keep admin password in secret

### 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
- keep admin password in secret
```
2025-07-31 10:35:45 +03:00
Andrei Kvapil
8704767ac5 [dx] Allow to not specify BUILDER for makefile if PLATFORM specified
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-30 16:46:07 +02:00
IvanHunters
03c4bf904f add handle of patch in makefile
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 17:38:50 +03:00
IvanHunters
dca2eb7ae8 fix chart version
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 17:08:54 +03:00
IvanHunters
1d9465d662 revert of metrics port for s3
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:59:11 +03:00
IvanHunters
53241efe63 fix values file
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:46:27 +03:00
IvanHunters
940b0b18b0 fix values for seadeed monitoring
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:39:14 +03:00
IvanHunters
824c72318a fix patches
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:31:24 +03:00
IvanHunters
0d7e856186 delete patch for seaweedfs
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:29:44 +03:00
IvanHunters
2897813dda revert bump version of seaweed in extra
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:26:01 +03:00
IvanHunters
e3a61b23af fix versions map
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:20:09 +03:00
kklinch0
7918e282bf keycloak enable cookie-secure
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-07-30 16:07:30 +03:00
IvanHunters
0e428810fd [fix] add patches
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 16:02:12 +03:00
IvanHunters
fa4fff2292 [monitoring] add seaweedfs monitoring
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 15:17:46 +03:00
IvanHunters
0e875b17d1 [keycloak] keep admin password in secret
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-30 14:06:57 +03:00
kklinch0
efb2c632e2 k8s make volumesnapshot crd name shorter
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-07-30 13:28:23 +03:00
Timofei Larkin
8951bc13d7 [cozystack-api] Configure dynamic api via custom resources (#1230)
## What this PR does

This patch introduces a new CRD to manage the configuration of the
Cozystack extension API server. Configuration previously done with a
single ConfigMap containing a list of objects is now decomposed into a
number of custom resources. Platform administrators receive a better UX
for defining their own custom Cozystack managed applications in addition
to the existing apps shipped with the default Cozystack installation.

### Release note

```release-note
[cozystack-api] Provide an API for administrators to define custom managed applications alongside existing managed apps.
```

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

## Summary by CodeRabbit

* **New Features**
* Introduced a new Kubernetes CustomResourceDefinition (CRD) for
managing resource definitions dynamically via the cluster.
* Added multiple resource definitions for various application types
using the new CRD.

* **Improvements**
* The API server now loads resource definitions dynamically from the
cluster instead of a static configuration file.
* Updated RBAC permissions to allow access to the new resource
definitions.

* **Removals**
* Removed the static ConfigMap-based resource configuration and related
file loading logic from the deployment and codebase.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-07-29 18:31:53 +04:00
kklinch0
830ec252b9 Scaffold CozyRD CRD
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-07-29 17:03:22 +03:00
IvanHunters
730584bd15 [seaweed] add tests
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-28 16:18:07 +03:00
Andrei Kvapil
0e47e1e8ac Release v0.35.0-alpha.2 (#1278)
This PR prepares the release `v0.35.0-alpha.2`.

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

* **Chores**
* Updated container image tags and digests across multiple components to
new versions, primarily moving from `v0.35.0-alpha.1` to
`v0.35.0-alpha.2`.
* Refreshed image digests for several services, ensuring the latest
builds are used.
  * Updated dashboard version display to reflect the new release.
* Incremented version tags for Kubernetes-related images and other
system components with corresponding digest updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-25 21:40:16 +02:00
cozystack-bot
9617071ada Prepare release v0.35.0-alpha.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-07-25 16:43:50 +00:00
Andrei Kvapil
3b32bfe149 Fix building Kubevirt CCM
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-25 17:49:11 +02:00
Andrei Kvapil
d9a5e9d628 Fix regression with optiona=true field
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-25 17:44:38 +02:00
Andrei Kvapil
0feeaadb9c [kubernetes] Add dependency for snapshot CRD and migration to latest version (#1275)
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
[kubernetes] Add dependency for snapshot CRD and migration to latest version
```

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

## Summary by CodeRabbit

* **New Features**
* Added a migration script to automatically update Kubernetes custom
resources to app version 0.26.1 and track migration status.
* **Bug Fixes**
* Improved HelmRelease dependency management by adding a required
dependency for volume snapshot CRDs.
* **Chores**
  * Updated Kubernetes app version to 0.26.1.
  * Refreshed version mapping to reflect the latest release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-25 17:11:06 +02:00
Andrei Kvapil
8fac3bfcb1 [seaweedfs] Client mode refactoring and fix issues (#1277)
- update cosi-driver
- add support exporting via nginx-ingress
- add support for whitelist

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] Client mode refactoring and fix issues

- update cosi-driver
- add support exporting via nginx-ingress
- add support for whitelist
```
2025-07-25 04:06:47 +02:00
Andrei Kvapil
b1e4ebeafc [seaweedfs] Client mode refactoring and fix issues
- update cosi-driver
- add support exporting via nginx-ingress
- add support for whitelist

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-25 04:06:11 +02:00
Timofei Larkin
2f61798fa8 [platform] Autodetect RobotLB (#1271)
If running in Hetzner and using Hetzner's cloud load balancers, node
ports need to be allocated for the load balancer to function correctly.
Therefore if RobotLB is enabled, we probably need to assign node ports.

Release note:
[platform] Autodetect if node ports should be assigned to load balancer
services.
2025-07-24 22:41:10 +04:00
Andrei Kvapil
02436f312f [kubernetes] Add dependency for snapshot CRD and migration to latest version
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-24 20:27:26 +02:00
Andrei Kvapil
68a47097c1 Release v0.35.0-alpha.1 (#1274)
This PR prepares the release `v0.35.0-alpha.1`.

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

## Summary by CodeRabbit

* **Chores**
* Updated multiple container image tags and digests across various
components to version v0.35.0-alpha.1, ensuring use of the latest
pre-release images.
* Switched some image references from generic or "latest" tags to
specific versioned tags for improved reproducibility.
* Updated version references in configuration files and dashboards to
reflect the new pre-release version.
* Applied minor formatting and whitespace cleanups in configuration
files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-24 19:10:46 +02:00
Timofei Larkin
917a6f354d [platform] Autodetect RobotLB
If running in Hetzner and using Hetzner's cloud load balancers, node
ports need to be allocated for the load balancer to function correctly.
Therefore if RobotLB is enabled, we probably need to assign node ports.

Release note:
[platform] Autodetect if node ports should be assigned to load balancer
services.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-07-24 18:55:30 +03:00
Andrei Kvapil
847a834920 [robotlb] fix chart name for installing (#1237)
<!-- 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
Rename of chart name for fixing installing issues

### 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
[robotlb] fix chart name for installing
```

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

## Summary by CodeRabbit

* **Chores**
  * Updated the Helm chart name to "cozy-hetzner-robotlb".

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-24 17:45:55 +02:00
cozystack-bot
3088e987e3 Prepare release v0.35.0-alpha.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-07-24 15:39:28 +00:00
Andrei Kvapil
fddeea03f0 [cozystack-api] show default values from openapi spec (#1241)
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
[cozystack-api] show default values from openapi spec
```

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

* **New Features**
* Application resources now automatically receive default values in
their specifications when converted from HelmRelease, ensuring more
complete and accurate configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-24 17:26:14 +02:00
Andrei Kvapil
2fefafd061 [seaweedfs] Add Client topology (#1239)
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] Add Client topology
```

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

* **New Features**
* Added support for a new "Client" topology mode in SeaweedFS, enabling
integration with remote filer endpoints.
* Introduced new configuration options: `filer.external` to allow
external filer access, and `remoteEndpoint` for specifying a remote
filer service when using "Client" topology.
* Added new Kubernetes resources (Deployment, ServiceAccount,
ClusterRole, ClusterRoleBinding, BucketClass, BucketAccessClass) for
object storage provisioner in "Client" mode.
  * Added a LoadBalancer service for external filer access when enabled.

* **Improvements**
* Enhanced configuration schema and documentation to reflect new
topology and parameters.
  * Updated role and access control for dashboard resources.
* Improved detection and validation of deployment topology, preventing
unsupported changes post-deployment.

* **Bug Fixes**
* Ensured VerticalPodAutoscaler resources are not created when using
"Client" topology.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-24 17:25:55 +02:00
Andrei Kvapil
084be87618 fix net pod policy (#1232)
<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

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

```release-note
- fix net pod policy
```

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

* **Chores**
  * Updated tenant application version to 1.11.2.
  * Updated version mapping to reflect the new release.

* **New Features**
* Extended network policy to allow traffic to additional tenant-related
services across namespace hierarchies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-07-24 17:25:41 +02:00
kklinch0
6598213b58 fix net pod policy
Signed-off-by: kklinch0 <kklinch0@gmail.com>
2025-07-24 17:28:10 +03:00
Andrei Kvapil
4079a69335 [seaweedfs] Add Client topology
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-24 15:59:14 +02:00
Andrei Kvapil
553c2d5482 [cozystack-api] show default values from openapi spec
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-24 15:42:05 +02:00
Andrei Kvapil
0c9ab17a12 Fix recording image for objectstorage
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-24 15:19:27 +02:00
Andrei Kvapil
5e8f6e0503 [cosi] fix building objectstorage images
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-24 01:24:13 +02:00
Andrei Kvapil
f04cd55f2a [kubernetes] fix volumesnapshotclass installation (#1238)
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

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

## What this PR does

This PR fixes regression introduced by
https://github.com/cozystack/cozystack/pull/1203

error:
```
Helm install failed for release cozy-volumesnapshot-crd-for-tenant-k8s/volumesnapshot-crd-for-tenant-k8s with chart cozy-volumesnapshot-crd-for-tenant-k8s@0.34.0: unable to build kubernetes objects from release manifest: resource mapping not found for name: "kubevirt-snapshots" namespace: "" from "": no matches for kind "VolumeSnapshotClass" in version "snapshot.storage.k8s.io/v1"...
```

### 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
[kubernetes] fix volumesnapshotclass installation
```
2025-07-24 01:20:25 +02:00
Andrei Kvapil
53d9cf365d [kubernetes] fix volumesnapshotclass installation
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2025-07-23 17:28:28 +02:00
Timofei Larkin
94e2fd0ff9 [ci] Refactor testing logic (#1236)
* Simplify test discovery logic in workflow.
* Delete Clickhouse after successful test.
* Separate two k8s tests into separate jobs.
2025-07-23 18:33:05 +04:00
IvanHunters
0618446b95 fix chart name
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-23 14:45:25 +03:00
Timofei Larkin
640d0f10ac [ci] Refactor testing logic
* Simplify test discovery logic in workflow.
* Delete Clickhouse after successful test.
* Separate two k8s tests into separate jobs.

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2025-07-23 14:00:24 +03:00
Timofei Larkin
a03530a72f [robotlb] add Hetzner Robotlb balancer (#1233)
[robotlb] Add support for Hetzner load balancers

Co-authored-by: Ahmad Murzahmatov <gwynbleidd2106@yandex.com>
2025-07-23 14:55:57 +04:00
IvanHunters
3612bbd8ca [fix] add robotlb to bundles
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2025-07-23 13:26:36 +03:00
IvanHunters
028bb365ff [lb] add hetzner robotlb balancer
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
Co-authored-by: Ahmad Murzahmatov <gwynbleidd2106@yandex.com>
2025-07-23 12:34:57 +03:00
101 changed files with 6505 additions and 595 deletions

View File

@@ -264,8 +264,7 @@ jobs:
- uses: actions/checkout@v4
- id: set
run: |
apps=$(find hack/e2e-apps -maxdepth 1 -mindepth 1 -name '*.bats' | \
awk -F/ '{sub(/\..+/, "", $NF); print $NF}' | jq -R . | jq -cs .)
apps=$(ls hack/e2e-apps/*.bats | cut -f3 -d/ | cut -f1 -d. | jq -R | jq -cs)
echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT"
test_apps:

2
.gitignore vendored
View File

@@ -77,3 +77,5 @@ fabric.properties
.DS_Store
**/.DS_Store
tmp/

View File

@@ -0,0 +1,89 @@
/*
Copyright 2025.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +kubebuilder:object:root=true
// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API
type CozystackResourceDefinition struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CozystackResourceDefinitionSpec `json:"spec,omitempty"`
}
// +kubebuilder:object:root=true
// CozystackResourceDefinitionList contains a list of CozystackResourceDefinition
type CozystackResourceDefinitionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CozystackResourceDefinition `json:"items"`
}
func init() {
SchemeBuilder.Register(&CozystackResourceDefinition{}, &CozystackResourceDefinitionList{})
}
type CozystackResourceDefinitionSpec struct {
// Application configuration
Application CozystackResourceDefinitionApplication `json:"application"`
// Release configuration
Release CozystackResourceDefinitionRelease `json:"release"`
}
type CozystackResourceDefinitionChart struct {
// Name of the Helm chart
Name string `json:"name"`
// Source reference for the Helm chart
SourceRef SourceRef `json:"sourceRef"`
}
type SourceRef struct {
// Kind of the source reference
// +kubebuilder:default:="HelmRepository"
Kind string `json:"kind"`
// Name of the source reference
Name string `json:"name"`
// Namespace of the source reference
// +kubebuilder:default:="cozy-public"
Namespace string `json:"namespace"`
}
type CozystackResourceDefinitionApplication struct {
// Kind of the application, used for UI and API
Kind string `json:"kind"`
// OpenAPI schema for the application, used for API validation
OpenAPISchema string `json:"openAPISchema"`
// Plural name of the application, used for UI and API
Plural string `json:"plural"`
// Singular name of the application, used for UI and API
Singular string `json:"singular"`
}
type CozystackResourceDefinitionRelease struct {
// Helm chart configuration
Chart CozystackResourceDefinitionChart `json:"chart"`
// Labels for the release
Labels map[string]string `json:"labels,omitempty"`
// Prefix for the release name
Prefix string `json:"prefix"`
}

View File

@@ -25,6 +25,135 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinition.
func (in *CozystackResourceDefinition) DeepCopy() *CozystackResourceDefinition {
if in == nil {
return nil
}
out := new(CozystackResourceDefinition)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CozystackResourceDefinition) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionApplication) DeepCopyInto(out *CozystackResourceDefinitionApplication) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionApplication.
func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceDefinitionApplication {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionApplication)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionChart) DeepCopyInto(out *CozystackResourceDefinitionChart) {
*out = *in
out.SourceRef = in.SourceRef
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionChart.
func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinitionChart {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionChart)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CozystackResourceDefinition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionList.
func (in *CozystackResourceDefinitionList) DeepCopy() *CozystackResourceDefinitionList {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) {
*out = *in
out.Chart = in.Chart
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionRelease.
func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefinitionRelease {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionRelease)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) {
*out = *in
out.Application = in.Application
in.Release.DeepCopyInto(&out.Release)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.
func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefinitionSpec {
if in == nil {
return nil
}
out := new(CozystackResourceDefinitionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in Selector) DeepCopyInto(out *Selector) {
{
@@ -46,6 +175,21 @@ func (in Selector) DeepCopy() Selector {
return *out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SourceRef) DeepCopyInto(out *SourceRef) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceRef.
func (in *SourceRef) DeepCopy() *SourceRef {
if in == nil {
return nil
}
out := new(SourceRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Workload) DeepCopyInto(out *Workload) {
*out = *in

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

File diff suppressed because it is too large Load Diff

View File

@@ -81,6 +81,7 @@ modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//main/capacity-p
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//flux/flux-control-plane.json
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//flux/flux-stats.json
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//kafka/strimzi-kafka.json
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//seaweedfs/seaweedfs.json
modules/340-monitoring-kubernetes/monitoring/grafana-dashboards//goldpinger/goldpinger.json
EOT

View File

@@ -38,4 +38,5 @@ EOF
timeout 100 sh -ec "until kubectl -n tenant-test get svc chi-clickhouse-$name-clickhouse-0-0 -o jsonpath='{.spec.ports[*].port}' | grep -q '9000 8123 9009'; do sleep 10; done"
timeout 80 sh -ec "until kubectl -n tenant-test get sts chi-clickhouse-$name-clickhouse-0-1 ; do sleep 10; done"
kubectl -n tenant-test wait statefulset.apps/chi-clickhouse-$name-clickhouse-0-1 --timeout=140s --for=jsonpath='{.status.replicas}'=1
kubectl -n tenant-test delete clickhouse $name
}

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bats
@test "Create a tenant Kubernetes control plane with latest version" {
. hack/e2e-apps/run-kubernetes.sh
run_kubernetes_test 'keys | sort_by(.) | .[-1]' 'test-latest-version' '59991'
}

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bats
@test "Create a tenant Kubernetes control plane with previous version" {
. hack/e2e-apps/run-kubernetes.sh
run_kubernetes_test 'keys | sort_by(.) | .[-2]' 'test-previous-version' '59992'
}

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env bats
run_kubernetes_test() {
local version_expr="$1"
local test_name="$2"
@@ -104,10 +102,3 @@ EOF
kubectl -n tenant-test delete kuberneteses.apps.cozystack.io $test_name
}
@test "Create a tenant Kubernetes control plane with latest version" {
run_kubernetes_test 'keys | sort_by(.) | .[-1]' 'test-latest-version' '59991'
}
@test "Create a tenant Kubernetes control plane with previous version" {
run_kubernetes_test 'keys | sort_by(.) | .[-2]' 'test-previous-version' '59992'
}

View File

@@ -32,6 +32,10 @@ kube::codegen::gen_helpers \
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
"${SCRIPT_ROOT}/pkg/apis"
kube::codegen::gen_helpers \
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
"${SCRIPT_ROOT}/api"
if [[ -n "${API_KNOWN_VIOLATIONS_DIR:-}" ]]; then
report_filename="${API_KNOWN_VIOLATIONS_DIR}/cozystack_api_violation_exceptions.list"
if [[ "${UPDATE_API_KNOWN_VIOLATIONS:-}" == "true" ]]; then

View File

@@ -8,7 +8,9 @@ spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- end }}
ports:
- name: ferretdb

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/nginx-cache:0.6.1@sha256:50ac1581e3100bd6c477a71161cb455a341ffaf9e5e2f6086802e4e25271e8af
ghcr.io/cozystack/cozystack/nginx-cache:0.6.1@sha256:b7633717cd7449c0042ae92d8ca9b36e4d69566561f5c7d44e21058e7d05c6d5

View File

@@ -10,7 +10,9 @@ spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- end }}
selector:
app: {{ .Release.Name }}-haproxy

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.26.3@sha256:e4fbb7d2043f25b90cc8840468d0880e9d3d72ae8b1c8801bf8c35f944cc485d
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.26.2@sha256:3a8170433e1632e5cc2b6d9db34d0605e8e6c63c158282c38450415e700e932e

View File

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

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.26.3@sha256:df3a2f503b4a035567b20b81a0f105c15971274fd675101c3b3eb2413d966d2e
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.26.2@sha256:761e7235ff9cb7f6f223f00954943e6a5af32ed6624ee592a8610122f96febb0

View File

@@ -3,8 +3,8 @@
{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }}
{{- $myNS := lookup "v1" "Namespace" "" .Release.Namespace }}
{{- $targetTenant := index $myNS.metadata.annotations "namespace.cozystack.io/monitoring" }}
vpaForVPA: false
vertical-pod-autoscaler:
vpaForVPA: false
recommender:
extraArgs:
container-name-label: container

View File

@@ -7,7 +7,9 @@ spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- end }}
ports:
- name: postgres

View File

@@ -11,7 +11,9 @@ spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- end }}
selector:
app.kubernetes.io/component: redis

View File

@@ -10,7 +10,9 @@ spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- end }}
selector:
app: {{ .Release.Name }}-haproxy

View File

@@ -39,6 +39,11 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantsecrets
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
@@ -188,6 +193,11 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantsecrets
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -279,6 +289,11 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantsecrets
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -346,6 +361,11 @@ rules:
resources:
- workloadmonitors
verbs: ["get", "list", "watch"]
- apiGroups:
- core.cozystack.io
resources:
- tenantsecrets
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -13,7 +13,9 @@ metadata:
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
selector:
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
ports:

View File

@@ -13,7 +13,9 @@ metadata:
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
externalTrafficPolicy: Local
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
selector:
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
ports:

View File

@@ -1,2 +1,2 @@
cozystack:
image: ghcr.io/cozystack/cozystack/installer:v0.34.8@sha256:c9dae64d1cbb3882749aae3af47e63152a835cb603af12ba573618c0d0940437
image: ghcr.io/cozystack/cozystack/installer:v0.35.0-alpha.3@sha256:b43615350697dbbc377d31152b290722c2bba7a053fd7c69403d666e6c7d53ee

View File

@@ -258,3 +258,10 @@ releases:
privileged: true
optional: true
dependsOn: [cilium]
- name: hetzner-robotlb
releaseName: robotlb
optional: true
chart: cozy-hetzner-robotlb
namespace: cozy-hetzner-robotlb
dependsOn: [cilium]

View File

@@ -171,3 +171,9 @@ releases:
namespace: cozy-velero
privileged: true
optional: true
- name: hetzner-robotlb
releaseName: robotlb
optional: true
chart: cozy-hetzner-robotlb
namespace: cozy-hetzner-robotlb

View File

@@ -415,3 +415,10 @@ releases:
privileged: true
optional: true
dependsOn: [monitoring-agents]
- name: hetzner-robotlb
releaseName: robotlb
optional: true
chart: cozy-hetzner-robotlb
namespace: cozy-hetzner-robotlb
dependsOn: [cilium, kubeovn]

View File

@@ -238,3 +238,9 @@ releases:
privileged: true
optional: true
dependsOn: [monitoring-agents]
- name: hetzner-robotlb
releaseName: robotlb
optional: true
chart: cozy-hetzner-robotlb
namespace: cozy-hetzner-robotlb

View File

@@ -1,2 +1,2 @@
e2e:
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.34.8@sha256:f88293295f95419e0ec5484430ae3b46c58dc07050ccb12104b7b0e7902b1712
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.35.0-alpha.3@sha256:cd6d65230150171c0e3934f71fcc270718f2bfec7509989b9d4d996ddef916d9

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/matchbox:v0.34.8@sha256:9280492dad668db1aeb678e1d5d5cf29a86ada26776fbcbe36bbfa1fc1e9d311
ghcr.io/cozystack/cozystack/matchbox:v0.35.0-alpha.3@sha256:043051d7c368b73943051b51bacfb3e383541b53190f74452d552ea2f30e420e

View File

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

View File

@@ -49,14 +49,6 @@ spec:
{{- with .Values.resources }}
resources: {{- include "cozy-lib.resources.sanitize" (list . $) | nindent 10 }}
{{- end }}
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }}
{{- $rawConstraints := "" }}
{{- if $configMap }}
{{- $rawConstraints = get $configMap.data "globalAppTopologySpreadConstraints" }}
{{- end }}
{{- if $rawConstraints }}
{{- $rawConstraints | fromYaml | toYaml | nindent 6 }}
{{- else }}
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "kubernetes.io/hostname"
@@ -64,7 +56,6 @@ spec:
labelSelector:
matchLabels:
app.kubernetes.io/instance: etcd
{{- end }}
podDisruptionBudgetTemplate: {}
---
apiVersion: cert-manager.io/v1

View File

@@ -3,4 +3,4 @@ name: monitoring
description: Monitoring and observability stack
icon: /logos/monitoring.svg
type: application
version: 1.12.0
version: 1.12.1

View File

@@ -37,4 +37,5 @@ flux/flux-stats
kafka/strimzi-kafka
goldpinger/goldpinger
clickhouse/altinity-clickhouse-operator-dashboard
storage/linstor
storage/linstor
seaweedfs/seaweedfs

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/grafana:1.12.0@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399
ghcr.io/cozystack/cozystack/grafana:1.12.1@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.35.0-alpha.1@sha256:e4f9a7302285ea9febeb28fc2fa97cb7c01bb91e602f975c31aad1fe46f778f7
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.35.0-alpha.3@sha256:4a67ec40c13bf912151957dc0e7409f7e01a2859905285c98c20baae7b667ae2

View File

@@ -110,7 +110,6 @@ spec:
{{- else if $.Values.storageClass }}
storageClass: {{ $.Values.storageClass }}
{{- end }}
maxVolumes: 0
nodeSelector: |
topology.kubernetes.io/zone: {{ $zoneName }}
dataCenter: {{ $zone.dataCenter | default $zoneName }}

View File

@@ -13,8 +13,7 @@ etcd 2.6.0 8c460528
etcd 2.6.1 45a7416c
etcd 2.7.0 632224a3
etcd 2.8.0 4369b031
etcd 2.9.0 8ddbe32e
etcd 2.9.1 HEAD
etcd 2.9.0 HEAD
info 1.0.0 93bdf411
info 1.0.1 632224a3
info 1.1.0 HEAD
@@ -48,7 +47,8 @@ monitoring 1.9.2 f9f8bb2f
monitoring 1.10.0 632224a3
monitoring 1.10.1 8c86905b
monitoring 1.11.0 4369b031
monitoring 1.12.0 HEAD
monitoring 1.12.0 0e47e1e8
monitoring 1.12.1 HEAD
seaweedfs 0.1.0 71514249
seaweedfs 0.2.0 5fb9cfe3
seaweedfs 0.2.1 fde4bcfa

View File

@@ -0,0 +1,23 @@
{{- define "cozy-lib.network.defaultDisableLoadBalancerNodePorts" }}
{{/* Default behavior prior to introduction */}}
{{- `true` }}
{{- end }}
{{/*
Invoke as {{ include "cozy-lib.network.disableLoadBalancerNodePorts" $ }}.
Detects whether the current load balancer class requires nodeports to function
correctly. Currently just checks if Hetzner's RobotLB is enabled, which does
require nodeports, and so, returns `false`. Otherwise assumes that metallb is
in use and returns `true`.
*/}}
{{- define "cozy-lib.network.disableLoadBalancerNodePorts" }}
{{- include "cozy-lib.loadCozyConfig" (list "" .) }}
{{- $cozyConfig := index . 1 "cozyConfig" }}
{{- if not $cozyConfig }}
{{- include "cozy-lib.network.defaultDisableLoadBalancerNodePorts" . }}
{{- else }}
{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }}
{{- not (has "robotlb" $enabledComponents) }}
{{- end }}
{{- end }}

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:a9c0a5d5519b2da200d7d035fe30087ea4834fc8013a482848c160610b5b9716
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:b39a00543d28cb72617bc50c7a9f873d65c3cd34114d9690cf044f7fac66c8da

View File

@@ -11,3 +11,17 @@ spec:
name: cozystack-api
namespace: cozy-system
version: v1alpha1
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1alpha1.core.cozystack.io
spec:
insecureSkipTLSVerify: true
group: core.cozystack.io
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: cozystack-api
namespace: cozy-system
version: v1alpha1

View File

@@ -1,353 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cozystack-api
namespace: cozy-system
data:
config.yaml: |
resources:
- application:
kind: Bucket
singular: bucket
plural: buckets
openAPISchema: {{ .Files.Get "openapi-schemas/bucket.json" | fromJson | toJson | quote }}
release:
prefix: bucket-
labels:
cozystack.io/ui: "true"
chart:
name: bucket
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: ClickHouse
singular: clickhouse
plural: clickhouses
openAPISchema: {{ .Files.Get "openapi-schemas/clickhouse.json" | fromJson | toJson | quote }}
release:
prefix: clickhouse-
labels:
cozystack.io/ui: "true"
chart:
name: clickhouse
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: HTTPCache
singular: httpcache
plural: httpcaches
openAPISchema: {{ .Files.Get "openapi-schemas/http-cache.json" | fromJson | toJson | quote }}
release:
prefix: http-cache-
labels:
cozystack.io/ui: "true"
chart:
name: http-cache
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: NATS
singular: nats
plural: natses
openAPISchema: {{ .Files.Get "openapi-schemas/nats.json" | fromJson | toJson | quote }}
release:
prefix: nats-
labels:
cozystack.io/ui: "true"
chart:
name: nats
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: TCPBalancer
singular: tcpbalancer
plural: tcpbalancers
openAPISchema: {{ .Files.Get "openapi-schemas/tcp-balancer.json" | fromJson | toJson | quote }}
release:
prefix: tcp-balancer-
labels:
cozystack.io/ui: "true"
chart:
name: tcp-balancer
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: VirtualMachine
singular: virtualmachine
plural: virtualmachines
openAPISchema: {{ .Files.Get "openapi-schemas/virtual-machine.json" | fromJson | toJson | quote }}
release:
prefix: virtual-machine-
labels:
cozystack.io/ui: "true"
chart:
name: virtual-machine
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: VPN
singular: vpn
plural: vpns
openAPISchema: {{ .Files.Get "openapi-schemas/vpn.json" | fromJson | toJson | quote }}
release:
prefix: vpn-
labels:
cozystack.io/ui: "true"
chart:
name: vpn
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: MySQL
singular: mysql
plural: mysqls
openAPISchema: {{ .Files.Get "openapi-schemas/mysql.json" | fromJson | toJson | quote }}
release:
prefix: mysql-
labels:
cozystack.io/ui: "true"
chart:
name: mysql
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Tenant
singular: tenant
plural: tenants
openAPISchema: {{ .Files.Get "openapi-schemas/tenant.json" | fromJson | toJson | quote }}
release:
prefix: tenant-
labels:
cozystack.io/ui: "true"
chart:
name: tenant
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Kubernetes
singular: kubernetes
plural: kuberneteses
openAPISchema: {{ .Files.Get "openapi-schemas/kubernetes.json" | fromJson | toJson | quote }}
release:
prefix: kubernetes-
labels:
cozystack.io/ui: "true"
chart:
name: kubernetes
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Redis
singular: redis
plural: redises
openAPISchema: {{ .Files.Get "openapi-schemas/redis.json" | fromJson | toJson | quote }}
release:
prefix: redis-
labels:
cozystack.io/ui: "true"
chart:
name: redis
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: RabbitMQ
singular: rabbitmq
plural: rabbitmqs
openAPISchema: {{ .Files.Get "openapi-schemas/rabbitmq.json" | fromJson | toJson | quote }}
release:
prefix: rabbitmq-
labels:
cozystack.io/ui: "true"
chart:
name: rabbitmq
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Postgres
singular: postgres
plural: postgreses
openAPISchema: {{ .Files.Get "openapi-schemas/postgres.json" | fromJson | toJson | quote }}
release:
prefix: postgres-
labels:
cozystack.io/ui: "true"
chart:
name: postgres
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: FerretDB
singular: ferretdb
plural: ferretdb
openAPISchema: {{ .Files.Get "openapi-schemas/ferretdb.json" | fromJson | toJson | quote }}
release:
prefix: ferretdb-
labels:
cozystack.io/ui: "true"
chart:
name: ferretdb
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Kafka
singular: kafka
plural: kafkas
openAPISchema: {{ .Files.Get "openapi-schemas/kafka.json" | fromJson | toJson | quote }}
release:
prefix: kafka-
labels:
cozystack.io/ui: "true"
chart:
name: kafka
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: VMDisk
plural: vmdisks
singular: vmdisk
openAPISchema: {{ .Files.Get "openapi-schemas/vm-disk.json" | fromJson | toJson | quote }}
release:
prefix: vm-disk-
labels:
cozystack.io/ui: "true"
chart:
name: vm-disk
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: VMInstance
plural: vminstances
singular: vminstance
openAPISchema: {{ .Files.Get "openapi-schemas/vm-instance.json" | fromJson | toJson | quote }}
release:
prefix: vm-instance-
labels:
cozystack.io/ui: "true"
chart:
name: vm-instance
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
- application:
kind: Monitoring
plural: monitorings
singular: monitoring
openAPISchema: {{ .Files.Get "openapi-schemas/monitoring.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: monitoring
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
- application:
kind: Etcd
plural: etcds
singular: etcd
openAPISchema: {{ .Files.Get "openapi-schemas/etcd.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: etcd
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
- application:
kind: Ingress
plural: ingresses
singular: ingress
openAPISchema: {{ .Files.Get "openapi-schemas/ingress.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: ingress
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
- application:
kind: SeaweedFS
plural: seaweedfses
singular: seaweedfs
openAPISchema: {{ .Files.Get "openapi-schemas/seaweedfs.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: seaweedfs
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
- application:
kind: BootBox
plural: bootboxes
singular: bootbox
openAPISchema: {{ .Files.Get "openapi-schemas/bootbox.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: bootbox
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
- application:
kind: Info
plural: infos
singular: info
openAPISchema: {{ .Files.Get "openapi-schemas/info.json" | fromJson | toJson | quote }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: info
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public

View File

@@ -0,0 +1,505 @@
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: bucket
spec:
application:
kind: Bucket
singular: bucket
plural: buckets
openAPISchema: |
{{- .Files.Get "openapi-schemas/bucket.json" | fromJson | toJson | nindent 6 }}
release:
prefix: bucket-
labels:
cozystack.io/ui: "true"
chart:
name: bucket
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: clickhouse
spec:
application:
kind: ClickHouse
singular: clickhouse
plural: clickhouses
openAPISchema: |
{{- .Files.Get "openapi-schemas/clickhouse.json" | fromJson | toJson | nindent 6 }}
release:
prefix: clickhouse-
labels:
cozystack.io/ui: "true"
chart:
name: clickhouse
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: httpcache
spec:
application:
kind: HTTPCache
singular: httpcache
plural: httpcaches
openAPISchema: |
{{- .Files.Get "openapi-schemas/http-cache.json" | fromJson | toJson | nindent 6 }}
release:
prefix: http-cache-
labels:
cozystack.io/ui: "true"
chart:
name: http-cache
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: nats
spec:
application:
kind: NATS
singular: nats
plural: natses
openAPISchema: |
{{- .Files.Get "openapi-schemas/nats.json" | fromJson | toJson | nindent 6 }}
release:
prefix: nats-
labels:
cozystack.io/ui: "true"
chart:
name: nats
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: tcpbalancer
spec:
application:
kind: TCPBalancer
singular: tcpbalancer
plural: tcpbalancers
openAPISchema: |
{{- .Files.Get "openapi-schemas/tcp-balancer.json" | fromJson | toJson | nindent 6 }}
release:
prefix: tcp-balancer-
labels:
cozystack.io/ui: "true"
chart:
name: tcp-balancer
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: virtualmachine
spec:
application:
kind: VirtualMachine
singular: virtualmachine
plural: virtualmachines
openAPISchema: |
{{- .Files.Get "openapi-schemas/virtual-machine.json" | fromJson | toJson | nindent 6 }}
release:
prefix: virtual-machine-
labels:
cozystack.io/ui: "true"
chart:
name: virtual-machine
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vpn
spec:
application:
kind: VPN
singular: vpn
plural: vpns
openAPISchema: |
{{- .Files.Get "openapi-schemas/vpn.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vpn-
labels:
cozystack.io/ui: "true"
chart:
name: vpn
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: mysql
spec:
application:
kind: MySQL
singular: mysql
plural: mysqls
openAPISchema: |
{{- .Files.Get "openapi-schemas/mysql.json" | fromJson | toJson | nindent 6 }}
release:
prefix: mysql-
labels:
cozystack.io/ui: "true"
chart:
name: mysql
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: tenant
spec:
application:
kind: Tenant
singular: tenant
plural: tenants
openAPISchema: |
{{- .Files.Get "openapi-schemas/tenant.json" | fromJson | toJson | nindent 6 }}
release:
prefix: tenant-
labels:
cozystack.io/ui: "true"
chart:
name: tenant
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: kubernetes
spec:
application:
kind: Kubernetes
singular: kubernetes
plural: kuberneteses
openAPISchema: |
{{- .Files.Get "openapi-schemas/kubernetes.json" | fromJson | toJson | nindent 6 }}
release:
prefix: kubernetes-
labels:
cozystack.io/ui: "true"
chart:
name: kubernetes
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: redis
spec:
application:
kind: Redis
singular: redis
plural: redises
openAPISchema: |
{{- .Files.Get "openapi-schemas/redis.json" | fromJson | toJson | nindent 6 }}
release:
prefix: redis-
labels:
cozystack.io/ui: "true"
chart:
name: redis
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: rabbitmq
spec:
application:
kind: RabbitMQ
singular: rabbitmq
plural: rabbitmqs
openAPISchema: |
{{- .Files.Get "openapi-schemas/rabbitmq.json" | fromJson | toJson | nindent 6 }}
release:
prefix: rabbitmq-
labels:
cozystack.io/ui: "true"
chart:
name: rabbitmq
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: postgres
spec:
application:
kind: Postgres
singular: postgres
plural: postgreses
openAPISchema: |
{{- .Files.Get "openapi-schemas/postgres.json" | fromJson | toJson | nindent 6 }}
release:
prefix: postgres-
labels:
cozystack.io/ui: "true"
chart:
name: postgres
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ferretdb
spec:
application:
kind: FerretDB
singular: ferretdb
plural: ferretdbs
openAPISchema: |
{{- .Files.Get "openapi-schemas/ferretdb.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ferretdb-
labels:
cozystack.io/ui: "true"
chart:
name: ferretdb
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: kafka
spec:
application:
kind: Kafka
singular: kafka
plural: kafkas
openAPISchema: |
{{- .Files.Get "openapi-schemas/kafka.json" | fromJson | toJson | nindent 6 }}
release:
prefix: kafka-
labels:
cozystack.io/ui: "true"
chart:
name: kafka
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vmdisk
spec:
application:
kind: VMDisk
singular: vmdisk
plural: vmdisks
openAPISchema: |
{{- .Files.Get "openapi-schemas/vm-disk.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vm-disk-
labels:
cozystack.io/ui: "true"
chart:
name: vm-disk
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: vminstance
spec:
application:
kind: VMInstance
singular: vminstance
plural: vminstances
openAPISchema: |
{{- .Files.Get "openapi-schemas/vm-instance.json" | fromJson | toJson | nindent 6 }}
release:
prefix: vm-instance-
labels:
cozystack.io/ui: "true"
chart:
name: vm-instance
sourceRef:
kind: HelmRepository
name: cozystack-apps
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: monitoring
spec:
application:
kind: Monitoring
singular: monitoring
plural: monitorings
openAPISchema: |
{{- .Files.Get "openapi-schemas/monitoring.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: monitoring
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: etcd
spec:
application:
kind: Etcd
singular: etcd
plural: etcds
openAPISchema: |
{{- .Files.Get "openapi-schemas/etcd.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: etcd
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: ingress
spec:
application:
kind: Ingress
singular: ingress
plural: ingresses
openAPISchema: |
{{- .Files.Get "openapi-schemas/ingress.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: ingress
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: seaweedfs
spec:
application:
kind: SeaweedFS
singular: seaweedfs
plural: seaweedfses
openAPISchema: |
{{- .Files.Get "openapi-schemas/seaweedfs.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: seaweedfs
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: bootbox
spec:
application:
kind: BootBox
singular: bootbox
plural: bootboxes
openAPISchema: |
{{- .Files.Get "openapi-schemas/bootbox.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: bootbox
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public
---
apiVersion: cozystack.io/v1alpha1
kind: CozystackResourceDefinition
metadata:
name: info
spec:
application:
kind: Info
singular: info
plural: infos
openAPISchema: |
{{- .Files.Get "openapi-schemas/info.json" | fromJson | toJson | nindent 6 }}
release:
prefix: ""
labels:
cozystack.io/ui: "true"
chart:
name: info
sourceRef:
kind: HelmRepository
name: cozystack-extra
namespace: cozy-public

View File

@@ -14,22 +14,8 @@ spec:
metadata:
labels:
app: cozystack-api
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
spec:
serviceAccountName: cozystack-api
containers:
- name: cozystack-api
image: "{{ .Values.cozystackAPI.image }}"
args: ["--config=/config/config.yaml"]
volumeMounts:
- name: config-volume
mountPath: /config/config.yaml
subPath: config.yaml
volumes:
- name: config-volume
configMap:
name: cozystack-api
items:
- key: config.yaml
path: config.yaml

View File

@@ -4,14 +4,17 @@ metadata:
name: cozystack-api
rules:
- apiGroups: [""]
resources: ["namespaces"]
resources: ["namespaces", "secrets"]
verbs: ["get", "watch", "list"]
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations", "validatingadmissionpolicies", "validatingadmissionpolicybindings"]
verbs: ["get", "watch", "list"]
- apiGroups: ["flowcontrol.apiserver.k8s.io"]
resources: ['prioritylevelconfigurations', 'flowschemas']
verbs: ['list', 'watch']
- apiGroups: ['helm.toolkit.fluxcd.io']
resources: ['*']
verbs: ['*']
resources: ["prioritylevelconfigurations", "flowschemas"]
verbs: ["list", "watch"]
- apiGroups: ["cozystack.io"]
resources: ["*"]
verbs: ["get", "watch", "list"]
- apiGroups: ["helm.toolkit.fluxcd.io"]
resources: ["*"]
verbs: ["*"]

View File

@@ -0,0 +1,26 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tenantnamespaces-read
rules:
- apiGroups:
- core.cozystack.io
resources:
- tenantnamespaces
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: tenantnamespaces-read-authenticated
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tenantnamespaces-read
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated

View File

@@ -1,2 +1,2 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.34.8@sha256:86ef89a7be84761038cc25507a1b6e195c9f86c8051ac8aca8044dd8de39dedb
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.35.0-alpha.3@sha256:d4cfc2afacc30d383f7b5454e97bb77f2e9bfc33dbc3dc512bfe671300e3ad51

View File

@@ -0,0 +1,115 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.4
name: cozystackresourcedefinitions.cozystack.io
spec:
group: cozystack.io
names:
kind: CozystackResourceDefinition
listKind: CozystackResourceDefinitionList
plural: cozystackresourcedefinitions
singular: cozystackresourcedefinition
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions
API
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
properties:
application:
description: Application configuration
properties:
kind:
description: Kind of the application, used for UI and API
type: string
openAPISchema:
description: OpenAPI schema for the application, used for API
validation
type: string
plural:
description: Plural name of the application, used for UI and API
type: string
singular:
description: Singular name of the application, used for UI and
API
type: string
required:
- kind
- openAPISchema
- plural
- singular
type: object
release:
description: Release configuration
properties:
chart:
description: Helm chart configuration
properties:
name:
description: Name of the Helm chart
type: string
sourceRef:
description: Source reference for the Helm chart
properties:
kind:
default: HelmRepository
description: Kind of the source reference
type: string
name:
description: Name of the source reference
type: string
namespace:
default: cozy-public
description: Namespace of the source reference
type: string
required:
- kind
- name
- namespace
type: object
required:
- name
- sourceRef
type: object
labels:
additionalProperties:
type: string
description: Labels for the release
type: object
prefix:
description: Prefix for the release name
type: string
required:
- chart
- prefix
type: object
required:
- application
- release
type: object
type: object
served: true
storage: true

View File

@@ -1,5 +1,5 @@
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.34.8@sha256:cd4082e3672e793cbead467041ffb372d465085653614885f109fc7c582cd112
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.35.0-alpha.3@sha256:98d0b945630c3dd7c39adc7a5326f0e0e0b49a18641bec911aeb6c62beca4e1f
debug: false
disableTelemetry: false
cozystackVersion: "v0.34.8"
cozystackVersion: "v0.35.0-alpha.3"

View File

@@ -76,7 +76,7 @@ data:
"kubeappsNamespace": {{ .Release.Namespace | quote }},
"helmGlobalNamespace": {{ include "kubeapps.helmGlobalPackagingNamespace" . | quote }},
"carvelGlobalNamespace": {{ .Values.kubeappsapis.pluginConfig.kappController.packages.v1alpha1.globalPackagingNamespace | quote }},
"appVersion": "v0.34.8",
"appVersion": "v0.35.0-alpha.3",
"authProxyEnabled": {{ .Values.authProxy.enabled }},
"oauthLoginURI": {{ .Values.authProxy.oauthLoginURI | quote }},
"oauthLogoutURI": {{ .Values.authProxy.oauthLogoutURI | quote }},

View File

@@ -19,8 +19,8 @@ kubeapps:
image:
registry: ghcr.io/cozystack/cozystack
repository: dashboard
tag: v0.34.8
digest: "sha256:54906b3d2492c8603a347a5938b6db36e5ed5c4149111cae1804ac9110361947"
tag: v0.35.0-alpha.3
digest: "sha256:ad4b95660b6c5c1b9736ca4768a3f9648705c2855d0a08880d570b4e480dba78"
redis:
master:
resourcesPreset: "none"
@@ -37,8 +37,8 @@ kubeapps:
image:
registry: ghcr.io/cozystack/cozystack
repository: kubeapps-apis
tag: v0.34.8
digest: "sha256:6a02c7ef1e851118472e80ec7dac961e7637a6d32f4bd35dc1d2f0b32bf217a3"
tag: v0.35.0-alpha.3
digest: "sha256:55b1aacdcb8f7e96b75396e461fbba612498095515b9a437b8f925802f06c485"
pluginConfig:
flux:
packages:

View File

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

View File

@@ -0,0 +1,9 @@
export NAME=hetzner-robotlb
export NAMESPACE=cozy-$(NAME)
include ../../../scripts/package.mk
update:
rm -rf charts
mkdir -p charts
helm pull oci://ghcr.io/intreecom/charts/robotlb --untar --untardir charts

View File

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

View File

@@ -0,0 +1,6 @@
apiVersion: v2
appVersion: 0.0.5
description: A Helm chart for robotlb (loadbalancer on hetzner cloud).
name: robotlb
type: application
version: 0.1.3

View File

@@ -0,0 +1,4 @@
The RobotLB Operator was successfully installed.
Please follow the readme to create loadbalanced services.
README: https://github.com/intreecom/robotlb

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "robotlb.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "robotlb.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "robotlb.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "robotlb.labels" -}}
helm.sh/chart: {{ include "robotlb.chart" . }}
{{ include "robotlb.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "robotlb.selectorLabels" -}}
app.kubernetes.io/name: {{ include "robotlb.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "robotlb.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "robotlb.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,66 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "robotlb.fullname" . }}
labels:
{{- include "robotlb.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
{{- include "robotlb.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "robotlb.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "robotlb.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /usr/local/bin/robotlb
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.envs }}
env:
{{- range $key, $val := . }}
- name: {{ $key | quote }}
value: {{ $val | quote }}
{{ end -}}
{{- end }}
{{- with .Values.existingSecrets }}
envFrom:
{{- range $val := . }}
- secretRef:
name: {{ $val | quote }}
{{ end -}}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,20 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "robotlb.fullname" . }}-cr
rules: {{- toYaml .Values.serviceAccount.permissions | nindent 2 }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "robotlb.fullname" . }}-crb
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "robotlb.fullname" . }}-cr
subjects:
- kind: ServiceAccount
name: {{ include "robotlb.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "robotlb.serviceAccountName" . }}
labels:
{{- include "robotlb.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}

View File

@@ -0,0 +1,73 @@
# Default values for robotlb.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
repository: ghcr.io/intreecom/robotlb
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
envs:
ROBOTLB_LOG_LEVEL: "INFO"
existingSecrets: []
serviceAccount:
# Specifies whether a service account should be created
create: true
# Automatically mount a ServiceAccount's API credentials?
automount: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
# This is a list of cluster permissions to apply to the service account.
# By default it grants all permissions.
permissions:
- apiGroups: [""]
resources: [services, services/status]
verbs: [get, list, patch, update, watch]
- apiGroups: [""]
resources: [nodes, pods]
verbs: [get, list, watch]
podAnnotations: {}
podLabels: {}
podSecurityContext:
{}
# fsGroup: 2000
securityContext:
{}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
resources:
{}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -0,0 +1,3 @@
robotlb:
replicas: 1
existingSecrets: ["hetzner-robotlb-credentials"]

View File

@@ -27,5 +27,4 @@ image:
yq -i '.kamaji.image.repository = strenv(REPOSITORY)' values.yaml
TAG=$(TAG)@$$(yq e '."containerimage.digest"' images/kamaji.json -o json -r) \
yq -i '.kamaji.image.tag = strenv(TAG)' values.yaml
yq -i '.kamaji.extraArgs[0] = "--migrate-image=" + .kamaji.image.repository + ":" + .kamaji.image.tag' values.yaml
rm -f images/kamaji.json

View File

@@ -3,7 +3,7 @@ kamaji:
deploy: false
image:
pullPolicy: IfNotPresent
tag: v0.34.8@sha256:908dc829ce8493607dffcf6de95e4993ff2bfc0d65949c501bd7119fad45127a
tag: v0.35.0-alpha.3@sha256:34500654751ceef7a57562fb1edc6928d91245ad8e41da732788dcf10ec330b5
repository: ghcr.io/cozystack/cozystack/kamaji
resources:
limits:
@@ -12,5 +12,3 @@ kamaji:
requests:
cpu: 100m
memory: 100Mi
extraArgs:
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.34.8@sha256:908dc829ce8493607dffcf6de95e4993ff2bfc0d65949c501bd7119fad45127a

View File

@@ -87,7 +87,10 @@ spec:
- name: KEYCLOAK_ADMIN
value: admin
- name: KEYCLOAK_ADMIN_PASSWORD
value: {{ $password }}
valueFrom:
secretKeyRef:
name: {{ .Release.Name }}-credentials
key: password
- name: KC_DB
value: "postgres"
- name: KC_DB_URL_HOST

View File

@@ -1,3 +1,3 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.34.8@sha256:e6c845b65142d554d34187aef29afcbd8f67fae7dcc787255fb53de2b6103386
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.35.0-alpha.3@sha256:ad4e506065458b039a86c7f03a35dbb6918756e117ce74dd931089383cfd91f8

View File

@@ -64,4 +64,4 @@ global:
images:
kubeovn:
repository: kubeovn
tag: v1.13.14@sha256:0f27c268300fe30fe3ad61d145fdb276d5dddba2d792aacc26f3f512f7ef205d
tag: v1.13.14@sha256:bacd68243ee9fd94f5f0e377801ed3e99cd0762ff5df0d0ec73a73130180d8ca

View File

@@ -1,3 +1,3 @@
storageClass: replicated
csiDriver:
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.26.3@sha256:df3a2f503b4a035567b20b81a0f105c15971274fd675101c3b3eb2413d966d2e
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.26.2@sha256:761e7235ff9cb7f6f223f00954943e6a5af32ed6624ee592a8610122f96febb0

View File

@@ -37,7 +37,7 @@ spec:
- replacement: linstor-satellite
targetLabel: job
- sourceLabels: [__meta_kubernetes_pod_node_name]
targetLabel: controller_node
targetLabel: node
- targetLabel: tier
replacement: cluster
selector:

View File

@@ -27,3 +27,4 @@ image-controller image-sidecar:
IMAGE="$(REGISTRY)/objectstorage-$(TARGET):$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/$(TARGET).json -r)" \
yq -i '$(YAML_PATH) = strenv(IMAGE)' $(VALUES_FILE)
rm -f images/$(TARGET).json
yq .seaweedfs.cosi.sidecar.image ../seaweedfs/values.yaml > ../../extra/seaweedfs/images/objectstorage-sidecar.tag

View File

@@ -1,3 +1,3 @@
objectstorage:
controller:
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.34.8@sha256:5891c7fb46fb7c6ada4abc4e78e69b34631d13e32a30a86c0abc15cc17675450"
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.35.0-alpha.3@sha256:4d3f20ae946736d9260193dd1ce0e72114ad6bd6f78fb6528a68efffa2092b07"

View File

@@ -8,4 +8,5 @@ update:
curl -sSL https://github.com/seaweedfs/seaweedfs/archive/refs/heads/master.tar.gz | \
tar xzvf - --strip 3 -C charts seaweedfs-master/k8s/charts/seaweedfs
patch --no-backup-if-mismatch -p4 < patches/resize-api-server-annotation.diff
patch --no-backup-if-mismatch -p4 < patches/fix-volume-servicemonitor.patch
#patch --no-backup-if-mismatch -p4 < patches/retention-policy-delete.yaml

View File

@@ -21,9 +21,9 @@ metadata:
{{- with $.Values.global.monitoring.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if .Values.volume.annotations }}
{{- if $.Values.volume.annotations }}
annotations:
{{- toYaml .Values.volume.annotations | nindent 4 }}
{{- toYaml $.Values.volume.annotations | nindent 4 }}
{{- end }}
spec:
endpoints:

View File

@@ -0,0 +1,15 @@
diff --git a/packages/system/seaweedfs/charts/seaweedfs/templates/volume-servicemonitor.yaml b/packages/system/seaweedfs/charts/seaweedfs/templates/volume-servicemonitor.yaml
--- a/packages/system/seaweedfs/charts/seaweedfs/templates/volume-servicemonitor.yaml (revision 8951bc13d7d02b5e6982a239570ed58ed7cb025a)
+++ b/packages/system/seaweedfs/charts/seaweedfs/templates/volume-servicemonitor.yaml (revision fa4fff2292c4b79a92db5cd654a3c6bf590252a6)
@@ -21,9 +21,9 @@
{{- with $.Values.global.monitoring.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
-{{- if .Values.volume.annotations }}
+{{- if $.Values.volume.annotations }}
annotations:
- {{- toYaml .Values.volume.annotations | nindent 4 }}
+ {{- toYaml $.Values.volume.annotations | nindent 4 }}
{{- end }}
spec:
endpoints:

View File

@@ -4,10 +4,12 @@ global:
extraEnvironmentVars:
WEED_CLUSTER_SW_MASTER: "seaweedfs-master:9333"
WEED_CLUSTER_SW_FILER: "seaweedfs-filer-client:8888"
monitoring:
enabled: true
seaweedfs:
master:
volumeSizeLimitMB: 30000
replicas: 3
volumeSizeLimitMB: 100
# replication type is XYZ:
# X number of replica in other data centers
# Y number of replica in other racks in the same data center
@@ -78,7 +80,10 @@ seaweedfs:
existingConfigSecret: null
auditLogConfig: {}
s3:
enabled: true
enableAuth: true
readinessProbe:
scheme: HTTPS
logs:
type: ""
ingress:
@@ -87,8 +92,6 @@ seaweedfs:
host: "seaweedfs2.demo.cozystack.io"
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
acme.cert-manager.io/http01-ingress-class: tenant-root
cert-manager.io/cluster-issuer: letsencrypt-prod
@@ -96,6 +99,13 @@ seaweedfs:
- hosts:
- seaweedfs.demo.cozystack.io
secretName: seaweedfs-s3-ingress-tls
resources:
limits:
cpu: "2"
memory: "2Gi"
requests:
cpu: "500m"
memory: "1Gi"
cosi:
enabled: true
podLabels:
@@ -104,7 +114,7 @@ seaweedfs:
bucketClassName: "seaweedfs"
region: ""
sidecar:
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.34.8@sha256:ccc7ec31b2beebf09413b9a41ffa8fd06899510127572c9e1c1e30ddab45ebe9"
image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.35.0-alpha.3@sha256:4a67ec40c13bf912151957dc0e7409f7e01a2859905285c98c20baae7b667ae2"
certificates:
commonName: "SeaweedFS CA"
ipAddresses: []

View File

@@ -1,18 +1,5 @@
/*
Copyright 2024 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.
*/
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 The Cozystack Authors.
package v1alpha1
@@ -24,46 +11,50 @@ import (
"k8s.io/klog/v2"
)
// GroupName holds the API group name.
// -----------------------------------------------------------------------------
// Group / version boiler-plate
// -----------------------------------------------------------------------------
// GroupName is the API group for every resource in this package.
const GroupName = "apps.cozystack.io"
var (
RegisteredGVKs []schema.GroupVersionKind
)
// SchemeGroupVersion is group version used to register these objects
// SchemeGroupVersion is the canonical {group,version} for v1alpha1.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// -----------------------------------------------------------------------------
// Scheme registration helpers
// -----------------------------------------------------------------------------
var (
// SchemeBuilder allows to add this group to a scheme.
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is used by generated deepcopy code.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
// AddToScheme adds this group to a scheme.
AddToScheme = localSchemeBuilder.AddToScheme
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
// Manually-written types go here. Generated deepcopy code is wired in
// via `zz_generated.deepcopy.go`.
localSchemeBuilder.Register(addKnownTypes)
}
// Adds the list of known types to the given scheme.
// addKnownTypes is called from init().
func addKnownTypes(scheme *runtime.Scheme) error {
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
// Resource turns an unqualified resource name into a fully-qualified one.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// RegisterDynamicTypes registers types dynamically based on config
// -----------------------------------------------------------------------------
// Public helpers consumed by the apiserver wiring
// -----------------------------------------------------------------------------
// RegisterDynamicTypes adds per-tenant “Application” kinds that are only known
// at runtime from a config file.
func RegisterDynamicTypes(scheme *runtime.Scheme, cfg *config.ResourceConfig) error {
for _, res := range cfg.Resources {
kind := res.Application.Kind
@@ -76,9 +67,7 @@ func RegisterDynamicTypes(scheme *runtime.Scheme, cfg *config.ResourceConfig) er
scheme.AddKnownTypeWithName(gvkInternal, &Application{})
scheme.AddKnownTypeWithName(gvkInternal.GroupVersion().WithKind(kind+"List"), &ApplicationList{})
klog.V(1).Infof("Registered kind: %s\n", kind)
RegisteredGVKs = append(RegisteredGVKs, gvk)
klog.V(1).Infof("Registered dynamic kind: %s", kind)
}
return nil
}

View File

@@ -0,0 +1,33 @@
/*
Copyright 2024 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.
*/
package fuzzer
import (
"github.com/cozystack/cozystack/pkg/apis/core"
fuzz "github.com/google/gofuzz"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
)
// Funcs returns the fuzzer functions for the core api group.
var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(s *core.TenantNamespaceSpec, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
},
}
}

View File

@@ -0,0 +1,29 @@
/*
Copyright 2024 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.
*/
package install
import (
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(corev1alpha1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(corev1alpha1.SchemeGroupVersion))
}

View File

@@ -0,0 +1,30 @@
/*
Copyright 2024 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.
*/
package install
import (
"testing"
corefuzzer "github.com/cozystack/cozystack/pkg/apis/core/fuzzer"
"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
)
func TestRoundTripTypes(t *testing.T) {
roundtrip.RoundTripTestForAPIGroup(t, Install, corefuzzer.Funcs)
// TODO: enable protobuf generation for the sample-apiserver
// roundtrip.RoundTripProtobufTestForAPIGroup(t, Install, corefuzzer.Funcs)
}

22
pkg/apis/core/register.go Normal file
View File

@@ -0,0 +1,22 @@
/*
Copyright 2024 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.
*/
package core
// GroupName is the group name used in this package
const (
GroupName = "core.cozystack.io"
)

View File

@@ -0,0 +1,25 @@
/*
Copyright 2024 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.
*/
// +k8s:openapi-gen=true
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=github.com/cozystack/cozystack/pkg/apis/core
// +k8s:conversion-gen=k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
// +k8s:defaulter-gen=TypeMeta
// +groupName=core.cozystack.io
// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1 // import "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"

View File

@@ -0,0 +1,65 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 The Cozystack Authors.
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
// -----------------------------------------------------------------------------
// Group / version boiler-plate
// -----------------------------------------------------------------------------
// GroupName is the API group for every resource in this package.
const GroupName = "core.cozystack.io"
// SchemeGroupVersion is the canonical {group,version} for v1alpha1.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// -----------------------------------------------------------------------------
// Scheme registration helpers
// -----------------------------------------------------------------------------
var (
// SchemeBuilder is used by generated deepcopy code.
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// Manually-written types go here. Generated deepcopy code is wired in
// via `zz_generated.deepcopy.go`.
localSchemeBuilder.Register(addKnownTypes)
}
// addKnownTypes is called from init().
func addKnownTypes(scheme *runtime.Scheme) error {
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
// Resource turns an unqualified resource name into a fully-qualified one.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
// -----------------------------------------------------------------------------
// Public helpers consumed by the apiserver wiring
// -----------------------------------------------------------------------------
// RegisterStaticTypes adds *compile-time* resources such as TenantNamespace.
func RegisterStaticTypes(scheme *runtime.Scheme) {
scheme.AddKnownTypes(SchemeGroupVersion,
&TenantNamespace{},
&TenantNamespaceList{},
&TenantSecret{},
&TenantSecretList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
klog.V(1).Info("Registered static kinds: TenantNamespace, TenantSecret")
}

View File

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 The Cozystack Authors.
// This file contains the cluster-scoped “TenantNamespace” resource.
// A TenantNamespace represents an existing Kubernetes Namespace whose
// *name* starts with the prefix “tenant-”.
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// TenantNamespace is a thin wrapper around ObjectMeta. It has no spec/status
// because it merely reflects an existing Namespace object.
type TenantNamespace struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// TenantNamespaceList is the list variant for TenantNamespace.
type TenantNamespaceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TenantNamespace `json:"items"`
}

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TenantSecret struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Same semantics as core/v1 Secret.
Type string `json:"type,omitempty"`
Data map[string][]byte `json:"data,omitempty"`
StringData map[string]string `json:"stringData,omitempty"` // write-only hint
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type TenantSecretList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TenantSecret `json:"items"`
}

View File

@@ -0,0 +1,36 @@
//go:build !ignore_autogenerated
// +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 conversion-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
return nil
}

View File

@@ -0,0 +1,166 @@
//go:build !ignore_autogenerated
// +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 deepcopy-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TenantNamespace) DeepCopyInto(out *TenantNamespace) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantNamespace.
func (in *TenantNamespace) DeepCopy() *TenantNamespace {
if in == nil {
return nil
}
out := new(TenantNamespace)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TenantNamespace) 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 *TenantNamespaceList) DeepCopyInto(out *TenantNamespaceList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TenantNamespace, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantNamespaceList.
func (in *TenantNamespaceList) DeepCopy() *TenantNamespaceList {
if in == nil {
return nil
}
out := new(TenantNamespaceList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TenantNamespaceList) 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 *TenantSecret) DeepCopyInto(out *TenantSecret) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = make(map[string][]byte, len(*in))
for key, val := range *in {
var outVal []byte
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make([]byte, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
if in.StringData != nil {
in, out := &in.StringData, &out.StringData
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantSecret.
func (in *TenantSecret) DeepCopy() *TenantSecret {
if in == nil {
return nil
}
out := new(TenantSecret)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TenantSecret) 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 *TenantSecretList) DeepCopyInto(out *TenantSecretList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]TenantSecret, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantSecretList.
func (in *TenantSecretList) DeepCopy() *TenantSecretList {
if in == nil {
return nil
}
out := new(TenantSecretList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *TenantSecretList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@@ -0,0 +1,33 @@
//go:build !ignore_autogenerated
// +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 defaulter-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}

View File

@@ -0,0 +1,40 @@
/*
Copyright 2024 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.
*/
package validation
import (
"github.com/cozystack/cozystack/pkg/apis/core"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// ValidateTenantNamespace validates a TenantNamespace.
func ValidateTenantNamespace(f *core.TenantNamespace) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateTenantNamespaceSpec(&f.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateTenantNamespaceSpec validates a TenantNamespaceSpec.
func ValidateTenantNamespaceSpec(s *core.TenantNamespaceSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// TODO validation
return allErrs
}

View File

@@ -27,13 +27,18 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"github.com/cozystack/cozystack/pkg/apis/apps"
"github.com/cozystack/cozystack/pkg/apis/apps/install"
appsinstall "github.com/cozystack/cozystack/pkg/apis/apps/install"
coreinstall "github.com/cozystack/cozystack/pkg/apis/apps/install"
"github.com/cozystack/cozystack/pkg/apis/core"
"github.com/cozystack/cozystack/pkg/config"
appsregistry "github.com/cozystack/cozystack/pkg/registry"
cozyregistry "github.com/cozystack/cozystack/pkg/registry"
applicationstorage "github.com/cozystack/cozystack/pkg/registry/apps/application"
tenantnamespacestorage "github.com/cozystack/cozystack/pkg/registry/core/tenantnamespace"
tenantsecretstorage "github.com/cozystack/cozystack/pkg/registry/core/tenantsecret"
)
var (
@@ -42,11 +47,12 @@ var (
// Codecs provides methods for retrieving codecs and serializers for specific
// versions and content types.
Codecs = serializer.NewCodecFactory(Scheme)
AppsComponentName = "apps"
CozyComponentName = "cozy"
)
func init() {
install.Install(Scheme)
appsinstall.Install(Scheme)
coreinstall.Install(Scheme)
// Register HelmRelease types.
if err := helmv2.AddToScheme(Scheme); err != nil {
@@ -73,8 +79,8 @@ type Config struct {
ResourceConfig *config.ResourceConfig
}
// AppsServer holds the state for the Kubernetes master/api server.
type AppsServer struct {
// CozyServer holds the state for the Kubernetes master/api server.
type CozyServer struct {
GenericAPIServer *genericapiserver.GenericAPIServer
}
@@ -98,19 +104,17 @@ func (cfg *Config) Complete() CompletedConfig {
return CompletedConfig{&c}
}
// New returns a new instance of AppsServer from the given configuration.
func (c completedConfig) New() (*AppsServer, error) {
genericServer, err := c.GenericConfig.New("apps-apiserver", genericapiserver.NewEmptyDelegate())
// New returns a new instance of CozyServer from the given configuration.
func (c completedConfig) New() (*CozyServer, error) {
genericServer, err := c.GenericConfig.New("cozy-apiserver", genericapiserver.NewEmptyDelegate())
if err != nil {
return nil, err
}
s := &AppsServer{
s := &CozyServer{
GenericAPIServer: genericServer,
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apps.GroupName, Scheme, metav1.ParameterCodec, Codecs)
// Create a dynamic client for HelmRelease using InClusterConfig.
inClusterConfig, err := restclient.InClusterConfig()
if err != nil {
@@ -122,16 +126,41 @@ func (c completedConfig) New() (*AppsServer, error) {
return nil, fmt.Errorf("unable to create dynamic client: %v", err)
}
v1alpha1storage := map[string]rest.Storage{}
for _, resConfig := range c.ResourceConfig.Resources {
storage := applicationstorage.NewREST(dynamicClient, &resConfig)
v1alpha1storage[resConfig.Application.Plural] = appsregistry.RESTInPeace(storage)
clientset, err := kubernetes.NewForConfig(inClusterConfig)
if err != nil {
return nil, fmt.Errorf("create kube clientset: %v", err)
}
apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage
// --- static, cluster-scoped resource for core group ---
coreV1alpha1Storage := map[string]rest.Storage{}
coreV1alpha1Storage["tenantnamespaces"] = cozyregistry.RESTInPeace(
tenantnamespacestorage.NewREST(
clientset.CoreV1(),
clientset.AuthorizationV1(),
20,
),
)
coreV1alpha1Storage["tenantsecrets"] = cozyregistry.RESTInPeace(
tenantsecretstorage.NewREST(
clientset.CoreV1(),
),
)
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
coreApiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(core.GroupName, Scheme, metav1.ParameterCodec, Codecs)
coreApiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = coreV1alpha1Storage
if err := s.GenericAPIServer.InstallAPIGroup(&coreApiGroupInfo); err != nil {
return nil, err
}
// --- dynamically-configured, per-tenant resources ---
appsV1alpha1Storage := map[string]rest.Storage{}
for _, resConfig := range c.ResourceConfig.Resources {
storage := applicationstorage.NewREST(dynamicClient, &resConfig)
appsV1alpha1Storage[resConfig.Application.Plural] = cozyregistry.RESTInPeace(storage)
}
appsApiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apps.GroupName, Scheme, metav1.ParameterCodec, Codecs)
appsApiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = appsV1alpha1Storage
if err := s.GenericAPIServer.InstallAPIGroup(&appsApiGroupInfo); err != nil {
return nil, err
}

View File

@@ -20,9 +20,11 @@ import (
"testing"
appsfuzzer "github.com/cozystack/cozystack/pkg/apis/apps/fuzzer"
corefuzzer "github.com/cozystack/cozystack/pkg/apis/core/fuzzer"
"k8s.io/apimachinery/pkg/api/apitesting/roundtrip"
)
func TestRoundTripTypes(t *testing.T) {
roundtrip.RoundTripTestForScheme(t, Scheme, appsfuzzer.Funcs)
roundtrip.RoundTripTestForScheme(t, Scheme, corefuzzer.Funcs)
}

View File

@@ -25,11 +25,14 @@ import (
"io"
"net"
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1"
v1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1"
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
"github.com/cozystack/cozystack/pkg/apiserver"
"github.com/cozystack/cozystack/pkg/config"
sampleopenapi "github.com/cozystack/cozystack/pkg/generated/openapi"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/version"
@@ -38,35 +41,37 @@ import (
genericoptions "k8s.io/apiserver/pkg/server/options"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilversionpkg "k8s.io/apiserver/pkg/util/version"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/component-base/featuregate"
baseversion "k8s.io/component-base/version"
netutils "k8s.io/utils/net"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// AppsServerOptions holds the state for the Apps API server
type AppsServerOptions struct {
// CozyServerOptions holds the state for the Cozy API server
type CozyServerOptions struct {
RecommendedOptions *genericoptions.RecommendedOptions
StdOut io.Writer
StdErr io.Writer
AlternateDNS []string
// Add a field to store the configuration path
ResourceConfigPath string
Client client.Client
// Add a field to store the configuration
ResourceConfig *config.ResourceConfig
}
// NewAppsServerOptions returns a new instance of AppsServerOptions
func NewAppsServerOptions(out, errOut io.Writer) *AppsServerOptions {
o := &AppsServerOptions{
// NewCozyServerOptions returns a new instance of CozyServerOptions
func NewCozyServerOptions(out, errOut io.Writer) *CozyServerOptions {
o := &CozyServerOptions{
RecommendedOptions: genericoptions.NewRecommendedOptions(
"",
apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion),
apiserver.Codecs.LegacyCodec(
corev1alpha1.SchemeGroupVersion,
appsv1alpha1.SchemeGroupVersion,
),
),
StdOut: out,
StdErr: errOut,
}
@@ -74,12 +79,12 @@ func NewAppsServerOptions(out, errOut io.Writer) *AppsServerOptions {
return o
}
// NewCommandStartAppsServer provides a CLI handler for the 'start apps-server' command
func NewCommandStartAppsServer(ctx context.Context, defaults *AppsServerOptions) *cobra.Command {
// NewCommandStartCozyServer provides a CLI handler for the 'start apps-server' command
func NewCommandStartCozyServer(ctx context.Context, defaults *CozyServerOptions) *cobra.Command {
o := *defaults
cmd := &cobra.Command{
Short: "Launch an Apps API server",
Long: "Launch an Apps API server",
Short: "Launch an Cozystack API server",
Long: "Launch an Cozystack API server",
PersistentPreRunE: func(*cobra.Command, []string) error {
return utilversionpkg.DefaultComponentGlobalsRegistry.Set()
},
@@ -90,7 +95,7 @@ func NewCommandStartAppsServer(ctx context.Context, defaults *AppsServerOptions)
if err := o.Validate(args); err != nil {
return err
}
if err := o.RunAppsServer(c.Context()); err != nil {
if err := o.RunCozyServer(c.Context()); err != nil {
return err
}
return nil
@@ -101,22 +106,19 @@ func NewCommandStartAppsServer(ctx context.Context, defaults *AppsServerOptions)
flags := cmd.Flags()
o.RecommendedOptions.AddFlags(flags)
// Add a flag for the config path
flags.StringVar(&o.ResourceConfigPath, "config", "config.yaml", "Path to the resource configuration file")
// The following lines demonstrate how to configure version compatibility and feature gates
// for the "Apps" component according to KEP-4330.
// for the "Cozy" component according to KEP-4330.
// Create a default version object for the "Apps" component.
defaultAppsVersion := "1.1"
// Register the "Apps" component in the global component registry,
// Create a default version object for the "Cozy" component.
defaultCozyVersion := "1.1"
// Register the "Cozy" component in the global component registry,
// associating it with its effective version and feature gate configuration.
_, appsFeatureGate := utilversionpkg.DefaultComponentGlobalsRegistry.ComponentGlobalsOrRegister(
apiserver.AppsComponentName, utilversionpkg.NewEffectiveVersion(defaultAppsVersion),
featuregate.NewVersionedFeatureGate(version.MustParse(defaultAppsVersion)),
apiserver.CozyComponentName, utilversionpkg.NewEffectiveVersion(defaultCozyVersion),
featuregate.NewVersionedFeatureGate(version.MustParse(defaultCozyVersion)),
)
// Add feature gate specifications for the "Apps" component.
// Add feature gate specifications for the "Cozy" component.
utilruntime.Must(appsFeatureGate.AddVersioned(map[featuregate.Feature]featuregate.VersionedSpecs{
// Example of adding feature gates:
// "FeatureName": {{"v1", true}, {"v2", false}},
@@ -129,9 +131,9 @@ func NewCommandStartAppsServer(ctx context.Context, defaults *AppsServerOptions)
utilfeature.DefaultMutableFeatureGate,
)
// Set the version emulation mapping from the "Apps" component to the kube component.
// Set the version emulation mapping from the "Cozy" component to the kube component.
utilruntime.Must(utilversionpkg.DefaultComponentGlobalsRegistry.SetEmulationVersionMapping(
apiserver.AppsComponentName, utilversionpkg.DefaultKubeComponent, AppsVersionToKubeVersion,
apiserver.CozyComponentName, utilversionpkg.DefaultKubeComponent, CozyVersionToKubeVersion,
))
// Add flags from the global component registry.
@@ -141,26 +143,68 @@ func NewCommandStartAppsServer(ctx context.Context, defaults *AppsServerOptions)
}
// Complete fills in the fields that are not set
func (o *AppsServerOptions) Complete() error {
// Load the configuration file
cfg, err := config.LoadConfig(o.ResourceConfigPath)
if err != nil {
return fmt.Errorf("failed to load config from %s: %v", o.ResourceConfigPath, err)
func (o *CozyServerOptions) Complete() error {
scheme := runtime.NewScheme()
if err := v1alpha1.AddToScheme(scheme); err != nil {
return fmt.Errorf("failed to register types: %w", err)
}
o.ResourceConfig = cfg
cfg, err := clientcmd.BuildConfigFromFlags("", "")
if err != nil {
return fmt.Errorf("failed to get kubeconfig: %w", err)
}
o.Client, err = client.New(cfg, client.Options{Scheme: scheme})
if err != nil {
return fmt.Errorf("client initialization failed: %w", err)
}
crdList := &v1alpha1.CozystackResourceDefinitionList{}
if err := o.Client.List(context.Background(), crdList); err != nil {
return fmt.Errorf("failed to list CozystackResourceDefinitions: %w", err)
}
// Convert to ResourceConfig
o.ResourceConfig = &config.ResourceConfig{}
for _, crd := range crdList.Items {
resource := config.Resource{
Application: config.ApplicationConfig{
Kind: crd.Spec.Application.Kind,
Singular: crd.Spec.Application.Singular,
Plural: crd.Spec.Application.Plural,
ShortNames: []string{}, // TODO: implement shortnames
OpenAPISchema: crd.Spec.Application.OpenAPISchema,
},
Release: config.ReleaseConfig{
Prefix: crd.Spec.Release.Prefix,
Labels: crd.Spec.Release.Labels,
Chart: config.ChartConfig{
Name: crd.Spec.Release.Chart.Name,
SourceRef: config.SourceRefConfig{
Kind: crd.Spec.Release.Chart.SourceRef.Kind,
Name: crd.Spec.Release.Chart.SourceRef.Name,
Namespace: crd.Spec.Release.Chart.SourceRef.Namespace,
},
},
},
}
o.ResourceConfig.Resources = append(o.ResourceConfig.Resources, resource)
}
return nil
}
// Validate checks the correctness of the options
func (o AppsServerOptions) Validate(args []string) error {
func (o CozyServerOptions) Validate(args []string) error {
var allErrors []error
allErrors = append(allErrors, o.RecommendedOptions.Validate()...)
allErrors = append(allErrors, utilversionpkg.DefaultComponentGlobalsRegistry.Validate()...)
return utilerrors.NewAggregate(allErrors)
}
// Config returns the configuration for the API server based on AppsServerOptions
func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
// Config returns the configuration for the API server based on CozyServerOptions
func (o *CozyServerOptions) Config() (*apiserver.Config, error) {
// TODO: set the "real" external address
if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts(
"localhost", o.AlternateDNS, []net.IP{netutils.ParseIPSloppy("127.0.0.1")},
@@ -168,8 +212,11 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
// First, register the dynamic types
err := v1alpha1.RegisterDynamicTypes(apiserver.Scheme, o.ResourceConfig)
// Register *compile-time* resources first.
corev1alpha1.RegisterStaticTypes(apiserver.Scheme)
// Register *run-time* resources (from the users config file).
err := appsv1alpha1.RegisterDynamicTypes(apiserver.Scheme, o.ResourceConfig)
if err != nil {
return nil, fmt.Errorf("failed to register dynamic types: %v", err)
}
@@ -196,14 +243,14 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
kindSchemas[r.Application.Kind] = r.Application.OpenAPISchema
}
serverConfig.OpenAPIConfig.Info.Title = "Apps"
serverConfig.OpenAPIConfig.Info.Title = "Cozy"
serverConfig.OpenAPIConfig.Info.Version = version
serverConfig.OpenAPIConfig.PostProcessSpec = buildPostProcessV2(kindSchemas)
serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(
sampleopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(apiserver.Scheme),
)
serverConfig.OpenAPIV3Config.Info.Title = "Apps"
serverConfig.OpenAPIV3Config.Info.Title = "Cozy"
serverConfig.OpenAPIV3Config.Info.Version = version
serverConfig.OpenAPIV3Config.PostProcessSpec = buildPostProcessV3(kindSchemas)
@@ -212,7 +259,7 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
utilversionpkg.DefaultKubeComponent,
)
serverConfig.EffectiveVersion = utilversionpkg.DefaultComponentGlobalsRegistry.EffectiveVersionFor(
apiserver.AppsComponentName,
apiserver.CozyComponentName,
)
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
@@ -226,8 +273,8 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
return config, nil
}
// RunAppsServer launches a new AppsServer based on AppsServerOptions
func (o AppsServerOptions) RunAppsServer(ctx context.Context) error {
// RunCozyServer launches a new CozyServer based on CozyServerOptions
func (o CozyServerOptions) RunCozyServer(ctx context.Context) error {
config, err := o.Config()
if err != nil {
return err
@@ -246,8 +293,8 @@ func (o AppsServerOptions) RunAppsServer(ctx context.Context) error {
return server.GenericAPIServer.PrepareRun().RunWithContext(ctx)
}
// AppsVersionToKubeVersion defines the version mapping between the Apps component and kube
func AppsVersionToKubeVersion(ver *version.Version) *version.Version {
// CozyVersionToKubeVersion defines the version mapping between the Cozy component and kube
func CozyVersionToKubeVersion(ver *version.Version) *version.Version {
if ver.Major() != 1 {
return nil
}

View File

@@ -25,7 +25,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestAppsEmulationVersionToKubeEmulationVersion(t *testing.T) {
func TestCozyEmulationVersionToKubeEmulationVersion(t *testing.T) {
defaultKubeEffectiveVersion := utilversion.DefaultKubeEffectiveVersion()
testCases := []struct {
@@ -61,7 +61,7 @@ func TestAppsEmulationVersionToKubeEmulationVersion(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
mappedKubeEmulationVer := AppsVersionToKubeVersion(tc.appsEmulationVer)
mappedKubeEmulationVer := CozyVersionToKubeVersion(tc.appsEmulationVer)
assert.True(t, mappedKubeEmulationVer.EqualTo(tc.expectedKubeEmulationVer))
})
}

View File

@@ -16,13 +16,6 @@ limitations under the License.
package config
import (
"fmt"
"os"
"gopkg.in/yaml.v2"
)
// ResourceConfig represents the structure of the configuration file.
type ResourceConfig struct {
Resources []Resource `yaml:"resources"`
@@ -62,33 +55,3 @@ type SourceRefConfig struct {
Name string `yaml:"name"`
Namespace string `yaml:"namespace"`
}
// LoadConfig loads the configuration from the specified path and validates it.
func LoadConfig(path string) (*ResourceConfig, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var config ResourceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
// Validate the configuration.
for i, res := range config.Resources {
if res.Application.Kind == "" {
return nil, fmt.Errorf("resource at index %d has an empty kind", i)
}
if res.Application.Plural == "" {
return nil, fmt.Errorf("resource at index %d has an empty plural", i)
}
if res.Release.Chart.Name == "" {
return nil, fmt.Errorf("resource at index %d has an empty chart name in release", i)
}
if res.Release.Chart.SourceRef.Kind == "" || res.Release.Chart.SourceRef.Name == "" || res.Release.Chart.SourceRef.Namespace == "" {
return nil, fmt.Errorf("resource at index %d has an incomplete sourceRef for chart in release", i)
}
}
return &config, nil
}

View File

@@ -30,9 +30,13 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.Application": schema_pkg_apis_apps_v1alpha1_Application(ref),
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.ApplicationList": schema_pkg_apis_apps_v1alpha1_ApplicationList(ref),
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.ApplicationStatus": schema_pkg_apis_apps_v1alpha1_ApplicationStatus(ref),
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.Application": schema_pkg_apis_apps_v1alpha1_Application(ref),
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.ApplicationList": schema_pkg_apis_apps_v1alpha1_ApplicationList(ref),
"github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1.ApplicationStatus": schema_pkg_apis_apps_v1alpha1_ApplicationStatus(ref),
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantNamespace": schema_pkg_apis_core_v1alpha1_TenantNamespace(ref),
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantNamespaceList": schema_pkg_apis_core_v1alpha1_TenantNamespaceList(ref),
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantSecret": schema_pkg_apis_core_v1alpha1_TenantSecret(ref),
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantSecretList": schema_pkg_apis_core_v1alpha1_TenantSecretList(ref),
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.ConversionRequest": schema_pkg_apis_apiextensions_v1_ConversionRequest(ref),
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.ConversionResponse": schema_pkg_apis_apiextensions_v1_ConversionResponse(ref),
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1.ConversionReview": schema_pkg_apis_apiextensions_v1_ConversionReview(ref),
@@ -252,6 +256,208 @@ func schema_pkg_apis_apps_v1alpha1_ApplicationStatus(ref common.ReferenceCallbac
}
}
func schema_pkg_apis_core_v1alpha1_TenantNamespace(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "TenantNamespace is a thin wrapper around ObjectMeta. It has no spec/status because it merely reflects an existing Namespace object.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_core_v1alpha1_TenantNamespaceList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "TenantNamespaceList is the list variant for TenantNamespace.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantNamespace"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantNamespace", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_core_v1alpha1_TenantSecret(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"type": {
SchemaProps: spec.SchemaProps{
Description: "Same semantics as core/v1 Secret.",
Type: []string{"string"},
Format: "",
},
},
"data": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "byte",
},
},
},
},
},
"stringData": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_core_v1alpha1_TenantSecretList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantSecret"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"github.com/cozystack/cozystack/pkg/apis/core/v1alpha1.TenantSecret", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_apiextensions_v1_ConversionRequest(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

View File

@@ -18,6 +18,7 @@ package application
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -41,6 +42,10 @@ import (
appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1"
"github.com/cozystack/cozystack/pkg/config"
internalapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
schemadefault "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
// Importing API errors package to construct appropriate error responses
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -78,10 +83,21 @@ type REST struct {
kindName string
singularName string
releaseConfig config.ReleaseConfig
specSchema *structuralschema.Structural
}
// NewREST creates a new REST storage for Application with specific configuration
func NewREST(dynamicClient dynamic.Interface, config *config.Resource) *REST {
var specSchema *structuralschema.Structural
if raw := strings.TrimSpace(config.Application.OpenAPISchema); raw != "" {
var js internalapiext.JSONSchemaProps
if err := json.Unmarshal([]byte(raw), &js); err != nil {
klog.Errorf("Failed to unmarshal OpenAPI schema: %v", err)
} else if specSchema, err = structuralschema.NewStructural(&js); err != nil {
klog.Errorf("Failed to create structural schema: %v", err)
}
}
return &REST{
dynamicClient: dynamicClient,
gvr: schema.GroupVersionResource{
@@ -96,6 +112,7 @@ func NewREST(dynamicClient dynamic.Interface, config *config.Resource) *REST {
kindName: config.Application.Kind,
singularName: config.Application.Singular,
releaseConfig: config.Release,
specSchema: specSchema,
}
}
@@ -920,6 +937,10 @@ func (r *REST) ConvertHelmReleaseToApplication(hr *unstructured.Unstructured) (a
return appsv1alpha1.Application{}, err
}
if err := r.applySpecDefaults(&app); err != nil {
return app, fmt.Errorf("defaulting error: %w", err)
}
klog.V(6).Infof("Successfully converted HelmRelease %s to Application", hr.GetName())
return app, nil
}
@@ -1184,3 +1205,28 @@ func (e errNotAcceptable) Status() metav1.Status {
Message: e.Error(),
}
}
// applySpecDefaults applies default values to the Application spec based on the schema
func (r *REST) applySpecDefaults(app *appsv1alpha1.Application) error {
if r.specSchema == nil {
return nil
}
var m map[string]any
if app.Spec != nil && len(app.Spec.Raw) > 0 {
if err := json.Unmarshal(app.Spec.Raw, &m); err != nil {
return err
}
}
if m == nil {
m = map[string]any{}
}
schemadefault.Default(m, r.specSchema)
raw, err := json.Marshal(m)
if err != nil {
return err
}
app.Spec = &apiextv1.JSON{Raw: raw}
return nil
}

View File

@@ -0,0 +1,363 @@
// SPDX-License-Identifier: Apache-2.0
// TenantNamespace registry: read-only view over Namespaces whose names start
// with “tenant-”.
package tenantnamespace
import (
"context"
"fmt"
"math"
"net/http"
"strings"
"sync"
"time"
authorizationv1 "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/klog/v2"
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
)
const (
prefix = "tenant-"
singularName = "tenantnamespace"
)
// -----------------------------------------------------------------------------
// REST storage
// -----------------------------------------------------------------------------
var (
_ rest.Lister = &REST{}
_ rest.Getter = &REST{}
_ rest.Watcher = &REST{}
_ rest.TableConvertor = &REST{}
_ rest.Scoper = &REST{}
_ rest.SingularNameProvider = &REST{}
)
type REST struct {
core corev1client.CoreV1Interface
authClient authorizationv1client.AuthorizationV1Interface
maxWorkers int
gvr schema.GroupVersionResource
}
func NewREST(
coreCli corev1client.CoreV1Interface,
authCli authorizationv1client.AuthorizationV1Interface,
maxWorkers int,
) *REST {
return &REST{
core: coreCli,
authClient: authCli,
maxWorkers: maxWorkers,
gvr: schema.GroupVersionResource{
Group: corev1alpha1.GroupName,
Version: "v1alpha1",
Resource: "tenantnamespaces",
},
}
}
// -----------------------------------------------------------------------------
// Basic meta
// -----------------------------------------------------------------------------
func (*REST) NamespaceScoped() bool { return false }
func (*REST) New() runtime.Object { return &corev1alpha1.TenantNamespace{} }
func (*REST) NewList() runtime.Object {
return &corev1alpha1.TenantNamespaceList{}
}
func (*REST) Kind() string { return "TenantNamespace" }
func (r *REST) GroupVersionKind(_ schema.GroupVersion) schema.GroupVersionKind {
return r.gvr.GroupVersion().WithKind("TenantNamespace")
}
func (*REST) GetSingularName() string { return singularName }
// -----------------------------------------------------------------------------
// Lister / Getter
// -----------------------------------------------------------------------------
func (r *REST) List(
ctx context.Context,
_ *metainternal.ListOptions,
) (runtime.Object, error) {
nsList, err := r.core.Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
var tenantNames []string
for i := range nsList.Items {
if strings.HasPrefix(nsList.Items[i].Name, prefix) {
tenantNames = append(tenantNames, nsList.Items[i].Name)
}
}
allowed, err := r.filterAccessible(ctx, tenantNames)
if err != nil {
return nil, err
}
return r.makeList(nsList, allowed), nil
}
func (r *REST) Get(
ctx context.Context,
name string,
opts *metav1.GetOptions,
) (runtime.Object, error) {
if !strings.HasPrefix(name, prefix) {
return nil, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
ns, err := r.core.Namespaces().Get(ctx, name, *opts)
if err != nil {
return nil, err
}
return &corev1alpha1.TenantNamespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: "TenantNamespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: ns.Name,
UID: ns.UID,
ResourceVersion: ns.ResourceVersion,
CreationTimestamp: ns.CreationTimestamp,
Labels: ns.Labels,
Annotations: ns.Annotations,
},
}, nil
}
// -----------------------------------------------------------------------------
// Watcher
// -----------------------------------------------------------------------------
func (r *REST) Watch(ctx context.Context, opts *metainternal.ListOptions) (watch.Interface, error) {
nsWatch, err := r.core.Namespaces().Watch(ctx, metav1.ListOptions{
Watch: true,
ResourceVersion: opts.ResourceVersion,
})
if err != nil {
return nil, err
}
events := make(chan watch.Event)
pw := watch.NewProxyWatcher(events)
go func() {
defer pw.Stop()
for ev := range nsWatch.ResultChan() {
ns, ok := ev.Object.(*corev1.Namespace)
if !ok || !strings.HasPrefix(ns.Name, prefix) {
continue
}
out := &corev1alpha1.TenantNamespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: "TenantNamespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: ns.Name,
UID: ns.UID,
ResourceVersion: ns.ResourceVersion,
CreationTimestamp: ns.CreationTimestamp,
Labels: ns.Labels,
Annotations: ns.Annotations,
},
}
events <- watch.Event{Type: ev.Type, Object: out}
}
}()
return pw, nil
}
// -----------------------------------------------------------------------------
// TableConvertor
// -----------------------------------------------------------------------------
func (r *REST) ConvertToTable(_ context.Context, obj runtime.Object, _ runtime.Object) (*metav1.Table, error) {
now := time.Now()
row := func(o *corev1alpha1.TenantNamespace) metav1.TableRow {
return metav1.TableRow{
Cells: []interface{}{o.Name, duration.HumanDuration(now.Sub(o.CreationTimestamp.Time))},
Object: runtime.RawExtension{Object: o},
}
}
tbl := &metav1.Table{
TypeMeta: metav1.TypeMeta{APIVersion: "meta.k8s.io/v1", Kind: "Table"},
ColumnDefinitions: []metav1.TableColumnDefinition{
{Name: "NAME", Type: "string"},
{Name: "AGE", Type: "string"},
},
}
switch v := obj.(type) {
case *corev1alpha1.TenantNamespaceList:
for i := range v.Items {
tbl.Rows = append(tbl.Rows, row(&v.Items[i]))
}
tbl.ListMeta.ResourceVersion = v.ListMeta.ResourceVersion
case *corev1alpha1.TenantNamespace:
tbl.Rows = append(tbl.Rows, row(v))
tbl.ListMeta.ResourceVersion = v.ResourceVersion
default:
return nil, notAcceptable{r.gvr.GroupResource(), fmt.Sprintf("unexpected %T", obj)}
}
return tbl, nil
}
// -----------------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------------
func (r *REST) makeList(src *corev1.NamespaceList, allowed []string) *corev1alpha1.TenantNamespaceList {
set := map[string]struct{}{}
for _, n := range allowed {
set[n] = struct{}{}
}
out := &corev1alpha1.TenantNamespaceList{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: "TenantNamespaceList",
},
ListMeta: metav1.ListMeta{ResourceVersion: src.ResourceVersion},
}
for i := range src.Items {
ns := &src.Items[i]
if _, ok := set[ns.Name]; !ok {
continue
}
out.Items = append(out.Items, corev1alpha1.TenantNamespace{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: "TenantNamespace",
},
ObjectMeta: metav1.ObjectMeta{
Name: ns.Name,
UID: ns.UID,
ResourceVersion: ns.ResourceVersion,
CreationTimestamp: ns.CreationTimestamp,
Labels: ns.Labels,
Annotations: ns.Annotations,
},
})
}
return out
}
func (r *REST) filterAccessible(
ctx context.Context,
names []string,
) ([]string, error) {
workers := int(math.Min(float64(r.maxWorkers), float64(len(names))))
type job struct{ name string }
type res struct {
name string
allowed bool
err error
}
jobs := make(chan job, workers)
out := make(chan res, workers)
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := range jobs {
ok, err := r.sar(ctx, j.name)
out <- res{j.name, ok, err}
}
}()
}
go func() { wg.Wait(); close(out) }()
go func() {
for _, n := range names {
jobs <- job{n}
}
close(jobs)
}()
var allowed []string
for r := range out {
if r.err != nil {
klog.Errorf("SAR failed for %s: %v", r.name, r.err)
continue
}
if r.allowed {
allowed = append(allowed, r.name)
}
}
return allowed, nil
}
func (r *REST) sar(ctx context.Context, ns string) (bool, error) {
u, ok := request.UserFrom(ctx)
if !ok || u == nil {
return false, fmt.Errorf("user missing in context")
}
sar := &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
User: u.GetName(),
Groups: u.GetGroups(),
ResourceAttributes: &authorizationv1.ResourceAttributes{
Group: "cozystack.io",
Resource: "workloadmonitors",
Verb: "get",
Namespace: ns,
},
},
}
rsp, err := r.authClient.SubjectAccessReviews().
Create(ctx, sar, metav1.CreateOptions{})
if err != nil {
return false, err
}
return rsp.Status.Allowed, nil
}
// -----------------------------------------------------------------------------
// Boiler-plate
// -----------------------------------------------------------------------------
func (*REST) Destroy() {}
type notAcceptable struct {
resource schema.GroupResource
message string
}
func (e notAcceptable) Error() string { return e.message }
func (e notAcceptable) Status() metav1.Status {
return metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotAcceptable,
Reason: metav1.StatusReason("NotAcceptable"),
Message: e.message,
}
}

View File

@@ -0,0 +1,456 @@
// SPDX-License-Identifier: Apache-2.0
// TenantSecret registry namespaced view over Secrets labelled
// “cozystack.io/ui=true”. Internal labels/annotations are hidden.
package tenantsecret
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"sort"
"strings"
"time"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
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/selection"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/duration"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1"
)
// -----------------------------------------------------------------------------
// Constants & helpers
// -----------------------------------------------------------------------------
const (
uiLabelKey = "cozystack.io/ui"
uiLabelValue = "true"
systemLabelPrefix = "internal.cozystack.io/"
systemAnnotPrefix = "internal.cozystack.io/"
singularName = "tenantsecret"
kindTenantSecret = "TenantSecret"
kindTenantSecretList = "TenantSecretList"
)
func stripInternal(m map[string]string) map[string]string {
if m == nil {
return nil
}
out := make(map[string]string, len(m))
for k, v := range m {
if k == uiLabelKey ||
strings.HasPrefix(k, systemLabelPrefix) ||
strings.HasPrefix(k, systemAnnotPrefix) {
continue
}
out[k] = v
}
return out
}
func encodeStringData(sd map[string]string) map[string][]byte {
if len(sd) == 0 {
return nil
}
out := make(map[string][]byte, len(sd))
for k, v := range sd {
out[k] = []byte(v)
}
return out
}
func decodeStringData(d map[string][]byte) map[string]string {
if len(d) == 0 {
return nil
}
out := make(map[string]string, len(d))
for k, v := range d {
out[k] = base64.StdEncoding.EncodeToString(v)
}
return out
}
func secretToTenant(sec *corev1.Secret) *corev1alpha1.TenantSecret {
return &corev1alpha1.TenantSecret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: kindTenantSecret,
},
ObjectMeta: metav1.ObjectMeta{
Name: sec.Name,
Namespace: sec.Namespace,
UID: sec.UID,
ResourceVersion: sec.ResourceVersion,
CreationTimestamp: sec.CreationTimestamp,
Labels: stripInternal(sec.Labels),
Annotations: stripInternal(sec.Annotations),
},
Type: string(sec.Type),
Data: sec.Data,
StringData: decodeStringData(sec.Data),
}
}
func tenantToSecret(ts *corev1alpha1.TenantSecret, cur *corev1.Secret) *corev1.Secret {
var out corev1.Secret
if cur != nil {
out = *cur.DeepCopy()
}
out.TypeMeta = metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"}
out.Name, out.Namespace = ts.Name, ts.Namespace
if out.Labels == nil {
out.Labels = map[string]string{}
}
out.Labels[uiLabelKey] = uiLabelValue
for k, v := range ts.Labels {
out.Labels[k] = v
}
if out.Annotations == nil {
out.Annotations = map[string]string{}
}
for k, v := range ts.Annotations {
out.Annotations[k] = v
}
if len(ts.Data) != 0 {
out.Data = ts.Data
} else if len(ts.StringData) != 0 {
out.Data = encodeStringData(ts.StringData)
}
out.Type = corev1.SecretType(ts.Type)
return &out
}
func nsFrom(ctx context.Context) (string, error) {
ns, ok := request.NamespaceFrom(ctx)
if !ok {
return "", apierrors.NewBadRequest("namespace required")
}
return ns, nil
}
// -----------------------------------------------------------------------------
// REST storage
// -----------------------------------------------------------------------------
var (
_ rest.Creater = &REST{}
_ rest.Getter = &REST{}
_ rest.Lister = &REST{}
_ rest.Updater = &REST{}
_ rest.Patcher = &REST{}
_ rest.GracefulDeleter = &REST{}
_ rest.Watcher = &REST{}
_ rest.TableConvertor = &REST{}
_ rest.Scoper = &REST{}
_ rest.SingularNameProvider = &REST{}
)
type REST struct {
core corev1client.CoreV1Interface
gvr schema.GroupVersionResource
}
func NewREST(coreCli corev1client.CoreV1Interface) *REST {
return &REST{
core: coreCli,
gvr: schema.GroupVersionResource{
Group: corev1alpha1.GroupName,
Version: "v1alpha1",
Resource: "tenantsecrets",
},
}
}
// -----------------------------------------------------------------------------
// Basic meta
// -----------------------------------------------------------------------------
func (*REST) NamespaceScoped() bool { return true }
func (*REST) New() runtime.Object { return &corev1alpha1.TenantSecret{} }
func (*REST) NewList() runtime.Object {
return &corev1alpha1.TenantSecretList{}
}
func (*REST) Kind() string { return kindTenantSecret }
func (r *REST) GroupVersionKind(_ schema.GroupVersion) schema.GroupVersionKind {
return r.gvr.GroupVersion().WithKind(kindTenantSecret)
}
func (*REST) GetSingularName() string { return singularName }
// -----------------------------------------------------------------------------
// CRUD
// -----------------------------------------------------------------------------
func (r *REST) Create(
ctx context.Context,
obj runtime.Object,
_ rest.ValidateObjectFunc,
opts *metav1.CreateOptions,
) (runtime.Object, error) {
in, ok := obj.(*corev1alpha1.TenantSecret)
if !ok {
return nil, fmt.Errorf("expected TenantSecret, got %T", obj)
}
sec := tenantToSecret(in, nil)
out, err := r.core.Secrets(sec.Namespace).Create(ctx, sec, *opts)
if err != nil {
return nil, err
}
return secretToTenant(out), nil
}
func (r *REST) Get(
ctx context.Context,
name string,
opts *metav1.GetOptions,
) (runtime.Object, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, err
}
sec, err := r.core.Secrets(ns).Get(ctx, name, *opts)
if err != nil {
return nil, err
}
return secretToTenant(sec), nil
}
func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtime.Object, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, err
}
ls := labels.NewSelector()
req, _ := labels.NewRequirement(uiLabelKey, selection.Equals, []string{uiLabelValue})
ls = ls.Add(*req)
if opts.LabelSelector != nil {
if reqs, _ := opts.LabelSelector.Requirements(); len(reqs) > 0 {
ls = ls.Add(reqs...)
}
}
fieldSel := ""
if opts.FieldSelector != nil {
fieldSel = opts.FieldSelector.String()
}
list, err := r.core.Secrets(ns).List(ctx, metav1.ListOptions{
LabelSelector: ls.String(),
FieldSelector: fieldSel,
})
if err != nil {
return nil, err
}
out := &corev1alpha1.TenantSecretList{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1alpha1.SchemeGroupVersion.String(),
Kind: kindTenantSecretList,
},
ListMeta: list.ListMeta,
}
for i := range list.Items {
out.Items = append(out.Items, *secretToTenant(&list.Items[i]))
}
sort.Slice(out.Items, func(i, j int) bool { return out.Items[i].Name < out.Items[j].Name })
return out, nil
}
func (r *REST) Update(
ctx context.Context,
name string,
objInfo rest.UpdatedObjectInfo,
_ rest.ValidateObjectFunc,
_ rest.ValidateObjectUpdateFunc,
forceCreate bool,
opts *metav1.UpdateOptions,
) (runtime.Object, bool, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, false, err
}
cur, err := r.core.Secrets(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return nil, false, err
}
newObj, err := objInfo.UpdatedObject(ctx, nil)
if err != nil {
return nil, false, err
}
in := newObj.(*corev1alpha1.TenantSecret)
newSec := tenantToSecret(in, cur)
if cur == nil {
if !forceCreate && err == nil {
return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
out, err := r.core.Secrets(ns).Create(ctx, newSec, metav1.CreateOptions{})
return secretToTenant(out), true, err
}
newSec.ResourceVersion = cur.ResourceVersion
out, err := r.core.Secrets(ns).Update(ctx, newSec, *opts)
return secretToTenant(out), false, err
}
func (r *REST) Delete(
ctx context.Context,
name string,
_ rest.ValidateObjectFunc,
opts *metav1.DeleteOptions,
) (runtime.Object, bool, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, false, err
}
err = r.core.Secrets(ns).Delete(ctx, name, *opts)
return nil, err == nil, err
}
func (r *REST) Patch(
ctx context.Context,
name string,
pt types.PatchType,
data []byte,
opts *metav1.PatchOptions,
subresources ...string,
) (runtime.Object, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, err
}
out, err := r.core.Secrets(ns).
Patch(ctx, name, pt, data, *opts, subresources...)
if err != nil {
return nil, err
}
// Ensure UI label is preserved
if out.Labels[uiLabelKey] != uiLabelValue {
out.Labels[uiLabelKey] = uiLabelValue
out, _ = r.core.Secrets(ns).Update(ctx, out, metav1.UpdateOptions{})
}
return secretToTenant(out), nil
}
// -----------------------------------------------------------------------------
// Watcher
// -----------------------------------------------------------------------------
func (r *REST) Watch(ctx context.Context, opts *metainternal.ListOptions) (watch.Interface, error) {
ns, err := nsFrom(ctx)
if err != nil {
return nil, err
}
ls := labels.Set{uiLabelKey: uiLabelValue}.AsSelector().String()
base, err := r.core.Secrets(ns).Watch(ctx, metav1.ListOptions{
Watch: true,
LabelSelector: ls,
ResourceVersion: opts.ResourceVersion,
})
if err != nil {
return nil, err
}
ch := make(chan watch.Event)
proxy := watch.NewProxyWatcher(ch)
go func() {
defer proxy.Stop()
for ev := range base.ResultChan() {
sec, ok := ev.Object.(*corev1.Secret)
if !ok || sec == nil {
continue
}
tenant := secretToTenant(sec)
ch <- watch.Event{
Type: ev.Type,
Object: tenant,
}
}
}()
return proxy, nil
}
// -----------------------------------------------------------------------------
// TableConvertor
// -----------------------------------------------------------------------------
func (r *REST) ConvertToTable(_ context.Context, obj runtime.Object, _ runtime.Object) (*metav1.Table, error) {
now := time.Now()
row := func(o *corev1alpha1.TenantSecret) metav1.TableRow {
return metav1.TableRow{
Cells: []interface{}{o.Name, o.Type, duration.HumanDuration(now.Sub(o.CreationTimestamp.Time))},
Object: runtime.RawExtension{Object: o},
}
}
tbl := &metav1.Table{
TypeMeta: metav1.TypeMeta{APIVersion: "meta.k8s.io/v1", Kind: "Table"},
ColumnDefinitions: []metav1.TableColumnDefinition{
{Name: "NAME", Type: "string"},
{Name: "TYPE", Type: "string"},
{Name: "AGE", Type: "string"},
},
}
switch v := obj.(type) {
case *corev1alpha1.TenantSecretList:
for i := range v.Items {
tbl.Rows = append(tbl.Rows, row(&v.Items[i]))
}
tbl.ListMeta.ResourceVersion = v.ListMeta.ResourceVersion
case *corev1alpha1.TenantSecret:
tbl.Rows = append(tbl.Rows, row(v))
tbl.ListMeta.ResourceVersion = v.ResourceVersion
default:
return nil, notAcceptable{r.gvr.GroupResource(), fmt.Sprintf("unexpected %T", obj)}
}
return tbl, nil
}
// -----------------------------------------------------------------------------
// Boiler-plate
// -----------------------------------------------------------------------------
func (*REST) Destroy() {}
type notAcceptable struct {
resource schema.GroupResource
message string
}
func (e notAcceptable) Error() string { return e.message }
func (e notAcceptable) Status() metav1.Status {
return metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotAcceptable,
Reason: metav1.StatusReason("NotAcceptable"),
Message: e.message,
}
}

View File

@@ -17,24 +17,17 @@ limitations under the License.
package registry
import (
"github.com/cozystack/cozystack/pkg/registry/apps/application"
"k8s.io/apimachinery/pkg/runtime/schema"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
)
// REST implements a RESTStorage for API services against etcd
// REST is a thin wrapper around genericregistry.Store that also satisfies
// the GroupVersionKindProvider interface if callers need it later.
type REST struct {
*genericregistry.Store
GVK schema.GroupVersionKind
}
// Implement the GroupVersionKindProvider interface
func (r *REST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
return r.GVK
}
// RESTInPeace creates REST for Application
func RESTInPeace(r *application.REST) rest.Storage {
return r
}
// RESTInPeace is a tiny helper so the call-site code reads nicely. It simply
// returns its argument, letting us defer (and centralise) any future error
// handling here.
func RESTInPeace(storage rest.Storage) rest.Storage { return storage }

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