├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── api └── main.go ├── docs ├── DSL.md ├── configs.md └── serializer.md ├── examples ├── matrix-tidb-tikv-pd.yaml └── matrix-tiup.yaml ├── go.mod ├── go.sum ├── pkg ├── context │ └── context.go ├── node │ ├── abstract.go │ ├── core.go │ ├── data │ │ ├── concrete.go │ │ ├── hollow.go │ │ ├── time.go │ │ └── types.go │ └── surface.go ├── parser │ ├── config.go │ ├── parser_test.go │ └── value.go ├── random │ └── random.go ├── serializer │ ├── base.go │ ├── stmt.go │ └── toml.go ├── synthesizer │ └── simple.go └── utils │ ├── limits.go │ └── utils.go └── src └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | .idea 3 | 4 | *.toml 5 | *.sql 6 | 7 | # tiup log 8 | /log -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.14.4-alpine3.12 2 | 3 | RUN apk add --no-cache make 4 | 5 | ENV GO111MODULE=on 6 | WORKDIR /src 7 | COPY . . 8 | 9 | RUN make all 10 | 11 | FROM alpine:3.12 12 | 13 | RUN apk update && apk upgrade && \ 14 | apk add --no-cache bash curl 15 | 16 | COPY --from=0 /src/bin/* /bin/ 17 | 18 | ENTRYPOINT ["/bin/matrix"] 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOARCH := $(if $(GOARCH),$(GOARCH),amd64) 2 | GO=GO15VENDOREXPERIMENT="1" CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) GO111MODULE=on go 3 | GOTEST=GO15VENDOREXPERIMENT="1" CGO_ENABLED=1 GO111MODULE=on go test # go race detector requires cgo 4 | VERSION := $(if $(VERSION),$(VERSION),latest) 5 | IMAGE := $(if $(IMAGE),$(IMAGE),pingcap/matrix) 6 | 7 | GOBUILD=$(GO) build 8 | 9 | ifeq (,$(shell go env GOBIN)) 10 | GOBIN=$(shell go env GOPATH)/bin 11 | else 12 | GOBIN=$(shell go env GOBIN) 13 | endif 14 | 15 | PACKAGE_LIST := go list ./... | grep -vE "matrix/test" 16 | PACKAGE_DIRECTORIES := $(PACKAGE_LIST) | sed 's|github.com/chaos-mesh/matrix/||' 17 | 18 | default: all 19 | 20 | all: 21 | $(GOBUILD) $(GOMOD) -o bin/matrix src/main.go 22 | 23 | groupimports: $(GOBIN)/goimports 24 | $< -w -l -local github.com/chaos-mesh/matrix $$($(PACKAGE_DIRECTORIES)) 25 | 26 | fmt: groupimports 27 | go fmt ./... 28 | 29 | tidy: 30 | @echo "go mod tidy" 31 | GO111MODULE=on go mod tidy 32 | @git diff --exit-code -- go.mod 33 | 34 | docker-build: 35 | docker build -t $(IMAGE):$(VERSION) . 36 | 37 | $(GOBIN)/goimports: 38 | $(GO) get golang.org/x/tools/cmd/goimports@v0.0.0-20200309202150-20ab64c0d93f 39 | 40 | test: 41 | $(GOTEST) $$($(PACKAGE_LIST)) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Matrix 2 | 3 | Matrix is a config fuzzer, i.e. generate a set of configurations for a program that need to be fuzzed. 4 | 5 | ## Quickstart 6 | ```bash 7 | $ make 8 | $ bin/matrix -c examples/matrix-tidb-tikv-pd.yaml 9 | # this will generate three TiDB-related configs in current folder 10 | # including `tidb.yaml`, `tikv.yaml`, `pd.yaml`, `mysql-system-vars.sql` and `tidb-system-vars.sql` 11 | ``` 12 | 13 | Output folder can be configured with `-d ` 14 | 15 | ## Usage 16 | ``` 17 | $ ./bin/matrix -h 18 | Usage of ./bin/matrix: 19 | -c string 20 | config file 21 | -d string 22 | output folder (default ".") 23 | -s int 24 | seed of rand (default UTC nanoseconds of now) 25 | ``` 26 | 27 | Matrix reads one config file contains generation rules, 28 | generate values based on those rules, 29 | then dump values with serializer. 30 | 31 | ## Matrix Configuration 32 | Matrix use DSL in yaml format to describe the configurations need to be fuzzed. 33 | See [DSL](./docs/DSL.md) for detailed information. 34 | 35 | ## Type of configs Matrix support to generate 36 | With different serializer, Matrix supports to generate various kinds of configurations, 37 | refer to [serializer](./docs/serializer.md) for detailed usage. 38 | 39 | Currently supported format: 40 | - toml 41 | - yaml 42 | - line-based generation 43 | 44 | ## TODO 45 | ### Parser 46 | - [ ] Better unit conversion 47 | ### Serializer 48 | - [x] toml serializer 49 | - [x] yaml serializer 50 | - [x] SQL serializer 51 | - [ ] Command line argument serializer 52 | ### Generator 53 | - [ ] List generation 54 | - [ ] Instructive information (e.g. default value) 55 | - [x] Simple recursive random generator 56 | - [ ] Non-recursive generator that supports dependency 57 | ### Random 58 | - [x] Random generate value 59 | - [x] Specific random seed to have same output with same seed, to support continuous testing 60 | -------------------------------------------------------------------------------- /api/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package api 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "io/ioutil" 20 | "time" 21 | 22 | "github.com/ghodss/yaml" 23 | "github.com/pingcap/log" 24 | 25 | "github.com/chaos-mesh/matrix/pkg/context" 26 | "github.com/chaos-mesh/matrix/pkg/node" 27 | "github.com/chaos-mesh/matrix/pkg/parser" 28 | ) 29 | 30 | func Gen(matrixConfig, output string, seed int64) error { 31 | cont, err := ioutil.ReadFile(matrixConfig) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | var body node.MatrixConfigFile 37 | err = yaml.Unmarshal(cont, &body) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | var ctx *context.MatrixContext 43 | ctx, err = parser.ParseFile(body) 44 | 45 | if err != nil { 46 | return errors.New(fmt.Sprintf("file not valid: %s", err.Error())) 47 | } 48 | 49 | if seed == 0 { 50 | seed = time.Now().UnixNano() 51 | } 52 | log.L().Info(fmt.Sprintf("Matrix SEED: %d", seed)) 53 | 54 | err = ctx.Dump(seed, output) 55 | if err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /docs/DSL.md: -------------------------------------------------------------------------------- 1 | # Configs of Matrix 2 | The config file of Matrix tool is a yaml file, formatted as followed: 3 | ```yaml 4 | config: 5 | - 6 | ``` 7 | 8 | Each `config_template` is a set of configs that will be generated as one entity (i.e. file). 9 | 10 | | Field | Description | Sample Value | 11 | |:------|:------------------|:--------------| 12 | | **tag** | Tag for this config | 13 | | **serializer** | Serializer used to dump this config | `yaml` / `toml` / `stmt` / `...` | 14 | | **target** | Path of dumped config | 15 | | **value** | Specification of this config | 16 | 17 | Each `value` is a map with its type and some other constraints, 18 | some grammar sugars can be used for simplification. 19 | 20 | ``` 21 | value := { type: [, when: ][, arguments] } 22 | 23 | config := { key: value, ... } 24 | 25 | value := # sugar for random generated value with no constraints, e.g. [u]int, bool, string 26 | | # sugar for literal value (int, float, string) 27 | | # sugar for type `choice` 28 | | 29 | | # sugar for map 30 | 31 | list_of_choices := [] 32 | | [] :: 33 | 34 | full_value_decl := {type: [, when: ][, arguments]} 35 | ``` 36 | 37 | ## Type of config values 38 | ``` 39 | type := bool 40 | | int 41 | | string 42 | | float 43 | | time 44 | | size 45 | | time 46 | | map | struct 47 | | list 48 | | choice | choice_n 49 | size := {kb,mb,gb,...} 50 | time := [h][m][s] # at least one part should exist 51 | ``` 52 | 53 | ## Arguments 54 | | Type | Field | Description | 55 | |:------|:------------------|:--------------| 56 | | **bool** | `value` | Optional, value to use instead of randomly generate. | 57 | | **string** | `value` | Optional, value to use instead of randomly generate. | 58 | | **list** | `value` | Value of list | 59 | | **map** | `value` | Value of map | 60 | | **int** | `range` | List, [start, end], end is optional | 61 | | **float** | `range` | List, [start, end], end is optional | 62 | | **size** | `range` | List, [start, end], end is optional | 63 | | **time** | `range` | List, [start, end], end is optional | 64 | | **choice** | `value` | List, one random element will be selected. | 65 | | **choice_n** | `value` | List, `n` random element will be selected. | 66 | | **choice_n** | `n` | int, `n` of `choice_n` | 67 | | **choice_n** | `sep` | Optional, string, if given and all elements in list is of type string, this will be used to join the selected elements. | 68 | 69 | ## Examples 70 | See `examples/matrix-tidb-tikv-pd.yaml` for an example of TiDB. 71 | -------------------------------------------------------------------------------- /docs/configs.md: -------------------------------------------------------------------------------- 1 | This file describes some important options fuzzing should pay attention to. 2 | 3 | # Mysql System Variables 4 | ## `sql_mode` 5 | ### `HIGH_NOT_PRECEDENCE` 6 | The behavior of `not` seems different with mysql, it (and `MySQL323`, `MYSQL40`) breaks `create table if not exists`. 7 | 8 | See https://github.com/pingcap/tidb/issues/18729 9 | -------------------------------------------------------------------------------- /docs/serializer.md: -------------------------------------------------------------------------------- 1 | # Serializers 2 | ## `yaml` / `toml` 3 | These two serializer simply dump values into corresponding format. 4 | ## `stmt` 5 | 6 | `stmt` serializer is a line-based serializer, 7 | it generates a file, each line of which is generated by one entity in config's value. 8 | 9 | To use `stmt` serializer, the `value` field of this config needs to be a list. 10 | The elements of this list should contain two fields: `template` and `value`. 11 | `template` is a string passed to `fmt.Sprintf`, `value` is the specification to generate value, which will be used to format `template` later. 12 | 13 | See [tidb-example](../examples/matrix-tidb-tikv-pd.yaml) for an example. -------------------------------------------------------------------------------- /examples/matrix-tidb-tikv-pd.yaml: -------------------------------------------------------------------------------- 1 | configs: 2 | - tag: pd 3 | serializer: toml 4 | target: "pd.toml" 5 | value: 6 | log: 7 | level: "info" 8 | schedule: 9 | max-snapshot-count: 10 | type: int 11 | range: [1, 10] 12 | default: 3 13 | max-pending-peer-count: 14 | type: int 15 | range: [1, 32] 16 | default: 16 17 | max-merge-region-size: 18 | type: int 19 | range: [1, 50] 20 | default: 20 21 | max-merge-region-keys: 22 | type: int 23 | range: [5000, 400000] 24 | default: 200000 25 | split-merge-interval: 26 | type: time 27 | range: [10m, 2h] 28 | default: 1h 29 | enable-one-way-merge: bool 30 | enable-cross-table-merge: bool 31 | patrol-region-interval: 32 | type: time 33 | range: [10ms, 1s] 34 | default: 100ms 35 | max-store-down-time: 36 | type: time 37 | range: [10m, 1h] 38 | default: 30m 39 | leader-schedule-limit: 40 | type: int 41 | range: [1, 10] 42 | default: 4 43 | leader-schedule-policy: 44 | type: choice 45 | value: 46 | - "count" 47 | - type: string 48 | value: "size" 49 | default: "count" 50 | region-schedule-limit: 51 | type: int 52 | range: [512, 4096] 53 | default: 2048 54 | replica-schedule-limit: 55 | type: int 56 | range: [16, 256] 57 | default: 64 58 | merge-schedule-limit: 59 | type: int 60 | range: [1, 32] 61 | default: 8 62 | hot-region-schedule-limit: 63 | type: int 64 | range: [1, 16] 65 | default: 4 66 | hot-region-cache-hits-threshold: 67 | type: int 68 | range: [1, 10] 69 | default: 3 70 | store-balance-rate: 71 | type: float 72 | range: [1, 50] 73 | default: 15 74 | tolerant-size-ratio: 75 | type: float 76 | range: [0, 10] 77 | default: 5 78 | low-space-ratio: 79 | type: float 80 | range: [0.7, 0.999] 81 | default: 0.8 82 | high-space-ratio: 83 | type: float 84 | range: [0.001, 0.7] 85 | default: 0.6 86 | scheduler-max-waiting-operator: 87 | type: int 88 | range: [1, 10] 89 | default: 5 90 | store-limit-mode: ["auto", "manual"] 91 | replication: 92 | max-replicas: 93 | type: int 94 | range: [1, 10] 95 | default: 3 96 | strictly-match-label: bool 97 | #location-labels: # need detail information 98 | # type: list 99 | # value: ["zone", "rack"] 100 | enable-placement-rules: bool # not in doc or example 101 | pd-server: 102 | use-region-storage: bool 103 | key-type: ["table", "raw", "txn"] # not in doc or example 104 | - tag: tikv 105 | serializer: toml 106 | target: "tikv.toml" 107 | value: 108 | server: 109 | grpc-concurrency: 110 | type: int 111 | range: [1, 10] 112 | default: 4 113 | enable-request-batch: bool 114 | request-batch-enable-cross-command: bool 115 | request-batch-wait-duration: 116 | type: time 117 | range: [1ms, 20ms] 118 | default: 1ms 119 | "readpool.unified": 120 | min-thread-count: 121 | type: int 122 | range: [1, 2] 123 | default: 1 124 | max-thread-count: 125 | type: int 126 | range: [2, 6] 127 | default: 4 # max(4, LOGICAL_CPU_NUM * 0.8) 128 | "readpool.storage": 129 | use-unified-pool: bool 130 | high-concurrency: 131 | type: int 132 | range: [1, 10] 133 | default: 4 134 | normal-concurrency: 135 | type: int 136 | range: [1, 10] 137 | default: 4 138 | low-concurrency: 139 | type: int 140 | range: [1, 10] 141 | default: 4 142 | "readpool.coprocessor": 143 | use-unified-pool: bool 144 | high-concurrency: 145 | type: int 146 | range: [1, 10] 147 | normal-concurrency: 148 | type: int 149 | range: [1, 10] 150 | low-concurrency: 151 | type: int 152 | range: [1, 10] 153 | max-tasks-per-worker-high: 154 | type: int 155 | range: [2, 4000] 156 | default: 2000 157 | max-tasks-per-worker-normal: 158 | type: int 159 | range: [2, 4000] 160 | default: 2000 161 | max-tasks-per-worker-low: 162 | type: int 163 | range: [2, 4000] 164 | default: 2000 165 | storage: 166 | scheduler-concurrency: 167 | type: int 168 | range: [1, 4096000] 169 | default: 2048000 170 | scheduler-worker-pool-size: 171 | type: int 172 | range: [1, 16] 173 | default: 4 174 | scheduler-pending-write-threshold: 175 | type: size 176 | range: [10MB, 1GB] 177 | default: 100MB 178 | block-cache: 179 | shared: bool 180 | capacity: 181 | type: size 182 | range: [500MB, 2GB] 183 | default: 1GB 184 | raftstore: 185 | sync-log: bool 186 | nitify-capacity: 187 | type: int 188 | range: [10240, 81920] 189 | default: 40960 190 | messages-per-tick: 191 | type: int 192 | range: [1024, 8192] 193 | default: 4096 194 | apply-pool-size: 195 | type: int 196 | range: [1, 4] 197 | store-pool-size: 198 | type: int 199 | range: [1, 4] 200 | coprocessor: 201 | split-region-on-table: bool 202 | rocksdb: 203 | max-background-jobs: 204 | type: int 205 | range: [1, 16] 206 | default: 8 207 | max-sub-compactions: 208 | type: int 209 | range: [1, 10] 210 | default: 3 211 | rate-bytes-per-sec: 212 | type: choice 213 | default: 0 214 | value: 215 | - 0 216 | - type: int 217 | range: [10485760, 104857600] # [10MB, 100MB] 218 | auto-tuned: bool 219 | titan: 220 | enabled: bool 221 | max-background-gc: 222 | type: int 223 | range: [1, 10] 224 | default: 4 225 | defaultcf: 226 | #compression-per-level: list todo: list 227 | level0-slowdown-writes-trigger: 228 | type: int 229 | range: [5, 30] 230 | default: 20 231 | level0-stop-writes-trigger: 232 | type: int 233 | range: [31, 50] 234 | default: 36 235 | max-write-buffer-number: 236 | type: int 237 | range: [2, 10] 238 | default: 5 239 | titan: 240 | min-blob-size: 241 | type: size 242 | range: [512B, 10KB] 243 | default: 1KB 244 | blob-file-compression: 245 | type: choice 246 | value: ["no", "snappy", "zlib", "bz2", "lz4", "lz4hc", "zstd"] 247 | default: "lz4" 248 | blob-cache-size: 249 | type: size 250 | range: [0GB, 1GB] 251 | default: 0GB 252 | discardable-ratio: 253 | type: float 254 | range: [0.1, 0.9] 255 | default: 0.5 256 | blob-run-mode: 257 | type: choice 258 | value: ["normal", "read-only", "fallback"] 259 | default: "normal" 260 | level_merge: bool # default: false 261 | gc-merge-rewrite: bool # default: false 262 | writecf: 263 | level0-slowdown-writes-trigger: 264 | type: int 265 | range: [5, 30] 266 | default: 20 267 | level0-stop-writes-trigger: 268 | type: int 269 | range: [31, 50] 270 | default: 36 271 | raftdb: 272 | max-background-jobs: 273 | type: int 274 | range: [1, 10] 275 | default: 4 276 | max-sub-compactions: 277 | type: int 278 | range: [1, 5] 279 | default: 2 280 | defaultcf: 281 | level0-slowdown-writes-trigger: 282 | type: int 283 | range: [5, 30] 284 | default: 20 285 | level0-stop-writes-trigger: 286 | type: int 287 | range: [31, 50] 288 | default: 36 289 | pessimistic-txn: 290 | wait-for-lock-timeout: 291 | type: time 292 | range: [500ms, 2s] 293 | default: 1s 294 | wake-up-delay-duration: 295 | type: time 296 | range: [5ms, 50ms] 297 | default: 20ms 298 | pipelined: bool # default: false 299 | - tag: tidb 300 | serializer: toml 301 | target: "tidb.toml" 302 | value: 303 | mem-quota-query: 304 | type: int 305 | range: [536870912, 4294967296] # [0.5, 4] * 1024 ^ 3 306 | default: 1073741824 # 1024 ^ 3 307 | oom-use-tmp-storage: bool 308 | tmp-storage-quota: [-1, 1073741824] 309 | oom-action: ["log", "cancel"] 310 | lease: 311 | type: time 312 | range: [0s, 0s] 313 | performance: 314 | max-memory: 315 | type: int 316 | value: 0 # todo: find suitable range to fuzz 317 | default: 0 318 | when: prepared-plan-cache.enabled 319 | stats-lease: 320 | type: time 321 | range: [0s, 10s] 322 | default: 3s 323 | bind-info-lease: 324 | type: time 325 | range: [0s, 10s] 326 | default: 3s 327 | feedback-probability: 328 | type: float 329 | range: [0, 0.5] 330 | default: 0.05 331 | query-feedback-limit: 332 | type: int 333 | range: [128, 4096] 334 | default: 1024 335 | prepared-plan-cache: 336 | enabled: bool 337 | capacity: 338 | type: int 339 | range: [10, 1000] 340 | default: 100 341 | memory-guard-ratio: 342 | type: float 343 | range: [0, 1] 344 | default: 0.1 345 | tikv-client: 346 | grpc-connection-count: 347 | type: int 348 | range: [1, 64] 349 | default: 16 350 | commit-timeout: 351 | type: time 352 | range: [1s, 1m] 353 | default: 41s 354 | max-batch-size: 355 | type: int 356 | range: [1, 512] 357 | default: 128 358 | max-batch-wait-time: 359 | type: int 360 | range: [0, 1000000000] # [1ns, 1s] 361 | default: 0 362 | copr-cache: 363 | type: struct 364 | when: ["version", ">=", "4.0.0"] 365 | value: 366 | enable: bool 367 | capacity-mb: 368 | type: float 369 | range: [100, 10000] 370 | default: 1000 371 | admission-max-result-mb: 372 | type: float 373 | range: [1, 100] 374 | default: 10 375 | admission-min-process-ms: 376 | type: int 377 | range: [1,50] 378 | default: 5 379 | stmt-summary: 380 | max-stmt-count: 381 | type: int 382 | range: [10, 200] 383 | default: 100 384 | max-sql-length: 385 | type: int 386 | range: [1024, 8192] 387 | default: 4096 388 | refresh-interval: 389 | type: int 390 | range: [300, 3000] 391 | default: 1800 392 | history-size: 393 | type: int 394 | range: [6, 48] 395 | default: 24 396 | - tag: mysql-system-vars 397 | serializer: stmt 398 | target: "mysql-system-vars.sql" 399 | value: 400 | type: list 401 | value: 402 | - template: 403 | - 'SET SESSION autocommit = %s;' 404 | - 'SET GLOBAL autocommit = %s;' 405 | value: ["ON", "OFF"] 406 | - template: 407 | - 'SET SESSION sql_mode = "%s";' 408 | - 'SET GLOBAL sql_mode = "%s";' 409 | value: 410 | type: choice_n 411 | sep: ',' 412 | value: [ALLOW_INVALID_DATES, ANSI_QUOTES, ERROR_FOR_DIVISION_BY_ZERO, HIGH_NOT_PRECEDENCE, IGNORE_SPACE, NO_AUTO_VALUE_ON_ZERO, NO_BACKSLASH_ESCAPES, NO_DIR_IN_CREATE, NO_ENGINE_SUBSTITUTION, NO_UNSIGNED_SUBTRACTION, NO_ZERO_DATE, NO_ZERO_IN_DATE, ONLY_FULL_GROUP_BY, PAD_CHAR_TO_FULL_LENGTH, PIPES_AS_CONCAT, REAL_AS_FLOAT, STRICT_ALL_TABLES, STRICT_TRANS_TABLES, TIME_TRUNCATE_FRACTIONAL] 413 | - template: 414 | - 'SET SESSION tx_isolation = "%s";' 415 | - 'SET GLOBAL tx_isolation = "%s";' 416 | value: [READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE] 417 | - template: 418 | - 'SET SESSION max_execution_time = %d;' 419 | - 'SET GLOBAL max_execution_time = %d;' 420 | value: 421 | - 0 422 | - type: int 423 | range: [100, 1000] 424 | - template: 425 | - 'SET SESSION innodb_lock_wait_timeout = %d;' 426 | - 'SET GLOBAL innodb_lock_wait_timeout = %d;' 427 | value: 428 | type: int 429 | range: [1, 1073741824] 430 | - template: 431 | - 'SET SESSION windowing_use_high_precision = %s;' 432 | - 'SET GLOBAL windowing_use_high_precision = %s;' 433 | value: ["ON", "OFF"] 434 | - template: 435 | - 'SET SESSION sql_select_limit = %d;' 436 | - 'SET GLOBAL sql_select_limit = %d;' 437 | value: uint 438 | - tag: tidb-system-vars 439 | serializer: stmt 440 | target: "tidb-system-vars.sql" 441 | value: 442 | type: list 443 | value: 444 | - template: 'SET SESSION tidb_opt_agg_push_down = %d;' 445 | value: [0, 1] 446 | - template: 'SET SESSION tidb_opt_distinct_agg_push_down = %d;' 447 | value: [0, 1] 448 | - template: 'SET GLOBAL tidb_auto_analyze_ratio = %f;' 449 | value: 450 | type: float 451 | range: [0.1, 0.9] 452 | - template: 'SET SESSION tidb_build_stats_concurrency = %d;' 453 | value: 454 | type: int 455 | range: [2, 10] 456 | - template: 'SET SESSION tidb_checksum_table_concurrency = %d;' 457 | value: 458 | type: int 459 | range: [2, 10] 460 | - template: 461 | - 'SET SESSION tidb_distsql_scan_concurrency = %d;' 462 | - 'SET GLOBAL tidb_distsql_scan_concurrency = %d;' 463 | value: 464 | type: int 465 | range: [1, 30] 466 | default: 15 467 | - template: 468 | - 'SET SESSION tidb_index_lookup_size = %d;' 469 | - 'SET GLOBAL tidb_index_lookup_size = %d;' 470 | value: 471 | type: int 472 | range: [10000, 40000] 473 | default: 20000 474 | - template: 475 | - 'SET SESSION tidb_index_lookup_concurrency = %d;' 476 | - 'SET GLOBAL tidb_index_lookup_concurrency = %d;' 477 | value: 478 | type: int 479 | range: [1, 10] 480 | default: 4 481 | - template: 482 | - 'SET SESSION tidb_index_lookup_join_concurrency = %d;' 483 | - 'SET GLOBAL tidb_index_lookup_join_concurrency = %d;' 484 | value: 485 | type: int 486 | range: [1, 10] 487 | default: 4 488 | - template: 489 | - 'SET SESSION tidb_hash_join_concurrency = %d;' 490 | - 'SET GLOBAL tidb_hash_join_concurrency = %d;' 491 | value: 492 | type: int 493 | range: [1, 10] 494 | default: 5 495 | - template: 496 | - 'SET SESSION tidb_index_serial_scan_concurrency = %d;' 497 | - 'SET GLOBAL tidb_index_serial_scan_concurrency = %d;' 498 | value: 499 | type: int 500 | range: [1, 10] 501 | default: 1 502 | - template: 503 | - 'SET SESSION tidb_projection_concurrency = %d;' 504 | - 'SET GLOBAL tidb_projection_concurrency = %d;' 505 | value: 506 | type: int 507 | range: [1, 10] 508 | default: 4 509 | - template: 510 | - 'SET SESSION tidb_hashagg_final_concurrency = %d;' 511 | - 'SET GLOBAL tidb_hashagg_final_concurrency = %d;' 512 | value: 513 | type: int 514 | range: [1, 10] 515 | default: 4 516 | - template: 517 | - 'SET SESSION tidb_index_join_batch_size = %d;' 518 | - 'SET GLOBAL tidb_index_join_batch_size = %d;' 519 | value: 520 | type: int 521 | range: [10000, 50000] 522 | default: 25000 523 | - template: 524 | - 'SET SESSION tidb_skip_utf8_check = %d;' 525 | - 'SET GLOBAL tidb_skip_utf8_check = %d;' 526 | value: [0, 1] 527 | - template: 528 | - 'SET SESSION tidb_init_chunk_size = %d;' 529 | - 'SET GLOBAL tidb_init_chunk_size = %d;' 530 | value: 531 | type: int 532 | range: [8, 128] 533 | default: 32 534 | - template: 535 | - 'SET SESSION tidb_max_chunk_size = %d;' 536 | - 'SET GLOBAL tidb_max_chunk_size = %d;' 537 | value: 538 | type: int 539 | range: [128, 8192] 540 | default: 1024 541 | - template: 'SET SESSION tidb_mem_quota_query = %s;' 542 | value: 543 | type: size 544 | range: [500M, 4GB] 545 | default: 1GB 546 | - template: 'SET SESSION tidb_mem_quota_hashjoin = %s;' 547 | value: 548 | type: size 549 | range: [8GB, 64GB] 550 | default: 32GB 551 | - template: 'SET SESSION tidb_mem_quota_mergejoin = %s;' 552 | value: 553 | type: size 554 | range: [8GB, 64GB] 555 | default: 32GB 556 | - template: 'SET SESSION tidb_mem_quota_sort = %s;' 557 | value: 558 | type: size 559 | range: [8GB, 64GB] 560 | default: 32GB 561 | - template: 'SET SESSION tidb_mem_quota_topn = %s;' 562 | value: 563 | type: size 564 | range: [8GB, 64GB] 565 | default: 32GB 566 | - template: 'SET SESSION tidb_mem_quota_indexlookupreader = %s;' 567 | value: 568 | type: size 569 | range: [8GB, 64GB] 570 | default: 32GB 571 | - template: 'SET SESSION tidb_mem_quota_indexlookupjoin = %s;' 572 | value: 573 | type: size 574 | range: [8GB, 64GB] 575 | default: 32GB 576 | - template: 'SET SESSION tidb_mem_quota_nestedloopapply = %s;' 577 | value: 578 | type: size 579 | range: [8GB, 64GB] 580 | default: 32GB 581 | - template: 'SET SERVER tidb_enable_streaming = %d;' 582 | value: [0, 1] 583 | - template: 584 | - 'SET SESSION tidb_retry_limit = %s;' 585 | - 'SET GLOBAL tidb_retry_limit = %s;' 586 | value: ['on', 'off'] 587 | - template: 588 | - 'SET SESSION tidb_backoff_weight = %d;' 589 | - 'SET GLOBAL tidb_backoff_weight = %d;' 590 | value: 591 | type: int 592 | range: [1, 5] 593 | default: 2 594 | - template: 595 | - 'SET SESSION tidb_enable_table_partition = %s;' 596 | - 'SET GLOBAL tidb_enable_table_partition = %s;' 597 | value: ['on', 'off', 'auto'] 598 | - template: 599 | - 'SET SESSION tidb_backoff_lock_fast = %d;' 600 | - 'SET GLOBAL tidb_backoff_lock_fast = %d;' 601 | value: 602 | type: int 603 | range: [20, 200] 604 | default: 100 605 | - template: 'SET GLOBAL tidb_ddl_reorg_worker_cnt = %d;' 606 | value: 607 | type: int 608 | range: [1, 10] 609 | default: 4 610 | - template: 'SET GLOBAL tidb_ddl_reorg_batch_size = %d;' 611 | value: 612 | type: int 613 | range: [32, 10240] 614 | default: 256 615 | - template: 616 | - 'SET SESSION tidb_ddl_reorg_priority = %s;' 617 | - 'SET GLOBAL tidb_ddl_reorg_priority = %s;' 618 | value: [PRIORITY_LOW, PRIORITY_NORMAL, PRIORITY_HIGH] 619 | - template: 'SET GLOBAL tidb_ddl_error_count_limit = %d;' 620 | value: 621 | type: int 622 | range: [128, 1024] 623 | default: 512 624 | - template: 'SET GLOBAL tidb_max_delta_schema_count = %d;' 625 | value: 626 | type: int 627 | range: [100, 16384] 628 | default: 1024 629 | - template: 'SET SESSION tidb_force_priority = %s;' 630 | value: [NO_PRIORITY, LOW_PRIORITY, DELAYED, HIGH_PRIORITY] 631 | - template: 'SET GLOBAL tidb_row_format_version = %d;' 632 | value: [1, 2] 633 | - template: 634 | - 'SET SESSION tidb_txn_mode = %s;' 635 | - 'SET GLOBAL tidb_txn_mode = %s;' 636 | value: ["pessimistic", "optimistic", ""] 637 | - template: 638 | - 'SET SESSION tidb_constraint_check_in_place = %d;' 639 | - 'SET GLOBAL tidb_constraint_check_in_place = %d;' 640 | value: [0, 1] 641 | - template: 'SET SERVER tidb_check_mb4_value_in_utf8 = %d;' 642 | value: [0, 1] 643 | - template: 644 | - 'SET SESSION tidb_opt_insubq_to_join_and_agg = %d;' 645 | - 'SET GLOBAL tidb_opt_insubq_to_join_and_agg = %d;' 646 | value: [0, 1] 647 | - template: 648 | - 'SET SESSION tidb_opt_correlation_threshold = %f;' 649 | - 'SET GLOBAL tidb_opt_correlation_threshold = %f;' 650 | value: 651 | type: float 652 | range: [0.5, 0.99] 653 | default: 0.9 654 | - template: 655 | - 'SET SESSION tidb_opt_correlation_exp_factor = %f;' 656 | - 'SET GLOBAL tidb_opt_correlation_exp_factor = %f;' 657 | value: 658 | type: float 659 | range: [0, 1] 660 | default: 1 661 | - template: 662 | - 'SET SESSION tidb_enable_window_function = %d;' 663 | - 'SET GLOBAL tidb_enable_window_function = %d;' 664 | value: [0, 1] 665 | - template: 666 | - 'SET SESSION tidb_enable_fast_analyze = %d;' 667 | - 'SET GLOBAL tidb_enable_fast_analyze = %d;' 668 | value: [0, 1] 669 | - template: 670 | - 'SET SESSION tidb_init_chunk_size = %d;' 671 | - 'SET GLOBAL tidb_init_chunk_size = %d;' 672 | value: 673 | type: int 674 | range: [8, 128] 675 | default: 32 676 | - template: 'SET SESSION tidb_wait_split_region_finish = %d;' 677 | value: [0, 1] 678 | - template: 'SET GLOBAL tidb_scatter_region = %d;' 679 | value: [0, 1] 680 | - template: 681 | - 'SET SESSION tidb_init_chunk_size = %d;' 682 | - 'SET GLOBAL tidb_init_chunk_size = %d;' 683 | value: 684 | type: int 685 | range: [8, 128] 686 | default: 32 687 | - template: 'SET SESSION tidb_allow_remove_auto_inc = %d;' 688 | value: [0, 1] 689 | - template: 690 | - 'SET SESSION tidb_enable_stmt_summary = %d;' 691 | - 'SET GLOBAL tidb_enable_stmt_summary = %d;' 692 | value: [0, 1] 693 | - template: 'SET SESSION tidb_enable_chunk_rpc = %d;' 694 | value: [0, 1] 695 | - template: 'SET SESSION last_plan_from_cache = %d;' 696 | value: [0, 1] 697 | - template: 698 | - 'SET SESSION tidb_allow_batch_cop = %d;' 699 | - 'SET GLOBAL tidb_allow_batch_cop = %d;' 700 | value: [0, 1, 2] 701 | - template: 'SET GLOBAL tidb_enable_telemetry = %d;' 702 | value: [0, 1] -------------------------------------------------------------------------------- /examples/matrix-tiup.yaml: -------------------------------------------------------------------------------- 1 | configs: 2 | - tag: tiup 3 | serializer: yaml 4 | target: "topology.yaml" 5 | value: 6 | global: 7 | user: "tidb" 8 | ssh_port: 22 9 | deploy_dir: "/tidb-deploy" 10 | data_dir: "/tidb-data" 11 | # # Resource Control is used to limit the resource of an instance. 12 | # # See: https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html 13 | # # Supports using instance-level `resource_control` to override global `resource_control`. 14 | # resource_control: 15 | # # See: https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IOReadBandwidthMax=device%20bytes 16 | # memory_limit: "2G" 17 | # # See: https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IOReadBandwidthMax=device%20bytes 18 | # cpu_quota: "20%" 19 | # # See: https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#IOReadBandwidthMax=device%20bytes 20 | # io_read_bandwidth_max: "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 100M" 21 | # io_write_bandwidth_max: "/dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0 100M" 22 | 23 | # # Monitored variables are applied to all the machines. 24 | monitored: 25 | node_exporter_port: 9100 26 | blackbox_exporter_port: 9115 27 | # deploy_dir: "/tidb-deploy/monitored-9100" 28 | # data_dir: "/tidb-data/monitored-9100" 29 | # log_dir: "/tidb-deploy/monitored-9100/log" 30 | 31 | # # Server configs are used to specify the runtime configuration of TiDB components. 32 | # # All configuration items can be found in TiDB docs: 33 | # # 34 | # # - TiDB: https://pingcap.com/docs/stable/reference/configuration/tidb-server/configuration-file/ 35 | # # - TiKV: https://pingcap.com/docs/stable/reference/configuration/tikv-server/configuration-file/ 36 | # # - PD: https://pingcap.com/docs/stable/reference/configuration/pd-server/configuration-file/ 37 | # # 38 | # # All configuration items use points to represent the hierarchy, e.g: 39 | # # readpool.storage.use-unified-pool 40 | # # ^ ^ 41 | # # You can overwrite this configuration via the instance-level `config` field. 42 | 43 | server_configs: 44 | tidb: 45 | log.slow-threshold: 300 46 | binlog.enable: false 47 | binlog.ignore-error: false 48 | tikv: 49 | # server.grpc-concurrency: 4 50 | # raftstore.apply-pool-size: 2 51 | # raftstore.store-pool-size: 2 52 | # rocksdb.max-sub-compactions: 1 53 | # storage.block-cache.capacity: "16GB" 54 | # readpool.unified.max-thread-count: 12 55 | readpool.storage.use-unified-pool: false 56 | readpool.coprocessor.use-unified-pool: true 57 | pd: 58 | schedule.leader-schedule-limit: 4 59 | schedule.region-schedule-limit: 2048 60 | schedule.replica-schedule-limit: 64 61 | tiflash: 62 | # path_realtime_mode: false 63 | logger.level: "info" 64 | tiflash-learner: 65 | log-level: "info" 66 | # raftstore.apply-pool-size: 4 67 | # raftstore.store-pool-size: 4 68 | pump: 69 | gc: 7 70 | 71 | pd_servers: 72 | type: list 73 | value: 74 | - host: 10.0.1.11 75 | # ssh_port: 22 76 | # name: "pd-1" 77 | # client_port: 2379 78 | # peer_port: 2380 79 | # deploy_dir: "/tidb-deploy/pd-2379" 80 | # data_dir: "/tidb-data/pd-2379" 81 | # log_dir: "/tidb-deploy/pd-2379/log" 82 | # numa_node: "0,1" 83 | # # The following configs are used to overwrite the `server_configs.pd` values. 84 | # config: 85 | # schedule.max-merge-region-size: 20 86 | # schedule.max-merge-region-keys: 200000 87 | - host: 10.0.1.12 88 | - host: 10.0.1.13 89 | 90 | tidb_servers: 91 | type: list 92 | value: 93 | - host: 10.0.1.11 94 | # ssh_port: 22 95 | # port: 4000 96 | # status_port: 10080 97 | # deploy_dir: "/tidb-deploy/tidb-4000" 98 | # log_dir: "/tidb-deploy/tidb-4000/log" 99 | # numa_node: "0,1" 100 | # # The following configs are used to overwrite the `server_configs.tidb` values. 101 | # config: 102 | # log.level: warn 103 | # log.slow-query-file: tidb-slow-overwrited.log 104 | - host: 10.0.1.12 105 | 106 | tikv_servers: 107 | type: list 108 | value: 109 | - host: 10.0.1.14 110 | # ssh_port: 22 111 | # port: 20160 112 | # status_port: 20180 113 | # deploy_dir: "/tidb-deploy/tikv-20160" 114 | # data_dir: "/tidb-data/tikv-20160" 115 | # log_dir: "/tidb-deploy/tikv-20160/log" 116 | # numa_node: "0,1" 117 | # # The following configs are used to overwrite the `server_configs.tikv` values. 118 | # config: 119 | # server.grpc-concurrency: 4 120 | # server.labels: { zone: "zone1", dc: "dc1", host: "host1" } 121 | - host: 10.0.1.15 122 | - host: 10.0.1.16 123 | 124 | tiflash_servers: 125 | type: list 126 | value: 127 | - host: 10.0.1.14 128 | # ssh_port: 22 129 | # tcp_port: 9000 130 | # http_port: 8123 131 | # flash_service_port: 3930 132 | # flash_proxy_port: 20170 133 | # flash_proxy_status_port: 20292 134 | # metrics_port: 8234 135 | # deploy_dir: /tidb-deploy/tiflash-9000 136 | # data_dir: /tidb-data/tiflash-9000 137 | # log_dir: /tidb-deploy/tiflash-9000/log 138 | # numa_node: "0,1" 139 | # # 140 | # # `path_realtime_mode` only works if `data_dir` is specified with multiple paths. 141 | # # 142 | # # path_realtime_mode: 143 | # # "true" means only other paths instead of first path can be used to store older data. 144 | # # "false" means all paths can be used to store older data. 145 | # # 146 | # # TiFlash only uses the first path to store the latest data (i.e. "delta"). And others for the older data (i.e. "stable". which is the majority of data), 147 | # # 148 | # # E.g, if you intend to use an fast and smaller NVMe SSD (256GB) to speed up data ingestion speed in TiFlash, 149 | # # and 4 extra normal SSDs (512GB each) for main storage. Then your configurations should be look like: 150 | # # 151 | # # data_dir: /nvme_ssd_256/data,/ssd1_512/data,/ssd2_512/data,/ssd3_512/data,/ssd4_512/data 152 | # # config: 153 | # # path_realtime_mode: true 154 | # # 155 | # # 156 | # # And if your first disk is big enough, to fully use the capacity of it, use configurations look like: 157 | # # 158 | # # data_dir: /nvme_ssd_512/data,/ssd1_512/data,/ssd2_512/data,/ssd3_512/data,/ssd4_512/data 159 | # # config: 160 | # # path_realtime_mode: false 161 | # # 162 | # # 163 | # # The following configs are used to overwrite the `server_configs.tiflash` values. 164 | # config: 165 | # path_realtime_mode: false 166 | # logger.level: "info" 167 | # # The following configs are used to overwrite the `server_configs.tiflash-learner` values. 168 | # learner_config: 169 | # log-level: "info" 170 | # raftstore.apply-pool-size: 4 171 | # raftstore.store-pool-size: 4 172 | - host: 10.0.1.15 173 | - host: 10.0.1.16 174 | 175 | # pump_servers: 176 | # - host: 10.0.1.17 177 | # ssh_port: 22 178 | # port: 8250 179 | # deploy_dir: "/tidb-deploy/pump-8249" 180 | # data_dir: "/tidb-data/pump-8249" 181 | # log_dir: "/tidb-deploy/pump-8249/log" 182 | # numa_node: "0,1" 183 | # # The following configs are used to overwrite the `server_configs.drainer` values. 184 | # config: 185 | # gc: 7 186 | # - host: 10.0.1.18 187 | # - host: 10.0.1.19 188 | 189 | # drainer_servers: 190 | # - host: 10.0.1.17 191 | # port: 8249 192 | # data_dir: "/tidb-data/drainer-8249" 193 | # # If drainer doesn't have a checkpoint, use initial commitTS as the initial checkpoint. 194 | # # Will get a latest timestamp from pd if commit_ts is set to -1 (the default value). 195 | # commit_ts: -1 196 | # deploy_dir: "/tidb-deploy/drainer-8249" 197 | # log_dir: "/tidb-deploy/drainer-8249/log" 198 | # numa_node: "0,1" 199 | # # The following configs are used to overwrite the `server_configs.drainer` values. 200 | # config: 201 | # syncer.db-type: "mysql" 202 | # syncer.to.host: "127.0.0.1" 203 | # syncer.to.user: "root" 204 | # syncer.to.password: "" 205 | # syncer.to.port: 3306 206 | # syncer.ignore-table: 207 | # - db-name: test 208 | # tbl-name: log 209 | # - db-name: test 210 | # tbl-name: audit 211 | # - host: 10.0.1.19 212 | 213 | # cdc_servers: 214 | # - host: 10.0.1.20 215 | # ssh_port: 22 216 | # port: 8300 217 | # deploy_dir: "/tidb-deploy/cdc-8300" 218 | # log_dir: "/tidb-deploy/cdc-8300/log" 219 | # numa_node: "0,1" 220 | # - host: 10.0.1.21 221 | # - host: 10.0.1.22 222 | 223 | monitoring_servers: 224 | type: list 225 | value: 226 | - host: 10.0.1.11 227 | # ssh_port: 22 228 | # port: 9090 229 | # deploy_dir: "/tidb-deploy/prometheus-8249" 230 | # data_dir: "/tidb-data/prometheus-8249" 231 | # log_dir: "/tidb-deploy/prometheus-8249/log" 232 | 233 | grafana_servers: 234 | type: list 235 | value: 236 | - host: 10.0.1.11 237 | # port: 3000 238 | # deploy_dir: /tidb-deploy/grafana-3000 239 | 240 | alertmanager_servers: 241 | type: list 242 | value: 243 | - host: 10.0.1.11 244 | # ssh_port: 22 245 | # web_port: 9093 246 | # cluster_port: 9094 247 | # deploy_dir: "/tidb-deploy/alertmanager-9093" 248 | # data_dir: "/tidb-data/alertmanager-9093" 249 | # log_dir: "/tidb-deploy/alertmanager-9093/log" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chaos-mesh/matrix 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/BurntSushi/toml v0.3.1 7 | github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee 8 | github.com/ghodss/yaml v1.0.0 9 | github.com/onsi/ginkgo v1.14.0 10 | github.com/onsi/gomega v1.10.1 11 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad 12 | golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f // indirect 13 | gopkg.in/yaml.v2 v2.3.0 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= 4 | github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 7 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 8 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 9 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 10 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 11 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 12 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 13 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 14 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 15 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 16 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 17 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 18 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 19 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 20 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 21 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 22 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 23 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 24 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 25 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 26 | github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= 27 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 28 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 29 | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= 30 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 31 | github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= 32 | github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 33 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= 34 | github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= 35 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 36 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 37 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 38 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 39 | go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= 40 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 41 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 42 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 43 | go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= 44 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 45 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 46 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 47 | golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= 48 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 49 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 50 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 51 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 52 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 53 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= 54 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 55 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 56 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 57 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 58 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 59 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 60 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 61 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 62 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 63 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 64 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 65 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= 66 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 68 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 69 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 70 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 71 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 72 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 73 | golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f h1:NbrfHxef+IfdI86qCgO/1Siq1BuMH2xG0NqgvCguRhQ= 74 | golang.org/x/tools v0.0.0-20200309202150-20ab64c0d93f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 75 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 76 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 77 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 78 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 79 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 80 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 81 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 82 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 83 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 84 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 85 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 86 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 87 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 88 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 89 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 90 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 91 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 92 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 93 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 94 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 95 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 96 | -------------------------------------------------------------------------------- /pkg/context/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package context 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "path" 20 | "sort" 21 | 22 | "github.com/chaos-mesh/matrix/pkg/node" 23 | "github.com/chaos-mesh/matrix/pkg/random" 24 | "github.com/chaos-mesh/matrix/pkg/serializer" 25 | "github.com/chaos-mesh/matrix/pkg/synthesizer" 26 | ) 27 | 28 | type MatrixContext struct { 29 | Configs map[string]node.AbstractConfig 30 | } 31 | 32 | func sortedKeys(configs map[string]node.AbstractConfig) []string { 33 | var sortedKeys []string 34 | for i, _ := range configs { 35 | sortedKeys = append(sortedKeys, i) 36 | } 37 | sort.Strings(sortedKeys) 38 | return sortedKeys 39 | } 40 | 41 | // This is to generate real value from an abstract tree 42 | func (c MatrixContext) gen(seed int64) node.ConfigGroup { 43 | var result node.ConfigGroup 44 | 45 | result.Configs = make(map[serializer.Config]interface{}) 46 | 47 | random.Seed(seed) 48 | for _, k := range sortedKeys(c.Configs) { 49 | result.Configs[c.Configs[k].Config] = synthesizer.SimpleRecGen(c.Configs[k].Hollow) 50 | } 51 | 52 | return result 53 | } 54 | 55 | func (c MatrixContext) Dump(seed int64, output string) error { 56 | var err error 57 | values := c.gen(seed) 58 | for config, concrete := range values.Configs { 59 | err = config.Serializer.Dump(concrete, path.Join(output, config.Target)) 60 | if err != nil { 61 | return errors.New(fmt.Sprintf("error %s when dumping %v", err.Error(), concrete)) 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/node/abstract.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package node 15 | 16 | import ( 17 | "github.com/chaos-mesh/matrix/pkg/serializer" 18 | ) 19 | 20 | type AbstractConfig struct { 21 | Tag string 22 | serializer.Config 23 | Hollow interface{} 24 | } 25 | -------------------------------------------------------------------------------- /pkg/node/core.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package node 15 | 16 | import ( 17 | "github.com/chaos-mesh/matrix/pkg/serializer" 18 | ) 19 | 20 | type ConfigGroup struct { 21 | Configs map[serializer.Config]interface{} 22 | } 23 | -------------------------------------------------------------------------------- /pkg/node/data/concrete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package data 15 | 16 | type ConcreteInterface interface { 17 | ConcreteValue() interface{} 18 | } 19 | -------------------------------------------------------------------------------- /pkg/node/data/hollow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package data 15 | 16 | import ( 17 | "github.com/c2h5oh/datasize" 18 | ) 19 | 20 | type HollowInterface interface { 21 | GetType() string 22 | GetAllCondition() ([]HollowCondition, bool) 23 | SetCondition(condition *HollowCondition) 24 | } 25 | 26 | type HollowCondition struct{ Raw []interface{} } 27 | 28 | type HollowBool struct { 29 | Value *bool 30 | Condition *HollowCondition 31 | } 32 | 33 | type HollowInt struct { 34 | RangeStart, RangeEnd int 35 | Condition *HollowCondition 36 | } 37 | type HollowFloat struct { 38 | RangeStart, RangeEnd float64 39 | Condition *HollowCondition 40 | } 41 | type HollowString struct { 42 | Value string 43 | Condition *HollowCondition 44 | } 45 | type HollowTime struct { 46 | RangeStart, RangeEnd Time 47 | Condition *HollowCondition 48 | } 49 | type HollowSize struct { 50 | RangeStart, RangeEnd datasize.ByteSize 51 | Condition *HollowCondition 52 | } 53 | type HollowChoice struct { 54 | List []interface{} 55 | Condition *HollowCondition 56 | } 57 | type HollowChoiceN struct { 58 | HollowChoice 59 | N int 60 | Sep string 61 | } 62 | type HollowList struct { 63 | List []interface{} 64 | Condition *HollowCondition 65 | } 66 | type HollowMap struct { 67 | Map map[string]interface{} 68 | Condition *HollowCondition 69 | } 70 | 71 | var _ HollowInterface = (*HollowBool)(nil) 72 | var _ HollowInterface = (*HollowInt)(nil) 73 | var _ HollowInterface = (*HollowFloat)(nil) 74 | var _ HollowInterface = (*HollowString)(nil) 75 | var _ HollowInterface = (*HollowTime)(nil) 76 | var _ HollowInterface = (*HollowSize)(nil) 77 | var _ HollowInterface = (*HollowChoice)(nil) 78 | var _ HollowInterface = (*HollowList)(nil) 79 | var _ HollowInterface = (*HollowMap)(nil) 80 | 81 | func (h HollowBool) GetType() string { return TypeBool } 82 | func (h HollowInt) GetType() string { return TypeInt } 83 | func (h HollowFloat) GetType() string { return TypeFloat } 84 | func (h HollowString) GetType() string { return TypeString } 85 | func (h HollowTime) GetType() string { return TypeTime } 86 | func (h HollowSize) GetType() string { return TypeSize } 87 | func (h HollowChoice) GetType() string { return TypeChoice } 88 | func (h HollowList) GetType() string { return TypeList } 89 | func (h HollowMap) GetType() string { return TypeMap } 90 | 91 | func returnCond(condition *HollowCondition) ([]HollowCondition, bool) { 92 | if condition == nil { 93 | return []HollowCondition{}, false 94 | } 95 | return []HollowCondition{*condition}, true 96 | } 97 | 98 | func returnListCond(condition *HollowCondition, list []interface{}) ([]HollowCondition, bool) { 99 | condList, _ := returnCond(condition) 100 | for _, v := range list { 101 | childCond, _ := v.(HollowInterface).GetAllCondition() 102 | for _, cond := range childCond { 103 | condList = append(condList, cond) 104 | } 105 | } 106 | return condList, len(condList) > 0 107 | } 108 | 109 | func (h HollowBool) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 110 | func (h HollowInt) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 111 | func (h HollowFloat) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 112 | func (h HollowString) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 113 | func (h HollowTime) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 114 | func (h HollowSize) GetAllCondition() ([]HollowCondition, bool) { return returnCond(h.Condition) } 115 | func (h HollowChoice) GetAllCondition() ([]HollowCondition, bool) { 116 | return returnListCond(h.Condition, h.List) 117 | } 118 | func (h HollowList) GetAllCondition() ([]HollowCondition, bool) { 119 | return returnListCond(h.Condition, h.List) 120 | } 121 | func (h HollowMap) GetAllCondition() ([]HollowCondition, bool) { 122 | condList, _ := returnCond(h.Condition) 123 | for _, v := range h.Map { 124 | childCond, _ := v.(HollowInterface).GetAllCondition() 125 | for _, cond := range childCond { 126 | condList = append(condList, cond) 127 | } 128 | } 129 | return condList, len(condList) > 0 130 | } 131 | 132 | func (h HollowBool) SetCondition(condition *HollowCondition) { h.Condition = condition } 133 | func (h HollowInt) SetCondition(condition *HollowCondition) { h.Condition = condition } 134 | func (h HollowFloat) SetCondition(condition *HollowCondition) { h.Condition = condition } 135 | func (h HollowString) SetCondition(condition *HollowCondition) { h.Condition = condition } 136 | func (h HollowTime) SetCondition(condition *HollowCondition) { h.Condition = condition } 137 | func (h HollowSize) SetCondition(condition *HollowCondition) { h.Condition = condition } 138 | func (h HollowChoice) SetCondition(condition *HollowCondition) { h.Condition = condition } 139 | func (h HollowList) SetCondition(condition *HollowCondition) { h.Condition = condition } 140 | func (h HollowMap) SetCondition(condition *HollowCondition) { h.Condition = condition } 141 | -------------------------------------------------------------------------------- /pkg/node/data/time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package data 15 | 16 | import "time" 17 | 18 | type Time struct { 19 | time time.Duration 20 | } 21 | 22 | func NewTime(t int64) Time { 23 | return Time{time: time.Duration(t)} 24 | } 25 | 26 | func NewTimeFromString(s string) (*Time, error) { 27 | d, err := time.ParseDuration(s) 28 | if err != nil { 29 | return nil, err 30 | } 31 | return &Time{time: d}, nil 32 | } 33 | 34 | func (t Time) MarshalText() ([]byte, error) { 35 | return []byte(t.time.String()), nil 36 | } 37 | 38 | func (t Time) Nanoseconds() int64 { return t.time.Nanoseconds() } 39 | -------------------------------------------------------------------------------- /pkg/node/data/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package data 15 | 16 | import "github.com/chaos-mesh/matrix/pkg/utils" 17 | 18 | const ( 19 | TypeBool = "bool" 20 | TypeInt = "int" 21 | TypeUint = "uint" // uint is a shortcut for int starts from zero rather than a representation of uint type 22 | TypeFloat = "float" 23 | TypeString = "string" 24 | TypeSize = "size" 25 | TypeTime = "time" 26 | TypeList = "list" 27 | TypeChoice = "choice" 28 | TypeChoiceN = "choice_n" // randomly choose n elements 29 | TypeMap = "map" 30 | TypeStruct = "struct" // struct is an alias of map 31 | ) 32 | 33 | var AllTypes = []string{TypeBool, TypeInt, TypeUint, TypeFloat, TypeString, TypeSize, TypeTime, TypeList, TypeChoice, TypeMap, TypeStruct, TypeChoiceN} 34 | 35 | var DefaultValue = map[string]interface{}{ 36 | TypeBool: HollowBool{}, 37 | TypeInt: HollowInt{RangeStart: utils.MinInt, RangeEnd: utils.MaxInt}, 38 | TypeUint: HollowInt{RangeStart: 0, RangeEnd: utils.MaxInt}, 39 | TypeFloat: HollowFloat{RangeStart: 0, RangeEnd: 1}, 40 | TypeString: HollowString{Value: ""}, 41 | TypeSize: HollowSize{}, 42 | TypeTime: HollowTime{}, 43 | } 44 | -------------------------------------------------------------------------------- /pkg/node/surface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package node 15 | 16 | // raw config file 17 | 18 | type MatrixConfigFile struct { 19 | Configs []RawConfig `yaml:"configs"` 20 | } 21 | 22 | type RawConfig struct { 23 | Tag string `yaml:"tag"` 24 | Serializer string `yaml:"serializer"` 25 | Target string `yaml:"target"` 26 | Value interface{} `yaml:"value"` 27 | } 28 | -------------------------------------------------------------------------------- /pkg/parser/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package parser 15 | 16 | import ( 17 | "errors" 18 | 19 | "github.com/chaos-mesh/matrix/pkg/context" 20 | "github.com/chaos-mesh/matrix/pkg/node" 21 | "github.com/chaos-mesh/matrix/pkg/serializer" 22 | ) 23 | 24 | func ParseFile(rawData node.MatrixConfigFile) (*context.MatrixContext, error) { 25 | var ctx context.MatrixContext 26 | 27 | ctx.Configs = make(map[string]node.AbstractConfig) 28 | 29 | for _, config := range rawData.Configs { 30 | conf, err := parseConfig(config) 31 | if err != nil { 32 | return nil, err 33 | } 34 | if _, exist := ctx.Configs[conf.Tag]; exist { 35 | return nil, errors.New("%s appear twice in config") 36 | } 37 | ctx.Configs[conf.Tag] = *conf 38 | } 39 | return &ctx, nil 40 | } 41 | 42 | func parseConfig(rawConfig node.RawConfig) (*node.AbstractConfig, error) { 43 | var conf node.AbstractConfig 44 | var err error 45 | 46 | conf.Tag = rawConfig.Tag 47 | 48 | if err = parseSerializerConfig(rawConfig, &conf.Config); err != nil { 49 | return nil, err 50 | } 51 | if conf.Hollow, err = parseTree(rawConfig.Value); err != nil { 52 | return nil, err 53 | } 54 | 55 | return &conf, nil 56 | } 57 | 58 | func parseSerializerConfig(rawConfig node.RawConfig, out *serializer.Config) error { 59 | var err error 60 | out.Serializer, err = serializer.ParseSerializerName(rawConfig.Serializer) 61 | if err != nil { 62 | return err 63 | } 64 | out.Target = rawConfig.Target 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /pkg/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package parser 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/chaos-mesh/matrix/pkg/node/data" 20 | 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | ) 24 | 25 | func TestParser(t *testing.T) { 26 | RegisterFailHandler(Fail) 27 | 28 | RunSpecs(t, "Parser Suite") 29 | } 30 | 31 | var _ = Describe("Parser", func() { 32 | Context("Parser", func() { 33 | It("Parser Types", func() { 34 | initValueParserMap() 35 | for _, dataType := range data.AllTypes { 36 | _, ok := valueParserMap[dataType] 37 | Expect(ok).To(BeTrue()) 38 | } 39 | }) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /pkg/parser/value.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package parser 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | 20 | "github.com/c2h5oh/datasize" 21 | 22 | "github.com/pingcap/log" 23 | 24 | "github.com/chaos-mesh/matrix/pkg/node/data" 25 | "github.com/chaos-mesh/matrix/pkg/utils" 26 | ) 27 | 28 | const ExprNotSupportedMessage = "expr not supported" 29 | 30 | func parseTree(rawValue interface{}) (interface{}, error) { 31 | switch rawValue.(type) { 32 | case map[string]interface{}: 33 | return parseMap(rawValue.(map[string]interface{})) 34 | case []interface{}: 35 | return parseChoice(rawValue.([]interface{})) 36 | default: 37 | return parseLiteral(rawValue) 38 | } 39 | } 40 | 41 | func parseLiteral(rawLiteral interface{}) (interface{}, error) { 42 | switch rawLiteral.(type) { 43 | case float64: 44 | return parseFloat(rawLiteral.(float64)), nil 45 | case bool: 46 | b := rawLiteral.(bool) 47 | return data.HollowBool{Value: &b}, nil 48 | case string: 49 | return parseString(rawLiteral.(string)) 50 | default: 51 | log.L().Warn(fmt.Sprintf("%s not handled, return HollowInt", rawLiteral)) 52 | return data.HollowInt{RangeStart: utils.MinInt, RangeEnd: utils.MaxInt}, nil 53 | } 54 | } 55 | 56 | func parseValueMap(rawMap map[string]interface{}) (interface{}, error) { 57 | valueType, ok := rawMap["type"] 58 | if !ok { 59 | return nil, errors.New("no `type` field") 60 | } 61 | stringValueType, ok := valueType.(string) 62 | if !ok { 63 | return nil, errors.New(fmt.Sprintf("`type` of %v is not of type string", rawMap)) 64 | } 65 | if valueParserMap == nil { 66 | initValueParserMap() 67 | } 68 | valueParser, ok := valueParserMap[stringValueType] 69 | if !ok { 70 | return nil, errors.New(fmt.Sprintf("no parser for type \"%s\"", stringValueType)) 71 | } 72 | hollowValue, err := valueParser(rawMap) 73 | if err == nil { 74 | if rawCondition, ok := rawMap["when"]; ok { 75 | delete(rawMap, "when") 76 | var hollowCondition *data.HollowCondition 77 | hollowCondition, err = parseCondition(rawCondition) 78 | if err != nil { 79 | return nil, err 80 | } 81 | hollowValue.(data.HollowInterface).SetCondition(hollowCondition) 82 | } 83 | 84 | if _, ok = rawMap["default"]; ok { 85 | // todo: store default value 86 | delete(rawMap, "default") 87 | } 88 | } 89 | return hollowValue, err 90 | } 91 | 92 | func parseMap(rawMap map[string]interface{}) (interface{}, error) { 93 | var err error 94 | hollowValue, err := parseValueMap(rawMap) 95 | if err == nil { 96 | return hollowValue, err 97 | } 98 | var hollowMap data.HollowMap 99 | hollowMap.Map = make(map[string]interface{}) 100 | for k := range rawMap { 101 | var v interface{} 102 | v, err = parseTree(rawMap[k]) 103 | if err != nil { 104 | return nil, err 105 | } 106 | hollowMap.Map[k] = v 107 | } 108 | return hollowMap, nil 109 | } 110 | 111 | func parseChoice(rawList []interface{}) (interface{}, error) { 112 | var hollowChoice data.HollowChoice 113 | if len(rawList) == 0 { 114 | return nil, errors.New("no available option for choice") 115 | } 116 | hollowChoice.List = make([]interface{}, len(rawList)) 117 | for i, k := range rawList { 118 | hollowBranch, err := parseTree(k) 119 | if err != nil { 120 | return nil, err 121 | } 122 | hollowChoice.List[i] = hollowBranch 123 | } 124 | return hollowChoice, nil 125 | } 126 | 127 | func parseCondition(rawCond interface{}) (*data.HollowCondition, error) { 128 | switch rawCond.(type) { 129 | case string: 130 | case []interface{}: 131 | default: 132 | log.L().Warn(fmt.Sprintf("ignoring constraint: %s", rawCond)) 133 | } 134 | return nil, nil 135 | } 136 | 137 | func parseDefault(rawString string) (interface{}, error) { 138 | if hollow, ok := data.DefaultValue[rawString]; ok { 139 | return hollow, nil 140 | } 141 | return nil, errors.New(fmt.Sprintf("not default value for %s", rawString)) 142 | } 143 | 144 | // parseString convert string literals to corresponding values, default value will be handled here 145 | func parseString(rawString string) (interface{}, error) { 146 | defaultValue, err := parseDefault(rawString) 147 | if err == nil { 148 | return defaultValue, nil 149 | } else { 150 | return data.HollowString{Value: rawString}, nil 151 | } 152 | } 153 | 154 | func parseFloat(rawFloat float64) interface{} { 155 | // int / float literal 156 | intValue := int(rawFloat) 157 | if rawFloat == float64(intValue) { 158 | return data.HollowInt{RangeStart: intValue, RangeEnd: intValue} 159 | } else { 160 | return data.HollowFloat{RangeStart: rawFloat, RangeEnd: rawFloat} 161 | } 162 | } 163 | 164 | func parseHollowInt(raw map[string]interface{}) (interface{}, error) { 165 | var hollowInt data.HollowInt 166 | var err error 167 | 168 | if intRange, ok := raw["range"]; ok { 169 | hollowInt.RangeStart, hollowInt.RangeEnd, err = parseIntRange(intRange) 170 | if err != nil { 171 | return nil, err 172 | } 173 | } 174 | return hollowInt, nil 175 | } 176 | 177 | func parseHollowFloat(raw map[string]interface{}) (interface{}, error) { 178 | var hollowFloat data.HollowFloat 179 | var err error 180 | 181 | if floatRange, ok := raw["range"]; ok { 182 | hollowFloat.RangeStart, hollowFloat.RangeEnd, err = parseFloatRange(floatRange) 183 | if err != nil { 184 | return nil, err 185 | } 186 | } 187 | return hollowFloat, nil 188 | } 189 | 190 | func parseHollowTime(raw map[string]interface{}) (interface{}, error) { 191 | var hollowTime data.HollowTime 192 | var err error 193 | 194 | if timeRange, ok := raw["range"]; ok { 195 | hollowTime.RangeStart, hollowTime.RangeEnd, err = parseTimeRange(timeRange) 196 | if err != nil { 197 | return nil, err 198 | } 199 | } 200 | return hollowTime, nil 201 | } 202 | 203 | func parseHollowSize(raw map[string]interface{}) (interface{}, error) { 204 | var hollowSize data.HollowSize 205 | var err error 206 | 207 | if timeRange, ok := raw["range"]; ok { 208 | hollowSize.RangeStart, hollowSize.RangeEnd, err = parseSizeRange(timeRange) 209 | if err != nil { 210 | return nil, err 211 | } 212 | } 213 | return hollowSize, nil 214 | } 215 | 216 | func parseIntRange(raw interface{}) (int, int, error) { 217 | rangeList := raw.([]interface{}) 218 | dur := make([]int, 2) 219 | for i, v := range rangeList { 220 | switch v.(type) { 221 | case float64: 222 | dur[i] = int(v.(float64)) 223 | default: 224 | return 0, 0, errors.New(fmt.Sprintf("%s cannot be parsed as int", v)) 225 | } 226 | } 227 | return dur[0], dur[1], nil 228 | } 229 | 230 | func parseFloatRange(raw interface{}) (float64, float64, error) { 231 | rangeList := raw.([]interface{}) 232 | dur := make([]float64, 2) 233 | for i, v := range rangeList { 234 | switch v.(type) { 235 | case float64: 236 | dur[i] = v.(float64) 237 | default: 238 | return 0, 0, errors.New(fmt.Sprintf("%s cannot be parsed as float", v)) 239 | } 240 | } 241 | return dur[0], dur[1], nil 242 | } 243 | 244 | func parseTimeRange(raw interface{}) (data.Time, data.Time, error) { 245 | rangeList := raw.([]interface{}) 246 | dur := make([]data.Time, 2) 247 | for i, v := range rangeList { 248 | if timeStr, ok := v.(string); ok { 249 | t, err := data.NewTimeFromString(timeStr) 250 | if err != nil { 251 | return data.NewTime(0), data.NewTime(0), err 252 | } 253 | dur[i] = *t 254 | } else { 255 | return data.NewTime(0), data.NewTime(0), errors.New(fmt.Sprintf("%s cannot be parsed as time", v)) 256 | } 257 | } 258 | return dur[0], dur[1], nil 259 | } 260 | 261 | func parseSizeRange(raw interface{}) (datasize.ByteSize, datasize.ByteSize, error) { 262 | var err error 263 | rangeList := raw.([]interface{}) 264 | dur := make([]datasize.ByteSize, 2) 265 | for i, v := range rangeList { 266 | if sizeStr, ok := v.(string); ok { 267 | var size datasize.ByteSize 268 | err = size.UnmarshalText([]byte(sizeStr)) 269 | if err != nil { 270 | return datasize.ByteSize(0), datasize.ByteSize(0), err 271 | } 272 | dur[i] = size 273 | } else { 274 | return datasize.ByteSize(0), datasize.ByteSize(0), errors.New(fmt.Sprintf("%s cannot be parsed as size", v)) 275 | } 276 | } 277 | return dur[0], dur[1], nil 278 | } 279 | 280 | func parseHollowList(raw map[string]interface{}) (interface{}, error) { 281 | if rawList, ok := raw["value"]; ok { 282 | if rawList, ok := rawList.([]interface{}); ok { 283 | var list data.HollowList 284 | var err error 285 | list.List = make([]interface{}, len(rawList)) 286 | for i, v := range rawList { 287 | list.List[i], err = parseTree(v) 288 | if err != nil { 289 | return nil, err 290 | } 291 | } 292 | return list, nil 293 | } else { 294 | return nil, errors.New(fmt.Sprintf("field `value` of type `list` is not if type `[]interface{}`: %s", rawList)) 295 | } 296 | } else { 297 | return nil, errors.New("type `list` does not contain field `value`") 298 | } 299 | } 300 | 301 | func parseHollowChoice(raw map[string]interface{}) (interface{}, error) { 302 | if rawList, ok := raw["value"]; ok { 303 | if rawList, ok := rawList.([]interface{}); ok { 304 | return parseChoice(rawList) 305 | } else { 306 | return nil, errors.New(fmt.Sprintf("field `value` of type `choice` is not if type `[]interface{}`: %s", rawList)) 307 | } 308 | } else { 309 | return nil, errors.New("type `choice` does not contain field `value`") 310 | } 311 | } 312 | 313 | func parseHollowChoiceN(raw map[string]interface{}) (interface{}, error) { 314 | hollowChoice, err := parseHollowChoice(raw) 315 | if err != nil { 316 | return nil, err 317 | } 318 | hollowChoiceN := data.HollowChoiceN{HollowChoice: hollowChoice.(data.HollowChoice)} 319 | if n, ok := raw["n"]; ok { 320 | if intN, ok := n.(int); ok { 321 | hollowChoiceN.N = intN 322 | } else { 323 | return nil, errors.New(fmt.Sprintf("`n` of choice_n is not of type int: %v", n)) 324 | } 325 | } 326 | if sep, ok := raw["sep"]; ok { 327 | if strSep, ok := sep.(string); ok { 328 | hollowChoiceN.Sep = strSep 329 | } else { 330 | return nil, errors.New(fmt.Sprintf("`sep` of choice_n is not of type string: %v", sep)) 331 | } 332 | } 333 | return hollowChoiceN, nil 334 | } 335 | 336 | func parseHollowMap(raw map[string]interface{}) (interface{}, error) { 337 | if rawList, ok := raw["value"]; ok { 338 | if rawMap, ok := rawList.(map[string]interface{}); ok { 339 | var hollowMap data.HollowMap 340 | var err error 341 | hollowMap.Map = make(map[string]interface{}, len(rawMap)) 342 | for i, v := range rawMap { 343 | hollowMap.Map[i], err = parseTree(v) 344 | if err != nil { 345 | return nil, err 346 | } 347 | } 348 | return hollowMap, nil 349 | } else { 350 | return nil, errors.New(fmt.Sprintf("field `value` of type `map` is not if type `map[string]interface{}`: %s", rawMap)) 351 | } 352 | } else { 353 | return nil, errors.New("type `map` does not contain field `value`") 354 | } 355 | } 356 | 357 | var valueParserMap map[string]func(map[string]interface{}) (interface{}, error) 358 | 359 | func initValueParserMap() { 360 | valueParserMap = map[string]func(map[string]interface{}) (interface{}, error){ 361 | data.TypeBool: func(rawBool map[string]interface{}) (interface{}, error) { 362 | if rawBoolValue, ok := rawBool["value"]; ok { 363 | if b, ok := rawBoolValue.(bool); ok { 364 | return data.HollowBool{Value: &b}, nil 365 | } else { 366 | return nil, errors.New("`value` field of bool is not of type bool") 367 | } 368 | } 369 | return data.HollowBool{}, nil 370 | }, 371 | data.TypeString: func(rawBool map[string]interface{}) (interface{}, error) { 372 | if rawBoolValue, ok := rawBool["value"]; ok { 373 | if s, ok := rawBoolValue.(string); ok { 374 | return data.HollowString{Value: s}, nil 375 | } else { 376 | return nil, errors.New("`value` field of string is not of type string") 377 | } 378 | } 379 | return data.HollowBool{}, nil 380 | }, 381 | data.TypeUint: func(_ map[string]interface{}) (interface{}, error) { 382 | return nil, errors.New("type `uint` is only used for simple type syntax") 383 | }, 384 | data.TypeInt: parseHollowInt, 385 | data.TypeFloat: parseHollowFloat, 386 | data.TypeList: parseHollowList, 387 | data.TypeChoice: parseHollowChoice, 388 | data.TypeMap: parseHollowMap, 389 | data.TypeStruct: parseHollowMap, 390 | data.TypeTime: parseHollowTime, 391 | data.TypeSize: parseHollowSize, 392 | data.TypeChoiceN: parseHollowChoiceN, 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /pkg/random/random.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package random 15 | 16 | import ( 17 | "fmt" 18 | "math/rand" 19 | 20 | "github.com/c2h5oh/datasize" 21 | 22 | "github.com/chaos-mesh/matrix/pkg/node/data" 23 | "github.com/chaos-mesh/matrix/pkg/utils" 24 | ) 25 | 26 | const selectRangeBoundaryProb = 0.05 27 | 28 | func Seed(seed int64) { 29 | rand.Seed(seed) 30 | } 31 | 32 | func randUIntN(ui uint) uint { 33 | if ui <= uint(utils.MaxInt) { 34 | return uint(rand.Intn(int(ui))) 35 | } else { 36 | return uint(rand.Intn(utils.MaxInt)) + randUIntN(ui-uint(utils.MaxInt)) 37 | } 38 | } 39 | 40 | // return true with a probability of `prob` 41 | func randProb(prob float64) bool { 42 | if prob < 0 { 43 | return false 44 | } else if prob >= 1 { 45 | return true 46 | } else { 47 | return prob > rand.Float64() 48 | } 49 | } 50 | 51 | func randAny(values ...interface{}) interface{} { 52 | return values[randUIntN(uint(len(values)))] 53 | } 54 | 55 | func RandInt(start int, end int) int { 56 | if start == end { 57 | return start 58 | } else if randProb(selectRangeBoundaryProb) { 59 | return randAny(start, end).(int) 60 | } else if start < end { 61 | return int(randUIntN(uint(end-start))) + start 62 | } else { 63 | panic("`RandInt` get a larger start than end") 64 | } 65 | } 66 | 67 | func RandFloat(start float64, end float64) float64 { 68 | if start == end { 69 | return start 70 | } else if randProb(selectRangeBoundaryProb) { 71 | return randAny(start, end).(float64) 72 | } else if start < end { 73 | return rand.Float64()*(start-end) + end 74 | } else { 75 | panic("`RandFloat` get a larger start than end") 76 | } 77 | } 78 | 79 | func RandChoose(brs []interface{}) interface{} { 80 | return brs[randUIntN(uint(len(brs)))] 81 | } 82 | 83 | func RandChooseN(brs []interface{}, n int) []interface{} { 84 | if n > len(brs) { 85 | panic(fmt.Sprintf("`RandChooseN` n out of bound: %d, %v", n, brs)) 86 | } 87 | var idx, selectedIdx []int 88 | for i := 0; i < len(brs); i++ { 89 | idx = append(idx, i) 90 | } 91 | rand.Shuffle(len(idx), func(i, j int) { idx[i], idx[j] = idx[j], idx[i] }) 92 | for i := 0; i < n; i++ { 93 | selectedIdx = append(selectedIdx, idx[i]) 94 | } 95 | results := make([]interface{}, n) 96 | for i, idx := range selectedIdx { 97 | results[i] = brs[idx] 98 | } 99 | return results 100 | } 101 | 102 | func sizeUnit(size datasize.ByteSize) datasize.ByteSize { 103 | switch { 104 | case size >= datasize.EB: 105 | return datasize.EB 106 | case size >= datasize.PB: 107 | return datasize.PB 108 | case size >= datasize.TB: 109 | return datasize.TB 110 | case size >= datasize.GB: 111 | return datasize.GB 112 | case size >= datasize.MB: 113 | return datasize.MB 114 | case size >= datasize.KB: 115 | return datasize.KB 116 | default: 117 | return 1 118 | } 119 | } 120 | 121 | func RandSize(start datasize.ByteSize, end datasize.ByteSize) datasize.ByteSize { 122 | if randProb(selectRangeBoundaryProb) { 123 | return randAny(start, end).(datasize.ByteSize) 124 | } 125 | 126 | var unit uint64 127 | if start == 0 { 128 | unit = uint64(sizeUnit(end)) 129 | } else { 130 | unit = uint64(sizeUnit(start)) 131 | } 132 | 133 | startInt, endInt := int(uint64(start)/unit), int(uint64(end)/unit) 134 | 135 | return datasize.ByteSize(uint64(RandInt(startInt, endInt)) * unit) 136 | } 137 | 138 | func RandTime(start data.Time, end data.Time) data.Time { 139 | if randProb(selectRangeBoundaryProb) { 140 | return randAny(start, end).(data.Time) 141 | } 142 | 143 | startInt, endInt := start.Nanoseconds(), end.Nanoseconds() 144 | 145 | var unit int64 = 1 146 | for startInt > 10 { 147 | startInt /= 10 148 | endInt /= 10 149 | unit *= 10 150 | } 151 | 152 | if startInt <= int64(utils.MaxInt) && endInt <= int64(utils.MaxInt) { 153 | return data.NewTime(int64(RandInt(int(startInt), int(endInt))) * unit) 154 | } else { 155 | panic(fmt.Sprintf("time range too wide: %v - %v", start, end)) 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /pkg/serializer/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package serializer 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | ) 20 | 21 | type Config struct { 22 | Target string 23 | Serializer Serializer 24 | } 25 | 26 | type Serializer interface { 27 | Dump(value interface{}, target string) error 28 | } 29 | 30 | func ParseSerializerName(name string) (Serializer, error) { 31 | serializer, ok := serializerMap[name] 32 | if ok { 33 | return serializer, nil 34 | } 35 | return nil, errors.New(fmt.Sprintf("%s not a valid serializer name", name)) 36 | } 37 | 38 | var serializerMap = map[string]Serializer{ 39 | "toml": TomlSerializer{}, 40 | "yaml": YamlSerializer{}, 41 | "stmt": StatementSerializer{}, 42 | } 43 | -------------------------------------------------------------------------------- /pkg/serializer/stmt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package serializer 15 | 16 | import ( 17 | "errors" 18 | "fmt" 19 | "os" 20 | "strings" 21 | 22 | "github.com/chaos-mesh/matrix/pkg/utils" 23 | ) 24 | 25 | type StatementSerializer struct{} 26 | 27 | func (s StatementSerializer) Dump(value interface{}, target string) error { 28 | var lines []string 29 | f, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 30 | if err != nil { 31 | return err 32 | } 33 | 34 | lines, err = valueToLines(value) 35 | if err != nil { 36 | return err 37 | } 38 | _, err = f.WriteString(strings.Join(lines, "\n")) 39 | return err 40 | } 41 | 42 | func itemToLine(value interface{}) (string, error) { 43 | if valueMap, ok := value.(map[string]interface{}); ok { 44 | templateValue, templateOk := valueMap["template"] 45 | valueValue, valueOk := valueMap["value"] 46 | if templateOk && valueOk && len(valueMap) == 2 { 47 | stringTemplateValue, templateOk := templateValue.(string) 48 | if templateOk { 49 | return fmt.Sprintf(stringTemplateValue, valueValue), nil 50 | } else { 51 | return "", errors.New(fmt.Sprintf("`template` not a string: %v", templateValue)) 52 | } 53 | } else { 54 | return "", errors.New(fmt.Sprintf("keys should be `template` and `value`, got: %v", utils.Keys(valueMap))) 55 | } 56 | } else { 57 | return "", errors.New("") 58 | } 59 | } 60 | 61 | func valueToLines(value interface{}) ([]string, error) { 62 | var lines []string 63 | if valueList, ok := value.([]interface{}); ok { 64 | for _, v := range valueList { 65 | line, err := itemToLine(v) 66 | if err != nil { 67 | return nil, err 68 | } 69 | lines = append(lines, line) 70 | } 71 | } else { 72 | return nil, errors.New(fmt.Sprintf("`%v` is not a list", value)) 73 | } 74 | 75 | if lines == nil { 76 | lines = append(lines, "") 77 | } 78 | return lines, nil 79 | } 80 | -------------------------------------------------------------------------------- /pkg/serializer/toml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package serializer 15 | 16 | import ( 17 | "os" 18 | 19 | "github.com/BurntSushi/toml" 20 | "gopkg.in/yaml.v2" 21 | ) 22 | 23 | type TomlSerializer struct{} 24 | type YamlSerializer struct{} 25 | 26 | func (s TomlSerializer) Dump(value interface{}, target string) error { 27 | f, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 28 | if err != nil { 29 | return err 30 | } 31 | encoder := toml.NewEncoder(f) 32 | encoder.Indent = "" 33 | return encoder.Encode(value) 34 | } 35 | 36 | func (s YamlSerializer) Dump(value interface{}, target string) error { 37 | var text []byte 38 | var err error 39 | var f *os.File 40 | text, err = yaml.Marshal(value) 41 | if err != nil { 42 | return err 43 | } 44 | f, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 45 | if err != nil { 46 | return err 47 | } 48 | _, err = f.Write(text) 49 | return err 50 | } 51 | -------------------------------------------------------------------------------- /pkg/synthesizer/simple.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package synthesizer 15 | 16 | import ( 17 | "fmt" 18 | "sort" 19 | "strings" 20 | 21 | "github.com/pingcap/log" 22 | 23 | "github.com/chaos-mesh/matrix/pkg/node/data" 24 | "github.com/chaos-mesh/matrix/pkg/random" 25 | ) 26 | 27 | func SimpleRecGen(hollow interface{}) interface{} { 28 | switch hollow.(type) { 29 | case data.HollowBool: 30 | b := hollow.(data.HollowBool).Value 31 | if b != nil { 32 | var boolValue bool = *b 33 | return boolValue 34 | } 35 | return random.RandChoose([]interface{}{true, false}) 36 | case data.HollowInt: 37 | return random.RandInt(hollow.(data.HollowInt).RangeStart, hollow.(data.HollowInt).RangeEnd) 38 | case data.HollowFloat: 39 | return random.RandFloat(hollow.(data.HollowFloat).RangeStart, hollow.(data.HollowFloat).RangeEnd) 40 | case data.HollowTime: 41 | return random.RandTime(hollow.(data.HollowTime).RangeStart, hollow.(data.HollowTime).RangeEnd) 42 | case data.HollowSize: 43 | return random.RandSize(hollow.(data.HollowSize).RangeStart, hollow.(data.HollowSize).RangeEnd) 44 | case data.HollowString: 45 | hollowString := hollow.(data.HollowString) 46 | if hollowString.Value != "" { 47 | return hollowString.Value 48 | } else { 49 | return "rand-string" 50 | } 51 | case data.HollowMap: 52 | res := make(map[string]interface{}) 53 | for _, k := range sortedMapKey(hollow.(data.HollowMap).Map) { 54 | 55 | res[k] = SimpleRecGen(hollow.(data.HollowMap).Map[k]) 56 | } 57 | return res 58 | case data.HollowList: 59 | var res []interface{} 60 | for _, v := range hollow.(data.HollowList).List { 61 | res = append(res, SimpleRecGen(v)) 62 | } 63 | return res 64 | case data.HollowChoice: 65 | return SimpleRecGen(random.RandChoose(hollow.(data.HollowChoice).List)) 66 | case data.HollowChoiceN: 67 | choiceN := hollow.(data.HollowChoiceN) 68 | n := choiceN.N 69 | if n == 0 { 70 | n = random.RandInt(1, len(choiceN.List)) 71 | } 72 | results := make([]interface{}, n) 73 | strResults := make([]string, n) 74 | tryJoin := true 75 | for i, hollow := range random.RandChooseN(choiceN.List, n) { 76 | v := SimpleRecGen(hollow) 77 | results[i] = v 78 | if tryJoin { 79 | s, isStr := v.(string) 80 | tryJoin = tryJoin && isStr 81 | strResults[i] = s 82 | } 83 | } 84 | if tryJoin { 85 | return strings.Join(strResults, choiceN.Sep) 86 | } else { 87 | return results 88 | } 89 | default: 90 | log.L().Warn(fmt.Sprintf("unhandled value: %v", hollow)) 91 | return fmt.Sprintf("%s", hollow) 92 | } 93 | } 94 | 95 | func sortedMapKey(rawMap map[string]interface{}) []string { 96 | var keys []string 97 | for s, _ := range rawMap { 98 | keys = append(keys, s) 99 | } 100 | sort.Strings(keys) 101 | return keys 102 | } 103 | -------------------------------------------------------------------------------- /pkg/utils/limits.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package utils 15 | 16 | const MaxUint = ^uint(0) 17 | const MaxInt = int(MaxUint >> 1) 18 | const MinInt = -MaxInt - 1 19 | -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package utils 15 | 16 | func Keys(_map map[string]interface{}) []string { 17 | var keys []string 18 | for k, _ := range _map { 19 | keys = append(keys, k) 20 | } 21 | return keys 22 | } 23 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 PingCAP, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package main 15 | 16 | import ( 17 | "flag" 18 | "fmt" 19 | "os" 20 | "path/filepath" 21 | 22 | "github.com/chaos-mesh/matrix/api" 23 | 24 | "github.com/pingcap/log" 25 | ) 26 | 27 | var ( 28 | conf string 29 | output string 30 | seed int64 31 | err error 32 | ) 33 | 34 | func main() { 35 | flag.StringVar(&conf, "c", "", "config file") 36 | flag.StringVar(&output, "d", ".", "output folder") 37 | flag.Int64Var(&seed, "s", 0, "seed of rand, default UTC nanoseconds of now") 38 | flag.Parse() 39 | 40 | if conf == "" { 41 | panic("config file not provided") 42 | } 43 | output, err = filepath.Abs(output) 44 | if err != nil { 45 | panic(fmt.Sprintf("output folder invalid: %s", output)) 46 | } 47 | if stat, err := os.Stat(output); os.IsNotExist(err) || !stat.IsDir() { 48 | panic(fmt.Sprintf("output folder does not exist: %s", output)) 49 | } else { 50 | log.L().Info(fmt.Sprintf("dumpping to %s", output)) 51 | } 52 | 53 | err := api.Gen(conf, output, seed) 54 | if err != nil { 55 | panic(err) 56 | } 57 | } 58 | --------------------------------------------------------------------------------