Files
Jose Diaz-Gonzalez 496ede44b7 feat: add scheduler-k3s:preview subcommand
Adds a `scheduler-k3s:preview <app>` command that renders a unified diff
between the manifests currently stored in the live Helm release for an app
and the manifests that the next deploy would roll out. The diff is produced
entirely via the Helm Go SDK that scheduler-k3s already uses, with no
external `helm` binary or `helm-diff` plugin install required. The
underlying diff and parse logic is vendored from `databus23/helm-diff`
(Apache 2.0) into a new internal package, trimmed of three-way-merge,
release-ownership tracking, and the non-default output formatters.

The chart-construction half of `TriggerSchedulerDeploy` has been extracted
into a reusable `BuildAppChart` helper so deploy and preview share the
same chart materialization without duplicating template logic. Secret
values are redacted by default in preview output; `--show-secrets` and
`--show-secrets-decoded` flags match the upstream `helm diff` UX. Only the
main app helm release is compared in this iteration; auxiliary releases
for config vars, image-pull secrets, and TLS certificates are out of scope
and documented as such.
2026-06-02 20:28:31 -04:00

119 lines
3.4 KiB
Go

package helmdiff
import (
"bytes"
"strings"
"testing"
)
const sampleDeployment = `# Source: app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default
spec:
replicas: 1
`
const sampleDeploymentChanged = `# Source: app/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default
spec:
replicas: 3
`
const sampleSecret = `# Source: app/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: creds
namespace: default
type: Opaque
data:
password: c2VjcmV0
`
const sampleSecretChanged = `# Source: app/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: creds
namespace: default
type: Opaque
data:
password: bmV3LXNlY3JldA==
`
func runDiff(t *testing.T, oldManifest, newManifest string, opts *Options) string {
t.Helper()
current := Parse([]byte(oldManifest), "default", true)
proposed := Parse([]byte(newManifest), "default", true)
var buf bytes.Buffer
Manifests(current, proposed, opts, &buf)
return buf.String()
}
func TestManifestsNoChange(t *testing.T) {
out := runDiff(t, sampleDeployment, sampleDeployment, &Options{OutputContext: 3})
if out != "" {
t.Fatalf("expected empty diff for identical manifests, got:\n%s", out)
}
}
func TestManifestsSingleFieldChange(t *testing.T) {
out := runDiff(t, sampleDeployment, sampleDeploymentChanged, &Options{OutputContext: 3})
if !strings.Contains(out, "has changed") {
t.Fatalf("expected MODIFY header in output, got:\n%s", out)
}
if !strings.Contains(out, "replicas: 1") {
t.Fatalf("expected removed line for replicas: 1, got:\n%s", out)
}
if !strings.Contains(out, "replicas: 3") {
t.Fatalf("expected added line for replicas: 3, got:\n%s", out)
}
}
func TestManifestsAdditiveWhenOldEmpty(t *testing.T) {
out := runDiff(t, "", sampleDeployment, &Options{OutputContext: 3})
if !strings.Contains(out, "has been added") {
t.Fatalf("expected ADD header in output, got:\n%s", out)
}
if !strings.Contains(out, "kind: Deployment") {
t.Fatalf("expected added line for kind: Deployment, got:\n%s", out)
}
}
func TestManifestsSecretRedactedByDefault(t *testing.T) {
out := runDiff(t, sampleSecret, sampleSecretChanged, &Options{OutputContext: -1})
if strings.Contains(out, "c2VjcmV0") || strings.Contains(out, "bmV3LXNlY3JldA==") {
t.Fatalf("expected raw base64 secret values to be redacted, got:\n%s", out)
}
if !strings.Contains(out, "# (") || !strings.Contains(out, "bytes)") {
t.Fatalf("expected byte-count placeholders in redacted output, got:\n%s", out)
}
}
func TestManifestsSecretShownWithShowSecrets(t *testing.T) {
out := runDiff(t, sampleSecret, sampleSecretChanged, &Options{OutputContext: -1, ShowSecrets: true})
if !strings.Contains(out, "c2VjcmV0") {
t.Fatalf("expected old base64 value to appear with ShowSecrets, got:\n%s", out)
}
if !strings.Contains(out, "bmV3LXNlY3JldA==") {
t.Fatalf("expected new base64 value to appear with ShowSecrets, got:\n%s", out)
}
}
func TestManifestsSecretDecodedWithShowSecretsDecoded(t *testing.T) {
out := runDiff(t, sampleSecret, sampleSecretChanged, &Options{OutputContext: -1, ShowSecretsDecoded: true})
if !strings.Contains(out, "password: secret") {
t.Fatalf("expected old plaintext value with ShowSecretsDecoded, got:\n%s", out)
}
if !strings.Contains(out, "password: new-secret") {
t.Fatalf("expected new plaintext value with ShowSecretsDecoded, got:\n%s", out)
}
}