- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
222 lines
6.4 KiB
Go
222 lines
6.4 KiB
Go
package discovery
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
)
|
|
|
|
// KubernetesDiscoveryAgent discovers Kubernetes resources
|
|
type KubernetesDiscoveryAgent struct {
|
|
client kubernetes.Interface
|
|
site string
|
|
region string
|
|
}
|
|
|
|
// NewKubernetesDiscoveryAgent creates a new Kubernetes discovery agent
|
|
func NewKubernetesDiscoveryAgent(client kubernetes.Interface, site, region string) *KubernetesDiscoveryAgent {
|
|
return &KubernetesDiscoveryAgent{
|
|
client: client,
|
|
site: site,
|
|
region: region,
|
|
}
|
|
}
|
|
|
|
// DiscoverNamespaces discovers all namespaces
|
|
func (a *KubernetesDiscoveryAgent) DiscoverNamespaces(ctx context.Context) ([]DiscoveredResource, error) {
|
|
namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to list namespaces")
|
|
}
|
|
|
|
var resources []DiscoveredResource
|
|
for _, ns := range namespaces.Items {
|
|
resources = append(resources, DiscoveredResource{
|
|
ResourceType: "NAMESPACE",
|
|
Provider: "KUBERNETES",
|
|
ProviderID: fmt.Sprintf("namespace:%s", ns.Name),
|
|
Name: ns.Name,
|
|
Region: a.region,
|
|
SiteID: a.site,
|
|
Metadata: map[string]interface{}{
|
|
"uid": string(ns.UID),
|
|
"labels": ns.Labels,
|
|
"annotations": ns.Annotations,
|
|
"phase": string(ns.Status.Phase),
|
|
},
|
|
Tags: getTagsFromLabels(ns.Labels),
|
|
})
|
|
}
|
|
|
|
return resources, nil
|
|
}
|
|
|
|
// DiscoverDeployments discovers all deployments
|
|
func (a *KubernetesDiscoveryAgent) DiscoverDeployments(ctx context.Context) ([]DiscoveredResource, error) {
|
|
namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to list namespaces")
|
|
}
|
|
|
|
var resources []DiscoveredResource
|
|
for _, ns := range namespaces.Items {
|
|
deployments, err := a.client.AppsV1().Deployments(ns.Name).List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
continue // Skip namespaces we can't access
|
|
}
|
|
|
|
for _, dep := range deployments.Items {
|
|
resources = append(resources, DiscoveredResource{
|
|
ResourceType: "DEPLOYMENT",
|
|
Provider: "KUBERNETES",
|
|
ProviderID: fmt.Sprintf("deployment:%s:%s", ns.Name, dep.Name),
|
|
Name: dep.Name,
|
|
Region: a.region,
|
|
SiteID: a.site,
|
|
Metadata: map[string]interface{}{
|
|
"namespace": ns.Name,
|
|
"uid": string(dep.UID),
|
|
"replicas": dep.Spec.Replicas,
|
|
"readyReplicas": dep.Status.ReadyReplicas,
|
|
"labels": dep.Labels,
|
|
"annotations": dep.Annotations,
|
|
},
|
|
Tags: getTagsFromLabels(dep.Labels),
|
|
})
|
|
}
|
|
}
|
|
|
|
return resources, nil
|
|
}
|
|
|
|
// DiscoverServices discovers all services
|
|
func (a *KubernetesDiscoveryAgent) DiscoverServices(ctx context.Context) ([]DiscoveredResource, error) {
|
|
namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to list namespaces")
|
|
}
|
|
|
|
var resources []DiscoveredResource
|
|
for _, ns := range namespaces.Items {
|
|
services, err := a.client.CoreV1().Services(ns.Name).List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, svc := range services.Items {
|
|
resources = append(resources, DiscoveredResource{
|
|
ResourceType: "SERVICE",
|
|
Provider: "KUBERNETES",
|
|
ProviderID: fmt.Sprintf("service:%s:%s", ns.Name, svc.Name),
|
|
Name: svc.Name,
|
|
Region: a.region,
|
|
SiteID: a.site,
|
|
Metadata: map[string]interface{}{
|
|
"namespace": ns.Name,
|
|
"uid": string(svc.UID),
|
|
"type": string(svc.Spec.Type),
|
|
"clusterIP": svc.Spec.ClusterIP,
|
|
"ports": svc.Spec.Ports,
|
|
"labels": svc.Labels,
|
|
"annotations": svc.Annotations,
|
|
},
|
|
Tags: getTagsFromLabels(svc.Labels),
|
|
})
|
|
}
|
|
}
|
|
|
|
return resources, nil
|
|
}
|
|
|
|
// DiscoverIngress discovers all ingress resources
|
|
func (a *KubernetesDiscoveryAgent) DiscoverIngress(ctx context.Context) ([]DiscoveredResource, error) {
|
|
namespaces, err := a.client.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to list namespaces")
|
|
}
|
|
|
|
var resources []DiscoveredResource
|
|
for _, ns := range namespaces.Items {
|
|
ingresses, err := a.client.NetworkingV1().Ingresses(ns.Name).List(ctx, metav1.ListOptions{})
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
for _, ing := range ingresses.Items {
|
|
resources = append(resources, DiscoveredResource{
|
|
ResourceType: "INGRESS",
|
|
Provider: "KUBERNETES",
|
|
ProviderID: fmt.Sprintf("ingress:%s:%s", ns.Name, ing.Name),
|
|
Name: ing.Name,
|
|
Region: a.region,
|
|
SiteID: a.site,
|
|
Metadata: map[string]interface{}{
|
|
"namespace": ns.Name,
|
|
"uid": string(ing.UID),
|
|
"hosts": ing.Spec.Rules,
|
|
"tls": ing.Spec.TLS,
|
|
"labels": ing.Labels,
|
|
"annotations": ing.Annotations,
|
|
},
|
|
Tags: getTagsFromLabels(ing.Labels),
|
|
})
|
|
}
|
|
}
|
|
|
|
return resources, nil
|
|
}
|
|
|
|
// DiscoverAll discovers all Kubernetes resources
|
|
func (a *KubernetesDiscoveryAgent) DiscoverAll(ctx context.Context) ([]DiscoveredResource, error) {
|
|
var allResources []DiscoveredResource
|
|
|
|
// Discover namespaces
|
|
namespaces, err := a.DiscoverNamespaces(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to discover namespaces")
|
|
}
|
|
allResources = append(allResources, namespaces...)
|
|
|
|
// Discover deployments
|
|
deployments, err := a.DiscoverDeployments(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to discover deployments")
|
|
}
|
|
allResources = append(allResources, deployments...)
|
|
|
|
// Discover services
|
|
services, err := a.DiscoverServices(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to discover services")
|
|
}
|
|
allResources = append(allResources, services...)
|
|
|
|
// Discover ingress
|
|
ingresses, err := a.DiscoverIngress(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to discover ingress")
|
|
}
|
|
allResources = append(allResources, ingresses...)
|
|
|
|
return allResources, nil
|
|
}
|
|
|
|
// getTagsFromLabels extracts tags from Kubernetes labels
|
|
func getTagsFromLabels(labels map[string]string) []string {
|
|
var tags []string
|
|
for key, value := range labels {
|
|
if key == "tags" || key == "tag" {
|
|
// If label value is comma-separated, split it
|
|
// Otherwise, use key=value format
|
|
tags = append(tags, value)
|
|
} else if key == "app" || key == "component" || key == "environment" {
|
|
tags = append(tags, fmt.Sprintf("%s:%s", key, value))
|
|
}
|
|
}
|
|
return tags
|
|
}
|
|
|