remove unused file

Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero 2026-01-25 19:05:01 +08:00
parent 8f9bcbd1ea
commit 9cad1ab490
6 changed files with 1 additions and 1389 deletions

View File

@ -1,238 +0,0 @@
# Custom Resource Tracking
This document describes how to configure custom resource tracking in the kubedog tracker.
## Overview
The kubedog tracker now supports flexible configuration for resource tracking through the `TrackOptions` struct. You can:
- **Track specific kinds**: Only track resources of specified types
- **Skip specific kinds**: Exclude certain resource types from tracking
- **Define custom trackable kinds**: Add new resource types that should be actively tracked
- **Define custom static kinds**: Add new resource types that don't need active tracking
## Configuration Options
### TrackKinds
When set, only resources in this list will be tracked. All other resources are ignored.
**Example:**
```go
opts := NewTrackOptions().WithTrackKinds([]string{"Deployment", "StatefulSet"})
```
This configuration will:
- Track only `Deployment` and `StatefulSet` resources
- Ignore all other resource types (Service, ConfigMap, etc.)
### SkipKinds
Resources in this list will be skipped, even if they would normally be tracked.
**Example:**
```go
opts := NewTrackOptions().WithSkipKinds([]string{"ConfigMap", "Secret"})
```
This configuration will:
- Track all normally trackable resources (Deployment, StatefulSet, etc.)
- Skip `ConfigMap` and `Secret` resources
### CustomTrackableKinds
Define additional resource types that should be actively tracked. When configured, only these custom types and resources in `TrackKinds` (if set) will be considered trackable.
**Example:**
```go
opts := NewTrackOptions().WithCustomTrackableKinds([]string{"CronJob", "ReplicationController"})
```
This configuration will:
- Treat `CronJob` and `ReplicationController` as trackable resources
- Ignore default trackable kinds (Deployment, StatefulSet, etc.) unless also in `TrackKinds`
### CustomStaticKinds
Define additional resource types that are considered static and don't need active tracking.
**Example:**
```go
opts := NewTrackOptions().WithCustomStaticKinds([]string{"NetworkPolicy", "PodDisruptionBudget"})
```
This configuration will:
- Treat `NetworkPolicy` and `PodDisruptionBudget` as static resources
- Ignore default static kinds unless not in this list
## Usage Examples
### Example 1: Track Only Deployments and StatefulSets
```go
package main
import (
"github.com/helmfile/helmfile/pkg/kubedog"
"go.uber.org/zap"
)
func main() {
// Configure tracker to only track Deployments and StatefulSets
opts := kubedog.NewTrackOptions().
WithTrackKinds([]string{"Deployment", "StatefulSet"}).
WithTimeout(600)
config := &kubedog.TrackerConfig{
Logger: zap.NewExample().Sugar(),
Namespace: "default",
KubeContext: "",
Kubeconfig: "",
TrackOptions: opts,
}
tracker, err := kubedog.NewTracker(config)
if err != nil {
panic(err)
}
manifest := []byte(`
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
spec:
replicas: 3
---
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: myapp
ports:
- port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-statefulset
namespace: default
spec:
replicas: 1
`)
// This will track Deployment and StatefulSet, but skip Service
err = tracker.TrackReleaseWithManifest(nil, "my-release", "default", manifest)
if err != nil {
panic(err)
}
tracker.Close()
}
```
### Example 2: Skip ConfigMaps and Secrets
```go
opts := kubedog.NewTrackOptions().
WithSkipKinds([]string{"ConfigMap", "Secret"})
// This will track all normally trackable resources (Deployment, StatefulSet, etc.)
// but skip ConfigMap and Secret resources
```
### Example 3: Add Custom Trackable Kind (CronJob)
```go
opts := kubedog.NewTrackOptions().
WithCustomTrackableKinds([]string{"CronJob"})
// This will treat CronJob as a trackable resource and wait for it
// Default trackable kinds (Deployment, StatefulSet) will not be tracked
```
### Example 4: Combined Configuration
```go
opts := kubedog.NewTrackOptions().
WithTrackKinds([]string{"Deployment", "CronJob"}).
WithSkipKinds([]string{"ConfigMap"})
// This configuration:
// 1. Only tracks Deployment and CronJob resources
// 2. Skips ConfigMap even if it appears in the manifest
// 3. Ignores all other resource types
```
## Priority and Behavior
The tracker evaluates configuration in the following order:
1. **SkipKinds**: If a resource kind is in SkipKinds, it's immediately skipped
2. **TrackKinds**: If TrackKinds is set, only resources in this list are considered
3. **CustomTrackableKinds / CustomStaticKinds**:
- If CustomTrackableKinds is set, only these kinds are considered trackable
- If CustomStaticKinds is set, only these kinds are considered static
- Otherwise, default trackable/static lists are used
## Resource Classification
### Default Trackable Kinds
These resources are actively tracked by default:
- Deployment
- StatefulSet
- DaemonSet
- Job
- Pod
- ReplicaSet
### Default Static Kinds
These resources don't need active tracking by default:
- Service
- ConfigMap
- Secret
- PersistentVolume
- PersistentVolumeClaim
- StorageClass
- Namespace
- ResourceQuota
- LimitRange
- PriorityClass
- ServiceAccount
- Role
- RoleBinding
- ClusterRole
- ClusterRoleBinding
- NetworkPolicy
- Ingress
- CustomResourceDefinition
## Integration with Helmfile
When using kubedog tracker with Helmfile, you can configure tracking options in your helmfile.yaml:
```yaml
releases:
- name: my-app
namespace: default
chart: ./charts/my-app
track:
timeout: 600
trackKinds:
- Deployment
- StatefulSet
skipKinds:
- ConfigMap
```
## See Also
- [Resource Detection Guide](./RESOURCE_DETECTION.md)
- [Helmfile README](../README.md)

