github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/fault/fault_http.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/apimachinery/pkg/util/json" 30 "k8s.io/cli-runtime/pkg/genericiooptions" 31 cmdutil "k8s.io/kubectl/pkg/cmd/util" 32 "k8s.io/kubectl/pkg/util/templates" 33 34 "github.com/1aal/kubeblocks/pkg/cli/create" 35 ) 36 37 var faultHTTPExample = templates.Examples(` 38 # By default, the method of GET from port 80 is blocked. 39 kbcli fault network http abort --duration=1m 40 41 # Block the method of GET from port 4399. 42 kbcli fault network http abort --port=4399 --duration=1m 43 44 # Block the method of POST from port 4399. 45 kbcli fault network http abort --port=4399 --method=POST --duration=1m 46 47 # Delays post requests from port 4399. 48 kbcli fault network http delay --port=4399 --method=POST --delay=15s 49 50 # Replace the GET method sent from port 80 with the PUT method. 51 kbcli fault network http replace --replace-method=PUT --duration=1m 52 53 # Replace the GET method sent from port 80 with the PUT method, and replace the request body. 54 kbcli fault network http replace --body="you are good luck" --replace-method=PUT --duration=2m 55 56 # Replace the response content "you" from port 80. 57 kbcli fault network http replace --target=Response --body=you --duration=30s 58 59 # Append content to the body of the post request sent from port 4399, in JSON format. 60 kbcli fault network http patch --method=POST --port=4399 --body="you are good luck" --type=JSON --duration=30s 61 `) 62 63 type HTTPReplace struct { 64 ReplaceBody []byte `json:"body,omitempty"` 65 InputReplaceBody string `json:"-"` 66 ReplacePath string `json:"path,omitempty"` 67 ReplaceMethod string `json:"method,omitempty"` 68 } 69 70 type HTTPPatch struct { 71 HTTPPatchBody `json:"body,omitempty"` 72 } 73 74 type HTTPPatchBody struct { 75 PatchBodyValue string `json:"value,omitempty"` 76 PatchBodyType string `json:"type,omitempty"` 77 } 78 79 type HTTPChaosOptions struct { 80 Target string `json:"target"` 81 Port int32 `json:"port"` 82 Path string `json:"path"` 83 Method string `json:"method"` 84 Code int32 `json:"code,omitempty"` 85 86 // abort command 87 Abort bool `json:"abort,omitempty"` 88 // delay command 89 Delay string `json:"delay,omitempty"` 90 // replace command 91 HTTPReplace `json:"replace,omitempty"` 92 // patch command 93 HTTPPatch `json:"patch,omitempty"` 94 95 FaultBaseOptions 96 } 97 98 func NewHTTPChaosOptions(f cmdutil.Factory, streams genericiooptions.IOStreams, action string) *HTTPChaosOptions { 99 o := &HTTPChaosOptions{ 100 FaultBaseOptions: FaultBaseOptions{ 101 CreateOptions: create.CreateOptions{ 102 Factory: f, 103 IOStreams: streams, 104 CueTemplateName: CueTemplateHTTPChaos, 105 GVR: GetGVR(Group, Version, ResourceHTTPChaos), 106 }, 107 Action: action, 108 }, 109 } 110 o.CreateOptions.PreCreate = o.PreCreate 111 o.CreateOptions.Options = o 112 return o 113 } 114 115 func NewHTTPChaosCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 116 cmd := &cobra.Command{ 117 Use: "http", 118 Short: "Intercept HTTP requests and responses.", 119 } 120 cmd.AddCommand( 121 NewAbortCmd(f, streams), 122 NewHTTPDelayCmd(f, streams), 123 NewReplaceCmd(f, streams), 124 NewPatchCmd(f, streams), 125 ) 126 return cmd 127 } 128 129 func NewAbortCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 130 o := NewHTTPChaosOptions(f, streams, "") 131 cmd := o.NewCobraCommand(Abort, AbortShort) 132 133 o.AddCommonFlag(cmd) 134 cmd.Flags().BoolVar(&o.Abort, "abort", true, `Indicates whether to inject the fault that interrupts the connection.`) 135 136 return cmd 137 } 138 139 func NewHTTPDelayCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 140 o := NewHTTPChaosOptions(f, streams, "") 141 cmd := o.NewCobraCommand(HTTPDelay, HTTPDelayShort) 142 143 o.AddCommonFlag(cmd) 144 cmd.Flags().StringVar(&o.Delay, "delay", "10s", `The time for delay.`) 145 146 return cmd 147 } 148 149 func NewReplaceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 150 o := NewHTTPChaosOptions(f, streams, "") 151 cmd := o.NewCobraCommand(Replace, ReplaceShort) 152 153 o.AddCommonFlag(cmd) 154 cmd.Flags().StringVar(&o.InputReplaceBody, "body", "", `The content of the request body or response body to replace the failure.`) 155 cmd.Flags().StringVar(&o.ReplacePath, "replace-path", "", `The URI path used to replace content.`) 156 cmd.Flags().StringVar(&o.ReplaceMethod, "replace-method", "", `The replaced content of the HTTP request method.`) 157 158 return cmd 159 } 160 161 func NewPatchCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 162 o := NewHTTPChaosOptions(f, streams, "") 163 cmd := o.NewCobraCommand(Patch, PatchShort) 164 165 o.AddCommonFlag(cmd) 166 cmd.Flags().StringVar(&o.PatchBodyValue, "body", "", `The fault of the request body or response body with patch faults.`) 167 cmd.Flags().StringVar(&o.PatchBodyType, "type", "", `The type of patch faults of the request body or response body. Currently, it only supports JSON.`) 168 169 return cmd 170 } 171 172 func (o *HTTPChaosOptions) NewCobraCommand(use, short string) *cobra.Command { 173 return &cobra.Command{ 174 Use: use, 175 Short: short, 176 Example: faultHTTPExample, 177 Run: func(cmd *cobra.Command, args []string) { 178 o.Args = args 179 cmdutil.CheckErr(o.CreateOptions.Complete()) 180 cmdutil.CheckErr(o.Validate()) 181 cmdutil.CheckErr(o.Complete()) 182 cmdutil.CheckErr(o.Run()) 183 }, 184 } 185 } 186 187 func (o *HTTPChaosOptions) AddCommonFlag(cmd *cobra.Command) { 188 o.FaultBaseOptions.AddCommonFlag(cmd) 189 190 cmd.Flags().StringVar(&o.Target, "target", "Request", `Specifies whether the target of fault injection is Request or Response. The target-related fields should be configured at the same time.`) 191 cmd.Flags().Int32Var(&o.Port, "port", 80, `The TCP port that the target service listens on.`) 192 cmd.Flags().StringVar(&o.Path, "path", "*", `The URI path of the target request. Supports Matching wildcards.`) 193 cmd.Flags().StringVar(&o.Method, "method", "GET", `The HTTP method of the target request method. For example: GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH.`) 194 cmd.Flags().Int32Var(&o.Code, "code", 0, `The status code responded by target.`) 195 196 // register flag completion func 197 registerFlagCompletionFunc(cmd, o.Factory) 198 } 199 200 func (o *HTTPChaosOptions) Validate() error { 201 if o.PatchBodyType != "" && o.PatchBodyType != "JSON" { 202 return fmt.Errorf("--type only supports JSON") 203 } 204 if o.PatchBodyValue != "" && o.PatchBodyType == "" { 205 return fmt.Errorf("--type is required when --body is specified") 206 } 207 if o.PatchBodyType != "" && o.PatchBodyValue == "" { 208 return fmt.Errorf("--body is required when --type is specified") 209 } 210 211 var msg interface{} 212 if o.PatchBodyValue != "" && json.Unmarshal([]byte(o.PatchBodyValue), &msg) != nil { 213 return fmt.Errorf("--body is not a valid JSON") 214 } 215 216 if o.Target == "Request" && o.Code != 0 { 217 return fmt.Errorf("--code is only supported when --target=Response") 218 } 219 220 if ok, err := IsRegularMatch(o.Delay); !ok { 221 return err 222 } 223 return o.BaseValidate() 224 } 225 226 func (o *HTTPChaosOptions) Complete() error { 227 o.ReplaceBody = []byte(o.InputReplaceBody) 228 return o.BaseComplete() 229 } 230 231 func (o *HTTPChaosOptions) PreCreate(obj *unstructured.Unstructured) error { 232 c := &v1alpha1.HTTPChaos{} 233 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, c); err != nil { 234 return err 235 } 236 237 data, e := runtime.DefaultUnstructuredConverter.ToUnstructured(c) 238 if e != nil { 239 return e 240 } 241 obj.SetUnstructuredContent(data) 242 return nil 243 }