Skip to content

feature: add container-index flag for container chaos #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 65 additions & 44 deletions exec/container/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package container
import (
"context"
"fmt"
"strconv"
"strings"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -50,79 +51,99 @@ func (*ExpController) Name() string {

// Create an experiment about container
func (e *ExpController) Create(ctx context.Context, expSpec v1alpha1.ExperimentSpec) *spec.Response {
expModel, ctx, response := e.convert(ctx, expSpec)
if !response.Success {
return response
}
return e.Exec(ctx, expModel)
}

func (e *ExpController) convert(ctx context.Context, expSpec v1alpha1.ExperimentSpec) (*spec.ExpModel, context.Context, *spec.Response) {
expModel := model.ExtractExpModelFromExperimentSpec(expSpec)
containerIdsValue := expModel.ActionFlags[model.ContainerIdsFlag.Name]
containerNamesValue := expModel.ActionFlags[model.ContainerNamesFlag.Name]
// priority id > name > index
containerIdsValue := strings.TrimSpace(expModel.ActionFlags[model.ContainerIdsFlag.Name])
containerNamesValue := strings.TrimSpace(expModel.ActionFlags[model.ContainerNamesFlag.Name])
containerIndexValue := strings.TrimSpace(expModel.ActionFlags[model.ContainerIndexFlag.Name])

if containerIdsValue == "" && containerNamesValue == "" {
errMsg := fmt.Sprintf("less %s or %s flag", model.ContainerIdsFlag.Name, model.ContainerNamesFlag.Name)
return spec.ReturnFailWitResult(spec.Code[spec.IllegalParameters], errMsg,
v1alpha1.CreateFailExperimentStatus(errMsg, nil))
}
if containerIdsValue != "" && containerNamesValue != "" {
errMsg := fmt.Sprintf("only one choice between %s and %s", model.ContainerIdsFlag.Name, model.ContainerNamesFlag.Name)
return spec.ReturnFailWitResult(spec.Code[spec.IllegalParameters], errMsg,
if containerIdsValue == "" && containerNamesValue == "" && containerIndexValue == "" {
errMsg := fmt.Sprintf("must specify one flag in %s %s %s",
model.ContainerIdsFlag.Name, model.ContainerNamesFlag.Name, model.ContainerIndexFlag.Name)
return nil, nil, spec.ReturnFailWitResult(spec.Code[spec.IllegalParameters], errMsg,
v1alpha1.CreateFailExperimentStatus(errMsg, nil))
}
pods, err := e.GetMatchedPodResources(*expModel)
if err != nil {
return spec.ReturnFailWitResult(spec.Code[spec.IgnoreCode], err.Error(),
return nil, nil, spec.ReturnFailWitResult(spec.Code[spec.IgnoreCode], err.Error(),
v1alpha1.CreateFailExperimentStatus(err.Error(), nil))
}
if len(pods) == 0 {
errMsg := "cannot find the target pods for container resource"
return spec.ReturnFailWitResult(spec.Code[spec.IgnoreCode], errMsg,
v1alpha1.CreateFailExperimentStatus(errMsg, nil))
return nil, nil, spec.ReturnFailWitResult(spec.Code[spec.IgnoreCode], err.Error(),
v1alpha1.CreateFailExperimentStatus("cannot find the target pods for container resource", nil))
}
ctx = setNecessaryObjectsToContext(ctx, pods, containerIdsValue, containerNamesValue)
return e.Exec(ctx, expModel)
ctx, err = setNecessaryObjectsToContext(ctx, pods, containerIdsValue, containerNamesValue, containerIndexValue)
if err != nil {
return nil, nil, spec.ReturnFailWitResult(spec.Code[spec.IllegalParameters], err.Error(),
v1alpha1.CreateFailExperimentStatus(err.Error(), nil))
}
return expModel, ctx, spec.ReturnSuccess("")
}

// setNecessaryObjectsToContext which will be used in the executor
func setNecessaryObjectsToContext(ctx context.Context, pods []v1.Pod,
containerIdsValue, containerNamesValue string) context.Context {
containerIdsValue, containerNamesValue, containerIndexValue string) (context.Context, error) {
nodeNameContainerObjectMetasMaps := model.NodeNameContainerObjectMetasMap{}
nodeNameUidMap := model.NodeNameUidMap{}
expectedContainerIds := strings.Split(containerIdsValue, ",")
expectedContainerNames := strings.Split(containerNamesValue, ",")
for _, pod := range pods {
containerStatuses := pod.Status.ContainerStatuses
if containerStatuses != nil {
for _, containerStatus := range containerStatuses {
containerId := model.TruncateContainerObjectMetaUid(containerStatus.ContainerID)
containerName := containerStatus.Name
if len(containerIdsValue) > 0 {
for _, expectedContainerId := range expectedContainerIds {
if expectedContainerId == "" {
continue
}
if strings.HasPrefix(containerId, expectedContainerId) {
// matched
nodeNameUidMap, nodeNameContainerObjectMetasMaps =
AddMatchedContainerAndNode(pod, containerId, containerName,
nodeNameContainerObjectMetasMaps, nodeNameUidMap)
}
if containerStatuses == nil {
continue
}
for _, containerStatus := range containerStatuses {
containerId := model.TruncateContainerObjectMetaUid(containerStatus.ContainerID)
containerName := containerStatus.Name
if containerIdsValue != "" {
for _, expectedContainerId := range expectedContainerIds {
if expectedContainerId == "" {
continue
}
if strings.HasPrefix(containerId, expectedContainerId) {
nodeNameUidMap, nodeNameContainerObjectMetasMaps =
AddMatchedContainerAndNode(pod, containerId, containerName,
nodeNameContainerObjectMetasMaps, nodeNameUidMap)
}
}
if len(containerNamesValue) > 0 {
for _, expectedName := range expectedContainerNames {
if expectedName == "" {
continue
}
if expectedName == containerName {
// matched
nodeNameUidMap, nodeNameContainerObjectMetasMaps =
AddMatchedContainerAndNode(pod, containerId, containerName,
nodeNameContainerObjectMetasMaps, nodeNameUidMap)
}
} else if containerNamesValue != "" {
for _, expectedName := range expectedContainerNames {
if expectedName == "" {
continue
}
if expectedName == containerName {
// matched
nodeNameUidMap, nodeNameContainerObjectMetasMaps =
AddMatchedContainerAndNode(pod, containerId, containerName,
nodeNameContainerObjectMetasMaps, nodeNameUidMap)
}
}
}
}
if containerIdsValue == "" && containerNamesValue == "" && containerIndexValue != "" {
idx, err := strconv.Atoi(containerIndexValue)
if err != nil {
return ctx, err
}
if idx > len(containerStatuses)-1 {
return ctx, fmt.Errorf("%s value is out of bound", containerIndexValue)
}
nodeNameUidMap, nodeNameContainerObjectMetasMaps =
AddMatchedContainerAndNode(pod, model.TruncateContainerObjectMetaUid(containerStatuses[idx].ContainerID),
containerStatuses[idx].Name, nodeNameContainerObjectMetasMaps, nodeNameUidMap)
}
}
ctx = context.WithValue(ctx, model.NodeNameUidMapKey, nodeNameUidMap)
ctx = context.WithValue(ctx, model.NodeNameContainerObjectMetasMapKey, nodeNameContainerObjectMetasMaps)
return ctx
return ctx, nil
}

func AddMatchedContainerAndNode(pod v1.Pod, containerId, containerName string, nodeNameContainerObjectMetasMaps model.NodeNameContainerObjectMetasMap,
Expand Down
96 changes: 51 additions & 45 deletions exec/model/filter_pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,34 @@ func (b *BaseExperimentController) filterByOtherFlags(pods []v1.Pod, flags map[s

// resourceFunc is used to query the target resource
var resourceFunc = func(client2 *channel.Client, flags map[string]string) ([]v1.Pod, error) {
pods := make([]v1.Pod, 0)
namespace := flags[ResourceNamespaceFlag.Name]
// labels
labels := flags[ResourceLabelsFlag.Name]
if labels != "" {
labelArr := strings.Split(labels, ",")
labelMap := make(map[string]string, 0)
for _, label := range labelArr {
keyValue := strings.SplitN(label, "=", 2)
if len(keyValue) != 2 {
logrus.Warningf("label %s is illegal", label)
labelsMap := parseLabels(labels)

pods := make([]v1.Pod, 0)
names := flags[ResourceNamesFlag.Name]
if names != "" {
nameArr := strings.Split(names, ",")
for _, name := range nameArr {
pod := v1.Pod{}
err := client2.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: name}, &pod)
if err != nil {
logrus.Warningf("can not find the pod by %s name in %s namespace, %v", name, namespace, err)
continue
}
labelMap[keyValue[0]] = keyValue[1]
}
if len(labelMap) == 0 {
return pods, fmt.Errorf("illegal labels %s", labels)
if mapContains(pod.Labels, labelsMap) {
pods = append(pods, pod)
}
}
logrus.Infof("get pods by names %s, len is %d", names, len(pods))
return pods, nil
}
if labels != "" && len(labelsMap) == 0 {
return pods, fmt.Errorf("illegal labels %s", labels)
}
if len(labelsMap) > 0 {
podList := v1.PodList{}
opts := client.ListOptions{Namespace: namespace, LabelSelector: pkglabels.SelectorFromSet(labelMap)}
opts := client.ListOptions{Namespace: namespace, LabelSelector: pkglabels.SelectorFromSet(labelsMap)}
err := client2.List(context.TODO(), &podList, &opts)
if err != nil {
return pods, err
Expand All @@ -156,37 +164,6 @@ var resourceFunc = func(client2 *channel.Client, flags map[string]string) ([]v1.
pods = podList.Items
logrus.Infof("get pods by labels %s, len is %d", labels, len(pods))
}

podsWithName := make([]v1.Pod, 0)
// names
names := flags[ResourceNamesFlag.Name]
if names != "" {
nameArr := strings.Split(names, ",")
if len(pods) == 0 {
for _, name := range nameArr {
pod := v1.Pod{}
err := client2.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: name}, &pod)
if err != nil {
logrus.Warningf("can not find the pod by %s name in %s namespace, %v", name, namespace, err)
} else {
podsWithName = append(podsWithName, pod)
}
}
} else {
for _, pod := range pods {
for _, name := range nameArr {
if pod.Name == name {
podsWithName = append(podsWithName, pod)
}
}
}
}
logrus.Infof("get pods by names %s, len is %d", names, len(podsWithName))
if len(podsWithName) == 0 {
return podsWithName, nil
}
pods = podsWithName
}
return pods, nil
}

Expand All @@ -201,3 +178,32 @@ func randomPodSelected(pods []v1.Pod, count int) []v1.Pod {
}
return pods[:count]
}

func parseLabels(labels string) map[string]string {
labelsMap := make(map[string]string, 0)
if labels == "" {
return labelsMap
}
labelArr := strings.Split(labels, ",")
for _, label := range labelArr {
keyValue := strings.SplitN(label, "=", 2)
if len(keyValue) != 2 {
logrus.Warningf("label %s is illegal", label)
continue
}
labelsMap[keyValue[0]] = keyValue[1]
}
return labelsMap
}

func mapContains(bigMap map[string]string, subMap map[string]string) bool {
if bigMap == nil || subMap == nil {
return false
}
for k, v := range subMap {
if bigMap[k] != v {
return false
}
}
return true
}
10 changes: 9 additions & 1 deletion exec/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,16 @@ var ContainerNamesFlag = &spec.ExpFlag{
Required: false,
}

var ContainerIndexFlag = &spec.ExpFlag{
Name: "container-index",
Desc: "Container index, default value is 0",
}

func GetContainerFlags() []spec.ExpFlagSpec {
return []spec.ExpFlagSpec{
ContainerIdsFlag,
ContainerNamesFlag,
ContainerIndexFlag,
}
}

Expand All @@ -187,7 +193,9 @@ func GetResourceFlagNames() map[string]spec.Empty {
ResourceNamespaceFlag.Name,
ResourceLabelsFlag.Name,
ContainerIdsFlag.Name,
ContainerNamesFlag.Name}
ContainerNamesFlag.Name,
ContainerIndexFlag.Name,
}
names := make(map[string]spec.Empty, 0)
for _, name := range flagNames {
names[name] = spec.Empty{}
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module github.com/chaosblade-io/chaosblade-operator

require (
github.com/chaosblade-io/chaosblade-exec-docker v0.6.1-0.20200805071438-6d16a1434e52
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200805070637-52adf80fc207
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200713091457-d3932a4b0129
github.com/chaosblade-io/chaosblade-exec-docker v0.6.1-0.20200916065009-654d2ecba736
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200917023854-8bea9f1ae34f
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200917030038-f8260e80f639
github.com/ethercflow/hookfs v0.3.0
github.com/go-openapi/spec v0.19.4
github.com/hanwen/go-fuse v1.0.0
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ github.com/chaosblade-io/chaosblade-exec-docker v0.5.1-0.20200527093631-611adcd8
github.com/chaosblade-io/chaosblade-exec-docker v0.5.1-0.20200527093631-611adcd89267/go.mod h1:E93GonYtD77cR4cXCt7mKhirdPvMAogo0sRDFM0WDj4=
github.com/chaosblade-io/chaosblade-exec-docker v0.6.0 h1:QNjbelZWNYa0TMlUSMlOhecff1Eka0fwPLyDQnDI0C4=
github.com/chaosblade-io/chaosblade-exec-docker v0.6.0/go.mod h1:6Z73bRiLBV6PqIg7vzn7n06UJPoc4hVf9DmAtKD7ekk=
github.com/chaosblade-io/chaosblade-exec-docker v0.6.1-0.20200916065009-654d2ecba736 h1:u4xVjwpAlxWo22J+RqUV7M+aZGZHo0oZhTaAT9c9i64=
github.com/chaosblade-io/chaosblade-exec-docker v0.6.1-0.20200916065009-654d2ecba736/go.mod h1:1Ccya5wvvdk86s8udCN4SszIySq9MchIjRSKZYcSku0=
github.com/chaosblade-io/chaosblade-exec-os v0.0.1 h1:D3ivaFDZ7PxR8lEUIpPxS+B0kbR3IM3OvYyS8MihFY4=
github.com/chaosblade-io/chaosblade-exec-os v0.0.1/go.mod h1:urXotEH/sG+4lhIGv++i7rmL+qV21HIEHzMx829NqlA=
github.com/chaosblade-io/chaosblade-exec-os v0.0.2-0.20191129081657-0b7ed2b9c260 h1:4h6rJbYYRvDayJT38PkCHNExDdm0EyJrIYPbCaFMGIw=
Expand Down Expand Up @@ -225,6 +227,9 @@ github.com/chaosblade-io/chaosblade-exec-os v0.6.0 h1:Qd+9/g9nXtQDFReK4jCf+TtKvc
github.com/chaosblade-io/chaosblade-exec-os v0.6.0/go.mod h1:VeR9OFFdeg0BXkEta0DDQ90J/vC5zOiUAOOmajodizM=
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200803064345-37c6bf66fdde h1:x3tSLrYhnwzc9MayulsgtN5fP+2ooB+0tmG7hIQTafM=
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200803064345-37c6bf66fdde/go.mod h1:bIgPH7EjFMj5bzpHdiGUJgXd5P4+D33asc67BUIW/3w=
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200805070637-52adf80fc207/go.mod h1:bIgPH7EjFMj5bzpHdiGUJgXd5P4+D33asc67BUIW/3w=
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200917023854-8bea9f1ae34f h1:7kqRHGrfvTPRcCoD3GQAcxmODgPY5Ybmwz1ANC2rWec=
github.com/chaosblade-io/chaosblade-exec-os v0.6.1-0.20200917023854-8bea9f1ae34f/go.mod h1:bIgPH7EjFMj5bzpHdiGUJgXd5P4+D33asc67BUIW/3w=
github.com/chaosblade-io/chaosblade-operator v0.4.0/go.mod h1:FfFGwher7xuvU1yjIZzIViBq5svntfk8BDzy3TGjqI0=
github.com/chaosblade-io/chaosblade-spec-go v0.0.1 h1:8Nch78qEYJLxW9Xd5pESSnLB1RCV4gMYgDnE7rlVgDs=
github.com/chaosblade-io/chaosblade-spec-go v0.0.1/go.mod h1:ybcCsIX1wZtPrH0IsI28yDNJ+AVPeDMUZlgw1HE++Ds=
Expand Down Expand Up @@ -252,6 +257,9 @@ github.com/chaosblade-io/chaosblade-spec-go v0.6.0 h1:PtNvXQ1NTtKS4lQb1y07ZmBJKd
github.com/chaosblade-io/chaosblade-spec-go v0.6.0/go.mod h1:mBn6I5u/E5liN2zimyhxOBL5wSNPt2biZ7mIpJBjoJ4=
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200628025133-fa9dc1fa51a6 h1:nijr287GpeysRa7i0coZbUiHSYy/3CM3L6A4wH0ZQ+4=
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200628025133-fa9dc1fa51a6/go.mod h1:xUF+8r54FphQjBR8fVPnweVqzu7EitE15UsnZ57O5gk=
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200713091457-d3932a4b0129/go.mod h1:xUF+8r54FphQjBR8fVPnweVqzu7EitE15UsnZ57O5gk=
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200917030038-f8260e80f639 h1:DcNqhlspn/PlouvWe7jzY3vZElA9Ob8OwdPEr18w5Cs=
github.com/chaosblade-io/chaosblade-spec-go v0.6.1-0.20200917030038-f8260e80f639/go.mod h1:xUF+8r54FphQjBR8fVPnweVqzu7EitE15UsnZ57O5gk=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down