2024-01-17 18:06:18 -05:00
|
|
|
package scheduler_k3s
|
|
|
|
|
|
|
|
|
|
import (
|
2024-03-12 03:55:40 -04:00
|
|
|
"bytes"
|
2024-01-17 18:06:18 -05:00
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2024-01-22 05:12:49 -05:00
|
|
|
"log"
|
2024-01-17 18:06:18 -05:00
|
|
|
"os"
|
2024-01-22 05:12:49 -05:00
|
|
|
"path/filepath"
|
2024-01-18 05:28:07 -05:00
|
|
|
"sort"
|
2024-01-17 18:06:18 -05:00
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
|
|
"github.com/dokku/dokku/plugins/common"
|
2025-07-08 22:30:20 -04:00
|
|
|
"github.com/fluxcd/pkg/kustomize/filesys"
|
2024-01-22 05:12:49 -05:00
|
|
|
"github.com/gofrs/flock"
|
|
|
|
|
"gopkg.in/yaml.v3"
|
2024-01-17 18:06:18 -05:00
|
|
|
"helm.sh/helm/v3/pkg/action"
|
|
|
|
|
"helm.sh/helm/v3/pkg/chart/loader"
|
2024-01-22 05:12:49 -05:00
|
|
|
"helm.sh/helm/v3/pkg/cli"
|
|
|
|
|
"helm.sh/helm/v3/pkg/getter"
|
2024-01-17 18:06:18 -05:00
|
|
|
"helm.sh/helm/v3/pkg/kube"
|
2025-07-08 22:30:20 -04:00
|
|
|
"helm.sh/helm/v3/pkg/postrender"
|
2024-11-13 01:18:40 -05:00
|
|
|
"helm.sh/helm/v3/pkg/release"
|
2024-01-22 05:12:49 -05:00
|
|
|
"helm.sh/helm/v3/pkg/repo"
|
2024-01-17 18:06:18 -05:00
|
|
|
"helm.sh/helm/v3/pkg/storage/driver"
|
2025-07-08 22:30:20 -04:00
|
|
|
"sigs.k8s.io/kustomize/api/konfig"
|
|
|
|
|
"sigs.k8s.io/kustomize/api/krusty"
|
|
|
|
|
"sigs.k8s.io/kustomize/api/types"
|
2024-01-17 18:06:18 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var DevNullPrinter = func(format string, v ...interface{}) {}
|
|
|
|
|
|
|
|
|
|
var DeployLogPrinter = func(format string, v ...interface{}) {
|
|
|
|
|
message := strings.TrimSpace(fmt.Sprintf(format, v...))
|
|
|
|
|
if message == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r := []rune(message)
|
|
|
|
|
r[0] = unicode.ToUpper(r[0])
|
|
|
|
|
s := string(r)
|
|
|
|
|
|
|
|
|
|
if strings.HasPrefix(s, "Beginning wait") {
|
|
|
|
|
common.LogExclaim(s)
|
|
|
|
|
} else if strings.HasPrefix(s, "Warning:") {
|
|
|
|
|
common.LogExclaim(s)
|
|
|
|
|
} else {
|
|
|
|
|
common.LogVerboseQuiet(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-19 00:38:06 -04:00
|
|
|
// ChartInput is the input for the InstallOrUpgradeChart function
|
2024-01-18 05:28:07 -05:00
|
|
|
type ChartInput struct {
|
2025-07-19 00:38:06 -04:00
|
|
|
// ChartPath is the path to the chart to install or upgrade
|
|
|
|
|
ChartPath string
|
|
|
|
|
|
2025-07-23 00:04:35 -04:00
|
|
|
// KustomizeRootPath is the path to the kustomize root path to use
|
|
|
|
|
KustomizeRootPath string
|
|
|
|
|
|
2025-07-19 00:38:06 -04:00
|
|
|
// Namespace is the namespace to install or upgrade the chart in
|
|
|
|
|
Namespace string
|
|
|
|
|
|
|
|
|
|
// ReleaseName is the name of the release to install or upgrade
|
|
|
|
|
ReleaseName string
|
|
|
|
|
|
|
|
|
|
// RepoURL is the URL of the chart repository to install or upgrade the chart from
|
|
|
|
|
RepoURL string
|
|
|
|
|
|
|
|
|
|
// RollbackOnFailure is whether to rollback on failure
|
2024-01-18 05:28:07 -05:00
|
|
|
RollbackOnFailure bool
|
2025-07-19 00:38:06 -04:00
|
|
|
|
|
|
|
|
// Timeout is the timeout for the install or upgrade
|
|
|
|
|
Timeout time.Duration
|
|
|
|
|
|
|
|
|
|
// Wait is whether to wait for the install or upgrade to complete
|
|
|
|
|
Wait bool
|
|
|
|
|
|
|
|
|
|
// Version is the version of the chart to install or upgrade
|
|
|
|
|
Version string
|
|
|
|
|
|
|
|
|
|
// Values is the values to pass to the chart
|
|
|
|
|
Values map[string]interface{}
|
2024-01-18 05:28:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Release struct {
|
2024-11-13 01:18:40 -05:00
|
|
|
AppVersion string
|
|
|
|
|
Name string
|
|
|
|
|
Namespace string
|
|
|
|
|
Revision int
|
|
|
|
|
Status release.Status
|
|
|
|
|
Version string
|
2024-01-18 05:28:07 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
type HelmAgent struct {
|
|
|
|
|
Configuration *action.Configuration
|
|
|
|
|
Namespace string
|
|
|
|
|
Logger action.DebugLog
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewHelmAgent(namespace string, logger action.DebugLog) (*HelmAgent, error) {
|
|
|
|
|
actionConfig := new(action.Configuration)
|
|
|
|
|
|
|
|
|
|
helmDriver := os.Getenv("HELM_DRIVER")
|
|
|
|
|
if helmDriver == "" {
|
|
|
|
|
helmDriver = "secrets"
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 03:50:15 -05:00
|
|
|
kubeconfigPath := getKubeconfigPath()
|
|
|
|
|
kubeContext := getKubeContext()
|
|
|
|
|
kubeConfig := kube.GetConfig(kubeconfigPath, kubeContext, namespace)
|
2024-01-17 18:06:18 -05:00
|
|
|
if err := actionConfig.Init(kubeConfig, namespace, helmDriver, logger); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-01-18 05:28:07 -05:00
|
|
|
|
|
|
|
|
return &HelmAgent{
|
|
|
|
|
Configuration: actionConfig,
|
|
|
|
|
Namespace: namespace,
|
|
|
|
|
Logger: logger,
|
|
|
|
|
}, nil
|
2024-01-17 18:06:18 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
type AddRepositoryInput struct {
|
|
|
|
|
Name string
|
|
|
|
|
URL string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *HelmAgent) AddRepository(ctx context.Context, helmRepo AddRepositoryInput) error {
|
|
|
|
|
settings := cli.New()
|
|
|
|
|
repoFile := settings.RepositoryConfig
|
|
|
|
|
|
|
|
|
|
err := os.MkdirAll(filepath.Dir(repoFile), os.ModePerm)
|
|
|
|
|
if err != nil && !os.IsExist(err) {
|
|
|
|
|
return fmt.Errorf("Error creating repository directory: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileLock := flock.New(strings.Replace(repoFile, filepath.Ext(repoFile), ".lock", 1))
|
|
|
|
|
locked, err := fileLock.TryLockContext(ctx, time.Second)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error acquiring file lock: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !locked {
|
|
|
|
|
return fmt.Errorf("Could not acquire file lock")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer fileLock.Unlock() // nolint: errcheck
|
|
|
|
|
|
|
|
|
|
b, err := os.ReadFile(repoFile)
|
|
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
|
return fmt.Errorf("Error reading repository file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var f repo.File
|
|
|
|
|
if err := yaml.Unmarshal(b, &f); err != nil {
|
|
|
|
|
return fmt.Errorf("Error unmarshalling yaml: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.Has(helmRepo.Name) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c := repo.Entry{
|
|
|
|
|
Name: helmRepo.Name,
|
|
|
|
|
URL: helmRepo.URL,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r, err := repo.NewChartRepository(&c, getter.All(settings))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error creating chart repository: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := r.DownloadIndexFile(); err != nil {
|
|
|
|
|
return fmt.Errorf("Specified repository '%q' is not a valid chart repository or cannot be reached: %w", helmRepo.URL, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.Update(&c)
|
|
|
|
|
|
|
|
|
|
if err := f.WriteFile(repoFile, 0644); err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
func (h *HelmAgent) ChartExists(releaseName string) (bool, error) {
|
2024-01-22 05:12:49 -05:00
|
|
|
if releaseName == "" {
|
|
|
|
|
return false, fmt.Errorf("Release name is required")
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
client := action.NewHistory(h.Configuration)
|
|
|
|
|
client.Max = 1
|
|
|
|
|
releases, err := client.Run(releaseName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, driver.ErrReleaseNotFound) {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(releases) > 0 {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
func (h *HelmAgent) DeleteRevision(ctx context.Context, releaseName string, revision int) error {
|
2024-01-18 05:28:07 -05:00
|
|
|
clientset, err := NewKubernetesClient()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error creating kubernetes client: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secretName := fmt.Sprintf("sh.helm.release.v1.%s.v%d", releaseName, revision)
|
2024-01-22 05:12:49 -05:00
|
|
|
err = clientset.DeleteSecret(ctx, DeleteSecretInput{
|
2024-01-18 16:14:33 -05:00
|
|
|
Name: secretName,
|
|
|
|
|
Namespace: h.Namespace,
|
|
|
|
|
})
|
2024-01-18 05:28:07 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error deleting secret: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *HelmAgent) GetValues(releaseName string) (map[string]interface{}, error) {
|
|
|
|
|
client := action.NewGetValues(h.Configuration)
|
|
|
|
|
client.AllValues = true
|
|
|
|
|
values, err := client.Run(releaseName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Error getting values: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return values, nil
|
2024-01-17 18:06:18 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
func (h *HelmAgent) InstallOrUpgradeChart(ctx context.Context, input ChartInput) error {
|
2024-01-17 18:06:18 -05:00
|
|
|
chartExists, err := h.ChartExists(input.ReleaseName)
|
|
|
|
|
if err != nil {
|
2024-01-18 05:28:07 -05:00
|
|
|
return fmt.Errorf("Error checking if chart exists: %w", err)
|
2024-01-17 18:06:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if chartExists {
|
2024-01-22 05:12:49 -05:00
|
|
|
return h.UpgradeChart(ctx, input)
|
2024-01-17 18:06:18 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
return h.InstallChart(ctx, input)
|
2024-01-17 18:06:18 -05:00
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
func (h *HelmAgent) InstallChart(ctx context.Context, input ChartInput) error {
|
2024-01-17 18:06:18 -05:00
|
|
|
namespace := input.Namespace
|
|
|
|
|
if namespace == "" {
|
|
|
|
|
namespace = h.Namespace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if input.ChartPath == "" {
|
|
|
|
|
return fmt.Errorf("Chart path is required")
|
|
|
|
|
}
|
|
|
|
|
if input.ReleaseName == "" {
|
|
|
|
|
return fmt.Errorf("Release name is required")
|
|
|
|
|
}
|
|
|
|
|
if input.Values == nil {
|
|
|
|
|
input.Values = map[string]interface{}{}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 00:04:35 -04:00
|
|
|
kustomizeRenderer := KustomizeRenderer{
|
2025-08-22 02:01:04 -04:00
|
|
|
ReleaseName: input.ReleaseName,
|
2025-07-23 00:04:35 -04:00
|
|
|
KustomizeRootPath: input.KustomizeRootPath,
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
client := action.NewInstall(h.Configuration)
|
|
|
|
|
client.Atomic = false
|
|
|
|
|
client.ChartPathOptions = action.ChartPathOptions{}
|
|
|
|
|
client.CreateNamespace = true
|
|
|
|
|
client.DryRun = false
|
2025-07-08 00:42:57 -04:00
|
|
|
if os.Getenv("DOKKU_TRACE") == "1" {
|
2025-07-23 00:04:35 -04:00
|
|
|
client.PostRenderer = &DebugRenderer{
|
|
|
|
|
Renderer: &kustomizeRenderer,
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
client.PostRenderer = &kustomizeRenderer
|
2025-07-08 00:42:57 -04:00
|
|
|
}
|
2024-01-17 18:06:18 -05:00
|
|
|
client.Namespace = namespace
|
|
|
|
|
client.ReleaseName = input.ReleaseName
|
|
|
|
|
client.Timeout = input.Timeout
|
2024-03-06 21:27:39 -05:00
|
|
|
client.Wait = input.Wait
|
2024-01-17 18:06:18 -05:00
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
settings := cli.New()
|
|
|
|
|
if input.RepoURL != "" {
|
|
|
|
|
client.ChartPathOptions.RepoURL = input.RepoURL
|
|
|
|
|
}
|
|
|
|
|
if input.Version != "" {
|
|
|
|
|
client.ChartPathOptions.Version = input.Version
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chart, err := client.ChartPathOptions.LocateChart(input.ChartPath, settings)
|
2024-01-17 18:06:18 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error locating chart: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chartRequested, err := loader.Load(chart)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error loading chart: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = client.RunWithContext(ctx, chartRequested, input.Values)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error deploying: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-13 01:18:40 -05:00
|
|
|
func (h *HelmAgent) InstalledRevision(releaseName string) (Release, error) {
|
|
|
|
|
revisions, err := h.ListRevisions(ListRevisionsInput{
|
|
|
|
|
ReleaseName: releaseName,
|
|
|
|
|
Max: 1,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return Release{}, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(revisions) == 0 {
|
|
|
|
|
return Release{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return revisions[0], nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ListRevisionsInput struct {
|
|
|
|
|
ReleaseName string
|
|
|
|
|
Max int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *HelmAgent) ListRevisions(input ListRevisionsInput) ([]Release, error) {
|
2024-01-18 05:28:07 -05:00
|
|
|
client := action.NewHistory(h.Configuration)
|
2024-11-13 01:18:40 -05:00
|
|
|
if input.Max > 0 {
|
|
|
|
|
client.Max = input.Max
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
releases := []Release{}
|
|
|
|
|
response, err := client.Run(input.ReleaseName)
|
2024-01-18 05:28:07 -05:00
|
|
|
if err != nil {
|
2024-11-13 01:18:40 -05:00
|
|
|
if errors.Is(err, driver.ErrReleaseNotFound) {
|
|
|
|
|
return releases, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 05:28:07 -05:00
|
|
|
return nil, fmt.Errorf("Error getting revisions: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, release := range response {
|
2024-11-13 01:18:40 -05:00
|
|
|
appVersion := "MISSING"
|
|
|
|
|
if release.Chart != nil && release.Chart.Metadata != nil {
|
|
|
|
|
appVersion = release.Chart.AppVersion()
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 05:28:07 -05:00
|
|
|
releases = append(releases, Release{
|
2024-11-13 01:18:40 -05:00
|
|
|
AppVersion: appVersion,
|
|
|
|
|
Name: release.Name,
|
|
|
|
|
Namespace: release.Namespace,
|
|
|
|
|
Revision: release.Version,
|
|
|
|
|
Status: release.Info.Status,
|
|
|
|
|
Version: release.Chart.Metadata.Version,
|
2024-01-18 05:28:07 -05:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.Slice(releases, func(i, j int) bool {
|
2024-11-13 01:18:40 -05:00
|
|
|
return releases[i].Revision < releases[j].Revision
|
2024-01-18 05:28:07 -05:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return releases, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-22 05:12:49 -05:00
|
|
|
func (h *HelmAgent) UpgradeChart(ctx context.Context, input ChartInput) error {
|
2024-01-17 18:06:18 -05:00
|
|
|
namespace := input.Namespace
|
|
|
|
|
if namespace == "" {
|
|
|
|
|
namespace = h.Namespace
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if input.ChartPath == "" {
|
|
|
|
|
return fmt.Errorf("Chart path is required")
|
|
|
|
|
}
|
|
|
|
|
if input.ReleaseName == "" {
|
|
|
|
|
return fmt.Errorf("Release name is required")
|
|
|
|
|
}
|
|
|
|
|
if input.Values == nil {
|
|
|
|
|
input.Values = map[string]interface{}{}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 00:04:35 -04:00
|
|
|
kustomizeRenderer := KustomizeRenderer{
|
2025-08-22 02:01:04 -04:00
|
|
|
ReleaseName: input.ReleaseName,
|
2025-07-23 00:04:35 -04:00
|
|
|
KustomizeRootPath: input.KustomizeRootPath,
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
client := action.NewUpgrade(h.Configuration)
|
|
|
|
|
client.Atomic = input.RollbackOnFailure
|
|
|
|
|
client.ChartPathOptions = action.ChartPathOptions{}
|
2024-01-18 05:28:07 -05:00
|
|
|
client.CleanupOnFail = true
|
|
|
|
|
client.MaxHistory = 10
|
2024-03-12 03:55:40 -04:00
|
|
|
if os.Getenv("DOKKU_TRACE") == "1" {
|
2025-07-23 00:04:35 -04:00
|
|
|
client.PostRenderer = &DebugRenderer{
|
|
|
|
|
Renderer: &kustomizeRenderer,
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
client.PostRenderer = &kustomizeRenderer
|
2024-03-12 03:55:40 -04:00
|
|
|
}
|
2024-01-17 18:06:18 -05:00
|
|
|
client.Namespace = namespace
|
|
|
|
|
client.Timeout = input.Timeout
|
2024-03-06 21:27:39 -05:00
|
|
|
client.Wait = input.Wait
|
2024-01-22 05:12:49 -05:00
|
|
|
if input.RepoURL != "" {
|
|
|
|
|
client.RepoURL = input.RepoURL
|
|
|
|
|
}
|
2024-01-17 18:06:18 -05:00
|
|
|
|
2024-11-13 01:17:27 -05:00
|
|
|
settings := cli.New()
|
|
|
|
|
chart, err := client.ChartPathOptions.LocateChart(input.ChartPath, settings)
|
2024-01-17 18:06:18 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error locating chart: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chartRequested, err := loader.Load(chart)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error loading chart: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = client.RunWithContext(ctx, input.ReleaseName, chartRequested, input.Values)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error deploying: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *HelmAgent) UninstallChart(releaseName string) error {
|
2024-01-19 19:21:17 -05:00
|
|
|
exists, err := h.ChartExists(releaseName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error checking if chart exists: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 18:06:18 -05:00
|
|
|
uninstall := action.NewUninstall(h.Configuration)
|
2024-01-18 17:41:42 -05:00
|
|
|
uninstall.DeletionPropagation = "foreground"
|
2024-01-19 19:21:17 -05:00
|
|
|
_, err = uninstall.Run(releaseName)
|
2024-01-17 18:06:18 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("Error uninstalling chart: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-07-08 22:30:20 -04:00
|
|
|
|
|
|
|
|
type DebugRenderer struct {
|
2025-07-19 00:37:46 -04:00
|
|
|
Renderer postrender.PostRenderer
|
2025-07-08 22:30:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *DebugRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
|
2025-07-19 00:37:46 -04:00
|
|
|
renderedManifests, err := p.Renderer.Run(renderedManifests)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2025-07-08 22:30:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, line := range strings.Split(renderedManifests.String(), "\n") {
|
|
|
|
|
common.LogWarn(line)
|
|
|
|
|
}
|
|
|
|
|
return renderedManifests, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 02:01:04 -04:00
|
|
|
// KustomizeRenderer is a post renderer that kustomizes the rendered manifests
|
2025-07-08 22:30:20 -04:00
|
|
|
type KustomizeRenderer struct {
|
2025-08-22 02:01:04 -04:00
|
|
|
// KustomizeRootPath is the path to the kustomize root path to use
|
2025-07-08 22:30:20 -04:00
|
|
|
KustomizeRootPath string
|
2025-08-22 02:01:04 -04:00
|
|
|
|
|
|
|
|
// ReleaseName is the name of the release to kustomize
|
|
|
|
|
ReleaseName string
|
2025-07-08 22:30:20 -04:00
|
|
|
}
|
|
|
|
|
|
2025-08-22 02:01:04 -04:00
|
|
|
// Run kustomizes the rendered manifests
|
2025-07-08 22:30:20 -04:00
|
|
|
func (p *KustomizeRenderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
|
|
|
|
|
if p.KustomizeRootPath == "" {
|
2025-07-23 17:28:36 -04:00
|
|
|
return renderedManifests, nil
|
2025-07-08 22:30:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !common.DirectoryExists(p.KustomizeRootPath) {
|
2025-07-23 17:28:36 -04:00
|
|
|
return renderedManifests, nil
|
2025-07-08 22:30:20 -04:00
|
|
|
}
|
|
|
|
|
|
2025-08-22 02:01:04 -04:00
|
|
|
common.LogVerboseQuiet(fmt.Sprintf("Applying kustomization to %s", p.ReleaseName))
|
2025-07-08 22:30:20 -04:00
|
|
|
fs, err := filesys.MakeFsOnDiskSecureBuild(p.KustomizeRootPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Error creating filesystem: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var kfile string
|
|
|
|
|
for _, f := range konfig.RecognizedKustomizationFileNames() {
|
|
|
|
|
if kf := filepath.Join(p.KustomizeRootPath, f); fs.Exists(kf) {
|
|
|
|
|
kfile = kf
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if kfile == "" {
|
|
|
|
|
return nil, fmt.Errorf("%s not found", konfig.DefaultKustomizationFileName())
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 20:35:02 -04:00
|
|
|
if err := fs.WriteFile(filepath.Join(p.KustomizeRootPath, "rendered.yaml"), renderedManifests.Bytes()); err != nil {
|
2025-07-08 22:30:20 -04:00
|
|
|
return nil, fmt.Errorf("Error writing rendered.yaml: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buildOptions := &krusty.Options{
|
|
|
|
|
LoadRestrictions: types.LoadRestrictionsNone,
|
|
|
|
|
PluginConfig: types.DisabledPluginConfig(),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
k := krusty.MakeKustomizer(buildOptions)
|
|
|
|
|
m, err := k.Run(fs, p.KustomizeRootPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resources, err := m.AsYaml()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bytes.NewBuffer(resources), nil
|
|
|
|
|
}
|