-
Notifications
You must be signed in to change notification settings - Fork 810
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add stress chaos type Co-authored-by: AngleNet <AngleNet@users.noreply.github.com>
- Loading branch information
Showing
28 changed files
with
2,724 additions
and
258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
// Copyright 2020 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1alpha1 | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/docker/go-units" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// Stress chaos is a chaos to generate plenty of stresses over a collection of pods. | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// StressChaos is the Schema for the stresschaos API | ||
type StressChaos struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
// Spec defines the behavior of a time chaos experiment | ||
Spec StressChaosSpec `json:"spec"` | ||
|
||
// +optional | ||
// Most recently observed status of the time chaos experiment | ||
Status StressChaosStatus `json:"status"` | ||
} | ||
|
||
// StressChaosSpec defines the desired state of StressChaos | ||
type StressChaosSpec struct { | ||
// Mode defines the mode to run chaos action. | ||
// Supported mode: one / all / fixed / fixed-percent / random-max-percent | ||
Mode PodMode `json:"mode"` | ||
|
||
// Value is required when the mode is set to `FixedPodMode` / `FixedPercentPodMod` / `RandomMaxPercentPodMod`. | ||
// If `FixedPodMode`, provide an integer of pods to do chaos action. | ||
// If `FixedPercentPodMod`, provide a number from 0-100 to specify the max % of pods the server can do chaos action. | ||
// If `RandomMaxPercentPodMod`, provide a number from 0-100 to specify the % of pods to do chaos action | ||
// +optional | ||
Value string `json:"value"` | ||
|
||
// Selector is used to select pods that are used to inject chaos action. | ||
Selector SelectorSpec `json:"selector"` | ||
|
||
// Stressors defines plenty of stressors supported to stress system components out. | ||
// You can use one or more of them to make up various kinds of stresses. At least | ||
// one of the stressors should be specified. | ||
// +optional | ||
Stressors *Stressors `json:"stressors,omitempty"` | ||
|
||
// StressngStressors defines plenty of stressors just like `Stressors` except that it's an experimental | ||
// feature and more powerful. You can define stressors in `stress-ng` (see also `man stress-ng`) dialect, | ||
// however not all of the supported stressors are well tested. It maybe retired in later releases. You | ||
// should always use `Stressors` to define the stressors and use this only when you want more stressors | ||
// unsupported by `Stressors`. When both `StressngStressors` and `Stressors` are defined, `StressngStressors` | ||
// wins. | ||
// +optional | ||
StressngStressors string `json:"stressngStressors,omitempty"` | ||
|
||
// Duration represents the duration of the chaos action | ||
// +optional | ||
Duration *string `json:"duration,omitempty"` | ||
|
||
// Scheduler defines some schedule rules to control the running time of the chaos experiment about time. | ||
// +optional | ||
Scheduler *SchedulerSpec `json:"scheduler,omitempty"` | ||
|
||
// Next time when this action will be applied again | ||
// +optional | ||
NextStart *metav1.Time `json:"nextStart,omitempty"` | ||
|
||
// Next time when this action will be recovered | ||
// +optional | ||
NextRecover *metav1.Time `json:"nextRecover,omitempty"` | ||
|
||
// If this chaos has been paused | ||
// +optional | ||
Paused bool `json:"paused"` | ||
} | ||
|
||
// GetSelector is a getter for Selector (for implementing SelectSpec) | ||
func (in *StressChaosSpec) GetSelector() SelectorSpec { | ||
return in.Selector | ||
} | ||
|
||
// GetMode is a getter for Mode (for implementing SelectSpec) | ||
func (in *StressChaosSpec) GetMode() PodMode { | ||
return in.Mode | ||
} | ||
|
||
// GetValue is a getter for Value (for implementing SelectSpec) | ||
func (in *StressChaosSpec) GetValue() string { | ||
return in.Value | ||
} | ||
|
||
// StressChaosStatus defines the observed state of StressChaos | ||
type StressChaosStatus struct { | ||
ChaosStatus `json:",inline"` | ||
// Instances always specifies stressing instances | ||
// +optional | ||
Instances map[string]StressInstance `json:"instances,omitempty"` | ||
} | ||
|
||
// StressInstance is an instance generates stresses | ||
type StressInstance struct { | ||
// UID is the instance identifier | ||
// +optional | ||
UID string `json:"uid"` | ||
// StartTime specifies when the instance starts | ||
// +optional | ||
StartTime *metav1.Time `json:"startTime"` | ||
} | ||
|
||
// GetDuration gets the duration of StressChaos | ||
func (in *StressChaos) GetDuration() (*time.Duration, error) { | ||
if in.Spec.Duration == nil { | ||
return nil, nil | ||
} | ||
duration, err := time.ParseDuration(*in.Spec.Duration) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &duration, nil | ||
} | ||
|
||
// GetNextStart gets NextStart field of StressChaos | ||
func (in *StressChaos) GetNextStart() time.Time { | ||
if in.Spec.NextStart == nil { | ||
return time.Time{} | ||
} | ||
return in.Spec.NextStart.Time | ||
} | ||
|
||
// SetNextStart sets NextStart field of StressChaos | ||
func (in *StressChaos) SetNextStart(t time.Time) { | ||
if t.IsZero() { | ||
in.Spec.NextStart = nil | ||
return | ||
} | ||
|
||
if in.Spec.NextStart == nil { | ||
in.Spec.NextStart = &metav1.Time{} | ||
} | ||
in.Spec.NextStart.Time = t | ||
} | ||
|
||
// GetNextRecover get NextRecover field of StressChaos | ||
func (in *StressChaos) GetNextRecover() time.Time { | ||
if in.Spec.NextRecover == nil { | ||
return time.Time{} | ||
} | ||
return in.Spec.NextRecover.Time | ||
} | ||
|
||
// SetNextRecover sets NextRecover field of StressChaos | ||
func (in *StressChaos) SetNextRecover(t time.Time) { | ||
if t.IsZero() { | ||
in.Spec.NextRecover = nil | ||
return | ||
} | ||
|
||
if in.Spec.NextRecover == nil { | ||
in.Spec.NextRecover = &metav1.Time{} | ||
} | ||
in.Spec.NextRecover.Time = t | ||
} | ||
|
||
// GetScheduler returns the scheduler of StressChaos | ||
func (in *StressChaos) GetScheduler() *SchedulerSpec { | ||
return in.Spec.Scheduler | ||
} | ||
|
||
// GetStatus returns the status of StressChaos | ||
func (in *StressChaos) GetStatus() *ChaosStatus { | ||
return &in.Status.ChaosStatus | ||
} | ||
|
||
// IsDeleted returns whether this resource has been deleted | ||
func (in *StressChaos) IsDeleted() bool { | ||
return !in.DeletionTimestamp.IsZero() | ||
} | ||
|
||
// IsPaused returns whether this resource has been paused | ||
func (in *StressChaos) IsPaused() bool { | ||
return in.Spec.Paused | ||
} | ||
|
||
// Stressors defines plenty of stressors supported to stress system components out. | ||
// You can use one or more of them to make up various kinds of stresses | ||
type Stressors struct { | ||
// MemoryStressor stresses virtual memory out | ||
// +optional | ||
MemoryStressor *MemoryStressor `json:"memory,omitempty"` | ||
// CPUStressor stresses CPU out | ||
// +optional | ||
CPUStressor *CPUStressor `json:"cpu,omitempty"` | ||
} | ||
|
||
// Normalize the stressors to comply with stress-ng | ||
func (in *Stressors) Normalize() (string, error) { | ||
stressors := "" | ||
if in.MemoryStressor != nil { | ||
stressors += fmt.Sprintf(" --vm %d --vm-keep", in.MemoryStressor.Workers) | ||
if len(in.MemoryStressor.Size) != 0 { | ||
if in.MemoryStressor.Size[len(in.MemoryStressor.Size)-1] != '%' { | ||
size, err := units.FromHumanSize(in.MemoryStressor.Size) | ||
if err != nil { | ||
return "", err | ||
} | ||
stressors += fmt.Sprintf(" --vm-bytes %d", size) | ||
} else { | ||
stressors += fmt.Sprintf("--vm-bytes %s", | ||
in.MemoryStressor.Size) | ||
} | ||
} | ||
} | ||
if in.CPUStressor != nil { | ||
stressors += fmt.Sprintf(" --cpu %d", in.CPUStressor.Workers) | ||
if in.CPUStressor.Load != nil { | ||
stressors += fmt.Sprintf(" --cpu-load %d", | ||
*in.CPUStressor.Load) | ||
} | ||
} | ||
return stressors, nil | ||
} | ||
|
||
// Stressor defines common configurations of a stressor | ||
type Stressor struct { | ||
// Workers specifies N workers to apply the stressor. | ||
Workers int `json:"workers"` | ||
} | ||
|
||
// MemoryStressor defines how to stress memory out | ||
type MemoryStressor struct { | ||
Stressor `json:",inline"` | ||
|
||
// Size specifies N bytes consumed per vm worker, default is the total available memory. | ||
// One can specify the size as % of total available memory or in units of B, KB/KiB, | ||
// MB/MiB, GB/GiB, TB/TiB. | ||
// +optional | ||
Size string `json:"size,omitempty"` | ||
} | ||
|
||
// CPUStressor defines how to stress CPU out | ||
type CPUStressor struct { | ||
Stressor `json:",inline"` | ||
// Load specifies P percent loading per CPU worker. 0 is effectively a sleep (no load) and 100 | ||
// is full loading. | ||
// +optional | ||
Load *int `json:"load,omitempty"` | ||
} | ||
|
||
// +kubebuilder:object:root=true | ||
|
||
// StressChaosList contains a list of StressChaos | ||
type StressChaosList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []StressChaos `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&StressChaos{}, &StressChaosList{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright 2020 PingCAP, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1alpha1 | ||
|
||
import ( | ||
"context" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/types" | ||
) | ||
|
||
// These tests are written in BDD-style using Ginkgo framework. Refer to | ||
// http://onsi.github.io/ginkgo to learn more. | ||
var _ = Describe("StressChaos", func() { | ||
|
||
Context("CRUD API", func() { | ||
var ( | ||
key types.NamespacedName | ||
created, fetched *StressChaos | ||
) | ||
|
||
It("Should create an object successfully", func() { | ||
key = types.NamespacedName{ | ||
Name: "foo", | ||
Namespace: "default", | ||
} | ||
created = &StressChaos{ | ||
ObjectMeta: v1.ObjectMeta{ | ||
Name: "foo", | ||
Namespace: "default", | ||
}, | ||
Spec: StressChaosSpec{ | ||
Stressors: &Stressors{MemoryStressor: &MemoryStressor{Stressor: Stressor{Workers: 1}}}, | ||
}, | ||
} | ||
By("creating an API object") | ||
Expect(k8sClient.Create(context.TODO(), created)).To(Succeed()) | ||
|
||
fetched = &StressChaos{} | ||
Expect(k8sClient.Get(context.TODO(), key, fetched)).To(Succeed()) | ||
Expect(fetched).To(Equal(created)) | ||
|
||
By("deleting the created object") | ||
Expect(k8sClient.Delete(context.TODO(), created)).To(Succeed()) | ||
Expect(k8sClient.Get(context.TODO(), key, created)).NotTo(Succeed()) | ||
}) | ||
}) | ||
|
||
}) |
Oops, something went wrong.