300 lines
7.5 KiB
Go
Raw Normal View History

package scheduledscaling
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
v1 "github.com/zalando-incubator/kube-metrics-adapter/pkg/apis/zalando.org/v1"
scalingschedulefake "github.com/zalando-incubator/kube-metrics-adapter/pkg/client/clientset/versioned/fake"
zalandov1 "github.com/zalando-incubator/kube-metrics-adapter/pkg/client/clientset/versioned/typed/zalando.org/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const (
hHMMFormat = "15:04"
)
type fakeClusterScalingScheduleStore struct {
client zalandov1.ZalandoV1Interface
}
func (s fakeClusterScalingScheduleStore) List() []interface{} {
schedules, err := s.client.ClusterScalingSchedules().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil
}
objects := make([]interface{}, 0, len(schedules.Items))
for _, schedule := range schedules.Items {
schedule := schedule
objects = append(objects, &schedule)
}
return objects
}
type fakeScalingScheduleStore struct {
client zalandov1.ZalandoV1Interface
}
func (s fakeScalingScheduleStore) List() []interface{} {
schedules, err := s.client.ScalingSchedules(corev1.NamespaceAll).List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil
}
objects := make([]interface{}, 0, len(schedules.Items))
for _, schedule := range schedules.Items {
schedule := schedule
objects = append(objects, &schedule)
}
return objects
}
type schedule struct {
schedules []v1.Schedule
expectedActive bool
preActiveStatus bool
}
func scheduleDate(date string) *v1.ScheduleDate {
d := v1.ScheduleDate(date)
return &d
}
func TestRunOnce(t *testing.T) {
nowRFC3339 := "2009-11-10T23:00:00+01:00" // +01:00 is Berlin timezone (default)
nowTime, _ := time.Parse(time.RFC3339, nowRFC3339)
nowWeekday := v1.TuesdaySchedule
// now func always return in UTC for test purposes
utc, _ := time.LoadLocation("UTC")
uTCNow := nowTime.In(utc)
// uTCNowRFC3339 := uTCNow.Format(time.RFC3339)
now := func() time.Time {
return uTCNow
}
// date := v1.ScheduleDate(schedule.date)
// endDate := v1.ScheduleDate(schedule.endDate)
for _, tc := range []struct {
msg string
schedules map[string]schedule
preActiveStatus bool
// scalingWindowDurationMinutes *int64
// expectedValue int64
// err error
// rampSteps int
}{
{
msg: "OneTime Schedules",
schedules: map[string]schedule{
"active": {
schedules: []v1.Schedule{
{
Type: v1.OneTimeSchedule,
Date: scheduleDate(nowRFC3339),
DurationMinutes: 15,
},
},
expectedActive: true,
},
"inactive": {
schedules: []v1.Schedule{
{
Type: v1.OneTimeSchedule,
Date: scheduleDate(nowTime.Add(1 * time.Hour).Format(time.RFC3339)),
DurationMinutes: 15,
},
},
expectedActive: false,
},
},
},
{
msg: "OneTime Schedules change active",
schedules: map[string]schedule{
"active": {
schedules: []v1.Schedule{
{
Type: v1.OneTimeSchedule,
Date: scheduleDate(nowRFC3339),
DurationMinutes: 15,
},
},
preActiveStatus: false,
expectedActive: true,
},
"inactive": {
schedules: []v1.Schedule{
{
Type: v1.OneTimeSchedule,
Date: scheduleDate(nowTime.Add(1 * time.Hour).Format(time.RFC3339)),
DurationMinutes: 15,
},
},
preActiveStatus: true,
expectedActive: false,
},
},
},
{
msg: "Repeating Schedules",
schedules: map[string]schedule{
"active": {
schedules: []v1.Schedule{
{
Type: v1.RepeatingSchedule,
DurationMinutes: 15,
Period: &v1.SchedulePeriod{
Days: []v1.ScheduleDay{nowWeekday},
StartTime: nowTime.Format(hHMMFormat),
},
},
},
expectedActive: true,
},
"inactive": {
schedules: []v1.Schedule{
{
Type: v1.RepeatingSchedule,
DurationMinutes: 15,
Period: &v1.SchedulePeriod{
Days: []v1.ScheduleDay{nowWeekday},
StartTime: nowTime.Add(1 * time.Hour).Format(hHMMFormat),
},
},
},
expectedActive: false,
},
},
},
{
msg: "Repeating Schedules change active",
schedules: map[string]schedule{
"active": {
schedules: []v1.Schedule{
{
Type: v1.RepeatingSchedule,
DurationMinutes: 15,
Period: &v1.SchedulePeriod{
Days: []v1.ScheduleDay{nowWeekday},
StartTime: nowTime.Format(hHMMFormat),
},
},
},
preActiveStatus: false,
expectedActive: true,
},
"inactive": {
schedules: []v1.Schedule{
{
Type: v1.RepeatingSchedule,
DurationMinutes: 15,
Period: &v1.SchedulePeriod{
Days: []v1.ScheduleDay{nowWeekday},
StartTime: nowTime.Add(1 * time.Hour).Format(hHMMFormat),
},
},
},
preActiveStatus: true,
expectedActive: false,
},
},
},
} {
t.Run(tc.msg, func(t *testing.T) {
// setup fake client and cache
client := scalingschedulefake.NewSimpleClientset()
clusterScalingSchedulesStore := fakeClusterScalingScheduleStore{
client: client.ZalandoV1(),
}
scalingSchedulesStore := fakeScalingScheduleStore{
client: client.ZalandoV1(),
}
// add schedules
err := applySchedules(client.ZalandoV1(), tc.schedules)
require.NoError(t, err)
controller := NewController(client.ZalandoV1(), scalingSchedulesStore, clusterScalingSchedulesStore, now, 0, "Europe/Berlin")
err = controller.runOnce(context.Background())
require.NoError(t, err)
// check schedule status
err = checkSchedules(t, client.ZalandoV1(), tc.schedules)
require.NoError(t, err)
})
}
}
func applySchedules(client zalandov1.ZalandoV1Interface, schedules map[string]schedule) error {
for name, schedule := range schedules {
spec := v1.ScalingScheduleSpec{
// ScalingWindowDurationMinutes *int64 `json:"scalingWindowDurationMinutes,omitempty"`
Schedules: schedule.schedules,
}
status := v1.ScalingScheduleStatus{
Active: schedule.preActiveStatus,
}
scalingSchedule := &v1.ScalingSchedule{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "default",
},
Spec: spec,
Status: status,
}
_, err := client.ScalingSchedules(scalingSchedule.Namespace).Create(context.Background(), scalingSchedule, metav1.CreateOptions{})
if err != nil {
return err
}
clusterScalingSchedule := &v1.ClusterScalingSchedule{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: spec,
Status: status,
}
_, err = client.ClusterScalingSchedules().Create(context.Background(), clusterScalingSchedule, metav1.CreateOptions{})
if err != nil {
return err
}
}
return nil
}
func checkSchedules(t *testing.T, client zalandov1.ZalandoV1Interface, schedules map[string]schedule) error {
for name, expectedSchedule := range schedules {
scalingSchedule, err := client.ScalingSchedules("default").Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
return err
}
require.Equal(t, expectedSchedule.expectedActive, scalingSchedule.Status.Active)
clusterScalingSchedule, err := client.ClusterScalingSchedules().Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
return err
}
require.Equal(t, expectedSchedule.expectedActive, clusterScalingSchedule.Status.Active)
}
return nil
}