mirror of https://github.com/helm/helm
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
7.0 KiB
221 lines
7.0 KiB
/*
|
|
Copyright The Helm Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package action
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"helm.sh/helm/v4/pkg/kube"
|
|
releaseutil "helm.sh/helm/v4/pkg/release/v1/util"
|
|
)
|
|
|
|
// makeTestManifest creates a minimal Manifest for testing.
|
|
func makeTestManifest(name, sourcePath string, annotations map[string]string) releaseutil.Manifest {
|
|
content := "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: " + name + "\n"
|
|
if len(annotations) > 0 {
|
|
content += " annotations:\n"
|
|
for k, v := range annotations {
|
|
content += " " + k + ": \"" + v + "\"\n"
|
|
}
|
|
}
|
|
head := &releaseutil.SimpleHead{}
|
|
head.Metadata = &struct {
|
|
Name string `json:"name"`
|
|
Annotations map[string]string `json:"annotations"`
|
|
}{
|
|
Name: name,
|
|
Annotations: annotations,
|
|
}
|
|
return releaseutil.Manifest{
|
|
Name: sourcePath,
|
|
Content: content,
|
|
Head: head,
|
|
}
|
|
}
|
|
|
|
func TestGroupManifestsByDirectSubchart(t *testing.T) {
|
|
manifests := []releaseutil.Manifest{
|
|
makeTestManifest("cm-parent", "mychart/templates/cm.yaml", nil),
|
|
makeTestManifest("deploy-db", "mychart/charts/database/templates/deploy.yaml", nil),
|
|
makeTestManifest("svc-db", "mychart/charts/database/templates/svc.yaml", nil),
|
|
makeTestManifest("cm-app", "mychart/charts/app/templates/cm.yaml", nil),
|
|
// nested subchart — belongs to "database" at parent level
|
|
makeTestManifest("cm-nested", "mychart/charts/database/charts/cache/templates/cm.yaml", nil),
|
|
}
|
|
|
|
groups := GroupManifestsByDirectSubchart(manifests, "mychart")
|
|
|
|
// Parent chart
|
|
if len(groups[""]) != 1 {
|
|
t.Errorf("expected 1 parent manifest, got %d", len(groups[""]))
|
|
}
|
|
if groups[""][0].Name != "mychart/templates/cm.yaml" {
|
|
t.Errorf("unexpected parent manifest: %s", groups[""][0].Name)
|
|
}
|
|
|
|
// database subchart (including its nested subchart at this level)
|
|
if len(groups["database"]) != 3 {
|
|
t.Errorf("expected 3 database manifests (including nested), got %d: %v",
|
|
len(groups["database"]), manifestNames(groups["database"]))
|
|
}
|
|
|
|
// app subchart
|
|
if len(groups["app"]) != 1 {
|
|
t.Errorf("expected 1 app manifest, got %d", len(groups["app"]))
|
|
}
|
|
}
|
|
|
|
func TestGroupManifestsByDirectSubchart_EmptyChartName(t *testing.T) {
|
|
// Edge case: chart name is empty — everything goes to parent
|
|
manifests := []releaseutil.Manifest{
|
|
makeTestManifest("cm", "templates/cm.yaml", nil),
|
|
}
|
|
groups := GroupManifestsByDirectSubchart(manifests, "")
|
|
if len(groups[""]) != 1 {
|
|
t.Errorf("expected 1 parent manifest for empty chart name, got %d", len(groups[""]))
|
|
}
|
|
}
|
|
|
|
func TestGroupManifestsByDirectSubchart_OnlySubcharts(t *testing.T) {
|
|
manifests := []releaseutil.Manifest{
|
|
makeTestManifest("deploy", "chart/charts/sub1/templates/deploy.yaml", nil),
|
|
makeTestManifest("svc", "chart/charts/sub2/templates/svc.yaml", nil),
|
|
}
|
|
groups := GroupManifestsByDirectSubchart(manifests, "chart")
|
|
if len(groups[""]) != 0 {
|
|
t.Errorf("expected no parent manifests, got %d", len(groups[""]))
|
|
}
|
|
if len(groups["sub1"]) != 1 {
|
|
t.Errorf("expected 1 sub1 manifest, got %d", len(groups["sub1"]))
|
|
}
|
|
if len(groups["sub2"]) != 1 {
|
|
t.Errorf("expected 1 sub2 manifest, got %d", len(groups["sub2"]))
|
|
}
|
|
}
|
|
|
|
func TestBuildManifestYAML(t *testing.T) {
|
|
manifests := []releaseutil.Manifest{
|
|
makeTestManifest("cm1", "chart/templates/cm1.yaml", nil),
|
|
makeTestManifest("cm2", "chart/templates/cm2.yaml", nil),
|
|
}
|
|
result := buildManifestYAML(manifests)
|
|
if result == "" {
|
|
t.Error("expected non-empty YAML output")
|
|
}
|
|
// Should contain content from both manifests
|
|
if !contains(result, "name: cm1") {
|
|
t.Error("expected cm1 in output")
|
|
}
|
|
if !contains(result, "name: cm2") {
|
|
t.Error("expected cm2 in output")
|
|
}
|
|
}
|
|
|
|
func TestBuildManifestYAML_Empty(t *testing.T) {
|
|
result := buildManifestYAML(nil)
|
|
if result != "" {
|
|
t.Errorf("expected empty string for nil manifests, got %q", result)
|
|
}
|
|
}
|
|
|
|
// contains checks if s contains substr.
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsStr(s, substr))
|
|
}
|
|
|
|
func containsStr(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// TestInstallRelease_StoresSequencingInfo verifies that a sequenced install stores
|
|
// SequencingInfo in the release record.
|
|
func TestInstallRelease_StoresSequencingInfo(t *testing.T) {
|
|
config := actionConfigFixture(t)
|
|
instAction := NewInstall(config)
|
|
instAction.Namespace = "spaced"
|
|
instAction.ReleaseName = "seq-info-test"
|
|
instAction.WaitStrategy = kube.OrderedWaitStrategy
|
|
instAction.Timeout = 5 * time.Minute
|
|
instAction.ReadinessTimeout = time.Minute
|
|
|
|
ch := buildChart(withSampleTemplates())
|
|
reli, err := instAction.RunWithContext(context.Background(), ch, map[string]interface{}{})
|
|
if err != nil {
|
|
t.Fatalf("install failed: %v", err)
|
|
}
|
|
rel, err := releaserToV1Release(reli)
|
|
if err != nil {
|
|
t.Fatalf("type assertion failed: %v", err)
|
|
}
|
|
if rel.SequencingInfo == nil {
|
|
t.Fatal("expected SequencingInfo to be set after ordered install, got nil")
|
|
}
|
|
if !rel.SequencingInfo.Enabled {
|
|
t.Error("expected SequencingInfo.Enabled to be true")
|
|
}
|
|
}
|
|
|
|
// TestInstallRelease_OrderedWaitStrategy verifies that --wait=ordered installs
|
|
// succeed end-to-end using the fake kube client.
|
|
func TestInstallRelease_OrderedWaitStrategy(t *testing.T) {
|
|
config := actionConfigFixture(t)
|
|
instAction := NewInstall(config)
|
|
instAction.Namespace = "spaced"
|
|
instAction.ReleaseName = "seq-test"
|
|
instAction.WaitStrategy = kube.OrderedWaitStrategy
|
|
instAction.Timeout = 5 * time.Minute
|
|
instAction.ReadinessTimeout = time.Minute
|
|
|
|
ch := buildChart(withSampleTemplates())
|
|
_, err := instAction.RunWithContext(context.Background(), ch, map[string]interface{}{})
|
|
if err != nil {
|
|
t.Fatalf("ordered install failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestInstallRelease_OrderedWaitStrategy_NilChart ensures a nil chart doesn't panic.
|
|
func TestInstallRelease_ReadinessTimeoutValidation(t *testing.T) {
|
|
config := actionConfigFixture(t)
|
|
instAction := NewInstall(config)
|
|
instAction.Namespace = "spaced"
|
|
instAction.ReleaseName = "timeout-test"
|
|
instAction.WaitStrategy = kube.OrderedWaitStrategy
|
|
instAction.Timeout = 5
|
|
instAction.ReadinessTimeout = 10 // exceeds Timeout
|
|
|
|
ch := buildChart(withSampleTemplates())
|
|
_, err := instAction.RunWithContext(context.Background(), ch, map[string]interface{}{})
|
|
if err == nil {
|
|
t.Fatal("expected error for ReadinessTimeout > Timeout, got nil")
|
|
}
|
|
}
|
|
|
|
func manifestNames(ms []releaseutil.Manifest) []string {
|
|
names := make([]string, len(ms))
|
|
for i, m := range ms {
|
|
names[i] = m.Name
|
|
}
|
|
return names
|
|
}
|