mirror of
https://github.com/zalando-incubator/kube-metrics-adapter.git
synced 2025-01-05 08:22:54 +00:00
300 lines
7.5 KiB
Go
300 lines
7.5 KiB
Go
|
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
|
||
|
}
|