Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
120 changes: 100 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ A Go-based Kubernetes controller with structured logging, environment configurat
## Features

- **FastHTTP Server** - High-performance HTTP server with configurable port and logging
- **REST API** - JSON API endpoints for deployment information with multi-namespace support
- **Deployment Informer** - Real-time Kubernetes Deployment event monitoring using client-go informers
- **Kubernetes Integration** - List deployments and manage Kubernetes resources with namespace support
- **Smart Configuration** - Load from `.env` files, environment variables, or CLI flags with proper priority
Expand All @@ -48,27 +49,33 @@ A Go-based Kubernetes controller with structured logging, environment configurat
```
k8s-controller/
├── cmd/
│ ├── root.go # Main CLI application
│ ├── server.go # FastHTTP server command with informer
│ ├── server_test.go # Tests for server command
│ ├── list.go # Kubernetes deployments list command
│ └── list_test.go # Tests for list command
│ ├── root.go # Main CLI application
│ ├── server.go # FastHTTP server command with informer
│ ├── server_test.go # Tests for server command
│ ├── list.go # Kubernetes deployments list command
│ └── list_test.go # Tests for list command
├── pkg/
│ ├── common/
│ │ ├── config/ # Configuration management
│ │ ├── config/ # Configuration management
│ │ │ ├── config.go
│ │ │ └── config_test.go
│ │ └── envs/ # Environment files
│ │ ├── utils/ # Utility functions
│ │ │ └── k8s.go # Kubernetes utilities
│ │ └── envs/ # Environment files
│ │ └── .env
│ ├── informer/ # Deployment informer implementation
│ ├── handlers/ # HTTP handlers for API endpoints
│ │ ├── handlers.go # Main handler implementation
│ │ ├── handlers_test.go # Unit tests
│ │ └── handlers_env_test.go # Integration tests with envtest
│ ├── informer/ # Deployment informer implementation
│ │ ├── informer.go
│ │ └── informer_test.go
│ └── testutil/ # Testing utilities and envtest setup
│ └── testutil/ # Testing utilities and envtest setup
│ ├── envtest.go
│ └── envtest_test.go
├── main.go # Application entry point
├── Makefile # Development and build commands
├── charts/app/ # Helm chart
├── main.go # Application entry point
├── Makefile # Development and build commands
├── charts/app/ # Helm chart
└── README.md
```

Expand All @@ -81,7 +88,7 @@ k8s-controller/
| `PORT` | Server port | `8080` |
| `KUBECONFIG` | Path to Kubernetes configuration file | `~/.kube/config` |
| `IN_CLUSTER` | Use in-cluster Kubernetes config | `false` |
| `NAMESPACE` | Kubernetes namespace for operations | `default` |
| `NAMESPACE` | Kubernetes namespace(s) for operations (comma-separated, e.g., "kube-system,monitoring") | `default` |
| `LOGGING_LEVEL` | Logging level (trace, debug, info, warn, error) | `info` |

### Configuration Priority
Expand All @@ -91,7 +98,7 @@ All commands follow the same configuration priority:
1. **CLI flags** (`--port`, `--log-level`, `--kubeconfig`, `--in-cluster`, `--namespace`) - highest priority
2. **Environment variables** (`PORT`, `LOGGING_LEVEL`, `KUBECONFIG`, `IN_CLUSTER`, `NAMESPACE`)
3. **`.env` file** values (`pkg/common/envs/.env`)
4. **Default values** (PORT=8080, LOGGING_LEVEL=info, KUBECONFIG=~/.kube/config, IN_CLUSTER=false, namespace=default)
4. **Default values** (PORT=8080, LOGGING_LEVEL=info, KUBECONFIG=~/.kube/config, IN_CLUSTER=false, NAMESPACE=default)

## Quick Start

Expand All @@ -110,6 +117,9 @@ make list
# List deployments in custom namespace
make list-namespace

# Start server with multiple namespaces via environment variable
export NAMESPACE=kube-system,monitoring && make server

# Development workflow
make dev-server

Expand Down Expand Up @@ -155,12 +165,26 @@ go run main.go server --port 9090 --log-level debug --kubeconfig ~/.kube/config
# Using in-cluster configuration
go run main.go server --in-cluster --namespace kube-system

# Multiple namespaces (comma-separated)
go run main.go server --namespace kube-system,monitoring,default

# Using environment variable for multiple namespaces
export NAMESPACE=kube-system,monitoring,default
go run main.go server

# List deployments
go run main.go list

# List deployments with custom namespace
go run main.go list --namespace kube-system

