Skip to content

Commit b094521

Browse files
committed
fix(helm-dependencies): automatically fill ~/.werf/local_cache on 'werf helm dependency update' command
Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
1 parent 3addc23 commit b094521

File tree

3 files changed

+124
-38
lines changed

3 files changed

+124
-38
lines changed

cmd/werf/helm/helm.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"path/filepath"
78
"time"
89

910
"github.com/spf13/cobra"
@@ -76,11 +77,35 @@ func NewCmd(ctx context.Context) (*cobra.Command, error) {
7677
common.SetupInsecureHelmDependencies(&_commonCmdData, cmd)
7778
common.SetupDockerConfig(&_commonCmdData, cmd, "")
7879

80+
dependencyCmd := helm_v3.NewDependencyCmd(actionConfig, os.Stdout)
81+
for _, depCmd := range dependencyCmd.Commands() {
82+
if depCmd.Name() == "update" {
83+
oldRunE := depCmd.RunE
84+
depCmd.RunE = func(cmd *cobra.Command, args []string) error {
85+
if err := oldRunE(cmd, args); err != nil {
86+
return err
87+
}
88+
89+
chartpath := "."
90+
if len(args) > 0 {
91+
chartpath = filepath.Clean(args[0])
92+
}
93+
94+
ch, err := loader.LoadDir(chartpath)
95+
if err != nil {
96+
return fmt.Errorf("error loading chart %q: %w", chartpath, err)
97+
}
98+
99+
return chart_extender.CopyChartDependenciesIntoCache(cmd.Context(), chartpath, ch)
100+
}
101+
}
102+
}
103+
79104
cmd.AddCommand(
80105
helm2.ReplaceHelmUninstallDocs(helm_v3.NewUninstallCmd(actionConfig, os.Stdout, helm_v3.UninstallCmdOptions{
81106
StagesSplitter: helm.NewStagesSplitter(),
82107
})),
83-
helm2.ReplaceHelmDependencyDocs(helm_v3.NewDependencyCmd(actionConfig, os.Stdout)),
108+
helm2.ReplaceHelmDependencyDocs(dependencyCmd),
84109
helm2.ReplaceHelmGetDocs(helm_v3.NewGetCmd(actionConfig, os.Stdout)),
85110
helm2.ReplaceHelmHistoryDocs(helm_v3.NewHistoryCmd(actionConfig, os.Stdout)),
86111
NewLintCmd(actionConfig, wc),

pkg/deploy/helm/chart_extender/chart_dependencies_loader.go

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"io/ioutil"
89
"os"
910
"path/filepath"
1011
"strings"
1112

13+
"github.com/otiai10/copy"
1214
"github.com/pkg/errors"
1315
uuid "github.com/satori/go.uuid"
1416
"helm.sh/helm/v3/pkg/chart"
@@ -20,13 +22,14 @@ import (
2022

2123
"github.com/werf/lockgate"
2224
"github.com/werf/logboek"
25+
"github.com/werf/logboek/pkg/types"
2326
"github.com/werf/werf/pkg/deploy/helm/command_helpers"
2427
"github.com/werf/werf/pkg/util"
2528
"github.com/werf/werf/pkg/werf"
2629
)
2730

28-
func GetChartDependenciesCacheDir(lockChecksum string) string {
29-
return filepath.Join(werf.GetLocalCacheDir(), "helm_chart_dependencies", "1", lockChecksum)
31+
func GetChartDependenciesCacheDir() string {
32+
return filepath.Join(werf.GetLocalCacheDir(), "helm_chart_dependencies", "1")
3033
}
3134

3235
func LoadMetadata(files []*chart.ChartExtenderBufferedFile) (*chart.Metadata, error) {
@@ -60,14 +63,54 @@ func LoadMetadata(files []*chart.ChartExtenderBufferedFile) (*chart.Metadata, er
6063
return metadata, nil
6164
}
6265

63-
func GetPreparedChartDependenciesDir(ctx context.Context, metadataFile, metadataLockFile *chart.ChartExtenderBufferedFile, helmEnvSettings *cli.EnvSettings, registryClient *registry.Client, buildChartDependenciesOpts command_helpers.BuildChartDependenciesOptions) (string, error) {
64-
depsDir := GetChartDependenciesCacheDir(util.Sha256Hash(string(metadataLockFile.Data)))
66+
func CopyChartDependenciesIntoCache(ctx context.Context, chartDir string, ch *chart.Chart) error {
67+
metadataBytes, err := yaml.Marshal(ch.Metadata)
68+
if err != nil {
69+
return fmt.Errorf("unable to marshal chart metadata into yaml: %w", err)
70+
}
71+
72+
metadataLockBytes, err := yaml.Marshal(ch.Lock)
73+
if err != nil {
74+
return fmt.Errorf("unable to marshal chart metadata lock into yaml: %w", err)
75+
}
76+
77+
_, err = prepareDependenciesDir(ctx, metadataBytes, metadataLockBytes, func(tmpDepsDir string) error {
78+
var archivesDependencies []*chart.File
79+
80+
FindArchivesDependencies:
81+
for _, f := range ch.Raw {
82+
if strings.HasPrefix(f.Name, "charts/") {
83+
for _, depLock := range ch.Lock.Dependencies {
84+
if filepath.Base(f.Name) == MakeDependencyArchiveName(depLock.Name, depLock.Version) {
85+
archivesDependencies = append(archivesDependencies, f)
86+
continue FindArchivesDependencies
87+
}
88+
}
89+
}
90+
}
91+
92+
for _, depArch := range archivesDependencies {
93+
srcPath := filepath.Join(chartDir, depArch.Name)
94+
destPath := filepath.Join(tmpDepsDir, depArch.Name)
95+
if err := copy.Copy(srcPath, destPath); err != nil {
96+
return fmt.Errorf("unable to copy %q into cache %q: %w", srcPath, tmpDepsDir, err)
97+
}
98+
}
99+
100+
return nil
101+
}, logboek.Context(ctx).Info())
102+
103+
return err
104+
}
105+
106+
func prepareDependenciesDir(ctx context.Context, metadataBytes, metadataLockBytes []byte, prepareFunc func(tmpDepsDir string) error, logger types.ManagerInterface) (string, error) {
107+
depsDir := filepath.Join(GetChartDependenciesCacheDir(), util.Sha256Hash(string(metadataLockBytes)))
65108

66109
_, err := os.Stat(depsDir)
67110
switch {
68111
case os.IsNotExist(err):
69-
if err := logboek.Context(ctx).Default().LogProcess("Building chart dependencies").DoError(func() error {
70-
logboek.Context(ctx).Default().LogF("Using chart dependencies directory: %s\n", depsDir)
112+
if err := logger.LogProcess("Preparing chart dependencies").DoError(func() error {
113+
logger.LogF("Using chart dependencies directory: %s\n", depsDir)
71114
_, lock, err := werf.AcquireHostLock(ctx, depsDir, lockgate.AcquireOptions{})
72115
if err != nil {
73116
return fmt.Errorf("error acquiring lock for %q: %w", depsDir, err)
@@ -85,32 +128,67 @@ func GetPreparedChartDependenciesDir(ctx context.Context, metadataFile, metadata
85128

86129
tmpDepsDir := fmt.Sprintf("%s.tmp.%s", depsDir, uuid.NewV4().String())
87130

88-
buildChartDependenciesOpts.LoadOptions = &loader.LoadOptions{
89-
ChartExtender: NewWerfChartStub(ctx, buildChartDependenciesOpts.IgnoreInvalidAnnotationsAndLabels),
90-
SubchartExtenderFactoryFunc: nil,
131+
if err := createChartDependenciesDir(tmpDepsDir, metadataBytes, metadataLockBytes); err != nil {
132+
return err
91133
}
92134

93-
if err := command_helpers.BuildChartDependenciesInDir(ctx, metadataFile, metadataLockFile, tmpDepsDir, helmEnvSettings, registryClient, buildChartDependenciesOpts); err != nil {
94-
return fmt.Errorf("error building chart dependencies: %w", err)
135+
if err := prepareFunc(tmpDepsDir); err != nil {
136+
return err
95137
}
96138

97139
if err := os.Rename(tmpDepsDir, depsDir); err != nil {
98140
return fmt.Errorf("error renaming %q to %q: %w", tmpDepsDir, depsDir, err)
99141
}
100-
101142
return nil
102143
}); err != nil {
103144
return "", err
104145
}
105146
case err != nil:
106147
return "", fmt.Errorf("error accessing %q: %w", depsDir, err)
107148
default:
108-
logboek.Context(ctx).Default().LogF("Using cached chart dependencies directory: %s\n", depsDir)
149+
logger.LogF("Using cached chart dependencies directory: %s\n", depsDir)
109150
}
110151

111152
return depsDir, nil
112153
}
113154

155+
func createChartDependenciesDir(destDir string, metadataBytes, metadataLockBytes []byte) error {
156+
if err := os.MkdirAll(destDir, os.ModePerm); err != nil {
157+
return fmt.Errorf("error creating dir %q: %w", destDir, err)
158+
}
159+
160+
files := []*chart.ChartExtenderBufferedFile{
161+
{Name: "Chart.yaml", Data: metadataBytes},
162+
{Name: "Chart.lock", Data: metadataLockBytes},
163+
}
164+
165+
for _, file := range files {
166+
if file == nil {
167+
continue
168+
}
169+
170+
path := filepath.Join(destDir, file.Name)
171+
if err := ioutil.WriteFile(path, file.Data, 0o644); err != nil {
172+
return fmt.Errorf("error writing %q: %w", path, err)
173+
}
174+
}
175+
176+
return nil
177+
}
178+
179+
func GetPreparedChartDependenciesDir(ctx context.Context, metadataFile, metadataLockFile *chart.ChartExtenderBufferedFile, helmEnvSettings *cli.EnvSettings, registryClient *registry.Client, buildChartDependenciesOpts command_helpers.BuildChartDependenciesOptions) (string, error) {
180+
return prepareDependenciesDir(ctx, metadataFile.Data, metadataLockFile.Data, func(tmpDepsDir string) error {
181+
buildChartDependenciesOpts.LoadOptions = &loader.LoadOptions{
182+
ChartExtender: NewWerfChartStub(ctx, buildChartDependenciesOpts.IgnoreInvalidAnnotationsAndLabels),
183+
SubchartExtenderFactoryFunc: nil,
184+
}
185+
if err := command_helpers.BuildChartDependenciesInDir(ctx, tmpDepsDir, helmEnvSettings, registryClient, buildChartDependenciesOpts); err != nil {
186+
return fmt.Errorf("error building chart dependencies: %w", err)
187+
}
188+
return nil
189+
}, logboek.Context(ctx).Default())
190+
}
191+
114192
type ChartDependenciesConfiguration struct {
115193
ChartMetadata *chart.Metadata
116194
ChartMetadataLock *chart.Lock
@@ -142,7 +220,7 @@ func (conf *ChartDependenciesConfiguration) GetExternalDependenciesFiles(loadedC
142220
metadata.APIVersion = "v2"
143221

144222
var externalDependenciesNames []string
145-
var isExternalDependency = func(depName string) bool {
223+
isExternalDependency := func(depName string) bool {
146224
for _, externalDepName := range externalDependenciesNames {
147225
if depName == externalDepName {
148226
return true
@@ -159,7 +237,7 @@ FindExternalDependencies:
159237

160238
for _, loadedFile := range loadedChartFiles {
161239
if strings.HasPrefix(loadedFile.Name, "charts/") {
162-
if filepath.Base(loadedFile.Name) == fmt.Sprintf("%s-%s.tgz", depLock.Name, depLock.Version) {
240+
if filepath.Base(loadedFile.Name) == MakeDependencyArchiveName(depLock.Name, depLock.Version) {
163241
continue FindExternalDependencies
164242
}
165243
}
@@ -343,3 +421,7 @@ func HashReq(req, lock []*chart.Dependency) (string, error) {
343421
s, err := provenance.Digest(bytes.NewBuffer(data))
344422
return "sha256:" + s, err
345423
}
424+
425+
func MakeDependencyArchiveName(depName, depVersion string) string {
426+
return fmt.Sprintf("%s-%s.tgz", depName, depVersion)
427+
}

pkg/deploy/helm/command_helpers/build_chart_dependencies.go

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ package command_helpers
33
import (
44
"context"
55
"fmt"
6-
"io/ioutil"
7-
"os"
8-
"path/filepath"
96

10-
"helm.sh/helm/v3/pkg/chart"
117
"helm.sh/helm/v3/pkg/chart/loader"
128
"helm.sh/helm/v3/pkg/cli"
139
"helm.sh/helm/v3/pkg/downloader"
@@ -25,26 +21,9 @@ type BuildChartDependenciesOptions struct {
2521
IgnoreInvalidAnnotationsAndLabels bool
2622
}
2723

28-
func BuildChartDependenciesInDir(ctx context.Context, chartFile, chartLockFile *chart.ChartExtenderBufferedFile, targetDir string, helmEnvSettings *cli.EnvSettings, registryClient *registry.Client, opts BuildChartDependenciesOptions) error {
24+
func BuildChartDependenciesInDir(ctx context.Context, targetDir string, helmEnvSettings *cli.EnvSettings, registryClient *registry.Client, opts BuildChartDependenciesOptions) error {
2925
logboek.Context(ctx).Debug().LogF("-- BuildChartDependenciesInDir\n")
3026

31-
if err := os.MkdirAll(targetDir, os.ModePerm); err != nil {
32-
return fmt.Errorf("error creating dir %q: %w", targetDir, err)
33-
}
34-
35-
files := []*chart.ChartExtenderBufferedFile{chartFile, chartLockFile}
36-
37-
for _, file := range files {
38-
if file == nil {
39-
continue
40-
}
41-
42-
path := filepath.Join(targetDir, file.Name)
43-
if err := ioutil.WriteFile(path, file.Data, 0o644); err != nil {
44-
return fmt.Errorf("error writing %q: %w", path, err)
45-
}
46-
}
47-
4827
man := &downloader.Manager{
4928
Out: logboek.Context(ctx).OutStream(),
5029
ChartPath: targetDir,

0 commit comments

Comments
 (0)