View File

@ -1,302 +0,0 @@
# Implementation Summary: Custom Resource Tracking
## Overview
Added custom resource tracking configuration to the kubedog tracker, allowing users to flexibly control which resources are tracked and how they are classified.
## Changes Made
### 1. TrackOptions Enhancements (`pkg/kubedog/options.go`)
Added new configuration fields to `TrackOptions` struct:
```go
type TrackOptions struct {
Timeout int
Logs bool
LogsSince int
Namespace string
KubeContext string
Kubeconfig string
// NEW FIELDS
TrackKinds []string // Only track resources of these kinds
SkipKinds []string // Skip resources of these kinds
CustomTrackableKinds []string // Custom kinds that should be actively tracked
CustomStaticKinds []string // Custom kinds that don't need tracking
}
```
Added builder methods:
```go
func (o *TrackOptions) WithTrackKinds(kinds []string) *TrackOptions
func (o *TrackOptions) WithSkipKinds(kinds []string) *TrackOptions
func (o *TrackOptions) WithCustomTrackableKinds(kinds []string) *TrackOptions
func (o *TrackOptions) WithCustomStaticKinds(kinds []string) *TrackOptions
```
### 2. TrackConfig Structure (`pkg/cluster/release.go`)
Added `TrackConfig` struct to pass tracking configuration:
```go
type TrackConfig struct {
TrackKinds []string
SkipKinds []string
CustomTrackableKinds []string
CustomStaticKinds []string
}
```
### 3. Enhanced Resource Detection (`pkg/cluster/release.go`)
Added new functions for resource filtering and classification:
```go
// Get resources with custom tracking configuration
func GetReleaseResourcesFromManifestWithConfig(
manifest []byte,
releaseName, releaseNamespace string,
config *TrackConfig,
) (*ReleaseResources, error)
// Filter resources based on TrackKinds and SkipKinds
func filterResourcesByConfig(
resources []Resource,
config *TrackConfig,
logger *zap.SugaredLogger,
) []Resource
// Check if resource is trackable with custom config
func IsTrackableKindWithConfig(kind string, config *TrackConfig) bool
// Check if resource is static with custom config
func IsStaticKindWithConfig(kind string, config *TrackConfig) bool
```
### 4. Enhanced Tracker (`pkg/kubedog/tracker.go`)
Updated `TrackReleaseWithManifest` to use custom configuration:
```go
func (t *Tracker) TrackReleaseWithManifest(
ctx interface{},
releaseName, releaseNamespace string,
manifest []byte,
) error {
// Create TrackConfig from TrackOptions
trackConfig := &cluster.TrackConfig{
TrackKinds: t.options.TrackKinds,
SkipKinds: t.options.SkipKinds,
CustomTrackableKinds: t.options.CustomTrackableKinds,
CustomStaticKinds: t.options.CustomStaticKinds,
}
// Use config when getting resources
releaseResources, err := cluster.GetReleaseResourcesFromManifestWithLogger(
t.logger, manifest, releaseName, releaseNamespace, trackConfig,
)
// Pass config when tracking resources
return t.trackResources(ctx, releaseResources, trackConfig)
}
```
Added support for custom resources:
```go
func (t *Tracker) trackCustomResource(ctx context.Context, res cluster.Resource) error {
t.logger.Infof("Waiting for custom resource %s/%s to become ready", res.Namespace, res.Name)
return nil
}
```
## Configuration Behavior
### Priority Order
1. **SkipKinds**: Applied first - if a resource kind is in SkipKinds, it's skipped
2. **TrackKinds**: If set, only resources in this list are considered
3. **CustomTrackableKinds**: If set, only these kinds are considered trackable
4. **CustomStaticKinds**: If set, only these kinds are considered static
5. **Default Lists**: Fall back to default trackable/static kinds if no custom config
### Example Configurations
#### Only Track Deployments
```go
opts := NewTrackOptions().
WithTrackKinds([]string{"Deployment"})
```
#### Skip ConfigMaps
```go
opts := NewTrackOptions().
WithSkipKinds([]string{"ConfigMap"})
```
#### Add Custom Trackable Kind
```go
opts := NewTrackOptions().
WithCustomTrackableKinds([]string{"CronJob"})
```
#### Combined Configuration
```go
opts := NewTrackOptions().
WithTrackKinds([]string{"Deployment", "StatefulSet"}).
WithSkipKinds([]string{"ConfigMap"})
```
## Testing
### New Test Cases
#### Cluster Package Tests
- `TestTrackConfig_TrackKinds` - Test filtering by TrackKinds
- `TestTrackConfig_SkipKinds` - Test skipping by SkipKinds
- `TestTrackConfig_CustomTrackableKinds` - Test custom trackable kinds
- `TestTrackConfig_CustomStaticKinds` - Test custom static kinds
- `TestTrackConfig_Combined` - Test combined configuration
- `TestTrackConfig_Nil` - Test behavior with nil config
#### Kubedog Package Tests
- `TestTrackReleaseWithManifest_TrackKinds` - Test tracker with TrackKinds
- `TestTrackReleaseWithManifest_SkipKinds` - Test tracker with SkipKinds
- `TestTrackReleaseWithManifest_CustomTrackableKinds` - Test tracker with custom kinds
### Test Results
```bash
$ go test ./pkg/cluster/... -v
PASS: TestGetReleaseResourcesFromManifest
PASS: TestGetReleaseResourcesFromManifestWithLogger
PASS: TestIsTrackableKind
PASS: TestIsStaticKind
PASS: TestGetHelmReleaseLabels
PASS: TestGetHelmReleaseAnnotations
PASS: TestParseManifest
PASS: TestParseManifest_Empty
PASS: TestParseManifest_Nil
PASS: TestResource_ManifestContent
PASS: TestTrackConfig_TrackKinds
PASS: TestTrackConfig_SkipKinds
PASS: TestTrackConfig_CustomTrackableKinds
PASS: TestTrackConfig_CustomStaticKinds
PASS: TestTrackConfig_Combined
PASS: TestTrackConfig_Nil
PASS: TestDetectServerVersion_Integration
PASS: TestDetectServerVersion_InvalidConfig
ok github.com/helmfile/helmfile/pkg/cluster
$ go test ./pkg/kubedog/... -v
PASS: TestNewTracker
PASS: TestTracker_Close
PASS: TestTrackRelease_WithNoNamespace
PASS: TestTrackOptions
PASS: TestTrackMode
PASS: TestTrackReleaseWithManifest
PASS: TestTrackReleaseWithManifest_Empty
PASS: TestTrackReleaseWithManifest_InvalidYAML
PASS: TestTrackReleaseWithManifest_TrackKinds
PASS: TestTrackReleaseWithManifest_SkipKinds
PASS: TestTrackReleaseWithManifest_CustomTrackableKinds
ok github.com/helmfile/helmfile/pkg/kubedog
$ make check
(All checks pass)
```
## Documentation
### New Documentation Files
1. **docs/CUSTOM_TRACKING.md** - Comprehensive guide on custom tracking configuration
- Overview of all configuration options
- Usage examples for each option
- Priority and behavior explanation
- Default resource classifications
- Integration examples
2. **examples/custom_tracking/main.go** - Working example program
- Example 1: Default tracking (all resources)
- Example 2: Track only Deployments and StatefulSets
- Example 3: Skip ConfigMaps
- Example 4: Custom trackable kinds (CronJob)
- Example 5: Custom static kinds
## Usage
### Basic Usage
```go
import (
"github.com/helmfile/helmfile/pkg/kubedog"
"go.uber.org/zap"
)
func main() {
// Configure tracker to track only Deployments and StatefulSets
opts := kubedog.NewTrackOptions().
WithTrackKinds([]string{"Deployment", "StatefulSet"}).
WithTimeout(600)
config := &kubedog.TrackerConfig{
Logger: zap.NewExample().Sugar(),
Namespace: "default",
TrackOptions: opts,
}
tracker, err := kubedog.NewTracker(config)
if err != nil {
panic(err)
}
manifest := []byte(`... helm template output ...`)
err = tracker.TrackReleaseWithManifest(nil, "my-release", "default", manifest)
if err != nil {
panic(err)
}
tracker.Close()
}
```
### Advanced Configuration
```go
// Track only specific kinds, skip certain kinds, and add custom trackable kinds
opts := kubedog.NewTrackOptions().
WithTrackKinds([]string{"Deployment", "StatefulSet", "CronJob"}).
WithSkipKinds([]string{"ConfigMap", "Secret"}).
WithCustomTrackableKinds([]string{"CronJob"}).
WithTimeout(300)
```
## Benefits
1. **Flexibility**: Users can control exactly which resources are tracked
2. **Performance**: Skip tracking unnecessary resources to save time
3. **Customization**: Support for custom resource types (CRDs)
4. **Fine-grained Control**: Combine multiple options for precise control
5. **Backward Compatible**: Default behavior unchanged when no custom config is set
## Backward Compatibility
All changes are backward compatible:
- New fields in `TrackOptions` have default nil values
- When all new fields are nil, behavior is identical to previous version
- Existing functions `IsTrackableKind()` and `IsStaticKind()` still work
- New functions `IsTrackableKindWithConfig()` and `IsStaticKindWithConfig()` accept nil config
## Future Enhancements
Potential future improvements:
1. **Pattern Matching**: Support wildcards and regex in TrackKinds/SkipKinds
2. **Label-based Filtering**: Track resources based on labels/annotations
3. **Resource Limits**: Limit number of resources tracked concurrently
4. **Custom Tracking Logic**: Allow users to provide custom tracking functions
5. **Configuration File**: Support loading tracking config from YAML/JSON files

