Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"path/filepath"
"regexp"
"strings"
"time"
)

// APIVersionV1 is the API version number for version 1.
Expand Down Expand Up @@ -48,9 +49,13 @@ type Chart struct {
Values map[string]interface{} `json:"values"`
// Schema is an optional JSON schema for imposing structure on Values
Schema []byte `json:"schema"`
// SchemaModTime the chart was last modified
SchemaModTime time.Time `json:"schemamodtime,omitempty"`
// Files are miscellaneous files in a chart archive,
// e.g. README, LICENSE, etc.
Files []*File `json:"files"`
// ModTime the chart was last modified
ModTime time.Time `json:"modtime,omitempty"`

parent *Chart
dependencies []*Chart
Expand Down
4 changes: 4 additions & 0 deletions pkg/chart/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ limitations under the License.

package chart

import "time"

// File represents a file as a name/value pair.
//
// By convention, name is a relative path within the scope of the chart's
Expand All @@ -24,4 +26,6 @@ type File struct {
Name string `json:"name"`
// Data is the template as byte data.
Data []byte `json:"data"`
// ModTime is the file's mod-time
ModTime time.Time `json:"modtime,omitempty"`
}
2 changes: 1 addition & 1 deletion pkg/chart/loader/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {

data := bytes.TrimPrefix(b.Bytes(), utf8bom)

files = append(files, &BufferedFile{Name: n, Data: data})
files = append(files, &BufferedFile{Name: n, ModTime: hd.FileInfo().ModTime(), Data: data})
b.Reset()
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/chart/loader/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func LoadDir(dir string) (*chart.Chart, error) {

data = bytes.TrimPrefix(data, utf8bom)

files = append(files, &BufferedFile{Name: n, Data: data})
files = append(files, &BufferedFile{Name: n, ModTime: fi.ModTime(), Data: data})
return nil
}
if err = sympath.Walk(topdir, walk); err != nil {
Expand Down
37 changes: 28 additions & 9 deletions pkg/chart/loader/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/pkg/errors"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -64,19 +65,21 @@ func Load(name string) (*chart.Chart, error) {

// BufferedFile represents an archive file buffered for later processing.
type BufferedFile struct {
Name string
Data []byte
Name string
ModTime time.Time
Data []byte
}

// LoadFiles loads from in-memory files.
func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
c := new(chart.Chart)
subcharts := make(map[string][]*BufferedFile)
var subChartsKeys []string

// do not rely on assumed ordering of files in the chart and crash
// if Chart.yaml was not coming early enough to initialize metadata
for _, f := range files {
c.Raw = append(c.Raw, &chart.File{Name: f.Name, Data: f.Data})
c.Raw = append(c.Raw, &chart.File{Name: f.Name, ModTime: f.ModTime, Data: f.Data})
if f.Name == "Chart.yaml" {
if c.Metadata == nil {
c.Metadata = new(chart.Metadata)
Expand All @@ -90,6 +93,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
if c.Metadata.APIVersion == "" {
c.Metadata.APIVersion = chart.APIVersionV1
}
c.ModTime = f.ModTime
}
}
for _, f := range files {
Expand All @@ -109,6 +113,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
}
case f.Name == "values.schema.json":
c.Schema = f.Data
c.SchemaModTime = f.ModTime

// Deprecated: requirements.yaml is deprecated use Chart.yaml.
// We will handle it for you because we are nice people
Expand All @@ -135,22 +140,26 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
c.Metadata = new(chart.Metadata)
}
if c.Metadata.APIVersion == chart.APIVersionV1 {
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data, ModTime: f.ModTime})
}

case strings.HasPrefix(f.Name, "templates/"):
c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data})
c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data, ModTime: f.ModTime})
case strings.HasPrefix(f.Name, "charts/"):
if filepath.Ext(f.Name) == ".prov" {
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data, ModTime: f.ModTime})
continue
}

fname := strings.TrimPrefix(f.Name, "charts/")
cname := strings.SplitN(fname, "/", 2)[0]
subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data})
// map[string] is unsorted - keep an array to keep things sorted
if !stringInSlice(cname, subChartsKeys) {
subChartsKeys = append(subChartsKeys, cname)
}
subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data, ModTime: f.ModTime})
default:
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data, ModTime: f.ModTime})
}
}

Expand All @@ -162,7 +171,8 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
return c, err
}

