148 lines
4.4 KiB
Go
148 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"slices"
|
|
"testing"
|
|
"time"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
|
dynamicfake "k8s.io/client-go/dynamic/fake"
|
|
)
|
|
|
|
func renderObjects(t *testing.T) ([]runtime.Object, []*unstructured.Unstructured) {
|
|
t.Helper()
|
|
path := os.Getenv("DELIVERY_RENDER")
|
|
if path == "" {
|
|
t.Skip("DELIVERY_RENDER is not set")
|
|
}
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
decoder := yamlutil.NewYAMLOrJSONDecoder(file, 4096)
|
|
var objects []runtime.Object
|
|
var templates []*unstructured.Unstructured
|
|
for {
|
|
object := &unstructured.Unstructured{}
|
|
if err := decoder.Decode(&object.Object); err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
switch object.GetKind() {
|
|
case "ConfigMap":
|
|
objects = append(objects, object)
|
|
case "WorkflowTemplate":
|
|
objects = append(objects, object)
|
|
templates = append(templates, object)
|
|
}
|
|
}
|
|
if len(templates) == 0 {
|
|
t.Fatal("render contains no WorkflowTemplates")
|
|
}
|
|
return objects, templates
|
|
}
|
|
|
|
func TestMailKeysGraph(t *testing.T) {
|
|
_, templates := renderObjects(t)
|
|
for _, template := range templates {
|
|
if template.GetName() != "mail-keys-build" {
|
|
continue
|
|
}
|
|
stages, _, err := unstructured.NestedSlice(template.Object, "spec", "templates")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, value := range stages {
|
|
stage := value.(map[string]any)
|
|
if stage["name"] != "delivery" {
|
|
continue
|
|
}
|
|
tasks, _, err := unstructured.NestedSlice(stage, "dag", "tasks")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := map[string][]string{
|
|
"source-pull": nil,
|
|
"cache-pull": nil,
|
|
"toolchain-pull": nil,
|
|
"build": {"cache-pull", "source-pull", "toolchain-pull"},
|
|
"image-publish": {"build"},
|
|
"cache-export": {"build"},
|
|
}
|
|
for _, value := range tasks {
|
|
task := value.(map[string]any)
|
|
name := task["name"].(string)
|
|
expected, found := want[name]
|
|
if !found {
|
|
t.Fatalf("unexpected or duplicate delivery task %q", name)
|
|
}
|
|
parents, _, err := unstructured.NestedStringSlice(task, "dependencies")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
slices.Sort(parents)
|
|
if !slices.Equal(parents, expected) || task["depends"] != nil {
|
|
t.Errorf("%s parents = %v (depends: %v), want %v", name, parents, task["depends"], expected)
|
|
}
|
|
delete(want, name)
|
|
}
|
|
if len(want) != 0 {
|
|
t.Fatalf("missing delivery tasks: %v", want)
|
|
}
|
|
return
|
|
}
|
|
t.Fatal("mail-keys-build has no delivery DAG")
|
|
}
|
|
t.Fatal("render contains no mail-keys-build")
|
|
}
|
|
|
|
func TestRenderedTemplates(t *testing.T) {
|
|
objects, templates := renderObjects(t)
|
|
for _, namespace := range []string{"project-delivery"} {
|
|
objects = append(objects, &unstructured.Unstructured{Object: map[string]any{
|
|
"apiVersion": "v1", "kind": "ConfigMap", "metadata": map[string]any{"name": "kube-root-ca.crt", "namespace": namespace},
|
|
"data": map[string]any{"ca.crt": "synthetic public CA"},
|
|
}})
|
|
}
|
|
client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{
|
|
workflowGVR: "WorkflowList", templateGVR: "WorkflowTemplateList", configMapGVR: "ConfigMapList",
|
|
}, objects...)
|
|
c := &controller{client: client, now: func() time.Time { return time.Date(2026, 10, 2, 0, 0, 0, 0, time.UTC) }}
|
|
for _, template := range templates {
|
|
t.Run(template.GetName(), func(t *testing.T) {
|
|
name, err := c.readyTemplate(context.Background(), template.GetNamespace(), template.GetName())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
copy, err := client.Resource(templateGVR).Namespace(template.GetNamespace()).Get(context.Background(), name, metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if copy.GetAnnotations()[templateKey] != template.GetName() {
|
|
t.Fatal("captured template lost its logical reference")
|
|
}
|
|
bundle, err := c.captureBundle(context.Background(), template)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, config := range bundle.configs {
|
|
captured, err := client.Resource(configMapGVR).Namespace(template.GetNamespace()).Get(context.Background(), config.GetName(), metav1.GetOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if captured.Object["immutable"] != true {
|
|
t.Fatalf("action ConfigMap %q is mutable", config.GetName())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|