View File

@ -1,229 +0,0 @@
# Resource Detection Based on Helm Template
This document describes the new resource detection feature based on Helm template manifest.
## Overview
The kubedog tracker now supports detecting resources by parsing Helm template output instead of querying the Kubernetes API. This approach has several advantages:
- No need to connect to a Kubernetes cluster
- Faster execution
- Works in dry-run and template modes
- Simpler and more reliable
## Usage
### Track Release from Manifest
```go
package main
import (
"github.com/helmfile/helmfile/pkg/kubedog"
"go.uber.org/zap"
)
func main() {
logger := zap.NewExample().Sugar()
config := &kubedog.TrackerConfig{
Logger: logger,
Namespace: "default",
KubeContext: "",
Kubeconfig: "",
TrackOptions: kubedog.NewTrackOptions(),
}
tracker, err := kubedog.NewTracker(config)
if err != nil {
panic(err)
}
manifest := []byte(`
---
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: myapp
ports:
- port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: nginx:latest
`)
err = tracker.TrackReleaseWithManifest(nil, "my-release", "default", manifest)
if err != nil {
logger.Errorf("Failed to track release: %v", err)
}
tracker.Close()
}
```
### Parse Manifest Directly
```go
import (
"github.com/helmfile/helmfile/pkg/cluster"
)
func parseHelmOutput(manifest []byte) {
releaseResources, err := cluster.GetReleaseResourcesFromManifest(
manifest,
"my-release",
"default",
)
if err != nil {
panic(err)
}
fmt.Printf("Found %d resources:\n", len(releaseResources.Resources))
for _, res := range releaseResources.Resources {
fmt.Printf(" - %s/%s in namespace %s\n", res.Kind, res.Name, res.Namespace)
}
}
```
## Resource Classification
### Trackable Resources
Resources that need active tracking (wait for ready/completed):
- **Deployment** - Wait for all replicas to be ready
- **StatefulSet** - Wait for all replicas to be ready
- **DaemonSet** - Wait for desired number of scheduled nodes
- **Job** - Wait for job completion
- **Pod** - Wait for pod to be ready
- **ReplicaSet** - Wait for all replicas to be ready
### Static Resources
Resources that don't need active tracking (instantaneous creation):
- Service
- ConfigMap
- Secret
- PersistentVolume
- PersistentVolumeClaim
- StorageClass
- Namespace
- ResourceQuota
- LimitRange
- PriorityClass
- ServiceAccount
- Role
- RoleBinding
- ClusterRole
- ClusterRoleBinding
- NetworkPolicy
- Ingress
- CustomResourceDefinition
## Helper Functions
### Check if a resource kind is trackable
```go
isTrackable := cluster.IsTrackableKind("Deployment")
```
### Check if a resource kind is static
```go
isStatic := cluster.IsStaticKind("ConfigMap")
```
### Get Helm release labels
```go
labels := cluster.GetHelmReleaseLabels("my-release", "default")
// Returns:
// map[string]string{
// "meta.helm.sh/release-name": "my-release",
// "meta.helm.sh/release-namespace": "default",
// }
```
### Get Helm release annotations
```go
annotations := cluster.GetHelmReleaseAnnotations("my-release")
// Returns:
// map[string]string{
// "meta.helm.sh/release-name": "my-release",
// }
```
## Integration with Helmfile
The tracker can be integrated with Helmfile to track releases after installation:
```go
func (st *HelmState) trackRelease(release *ReleaseSpec) error {
if st.Tracker == nil {
return nil
}
manifest, err := st.getManifest(release)
if err != nil {
return err
}
return st.Tracker.TrackReleaseWithManifest(
context.Background(),
release.Name,
release.Namespace,
manifest,
)
}
```
## Advantages Over API-Based Detection
1. **No Cluster Access**: Works even without connecting to the cluster
2. **Faster**: No need to query multiple resource types via API
3. **Deterministic**: Always returns the same resources for the same manifest
4. **Offline Friendly**: Can be used for planning and validation
5. **Simpler**: Less complex error handling and retry logic
## Testing
The feature includes comprehensive tests:
```bash
# Run cluster package tests
go test ./pkg/cluster/... -v
# Run kubedog package tests
go test ./pkg/kubedog/... -v
```
## Implementation Details
- Uses `k8s.io/apimachinery/pkg/util/yaml` for parsing
- Handles multi-document YAML files (separated by `---`)
- Extracts resource kind, name, namespace, and manifest
- Skips resources without kind or name
- Returns empty resource list for empty manifests

