diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index a3bed63a3..b5155d7c8 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -19,6 +19,7 @@ import ( "path/filepath" "regexp" "strings" + "time" ) // APIVersionV1 is the API version number for version 1. @@ -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 diff --git a/pkg/chart/file.go b/pkg/chart/file.go index 9dd7c08d5..2e3598804 100644 --- a/pkg/chart/file.go +++ b/pkg/chart/file.go @@ -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 @@ -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"` } diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go index 8b38cb89f..9f159b547 100644 --- a/pkg/chart/loader/archive.go +++ b/pkg/chart/loader/archive.go @@ -175,7 +175,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() } diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go index bbe543870..0c9e97306 100644 --- a/pkg/chart/loader/directory.go +++ b/pkg/chart/loader/directory.go @@ -109,7 +109,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 { diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index 7cc8878a8..d490ca774 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -22,6 +22,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/pkg/errors" "sigs.k8s.io/yaml" @@ -64,8 +65,9 @@ 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 + Data []byte + ModTime time.Time } // LoadFiles loads from in-memory files. @@ -76,7 +78,7 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { // 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) @@ -90,6 +92,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 { @@ -109,6 +112,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 diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 9605b3152..0d9f18704 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -217,8 +217,9 @@ func TestLoadFiles_BadCases(t *testing.T) { name: "These files contain only requirements.lock", bufferedFiles: []*BufferedFile{ { - Name: "requirements.lock", - Data: []byte(""), + Name: "requirements.lock", + Data: []byte(""), + ModTime: time.Now(), }, }, expectError: "validation: chart.metadata.apiVersion is required"}, @@ -255,22 +256,27 @@ sources: home: http://example.com icon: https://example.com/64x64.png `), + ModTime: time.Now(), }, { - Name: "values.yaml", - Data: []byte("var: some values"), + Name: "values.yaml", + Data: []byte("var: some values"), + ModTime: time.Now(), }, { - Name: "values.schema.json", - Data: []byte("type: Values"), + Name: "values.schema.json", + Data: []byte("type: Values"), + ModTime: time.Now(), }, { - Name: "templates/deployment.yaml", - Data: []byte("some deployment"), + Name: "templates/deployment.yaml", + Data: []byte("some deployment"), + ModTime: time.Now(), }, { - Name: "templates/service.yaml", - Data: []byte("some service"), + Name: "templates/service.yaml", + Data: []byte("some service"), + ModTime: time.Now(), }, } @@ -312,21 +318,25 @@ icon: https://example.com/64x64.png func TestLoadFilesOrder(t *testing.T) { goodFiles := []*BufferedFile{ { - Name: "requirements.yaml", - Data: []byte("dependencies:"), + Name: "requirements.yaml", + Data: []byte("dependencies:"), + ModTime: time.Now(), }, { - Name: "values.yaml", - Data: []byte("var: some values"), + Name: "values.yaml", + Data: []byte("var: some values"), + ModTime: time.Now(), }, { - Name: "templates/deployment.yaml", - Data: []byte("some deployment"), + Name: "templates/deployment.yaml", + Data: []byte("some deployment"), + ModTime: time.Now(), }, { - Name: "templates/service.yaml", - Data: []byte("some service"), + Name: "templates/service.yaml", + Data: []byte("some service"), + ModTime: time.Now(), }, { Name: "Chart.yaml", @@ -348,6 +358,7 @@ sources: home: http://example.com icon: https://example.com/64x64.png `), + ModTime: time.Now(), }, } diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index 2ce4eddaf..93e065254 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -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 } @@ -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 } } @@ -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 } } @@ -197,7 +197,8 @@ 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 } } @@ -205,7 +206,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { // 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 } } @@ -213,7 +214,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { // 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 } } @@ -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