@@ -19,6 +19,7 @@ package helm
1919import (
2020 "context"
2121 "fmt"
22+ "net/url"
2223 "os"
2324 "path/filepath"
2425 "strings"
@@ -30,22 +31,35 @@ import (
3031 "helm.sh/helm/v3/pkg/chart/loader"
3132)
3233
33- // DependencyWithRepository is a container for a dependency and its respective
34- // repository
34+ // DependencyWithRepository is a container for a Helm chart dependency
35+ // and its respective repository.
3536type DependencyWithRepository struct {
37+ // Dependency holds the reference to a chart.Chart dependency.
3638 Dependency * helmchart.Dependency
37- Repo * ChartRepository
39+ // Repository is the ChartRepository the dependency should be
40+ // available at and can be downloaded from. If there is none,
41+ // a local ('file://') dependency is assumed.
42+ Repository * ChartRepository
3843}
3944
40- // DependencyManager manages dependencies for helm charts
45+ // DependencyManager manages dependencies for a Helm chart.
4146type DependencyManager struct {
42- BaseDir string
43- ChartPath string
44- Chart * helmchart.Chart
47+ // WorkingDir is the chroot path for dependency manager operations,
48+ // Dependencies that hold a local (relative) path reference are not
49+ // allowed to traverse outside this directory.
50+ WorkingDir string
51+ // ChartPath is the path of the Chart relative to the WorkingDir,
52+ // the combination of the WorkingDir and ChartPath is used to
53+ // determine the absolute path of a local dependency.
54+ ChartPath string
55+ // Chart holds the loaded chart.Chart from the ChartPath.
56+ Chart * helmchart.Chart
57+ // Dependencies contains a list of dependencies, and the respective
58+ // repository the dependency can be found at.
4559 Dependencies []* DependencyWithRepository
4660}
4761
48- // Build compiles and builds the chart dependencies
62+ // Build compiles and builds the dependencies of the Chart.
4963func (dm * DependencyManager ) Build (ctx context.Context ) error {
5064 if len (dm .Dependencies ) == 0 {
5165 return nil
@@ -60,85 +74,90 @@ func (dm *DependencyManager) Build(ctx context.Context) error {
6074 default :
6175 }
6276
63- var (
64- ch * helmchart.Chart
65- err error
66- )
67- if strings .HasPrefix (item .Dependency .Repository , "file://" ) {
68- ch , err = chartForLocalDependency (item .Dependency , dm .BaseDir , dm .ChartPath )
69- } else {
70- ch , err = chartForRemoteDependency (item .Dependency , item .Repo )
71- }
72- if err != nil {
73- return err
77+ var err error
78+ switch item .Repository {
79+ case nil :
80+ err = dm .addLocalDependency (item )
81+ default :
82+ err = dm .addRemoteDependency (item )
7483 }
75- dm .Chart .AddDependency (ch )
76- return nil
84+ return err
7785 })
7886 }
7987
8088 return errs .Wait ()
8189}
8290
83- func chartForLocalDependency (dep * helmchart.Dependency , baseDir , chartPath string ) (* helmchart.Chart , error ) {
84- origPath , err := securejoin .SecureJoin (baseDir ,
85- filepath .Join (strings .TrimPrefix (chartPath , baseDir ), strings .TrimPrefix (dep .Repository , "file://" )))
91+ func (dm * DependencyManager ) addLocalDependency (dpr * DependencyWithRepository ) error {
92+ sLocalChartPath , err := dm .secureLocalChartPath (dpr )
8693 if err != nil {
87- return nil , err
94+ return err
8895 }
8996
90- if _ , err := os .Stat (origPath ); os .IsNotExist (err ) {
91- err := fmt .Errorf ("chart path %s not found: %w" , origPath , err )
92- return nil , err
93- } else if err != nil {
94- return nil , err
97+ if _ , err := os .Stat (sLocalChartPath ); err != nil {
98+ if os .IsNotExist (err ) {
99+ return fmt .Errorf ("no chart found at '%s' (reference '%s') for dependency '%s'" ,
100+ strings .TrimPrefix (sLocalChartPath , dm .WorkingDir ), dpr .Dependency .Repository , dpr .Dependency .Name )
101+ }
102+ return err
95103 }
96104
97- ch , err := loader .Load (origPath )
105+ ch , err := loader .Load (sLocalChartPath )
98106 if err != nil {
99- return nil , err
107+ return err
100108 }
101109
102- constraint , err := semver .NewConstraint (dep .Version )
110+ constraint , err := semver .NewConstraint (dpr . Dependency .Version )
103111 if err != nil {
104- err := fmt .Errorf ("dependency %s has an invalid version/constraint format: %w" , dep .Name , err )
105- return nil , err
112+ err := fmt .Errorf ("dependency '%s' has an invalid version/constraint format: %w" , dpr . Dependency .Name , err )
113+ return err
106114 }
107115
108116 v , err := semver .NewVersion (ch .Metadata .Version )
109117 if err != nil {
110- return nil , err
118+ return err
111119 }
112120
113121 if ! constraint .Check (v ) {
114- err = fmt .Errorf ("can't get a valid version for dependency %s " , dep .Name )
115- return nil , err
122+ err = fmt .Errorf ("can't get a valid version for dependency '%s' " , dpr . Dependency .Name )
123+ return err
116124 }
117125
118- return ch , nil
126+ dm .Chart .AddDependency (ch )
127+ return nil
119128}
120129
121- func chartForRemoteDependency ( dep * helmchart. Dependency , chartRepo * ChartRepository ) ( * helmchart. Chart , error ) {
122- if chartRepo == nil {
123- return nil , fmt .Errorf ("chartrepo should not be nil" )
130+ func ( dm * DependencyManager ) addRemoteDependency ( dpr * DependencyWithRepository ) error {
131+ if dpr . Repository == nil {
132+ return fmt .Errorf ("no ChartRepository given for '%s' dependency" , dpr . Dependency . Name )
124133 }
125134
126- // Lookup the chart version in the chart repository index
127- chartVer , err := chartRepo .Get (dep .Name , dep .Version )
135+ chartVer , err := dpr .Repository .Get (dpr .Dependency .Name , dpr .Dependency .Version )
128136 if err != nil {
129- return nil , err
137+ return err
130138 }
131139
132- // Download chart
133- res , err := chartRepo .DownloadChart (chartVer )
140+ res , err := dpr .Repository .DownloadChart (chartVer )
134141 if err != nil {
135- return nil , err
142+ return err
136143 }
137144
138145 ch , err := loader .LoadArchive (res )
139146 if err != nil {
140- return nil , err
147+ return err
141148 }
142149
143- return ch , nil
150+ dm .Chart .AddDependency (ch )
151+ return nil
152+ }
153+
154+ func (dm * DependencyManager ) secureLocalChartPath (dep * DependencyWithRepository ) (string , error ) {
155+ localUrl , err := url .Parse (dep .Dependency .Repository )
156+ if err != nil {
157+ return "" , fmt .Errorf ("failed to parse alleged local chart reference: %w" , err )
158+ }
159+ if localUrl .Scheme != "file" {
160+ return "" , fmt .Errorf ("'%s' is not a local chart reference" , dep .Dependency .Repository )
161+ }
162+ return securejoin .SecureJoin (dm .WorkingDir , filepath .Join (dm .ChartPath , localUrl .Host , localUrl .Path ))
144163}
0 commit comments