2
go.mod
View File

@ -107,7 +107,7 @@ require (
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/yaml v1.6.0
sigs.k8s.io/yaml v1.6.0 // indirect
)
require (

View File

@ -1,275 +0,0 @@
package cluster
import (
"bytes"
"fmt"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/yaml"
)
type Resource struct {
Kind string
Name string
Namespace string
Manifest string
}
type ReleaseResources struct {
ReleaseName string
Namespace string
Resources []Resource
}
type TrackConfig struct {
TrackKinds []string
SkipKinds []string
CustomTrackableKinds []string
CustomStaticKinds []string
}
func GetReleaseResourcesFromManifest(manifest []byte, releaseName, releaseNamespace string) (*ReleaseResources, error) {
return GetReleaseResourcesFromManifestWithLogger(nil, manifest, releaseName, releaseNamespace, nil)
}
func GetReleaseResourcesFromManifestWithConfig(manifest []byte, releaseName, releaseNamespace string, config *TrackConfig) (*ReleaseResources, error) {
return GetReleaseResourcesFromManifestWithLogger(nil, manifest, releaseName, releaseNamespace, config)
}
func GetReleaseResourcesFromManifestWithLogger(logger *zap.SugaredLogger, manifest []byte, releaseName, releaseNamespace string, config *TrackConfig) (*ReleaseResources, error) {
resources, err := parseManifest(manifest, logger)
if err != nil {
return nil, fmt.Errorf("failed to parse manifest: %w", err)
}
if len(resources) == 0 {
if logger != nil {
logger.Debugf("No resources found in manifest for release %s", releaseName)
}
return &ReleaseResources{
ReleaseName: releaseName,
Namespace: releaseNamespace,
Resources: resources,
}, nil
}
if config != nil {
filteredResources := filterResourcesByConfig(resources, config, logger)
if logger != nil {
logger.Infof("Found %d resources in manifest for release %s (filtered from %d total)", len(filteredResources), releaseName, len(resources))
for _, res := range filteredResources {
logger.Debugf(" - %s/%s in namespace %s", res.Kind, res.Name, res.Namespace)
}
}
return &ReleaseResources{
ReleaseName: releaseName,
Namespace: releaseNamespace,
Resources: filteredResources,
}, nil
}
if logger != nil {
logger.Infof("Found %d resources in manifest for release %s", len(resources), releaseName)
for _, res := range resources {
logger.Debugf(" - %s/%s in namespace %s", res.Kind, res.Name, res.Namespace)
}
}
return &ReleaseResources{
ReleaseName: releaseName,
Namespace: releaseNamespace,
Resources: resources,
}, nil
}
func filterResourcesByConfig(resources []Resource, config *TrackConfig, logger *zap.SugaredLogger) []Resource {
var filtered []Resource
for _, res := range resources {
if shouldSkipResource(res.Kind, config, logger) {
if logger != nil {
logger.Debugf("Skipping resource %s/%s (kind: %s) based on configuration", res.Kind, res.Name, res.Kind)
}
continue
}
filtered = append(filtered, res)
}
return filtered
}
func shouldSkipResource(kind string, config *TrackConfig, logger *zap.SugaredLogger) bool {
if len(config.TrackKinds) > 0 {
shouldTrack := false
for _, trackKind := range config.TrackKinds {
if kind == trackKind {
shouldTrack = true
break
}
}
if !shouldTrack {
if logger != nil {
logger.Debugf("Resource kind %s is not in TrackKinds list, skipping", kind)
}
return true
}
}
if len(config.SkipKinds) > 0 {
for _, skipKind := range config.SkipKinds {
if kind == skipKind {
if logger != nil {
logger.Debugf("Resource kind %s is in SkipKinds list, skipping", kind)
}
return true
}
}
}
return false
}
func parseManifest(manifest []byte, logger *zap.SugaredLogger) ([]Resource, error) {
var resources []Resource
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(manifest), 4096)
for {
var obj unstructured.Unstructured
err := decoder.Decode(&obj)
if err != nil {
if err.Error() == "EOF" {
break
}
return nil, fmt.Errorf("failed to decode manifest: %w", err)
}
if len(obj.Object) == 0 {
continue
}
kind := obj.GetKind()
if kind == "" {
if logger != nil {
logger.Debugf("Skipping resource without kind")
}
continue
}
name := obj.GetName()
if name == "" {
if logger != nil {
logger.Debugf("Skipping %s resource without name", kind)
}
continue
}
namespace := obj.GetNamespace()
if namespace == "" {
namespace = "default"
}
manifestBytes, err := yaml.Marshal(obj.Object)
if err != nil {
if logger != nil {
logger.Debugf("Failed to marshal %s/%s: %v", kind, name, err)
}
continue
}
res := Resource{
Kind: kind,
Name: name,
Namespace: namespace,
Manifest: string(manifestBytes),
}
resources = append(resources, res)
}
return resources, nil
}
func IsTrackableKind(kind string) bool {
trackableKinds := map[string]bool{
"Deployment": true,
"StatefulSet": true,
"DaemonSet": true,
"Job": true,
"Pod": true,
"ReplicaSet": true,
}
return trackableKinds[kind]
}
func IsTrackableKindWithConfig(kind string, config *TrackConfig) bool {
if config == nil {
return IsTrackableKind(kind)
}
if len(config.CustomTrackableKinds) > 0 {
for _, customKind := range config.CustomTrackableKinds {
if kind == customKind {
return true
}
}
return false
}
return IsTrackableKind(kind)
}
func IsStaticKind(kind string) bool {
staticKinds := map[string]bool{
"Service": true,
"ConfigMap": true,
"Secret": true,
"PersistentVolumeClaim": true,
"PersistentVolume": true,
"StorageClass": true,
"Namespace": true,
"ResourceQuota": true,
"LimitRange": true,
"PriorityClass": true,
"ServiceAccount": true,
"Role": true,
"RoleBinding": true,
"ClusterRole": true,
"ClusterRoleBinding": true,
"NetworkPolicy": true,
"Ingress": true,
"CustomResourceDefinition": true,
}
return staticKinds[kind]
}
func IsStaticKindWithConfig(kind string, config *TrackConfig) bool {
if config == nil {
return IsStaticKind(kind)
}
if len(config.CustomStaticKinds) > 0 {
for _, customKind := range config.CustomStaticKinds {
if kind == customKind {
return true
}
}
return false
}
return IsStaticKind(kind)
}
func GetHelmReleaseLabels(releaseName, releaseNamespace string) map[string]string {
return map[string]string{
"meta.helm.sh/release-name": releaseName,
"meta.helm.sh/release-namespace": releaseNamespace,
}
}
func GetHelmReleaseAnnotations(releaseName string) map[string]string {
return map[string]string{
"meta.helm.sh/release-name": releaseName,
}
}