# List deployments in multiple namespaces
go run main.go list --namespace kube-system,test

# Using environment variable for multiple namespaces
export NAMESPACE=kube-system,test
go run main.go list

# Server with environment variables
export PORT=9090 && export LOGGING_LEVEL=debug && go run main.go server

Expand All @@ -171,15 +195,61 @@ export KUBECONFIG=~/.kube/config-prod && export NAMESPACE=monitoring && go run m
#### What it does

- Starts a FastHTTP server on the specified port (default: 8080)
- Responds with "Hello from FastHTTP!" to any HTTP request
- Deployment Informer: Watches for Deployment events (add, update, delete) in the specified namespace
- Provides JSON API endpoints for deployment information:
- `/` - Root endpoint with version information
- `/namespaces` - List all watched namespaces
- `/deployments` - List deployments from all watched namespaces
- `/deployments/{namespace}` - List deployments in specific namespace

#### API Examples

```bash
# Start server with multiple namespaces
./k8s-controller server --kubeconfig ~/.kube/config --port 8080 -n kube-system,monitoring

# Get all watched namespaces
curl -s http://localhost:8080/namespaces
# Output: {"namespaces":["kube-system","monitoring"],"count":2}

# Get deployments from all watched namespaces
curl -s http://localhost:8080/deployments
# Output: {
# "namespaces": [
# {
# "namespace": "kube-system",
# "deployments": ["system-1"],
# "count": 1
# },
# {
# "namespace": "monitoring",
# "deployments": ["grafana", "loki", "prometheus"],
# "count": 3
# }
# ],
# "total_count": 4
# }

# Get deployments in specific namespace
curl -s http://localhost:8080/deployments/monitoring
# Output: {"namespace":"monitoring","deployments":["grafana","loki","prometheus"],"count":3}

curl -s http://localhost:8080/deployments/kube-system
# Output: {"namespace":"kube-system","deployments":["system-1"],"count":1}

# Get root endpoint with version info
curl -s http://localhost:8080/
# Output: {"endpoints":{"deployments":"/deployments","namespaces":"/namespaces"},"message":"Kubernetes Controller API","version":"v0.1.2"}
```

**Note:** The `/deployments` endpoint returns deployments from all namespaces being watched by the informer, not just the default namespace. This provides a comprehensive view of all deployments across monitored namespaces.
- Deployment Informer: Watches for Deployment events (add, update, delete) in the specified namespace(s)
- Uses structured logging with configurable levels
- Supports hot-reload configuration via environment variables
- Implements graceful shutdown with proper signal handling

### Kubernetes List Command

List Kubernetes deployments in the specified namespace with configurable kubeconfig and comprehensive error handling.
List Kubernetes deployments in the specified namespace(s) with configurable kubeconfig and comprehensive error handling.

#### Basic Usage

Expand Down Expand Up @@ -224,11 +294,14 @@ EOF

# List deployments in custom namespace
./k8s-controller list --namespace kube-system

# List deployments in multiple namespaces
./k8s-controller list --namespace kube-system,test
```

#### What it does

Lists deployments in the specified namespace with error handling and logging.
Lists deployments in the specified namespace(s) with error handling and logging. Supports multiple namespaces separated by commas. When multiple namespaces are specified, deployments are listed per namespace with a total count.

#### List Command Configuration

Expand All @@ -245,6 +318,13 @@ make build
# Custom namespace
./k8s-controller list --namespace kube-system

# Multiple namespaces
./k8s-controller list --namespace kube-system,monitoring

# Using environment variable for multiple namespaces
export NAMESPACE=kube-system,monitoring
./k8s-controller list

# Environment variable for kubeconfig
export KUBECONFIG=/path/to/kubeconfig && ./k8s-controller list

Expand Down Expand Up @@ -295,7 +375,7 @@ make help
**Variable Override:** You can override any Makefile variable:
```bash
# Override default values
make server SERVER_PORT=9090 LOGGING_LEVEL=debug NAMESPACE=kube-system
make server SERVER_PORT=9090 LOGGING_LEVEL=debug NAMESPACE=kube-system,monitoring

