pkg/k8score/workload_revision_identity_test.go
Rama main · réplica de lectura del repositorio original.
package k8score
import (
"testing"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
)
func TestRevisionBuildersExposePodIdentity(t *testing.T) {
deploymentRevision := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"name": "api-rev-2",
"annotations": map[string]any{"deployment.kubernetes.io/revision": "2"},
"labels": map[string]any{"pod-template-hash": "hash-2"},
},
"spec": map[string]any{"replicas": int64(1)},
}}
deploymentRevision.SetOwnerReferences([]metav1.OwnerReference{{UID: types.UID("deployment-uid")}})
gotDeployment := BuildDeploymentRevisions([]unstructured.Unstructured{deploymentRevision}, "deployment-uid")
if len(gotDeployment) != 1 || gotDeployment[0].PodHash != "hash-2" {
t.Fatalf("deployment revision identity = %#v", gotDeployment)
}
controllerRevision := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"name": "daemon-rev-3",
"labels": map[string]any{"controller-revision-hash": "hash-3"},
},
"revision": int64(3),
}}
controllerRevision.SetOwnerReferences([]metav1.OwnerReference{{Kind: "DaemonSet", UID: types.UID("daemon-uid")}})
gotController := BuildControllerRevisions([]unstructured.Unstructured{controllerRevision}, "daemon-uid")
if len(gotController) != 1 || gotController[0].PodHash != "hash-3" {
t.Fatalf("controller revision identity = %#v", gotController)
}
statefulRevision := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"name": "database-hash-4",
"labels": map[string]any{"controller.kubernetes.io/hash": "hash-4"},
},
"revision": int64(4),
}}
statefulRevision.SetOwnerReferences([]metav1.OwnerReference{{Kind: "StatefulSet", UID: types.UID("stateful-uid")}})
gotStateful := BuildControllerRevisions([]unstructured.Unstructured{statefulRevision}, "stateful-uid")
if len(gotStateful) != 1 || gotStateful[0].PodHash != "database-hash-4" {
t.Fatalf("stateful revision identity = %#v", gotStateful)
}
}
func TestCurrentDeploymentRevisionPodHashes(t *testing.T) {
replicaSets := []*appsv1.ReplicaSet{
{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"deployment.kubernetes.io/revision": "1"},
Labels: map[string]string{appsv1.DefaultDeploymentUniqueLabelKey: "old"},
OwnerReferences: []metav1.OwnerReference{{Kind: "Deployment", UID: types.UID("api")}},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"deployment.kubernetes.io/revision": "2"},
Labels: map[string]string{appsv1.DefaultDeploymentUniqueLabelKey: "new"},
OwnerReferences: []metav1.OwnerReference{{Kind: "Deployment", UID: types.UID("api")}},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{"deployment.kubernetes.io/revision": "not-a-number"},
Labels: map[string]string{appsv1.DefaultDeploymentUniqueLabelKey: "ignored"},
OwnerReferences: []metav1.OwnerReference{{Kind: "Deployment", UID: types.UID("api")}},
},
},
}
got := CurrentDeploymentRevisionPodHashes(replicaSets)
if got[types.UID("api")] != "new" {
t.Fatalf("current Deployment pod hash = %q, want new", got[types.UID("api")])
}
}
func TestCurrentControllerRevisionPodHashes(t *testing.T) {
revisions := []unstructured.Unstructured{
{Object: map[string]any{
"metadata": map[string]any{"labels": map[string]any{appsv1.ControllerRevisionHashLabelKey: "old"}},
"revision": int64(1),
}},
{Object: map[string]any{
"metadata": map[string]any{"labels": map[string]any{appsv1.ControllerRevisionHashLabelKey: "new"}},
"revision": int64(2),
}},
}
for i := range revisions {
revisions[i].SetOwnerReferences([]metav1.OwnerReference{{Kind: "DaemonSet", UID: types.UID("agent")}})
}
got := CurrentControllerRevisionPodHashes(revisions)
if got[types.UID("agent")] != "new" {
t.Fatalf("current ControllerRevision pod hash = %q, want new", got[types.UID("agent")])
}
}
func TestBuildDeploymentRevisionsReportReadiness(t *testing.T) {
replicaSet := func(revision string, ready int64) unstructured.Unstructured {
rs := unstructured.Unstructured{Object: map[string]any{
"metadata": map[string]any{
"annotations": map[string]any{"deployment.kubernetes.io/revision": revision},
},
"status": map[string]any{"readyReplicas": ready},
}}
return rs
}
got := BuildDeploymentRevisions([]unstructured.Unstructured{replicaSet("1", 3), replicaSet("2", 0)}, "")
if len(got) != 2 || got[0].Number != 2 {
t.Fatalf("revisions = %#v", got)
}
if *got[0].ReadyReplicas != 0 || *got[0].Serving {
t.Fatalf("newest revision ready=%d serving=%v, want 0/false", *got[0].ReadyReplicas, *got[0].Serving)
}
if *got[1].ReadyReplicas != 3 || !*got[1].Serving {
t.Fatalf("older revision ready=%d serving=%v, want 3/true", *got[1].ReadyReplicas, *got[1].Serving)
}
}
func TestApplyControllerRevisionServing(t *testing.T) {
revisions := func() []WorkloadRevision {
return []WorkloadRevision{
{Number: 2, PodHash: "db-new", IsCurrent: true},
{Number: 1, PodHash: "db-old"},
}
}
workload := func(generation int64, status map[string]any) *unstructured.Unstructured {
status["observedGeneration"] = generation
obj := &unstructured.Unstructured{Object: map[string]any{"status": status}}
obj.SetGeneration(generation)
return obj
}
t.Run("settled StatefulSet", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(4, map[string]any{
"currentRevision": "db-new", "updateRevision": "db-new", "readyReplicas": int64(3), "replicas": int64(3), "currentReplicas": int64(3),
}), "statefulsets")
if !*got[0].Serving || *got[0].ReadyReplicas != 3 || *got[1].Serving || *got[1].ReadyReplicas != 0 {
t.Fatalf("revisions = %+v / %+v", got[0], got[1])
}
})
t.Run("OnDelete StatefulSet with pods left on a reverted revision stays unknown", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(4, map[string]any{
"currentRevision": "db-new", "updateRevision": "db-new", "readyReplicas": int64(3), "replicas": int64(3), "currentReplicas": int64(1),
}), "statefulsets")
if got[0].Serving != nil || got[1].Serving != nil || got[0].ReadyReplicas != nil {
t.Fatalf("revisions = %+v / %+v", got[0], got[1])
}
})
t.Run("DaemonSet with a misscheduled pod stays unknown", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(4, map[string]any{
"desiredNumberScheduled": int64(3), "updatedNumberScheduled": int64(3), "numberReady": int64(3), "numberMisscheduled": int64(1),
}), "daemonsets")
if got[0].Serving != nil || got[1].Serving != nil || got[0].ReadyReplicas != nil {
t.Fatalf("revisions = %+v / %+v", got[0], got[1])
}
})
t.Run("mid-update StatefulSet stays unknown", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(4, map[string]any{
"currentRevision": "db-old", "updateRevision": "db-new", "readyReplicas": int64(3),
}), "statefulsets")
if got[0].Serving != nil || got[1].Serving != nil || got[0].ReadyReplicas != nil {
t.Fatalf("revisions = %+v / %+v", got[0], got[1])
}
})
t.Run("StatefulSet status behind spec stays unknown", func(t *testing.T) {
got := revisions()
stale := workload(4, map[string]any{"currentRevision": "db-new", "updateRevision": "db-new"})
stale.SetGeneration(5)
applyControllerRevisionServing(got, stale, "statefulsets")
if got[0].Serving != nil {
t.Fatalf("revision = %+v", got[0])
}
})
t.Run("settled DaemonSet", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(1, map[string]any{
"desiredNumberScheduled": int64(5), "updatedNumberScheduled": int64(5), "numberReady": int64(4),
}), "daemonsets")
if !*got[0].Serving || *got[0].ReadyReplicas != 4 || *got[1].Serving {
t.Fatalf("revisions = %+v / %+v", got[0], got[1])
}
})
t.Run("mid-update DaemonSet stays unknown", func(t *testing.T) {
got := revisions()
applyControllerRevisionServing(got, workload(1, map[string]any{
"desiredNumberScheduled": int64(5), "updatedNumberScheduled": int64(2), "numberReady": int64(5),
}), "daemonsets")
if got[0].Serving != nil {
t.Fatalf("revision = %+v", got[0])
}
})
}