View File

@ -1,344 +0,0 @@
package cluster
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
const testManifest = `---
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
selector:
app: myapp
ports:
- port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: nginx:latest
---
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
namespace: default
data:
key: value
`
const emptyManifest = `---
# Empty manifest
`
const malformedManifest = `
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: default
spec:
invalid: [unclosed
`
func TestGetReleaseResourcesFromManifest(t *testing.T) {
tests := []struct {
name string
manifest []byte
releaseName string
releaseNamespace string
expectedCount int
expectedKinds []string
wantErr bool
}{
{
name: "valid manifest",
manifest: []byte(testManifest),
releaseName: "my-release",
releaseNamespace: "default",
expectedCount: 3,
expectedKinds: []string{"Service", "Deployment", "ConfigMap"},
wantErr: false,
},
{
name: "empty manifest",
manifest: []byte(emptyManifest),
releaseName: "my-release",
releaseNamespace: "default",
expectedCount: 0,
expectedKinds: []string{},
wantErr: false,
},
{
name: "nil manifest",
manifest: nil,
releaseName: "my-release",
releaseNamespace: "default",
expectedCount: 0,
expectedKinds: []string{},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resources, err := GetReleaseResourcesFromManifest(tt.manifest, tt.releaseName, tt.releaseNamespace)
if tt.wantErr {
assert.Error(t, err)
assert.Nil(t, resources)
return
}
require.NoError(t, err)
require.NotNil(t, resources)
assert.Equal(t, tt.releaseName, resources.ReleaseName)
assert.Equal(t, tt.releaseNamespace, resources.Namespace)
assert.Len(t, resources.Resources, tt.expectedCount)
if tt.expectedKinds != nil {
actualKinds := make([]string, len(resources.Resources))
for i, res := range resources.Resources {
actualKinds[i] = res.Kind
}
assert.ElementsMatch(t, tt.expectedKinds, actualKinds)
}
})
}
}
func TestGetReleaseResourcesFromManifestWithLogger(t *testing.T) {
logger := zap.NewNop().Sugar()
resources, err := GetReleaseResourcesFromManifestWithLogger(logger, []byte(testManifest), "my-release", "default", nil)
require.NoError(t, err)
require.NotNil(t, resources)
assert.Equal(t, "my-release", resources.ReleaseName)
assert.Equal(t, "default", resources.Namespace)
assert.Len(t, resources.Resources, 3)
assert.Contains(t, []string{"Service", "Deployment", "ConfigMap"}, resources.Resources[0].Kind)
}
func TestIsTrackableKind(t *testing.T) {
tests := []struct {
kind string
expected bool
}{
{"Deployment", true},
{"StatefulSet", true},
{"DaemonSet", true},
{"Job", true},
{"Pod", true},
{"ReplicaSet", true},
{"Service", false},
{"ConfigMap", false},
{"Secret", false},
{"Ingress", false},
}
for _, tt := range tests {
t.Run(tt.kind, func(t *testing.T) {
result := IsTrackableKind(tt.kind)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIsStaticKind(t *testing.T) {
tests := []struct {
kind string
expected bool
}{
{"Service", true},
{"ConfigMap", true},
{"Secret", true},
{"PersistentVolumeClaim", true},
{"Ingress", true},
{"Deployment", false},
{"StatefulSet", false},
}
for _, tt := range tests {
t.Run(tt.kind, func(t *testing.T) {
result := IsStaticKind(tt.kind)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetHelmReleaseLabels(t *testing.T) {
labels := GetHelmReleaseLabels("my-release", "my-namespace")
expectedLabels := map[string]string{
"meta.helm.sh/release-name": "my-release",
"meta.helm.sh/release-namespace": "my-namespace",
}
assert.Equal(t, expectedLabels, labels)
}
func TestGetHelmReleaseAnnotations(t *testing.T) {
annotations := GetHelmReleaseAnnotations("my-release")
expectedAnnotations := map[string]string{
"meta.helm.sh/release-name": "my-release",
}
assert.Equal(t, expectedAnnotations, annotations)
}
func TestParseManifest(t *testing.T) {
resources, err := parseManifest([]byte(testManifest), nil)
require.NoError(t, err)
assert.Len(t, resources, 3)
assert.Equal(t, "Service", resources[0].Kind)
assert.Equal(t, "my-service", resources[0].Name)
assert.Equal(t, "default", resources[0].Namespace)
assert.Equal(t, "Deployment", resources[1].Kind)
assert.Equal(t, "my-deployment", resources[1].Name)
assert.Equal(t, "ConfigMap", resources[2].Kind)
assert.Equal(t, "my-config", resources[2].Name)
}
func TestParseManifest_Empty(t *testing.T) {
resources, err := parseManifest([]byte(emptyManifest), nil)
require.NoError(t, err)
assert.Empty(t, resources)
}
func TestParseManifest_Nil(t *testing.T) {
resources, err := parseManifest(nil, nil)
require.NoError(t, err)
assert.Empty(t, resources)
}
func TestResource_ManifestContent(t *testing.T) {
resources, err := parseManifest([]byte(testManifest), nil)
require.NoError(t, err)
require.Len(t, resources, 3)
for _, res := range resources {
assert.NotEmpty(t, res.Manifest)
assert.Contains(t, res.Manifest, "apiVersion")
assert.Contains(t, res.Manifest, "kind")
}
}
func TestTrackConfig_TrackKinds(t *testing.T) {
config := &TrackConfig{
TrackKinds: []string{"Deployment"},
}
resources, err := GetReleaseResourcesFromManifestWithConfig(
[]byte(testManifest),
"test-release",
"default",
config,
)
require.NoError(t, err)
require.NotNil(t, resources)
assert.Len(t, resources.Resources, 1)
assert.Equal(t, "Deployment", resources.Resources[0].Kind)
assert.Equal(t, "my-deployment", resources.Resources[0].Name)
}
func TestTrackConfig_SkipKinds(t *testing.T) {
config := &TrackConfig{
SkipKinds: []string{"ConfigMap"},
}
resources, err := GetReleaseResourcesFromManifestWithConfig(
[]byte(testManifest),
"test-release",
"default",
config,
)
require.NoError(t, err)
require.NotNil(t, resources)
assert.Len(t, resources.Resources, 2)
kinds := make([]string, len(resources.Resources))
for i, res := range resources.Resources {
kinds[i] = res.Kind
}
assert.NotContains(t, kinds, "ConfigMap")
}
func TestTrackConfig_CustomTrackableKinds(t *testing.T) {
config := &TrackConfig{
CustomTrackableKinds: []string{"CronJob"},
}
isTrackable := IsTrackableKindWithConfig("CronJob", config)
assert.True(t, isTrackable)
isNotTrackable := IsTrackableKindWithConfig("Deployment", config)
assert.False(t, isNotTrackable)
}
func TestTrackConfig_CustomStaticKinds(t *testing.T) {
config := &TrackConfig{
CustomStaticKinds: []string{"CustomResource"},
}
isStatic := IsStaticKindWithConfig("CustomResource", config)
assert.True(t, isStatic)
isNotStatic := IsStaticKindWithConfig("ConfigMap", config)
assert.False(t, isNotStatic)
}
func TestTrackConfig_Combined(t *testing.T) {
config := &TrackConfig{
SkipKinds: []string{"ConfigMap"},
CustomTrackableKinds: []string{"CronJob"},
CustomStaticKinds: []string{"CustomResource"},
}
trackable1 := IsTrackableKindWithConfig("CronJob", config)
assert.True(t, trackable1)
defaultTrackable := IsTrackableKindWithConfig("Service", config)
assert.False(t, defaultTrackable, "Service is default trackable, but CustomTrackableKinds is configured, so it should not be trackable")
static1 := IsStaticKindWithConfig("CustomResource", config)
assert.True(t, static1)
defaultStatic := IsStaticKindWithConfig("ConfigMap", config)
assert.False(t, defaultStatic, "ConfigMap is default static, but CustomStaticKinds is configured, so it should not be static")
}