fix: pin scheduler-k3s:preview deployment id to the deployed release
Without this, the preview build invented a fresh timestamp for `Values.global.deployment_id` and the resulting diff showed every Deployment changing its `app.kubernetes.io/version` annotation - even when nothing semantically changed. CommandPreview now reads the deployment id out of `release.Chart.Metadata.Version` and passes it through a new `BuildOptions.OverrideDeploymentID` parameter so the previewed manifests carry the same version label as the live release. The deploy path still generates a fresh timestamp.
This commit is contained in:
@@ -33,6 +33,15 @@ type BuildResult struct {
|
||||
KustomizeRootPath string
|
||||
}
|
||||
|
||||
// BuildOptions configures non-default behavior for BuildAppChart.
|
||||
type BuildOptions struct {
|
||||
// OverrideDeploymentID, when non-zero, replaces the timestamp-derived
|
||||
// deployment id. CommandPreview uses this to reuse the currently-deployed
|
||||
// release's id so app.kubernetes.io/version annotation churn does not
|
||||
// appear in the diff.
|
||||
OverrideDeploymentID int64
|
||||
}
|
||||
|
||||
// BuildAppChart constructs a Helm chart on disk that reflects dokku's configured
|
||||
// state for the given app and image tag. It reads from the Kubernetes cluster
|
||||
// where required (for KEDA values and existing cron-job suffixes) but performs
|
||||
@@ -42,7 +51,7 @@ type BuildResult struct {
|
||||
// The returned BuildResult holds the absolute chart path and the parameters a
|
||||
// subsequent Helm Install/Upgrade (dry-run or real) needs. The caller must
|
||||
// os.RemoveAll(result.ChartDir) when finished.
|
||||
func BuildAppChart(ctx context.Context, appName, imageTag string) (BuildResult, error) {
|
||||
func BuildAppChart(ctx context.Context, appName, imageTag string, opts BuildOptions) (BuildResult, error) {
|
||||
var result BuildResult
|
||||
|
||||
processesRes, err := common.CallPlugnTrigger(common.PlugnTriggerInput{
|
||||
@@ -142,7 +151,10 @@ func BuildAppChart(ctx context.Context, appName, imageTag string) (BuildResult,
|
||||
return cleanup(fmt.Errorf("Error creating chart templates directory: %w", err))
|
||||
}
|
||||
|
||||
deploymentId := time.Now().Unix()
|
||||
deploymentId := opts.OverrideDeploymentID
|
||||
if deploymentId == 0 {
|
||||
deploymentId = time.Now().Unix()
|
||||
}
|
||||
imagePullSecrets := getComputedImagePullSecrets(appName)
|
||||
if imagePullSecrets == "" {
|
||||
dockerConfigPath := filepath.Join(registry.GetComputedAppRegistryConfigDir(appName), "config.json")
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
)
|
||||
|
||||
@@ -50,29 +53,36 @@ func CommandPreview(appName string, diffContext int, showSecrets bool, showSecre
|
||||
cancel()
|
||||
}()
|
||||
|
||||
chartResult, err := BuildAppChart(ctx, appName, "")
|
||||
namespace := getComputedNamespace(appName)
|
||||
helmAgent, err := NewHelmAgent(namespace, DevNullPrinter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating helm agent: %w", err)
|
||||
}
|
||||
|
||||
currentRelease, err := fetchCurrentRelease(helmAgent, appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildOpts := BuildOptions{}
|
||||
if currentRelease != nil {
|
||||
buildOpts.OverrideDeploymentID = deploymentIDFromRelease(currentRelease)
|
||||
}
|
||||
|
||||
chartResult, err := BuildAppChart(ctx, appName, "", buildOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(chartResult.ChartDir)
|
||||
|
||||
helmAgent, err := NewHelmAgent(chartResult.Namespace, DevNullPrinter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating helm agent: %w", err)
|
||||
}
|
||||
|
||||
chartRequested, err := loadChartForPreview(chartResult.ChartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentManifest, releaseExists, err := fetchCurrentManifest(helmAgent, chartResult.ReleaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var proposedManifest string
|
||||
if releaseExists {
|
||||
var currentManifest, proposedManifest string
|
||||
if currentRelease != nil {
|
||||
currentManifest = currentRelease.Manifest
|
||||
proposedManifest, err = renderUpgradeDryRun(ctx, helmAgent, chartResult, chartRequested)
|
||||
} else {
|
||||
proposedManifest, err = renderInstallDryRun(ctx, helmAgent, chartResult, chartRequested)
|
||||
@@ -108,16 +118,38 @@ func loadChartForPreview(chartPath string) (*chart.Chart, error) {
|
||||
return chartRequested, nil
|
||||
}
|
||||
|
||||
func fetchCurrentManifest(helmAgent *HelmAgent, releaseName string) (string, bool, error) {
|
||||
func fetchCurrentRelease(helmAgent *HelmAgent, releaseName string) (*release.Release, error) {
|
||||
getAction := action.NewGet(helmAgent.Configuration)
|
||||
rel, err := getAction.Run(releaseName)
|
||||
if err != nil {
|
||||
if errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
return "", false, nil
|
||||
return nil, nil
|
||||
}
|
||||
return "", false, fmt.Errorf("Error fetching current release: %w", err)
|
||||
return nil, fmt.Errorf("Error fetching current release: %w", err)
|
||||
}
|
||||
return rel.Manifest, true, nil
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// deploymentIDFromRelease extracts the deployment id encoded in a release's
|
||||
// chart version. BuildAppChart writes the chart version as "0.0.<id>"
|
||||
// (chart.go); the parsed id lets CommandPreview pin the previewed manifests
|
||||
// to the currently-deployed value so the `app.kubernetes.io/version`
|
||||
// annotation does not churn in the diff. Returns 0 when the version does not
|
||||
// match the expected shape - the caller then falls back to the timestamp.
|
||||
func deploymentIDFromRelease(rel *release.Release) int64 {
|
||||
if rel == nil || rel.Chart == nil || rel.Chart.Metadata == nil {
|
||||
return 0
|
||||
}
|
||||
const prefix = "0.0."
|
||||
v := rel.Chart.Metadata.Version
|
||||
if !strings.HasPrefix(v, prefix) {
|
||||
return 0
|
||||
}
|
||||
id, err := strconv.ParseInt(strings.TrimPrefix(v, prefix), 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func renderUpgradeDryRun(ctx context.Context, helmAgent *HelmAgent, chartResult BuildResult, chartRequested *chart.Chart) (string, error) {
|
||||
|
||||
@@ -476,7 +476,7 @@ func TriggerSchedulerDeploy(scheduler string, appName string, imageTag string) e
|
||||
return fmt.Errorf("Error creating kubernetes namespace for deployment: %w", err)
|
||||
}
|
||||
|
||||
chartResult, err := BuildAppChart(ctx, appName, imageTag)
|
||||
chartResult, err := BuildAppChart(ctx, appName, imageTag, BuildOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user