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 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cyclops-ctrl/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ DISABLE_TELEMETRY=true
PORT=8888
WATCH_NAMESPACE=cyclops
CYCLOPS_VERSION=v0.0.0
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,
}
}
28 changes: 27 additions & 1 deletion cyclops-ctrl/cmd/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strconv"
"strings"
"time"

_ "github.com/joho/godotenv/autoload"
Expand Down Expand Up @@ -70,7 +71,9 @@ func main() {
)
telemetryClient.InstanceStart()

k8sClient, err := k8sclient.New()
config := getConfig()

k8sClient, err := k8sclient.New(config.ChildLabels)
if err != nil {
fmt.Println("error bootstrapping Kubernetes client", err)
panic(err)
Expand Down Expand Up @@ -168,3 +171,26 @@ func getWatchNamespace(key string) string {
}
return value
}

func getCustomChildLabels() map[string]string {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function is not in use

labels := make(map[string]string)

value := os.Getenv("CHILD_LABEL_SELECTOR")
if value == "" {
return labels
}

pairs := strings.Split(value, ";")

for _, pair := range pairs {
kv := strings.SplitN(pair, ":", 2)

if len(kv) == 2 {
key := kv[0]
value := kv[1]
labels[key] = value
}
}

return labels
}
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 @@ -626,13 +626,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
53 changes: 53 additions & 0 deletions cyclops-ctrl/pkg/cluster/k8sclient/childlabels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package k8sclient

import (
"bytes"
"text/template"

"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
}
25 changes: 8 additions & 17 deletions cyclops-ctrl/pkg/cluster/k8sclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package k8sclient

import (
"context"

apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -18,25 +17,16 @@ import (
"github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto"
)

const (
cyclopsNamespace = "cyclops"
)

type KubernetesClient struct {
Dynamic dynamic.Interface

Dynamic dynamic.Interface
clientset *kubernetes.Clientset

discovery *discovery.DiscoveryClient

moduleset *client.CyclopsV1Alpha1Client
}

func New() (*KubernetesClient, error) {
return createLocalClient()
childLabels ChildLabels
}

func createLocalClient() (*KubernetesClient, error) {
func New(childLabels ChildLabels) (*KubernetesClient, error) {
config := ctrl.GetConfigOrDie()
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
Expand All @@ -56,10 +46,11 @@ func createLocalClient() (*KubernetesClient, error) {
}

return &KubernetesClient{
Dynamic: dynamic,
discovery: discovery,
clientset: clientset,
moduleset: moduleSet,
Dynamic: dynamic,
discovery: discovery,
clientset: clientset,
moduleset: moduleSet,
childLabels: childLabels,
}, nil
}

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 @@ -107,6 +107,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 @@ -141,11 +141,51 @@ func (k *KubernetesClient) GetResource(group, version, kind, name, namespace str
return k.mapRole(group, version, kind, name, namespace)
case isNetworkPolicy(group, version, kind):
return k.mapNetworkPolicy(group, version, kind, name, namespace)
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