github.com/koderover/helm@v2.17.0+incompatible/pkg/chartutil/load_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 "compress/gzip" 22 "io/ioutil" 23 "os" 24 "path" 25 "path/filepath" 26 "runtime" 27 "strings" 28 "testing" 29 "time" 30 31 "k8s.io/helm/pkg/proto/hapi/chart" 32 ) 33 34 func TestLoadDir(t *testing.T) { 35 c, err := Load("testdata/frobnitz") 36 if err != nil { 37 t.Fatalf("Failed to load testdata: %s", err) 38 } 39 verifyFrobnitz(t, c) 40 verifyChart(t, c) 41 verifyRequirements(t, c) 42 } 43 44 func TestLoadNonV1Chart(t *testing.T) { 45 _, err := Load("testdata/frobnitz.v2") 46 if err != nil { 47 if strings.Compare(err.Error(), "apiVersion 'v2' is not valid. The value must be \"v1\"") != 0 { 48 t.Errorf("Unexpected message: %s", err) 49 } 50 return 51 } 52 t.Fatalf("chart with v2 apiVersion should not load") 53 } 54 55 func TestLoadDirWithSymlinks(t *testing.T) { 56 sym := filepath.Join("..", "frobnitz", "README.md") 57 link := filepath.Join("testdata", "frobnitz_symlinks", "README.md") 58 59 if err := os.Symlink(sym, link); err != nil { 60 t.Fatal(err) 61 } 62 63 defer os.Remove(link) 64 65 c, err := Load("testdata/frobnitz_symlinks") 66 if err != nil { 67 t.Fatalf("Failed to load testdata: %s", err) 68 } 69 verifyFrobnitz(t, c) 70 verifyChart(t, c) 71 verifyRequirements(t, c) 72 } 73 74 func TestLoadDirWithBadSymlinks(t *testing.T) { 75 if runtime.GOOS == "windows" { 76 t.Skip("test only works on unix systems with /dev/null present") 77 } 78 79 _, err := Load("testdata/bad_symlink") 80 if err == nil { 81 t.Fatal("Failed to detect bad symlink") 82 } 83 84 if !strings.HasPrefix(err.Error(), "cannot load irregular file") { 85 t.Errorf("Expected bad symlink error got %q", err) 86 } 87 } 88 89 func TestLoadFile(t *testing.T) { 90 c, err := Load("testdata/frobnitz-1.2.3.tgz") 91 if err != nil { 92 t.Fatalf("Failed to load testdata: %s", err) 93 } 94 verifyFrobnitz(t, c) 95 verifyChart(t, c) 96 verifyRequirements(t, c) 97 } 98 99 func TestLoadArchive_InvalidArchive(t *testing.T) { 100 tmpdir, err := ioutil.TempDir("", "helm-test-") 101 if err != nil { 102 t.Fatal(err) 103 } 104 defer os.Remove(tmpdir) 105 106 writeTar := func(filename, internalPath string, body []byte) { 107 dest, err := os.Create(filename) 108 if err != nil { 109 t.Fatal(err) 110 } 111 zipper := gzip.NewWriter(dest) 112 tw := tar.NewWriter(zipper) 113 114 h := &tar.Header{ 115 Name: internalPath, 116 Mode: 0755, 117 Size: int64(len(body)), 118 ModTime: time.Now(), 119 } 120 if err := tw.WriteHeader(h); err != nil { 121 t.Fatal(err) 122 } 123 if _, err := tw.Write(body); err != nil { 124 t.Fatal(err) 125 } 126 tw.Close() 127 zipper.Close() 128 dest.Close() 129 } 130 131 for _, tt := range []struct { 132 chartname string 133 internal string 134 expectError string 135 }{ 136 {"illegal-dots.tgz", "../../malformed-helm-test", "chart illegally references parent directory"}, 137 {"illegal-dots2.tgz", "/foo/../../malformed-helm-test", "chart illegally references parent directory"}, 138 {"illegal-dots3.tgz", "/../../malformed-helm-test", "chart illegally references parent directory"}, 139 {"illegal-dots4.tgz", "./../../malformed-helm-test", "chart illegally references parent directory"}, 140 {"illegal-name.tgz", "./.", "chart illegally contains content outside the base directory: \"./.\""}, 141 {"illegal-name2.tgz", "/./.", "chart illegally contains content outside the base directory: \"/./.\""}, 142 {"illegal-name3.tgz", "missing-leading-slash", "chart illegally contains content outside the base directory: \"missing-leading-slash\""}, 143 {"illegal-name5.tgz", "content-outside-base-dir", "chart illegally contains content outside the base directory: \"content-outside-base-dir\""}, 144 {"illegal-name4.tgz", "/missing-leading-slash", "chart metadata (Chart.yaml) missing"}, 145 {"illegal-abspath.tgz", "//foo", "chart illegally contains absolute paths"}, 146 {"illegal-abspath2.tgz", "///foo", "chart illegally contains absolute paths"}, 147 {"illegal-abspath3.tgz", "\\\\foo", "chart illegally contains absolute paths"}, 148 {"illegal-abspath3.tgz", "\\..\\..\\foo", "chart illegally references parent directory"}, 149 150 // Under special circumstances, this can get normalized to things that look like absolute Windows paths 151 {"illegal-abspath4.tgz", "\\.\\c:\\\\foo", "chart contains illegally named files"}, 152 {"illegal-abspath5.tgz", "/./c://foo", "chart contains illegally named files"}, 153 {"illegal-abspath6.tgz", "\\\\?\\Some\\windows\\magic", "chart illegally contains absolute paths"}, 154 } { 155 illegalChart := filepath.Join(tmpdir, tt.chartname) 156 writeTar(illegalChart, tt.internal, []byte("hello: world")) 157 _, err = Load(illegalChart) 158 if err == nil { 159 t.Fatal("expected error when unpacking illegal files") 160 } 161 if err.Error() != tt.expectError { 162 t.Errorf("Expected %q, got %q for %s", tt.expectError, err.Error(), tt.chartname) 163 } 164 } 165 166 // Make sure that absolute path gets interpreted as relative 167 illegalChart := filepath.Join(tmpdir, "abs-path.tgz") 168 writeTar(illegalChart, "/Chart.yaml", []byte("hello: world")) 169 _, err = Load(illegalChart) 170 if err.Error() != "invalid chart (Chart.yaml): name must not be empty" { 171 t.Error(err) 172 } 173 174 // And just to validate that the above was not spurious 175 illegalChart = filepath.Join(tmpdir, "abs-path2.tgz") 176 writeTar(illegalChart, "files/whatever.yaml", []byte("hello: world")) 177 _, err = Load(illegalChart) 178 if err.Error() != "chart metadata (Chart.yaml) missing" { 179 t.Error(err) 180 } 181 182 // Finally, test that drive letter gets stripped off on Windows 183 illegalChart = filepath.Join(tmpdir, "abs-winpath.tgz") 184 writeTar(illegalChart, "c:\\Chart.yaml", []byte("hello: world")) 185 _, err = Load(illegalChart) 186 if err.Error() != "invalid chart (Chart.yaml): name must not be empty" { 187 t.Error(err) 188 } 189 } 190 191 func TestLoadFiles(t *testing.T) { 192 goodFiles := []*BufferedFile{ 193 { 194 Name: ChartfileName, 195 Data: []byte(`apiVersion: v1 196 name: frobnitz 197 description: This is a frobnitz. 198 version: "1.2.3" 199 keywords: 200 - frobnitz 201 - sprocket 202 - dodad 203 maintainers: 204 - name: The Helm Team 205 email: helm@example.com 206 - name: Someone Else 207 email: nobody@example.com 208 sources: 209 - https://example.com/foo/bar 210 home: http://example.com 211 icon: https://example.com/64x64.png 212 `), 213 }, 214 { 215 Name: ValuesfileName, 216 Data: []byte(defaultValues), 217 }, 218 { 219 Name: path.Join("templates", DeploymentName), 220 Data: []byte(defaultDeployment), 221 }, 222 { 223 Name: path.Join("templates", ServiceName), 224 Data: []byte(defaultService), 225 }, 226 } 227 228 c, err := LoadFiles(goodFiles) 229 if err != nil { 230 t.Errorf("Expected good files to be loaded, got %v", err) 231 } 232 233 if c.Metadata.Name != "frobnitz" { 234 t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Metadata.Name) 235 } 236 237 if c.Values.Raw != defaultValues { 238 t.Error("Expected chart values to be populated with default values") 239 } 240 241 if len(c.Templates) != 2 { 242 t.Errorf("Expected number of templates == 2, got %d", len(c.Templates)) 243 } 244 245 c, err = LoadFiles([]*BufferedFile{}) 246 if err == nil { 247 t.Fatal("Expected err to be non-nil") 248 } 249 if err.Error() != "chart metadata (Chart.yaml) missing" { 250 t.Errorf("Expected chart metadata missing error, got '%s'", err.Error()) 251 } 252 253 // legacy check 254 c, err = LoadFiles([]*BufferedFile{ 255 { 256 Name: "values.toml", 257 Data: []byte{}, 258 }, 259 }) 260 if err == nil { 261 t.Fatal("Expected err to be non-nil") 262 } 263 if err.Error() != "values.toml is illegal as of 2.0.0-alpha.2" { 264 t.Errorf("Expected values.toml to be illegal, got '%s'", err.Error()) 265 } 266 } 267 268 // Packaging the chart on a Windows machine will produce an 269 // archive that has \\ as delimiters. Test that we support these archives 270 func TestLoadFileBackslash(t *testing.T) { 271 c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz") 272 if err != nil { 273 t.Fatalf("Failed to load testdata: %s", err) 274 } 275 verifyChartFileAndTemplate(t, c, "frobnitz_backslash") 276 verifyChart(t, c) 277 verifyRequirements(t, c) 278 } 279 280 func verifyChart(t *testing.T, c *chart.Chart) { 281 if c.Metadata.Name == "" { 282 t.Fatalf("No chart metadata found on %v", c) 283 } 284 t.Logf("Verifying chart %s", c.Metadata.Name) 285 if len(c.Templates) != 1 { 286 t.Errorf("Expected 1 template, got %d", len(c.Templates)) 287 } 288 289 numfiles := 8 290 if len(c.Files) != numfiles { 291 t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files)) 292 for _, n := range c.Files { 293 t.Logf("\t%s", n.TypeUrl) 294 } 295 } 296 297 if len(c.Dependencies) != 2 { 298 t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies), c.Dependencies) 299 for _, d := range c.Dependencies { 300 t.Logf("\tSubchart: %s\n", d.Metadata.Name) 301 } 302 } 303 304 expect := map[string]map[string]string{ 305 "alpine": { 306 "version": "0.1.0", 307 }, 308 "mariner": { 309 "version": "4.3.2", 310 }, 311 } 312 313 for _, dep := range c.Dependencies { 314 if dep.Metadata == nil { 315 t.Fatalf("expected metadata on dependency: %v", dep) 316 } 317 exp, ok := expect[dep.Metadata.Name] 318 if !ok { 319 t.Fatalf("Unknown dependency %s", dep.Metadata.Name) 320 } 321 if exp["version"] != dep.Metadata.Version { 322 t.Errorf("Expected %s version %s, got %s", dep.Metadata.Name, exp["version"], dep.Metadata.Version) 323 } 324 } 325 326 } 327 328 func verifyRequirements(t *testing.T, c *chart.Chart) { 329 r, err := LoadRequirements(c) 330 if err != nil { 331 t.Fatal(err) 332 } 333 if len(r.Dependencies) != 2 { 334 t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies)) 335 } 336 tests := []*Dependency{ 337 {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, 338 {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, 339 } 340 for i, tt := range tests { 341 d := r.Dependencies[i] 342 if d.Name != tt.Name { 343 t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) 344 } 345 if d.Version != tt.Version { 346 t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) 347 } 348 if d.Repository != tt.Repository { 349 t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) 350 } 351 } 352 } 353 func verifyRequirementsLock(t *testing.T, c *chart.Chart) { 354 r, err := LoadRequirementsLock(c) 355 if err != nil { 356 t.Fatal(err) 357 } 358 if len(r.Dependencies) != 2 { 359 t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies)) 360 } 361 tests := []*Dependency{ 362 {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, 363 {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, 364 } 365 for i, tt := range tests { 366 d := r.Dependencies[i] 367 if d.Name != tt.Name { 368 t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) 369 } 370 if d.Version != tt.Version { 371 t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) 372 } 373 if d.Repository != tt.Repository { 374 t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) 375 } 376 } 377 } 378 379 func verifyFrobnitz(t *testing.T, c *chart.Chart) { 380 verifyChartFileAndTemplate(t, c, "frobnitz") 381 } 382 383 func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) { 384 385 verifyChartfile(t, c.Metadata, name) 386 387 if len(c.Templates) != 1 { 388 t.Fatalf("Expected 1 template, got %d", len(c.Templates)) 389 } 390 391 if c.Templates[0].Name != "templates/template.tpl" { 392 t.Errorf("Unexpected template: %s", c.Templates[0].Name) 393 } 394 395 if len(c.Templates[0].Data) == 0 { 396 t.Error("No template data.") 397 } 398 }