github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/fault/fault_network.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package fault
    21  
    22  import (
    23  	"fmt"
    24  
    25  	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    26  	"github.com/spf13/cobra"
    27  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/cli-runtime/pkg/genericiooptions"
    30  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    31  	"k8s.io/kubectl/pkg/util/templates"
    32  
    33  	"github.com/1aal/kubeblocks/pkg/cli/create"
    34  	"github.com/1aal/kubeblocks/pkg/cli/util"
    35  )
    36  
    37  var faultNetWorkExample = templates.Examples(`
    38  	# Isolate all pods network under the default namespace from the outside world, including the k8s internal network.
    39  	kbcli fault network partition
    40  
    41  	# The specified pod is isolated from the k8s external network "kubeblocks.io".
    42  	kbcli fault network partition mycluster-mysql-1 --external-targets=kubeblocks.io
    43  	
    44  	# Isolate the network between two pods.
    45  	kbcli fault network partition mycluster-mysql-1 --target-label=statefulset.kubernetes.io/pod-name=mycluster-mysql-2
    46  	
    47  	// Like the partition command, the target can be specified through --target-label or --external-targets. The pod only has obstacles in communicating with this target. If the target is not specified, all communication will be blocked.
    48  	# Block all pod communication under the default namespace, resulting in a 50% packet loss rate.
    49  	kbcli fault network loss --loss=50
    50  	
    51  	# Block the specified pod communication, so that the packet loss rate is 50%.
    52  	kbcli fault network loss mysql-cluster-mysql-2 --loss=50
    53  	
    54  	kbcli fault network corrupt --corrupt=50
    55  
    56  	# Blocks specified pod communication with a 50% packet corruption rate.
    57  	kbcli fault network corrupt mysql-cluster-mysql-2 --corrupt=50
    58  	
    59  	kbcli fault network duplicate --duplicate=50
    60  
    61  	# Block specified pod communication so that the packet repetition rate is 50%.
    62  	kbcli fault network duplicate mysql-cluster-mysql-2 --duplicate=50
    63  	
    64  	kbcli fault network delay --latency=10s
    65  
    66  	# Block the communication of the specified pod, causing its network delay for 10s.
    67  	kbcli fault network delay mysql-cluster-mysql-2 --latency=10s
    68  
    69  	# Limit the communication bandwidth between mysql-cluster-mysql-2 and the outside.
    70  	kbcli fault network bandwidth mysql-cluster-mysql-2 --rate=1kbps --duration=1m
    71  `)
    72  
    73  type Target struct {
    74  	TargetMode     string `json:"mode,omitempty"`
    75  	TargetValue    string `json:"value,omitempty"`
    76  	TargetSelector `json:"selector,omitempty"`
    77  }
    78  
    79  type TargetSelector struct {
    80  	// Specifies the labels that target Pods come with.
    81  	TargetLabelSelectors map[string]string `json:"labelSelectors,omitempty"`
    82  	// Specifies the namespaces to which target Pods belong.
    83  	TargetNamespaceSelectors []string `json:"namespaces,omitempty"`
    84  }
    85  
    86  // NetworkLoss Loss command
    87  type NetworkLoss struct {
    88  	// The percentage of packet loss
    89  	Loss string `json:"loss,omitempty"`
    90  	// The correlation of loss or corruption or duplication or delay
    91  	Correlation string `json:"correlation,omitempty"`
    92  }
    93  
    94  // NetworkDelay Delay command
    95  type NetworkDelay struct {
    96  	// The latency of delay
    97  	Latency string `json:"latency,omitempty"`
    98  	// The jitter of delay
    99  	Jitter string `json:"jitter,omitempty"`
   100  	// The correlation of loss or corruption or duplication or delay
   101  	Correlation string `json:"correlation,omitempty"`
   102  }
   103  
   104  // NetworkDuplicate Duplicate command
   105  type NetworkDuplicate struct {
   106  	// The percentage of packet duplication
   107  	Duplicate string `json:"duplicate,omitempty"`
   108  	// The correlation of loss or corruption or duplication or delay
   109  	Correlation string `json:"correlation,omitempty"`
   110  }
   111  
   112  // NetworkCorrupt Corrupt command
   113  type NetworkCorrupt struct {
   114  	// The percentage of packet corruption
   115  	Corrupt string `json:"corrupt,omitempty"`
   116  	// The correlation of loss or corruption or duplication or delay
   117  	Correlation string `json:"correlation,omitempty"`
   118  }
   119  
   120  // NetworkBandwidth Bandwidth command
   121  type NetworkBandwidth struct {
   122  	// the rate at which the bandwidth is limited.
   123  	Rate string `json:"rate,omitempty"`
   124  	// the number of bytes waiting in the queue.
   125  	Limit uint32 `json:"limit,omitempty"`
   126  	// the maximum number of bytes that can be sent instantaneously.
   127  	Buffer uint32 `json:"buffer,omitempty"`
   128  	// the bucket's maximum consumption rate. Reference: https://man7.org/linux/man-pages/man8/tc-tbf.8.html.
   129  	Peakrate uint64 `json:"peakrate,omitempty"`
   130  	// the size of the peakrate bucket. Reference: https://man7.org/linux/man-pages/man8/tc-tbf.8.html.
   131  	Minburst uint32 `json:"minburst,omitempty"`
   132  }
   133  
   134  type NetworkChaosOptions struct {
   135  	// Specify the network direction
   136  	Direction string `json:"direction"`
   137  
   138  	// A network target outside of Kubernetes, which can be an IPv4 address or a domain name,
   139  	// such as "kubeblocks.io". Only works with direction: to.
   140  	ExternalTargets []string `json:"externalTargets,omitempty"`
   141  
   142  	// A collection of target pods. Pods can be selected by namespace and label.
   143  	Target `json:"target,omitempty"`
   144  
   145  	NetworkLoss `json:"loss,omitempty"`
   146  
   147  	NetworkDelay `json:"delay,omitempty"`
   148  
   149  	NetworkDuplicate `json:"duplicate,omitempty"`
   150  
   151  	NetworkCorrupt `json:"corrupt,omitempty"`
   152  
   153  	NetworkBandwidth `json:"bandwidth,omitempty"`
   154  
   155  	FaultBaseOptions
   156  }
   157  
   158  func NewNetworkChaosOptions(f cmdutil.Factory, streams genericiooptions.IOStreams, action string) *NetworkChaosOptions {
   159  	o := &NetworkChaosOptions{
   160  		FaultBaseOptions: FaultBaseOptions{CreateOptions: create.CreateOptions{
   161  			Factory:         f,
   162  			IOStreams:       streams,
   163  			CueTemplateName: CueTemplateNetworkChaos,
   164  			GVR:             GetGVR(Group, Version, ResourceNetworkChaos),
   165  		},
   166  			Action: action,
   167  		},
   168  	}
   169  	o.CreateOptions.PreCreate = o.PreCreate
   170  	o.CreateOptions.Options = o
   171  	return o
   172  }
   173  
   174  func NewNetworkChaosCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   175  	cmd := &cobra.Command{
   176  		Use:   "network",
   177  		Short: "Network chaos.",
   178  	}
   179  	cmd.AddCommand(
   180  		NewPartitionCmd(f, streams),
   181  		NewLossCmd(f, streams),
   182  		NewDelayCmd(f, streams),
   183  		NewDuplicateCmd(f, streams),
   184  		NewCorruptCmd(f, streams),
   185  		NewBandwidthCmd(f, streams),
   186  		NewDNSChaosCmd(f, streams),
   187  		NewHTTPChaosCmd(f, streams),
   188  	)
   189  	return cmd
   190  }
   191  
   192  func NewPartitionCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   193  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.PartitionAction))
   194  	cmd := o.NewCobraCommand(Partition, PartitionShort)
   195  
   196  	o.AddCommonFlag(cmd)
   197  
   198  	return cmd
   199  }
   200  
   201  func NewLossCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   202  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.LossAction))
   203  	cmd := o.NewCobraCommand(Loss, LossShort)
   204  
   205  	o.AddCommonFlag(cmd)
   206  	cmd.Flags().StringVar(&o.Loss, "loss", "", `Indicates the probability of a packet error occurring. Value range: [0, 100].`)
   207  	cmd.Flags().StringVarP(&o.NetworkLoss.Correlation, "correlation", "c", "", `Indicates the correlation between the probability of a packet error occurring and whether it occurred the previous time. Value range: [0, 100].`)
   208  
   209  	util.CheckErr(cmd.MarkFlagRequired("loss"))
   210  
   211  	return cmd
   212  }
   213  
   214  func NewDelayCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   215  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.DelayAction))
   216  	cmd := o.NewCobraCommand(Delay, DelayShort)
   217  
   218  	o.AddCommonFlag(cmd)
   219  	cmd.Flags().StringVar(&o.Latency, "latency", "", `the length of time to delay.`)
   220  	cmd.Flags().StringVar(&o.Jitter, "jitter", "", `the variation range of the delay time.`)
   221  	cmd.Flags().StringVarP(&o.NetworkDelay.Correlation, "correlation", "c", "", `Indicates the probability of a packet error occurring. Value range: [0, 100].`)
   222  
   223  	util.CheckErr(cmd.MarkFlagRequired("latency"))
   224  
   225  	return cmd
   226  }
   227  
   228  func NewDuplicateCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   229  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.DuplicateAction))
   230  	cmd := o.NewCobraCommand(Duplicate, DuplicateShort)
   231  
   232  	o.AddCommonFlag(cmd)
   233  	cmd.Flags().StringVar(&o.Duplicate, "duplicate", "", `the probability of a packet being repeated. Value range: [0, 100].`)
   234  	cmd.Flags().StringVarP(&o.NetworkDuplicate.Correlation, "correlation", "c", "", `Indicates the correlation between the probability of a packet error occurring and whether it occurred the previous time. Value range: [0, 100].`)
   235  
   236  	util.CheckErr(cmd.MarkFlagRequired("duplicate"))
   237  
   238  	return cmd
   239  }
   240  
   241  func NewCorruptCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   242  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.CorruptAction))
   243  	cmd := o.NewCobraCommand(Corrupt, CorruptShort)
   244  
   245  	o.AddCommonFlag(cmd)
   246  	cmd.Flags().StringVar(&o.Corrupt, "corrupt", "", `Indicates the probability of a packet error occurring. Value range: [0, 100].`)
   247  	cmd.Flags().StringVarP(&o.NetworkCorrupt.Correlation, "correlation", "c", "", `Indicates the correlation between the probability of a packet error occurring and whether it occurred the previous time. Value range: [0, 100].`)
   248  
   249  	util.CheckErr(cmd.MarkFlagRequired("corrupt"))
   250  
   251  	return cmd
   252  }
   253  
   254  func NewBandwidthCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   255  	o := NewNetworkChaosOptions(f, streams, string(v1alpha1.BandwidthAction))
   256  	cmd := o.NewCobraCommand(Bandwidth, BandwidthShort)
   257  
   258  	o.AddCommonFlag(cmd)
   259  	cmd.Flags().StringVar(&o.Rate, "rate", "", `the rate at which the bandwidth is limited. For example : 10 bps/kbps/mbps/gbps.`)
   260  	cmd.Flags().Uint32Var(&o.Limit, "limit", 1, `the number of bytes waiting in the queue.`)
   261  	cmd.Flags().Uint32Var(&o.Buffer, "buffer", 1, `the maximum number of bytes that can be sent instantaneously.`)
   262  	cmd.Flags().Uint64Var(&o.Peakrate, "peakrate", 0, `the maximum consumption rate of the bucket.`)
   263  	cmd.Flags().Uint32Var(&o.Minburst, "minburst", 0, `the size of the peakrate bucket.`)
   264  
   265  	util.CheckErr(cmd.MarkFlagRequired("rate"))
   266  
   267  	return cmd
   268  }
   269  
   270  func (o *NetworkChaosOptions) NewCobraCommand(use, short string) *cobra.Command {
   271  	return &cobra.Command{
   272  		Use:     use,
   273  		Short:   short,
   274  		Example: faultNetWorkExample,
   275  		Run: func(cmd *cobra.Command, args []string) {
   276  			o.Args = args
   277  			cmdutil.CheckErr(o.CreateOptions.Complete())
   278  			cmdutil.CheckErr(o.Validate())
   279  			cmdutil.CheckErr(o.Complete())
   280  			cmdutil.CheckErr(o.Run())
   281  		},
   282  	}
   283  }
   284  
   285  func (o *NetworkChaosOptions) AddCommonFlag(cmd *cobra.Command) {
   286  	o.FaultBaseOptions.AddCommonFlag(cmd)
   287  
   288  	cmd.Flags().StringVar(&o.Direction, "direction", "to", `You can select "to"" or "from"" or "both"".`)
   289  	cmd.Flags().StringArrayVarP(&o.ExternalTargets, "external-target", "e", nil, "a network target outside of Kubernetes, which can be an IPv4 address or a domain name,\n\t such as \"www.baidu.com\". Only works with direction: to.")
   290  	cmd.Flags().StringVar(&o.TargetMode, "target-mode", "", `You can select "one", "all", "fixed", "fixed-percent", "random-max-percent", Specify the experimental mode, that is, which Pods to experiment with.`)
   291  	cmd.Flags().StringVar(&o.TargetValue, "target-value", "", `If you choose mode=fixed or fixed-percent or random-max-percent, you can enter a value to specify the number or percentage of pods you want to inject.`)
   292  	cmd.Flags().StringToStringVar(&o.TargetLabelSelectors, "target-label", nil, `label for pod, such as '"app.kubernetes.io/component=mysql, statefulset.kubernetes.io/pod-name=mycluster-mysql-0"'`)
   293  	cmd.Flags().StringArrayVar(&o.TargetNamespaceSelectors, "target-ns-fault", nil, `Specifies the namespace into which you want to inject faults.`)
   294  
   295  	// register flag completion func
   296  	registerFlagCompletionFunc(cmd, o.Factory)
   297  }
   298  
   299  func (o *NetworkChaosOptions) Validate() error {
   300  	if o.TargetValue == "" && (o.TargetMode == "fixed" || o.TargetMode == "fixed-percent" || o.TargetMode == "random-max-percent") {
   301  		return fmt.Errorf("--value is required to specify pod nums or percentage")
   302  	}
   303  
   304  	if (o.TargetNamespaceSelectors != nil || o.TargetLabelSelectors != nil) && o.TargetMode == "" {
   305  		return fmt.Errorf("--target-mode is required to specify a target mode")
   306  	}
   307  
   308  	if o.ExternalTargets != nil && o.Direction != "to" {
   309  		return fmt.Errorf("--direction=to is required when specifying external targets")
   310  	}
   311  
   312  	if ok, err := IsInteger(o.TargetValue); !ok {
   313  		return err
   314  	}
   315  
   316  	if ok, err := IsInteger(o.Loss); !ok {
   317  		return err
   318  	}
   319  
   320  	if ok, err := IsInteger(o.Corrupt); !ok {
   321  		return err
   322  	}
   323  
   324  	if ok, err := IsInteger(o.Duplicate); !ok {
   325  		return err
   326  	}
   327  
   328  	if ok, err := IsRegularMatch(o.Latency); !ok {
   329  		return err
   330  	}
   331  
   332  	if ok, err := IsRegularMatch(o.Jitter); !ok {
   333  		return err
   334  	}
   335  
   336  	return o.BaseValidate()
   337  }
   338  
   339  func (o *NetworkChaosOptions) Complete() error {
   340  	return o.BaseComplete()
   341  }
   342  
   343  func (o *NetworkChaosOptions) PreCreate(obj *unstructured.Unstructured) error {
   344  	c := &v1alpha1.NetworkChaos{}
   345  	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, c); err != nil {
   346  		return err
   347  	}
   348  
   349  	data, e := runtime.DefaultUnstructuredConverter.ToUnstructured(c)
   350  	if e != nil {
   351  		return e
   352  	}
   353  	obj.SetUnstructuredContent(data)
   354  	return nil
   355  }