internal/flux/latest_test.go
Rama main · réplica de lectura del repositorio original.
package flux
import (
"context"
"testing"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic/fake"
"github.com/sophotechlabs/spinoza/internal/api"
"github.com/sophotechlabs/spinoza/internal/charts"
"github.com/sophotechlabs/spinoza/internal/discovery"
)
type stubCharts struct {
versions map[string]string
warmed []string
}
func (s *stubCharts) Latest(repo charts.Repo, chart string) string {
return s.versions[repo.URL+"|"+chart]
}
func (s *stubCharts) Warm(repo charts.Repo, chart string) {
s.warmed = append(s.warmed, repo.URL+"|"+chart)
}
var (
releaseGVR = schema.GroupVersionResource{
Group: "helm.toolkit.fluxcd.io",
Version: "v2",
Resource: "helmreleases",
}
repoGVR = schema.GroupVersionResource{
Group: "source.toolkit.fluxcd.io",
Version: "v1",
Resource: "helmrepositories",
}
)
func latestDescs() map[string]api.ResourceDescriptor {
return map[string]api.ResourceDescriptor{
discovery.Key("helm.toolkit.fluxcd.io", "v2", "helmreleases"): {
Group: "helm.toolkit.fluxcd.io", Version: "v2", Resource: "helmreleases",
Kind: "HelmRelease", Namespaced: true,
},
discovery.Key("source.toolkit.fluxcd.io", "v1", "helmrepositories"): {
Group: "source.toolkit.fluxcd.io", Version: "v1", Resource: "helmrepositories",
Kind: "HelmRepository", Namespaced: true,
},
}
}
func newRepo(name, url, repoType string) *unstructured.Unstructured {
spec := map[string]any{"url": url}
if repoType != "" {
spec["type"] = repoType
}
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "source.toolkit.fluxcd.io/v1",
"kind": "HelmRepository",
"metadata": map[string]any{"name": name, "namespace": "flux-system"},
"spec": spec,
}}
}
func newRelease(namespace, name, chart, sourceName, sourceNs, revision string) *unstructured.Unstructured {
sourceRef := map[string]any{"kind": "HelmRepository", "name": sourceName}
if sourceNs != "" {
sourceRef["namespace"] = sourceNs
}
return &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "helm.toolkit.fluxcd.io/v2",
"kind": "HelmRelease",
"metadata": map[string]any{"name": name, "namespace": namespace},
"spec": map[string]any{
"chart": map[string]any{
"spec": map[string]any{"chart": chart, "sourceRef": sourceRef},
},
},
"status": map[string]any{
"history": []any{
map[string]any{"chartVersion": revision},
},
},
}}
}
func latestClient(objs ...runtime.Object) *fake.FakeDynamicClient {
kinds := map[schema.GroupVersionResource]string{
releaseGVR: "HelmReleaseList",
repoGVR: "HelmRepositoryList",
}
return fake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), kinds, objs...)
}
func releaseRow(t *testing.T, dash api.FluxDashboard, name string) api.FluxResource {
t.Helper()
for _, group := range dash.Groups {
if group.Name != "Helm Releases" {
continue
}
for _, resource := range group.Resources {
if resource.Name == name {
return resource
}
}
}
t.Fatalf("release %q not found in %+v", name, dash.Groups)
return api.FluxResource{}
}
func TestBuildMarksAnOutdatedRelease(t *testing.T) {
client := latestClient(
newRepo("podinfo", "https://example.test/charts", ""),
newRelease("apps", "podinfo", "podinfo", "podinfo", "flux-system", "6.14.0"),
)
index := &stubCharts{versions: map[string]string{"https://example.test/charts|podinfo": "6.15.1"}}
dash := Build(context.Background(), listerFor(client), latestDescs(), index)
row := releaseRow(t, dash, "podinfo")
if row.Latest != "6.15.1" {
t.Fatalf("latest = %q, want 6.15.1", row.Latest)
}
if !row.Outdated {
t.Fatalf("outdated = false, want true")
}
if len(index.warmed) != 1 || index.warmed[0] != "https://example.test/charts|podinfo" {
t.Fatalf("warmed = %v", index.warmed)
}
}
func TestBuildLeavesACurrentReleaseUnmarked(t *testing.T) {
client := latestClient(
newRepo("podinfo", "https://example.test/charts", ""),
newRelease("apps", "podinfo", "podinfo", "podinfo", "flux-system", "6.15.1"),
)
index := &stubCharts{versions: map[string]string{"https://example.test/charts|podinfo": "6.15.1"}}
row := releaseRow(t, Build(context.Background(), listerFor(client), latestDescs(), index), "podinfo")
if row.Latest != "6.15.1" {
t.Fatalf("latest = %q", row.Latest)
}
if row.Outdated {
t.Fatalf("outdated = true for an up-to-date release")
}
}
func TestBuildDefaultsTheSourceNamespaceToTheRelease(t *testing.T) {
client := latestClient(
newRepo("podinfo", "https://example.test/charts", ""),
newRelease("flux-system", "podinfo", "podinfo", "podinfo", "", "6.14.0"),
)
index := &stubCharts{versions: map[string]string{"https://example.test/charts|podinfo": "6.15.1"}}
row := releaseRow(t, Build(context.Background(), listerFor(client), latestDescs(), index), "podinfo")
if row.Latest != "6.15.1" {
t.Fatalf("latest = %q, want the same-namespace source to resolve", row.Latest)
}
}
func TestBuildFlagsOCIRepositories(t *testing.T) {
client := latestClient(
newRepo("keycloak", "oci://registry.test/team", "oci"),
newRelease("keycloak", "keycloak", "keycloak", "keycloak", "flux-system", "0.21.14"),
)
index := &stubCharts{versions: map[string]string{"oci://registry.test/team|keycloak": "0.22.0"}}
dash := Build(context.Background(), listerFor(client), latestDescs(), index)
if !releaseRow(t, dash, "keycloak").Outdated {
t.Fatalf("expected the oci-sourced release to be marked outdated")
}
}
func TestBuildSkipsReleasesWithoutAResolvableSource(t *testing.T) {
cases := map[string]*unstructured.Unstructured{
"unknown repository": newRelease("apps", "a", "podinfo", "missing", "flux-system", "1.0.0"),
"no chart name": newRelease("apps", "b", "", "podinfo", "flux-system", "1.0.0"),
"no source name": newRelease("apps", "c", "podinfo", "", "flux-system", "1.0.0"),
}
for name, release := range cases {
t.Run(name, func(t *testing.T) {
client := latestClient(newRepo("podinfo", "https://example.test/charts", ""), release)
index := &stubCharts{versions: map[string]string{"https://example.test/charts|podinfo": "9.9.9"}}
dash := Build(context.Background(), listerFor(client), latestDescs(), index)
if releaseRow(t, dash, release.GetName()).Latest != "" {
t.Fatalf("expected no latest version for %s", name)
}
})
}
}
func TestBuildSkipsANonHelmRepositorySource(t *testing.T) {
release := newRelease("apps", "chart-from-git", "podinfo", "flux-system", "flux-system", "1.0.0")
if err := unstructured.SetNestedField(release.Object, "GitRepository", "spec", "chart", "spec", "sourceRef", "kind"); err != nil {
t.Fatalf("seed: %v", err)
}
client := latestClient(newRepo("flux-system", "https://example.test/charts", ""), release)
index := &stubCharts{versions: map[string]string{"https://example.test/charts|podinfo": "9.9.9"}}
dash := Build(context.Background(), listerFor(client), latestDescs(), index)
if releaseRow(t, dash, "chart-from-git").Latest != "" {
t.Fatalf("a git chart source must not get a latest version")
}
}
func TestBuildSkipsARepositoryWithoutAURL(t *testing.T) {
client := latestClient(
newRepo("podinfo", "", ""),
newRelease("apps", "podinfo", "podinfo", "podinfo", "flux-system", "6.14.0"),
)
index := &stubCharts{versions: map[string]string{"|podinfo": "6.15.1"}}
if releaseRow(t, Build(context.Background(), listerFor(client), latestDescs(), index), "podinfo").Latest != "" {
t.Fatalf("a repository without a url must be skipped")
}
}
func TestBuildSkipsARepositoryAimedAtThisMachine(t *testing.T) {
cases := []string{
"http://169.254.169.254/latest/meta-data",
"http://127.0.0.1:9090/charts",
"file:///etc/passwd",
}
for _, repoURL := range cases {
t.Run(repoURL, func(t *testing.T) {
client := latestClient(
newRepo("podinfo", repoURL, ""),
newRelease("apps", "podinfo", "podinfo", "podinfo", "flux-system", "6.14.0"),
)
index := &stubCharts{versions: map[string]string{repoURL + "|podinfo": "6.15.1"}}
row := releaseRow(t, Build(context.Background(), listerFor(client), latestDescs(), index), "podinfo")
if row.Latest != "" {
t.Fatalf("latest = %q, want a repository the cluster aimed at us skipped", row.Latest)
}
if len(index.warmed) != 0 {
t.Fatalf("warmed = %v, want no fetch at all", index.warmed)
}
})
}
}
func TestBuildWithoutAChartIndex(t *testing.T) {
client := latestClient(
newRepo("podinfo", "https://example.test/charts", ""),
newRelease("apps", "podinfo", "podinfo", "podinfo", "flux-system", "6.14.0"),
)
row := releaseRow(t, Build(context.Background(), listerFor(client), latestDescs(), nil), "podinfo")
if row.Latest != "" {
t.Fatalf("latest = %q, want empty when no index is configured", row.Latest)
}
}
func TestRepositoryIndexSkipsAListThatFailed(t *testing.T) {
got := repoIndex(t.Context(), failingLister{}, latestDescs())
if len(got) != 0 {
t.Fatalf("repositories = %v, want none from a failed list", got)
}
}
func TestLatestVersionToleratesARowWithoutItsObject(t *testing.T) {
rows := map[string][]api.FluxResource{
"Helm Releases": {{Name: "podinfo", Revision: "6.14.0"}},
}
index := &stubCharts{versions: map[string]string{}}
applyLatest(rows, map[string][]*unstructured.Unstructured{}, nil, index)
if rows["Helm Releases"][0].Latest != "" {
t.Fatalf("row = %+v, want no version without its source object", rows["Helm Releases"][0])
}
if len(index.warmed) != 0 {
t.Fatalf("warmed = %v, want no repository fetch", index.warmed)
}
}