Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package runner
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/go-cmd/cmd"
"gopkg.in/yaml.v3"
)
type HelmDependency struct {
Name string
Repository string
}
type HelmChart struct {
Dependencies []HelmDependency
}
func (c CmdConfig) HelmDependencyBuild() error {
paths, err := c.HelmChartsPaths()
if err != nil {
return err
}
c.Logger.Debug().Strs("paths", paths).Msg("found helm dependencies")
for _, p := range paths {
if err := c.HelmBuildDependency(p); err != nil {
return err
}
}
return nil
}
func (c CmdConfig) HelmBuildDependency(path string) error {
args := []string{"dependency", "build", path}
apiCmd := cmd.NewCmd(helmCmd, args...)
stdOut, stdErr, err := RunCMD(apiCmd)
if err != nil {
c.Logger.Err(err).
Str("command", helmCmd).
Str("args", strings.Join(args, " ")).
Str("sdtout", strings.Join(stdOut, "\n")).
Str("stderr", strings.Join(stdErr, "\n")).
Msg("failed to run command")
// Error must be pretty printed to end users /!\
fmt.Printf("\n%s\n\n", strings.Join(stdErr, "\n"))
return fmt.Errorf("failed to run command: %w", err)
}
c.Logger.Debug().
Strs("stdout", stdOut).
Str("path", path).
Msg("helm dependencies successfully built")
return nil
}
func (c CmdConfig) HelmChartsPaths() ([]string, error) {
var allPaths []string
for name, chart := range c.Spec.Charts {
if chart.Type == "helm" {
c.Logger.Debug().
Str("chart", name).
Str("type", chart.Type).
Str("path", chart.Path).
Msg("search helm dependencies for")
paths, err := c.pathsByChart(chart.Path)
if err != nil {
return nil, err
}
for _, p := range paths {
// Avoid infinite loop with circular dependencies.
// Also improve the performance by templating only
// once any given chart in case the dependency is
// used multiple times.
if !contains(allPaths, p) {
allPaths = append(allPaths, p)
}
}
}
}
return allPaths, nil
}
func (c CmdConfig) pathsByChart(path string) ([]string, error) {
var allPaths []string
helmChart, err := getHelmChart(path)
if err != nil {
return nil, err
}
for _, dependency := range helmChart.Dependencies {
c.Logger.Debug().
Str("chart", dependency.Name).
Str("repository", dependency.Repository).
Msg("found helm dependency")
if strings.HasPrefix(dependency.Repository, "file://") {
subChartPath := filepath.Join(
path, strings.TrimPrefix(dependency.Repository, "file://"))
subChartsDependenciesPaths, err := c.pathsByChart(subChartPath)
if err != nil {
return nil, err
}
allPaths = append(allPaths, subChartsDependenciesPaths...)
}
}
allPaths = append(allPaths, path)
return allPaths, nil
}
func getHelmChart(path string) (*HelmChart, error) {
helmChart := HelmChart{}
for _, ext := range []string{"yaml", "yml"} {
helmChartFile := filepath.Join(path, fmt.Sprintf("%s.%s", "Chart", ext))
fileInfo, err := os.Stat(helmChartFile)
if err != nil || fileInfo.IsDir() {
continue
}
helmChartContent, err := os.ReadFile(helmChartFile)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(helmChartContent, &helmChart)
if err != nil {
return nil, err
}
return &helmChart, nil
}
return nil, fmt.Errorf("helm Chart.yaml file not found in %s", path)
}
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}