# Cross-compilation
make build TARGETOS=linux TARGETARCH=amd64
Expand Down Expand Up @@ -331,7 +411,7 @@ docker run --rm \
-e KUBECONFIG=/root/.kube/config \
-e IN_CLUSTER=false \
-e LOGGING_LEVEL=debug \
-e NAMESPACE=default \
-e NAMESPACE=kube-system,monitoring,default \
-p 8080:8080 \
ghcr.io/vanelin/k8s-controller:latest server
```
Expand Down
80 changes: 62 additions & 18 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"strings"

"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
Expand All @@ -18,37 +19,53 @@ var namespaceFlag string

var listCmd = &cobra.Command{
Use: "list",
Short: "List Kubernetes deployments in the specified namespace",
Short: "List Kubernetes deployments in the specified namespace(s)",
Run: func(cmd *cobra.Command, args []string) {
// Get kubeconfig path with proper priority using existing config logic
kubeconfigPath := getKubeconfigPath()

log.Info().Str("kubeconfig", kubeconfigPath).Str("namespace", namespaceFlag).Msg("Using kubeconfig path and namespace")
// Get namespace with proper priority: CLI flag > env vars > .env file > defaults
namespaceToUse := getNamespaceWithPriority()

log.Info().Str("kubeconfig", kubeconfigPath).Str("namespace", namespaceToUse).Msg("Using kubeconfig path and namespace")

clientset, err := getKubeClient(kubeconfigPath)
if err != nil {
log.Error().Err(err).Str("kubeconfig", kubeconfigPath).Msg("Failed to create Kubernetes client")
os.Exit(1)
}

// Check if namespace exists using utility function
result := utils.CheckNamespace(context.Background(), clientset, namespaceFlag)
if !result.Exists {
utils.LogNamespaceCheck(result, "error")
os.Exit(1)
}
// Parse namespaces from the determined namespace value (comma-separated)
namespaces := parseNamespaces(namespaceToUse)

log.Info().Str("namespace", result.Namespace).Msg("Namespace exists, listing deployments")
// List deployments for each namespace
totalDeployments := 0
for _, namespace := range namespaces {
// Check if namespace exists using utility function
result := utils.CheckNamespace(context.Background(), clientset, namespace)
if !result.Exists {
log.Warn().Err(result.Error).Str("namespace", namespace).Msg("Namespace does not exist, skipping")
continue
}

// List Deployments
deployments, err := clientset.AppsV1().Deployments(namespaceFlag).List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Error().Err(err).Str("namespace", namespaceFlag).Msg("Failed to list deployments")
os.Exit(1)
log.Info().Str("namespace", namespace).Msg("Listing deployments in namespace")

// List Deployments
deployments, err := clientset.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{})
if err != nil {
log.Error().Err(err).Str("namespace", namespace).Msg("Failed to list deployments")
continue
}

fmt.Printf("Found %d deployments in '%s' namespace:\n", len(deployments.Items), namespace)
for _, d := range deployments.Items {
fmt.Println("-", d.Name)
}
totalDeployments += len(deployments.Items)
}
fmt.Printf("Found %d deployments in '%s' namespace:\n", len(deployments.Items), namespaceFlag)
for _, d := range deployments.Items {
fmt.Println("-", d.Name)

if len(namespaces) > 1 {
fmt.Printf("\nTotal deployments across all namespaces: %d\n", totalDeployments)
}
},
}
Expand All @@ -65,6 +82,20 @@ func getKubeconfigPath() string {
return utils.ExpandTilde(appConfig.KUBECONFIG)
}

// getNamespaceWithPriority returns the namespace with proper priority: CLI flag > env vars > .env file > defaults
func getNamespaceWithPriority() string {
// 1. CLI flag takes highest priority
if namespaceFlag != "" {
return namespaceFlag
}
// 2. Config (env/.env)
if appConfig.Namespace != "" {
return appConfig.Namespace
}
// 3. Default fallback
return "default"
}

func getKubeClient(kubeconfigPath string) (*kubernetes.Clientset, error) {
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
if err != nil {
Expand All @@ -73,8 +104,21 @@ func getKubeClient(kubeconfigPath string) (*kubernetes.Clientset, error) {
return kubernetes.NewForConfig(config)
}

// parseNamespaces splits a comma-separated string of namespaces and trims whitespace
func parseNamespaces(namespaceString string) []string {
if namespaceString == "" {
return []string{"default"}
}

namespaces := strings.Split(namespaceString, ",")
for i, ns := range namespaces {
namespaces[i] = strings.TrimSpace(ns)
}
return namespaces
}

func init() {
rootCmd.AddCommand(listCmd)
listCmd.Flags().StringVar(&kubeconfigFlag, "kubeconfig", "", "Path to the kubeconfig file (overrides env vars and config)")
listCmd.Flags().StringVarP(&namespaceFlag, "namespace", "n", "default", "Namespace to list deployments from")
listCmd.Flags().StringVarP(&namespaceFlag, "namespace", "n", "", "Namespace(s) to list deployments from (comma-separated)")
}
Loading