for n, files := range subcharts {
for _, n := range subChartsKeys {
files := subcharts[n]
var sc *chart.Chart
var err error
switch {
Expand Down Expand Up @@ -198,3 +208,12 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {

return c, nil
}

func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
54 changes: 35 additions & 19 deletions pkg/chart/loader/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,9 @@ func TestLoadFiles_BadCases(t *testing.T) {
name: "These files contain only requirements.lock",
bufferedFiles: []*BufferedFile{
{
Name: "requirements.lock",
Data: []byte(""),
Name: "requirements.lock",
ModTime: time.Now(),
Data: []byte(""),
},
},
expectError: "validation: chart.metadata.apiVersion is required"},
Expand All @@ -233,6 +234,7 @@ func TestLoadFiles_BadCases(t *testing.T) {
}

func TestLoadFiles(t *testing.T) {
modTime := time.Now()
goodFiles := []*BufferedFile{
{
Name: "Chart.yaml",
Expand All @@ -254,22 +256,27 @@ sources:
home: http://example.com
icon: https://example.com/64x64.png
`),
ModTime: modTime,
},
{
Name: "values.yaml",
Data: []byte("var: some values"),
Name: "values.yaml",
ModTime: modTime,
Data: []byte("var: some values"),
},
{
Name: "values.schema.json",
Data: []byte("type: Values"),
Name: "values.schema.json",
ModTime: modTime,
Data: []byte("type: Values"),
},
{
Name: "templates/deployment.yaml",
Data: []byte("some deployment"),
Name: "templates/deployment.yaml",
ModTime: modTime,
Data: []byte("some deployment"),
},
{
Name: "templates/service.yaml",
Data: []byte("some service"),
Name: "templates/service.yaml",
ModTime: modTime,
Data: []byte("some service"),
},
}

Expand Down Expand Up @@ -298,6 +305,10 @@ icon: https://example.com/64x64.png
t.Errorf("Expected number of templates == 2, got %d", len(c.Templates))
}

if !c.ModTime.Equal(modTime) {
t.Errorf("Expected chart modtime to be %v got %v\n", modTime, c.ModTime)
}

if _, err = LoadFiles([]*BufferedFile{}); err == nil {
t.Fatal("Expected err to be non-nil")
}
Expand All @@ -311,24 +322,29 @@ icon: https://example.com/64x64.png
func TestLoadFilesOrder(t *testing.T) {
goodFiles := []*BufferedFile{
{
Name: "requirements.yaml",
Data: []byte("dependencies:"),
Name: "requirements.yaml",
ModTime: time.Now(),
Data: []byte("dependencies:"),
},
{
Name: "values.yaml",
Data: []byte("var: some values"),
Name: "values.yaml",
ModTime: time.Now(),
Data: []byte("var: some values"),
},

{
Name: "templates/deployment.yaml",
Data: []byte("some deployment"),
Name: "templates/deployment.yaml",
ModTime: time.Now(),
Data: []byte("some deployment"),
},
{
Name: "templates/service.yaml",
Data: []byte("some service"),
Name: "templates/service.yaml",
ModTime: time.Now(),
Data: []byte("some service"),
},
{
Name: "Chart.yaml",
Name: "Chart.yaml",
ModTime: time.Now(),
Data: []byte(`apiVersion: v1
name: frobnitz
description: This is a frobnitz.
Expand Down
17 changes: 9 additions & 8 deletions pkg/chartutil/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
if err != nil {
return err
}
if err := writeToTar(out, filepath.Join(base, ChartfileName), cdata); err != nil {
if err := writeToTar(out, filepath.Join(base, ChartfileName), cdata, c.ModTime); err != nil {
return err
}

Expand All @@ -177,7 +177,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
if err != nil {
return err
}
if err := writeToTar(out, filepath.Join(base, "Chart.lock"), ldata); err != nil {
if err := writeToTar(out, filepath.Join(base, "Chart.lock"), ldata, c.Lock.Generated); err != nil {
return err
}
}
Expand All @@ -186,7 +186,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
// Save values.yaml
for _, f := range c.Raw {
if f.Name == ValuesfileName {
if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data); err != nil {
if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data, f.ModTime); err != nil {
return err
}
}
Expand All @@ -197,23 +197,24 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
if !json.Valid(c.Schema) {
return errors.New("Invalid JSON in " + SchemafileName)
}
if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema); err != nil {
// TODO: read the modtime for the values.schema.json
if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema, c.SchemaModTime); err != nil {
return err
}
}

// Save templates
for _, f := range c.Templates {
n := filepath.Join(base, f.Name)
if err := writeToTar(out, n, f.Data); err != nil {
if err := writeToTar(out, n, f.Data, f.ModTime); err != nil {
return err
}
}

// Save files
for _, f := range c.Files {
n := filepath.Join(base, f.Name)
if err := writeToTar(out, n, f.Data); err != nil {
if err := writeToTar(out, n, f.Data, f.ModTime); err != nil {
return err
}
}
Expand All @@ -228,13 +229,13 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
}

// writeToTar writes a single file to a tar archive.
func writeToTar(out *tar.Writer, name string, body []byte) error {
func writeToTar(out *tar.Writer, name string, body []byte, modTime time.Time) error {
// TODO: Do we need to create dummy parent directory names if none exist?
h := &tar.Header{
Name: filepath.ToSlash(name),
Mode: 0644,
Size: int64(len(body)),
ModTime: time.Now(),
ModTime: modTime,
}
if err := out.WriteHeader(h); err != nil {
return err
Expand Down
Loading