From 4ce9d33f55c47056e8864a00821edee538a9e4a7 Mon Sep 17 00:00:00 2001 From: Sanjay Nathani Date: Sat, 29 Aug 2020 20:09:05 +0530 Subject: [PATCH 1/2] schema update for repeat strategy and add includedHours Signed-off-by: Sanjay Nathani --- .../v1alpha1/chaosschedule_types.go | 38 +++++-- .../v1alpha1/zz_generated.deepcopy.go | 100 +++++++++++++++++- .../chaosscheduler_controller.go | 3 +- .../createEngineForNowAndOnce.go | 12 +-- .../chaosscheduler/createEngineForRepeat.go | 94 +++++++++++----- pkg/controller/chaosscheduler/schedule.go | 12 ++- 6 files changed, 210 insertions(+), 49 deletions(-) diff --git a/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go b/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go index e57e29f2..f5f19d09 100644 --- a/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go +++ b/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go @@ -81,9 +81,9 @@ type ScheduleStatus struct { //Status defines the current running status of the schedule Status ChaosStatus `json:"status"` //StartTime defines the starting timestamp of the schedule - StartTime metav1.Time `json:"startTime,omitempty"` + StartTime *metav1.Time `json:"startTime,omitempty"` //EndTime defines the end timestamp of the schedule - EndTime metav1.Time `json:"endTime,omitempty"` + EndTime *metav1.Time `json:"endTime,omitempty"` //TotalInstances defines the total no. of instances to be executed TotalInstances int `json:"totalInstances,omitempty"` //RunInstances defines number of already ran instances at that point of time @@ -124,20 +124,42 @@ type ScheduleOnce struct { // ScheduleRepeat will contain parameters for executing the repeat strategy of scheduling type ScheduleRepeat struct { + TimeRange *TimeRange `json:"timeRange"` + Properties ScheduleRepeatProperties `json:"properties"` + WorkHours *WorkHours `json:"workHours"` + WorkDays *WorkDays `json:"workDays"` +} + +//TimeRange will contain time constraints for the chaos to be injected +type TimeRange struct { + //Start limit of the time range in which experiment is to be run + StartTime *metav1.Time `json:"startTime"` + //End limit of the time range in which experiment is to be run + EndTime *metav1.Time `json:"endTime"` +} + +//ScheduleRepeatProperties will define the properties needed by the schedule to inject chaos +type ScheduleRepeatProperties struct { //Minimum Period b/w two iterations of chaos experiments batch run MinChaosInterval string `json:"minChaosInterval"` //Number of Instances of the experiment to be run within a given time range InstanceCount string `json:"instanceCount"` - //Days of week when experiments batch run is scheduled - IncludedDays string `json:"includedDays"` - //Start limit of the time range in which experiment is to be run - StartTime metav1.Time `json:"startTime"` - //End limit of the time range in which experiment is to be run - EndTime metav1.Time `json:"endTime"` //Whether the chaos is to be scheduled at a random time or not Random bool `json:"random"` } +//WorkHours specify in which hours of the day chaos is to be injected +type WorkHours struct { + //Hours of the day when experiments batch run is scheduled + IncludedHours string `json:"includedHours"` +} + +//WorkDays specify in which hours of the day chaos is to be injected +type WorkDays struct { + //Days of week when experiments batch run is scheduled + IncludedDays string `json:"includedDays"` +} + // +genclient // +resource:path=chaosschedule // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go index bd990f81..c3c152bf 100644 --- a/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go @@ -176,8 +176,22 @@ func (in *ScheduleOnce) DeepCopy() *ScheduleOnce { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ScheduleRepeat) DeepCopyInto(out *ScheduleRepeat) { *out = *in - in.StartTime.DeepCopyInto(&out.StartTime) - in.EndTime.DeepCopyInto(&out.EndTime) + if in.TimeRange != nil { + in, out := &in.TimeRange, &out.TimeRange + *out = new(TimeRange) + (*in).DeepCopyInto(*out) + } + out.Properties = in.Properties + if in.WorkHours != nil { + in, out := &in.WorkHours, &out.WorkHours + *out = new(WorkHours) + **out = **in + } + if in.WorkDays != nil { + in, out := &in.WorkDays, &out.WorkDays + *out = new(WorkDays) + **out = **in + } return } @@ -191,11 +205,33 @@ func (in *ScheduleRepeat) DeepCopy() *ScheduleRepeat { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ScheduleRepeatProperties) DeepCopyInto(out *ScheduleRepeatProperties) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScheduleRepeatProperties. +func (in *ScheduleRepeatProperties) DeepCopy() *ScheduleRepeatProperties { + if in == nil { + return nil + } + out := new(ScheduleRepeatProperties) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) { *out = *in - in.StartTime.DeepCopyInto(&out.StartTime) - in.EndTime.DeepCopyInto(&out.EndTime) + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.EndTime != nil { + in, out := &in.EndTime, &out.EndTime + *out = (*in).DeepCopy() + } in.ExpectedNextRunTime.DeepCopyInto(&out.ExpectedNextRunTime) return } @@ -209,3 +245,59 @@ func (in *ScheduleStatus) DeepCopy() *ScheduleStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TimeRange) DeepCopyInto(out *TimeRange) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.EndTime != nil { + in, out := &in.EndTime, &out.EndTime + *out = (*in).DeepCopy() + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeRange. +func (in *TimeRange) DeepCopy() *TimeRange { + if in == nil { + return nil + } + out := new(TimeRange) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkDays) DeepCopyInto(out *WorkDays) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkDays. +func (in *WorkDays) DeepCopy() *WorkDays { + if in == nil { + return nil + } + out := new(WorkDays) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkHours) DeepCopyInto(out *WorkHours) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkHours. +func (in *WorkHours) DeepCopy() *WorkHours { + if in == nil { + return nil + } + out := new(WorkHours) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/chaosscheduler/chaosscheduler_controller.go b/pkg/controller/chaosscheduler/chaosscheduler_controller.go index 963c1895..c285b66a 100644 --- a/pkg/controller/chaosscheduler/chaosscheduler_controller.go +++ b/pkg/controller/chaosscheduler/chaosscheduler_controller.go @@ -3,6 +3,7 @@ package chaosscheduler import ( "context" "fmt" + "time" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -164,7 +165,7 @@ func (schedulerReconcile *reconcileScheduler) reconcileForComplete(cs *chaosType opts := client.UpdateOptions{} cs.Instance.Status.Schedule.Status = schedulerV1.StatusCompleted - cs.Instance.Status.Schedule.EndTime = metav1.Now() + cs.Instance.Status.Schedule.EndTime = &metav1.Time{Time: time.Now()} if err := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance, &opts); err != nil { schedulerReconcile.r.recorder.Eventf(cs.Instance, corev1.EventTypeWarning, "ScheduleCompleted", "Cannot update status as completed") return reconcile.Result{}, fmt.Errorf("Unable to update chaosSchedule for status completed, due to error: %v", err) diff --git a/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go b/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go index cc8b3339..071852de 100644 --- a/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go +++ b/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go @@ -25,6 +25,7 @@ func (schedulerReconcile *reconcileScheduler) createForNowAndOnce(cs *chaosTypes return reconcile.Result{}, errUpdate } + currentTime := metav1.Now() engine := &operatorV1.ChaosEngine{} err = schedulerReconcile.r.client.Get(context.TODO(), types.NamespacedName{Name: cs.Instance.Name, Namespace: cs.Instance.Namespace}, engine) if err != nil && k8serrors.IsNotFound(err) { @@ -43,14 +44,13 @@ func (schedulerReconcile *reconcileScheduler) createForNowAndOnce(cs *chaosTypes cs.Instance.Spec.ScheduleState = schedulerV1.StateActive cs.Instance.Status.Schedule.Status = schedulerV1.StatusRunning cs.Instance.Status.Schedule.TotalInstances = 1 - cs.Instance.Status.Schedule.StartTime = metav1.Now() - cs.Instance.Status.LastScheduleTime = &metav1.Time{Time: metav1.Now().Time} + cs.Instance.Status.Schedule.StartTime = ¤tTime + cs.Instance.Status.LastScheduleTime = ¤tTime ref, errRef := schedulerReconcile.r.getRef(engine) if errRef != nil { - // klog.V(2).Infof("Unable to make object reference for job for %s", nameForLog) - } else { - cs.Instance.Status.Active = append(cs.Instance.Status.Active, *ref) + return reconcile.Result{}, errRef } + cs.Instance.Status.Active = append(cs.Instance.Status.Active, *ref) if err := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); err != nil { return reconcile.Result{}, err } @@ -59,7 +59,7 @@ func (schedulerReconcile *reconcileScheduler) createForNowAndOnce(cs *chaosTypes return reconcile.Result{}, err } else if IsEngineFinished(engine) { cs.Instance.Spec.ScheduleState = schedulerV1.StateCompleted - cs.Instance.Status.Schedule.EndTime = metav1.Now() + cs.Instance.Status.Schedule.EndTime = ¤tTime if err := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); err != nil { return reconcile.Result{}, err } diff --git a/pkg/controller/chaosscheduler/createEngineForRepeat.go b/pkg/controller/chaosscheduler/createEngineForRepeat.go index 1a5f678d..6b4b8285 100644 --- a/pkg/controller/chaosscheduler/createEngineForRepeat.go +++ b/pkg/controller/chaosscheduler/createEngineForRepeat.go @@ -29,14 +29,18 @@ func (schedulerReconcile *reconcileScheduler) createEngineRepeat(cs *chaosTypes. return reconcile.Result{}, errUpdate } - if metav1.Now().After(cs.Instance.Spec.Schedule.Repeat.EndTime.Time) { - - schedulerReconcile.reqLogger.Info("end time already passed", "endTime", cs.Instance.Spec.Schedule.Repeat.EndTime) - cs.Instance.Spec.ScheduleState = schedulerV1.StateCompleted - if errUpdate := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); errUpdate != nil { - return reconcile.Result{}, errUpdate + timeRange := cs.Instance.Spec.Schedule.Repeat.TimeRange + if timeRange != nil { + endTime := timeRange.EndTime + if endTime != nil && metav1.Now().After(endTime.Time) { + + schedulerReconcile.reqLogger.Info("end time already passed", "endTime", endTime) + cs.Instance.Spec.ScheduleState = schedulerV1.StateCompleted + if errUpdate := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); errUpdate != nil { + return reconcile.Result{}, errUpdate + } + return reconcile.Result{}, nil } - return reconcile.Result{}, nil } if cs.Instance.DeletionTimestamp != nil { @@ -110,7 +114,16 @@ func (schedulerReconcile *reconcileScheduler) createNewEngine(cs *chaosTypes.Sch } cs.Instance.Status.LastScheduleTime = &metav1.Time{Time: scheduledTime} cs.Instance.Status.Schedule.RunInstances = cs.Instance.Status.Schedule.RunInstances + 1 - cs.Instance.Status.Schedule.StartTime = cs.Instance.Spec.Schedule.Repeat.StartTime + + var startTime *metav1.Time + if cs.Instance.Spec.Schedule.Repeat.TimeRange != nil { + startTime = cs.Instance.Spec.Schedule.Repeat.TimeRange.StartTime + } + + if startTime == nil { + startTime = &cs.Instance.CreationTimestamp + } + cs.Instance.Status.Schedule.StartTime = startTime if errUpdate := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); errUpdate != nil { return reconcile.Result{}, errUpdate @@ -127,17 +140,20 @@ func getRecentUnmetScheduleTime(cs *chaosTypes.SchedulerInfo, cronString string) return nil, fmt.Errorf("unparseable schedule: %s : %s", cronString, err) } + timeRange := cs.Instance.Spec.Schedule.Repeat.TimeRange + var earliestTime time.Time if cs.Instance.Status.LastScheduleTime != nil { earliestTime = cs.Instance.Status.LastScheduleTime.Time - } else { + } else if timeRange != nil && timeRange.StartTime != nil { // If none found, then this is either a recently created schedule, // or the active/completed info was somehow lost (contract for status // in kubernetes says it may need to be recreated), or that we have // started a engine, but have not noticed it yet (distributed systems can - // have arbitrary delays). In any case, use the creation time of the - // Schedule as last known start time. - earliestTime = cs.Instance.Spec.Schedule.Repeat.StartTime.Time.Add(time.Minute * -1) + // have arbitrary delays). + earliestTime = timeRange.StartTime.Time.Add(time.Minute * -1) + } else { + earliestTime = cs.Instance.GetCreationTimestamp().Time } if earliestTime.After(now) { @@ -155,27 +171,45 @@ func getRecentUnmetScheduleTime(cs *chaosTypes.SchedulerInfo, cronString string) func scheduleRepeat(cs *chaosTypes.SchedulerInfo) (string, time.Duration, error) { - interval, err := fetchInterval(cs.Instance.Spec.Schedule.Repeat.MinChaosInterval) + interval, err := fetchInterval(cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval) if err != nil { return "", time.Duration(0), errors.New("error in parsing minChaosInterval(make sure to include 'm' or 'h' suffix for minutes and hours respectively)") } - instances, err := fetchInstances(cs.Instance.Spec.Schedule.Repeat.InstanceCount) + instances, err := fetchInstances(cs.Instance.Spec.Schedule.Repeat.Properties.InstanceCount) if err != nil { return "", time.Duration(0), errors.New("error in parsing instanceCount") } - startTime := cs.Instance.Spec.Schedule.Repeat.StartTime - endTime := cs.Instance.Spec.Schedule.Repeat.EndTime + var startTime *metav1.Time + var endTime *metav1.Time + if cs.Instance.Spec.Schedule.Repeat.TimeRange != nil { + startTime = cs.Instance.Spec.Schedule.Repeat.TimeRange.StartTime + endTime = cs.Instance.Spec.Schedule.Repeat.TimeRange.EndTime + } + + if startTime == nil { + startTime = &cs.Instance.CreationTimestamp + } + /* includedDays will be given in form comma seperated * list such as 0,2,4 or Mon,Wed,Sat * or in the range form such as 2-4 or Mon-Wed * 0 represents Sunday and 6 represents Saturday */ - includedDays := cs.Instance.Spec.Schedule.Repeat.IncludedDays - if includedDays == "" { - return "", time.Duration(0), errors.New("Missing IncludedDays") + var includedDays string + if cs.Instance.Spec.Schedule.Repeat.WorkDays != nil { + includedDays = cs.Instance.Spec.Schedule.Repeat.WorkDays.IncludedDays + } else { + includedDays = "*" } - duration := endTime.Sub(startTime.UTC()) + + var includedHours string + if cs.Instance.Spec.Schedule.Repeat.WorkHours != nil { + includedHours = cs.Instance.Spec.Schedule.Repeat.WorkHours.IncludedHours + } else { + includedHours = "*" + } + // One of the minChaosInterval or instances is mandatory to be given if interval != 0 { /* MinChaosInterval will be in form of "10m" or "2h" @@ -185,28 +219,30 @@ func scheduleRepeat(cs *chaosTypes.SchedulerInfo) (string, time.Duration, error) // if err != nil { // return "", err // } - if strings.Contains(cs.Instance.Spec.Schedule.Repeat.MinChaosInterval, "m") { - return fmt.Sprintf("*/%d * * * %s", interval, includedDays), time.Minute * time.Duration(interval), nil + if strings.Contains(cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval, "m") { + return fmt.Sprintf("*/%d %s * * %s", interval, includedHours, includedDays), time.Minute * time.Duration(interval), nil } - return fmt.Sprintf("* */%d * * %s", interval, includedDays), time.Hour * time.Duration(interval), nil - } else if instances != 0 { + return fmt.Sprintf("* %s/%d * * %s", includedHours, interval, includedDays), time.Hour * time.Duration(interval), nil + } else if instances != 0 && endTime != nil { + cs.Instance.Status.Schedule.TotalInstances = instances + duration := endTime.Sub(startTime.UTC()) //schedule at the end time will not be able to schedule so increaing the no. of instance intervalHours := duration.Hours() / float64(instances) intervalMinutes := duration.Minutes() / float64(instances) if intervalHours >= 1 { // to be sent in form of EnvVariable to executor - cs.Instance.Spec.Schedule.Repeat.MinChaosInterval = fmt.Sprintf("%dh", int(intervalHours)) - return fmt.Sprintf("* */%d * * %s", int(intervalHours), includedDays), time.Hour * time.Duration(intervalHours), nil + cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval = fmt.Sprintf("%dh", int(intervalHours)) + return fmt.Sprintf("* %s/%d * * %s", includedHours, int(intervalHours), includedDays), time.Hour * time.Duration(intervalHours), nil } else if intervalMinutes >= 1 { // to be sent in form of EnvVariable to executor - cs.Instance.Spec.Schedule.Repeat.MinChaosInterval = fmt.Sprintf("%dm", int(intervalMinutes)) - return fmt.Sprintf("*/%d * * * %s", int(intervalMinutes), includedDays), time.Minute * time.Duration(intervalMinutes), nil + cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval = fmt.Sprintf("%dm", int(intervalMinutes)) + return fmt.Sprintf("*/%d %s * * %s", int(intervalMinutes), includedHours, includedDays), time.Minute * time.Duration(intervalMinutes), nil } return "", time.Duration(0), errors.New("Too many instances to execute") } - return "", time.Duration(0), errors.New("MinChaosInterval and InstanceCount both not found") + return "", time.Duration(0), errors.New("MinChaosInterval and InstanceCount with combination of endTime not found") } func fetchInterval(minChaosInterval string) (int, error) { diff --git a/pkg/controller/chaosscheduler/schedule.go b/pkg/controller/chaosscheduler/schedule.go index 5461f20f..2f0a3e64 100644 --- a/pkg/controller/chaosscheduler/schedule.go +++ b/pkg/controller/chaosscheduler/schedule.go @@ -43,8 +43,18 @@ func schedule(schedulerReconcile *reconcileScheduler, scheduler *chaosTypes.Sche * that much duration * Chaos is being started 1 min before the scheduled time */ + + var startTime *metav1.Time + if scheduler.Instance.Spec.Schedule.Repeat.TimeRange != nil { + startTime = scheduler.Instance.Spec.Schedule.Repeat.TimeRange.StartTime + } + + if startTime == nil { + startTime = &scheduler.Instance.CreationTimestamp + } + scheduleTime := time.Now() - startDuration := scheduler.Instance.Spec.Schedule.Repeat.StartTime.Local().Sub(scheduleTime) + startDuration := startTime.Local().Sub(scheduleTime) if startDuration.Seconds() < 0 { return schedulerReconcile.createEngineRepeat(scheduler) From 4d190a1044cc5a47831511d6c040d408f33fcd74 Mon Sep 17 00:00:00 2001 From: Sanjay Nathani Date: Sun, 13 Sep 2020 08:31:02 +0530 Subject: [PATCH 2/2] updating crd and removing instanceCount field from spec Signed-off-by: Sanjay Nathani --- deploy/crds/chaosschedule_cr.yaml | 17 +- deploy/crds/chaosschedule_crd.yaml | 240 ++++++++++++++---- go.mod | 2 + go.sum | 20 ++ .../v1alpha1/chaosschedule_types.go | 38 ++- .../v1alpha1/zz_generated.deepcopy.go | 5 +- .../createEngineForNowAndOnce.go | 1 - .../chaosscheduler/createEngineForRepeat.go | 62 ++--- 8 files changed, 263 insertions(+), 122 deletions(-) diff --git a/deploy/crds/chaosschedule_cr.yaml b/deploy/crds/chaosschedule_cr.yaml index 85fd906c..80fa88ef 100644 --- a/deploy/crds/chaosschedule_cr.yaml +++ b/deploy/crds/chaosschedule_cr.yaml @@ -4,13 +4,16 @@ metadata: name: schedule-nginx spec: schedule: - type: "now" - executionTime: "2020-05-11T20:30:00Z" #should be set for type=once - startTime: "2020-05-12T05:47:00Z" #should be modified according to current UTC Time, for type=repeat - endTime: "2020-05-12T05:52:00Z" #should be modified according to current UTC Time, for type=repeat - minChaosInterval: "2m" #format should be like "10m" or "2h" accordingly for minutes and hours, for type=repeat - instanceCount: "2" #should be set for type=repeat - includedDays: "mon,tue,wed" #should be set for type=repeat + repeat: + timeRange: +# startTime: "2020-05-12T05:47:00Z" #should be modified according to current UTC Time, for type=repeat + endTime: "2020-09-13T02:58:00Z" #should be modified according to current UTC Time, for type=repeat + properties: + minChaosInterval: "2m" #format should be like "10m" or "2h" accordingly for minutes and hours, for type=repeat + workHours: + includedHours: 0-12 + workDays: + includedDays: "Mon,Tue,Wed,Sat,Sun" #should be set for type=repeat engineTemplateSpec: appinfo: appns: 'default' diff --git a/deploy/crds/chaosschedule_crd.yaml b/deploy/crds/chaosschedule_crd.yaml index d0a5262a..a1973e31 100644 --- a/deploy/crds/chaosschedule_crd.yaml +++ b/deploy/crds/chaosschedule_crd.yaml @@ -29,88 +29,212 @@ spec: spec: type: object properties: - chaosServiceAccount: - type: string engineTemplateSpec: + type: object properties: + monitoring: + type: boolean + jobCleanUpPolicy: + type: string + pattern: "^(delete|retain)$" annotationCheck: - pattern: ^(true|false)$ type: string + pattern: "^(true|false)$" appinfo: + type: object properties: appkind: - pattern: ^(deployment|statefulset|daemonset)$ type: string + pattern: "^(deployment|statefulset|daemonset|deploymentconfig)$" applabel: + pattern: "([a-z0-9A-Z_\\.-/]+)=([a-z0-9A-Z_\\.-/_]+)" type: string appns: type: string - type: object auxiliaryAppInfo: type: string + engineState: + type: string + pattern: "^(active|stop|initialized|stopped)$" chaosServiceAccount: type: string components: + type: object properties: - monitor: - properties: - image: - type: string - type: object runner: + type: object properties: image: type: string type: - pattern: ^(go|ansible)$ type: string - type: object - type: object + pattern: "^(go)$" + runnerannotation: + type: object + additionalProperties: + type: string + properties: + key: + type: string + minLength: 1 + allowEmptyValue: false + value: + type: string + minLength: 1 + allowEmptyValue: false experiments: + type: array items: + type: object properties: name: type: string spec: + type: object properties: - components: - properties: - configMaps: - items: + k8sProbe: + type: array + items: + type: object + properties: + name: + type: string + inputs: + type: object properties: - mountPath: + command: + type: object + properties: + group: + type: string + version: + type: string + resource: + type: string + namespace: + type: string + fieldSelector: + type: string + expectedResult: type: string - name: + runProperties: + type: object + properties: + probeTimeout: + type: integer + interval: + type: integer + retry: + type: integer + mode: + type: string + cmdProbe: + type: array + items: + type: object + properties: + name: + type: string + inputs: + type: object + properties: + command: + type: string + expectedResult: type: string + source: + type: string + runProperties: type: object - type: array + properties: + probeTimeout: + type: integer + interval: + type: integer + retry: + type: integer + mode: + type: string + httpProbe: + type: array + items: + type: object + properties: + name: + type: string + inputs: + type: object + properties: + url: + type: string + expectedResponseCode: + type: string + runProperties: + type: object + properties: + probeTimeout: + type: integer + interval: + type: integer + retry: + type: integer + mode: + type: string + components: + type: object + properties: + statusCheckTimeouts: + type: object + properties: + delay: + type: integer + timeout: + type: integer + nodeSelector: + type: object + minLength: 1 + experimentImage: + type: string env: + type: array items: + type: object properties: name: type: string value: type: string - type: object + configMaps: type: array - secrets: items: + type: object properties: + name: + type: string mountPath: type: string + secrets: + type: array + items: + type: object + properties: name: type: string - type: object - type: array - type: object - type: object - type: object - type: array - jobCleanUpPolicy: - pattern: ^(delete|retain)$ - type: string - monitoring: - type: boolean + mountPath: + type: string + experimentannotation: + type: object + additionalProperties: + type: string + properties: + key: + type: string + minLength: 1 + allowEmptyValue: false + value: + type: string + minLength: 1 + allowEmptyValue: false schedule: oneOf: - required: @@ -130,21 +254,43 @@ spec: type: object repeat: properties: - endTime: - format: date-time - type: date - includedDays: - pattern: ((Mon|Tue|Wed|Thu|Fri|Sat|Sun)(,))*(Mon|Tue|Wed|Thu|Fri|Sat|Sun) - type: string - instances: - type: number - minChaosInterval: - pattern: (([1-6][0-9]|[1-9])m)|(\d+h) - type: string - startTime: - format: date-time - type: date + timeRange: + properties: + endTime: + format: date-time + type: date + startTime: + format: date-time + type: date + type: object + workkHours: + properties: + includedHours: + type: string + type: object + required: + - includedHours + workDays: + properties: + includedDays: + pattern: ((Mon|Tue|Wed|Thu|Fri|Sat|Sun)(,))*(Mon|Tue|Wed|Thu|Fri|Sat|Sun) + type: string + type: object + required: + - includedDays + properties: + properties: + minChaosInterval: + pattern: (([1-6][0-9]|[1-9])m)|(\d+h) + type: string + random: + type: boolean + type: object + required: + - minChaosInterval type: object + required: + - properties type: object status: type: object diff --git a/go.mod b/go.mod index 5fe3936c..76e0c50d 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/operator-framework/operator-sdk v0.15.2 github.com/robfig/cron/v3 v3.0.1 github.com/spf13/pflag v1.0.5 + golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect + golang.org/x/tools v0.0.0-20200911193555-6422fca01df9 // indirect k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/client-go v12.0.0+incompatible diff --git a/go.sum b/go.sum index 5be2f321..5aeea5bd 100644 --- a/go.sum +++ b/go.sum @@ -649,6 +649,7 @@ github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.6/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= @@ -683,8 +684,11 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow= golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -694,9 +698,14 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -727,6 +736,8 @@ golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -739,6 +750,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -769,6 +781,7 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE= golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -802,8 +815,15 @@ golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a h1:UuQ+70Pi/ZdWHuP4v457pkXeOynTdgd/4enxeIO/98k= golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200911193555-6422fca01df9 h1:M8mz5B5dntyYZSIscpClTVyMIJbg6kKKIyysZM++kf8= +golang.org/x/tools v0.0.0-20200911193555-6422fca01df9/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= diff --git a/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go b/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go index f5f19d09..cdca313f 100644 --- a/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go +++ b/pkg/apis/litmuschaos/v1alpha1/chaosschedule_types.go @@ -17,11 +17,11 @@ type ChaosScheduleSpec struct { // Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file // Add custom validation using kube-builder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html // ChaosServiceAccount is the SA specified for chaos runner pods - ChaosServiceAccount string `json:"chaosServiceAccount"` + ChaosServiceAccount string `json:"chaosServiceAccount,omitempty"` // Execution schedule of batch of chaos experiments - Schedule Schedule `json:"schedule"` + Schedule Schedule `json:"schedule,omitempty"` // ScheduleState determines whether to "halt", "abort" or "active" the schedule - ScheduleState ScheduleState `json:"scheduleState"` + ScheduleState ScheduleState `json:"scheduleState,omitempty"` // TODO // ConcurrencyPolicy will state whether two engines from the same schedule // can exist simultaneously or not @@ -79,17 +79,15 @@ const ( //ScheduleStatus describes the overall status of the schedule type ScheduleStatus struct { //Status defines the current running status of the schedule - Status ChaosStatus `json:"status"` + Status ChaosStatus `json:"status,omitempty"` //StartTime defines the starting timestamp of the schedule StartTime *metav1.Time `json:"startTime,omitempty"` //EndTime defines the end timestamp of the schedule EndTime *metav1.Time `json:"endTime,omitempty"` - //TotalInstances defines the total no. of instances to be executed - TotalInstances int `json:"totalInstances,omitempty"` //RunInstances defines number of already ran instances at that point of time RunInstances int `json:"runInstances,omitempty"` //ExpectedNextRunTime defines the approximate time at which execution of the next instance will take place - ExpectedNextRunTime metav1.Time `json:"expectedNextRunTime,omitempty"` + ExpectedNextRunTime *metav1.Time `json:"expectedNextRunTime,omitempty"` } // ChaosScheduleStatus derives information about status of individual experiments @@ -101,9 +99,9 @@ type ChaosScheduleStatus struct { // Schedule depicts status of the schedule whether active, aborted or halted Schedule ScheduleStatus `json:"schedule,omitempty"` // LastScheduleTime states the last time an engine was created - LastScheduleTime *metav1.Time + LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` // Active states the list of chaosengines that are currently running - Active []coreV1.ObjectReference + Active []coreV1.ObjectReference `json:"active,omitempty"` } // Schedule defines information about schedule of chaos batch run @@ -124,40 +122,38 @@ type ScheduleOnce struct { // ScheduleRepeat will contain parameters for executing the repeat strategy of scheduling type ScheduleRepeat struct { - TimeRange *TimeRange `json:"timeRange"` - Properties ScheduleRepeatProperties `json:"properties"` - WorkHours *WorkHours `json:"workHours"` - WorkDays *WorkDays `json:"workDays"` + TimeRange *TimeRange `json:"timeRange,omitempty"` + Properties ScheduleRepeatProperties `json:"properties,omitempty"` + WorkHours *WorkHours `json:"workHours,omitempty"` + WorkDays *WorkDays `json:"workDays,omitempty"` } //TimeRange will contain time constraints for the chaos to be injected type TimeRange struct { //Start limit of the time range in which experiment is to be run - StartTime *metav1.Time `json:"startTime"` + StartTime *metav1.Time `json:"startTime,omitempty"` //End limit of the time range in which experiment is to be run - EndTime *metav1.Time `json:"endTime"` + EndTime *metav1.Time `json:"endTime,omitempty"` } //ScheduleRepeatProperties will define the properties needed by the schedule to inject chaos type ScheduleRepeatProperties struct { //Minimum Period b/w two iterations of chaos experiments batch run - MinChaosInterval string `json:"minChaosInterval"` - //Number of Instances of the experiment to be run within a given time range - InstanceCount string `json:"instanceCount"` + MinChaosInterval string `json:"minChaosInterval,omitempty"` //Whether the chaos is to be scheduled at a random time or not - Random bool `json:"random"` + Random bool `json:"random,omitempty"` } //WorkHours specify in which hours of the day chaos is to be injected type WorkHours struct { //Hours of the day when experiments batch run is scheduled - IncludedHours string `json:"includedHours"` + IncludedHours string `json:"includedHours,omitempty"` } //WorkDays specify in which hours of the day chaos is to be injected type WorkDays struct { //Days of week when experiments batch run is scheduled - IncludedDays string `json:"includedDays"` + IncludedDays string `json:"includedDays,omitempty"` } // +genclient diff --git a/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go index c3c152bf..2caa59d6 100644 --- a/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/litmuschaos/v1alpha1/zz_generated.deepcopy.go @@ -232,7 +232,10 @@ func (in *ScheduleStatus) DeepCopyInto(out *ScheduleStatus) { in, out := &in.EndTime, &out.EndTime *out = (*in).DeepCopy() } - in.ExpectedNextRunTime.DeepCopyInto(&out.ExpectedNextRunTime) + if in.ExpectedNextRunTime != nil { + in, out := &in.ExpectedNextRunTime, &out.ExpectedNextRunTime + *out = (*in).DeepCopy() + } return } diff --git a/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go b/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go index 071852de..358f8d35 100644 --- a/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go +++ b/pkg/controller/chaosscheduler/createEngineForNowAndOnce.go @@ -43,7 +43,6 @@ func (schedulerReconcile *reconcileScheduler) createForNowAndOnce(cs *chaosTypes schedulerReconcile.r.recorder.Eventf(cs.Instance, corev1.EventTypeNormal, "SuccessfulCreate", "Created engine %v", engine.Name) cs.Instance.Spec.ScheduleState = schedulerV1.StateActive cs.Instance.Status.Schedule.Status = schedulerV1.StatusRunning - cs.Instance.Status.Schedule.TotalInstances = 1 cs.Instance.Status.Schedule.StartTime = ¤tTime cs.Instance.Status.LastScheduleTime = ¤tTime ref, errRef := schedulerReconcile.r.getRef(engine) diff --git a/pkg/controller/chaosscheduler/createEngineForRepeat.go b/pkg/controller/chaosscheduler/createEngineForRepeat.go index 6b4b8285..dc8acd05 100644 --- a/pkg/controller/chaosscheduler/createEngineForRepeat.go +++ b/pkg/controller/chaosscheduler/createEngineForRepeat.go @@ -25,6 +25,7 @@ func (schedulerReconcile *reconcileScheduler) createEngineRepeat(cs *chaosTypes. } if errUpdate := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); errUpdate != nil { + schedulerReconcile.reqLogger.Info("schedule ", "instance", cs.Instance) schedulerReconcile.reqLogger.Error(errUpdate, "error updating status") return reconcile.Result{}, errUpdate } @@ -37,7 +38,7 @@ func (schedulerReconcile *reconcileScheduler) createEngineRepeat(cs *chaosTypes. schedulerReconcile.reqLogger.Info("end time already passed", "endTime", endTime) cs.Instance.Spec.ScheduleState = schedulerV1.StateCompleted if errUpdate := schedulerReconcile.r.client.Update(context.TODO(), cs.Instance); errUpdate != nil { - return reconcile.Result{}, errUpdate + return reconcile.Result{Requeue: true}, errUpdate } return reconcile.Result{}, nil } @@ -49,7 +50,7 @@ func (schedulerReconcile *reconcileScheduler) createEngineRepeat(cs *chaosTypes. return reconcile.Result{}, nil } - cronString, duration, err := scheduleRepeat(cs) + cronString, duration, err := schedulerReconcile.scheduleRepeat(cs) if err != nil { return reconcile.Result{}, err } @@ -147,10 +148,12 @@ func getRecentUnmetScheduleTime(cs *chaosTypes.SchedulerInfo, cronString string) earliestTime = cs.Instance.Status.LastScheduleTime.Time } else if timeRange != nil && timeRange.StartTime != nil { // If none found, then this is either a recently created schedule, - // or the active/completed info was somehow lost (contract for status - // in kubernetes says it may need to be recreated), or that we have - // started a engine, but have not noticed it yet (distributed systems can - // have arbitrary delays). + // or the active/completed info was somehow lost (it may need to be recreated), + // or that we have started a engine, but have not noticed it yet (distributed + // systems can have arbitrary delays). + // Since we are checking the startTime with currentTime. It is + // possible that we might miss a schedule at the TIME of CREATION + // by just a few seconds because of the slight delay in reconciliation process. earliestTime = timeRange.StartTime.Time.Add(time.Minute * -1) } else { earliestTime = cs.Instance.GetCreationTimestamp().Time @@ -169,22 +172,16 @@ func getRecentUnmetScheduleTime(cs *chaosTypes.SchedulerInfo, cronString string) return previousTime, nil } -func scheduleRepeat(cs *chaosTypes.SchedulerInfo) (string, time.Duration, error) { +func (schedulerReconcile *reconcileScheduler) scheduleRepeat(cs *chaosTypes.SchedulerInfo) (string, time.Duration, error) { interval, err := fetchInterval(cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval) if err != nil { return "", time.Duration(0), errors.New("error in parsing minChaosInterval(make sure to include 'm' or 'h' suffix for minutes and hours respectively)") } - instances, err := fetchInstances(cs.Instance.Spec.Schedule.Repeat.Properties.InstanceCount) - if err != nil { - return "", time.Duration(0), errors.New("error in parsing instanceCount") - } var startTime *metav1.Time - var endTime *metav1.Time if cs.Instance.Spec.Schedule.Repeat.TimeRange != nil { startTime = cs.Instance.Spec.Schedule.Repeat.TimeRange.StartTime - endTime = cs.Instance.Spec.Schedule.Repeat.TimeRange.EndTime } if startTime == nil { @@ -215,34 +212,16 @@ func scheduleRepeat(cs *chaosTypes.SchedulerInfo) (string, time.Duration, error) /* MinChaosInterval will be in form of "10m" or "2h" * where 'm' or 'h' indicating "minutes" or "hours" respectively */ - // cs.Instance.Status.Schedule.TotalInstances, err = getTotalInstances(cs) - // if err != nil { - // return "", err - // } if strings.Contains(cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval, "m") { - return fmt.Sprintf("*/%d %s * * %s", interval, includedHours, includedDays), time.Minute * time.Duration(interval), nil + cron := fmt.Sprintf("*/%d %s * * %s", interval, includedHours, includedDays) + schedulerReconcile.reqLogger.Info("CronString formed ", "Cron String", cron) + return cron, time.Minute * time.Duration(interval), nil } - return fmt.Sprintf("* %s/%d * * %s", includedHours, interval, includedDays), time.Hour * time.Duration(interval), nil - } else if instances != 0 && endTime != nil { - - cs.Instance.Status.Schedule.TotalInstances = instances - duration := endTime.Sub(startTime.UTC()) - //schedule at the end time will not be able to schedule so increaing the no. of instance - intervalHours := duration.Hours() / float64(instances) - intervalMinutes := duration.Minutes() / float64(instances) - - if intervalHours >= 1 { - // to be sent in form of EnvVariable to executor - cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval = fmt.Sprintf("%dh", int(intervalHours)) - return fmt.Sprintf("* %s/%d * * %s", includedHours, int(intervalHours), includedDays), time.Hour * time.Duration(intervalHours), nil - } else if intervalMinutes >= 1 { - // to be sent in form of EnvVariable to executor - cs.Instance.Spec.Schedule.Repeat.Properties.MinChaosInterval = fmt.Sprintf("%dm", int(intervalMinutes)) - return fmt.Sprintf("*/%d %s * * %s", int(intervalMinutes), includedHours, includedDays), time.Minute * time.Duration(intervalMinutes), nil - } - return "", time.Duration(0), errors.New("Too many instances to execute") + cron := fmt.Sprintf("* %s/%d * * %s", includedHours, interval, includedDays) + schedulerReconcile.reqLogger.Info("CronString formed ", "Cron String", cron) + return cron, time.Hour * time.Duration(interval), nil } - return "", time.Duration(0), errors.New("MinChaosInterval and InstanceCount with combination of endTime not found") + return "", time.Duration(0), errors.New("MinChaosInterval not found") } func fetchInterval(minChaosInterval string) (int, error) { @@ -259,13 +238,6 @@ func fetchInterval(minChaosInterval string) (int, error) { return 0, errors.New("minChaosInterval should be in either minutes or hours and the prefix should be 'm' or 'h' respectively") } -func fetchInstances(instanceCount string) (int, error) { - if instanceCount == "" { - return 0, nil - } - return strconv.Atoi(instanceCount) -} - // getTimeHash returns Unix Epoch Time func getTimeHash(scheduledTime time.Time) int64 { return scheduledTime.Unix()