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.
119 lines
3.4 KiB
Go
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)
|
|
}
|
|
}
|