Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

list custom resource managed resources #634

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions cyclops-ctrl/.env
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ WATCH_NAMESPACE=cyclops
WATCH_NAMESPACE_HELM=
CYCLOPS_VERSION=v0.0.0
MODULE_TARGET_NAMESPACE=
CONFIG_PATH=config.yaml
67 changes: 67 additions & 0 deletions cyclops-ctrl/cmd/main/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"github.com/cyclops-ui/cyclops/cyclops-ctrl/pkg/cluster/k8sclient"
"gopkg.in/yaml.v3"
"io"
"os"
)

type ControllerConfig struct {
ChildLabels k8sclient.ChildLabels `json:"childLabels"`
}

func getConfig() ControllerConfig {
configPath := os.Getenv("CONFIG_PATH")
if configPath == "" {
configPath = "/etc/config/config.yaml"
}

configFile, err := os.Open(configPath)
if err != nil {
setupLog.Error(err, "Failed to open controller config", "configPath", configPath)
return ControllerConfig{}
}
defer configFile.Close()

b, err := io.ReadAll(configFile)
if err != nil {
setupLog.Error(err, "Failed to read controller config", "configPath", configPath)
return ControllerConfig{}
}

type resourceChildLabels struct {
Group string `yaml:"group"`
Version string `yaml:"version"`
Kind string `yaml:"kind"`
MatchLabels map[string]string `yaml:"matchLabels"`
}

type yamlConfig struct {
ChildLabels []resourceChildLabels `yaml:"childLabels"`
}

var c *yamlConfig
err = yaml.Unmarshal(b, &c)
if err != nil {
setupLog.Error(err, "Failed to YAML unmarshal controller config", "configPath", configPath)
return ControllerConfig{}
}

if c == nil {
return ControllerConfig{}
}

childLabels := make(map[k8sclient.GroupVersionKind]map[string]string)
for _, label := range c.ChildLabels {
childLabels[k8sclient.GroupVersionKind{
Group: label.Group,
Version: label.Version,
Kind: label.Kind,
}] = label.MatchLabels
}

return ControllerConfig{
ChildLabels: childLabels,
}
}
2 changes: 1 addition & 1 deletion cyclops-ctrl/cmd/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func main() {
}),
Cache: ctrlCache.Options{
DefaultNamespaces: map[string]ctrlCache.Config{
watchNamespace: {},
getWatchNamespace(): {},
},
},
})
Expand Down
6 changes: 6 additions & 0 deletions cyclops-ctrl/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
childLabels:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment so that people know it's an example for testing

- group: postgresql.cnpg.io
version: v1
kind: Cluster
matchLabels:
cnpg.io/cluster: "{{ .metadata.name }}"
15 changes: 8 additions & 7 deletions cyclops-ctrl/internal/models/dto/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,13 +633,14 @@ type IPBlock struct {
}

type Other struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Status string `json:"status"`
Deleted bool `json:"deleted"`
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Status string `json:"status"`
Deleted bool `json:"deleted"`
Children []Resource `json:"children"`
}

