helmfile/pkg/state/storage.go

158 lines
3.8 KiB
Go

package state
import (
"fmt"
"net/url"
"path/filepath"
"sort"
"strings"
"go.uber.org/zap"
"github.com/helmfile/helmfile/pkg/filesystem"
"github.com/helmfile/helmfile/pkg/remote"
)
type Storage struct {
logger *zap.SugaredLogger
FilePath string
basePath string
fs *filesystem.FileSystem
}
func NewStorage(forFile string, logger *zap.SugaredLogger, fs *filesystem.FileSystem) *Storage {
return &Storage{
FilePath: forFile,
basePath: filepath.Dir(forFile),
logger: logger,
fs: fs,
}
}
type resolveFileConfig struct {
IgnoreMissingGitBranch bool
}
type resolveFileOption func(*resolveFileConfig)
func ignoreMissingGitBranch(v bool) func(c *resolveFileConfig) {
return func(c *resolveFileConfig) {
c.IgnoreMissingGitBranch = v
}
}
func (st *Storage) resolveFile(missingFileHandler *string, tpe, path string, opts ...resolveFileOption) ([]string, bool, error) {
title := fmt.Sprintf("%s file", tpe)
var (
files []string
err error
conf resolveFileConfig
)
for _, o := range opts {
o(&conf)
}
if remote.IsRemote(path) {
r := remote.NewRemote(st.logger, "", st.fs)
fetchedFilePath, err := r.Fetch(path, "values")
if err != nil {
// https://github.com/helmfile/helmfile/issues/392
if conf.IgnoreMissingGitBranch && strings.Contains(err.Error(), "' did not match any file(s) known to git") {
st.logger.Debugf("Ignored missing git branch error: %v", err)
} else {
return nil, false, err
}
}
if st.fs.FileExistsAt(fetchedFilePath) {
files = []string{fetchedFilePath}
}
} else {
files, err = st.ExpandPaths(path)
}
if err != nil {
return nil, false, err
}
var handlerId string
if missingFileHandler != nil {
handlerId = *missingFileHandler
} else {
handlerId = MissingFileHandlerError
}
if len(files) == 0 {
switch handlerId {
case MissingFileHandlerError:
return nil, false, fmt.Errorf("%s matching \"%s\" does not exist in \"%s\"", title, path, st.basePath)
case MissingFileHandlerWarn:
st.logger.Warnf("skipping missing %s matching \"%s\"", title, path)
return nil, true, nil
case MissingFileHandlerInfo:
st.logger.Infof("skipping missing %s matching \"%s\"", title, path)
return nil, true, nil
case MissingFileHandlerDebug:
st.logger.Debugf("skipping missing %s matching \"%s\"", title, path)
return nil, true, nil
default:
available := []string{
MissingFileHandlerError,
MissingFileHandlerWarn,
MissingFileHandlerInfo,
MissingFileHandlerDebug,
}
return nil, false, fmt.Errorf("invalid missing file handler \"%s\" while processing \"%s\" in \"%s\": it must be one of %s", handlerId, path, st.FilePath, available)
}
}
return files, false, nil
}
func (st *Storage) ExpandPaths(globPattern string) ([]string, error) {
result := []string{}
absPathPattern := st.normalizePath(globPattern)
matches, err := st.fs.Glob(absPathPattern)
if err != nil {
return nil, fmt.Errorf("failed processing %s: %v", globPattern, err)
}
sort.Strings(matches)
result = append(result, matches...)
return result, nil
}
// normalizes relative path to absolute one
func (st *Storage) normalizePath(path string) string {
u, _ := url.Parse(path)
if u != nil && (u.Scheme != "" || filepath.IsAbs(path)) {
return path
} else {
return st.JoinBase(path)
}
}
// JoinBase returns an absolute path in the form basePath/relative
// Helm's setFiles command does not support unescaped filepath separators (\) on Windows.
// Instead, it requires double backslashes (\\) as filepath separators.
// See https://github.com/helm/helm/issues/9537
func (st *Storage) JoinBase(relPath string) string {
path := filepath.Join(st.basePath, relPath)
return path
}
func (st *Storage) normalizeSetFilePath(path, goos string) string {
normalizedPath := st.normalizePath(path)
if goos == "windows" {
return strings.ReplaceAll(normalizedPath, "\\", "\\\\")
}
return normalizedPath
}