kuberator 0.4.0

A Kubernetes operator framework in Rust
Documentation
# Migration Guide: v0.3 → v0.4

## Breaking Change: Event API migrated to `events.k8s.io/v1` with deduplication

Kuberator v0.4 replaces the legacy `core/v1/Event` API with `events.k8s.io/v1` and adds built-in event deduplication. Identical events are now collapsed into a single Event object with an incrementing series count instead of flooding the API server with duplicates.

## Why This Change?

Operators that emit events on every reconciliation cycle (e.g., every 15 seconds for persistent errors) create ~240 duplicate events per hour. This floods `kubectl describe` output, loads the API server, and makes it hard to find meaningful events.

**Benefits you gain:**
- **Deduplication**: Identical events within a 6-minute window are PATCHed (incrementing `series.count`) instead of CREATEd
- **Clean output**: `kubectl events` shows `(x52 over 13m)` instead of 52 separate lines
- **Reduced API load**: One PATCH vs one CREATE per repeated event
- **Note truncation**: Messages exceeding the 1024-character `events.k8s.io/v1` limit are automatically truncated with `...`
- **Action defaulting**: The required `action` field defaults to the reason string when not explicitly set

## Migration Steps

### Step 1: Update import

Change the Event type in your `StaticApiProvider` type signatures:

```rust
// Before
use k8s_openapi::api::core::v1::Event;

// After
use k8s_openapi::api::events::v1::Event;
```

This typically affects:
- Your operator setup code where `StaticApiProvider<Event>` is constructed
- Type aliases or struct fields referencing `EventRecorder<StaticApiProvider<Event>>`

### Step 2: Update RBAC / ClusterRole

The `events.k8s.io/v1` API requires its own RBAC rules. Add the new rule alongside the existing one during transition:

```yaml
rules:
  # Legacy core/v1 events (keep during transition)
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "patch"]
  # New events.k8s.io/v1 (required for kuberator v0.4)
  - apiGroups: ["events.k8s.io"]
    resources: ["events"]
    verbs: ["create", "patch"]
```

After all deployments are running v0.4, you can remove the legacy `core/v1` rule.

### Step 3: Update test code

If you use `MockEventRecorder` in tests, update field access:

```rust
// Before (tuple access)
let events = recorder.events();
assert_eq!(events[0].0, "resource-name");  // name
assert_eq!(events[0].1, MyReason::Created); // reason
assert_eq!(events[0].2, "message");         // message

// After (struct fields)
let events = recorder.events();
assert_eq!(events[0].resource_name, "resource-name");
assert_eq!(events[0].reason, MyReason::Created);
assert_eq!(events[0].message, "message");
assert_eq!(events[0].count, 1); // dedup count
```

The mock now deduplicates events the same way the real recorder does: same `resource_name` + `reason` increments `count` instead of creating a new entry.

## New Features

### Event Deduplication

Events with the same (object UID, event type, reason, action) are automatically deduplicated within a 6-minute window. The first occurrence creates a new Event; subsequent identical events PATCH the existing Event's `series.count`.

This happens transparently - no code changes needed in your event emission logic.

### Customizing the Dedup Window

```rust
use std::time::Duration;

let event_recorder = EventRecorder::new(event_api_provider, "my-operator")
    .with_cache_ttl(Duration::from_secs(10 * 60)); // 10 minutes
```

After the TTL expires, the next emission creates a fresh Event object.

### Action Field

The `events.k8s.io/v1` API requires the `action` field. If you don't set it via `with_action()`, it defaults to the reason string (e.g., `"DriftDetected"`, `"ReconciliationFailed"`).

To set a custom action:

```rust
EventData::normal(MyReason::ResourceCreated, "Resource created")
    .with_action("Reconcile")
```

### Note Truncation

Messages longer than 1024 characters are automatically truncated with a `...` suffix, respecting UTF-8 character boundaries. No action needed.

## Viewing Events

Events created via `events.k8s.io/v1` are best viewed with:

```bash
# Recommended - shows events.k8s.io/v1, sorted by time, with dedup counts
kubectl events -n <namespace>

# Filter for a specific resource
kubectl events -n <namespace> --for <kind>/<name>

# Also works - kubectl describe aggregates from both APIs
kubectl describe <kind> <name> -n <namespace>
```

Note: `kubectl get events` only shows legacy `core/v1` events. Use `kubectl events` (without `get`) for the new API.

## Troubleshooting

### "Forbidden" errors in operator logs

Your ClusterRole is missing the `events.k8s.io` RBAC rule. See Step 2 above.

### "action: Required value" errors

You're running kuberator v0.4.0 code but the `action` field isn't being set. This was fixed in the initial release - make sure you're on the latest commit.

### Events not showing in `kubectl get events`

Use `kubectl events` instead. The `get events` command only queries the legacy `core/v1` API.

### Old duplicate events still visible

Events created before the migration (via `core/v1`) remain until they expire (default TTL is 1 hour in most clusters). New events will be deduplicated.