[cozystack-api][dashboard] Fix filtering for application services/ingresses/secrets (#1612)

<!-- 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-controller] Fix labelSelectors**
- **[cozystack-api] Enhance TenantSecrets filtering**
- **[cozystack-api] Fix sorting for TenantSecrets**

### 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][dashboard] Fix filtering for application services/ingresses/secrets
```

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

## Summary by CodeRabbit

* **Refactor**
* Standardized internal configuration naming conventions across
dashboard components.
* Enhanced tenant secret validation and filtering logic with improved
label-based operations for consistency and correctness.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil
2025-11-07 15:36:24 +01:00
committed by GitHub
3 changed files with 61 additions and 21 deletions

View File

@@ -221,7 +221,7 @@ func workloadsTab(kind string) map[string]any {
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.cozystack.io.workloadmonitors",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
@@ -246,7 +246,7 @@ func servicesTab(kind string) map[string]any {
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1.services",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
@@ -272,7 +272,7 @@ func ingressesTab(kind string) map[string]any {
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-networking.k8s.io.v1.ingresses",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",
@@ -298,7 +298,7 @@ func secretsTab(kind string) map[string]any {
"baseprefix": "/openapi-ui",
"customizationId": "factory-details-v1alpha1.core.cozystack.io.tenantsecrets",
"pathToItems": []any{"items"},
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"apps.cozystack.io/application.group": "apps.cozystack.io",
"apps.cozystack.io/application.kind": kind,
"apps.cozystack.io/application.name": "{reqs[0]['metadata','name']}",

View File

@@ -1056,7 +1056,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
"clusterNamePartOfUrl": "{2}",
"customizationId": "factory-kube-service-details-endpointslice",
"fetchUrl": "/api/clusters/{2}/k8s/apis/discovery.k8s.io/v1/namespaces/{3}/endpointslices",
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"kubernetes.io/service-name": "{reqsJsonPath[0]['.metadata.name']['-']}",
},
"pathToItems": ".items[*].endpoints",
@@ -1397,7 +1397,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
"clusterNamePartOfUrl": "{2}",
"customizationId": "factory-details-v1alpha1.cozystack.io.workloads",
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloads",
"labelsSelector": map[string]any{
"labelSelector": map[string]any{
"workloads.cozystack.io/monitor": "{reqs[0]['metadata','name']}",
},
"pathToItems": []any{"items"},

View File

@@ -9,7 +9,7 @@ import (
"encoding/base64"
"fmt"
"net/http"
"sort"
"slices"
"time"
corev1 "k8s.io/api/core/v1"
@@ -226,6 +226,9 @@ func (r *REST) Get(
if err != nil {
return nil, err
}
if sec.Labels == nil || sec.Labels[tsLabelKey] != tsLabelValue {
return nil, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
return secretToTenant(sec), nil
}
@@ -253,11 +256,13 @@ func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtim
list := &corev1.SecretList{}
err = r.c.List(ctx, list,
&client.ListOptions{
Namespace: ns,
Namespace: ns,
LabelSelector: ls,
Raw: &metav1.ListOptions{
LabelSelector: ls.String(),
FieldSelector: fieldSel,
}})
},
})
if err != nil {
return nil, err
}
@@ -273,7 +278,17 @@ func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtim
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 })
slices.SortFunc(out.Items, func(a, b corev1alpha1.TenantSecret) int {
aKey := fmt.Sprintf("%s/%s", a.Namespace, a.Name)
bKey := fmt.Sprintf("%s/%s", b.Namespace, b.Name)
switch {
case aKey < bKey:
return -1
case aKey > bKey:
return 1
}
return 0
})
return out, nil
}
@@ -291,10 +306,17 @@ func (r *REST) Update(
return nil, false, err
}
cur := &corev1.Secret{}
err = r.c.Get(ctx, types.NamespacedName{Namespace: ns, Name: name}, cur, &client.GetOptions{Raw: &metav1.GetOptions{}})
if err != nil && !apierrors.IsNotFound(err) {
return nil, false, err
var cur *corev1.Secret
previous := &corev1.Secret{}
if err := r.c.Get(ctx, types.NamespacedName{Namespace: ns, Name: name}, previous, &client.GetOptions{Raw: &metav1.GetOptions{}}); err != nil {
if !apierrors.IsNotFound(err) {
return nil, false, err
}
} else {
if previous.Labels == nil || previous.Labels[tsLabelKey] != tsLabelValue {
return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
cur = previous
}
newObj, err := objInfo.UpdatedObject(ctx, nil)
@@ -306,7 +328,7 @@ func (r *REST) Update(
newSec := tenantToSecret(in, cur)
newSec.Namespace = ns
if cur == nil {
if !forceCreate && err == nil {
if !forceCreate {
return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
err := r.c.Create(ctx, newSec, &client.CreateOptions{Raw: &metav1.CreateOptions{}})
@@ -328,6 +350,13 @@ func (r *REST) Delete(
if err != nil {
return nil, false, err
}
current := &corev1.Secret{}
if err := r.c.Get(ctx, types.NamespacedName{Namespace: ns, Name: name}, current, &client.GetOptions{Raw: &metav1.GetOptions{}}); err != nil {
return nil, false, err
}
if current.Labels == nil || current.Labels[tsLabelKey] != tsLabelValue {
return nil, false, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
err = r.c.Delete(ctx, &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: name}}, &client.DeleteOptions{Raw: opts})
return nil, err == nil, err
}
@@ -347,6 +376,13 @@ func (r *REST) Patch(
if err != nil {
return nil, err
}
current := &corev1.Secret{}
if err := r.c.Get(ctx, types.NamespacedName{Namespace: ns, Name: name}, current, &client.GetOptions{Raw: &metav1.GetOptions{}}); err != nil {
return nil, err
}
if current.Labels == nil || current.Labels[tsLabelKey] != tsLabelValue {
return nil, apierrors.NewNotFound(r.gvr.GroupResource(), name)
}
out := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns,
@@ -383,12 +419,16 @@ func (r *REST) Watch(ctx context.Context, opts *metainternal.ListOptions) (watch
}
secList := &corev1.SecretList{}
ls := labels.Set{tsLabelKey: tsLabelValue}.AsSelector().String()
base, err := r.w.Watch(ctx, secList, &client.ListOptions{Namespace: ns, Raw: &metav1.ListOptions{
Watch: true,
LabelSelector: ls,
ResourceVersion: opts.ResourceVersion,
}})
ls := labels.Set{tsLabelKey: tsLabelValue}.AsSelector()
base, err := r.w.Watch(ctx, secList, &client.ListOptions{
Namespace: ns,
LabelSelector: ls,
Raw: &metav1.ListOptions{
Watch: true,
LabelSelector: ls.String(),
ResourceVersion: opts.ResourceVersion,
},
})
if err != nil {
return nil, err
}