github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/chartutil/save_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package chartutil 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "compress/gzip" 23 "io" 24 "os" 25 "path" 26 "path/filepath" 27 "regexp" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/stefanmcshane/helm/internal/test/ensure" 33 "github.com/stefanmcshane/helm/pkg/chart" 34 "github.com/stefanmcshane/helm/pkg/chart/loader" 35 ) 36 37 func TestSave(t *testing.T) { 38 tmp := ensure.TempDir(t) 39 defer os.RemoveAll(tmp) 40 41 for _, dest := range []string{tmp, path.Join(tmp, "newdir")} { 42 t.Run("outDir="+dest, func(t *testing.T) { 43 c := &chart.Chart{ 44 Metadata: &chart.Metadata{ 45 APIVersion: chart.APIVersionV1, 46 Name: "ahab", 47 Version: "1.2.3", 48 }, 49 Lock: &chart.Lock{ 50 Digest: "testdigest", 51 }, 52 Files: []*chart.File{ 53 {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, 54 }, 55 Schema: []byte("{\n \"title\": \"Values\"\n}"), 56 } 57 chartWithInvalidJSON := withSchema(*c, []byte("{")) 58 59 where, err := Save(c, dest) 60 if err != nil { 61 t.Fatalf("Failed to save: %s", err) 62 } 63 if !strings.HasPrefix(where, dest) { 64 t.Fatalf("Expected %q to start with %q", where, dest) 65 } 66 if !strings.HasSuffix(where, ".tgz") { 67 t.Fatalf("Expected %q to end with .tgz", where) 68 } 69 70 c2, err := loader.LoadFile(where) 71 if err != nil { 72 t.Fatal(err) 73 } 74 if c2.Name() != c.Name() { 75 t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) 76 } 77 if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { 78 t.Fatal("Files data did not match") 79 } 80 if c2.Lock != nil { 81 t.Fatal("Expected v1 chart archive not to contain Chart.lock file") 82 } 83 84 if !bytes.Equal(c.Schema, c2.Schema) { 85 indentation := 4 86 formattedExpected := Indent(indentation, string(c.Schema)) 87 formattedActual := Indent(indentation, string(c2.Schema)) 88 t.Fatalf("Schema data did not match.\nExpected:\n%s\nActual:\n%s", formattedExpected, formattedActual) 89 } 90 if _, err := Save(&chartWithInvalidJSON, dest); err == nil { 91 t.Fatalf("Invalid JSON was not caught while saving chart") 92 } 93 94 c.Metadata.APIVersion = chart.APIVersionV2 95 where, err = Save(c, dest) 96 if err != nil { 97 t.Fatalf("Failed to save: %s", err) 98 } 99 c2, err = loader.LoadFile(where) 100 if err != nil { 101 t.Fatal(err) 102 } 103 if c2.Lock == nil { 104 t.Fatal("Expected v2 chart archive to contain a Chart.lock file") 105 } 106 if c2.Lock.Digest != c.Lock.Digest { 107 t.Fatal("Chart.lock data did not match") 108 } 109 }) 110 } 111 } 112 113 // Creates a copy with a different schema; does not modify anything. 114 func withSchema(chart chart.Chart, schema []byte) chart.Chart { 115 chart.Schema = schema 116 return chart 117 } 118 119 func Indent(n int, text string) string { 120 startOfLine := regexp.MustCompile(`(?m)^`) 121 indentation := strings.Repeat(" ", n) 122 return startOfLine.ReplaceAllLiteralString(text, indentation) 123 } 124 125 func TestSavePreservesTimestamps(t *testing.T) { 126 // Test executes so quickly that if we don't subtract a second, the 127 // check will fail because `initialCreateTime` will be identical to the 128 // written timestamp for the files. 129 initialCreateTime := time.Now().Add(-1 * time.Second) 130 131 tmp := t.TempDir() 132 133 c := &chart.Chart{ 134 Metadata: &chart.Metadata{ 135 APIVersion: chart.APIVersionV1, 136 Name: "ahab", 137 Version: "1.2.3", 138 }, 139 Values: map[string]interface{}{ 140 "imageName": "testimage", 141 "imageId": 42, 142 }, 143 Files: []*chart.File{ 144 {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, 145 }, 146 Schema: []byte("{\n \"title\": \"Values\"\n}"), 147 } 148 149 where, err := Save(c, tmp) 150 if err != nil { 151 t.Fatalf("Failed to save: %s", err) 152 } 153 154 allHeaders, err := retrieveAllHeadersFromTar(where) 155 if err != nil { 156 t.Fatalf("Failed to parse tar: %v", err) 157 } 158 159 for _, header := range allHeaders { 160 if header.ModTime.Before(initialCreateTime) { 161 t.Fatalf("File timestamp not preserved: %v", header.ModTime) 162 } 163 } 164 } 165 166 // We could refactor `load.go` to use this `retrieveAllHeadersFromTar` function 167 // as well, so we are not duplicating components of the code which iterate 168 // through the tar. 169 func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) { 170 raw, err := os.Open(path) 171 if err != nil { 172 return nil, err 173 } 174 defer raw.Close() 175 176 unzipped, err := gzip.NewReader(raw) 177 if err != nil { 178 return nil, err 179 } 180 defer unzipped.Close() 181 182 tr := tar.NewReader(unzipped) 183 headers := []*tar.Header{} 184 for { 185 hd, err := tr.Next() 186 if err == io.EOF { 187 break 188 } 189 190 if err != nil { 191 return nil, err 192 } 193 194 headers = append(headers, hd) 195 } 196 197 return headers, nil 198 } 199 200 func TestSaveDir(t *testing.T) { 201 tmp := t.TempDir() 202 203 c := &chart.Chart{ 204 Metadata: &chart.Metadata{ 205 APIVersion: chart.APIVersionV1, 206 Name: "ahab", 207 Version: "1.2.3", 208 }, 209 Files: []*chart.File{ 210 {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, 211 }, 212 Templates: []*chart.File{ 213 {Name: filepath.Join(TemplatesDir, "nested", "dir", "thing.yaml"), Data: []byte("abc: {{ .Values.abc }}")}, 214 }, 215 } 216 217 if err := SaveDir(c, tmp); err != nil { 218 t.Fatalf("Failed to save: %s", err) 219 } 220 221 c2, err := loader.LoadDir(tmp + "/ahab") 222 if err != nil { 223 t.Fatal(err) 224 } 225 226 if c2.Name() != c.Name() { 227 t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) 228 } 229 230 if len(c2.Templates) != 1 || c2.Templates[0].Name != filepath.Join(TemplatesDir, "nested", "dir", "thing.yaml") { 231 t.Fatal("Templates data did not match") 232 } 233 234 if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { 235 t.Fatal("Files data did not match") 236 } 237 }