package util
import (
"reflect"
"sort"
"strconv"
"testing"
"time"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
deployapi "github.com/openshift/origin/pkg/deploy/api"
deploytest "github.com/openshift/origin/pkg/deploy/api/test"
deployv1 "github.com/openshift/origin/pkg/deploy/api/v1"
_ "github.com/openshift/origin/pkg/api/install"
)
func podTemplateA() *kapi.PodTemplateSpec {
t := deploytest.OkPodTemplate()
t.Spec.Containers = append(t.Spec.Containers, kapi.Container{
Name: "container1",
Image: "registry:8080/repo1:ref1",
})
return t
}
func podTemplateB() *kapi.PodTemplateSpec {
t := podTemplateA()
t.Labels = map[string]string{"c": "d"}
return t
}
func podTemplateC() *kapi.PodTemplateSpec {
t := podTemplateA()
t.Spec.Containers[0] = kapi.Container{
Name: "container2",
Image: "registry:8080/repo1:ref3",
}
return t
}
func podTemplateD() *kapi.PodTemplateSpec {
t := podTemplateA()
t.Spec.Containers = append(t.Spec.Containers, kapi.Container{
Name: "container2",
Image: "registry:8080/repo1:ref4",
})
return t
}
func TestPodName(t *testing.T) {
deployment := &kapi.ReplicationController{
ObjectMeta: kapi.ObjectMeta{
Name: "testName",
},
}
expected := "testName-deploy"
actual := DeployerPodNameForDeployment(deployment.Name)
if expected != actual {
t.Errorf("Unexpected pod name for deployment. Expected: %s Got: %s", expected, actual)
}
}
func TestMakeDeploymentOk(t *testing.T) {
config := deploytest.OkDeploymentConfig(1)
deployment, err := MakeDeployment(config, kapi.Codecs.LegacyCodec(deployv1.SchemeGroupVersion))
if err != nil {
t.Fatalf("unexpected error: %#v", err)
}
expectedAnnotations := map[string]string{
deployapi.DeploymentConfigAnnotation: config.Name,
deployapi.DeploymentStatusAnnotation: string(deployapi.DeploymentStatusNew),
deployapi.DeploymentVersionAnnotation: strconv.FormatInt(config.Status.LatestVersion, 10),
}
for key, expected := range expectedAnnotations {
if actual := deployment.Annotations[key]; actual != expected {
t.Fatalf("expected deployment annotation %s=%s, got %s", key, expected, actual)
}
}
expectedAnnotations = map[string]string{
deployapi.DeploymentAnnotation: deployment.Name,
deployapi.DeploymentConfigAnnotation: config.Name,
deployapi.DeploymentVersionAnnotation: strconv.FormatInt(config.Status.LatestVersion, 10),
}
for key, expected := range expectedAnnotations {
if actual := deployment.Spec.Template.Annotations[key]; actual != expected {
t.Fatalf("expected pod template annotation %s=%s, got %s", key, expected, actual)
}
}
if len(EncodedDeploymentConfigFor(deployment)) == 0 {
t.Fatalf("expected deployment with DeploymentEncodedConfigAnnotation annotation")
}
if decodedConfig, err := DecodeDeploymentConfig(deployment, kapi.Codecs.LegacyCodec(deployv1.SchemeGroupVersion)); err != nil {
t.Fatalf("invalid encoded config on deployment: %v", err)
} else {
if e, a := config.Name, decodedConfig.Name; e != a {
t.Fatalf("encoded config name doesn't match source config")
}
// TODO: more assertions
}
if deployment.Spec.Replicas != 0 {
t.Fatalf("expected deployment replicas to be 0")
}
if l, e, a := deployapi.DeploymentConfigAnnotation, config.Name, deployment.Labels[deployapi.DeploymentConfigAnnotation]; e != a {
t.Fatalf("expected label %s=%s, got %s", l, e, a)
}
if e, a := config.Name, deployment.Spec.Template.Labels[deployapi.DeploymentConfigLabel]; e != a {
t.Fatalf("expected label DeploymentConfigLabel=%s, got %s", e, a)
}
if e, a := deployment.Name, deployment.Spec.Template.Labels[deployapi.DeploymentLabel]; e != a {
t.Fatalf("expected label DeploymentLabel=%s, got %s", e, a)
}
if e, a := config.Name, deployment.Spec.Selector[deployapi.DeploymentConfigLabel]; e != a {
t.Fatalf("expected selector DeploymentConfigLabel=%s, got %s", e, a)
}
if e, a := deployment.Name, deployment.Spec.Selector[deployapi.DeploymentLabel]; e != a {
t.Fatalf("expected selector DeploymentLabel=%s, got %s", e, a)
}
}
func TestDeploymentsByLatestVersion_sorting(t *testing.T) {
mkdeployment := func(version int64) kapi.ReplicationController {
deployment, _ := MakeDeployment(deploytest.OkDeploymentConfig(version), kapi.Codecs.LegacyCodec(deployv1.SchemeGroupVersion))
return *deployment
}
deployments := []kapi.ReplicationController{
mkdeployment(4),
mkdeployment(1),
mkdeployment(2),
mkdeployment(3),
}
sort.Sort(ByLatestVersionAsc(deployments))
for i := int64(0); i < 4; i++ {
if e, a := i+1, DeploymentVersionFor(&deployments[i]); e != a {
t.Errorf("expected deployment[%d]=%d, got %d", i, e, a)
}
}
sort.Sort(ByLatestVersionDesc(deployments))
for i := int64(0); i < 4; i++ {
if e, a := 4-i, DeploymentVersionFor(&deployments[i]); e != a {
t.Errorf("expected deployment[%d]=%d, got %d", i, e, a)
}
}
}
// TestSort verifies that builds are sorted by most recently created
func TestSort(t *testing.T) {
present := unversioned.Now()
past := unversioned.NewTime(present.Time.Add(-1 * time.Minute))
controllers := []*kapi.ReplicationController{
{
ObjectMeta: kapi.ObjectMeta{
Name: "past",
CreationTimestamp: past,
},
},
{
ObjectMeta: kapi.ObjectMeta{
Name: "present",
CreationTimestamp: present,
},
},
}
sort.Sort(ByMostRecent(controllers))
if controllers[0].Name != "present" {
t.Errorf("Unexpected sort order")
}
if controllers[1].Name != "past" {
t.Errorf("Unexpected sort order")
}
}
func TestCanTransitionPhase(t *testing.T) {
tests := []struct {
name string
current, next deployapi.DeploymentStatus
expected bool
}{
{
name: "New->New",
current: deployapi.DeploymentStatusNew,
next: deployapi.DeploymentStatusNew,
expected: false,
},
{
name: "New->Pending",
current: deployapi.DeploymentStatusNew,
next: deployapi.DeploymentStatusPending,
expected: true,
},
{
name: "New->Running",
current: deployapi.DeploymentStatusNew,
next: deployapi.DeploymentStatusRunning,
expected: true,
},
{
name: "New->Complete",
current: deployapi.DeploymentStatusNew,
next: deployapi.DeploymentStatusComplete,
expected: true,
},
{
name: "New->Failed",
current: deployapi.DeploymentStatusNew,
next: deployapi.DeploymentStatusFailed,
expected: true,
},
{
name: "Pending->New",
current: deployapi.DeploymentStatusPending,
next: deployapi.DeploymentStatusNew,
expected: false,
},
{
name: "Pending->Pending",
current: deployapi.DeploymentStatusPending,
next: deployapi.DeploymentStatusPending,
expected: false,
},
{
name: "Pending->Running",
current: deployapi.DeploymentStatusPending,
next: deployapi.DeploymentStatusRunning,
expected: true,
},
{
name: "Pending->Failed",
current: deployapi.DeploymentStatusPending,
next: deployapi.DeploymentStatusFailed,
expected: true,
},
{
name: "Pending->Complete",
current: deployapi.DeploymentStatusPending,
next: deployapi.DeploymentStatusComplete,
expected: true,
},
{
name: "Running->New",
current: deployapi.DeploymentStatusRunning,
next: deployapi.DeploymentStatusNew,
expected: false,
},
{
name: "Running->Pending",
current: deployapi.DeploymentStatusRunning,
next: deployapi.DeploymentStatusPending,
expected: false,
},
{
name: "Running->Running",
current: deployapi.DeploymentStatusRunning,
next: deployapi.DeploymentStatusRunning,
expected: false,
},
{
name: "Running->Failed",
current: deployapi.DeploymentStatusRunning,
next: deployapi.DeploymentStatusFailed,
expected: true,
},
{
name: "Running->Complete",
current: deployapi.DeploymentStatusRunning,
next: deployapi.DeploymentStatusComplete,
expected: true,
},
{
name: "Complete->New",
current: deployapi.DeploymentStatusComplete,
next: deployapi.DeploymentStatusNew,
expected: false,
},
{
name: "Complete->Pending",
current: deployapi.DeploymentStatusComplete,
next: deployapi.DeploymentStatusPending,
expected: false,
},
{
name: "Complete->Running",
current: deployapi.DeploymentStatusComplete,
next: deployapi.DeploymentStatusRunning,
expected: false,
},
{
name: "Complete->Failed",
current: deployapi.DeploymentStatusComplete,
next: deployapi.DeploymentStatusFailed,
expected: false,
},
{
name: "Complete->Complete",
current: deployapi.DeploymentStatusComplete,
next: deployapi.DeploymentStatusComplete,
expected: false,
},
{
name: "Failed->New",
current: deployapi.DeploymentStatusFailed,
next: deployapi.DeploymentStatusNew,
expected: false,
},
{
name: "Failed->Pending",
current: deployapi.DeploymentStatusFailed,
next: deployapi.DeploymentStatusPending,
expected: false,
},
{
name: "Failed->Running",
current: deployapi.DeploymentStatusFailed,
next: deployapi.DeploymentStatusRunning,
expected: false,
},
{
name: "Failed->Complete",
current: deployapi.DeploymentStatusFailed,
next: deployapi.DeploymentStatusComplete,
expected: false,
},
{
name: "Failed->Failed",
current: deployapi.DeploymentStatusFailed,
next: deployapi.DeploymentStatusFailed,
expected: false,
},
}
for _, test := range tests {
got := CanTransitionPhase(test.current, test.next)
if got != test.expected {
t.Errorf("%s: expected %t, got %t", test.name, test.expected, got)
}
}
}
var (
now = unversioned.Now()
later = unversioned.Time{Time: now.Add(time.Minute)}
earlier = unversioned.Time{Time: now.Add(-time.Minute)}
condProgressing = func() deployapi.DeploymentCondition {
return deployapi.DeploymentCondition{
Type: deployapi.DeploymentProgressing,
Status: kapi.ConditionTrue,
LastTransitionTime: now,
Reason: "ForSomeReason",
}
}
condProgressingDifferentTime = func() deployapi.DeploymentCondition {
return deployapi.DeploymentCondition{
Type: deployapi.DeploymentProgressing,
Status: kapi.ConditionTrue,
LastTransitionTime: later,
Reason: "ForSomeReason",
}
}
condProgressingDifferentReason = func() deployapi.DeploymentCondition {
return deployapi.DeploymentCondition{
Type: deployapi.DeploymentProgressing,
Status: kapi.ConditionTrue,
LastTransitionTime: later,
Reason: "BecauseItIs",
}
}
condNotProgressing = func() deployapi.DeploymentCondition {
return deployapi.DeploymentCondition{
Type: deployapi.DeploymentProgressing,
Status: kapi.ConditionFalse,
LastUpdateTime: earlier,
LastTransitionTime: earlier,
Reason: "NotYet",
}
}
condAvailable = func() deployapi.DeploymentCondition {
return deployapi.DeploymentCondition{
Type: deployapi.DeploymentAvailable,
Status: kapi.ConditionTrue,
Reason: "AwesomeController",
}
}
)
func TestGetCondition(t *testing.T) {
exampleStatus := func() deployapi.DeploymentConfigStatus {
return deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{condProgressing(), condAvailable()},
}
}
tests := []struct {
name string
status deployapi.DeploymentConfigStatus
condType deployapi.DeploymentConditionType
condStatus kapi.ConditionStatus
condReason string
expected bool
}{
{
name: "condition exists",
status: exampleStatus(),
condType: deployapi.DeploymentAvailable,
expected: true,
},
{
name: "condition does not exist",
status: exampleStatus(),
condType: deployapi.DeploymentReplicaFailure,
expected: false,
},
}
for _, test := range tests {
cond := GetDeploymentCondition(test.status, test.condType)
exists := cond != nil
if exists != test.expected {
t.Errorf("%s: expected condition to exist: %t, got: %t", test.name, test.expected, exists)
}
}
}
func TestSetCondition(t *testing.T) {
tests := []struct {
name string
status *deployapi.DeploymentConfigStatus
cond deployapi.DeploymentCondition
expectedStatus *deployapi.DeploymentConfigStatus
}{
{
name: "set for the first time",
status: &deployapi.DeploymentConfigStatus{},
cond: condAvailable(),
expectedStatus: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condAvailable(),
},
},
},
{
name: "simple set",
status: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condProgressing(),
},
},
cond: condAvailable(),
expectedStatus: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condProgressing(), condAvailable(),
},
},
},
{
name: "replace if status changes",
status: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condNotProgressing(),
},
},
cond: condProgressing(),
expectedStatus: &deployapi.DeploymentConfigStatus{Conditions: []deployapi.DeploymentCondition{condProgressing()}},
},
{
name: "replace if reason changes",
status: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condProgressing(),
},
},
cond: condProgressingDifferentReason(),
expectedStatus: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
{
Type: deployapi.DeploymentProgressing,
Status: kapi.ConditionTrue,
// Note that LastTransitionTime stays the same.
LastTransitionTime: now,
// Only the reason changes.
Reason: "BecauseItIs",
},
},
},
},
{
name: "don't replace if status and reason don't change",
status: &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{
condProgressing(),
},
},
cond: condProgressingDifferentTime(),
expectedStatus: &deployapi.DeploymentConfigStatus{Conditions: []deployapi.DeploymentCondition{condProgressing()}},
},
}
for _, test := range tests {
t.Logf("running test %q", test.name)
SetDeploymentCondition(test.status, test.cond)
if !reflect.DeepEqual(test.status, test.expectedStatus) {
t.Errorf("expected status: %v, got: %v", test.expectedStatus, test.status)
}
}
}
func TestRemoveCondition(t *testing.T) {
exampleStatus := func() *deployapi.DeploymentConfigStatus {
return &deployapi.DeploymentConfigStatus{
Conditions: []deployapi.DeploymentCondition{condProgressing(), condAvailable()},
}
}
tests := []struct {
name string
status *deployapi.DeploymentConfigStatus
condType deployapi.DeploymentConditionType
expectedStatus *deployapi.DeploymentConfigStatus
}{
{
name: "remove from empty status",
status: &deployapi.DeploymentConfigStatus{},
condType: deployapi.DeploymentProgressing,
expectedStatus: &deployapi.DeploymentConfigStatus{},
},
{
name: "simple remove",
status: &deployapi.DeploymentConfigStatus{Conditions: []deployapi.DeploymentCondition{condProgressing()}},
condType: deployapi.DeploymentProgressing,
expectedStatus: &deployapi.DeploymentConfigStatus{},
},
{
name: "doesn't remove anything",
status: exampleStatus(),
condType: deployapi.DeploymentReplicaFailure,
expectedStatus: exampleStatus(),
},
}
for _, test := range tests {
RemoveDeploymentCondition(test.status, test.condType)
if !reflect.DeepEqual(test.status, test.expectedStatus) {
t.Errorf("%s: expected status: %v, got: %v", test.name, test.expectedStatus, test.status)
}
}
}