func (s *Other) GetGroupVersionKind() string {
Expand Down
101 changes: 101 additions & 0 deletions cyclops-ctrl/pkg/cluster/k8sclient/childlabels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package k8sclient

import (
"bytes"
"context"
"text/template"

"gopkg.in/yaml.v3"

v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

const (
cyclopsNamespace = "cyclops"
)

type GroupVersionKind struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
}

type ChildLabels map[GroupVersionKind]map[string]string

func (k *KubernetesClient) getChildLabel(
group, version, kind string,
obj *unstructured.Unstructured,
) (map[string]string, bool, error) {
labels, exists := k.childLabels[GroupVersionKind{
Group: group,
Version: version,
Kind: kind,
}]

if !exists {
return nil, false, nil
}

matchLabels := make(map[string]string)
for k, v := range labels {
t, err := template.New("matchLabel").Parse(v)
if err != nil {
return nil, false, err
}

var o bytes.Buffer
err = t.Execute(&o, obj.Object)
if err != nil {
return nil, false, err
}

matchLabels[k] = o.String()
}

return matchLabels, exists, nil
}

func (k *KubernetesClient) loadResourceRelationsLabels() {
configmap, err := k.clientset.CoreV1().ConfigMaps("cyclops").Get(context.Background(), "cyclops-ctrl", v1.GetOptions{})
if err != nil {
return
}

d, ok := configmap.Data["resource-relations"]
if !ok {
return
}

type resourceChildLabels struct {
Group string `yaml:"group"`
Version string `yaml:"version"`
Kind string `yaml:"kind"`
MatchLabels map[string]string `yaml:"matchLabels"`
}

type yamlConfig struct {
ChildLabels []resourceChildLabels `yaml:"childLabels"`
}

var c *yamlConfig
err = yaml.Unmarshal([]byte(d), &c)
if err != nil {
return
}

if c == nil {
return
}

childLabels := make(map[GroupVersionKind]map[string]string)
for _, label := range c.ChildLabels {
childLabels[GroupVersionKind{
Group: label.Group,
Version: label.Version,
Kind: label.Kind,
}] = label.MatchLabels
}

k.childLabels = childLabels
}
10 changes: 8 additions & 2 deletions cyclops-ctrl/pkg/cluster/k8sclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type KubernetesClient struct {
helmReleaseNamespace string
moduleTargetNamespace string

childLabels ChildLabels

logger logr.Logger
}

Expand Down Expand Up @@ -56,7 +58,7 @@ func New(
panic(err.Error())
}

return &KubernetesClient{
k := &KubernetesClient{
Dynamic: dynamic,
discovery: discovery,
clientset: clientset,
Expand All @@ -65,7 +67,11 @@ func New(
helmReleaseNamespace: helmReleaseNamespace,
moduleTargetNamespace: moduleTargetNamespace,
logger: logger,
}, nil
}

k.loadResourceRelationsLabels()

return k, nil
}

type IKubernetesClient interface {
Expand Down
50 changes: 50 additions & 0 deletions cyclops-ctrl/pkg/cluster/k8sclient/modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,56 @@ func (k *KubernetesClient) GetResourcesForModule(name string) ([]dto.Resource, e
return out, nil
}

func (k *KubernetesClient) GetResourcesForCRD(childLabels map[string]string, name string) ([]dto.Resource, error) {
out := make([]dto.Resource, 0, 0)

managedGVRs, err := k.getManagedGVRs("")
if err != nil {
return nil, err
}

other := make([]unstructured.Unstructured, 0)
for _, gvr := range managedGVRs {
rs, err := k.Dynamic.Resource(gvr).List(context.Background(), metav1.ListOptions{
LabelSelector: labels.Set(childLabels).String(),
})
if err != nil {
continue
}

for _, item := range rs.Items {
other = append(other, item)
}
}

for _, o := range other {
status, err := k.getResourceStatus(o)
if err != nil {
return nil, err
}

out = append(out, &dto.Other{
Group: o.GroupVersionKind().Group,
Version: o.GroupVersionKind().Version,
Kind: o.GroupVersionKind().Kind,
Name: o.GetName(),
Namespace: o.GetNamespace(),
Status: status,
Deleted: false,
})
}

sort.Slice(out, func(i, j int) bool {
if out[i].GetGroupVersionKind() != out[j].GetGroupVersionKind() {
return out[i].GetGroupVersionKind() < out[j].GetGroupVersionKind()
}

return out[i].GetName() < out[j].GetName()
})

return out, nil
}

func (k *KubernetesClient) GetWorkloadsForModule(name string) ([]dto.Resource, error) {
out := make([]dto.Resource, 0, 0)

Expand Down
40 changes: 40 additions & 0 deletions cyclops-ctrl/pkg/cluster/k8sclient/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,51 @@ func (k *KubernetesClient) GetResource(group, version, kind, name, namespace str
return k.mapNetworkPolicy(group, version, kind, name, namespace)
case isClusterRole(group, version, kind):
return k.mapClusterRole(group, version, kind, name)
default:
return k.mapDefaultResource(group, version, kind, name, namespace)
}

return nil, nil
}

func (k *KubernetesClient) mapDefaultResource(group, version, kind, name, namespace string) (any, error) {
apiResourceName, err := k.GVKtoAPIResourceName(schema.GroupVersion{Group: group, Version: version}, kind)
if err != nil {
return nil, err
}

resource, err := k.Dynamic.Resource(schema.GroupVersionResource{
Group: group,
Version: version,
Resource: apiResourceName,
}).Namespace(namespace).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
return "", err
}

childLabels, exists, err := k.getChildLabel(group, version, kind, resource)
if err != nil {
return nil, err
}

var children []dto.Resource
if exists {
children, err = k.GetResourcesForCRD(childLabels, name)
if err != nil {
return nil, err
}
}

return &dto.Other{
Group: group,
Version: version,
Kind: kind,
Name: name,
Namespace: namespace,
Children: children,
}, nil
}

func (k *KubernetesClient) Delete(resource dto.Resource) error {
apiResourceName, err := k.GVKtoAPIResourceName(
schema.GroupVersion{
Expand Down
Loading
Loading