Skip to content

Commit eb030db

Browse files
Code optimization (#344)
* Code optimization * Code optimization
1 parent 025bda5 commit eb030db

File tree

2 files changed

+70
-145
lines changed

2 files changed

+70
-145
lines changed

flexpack/helm_flexpack.go

Lines changed: 24 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"path/filepath"
1010
"runtime"
1111
"strings"
12-
"sync"
1312
"time"
1413

1514
"github.com/jfrog/build-info-go/entities"
@@ -24,16 +23,15 @@ const (
2423

2524
// HelmFlexPack implements the FlexPackManager interface for Helm package manager
2625
type HelmFlexPack struct {
27-
config HelmConfig
28-
dependencies []DependencyInfo
29-
dependencyGraph map[string][]string
30-
chartData *HelmChartYAML
31-
lockData *HelmChartLock
32-
cliPath string
33-
cacheIndex map[string]string
34-
cacheIndexBuilt bool
35-
repoAliases map[string]string // Maps repository alias (without @) to URL
36-
mu sync.RWMutex
26+
config HelmConfig
27+
dependencies []DependencyInfo
28+
dependencyGraph map[string][]string
29+
chartData *HelmChartYAML
30+
lockData *HelmChartLock
31+
cliPath string
32+
cacheIndex map[string]string
33+
repoAliases map[string]string
34+
cacheDirectories []string
3735
}
3836

3937
// HelmConfig represents configuration for Helm FlexPack
@@ -125,8 +123,14 @@ func NewHelmFlexPack(config HelmConfig) (*HelmFlexPack, error) {
125123
return nil, fmt.Errorf("failed to load Chart.yaml: %w", err)
126124
}
127125

128-
// Load Chart.lock if it exists (optional)
129-
_ = hf.loadChartLock() // Ignore error if lock file doesn't exist
126+
// Load Chart.lock
127+
if err := hf.loadChartLock(); err != nil {
128+
return nil, fmt.Errorf("failed to load Chart.lock: %w", err)
129+
}
130+
131+
// Load cache directories and build cache index during initialization
132+
hf.cacheDirectories = hf.loadCacheDirectories()
133+
hf.buildCacheIndex()
130134

131135
return hf, nil
132136
}
@@ -177,9 +181,6 @@ func (hf *HelmFlexPack) loadChartLock() error {
177181

178182
data, err := os.ReadFile(lockPath)
179183
if err != nil {
180-
if os.IsNotExist(err) {
181-
return nil // Chart.lock is optional
182-
}
183184
return fmt.Errorf("failed to read Chart.lock: %w", err)
184185
}
185186

@@ -193,115 +194,16 @@ func (hf *HelmFlexPack) loadChartLock() error {
193194

194195
// getDependencies returns all dependencies (lazy loading)
195196
func (hf *HelmFlexPack) getDependencies() []DependencyInfo {
196-
hf.mu.RLock()
197197
if len(hf.dependencies) > 0 {
198198
deps := hf.dependencies
199-
hf.mu.RUnlock()
200199
return deps
201200
}
202-
hf.mu.RUnlock()
203-
hf.mu.Lock()
204-
defer hf.mu.Unlock()
205-
// Double-check after acquiring write lock
206-
if len(hf.dependencies) > 0 {
207-
return hf.dependencies
208-
}
209-
// Resolve dependencies using hybrid approach
210-
if err := hf.resolveDependencies(); err != nil {
211-
// Fallback to file-based parsing if helm dependency list fails
212-
hf.parseDependenciesFromChartYamlAndLockfile()
213-
}
201+
// Parse dependencies directly from Chart.yaml and Chart.lock
202+
hf.parseDependenciesFromChartYamlAndLockfile()
214203
return hf.dependencies
215204
}
216205

217-
// resolveDependencies uses CLI to resolve dependencies (primary strategy)
218-
func (hf *HelmFlexPack) resolveDependencies() error {
219-
// Try using helm dependency list command
220-
cmd := exec.Command(hf.cliPath, "dependency", "list")
221-
cmd.Dir = hf.config.WorkingDirectory
222-
output, err := cmd.CombinedOutput()
223-
if err != nil {
224-
outputStr := strings.TrimSpace(string(output))
225-
if outputStr != "" {
226-
return fmt.Errorf("helm dependency list failed: %w\nOutput: %s", err, outputStr)
227-
}
228-
return fmt.Errorf("helm dependency list failed: %w", err)
229-
}
230-
return hf.parseHelmDependencyList(string(output))
231-
}
232-
233-
// parseHelmDependencyList parses output from `helm dependency list`
234-
// Only includes dependencies that are declared in the current chart's Chart.yaml
235-
func (hf *HelmFlexPack) parseHelmDependencyList(output string) error {
236-
scanner := bufio.NewScanner(strings.NewReader(output))
237-
headerSkipped := false
238-
// Build a set of valid dependency names from Chart.yaml to filter out dependencies from other directories
239-
validDependencyNames := hf.getValidDependencyNames()
240-
for scanner.Scan() {
241-
line := strings.TrimSpace(scanner.Text())
242-
if line == "" {
243-
continue
244-
}
245-
if !headerSkipped && hf.isHeaderLine(line) {
246-
headerSkipped = true
247-
continue
248-
}
249-
headerSkipped = true
250-
if dep := hf.parseDependencyLine(line); dep != nil {
251-
// Only include dependencies that are declared in the current chart's Chart.yaml
252-
if !hf.isValidDependency(dep.Name, validDependencyNames) {
253-
continue
254-
}
255-
// Resolve repository alias if needed
256-
hf.resolveRepositoryAlias(dep)
257-
hf.dependencies = append(hf.dependencies, *dep)
258-
}
259-
}
260-
return scanner.Err()
261-
}
262-
263-
// getValidDependencyNames returns a set of dependency names declared in Chart.yaml
264-
func (hf *HelmFlexPack) getValidDependencyNames() map[string]bool {
265-
validNames := make(map[string]bool)
266-
if hf.chartData == nil {
267-
return validNames
268-
}
269-
for _, dep := range hf.chartData.Dependencies {
270-
validNames[dep.Name] = true
271-
}
272-
return validNames
273-
}
274-
275-
// isValidDependency checks if a dependency name is declared in the current chart's Chart.yaml
276-
func (hf *HelmFlexPack) isValidDependency(depName string, validNames map[string]bool) bool {
277-
// Only include dependencies that are explicitly declared in Chart.yaml
278-
// This filters out dependencies from other directories that helm dependency list might return
279-
return validNames[depName]
280-
}
281-
282-
// isHeaderLine checks if a line is the header line
283-
func (hf *HelmFlexPack) isHeaderLine(line string) bool {
284-
return strings.Contains(line, "NAME")
285-
}
286-
287-
// parseDependencyLine parses a single dependency line from helm dependency list output
288-
// Format: NAME VERSION REPOSITORY STATUS
289-
func (hf *HelmFlexPack) parseDependencyLine(line string) *DependencyInfo {
290-
fields := strings.Fields(line)
291-
if len(fields) < 3 {
292-
return nil
293-
}
294-
return &DependencyInfo{
295-
Name: fields[0],
296-
Version: fields[1],
297-
Repository: fields[2],
298-
Type: "helm",
299-
ID: fmt.Sprintf("%s:%s", fields[0], fields[1]),
300-
}
301-
}
302-
303206
// getHelmRepoList gets and parses the output of 'helm repo list' command
304-
// Returns a map of repository alias (without @) to URL
305207
func (hf *HelmFlexPack) getHelmRepoList() (map[string]string, error) {
306208
cmd := exec.Command(hf.cliPath, "repo", "list")
307209
output, err := cmd.CombinedOutput()
@@ -356,18 +258,14 @@ func (hf *HelmFlexPack) resolveRepositoryAlias(dep *DependencyInfo) {
356258
// Extract alias name (remove @ prefix)
357259
alias := strings.TrimPrefix(dep.Repository, "@")
358260

359-
// Load repo list if not already loaded
360-
hf.mu.Lock()
361261
if len(hf.repoAliases) == 0 {
362262
repoMap, err := hf.getHelmRepoList()
363263
if err != nil {
364264
log.Warn(fmt.Sprintf("Failed to get helm repo list: %v", err))
365-
hf.mu.Unlock()
366265
return
367266
}
368267
hf.repoAliases = repoMap
369268
}
370-
hf.mu.Unlock()
371269

372270
// Resolve alias to URL
373271
if url, found := hf.repoAliases[alias]; found {
@@ -445,7 +343,6 @@ func (hf *HelmFlexPack) calculateChecksumWithFallback(dep DependencyInfo) map[st
445343
}
446344

447345
// Strategy 2: Try to find dependency chart in charts/ directory
448-
// When helm dependency build/update is run, dependencies are stored in charts/ subdirectory
449346
if chartFile := hf.findDependencyInChartsDir(dep.Name, dep.Version); chartFile != "" {
450347
if Sha1, Sha256, Md5, err := hf.calculateFileChecksum(chartFile); err == nil {
451348
checksumMap["sha1"] = Sha1
@@ -464,11 +361,6 @@ func (hf *HelmFlexPack) calculateChecksumWithFallback(dep DependencyInfo) map[st
464361

465362
// findChartFile searches for chart archive in Helm cache
466363
func (hf *HelmFlexPack) findChartFile(name, version string) string {
467-
// Build cache index if not already built
468-
if !hf.cacheIndexBuilt {
469-
hf.buildCacheIndex()
470-
hf.cacheIndexBuilt = true
471-
}
472364
key := fmt.Sprintf("%s-%s", name, version)
473365
if path, found := hf.cacheIndex[key]; found {
474366
return path
@@ -493,12 +385,8 @@ func (hf *HelmFlexPack) findDependencyInChartsDir(name, version string) string {
493385
}
494386

495387
// buildCacheIndex builds an index of cached chart files
496-
// Validates cache directories to prevent path traversal attacks
497388
func (hf *HelmFlexPack) buildCacheIndex() {
498-
cacheDirs := hf.getCacheDirectories()
499-
for _, cacheDir := range cacheDirs {
500-
// Additional validation at point of use to prevent path traversal
501-
// Even though getCacheDirectories() sanitizes paths, we validate again here
389+
for _, cacheDir := range hf.cacheDirectories {
502390
cleanDir := filepath.Clean(cacheDir)
503391
if cleanDir == "" || cleanDir == "." || cleanDir == ".." {
504392
continue
@@ -566,8 +454,7 @@ func (hf *HelmFlexPack) buildCacheIndex() {
566454

567455
// recursiveSearchCache performs recursive search for chart files
568456
func (hf *HelmFlexPack) searchCache(name, version string) string {
569-
cacheDirs := hf.getCacheDirectories()
570-
for _, cacheDir := range cacheDirs {
457+
for _, cacheDir := range hf.cacheDirectories {
571458
if foundPath := hf.searchCacheDirectory(cacheDir, name, version); foundPath != "" {
572459
return foundPath
573460
}
@@ -653,13 +540,11 @@ func (hf *HelmFlexPack) findFileInDirectory(dir, pattern string) string {
653540
return foundPath
654541
}
655542

656-
// getCacheDirectories returns Helm cache directory paths
657-
// Validates and sanitizes paths to prevent path traversal attacks
658-
func (hf *HelmFlexPack) getCacheDirectories() []string {
543+
// loadCacheDirectories loads and returns Helm cache directory paths
544+
func (hf *HelmFlexPack) loadCacheDirectories() []string {
659545
var paths []string
660546

661547
// Environment variable override (highest priority)
662-
// Validate and sanitize environment variable path to prevent path traversal
663548
if envPath := os.Getenv("HELM_REPOSITORY_CACHE"); envPath != "" {
664549
// Sanitize the path
665550
cleanPath := filepath.Clean(envPath)
@@ -723,6 +608,7 @@ func (hf *HelmFlexPack) getCacheDirectories() []string {
723608
existingPaths = append(existingPaths, absPath)
724609
}
725610
}
611+
726612
return existingPaths
727613
}
728614

0 commit comments

Comments
 (0)