|
|
@ -74,35 +74,17 @@ func (c ChartVersions) Less(a, b int) bool {
|
|
|
|
return i.LessThan(j)
|
|
|
|
return i.LessThan(j)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChartRepositoryIndex represents the index file in a chart repository
|
|
|
|
// IndexFile represents the index file in a chart repository
|
|
|
|
type ChartRepositoryIndex struct {
|
|
|
|
type IndexFile struct {
|
|
|
|
APIVersion string `json:"apiVersion"`
|
|
|
|
APIVersion string `json:"apiVersion"`
|
|
|
|
Generated time.Time `json:"generated"`
|
|
|
|
Generated time.Time `json:"generated"`
|
|
|
|
Entries map[string]ChartVersions `json:"entries"`
|
|
|
|
Entries map[string]ChartVersions `json:"entries"`
|
|
|
|
PublicKeys []string `json:"publicKeys,omitempty"`
|
|
|
|
PublicKeys []string `json:"publicKeys,omitempty"`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ChartVersion represents a chart entry in the ChartRepositoryIndex
|
|
|
|
// NewIndexFile initializes an index.
|
|
|
|
type ChartVersion struct {
|
|
|
|
func NewIndexFile() *IndexFile {
|
|
|
|
*chart.Metadata
|
|
|
|
return &IndexFile{
|
|
|
|
URLs []string `json:"urls"`
|
|
|
|
|
|
|
|
Created time.Time `json:"created,omitempty"`
|
|
|
|
|
|
|
|
Removed bool `json:"removed,omitempty"`
|
|
|
|
|
|
|
|
Digest string `json:"digest,omitempty"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// unversionedEntry represents a deprecated pre-Alpha.5 format.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This will be removed prior to v2.0.0
|
|
|
|
|
|
|
|
type unversionedEntry struct {
|
|
|
|
|
|
|
|
Checksum string `json:"checksum"`
|
|
|
|
|
|
|
|
URL string `json:"url"`
|
|
|
|
|
|
|
|
Chartfile *chart.Metadata `json:"chartfile"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NewChartRepositoryIndex initializes an index.
|
|
|
|
|
|
|
|
func NewChartRepositoryIndex() *ChartRepositoryIndex {
|
|
|
|
|
|
|
|
return &ChartRepositoryIndex{
|
|
|
|
|
|
|
|
APIVersion: APIVersionV1,
|
|
|
|
APIVersion: APIVersionV1,
|
|
|
|
Generated: time.Now(),
|
|
|
|
Generated: time.Now(),
|
|
|
|
Entries: map[string]ChartVersions{},
|
|
|
|
Entries: map[string]ChartVersions{},
|
|
|
@ -110,8 +92,8 @@ func NewChartRepositoryIndex() *ChartRepositoryIndex {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewChartRepositoryIndexFromFile takes a file at the given path and returns an ChartRepositoryIndex object
|
|
|
|
// LoadIndexFile takes a file at the given path and returns an IndexFile object
|
|
|
|
func NewChartRepositoryIndexFromFile(path string) (*ChartRepositoryIndex, error) {
|
|
|
|
func LoadIndexFile(path string) (*IndexFile, error) {
|
|
|
|
b, err := ioutil.ReadFile(path)
|
|
|
|
b, err := ioutil.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
@ -119,89 +101,9 @@ func NewChartRepositoryIndexFromFile(path string) (*ChartRepositoryIndex, error)
|
|
|
|
return loadIndex(b)
|
|
|
|
return loadIndex(b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewChartRepositoryIndexFromDirectory reads a (flat) directory and generates an index.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// It indexes only charts that have been packaged (*.tgz).
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// The index returned will be in an unsorted state
|
|
|
|
|
|
|
|
func NewChartRepositoryIndexFromDirectory(dir, baseURL string) (*ChartRepositoryIndex, error) {
|
|
|
|
|
|
|
|
archives, err := filepath.Glob(filepath.Join(dir, "*.tgz"))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
index := NewChartRepositoryIndex()
|
|
|
|
|
|
|
|
for _, arch := range archives {
|
|
|
|
|
|
|
|
fname := filepath.Base(arch)
|
|
|
|
|
|
|
|
c, err := chartutil.Load(arch)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
// Assume this is not a chart.
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := provenance.DigestFile(arch)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return index, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
index.Add(c.Metadata, fname, baseURL, hash)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return index, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loadIndex loads an index file and does minimal validity checking.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
|
|
|
|
|
|
|
|
func loadIndex(data []byte) (*ChartRepositoryIndex, error) {
|
|
|
|
|
|
|
|
i := &ChartRepositoryIndex{}
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(data, i); err != nil {
|
|
|
|
|
|
|
|
return i, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if i.APIVersion == "" {
|
|
|
|
|
|
|
|
// When we leave Beta, we should remove legacy support and just
|
|
|
|
|
|
|
|
// return this error:
|
|
|
|
|
|
|
|
//return i, ErrNoAPIVersion
|
|
|
|
|
|
|
|
return loadUnversionedIndex(data)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return i, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loadUnversionedIndex loads a pre-Alpha.5 index.yaml file.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This format is deprecated. This function will be removed prior to v2.0.0.
|
|
|
|
|
|
|
|
func loadUnversionedIndex(data []byte) (*ChartRepositoryIndex, error) {
|
|
|
|
|
|
|
|
fmt.Fprintln(os.Stderr, "WARNING: Deprecated index file format. Try 'helm repo update'")
|
|
|
|
|
|
|
|
i := map[string]unversionedEntry{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This gets around an error in the YAML parser. Instead of parsing as YAML,
|
|
|
|
|
|
|
|
// we convert to JSON, and then decode again.
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
data, err = yaml.YAMLToJSON(data)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &i); err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(i) == 0 {
|
|
|
|
|
|
|
|
return nil, ErrNoAPIVersion
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ni := NewChartRepositoryIndex()
|
|
|
|
|
|
|
|
for n, item := range i {
|
|
|
|
|
|
|
|
if item.Chartfile == nil || item.Chartfile.Name == "" {
|
|
|
|
|
|
|
|
parts := strings.Split(n, "-")
|
|
|
|
|
|
|
|
ver := ""
|
|
|
|
|
|
|
|
if len(parts) > 1 {
|
|
|
|
|
|
|
|
ver = strings.TrimSuffix(parts[1], ".tgz")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
item.Chartfile = &chart.Metadata{Name: parts[0], Version: ver}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ni.Add(item.Chartfile, item.URL, "", item.Checksum)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ni, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add adds a file to the index
|
|
|
|
// Add adds a file to the index
|
|
|
|
// This can leave the index in an unsorted state
|
|
|
|
// This can leave the index in an unsorted state
|
|
|
|
func (i ChartRepositoryIndex) Add(md *chart.Metadata, filename, baseURL, digest string) {
|
|
|
|
func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) {
|
|
|
|
u := filename
|
|
|
|
u := filename
|
|
|
|
if baseURL != "" {
|
|
|
|
if baseURL != "" {
|
|
|
|
var err error
|
|
|
|
var err error
|
|
|
@ -225,7 +127,7 @@ func (i ChartRepositoryIndex) Add(md *chart.Metadata, filename, baseURL, digest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Has returns true if the index has an entry for a chart with the given name and exact version.
|
|
|
|
// Has returns true if the index has an entry for a chart with the given name and exact version.
|
|
|
|
func (i ChartRepositoryIndex) Has(name, version string) bool {
|
|
|
|
func (i IndexFile) Has(name, version string) bool {
|
|
|
|
_, err := i.Get(name, version)
|
|
|
|
_, err := i.Get(name, version)
|
|
|
|
return err == nil
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -236,7 +138,7 @@ func (i ChartRepositoryIndex) Has(name, version string) bool {
|
|
|
|
// the most recent release for every version is in the 0th slot in the
|
|
|
|
// the most recent release for every version is in the 0th slot in the
|
|
|
|
// Entries.ChartVersions array. That way, tooling can predict the newest
|
|
|
|
// Entries.ChartVersions array. That way, tooling can predict the newest
|
|
|
|
// version without needing to parse SemVers.
|
|
|
|
// version without needing to parse SemVers.
|
|
|
|
func (i ChartRepositoryIndex) SortEntries() {
|
|
|
|
func (i IndexFile) SortEntries() {
|
|
|
|
for _, versions := range i.Entries {
|
|
|
|
for _, versions := range i.Entries {
|
|
|
|
sort.Sort(sort.Reverse(versions))
|
|
|
|
sort.Sort(sort.Reverse(versions))
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -245,7 +147,7 @@ func (i ChartRepositoryIndex) SortEntries() {
|
|
|
|
// Get returns the ChartVersion for the given name.
|
|
|
|
// Get returns the ChartVersion for the given name.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// If version is empty, this will return the chart with the highest version.
|
|
|
|
// If version is empty, this will return the chart with the highest version.
|
|
|
|
func (i ChartRepositoryIndex) Get(name, version string) (*ChartVersion, error) {
|
|
|
|
func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
|
|
|
|
vs, ok := i.Entries[name]
|
|
|
|
vs, ok := i.Entries[name]
|
|
|
|
if !ok {
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrNoChartName
|
|
|
|
return nil, ErrNoChartName
|
|
|
@ -268,7 +170,7 @@ func (i ChartRepositoryIndex) Get(name, version string) (*ChartVersion, error) {
|
|
|
|
// WriteFile writes an index file to the given destination path.
|
|
|
|
// WriteFile writes an index file to the given destination path.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// The mode on the file is set to 'mode'.
|
|
|
|
// The mode on the file is set to 'mode'.
|
|
|
|
func (i ChartRepositoryIndex) WriteFile(dest string, mode os.FileMode) error {
|
|
|
|
func (i IndexFile) WriteFile(dest string, mode os.FileMode) error {
|
|
|
|
b, err := yaml.Marshal(i)
|
|
|
|
b, err := yaml.Marshal(i)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
@ -284,7 +186,7 @@ func (i ChartRepositoryIndex) WriteFile(dest string, mode os.FileMode) error {
|
|
|
|
// In all other cases, the existing record is preserved.
|
|
|
|
// In all other cases, the existing record is preserved.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// This can leave the index in an unsorted state
|
|
|
|
// This can leave the index in an unsorted state
|
|
|
|
func (i *ChartRepositoryIndex) Merge(f *ChartRepositoryIndex) {
|
|
|
|
func (i *IndexFile) Merge(f *IndexFile) {
|
|
|
|
for _, cvs := range f.Entries {
|
|
|
|
for _, cvs := range f.Entries {
|
|
|
|
for _, cv := range cvs {
|
|
|
|
for _, cv := range cvs {
|
|
|
|
if !i.Has(cv.Name, cv.Version) {
|
|
|
|
if !i.Has(cv.Name, cv.Version) {
|
|
|
@ -294,3 +196,103 @@ func (i *ChartRepositoryIndex) Merge(f *ChartRepositoryIndex) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Need both JSON and YAML annotations until we get rid of gopkg.in/yaml.v2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ChartVersion represents a chart entry in the IndexFile
|
|
|
|
|
|
|
|
type ChartVersion struct {
|
|
|
|
|
|
|
|
*chart.Metadata
|
|
|
|
|
|
|
|
URLs []string `json:"urls"`
|
|
|
|
|
|
|
|
Created time.Time `json:"created,omitempty"`
|
|
|
|
|
|
|
|
Removed bool `json:"removed,omitempty"`
|
|
|
|
|
|
|
|
Digest string `json:"digest,omitempty"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IndexDirectory reads a (flat) directory and generates an index.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// It indexes only charts that have been packaged (*.tgz).
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// The index returned will be in an unsorted state
|
|
|
|
|
|
|
|
func IndexDirectory(dir, baseURL string) (*IndexFile, error) {
|
|
|
|
|
|
|
|
archives, err := filepath.Glob(filepath.Join(dir, "*.tgz"))
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
index := NewIndexFile()
|
|
|
|
|
|
|
|
for _, arch := range archives {
|
|
|
|
|
|
|
|
fname := filepath.Base(arch)
|
|
|
|
|
|
|
|
c, err := chartutil.Load(arch)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
// Assume this is not a chart.
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err := provenance.DigestFile(arch)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return index, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
index.Add(c.Metadata, fname, baseURL, hash)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return index, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loadIndex loads an index file and does minimal validity checking.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails.
|
|
|
|
|
|
|
|
func loadIndex(data []byte) (*IndexFile, error) {
|
|
|
|
|
|
|
|
i := &IndexFile{}
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(data, i); err != nil {
|
|
|
|
|
|
|
|
return i, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if i.APIVersion == "" {
|
|
|
|
|
|
|
|
// When we leave Beta, we should remove legacy support and just
|
|
|
|
|
|
|
|
// return this error:
|
|
|
|
|
|
|
|
//return i, ErrNoAPIVersion
|
|
|
|
|
|
|
|
return loadUnversionedIndex(data)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return i, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// unversionedEntry represents a deprecated pre-Alpha.5 format.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This will be removed prior to v2.0.0
|
|
|
|
|
|
|
|
type unversionedEntry struct {
|
|
|
|
|
|
|
|
Checksum string `json:"checksum"`
|
|
|
|
|
|
|
|
URL string `json:"url"`
|
|
|
|
|
|
|
|
Chartfile *chart.Metadata `json:"chartfile"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// loadUnversionedIndex loads a pre-Alpha.5 index.yaml file.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// This format is deprecated. This function will be removed prior to v2.0.0.
|
|
|
|
|
|
|
|
func loadUnversionedIndex(data []byte) (*IndexFile, error) {
|
|
|
|
|
|
|
|
fmt.Fprintln(os.Stderr, "WARNING: Deprecated index file format. Try 'helm repo update'")
|
|
|
|
|
|
|
|
i := map[string]unversionedEntry{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This gets around an error in the YAML parser. Instead of parsing as YAML,
|
|
|
|
|
|
|
|
// we convert to JSON, and then decode again.
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
data, err = yaml.YAMLToJSON(data)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &i); err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if len(i) == 0 {
|
|
|
|
|
|
|
|
return nil, ErrNoAPIVersion
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ni := NewIndexFile()
|
|
|
|
|
|
|
|
for n, item := range i {
|
|
|
|
|
|
|
|
if item.Chartfile == nil || item.Chartfile.Name == "" {
|
|
|
|
|
|
|
|
parts := strings.Split(n, "-")
|
|
|
|
|
|
|
|
ver := ""
|
|
|
|
|
|
|
|
if len(parts) > 1 {
|
|
|
|
|
|
|
|
ver = strings.TrimSuffix(parts[1], ".tgz")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
item.Chartfile = &chart.Metadata{Name: parts[0], Version: ver}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ni.Add(item.Chartfile, item.URL, "", item.Checksum)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ni, nil
|
|
|
|
|
|
|
|
}
|
|
|
|