├── .bingo ├── .gitignore ├── README.md ├── Variables.mk ├── controller-gen.mod ├── controller-gen.sum ├── embedmd.mod ├── embedmd.sum ├── go.mod ├── gojsontoyaml.mod ├── gojsontoyaml.sum ├── jsonnet.mod ├── jsonnet.sum ├── jsonnetfmt.mod ├── jsonnetfmt.sum └── variables.env ├── .github └── workflows │ ├── artifacts.yml │ └── go.yml ├── .gitignore ├── .go-version ├── .goreleaser.yaml ├── Dockerfile ├── Dockerfile.goreleaser ├── LICENSE ├── Makefile ├── README.md ├── examples ├── basic │ ├── basic.jsonnet │ └── basic.yaml └── storage │ ├── storage.jsonnet │ └── storage.yaml ├── go.mod ├── go.sum ├── kubernetes.libsonnet ├── monitoring ├── alerts │ └── cockroachdb.libsonnet ├── config.libsonnet ├── examples.jsonnet ├── examples │ └── prometheus.yaml ├── mixin.libsonnet └── rules │ └── cockroachdb.libsonnet ├── operator ├── actions │ ├── decommission.go │ ├── initialize.go │ ├── initialize_test.go │ └── recommission.go ├── api │ └── v1alphav1 │ │ ├── cockroachdb_types.go │ │ ├── groupversion_info.go │ │ └── zz_generated.deepcopy.go ├── config.yaml ├── deployment.jsonnet ├── deployment.yaml ├── examples │ ├── basic.yaml │ ├── groovelink.yaml │ └── storage.yaml ├── main.go ├── main.jsonnet └── metalmatze.de_cockroachdbs.yaml └── screenshot.png /.bingo/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore everything 3 | * 4 | 5 | # But not these files: 6 | !.gitignore 7 | !*.mod 8 | !*.sum 9 | !README.md 10 | !Variables.mk 11 | !variables.env 12 | 13 | *tmp.mod 14 | -------------------------------------------------------------------------------- /.bingo/README.md: -------------------------------------------------------------------------------- 1 | # Project Development Dependencies. 2 | 3 | This is directory which stores Go modules with pinned buildable package that is used within this repository, managed by https://github.com/bwplotka/bingo. 4 | 5 | * Run `bingo get` to install all tools having each own module file in this directory. 6 | * Run `bingo get ` to install that have own module file in this directory. 7 | * For Makefile: Make sure to put `include .bingo/Variables.mk` in your Makefile, then use $() variable where is the .bingo/.mod. 8 | * For shell: Run `source .bingo/variables.env` to source all environment variable for each tool. 9 | * For go: Import `.bingo/variables.go` to for variable names. 10 | * See https://github.com/bwplotka/bingo or -h on how to add, remove or change binaries dependencies. 11 | 12 | ## Requirements 13 | 14 | * Go 1.14+ 15 | -------------------------------------------------------------------------------- /.bingo/Variables.mk: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | BINGO_DIR := $(dir $(lastword $(MAKEFILE_LIST))) 4 | GOPATH ?= $(shell go env GOPATH) 5 | GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin 6 | GO ?= $(shell which go) 7 | 8 | # Below generated variables ensure that every time a tool under each variable is invoked, the correct version 9 | # will be used; reinstalling only if needed. 10 | # For example for controller-gen variable: 11 | # 12 | # In your main Makefile (for non array binaries): 13 | # 14 | #include .bingo/Variables.mk # Assuming -dir was set to .bingo . 15 | # 16 | #command: $(CONTROLLER_GEN) 17 | # @echo "Running controller-gen" 18 | # @$(CONTROLLER_GEN) 19 | # 20 | CONTROLLER_GEN := $(GOBIN)/controller-gen-v0.8.0 21 | $(CONTROLLER_GEN): $(BINGO_DIR)/controller-gen.mod 22 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 23 | @echo "(re)installing $(GOBIN)/controller-gen-v0.8.0" 24 | @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=controller-gen.mod -o=$(GOBIN)/controller-gen-v0.8.0 "sigs.k8s.io/controller-tools/cmd/controller-gen" 25 | 26 | EMBEDMD := $(GOBIN)/embedmd-v1.0.0 27 | $(EMBEDMD): $(BINGO_DIR)/embedmd.mod 28 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 29 | @echo "(re)installing $(GOBIN)/embedmd-v1.0.0" 30 | @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=embedmd.mod -o=$(GOBIN)/embedmd-v1.0.0 "github.com/campoy/embedmd" 31 | 32 | GOJSONTOYAML := $(GOBIN)/gojsontoyaml-v0.1.0 33 | $(GOJSONTOYAML): $(BINGO_DIR)/gojsontoyaml.mod 34 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 35 | @echo "(re)installing $(GOBIN)/gojsontoyaml-v0.1.0" 36 | @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=gojsontoyaml.mod -o=$(GOBIN)/gojsontoyaml-v0.1.0 "github.com/brancz/gojsontoyaml" 37 | 38 | JSONNET := $(GOBIN)/jsonnet-v0.18.1-0.20220703203929-b42132a7a37d 39 | $(JSONNET): $(BINGO_DIR)/jsonnet.mod 40 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 41 | @echo "(re)installing $(GOBIN)/jsonnet-v0.18.1-0.20220703203929-b42132a7a37d" 42 | @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=jsonnet.mod -o=$(GOBIN)/jsonnet-v0.18.1-0.20220703203929-b42132a7a37d "github.com/google/go-jsonnet/cmd/jsonnet" 43 | 44 | JSONNETFMT := $(GOBIN)/jsonnetfmt-v0.18.0 45 | $(JSONNETFMT): $(BINGO_DIR)/jsonnetfmt.mod 46 | @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. 47 | @echo "(re)installing $(GOBIN)/jsonnetfmt-v0.18.0" 48 | @cd $(BINGO_DIR) && $(GO) build -mod=mod -modfile=jsonnetfmt.mod -o=$(GOBIN)/jsonnetfmt-v0.18.0 "github.com/google/go-jsonnet/cmd/jsonnetfmt" 49 | 50 | -------------------------------------------------------------------------------- /.bingo/controller-gen.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require sigs.k8s.io/controller-tools v0.8.0 // cmd/controller-gen 6 | -------------------------------------------------------------------------------- /.bingo/embedmd.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/campoy/embedmd v1.0.0 6 | -------------------------------------------------------------------------------- /.bingo/embedmd.sum: -------------------------------------------------------------------------------- 1 | github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY= 2 | github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | -------------------------------------------------------------------------------- /.bingo/go.mod: -------------------------------------------------------------------------------- 1 | module _ // Fake go.mod auto-created by 'bingo' for go -moddir compatibility with non-Go projects. Commit this file, together with other .mod files. -------------------------------------------------------------------------------- /.bingo/gojsontoyaml.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/brancz/gojsontoyaml v0.1.0 6 | -------------------------------------------------------------------------------- /.bingo/gojsontoyaml.sum: -------------------------------------------------------------------------------- 1 | github.com/brancz/gojsontoyaml v0.0.0-20200602132005-3697ded27e8c h1:hb6WqfcKQZlNx/vahy51SaIvKnoXD5609Nm0PC4msEM= 2 | github.com/brancz/gojsontoyaml v0.0.0-20200602132005-3697ded27e8c/go.mod h1:+00lOjYXPgMfxHVPvg9GDtc3BX5Xh5aFpB4gMB8gfMo= 3 | github.com/brancz/gojsontoyaml v0.0.0-20201216083616-202f76bf8c1f h1:eyhpHbo03QUlPHSTt5El8XayORJVl9/7Im3HXV0zRAY= 4 | github.com/brancz/gojsontoyaml v0.0.0-20201216083616-202f76bf8c1f/go.mod h1:eiVwwkJ9JtehmGNiFz03FXpw889jH63XGuhIeug3u4s= 5 | github.com/brancz/gojsontoyaml v0.1.0 h1:SdzR3+BCVOqaI42nFGTeaB7/2DgDM4fhuvRLqxatA8M= 6 | github.com/brancz/gojsontoyaml v0.1.0/go.mod h1:+ycZY94+V11XZBUaDEsbLr3hPNS/ZPrDVKKNUg3Sgvg= 7 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 8 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 10 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 11 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 12 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 13 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 14 | -------------------------------------------------------------------------------- /.bingo/jsonnet.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/google/go-jsonnet v0.18.1-0.20220703203929-b42132a7a37d // cmd/jsonnet 6 | -------------------------------------------------------------------------------- /.bingo/jsonnet.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 4 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 5 | github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= 6 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 7 | github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= 8 | github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 9 | github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0= 10 | github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= 11 | github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY= 12 | github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= 13 | github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= 14 | github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= 15 | github.com/google/go-jsonnet v0.18.1-0.20220703203929-b42132a7a37d h1:mANOH4pk05EYdTmjIFFpeurQKh59HEvmpM3ecdIzWJg= 16 | github.com/google/go-jsonnet v0.18.1-0.20220703203929-b42132a7a37d/go.mod h1:y2qyoQwQbMuxzV/asvS6mLzYDkpNFzhfWhimDXTQ9qY= 17 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 18 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 19 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 20 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= 21 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 22 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 23 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 24 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 25 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 26 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 27 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 28 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 29 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 30 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 31 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 32 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 33 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 34 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 35 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 36 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 37 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= 38 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 39 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= 40 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 43 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 44 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 45 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 46 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 47 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 48 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 49 | -------------------------------------------------------------------------------- /.bingo/jsonnetfmt.mod: -------------------------------------------------------------------------------- 1 | module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT 2 | 3 | go 1.14 4 | 5 | require github.com/google/go-jsonnet v0.18.0 // cmd/jsonnetfmt 6 | -------------------------------------------------------------------------------- /.bingo/jsonnetfmt.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= 4 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 5 | github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= 6 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= 7 | github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0= 8 | github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= 9 | github.com/google/go-jsonnet v0.17.0 h1:/9NIEfhK1NQRKl3sP2536b2+x5HnZMdql7x3yK/l8JY= 10 | github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw= 11 | github.com/google/go-jsonnet v0.18.0 h1:/6pTy6g+Jh1a1I2UMoAODkqELFiVIdOxbNwv0DDzoOg= 12 | github.com/google/go-jsonnet v0.18.0/go.mod h1:C3fTzyVJDslXdiTqw/bTFk7vSGyCtH3MGRbDfvEwGd0= 13 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 14 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 15 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 16 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= 17 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 18 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= 19 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 20 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 21 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 22 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 23 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 24 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 25 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 26 | github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 27 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 28 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 29 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 30 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 31 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 32 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= 34 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 35 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 36 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 37 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 38 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 39 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 40 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 41 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 42 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 43 | -------------------------------------------------------------------------------- /.bingo/variables.env: -------------------------------------------------------------------------------- 1 | # Auto generated binary variables helper managed by https://github.com/bwplotka/bingo v0.6. DO NOT EDIT. 2 | # All tools are designed to be build inside $GOBIN. 3 | # Those variables will work only until 'bingo get' was invoked, or if tools were installed via Makefile's Variables.mk. 4 | GOBIN=${GOBIN:=$(go env GOBIN)} 5 | 6 | if [ -z "$GOBIN" ]; then 7 | GOBIN="$(go env GOPATH)/bin" 8 | fi 9 | 10 | 11 | CONTROLLER_GEN="${GOBIN}/controller-gen-v0.8.0" 12 | 13 | EMBEDMD="${GOBIN}/embedmd-v1.0.0" 14 | 15 | GOJSONTOYAML="${GOBIN}/gojsontoyaml-v0.1.0" 16 | 17 | JSONNET="${GOBIN}/jsonnet-v0.18.1-0.20220703203929-b42132a7a37d" 18 | 19 | JSONNETFMT="${GOBIN}/jsonnetfmt-v0.18.0" 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Artifacts 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | branches: 8 | - main 9 | - release-* 10 | pull_request: 11 | branches: 12 | - main 13 | - release-* 14 | 15 | env: 16 | GORELEASER_VERSION: v1.10.3 17 | 18 | jobs: 19 | build-and-publish: 20 | name: Build and publish using goreleaser 21 | runs-on: ubuntu-latest 22 | permissions: 23 | packages: write 24 | contents: write 25 | issues: write 26 | steps: 27 | - name: Check out code into the Go module directory 28 | uses: actions/checkout@v3.0.2 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: Fetch all tags 33 | run: git fetch --force --tags 34 | 35 | - name: Set up Go 36 | uses: actions/setup-go@v3.2.1 37 | with: 38 | go-version-file: .go-version 39 | check-latest: true 40 | cache: true 41 | 42 | - name: Set Tag 43 | run: | 44 | echo "goreleaser_current_tag=`git describe --match 'v*' --tags`" >> $GITHUB_ENV 45 | 46 | - name: Get branch name 47 | shell: bash 48 | run: echo "GITHUB_BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV 49 | 50 | - name: Login to container registry 51 | run: | 52 | echo "${{ secrets.GITHUB_TOKEN }}" | docker login -u metalmatze --password-stdin ghcr.io 53 | 54 | - name: Build and publish using goreleaser 55 | uses: goreleaser/goreleaser-action@v3.0.0 56 | with: 57 | distribution: goreleaser 58 | version: ${{ env.GORELEASER_VERSION }} 59 | args: release --rm-dist 60 | env: 61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | GORELEASER_CURRENT_TAG: "${{ env.goreleaser_current_tag }}" 63 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version-file: .go-version 20 | cache: true 21 | check-latest: true 22 | 23 | - name: Test 24 | run: go test -v ./... 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.tags 2 | /operator/kubernetes.libsonnet 3 | /operator/operator 4 | /tmp 5 | 6 | dist/ 7 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.20.0 2 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - main: ./operator 3 | goos: 4 | - linux 5 | goarch: 6 | - amd64 7 | - arm64 8 | env: 9 | - CGO_ENABLED=0 10 | dockers: 11 | - image_templates: 12 | - "ghcr.io/metalmatze/kube-cockroachdb:{{ .Version }}-amd64" 13 | use: buildx 14 | dockerfile: Dockerfile.goreleaser 15 | build_flag_templates: 16 | - "--platform=linux/amd64" 17 | extra_files: 18 | - operator/config.yaml 19 | - operator/main.jsonnet 20 | - kubernetes.libsonnet 21 | - image_templates: 22 | - "ghcr.io/metalmatze/kube-cockroachdb:{{ .Version }}-arm64" 23 | use: buildx 24 | goarch: arm64 25 | dockerfile: Dockerfile.goreleaser 26 | build_flag_templates: 27 | - "--platform=linux/arm64/v8" 28 | extra_files: 29 | - operator/config.yaml 30 | - operator/main.jsonnet 31 | - kubernetes.libsonnet 32 | docker_manifests: 33 | - name_template: ghcr.io/metalmatze/kube-cockroachdb:{{ .Version }} 34 | image_templates: 35 | - ghcr.io/metalmatze/kube-cockroachdb:{{ .Version }}-amd64 36 | - ghcr.io/metalmatze/kube-cockroachdb:{{ .Version }}-arm64 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20-alpine AS build 2 | 3 | ARG ARCH=amd64 4 | ENV CGO_ENABLED=0 5 | ENV GOOS=linux 6 | ENV GOARCH=$ARCH 7 | 8 | RUN mkdir /app && apk add --no-cache make 9 | WORKDIR /app 10 | COPY . /app 11 | 12 | RUN make build -o operator/api/v1alphav1/zz_generated.deepcopy.go 13 | RUN cp ./kubernetes.libsonnet ./operator/kubernetes.libsonnet 14 | 15 | FROM alpine:3.18 16 | 17 | COPY --from=build /app/operator/operator /kube-cockroachdb/operator/operator 18 | COPY --from=build /app/operator/config.yaml /kube-cockroachdb/operator/config.yaml 19 | COPY --from=build /app/operator/main.jsonnet /kube-cockroachdb/operator/main.jsonnet 20 | COPY --from=build /app/operator/kubernetes.libsonnet /kube-cockroachdb/operator/kubernetes.libsonnet 21 | 22 | WORKDIR /kube-cockroachdb/operator 23 | ENTRYPOINT [ "/kube-cockroachdb/operator/operator" ] 24 | -------------------------------------------------------------------------------- /Dockerfile.goreleaser: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | COPY kube-cockroachdb /kube-cockroachdb/operator 4 | COPY operator/config.yaml /kube-cockroachdb/config.yaml 5 | COPY operator/main.jsonnet /kube-cockroachdb/main.jsonnet 6 | COPY kubernetes.libsonnet /kube-cockroachdb/kubernetes.libsonnet 7 | 8 | WORKDIR /kube-cockroachdb 9 | ENTRYPOINT [ "/kube-cockroachdb/operator" ] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include .bingo/Variables.mk 2 | 3 | all: test build examples README.md 4 | 5 | build: operator/operator 6 | 7 | .PHONY: test 8 | test: 9 | CGO_ENABLED=0 go test -v ./operator/... 10 | 11 | operator/operator: $(shell find ./operator -type f -name '*.go') 12 | CGO_ENABLED=0 go build -v -o operator ./operator/... 13 | 14 | operator/metalmatze.de_cockroachdbs.yaml: $(shell find ./operator/api/v1alphav1 -type f -name '*.go') | $(CONTROLLER_GEN) 15 | $(CONTROLLER_GEN) crd paths="./operator/..." output:crd:artifacts:config=./operator 16 | 17 | operator/api/v1alphav1/zz_generated.deepcopy.go: $(shell find ./operator/api/v1alphav1 -type f -name '*.go' -not -name '*.deepcopy.go') | $(CONTROLLER_GEN) 18 | $(CONTROLLER_GEN) object paths="./operator/..." 19 | 20 | operator/deployment.yaml: operator/deployment.jsonnet 21 | $(JSONNETFMT) -i operator/deployment.jsonnet 22 | $(JSONNET) operator/deployment.jsonnet | $(GOJSONTOYAML) > operator/deployment.yaml 23 | 24 | examples: examples/basic/basic.yaml examples/storage/storage.yaml 25 | 26 | examples/basic/basic.yaml: examples/basic/basic.jsonnet kubernetes.libsonnet | $(JSONNET) $(JSONNETFMT) $(GOJSONTOYAML) 27 | $(JSONNETFMT) -i kubernetes.libsonnet examples/basic/basic.jsonnet 28 | $(JSONNET) examples/basic/basic.jsonnet | $(GOJSONTOYAML) > examples/basic/basic.yaml 29 | 30 | examples/storage/storage.yaml: examples/storage/storage.jsonnet kubernetes.libsonnet | $(JSONNET) $(JSONNETFMT) $(GOJSONTOYAML) 31 | $(JSONNETFMT) -i kubernetes.libsonnet examples/storage/storage.jsonnet 32 | $(JSONNET) examples/storage/storage.jsonnet | $(GOJSONTOYAML) > examples/storage/storage.yaml 33 | 34 | README.md: $(shell find examples/ -name "*.jsonnet") | $(EMBEDMD) .bingo/bin/gh-md-toc 35 | $(EMBEDMD) -w README.md 36 | .bingo/bin/gh-md-toc --insert README.md > /dev/null 37 | -rm -rf README.md.{orig,toc}.* 38 | 39 | PHONY: .tags 40 | .tags: 41 | echo "latest,$(shell git rev-parse --short HEAD)" > .tags 42 | 43 | monitoring/examples/prometheus.yaml: $(shell find monitoring/ -type f -and -name "*.jsonnet" -or -name "*.libsonnet") | $(JSONNET) $(GOJSONTOYAML) 44 | $(JSONNET) monitoring/examples.jsonnet | $(GOJSONTOYAML) > monitoring/examples/prometheus.yaml 45 | 46 | .bingo/bin/gh-md-toc: 47 | curl -Lo $@ https://raw.githubusercontent.com/ekalinin/github-markdown-toc/master/gh-md-toc 48 | chmod +x $@ 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kube-CockroachDB 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/metalmatze/kube-cockroachdb)](https://goreportcard.com/report/github.com/metalmatze/kube-cockroachdb) 3 | 4 | Deploy [CockroachDB](https://www.cockroachlabs.com/product/) on [Kubernetes](https://kubernetes.io/) using [YAML](https://yaml.org/) or an [Operator](https://coreos.com/blog/introducing-operators.html). 5 | 6 | ![](screenshot.png) 7 | 8 | 9 | * [Kube-CockroachDB](#kube-cockroachdb) 10 | * [YAML with Jsonnet & CockroachDB Operator](#yaml-with-jsonnet--cockroachdb-operator) 11 | * [Jsonnet Example](#jsonnet-example) 12 | * [Operator Example](#operator-example) 13 | * [Monitoring](#monitoring) 14 | * [Prometheus Alerting and Recording Rules & Grafana Dashboards](#prometheus-alerting-and-recording-rules--grafana-dashboards) 15 | * [Backups](#backups) 16 | * [Operator](#operator) 17 | * [Installation](#installation) 18 | * [Operator-only features](#operator-only-features) 19 | * [Autoscaling](#autoscaling) 20 | * [Autoscaling using the Horizontal Pod Autoscaler](#autoscaling-using-the-horizontal-pod-autoscaler) 21 | 22 | 23 | 24 | 25 | 26 | ## YAML with Jsonnet & CockroachDB Operator 27 | 28 | The main focus of this project is to generate all necessary Kubernetes files, like StatefulSets, Services, CronJobs, and more with [Jsonnet](https://jsonnet.org). 29 | The idea is to build an abstraction layer on top of the Kubernetes objects, which allows to specify high-level configuration for a CockroachDB instance. 30 | 31 | While using these Jsonnet files alone is fine, we can use them as foundation to build an Operator on top of it. The [Locutus](https://github.com/brancz/locutus) project, which used by the Operator as a library, makes use of Jsonnet to template all Kubernetes objects every time as Custom Resource Definition (CRD), in our case a CockroachDB, changes. Before and after these objects are generated we can hook into the [actions](https://github.com/metalmatze/kube-cockroachdb/tree/main/operator/actions) the Operator performs and run more specific things like [initializing](https://github.com/metalmatze/kube-cockroachdb/blob/main/operator/actions/initialize.go), [decommissioning](https://github.com/metalmatze/kube-cockroachdb/blob/main/operator/actions/decommission.go) and [recommissioning](https://github.com/metalmatze/kube-cockroachdb/blob/main/operator/actions/recommission.go) of nodes. 32 | That way we can make use of Jsonnet for templating and use Go for really specific actions we need to perform on the CockroachDB cluster. 33 | 34 | ### Jsonnet Example 35 | 36 | Generating a YAML file containing all necessary Kubernetes objects with: 37 | 38 | [embedmd]:#(examples/basic/basic.jsonnet) 39 | ```jsonnet 40 | local kubernetes = import '../../kubernetes.libsonnet'; 41 | 42 | local objects = kubernetes({ 43 | name: 'example', 44 | replicas: 3, 45 | }); 46 | 47 | // Let's generate a List containing all Kubernetes objects 48 | { 49 | apiVersion: 'v1', 50 | kind: 'List', 51 | items: 52 | [objects[name] for name in std.objectFields(objects)], 53 | } 54 | ``` 55 | 56 | *You can play around by changing this file `examples/basic/basic.jsonnet` and then running `make examples/basic/basic.yaml`.* 57 | 58 | ### Operator Example 59 | 60 | Similarly to the Jsonnet example above, where the object is passed into the `kubernetes()` function, we can specify a spec object in our 61 | CockroachDB Custom Resource Definition (CRD) to change the replicas or image. 62 | 63 | *In the background the Operator actually generates the same Jsonnet object and then passes it to the same function, as seen in the Jsonnet Example above: [operator/main.jsonnet](https://github.com/metalmatze/kube-cockroachdb/blob/aeb623c601ed78f1e48421b77c68f73c51180d0c/operator/main.jsonnet#L20-L31)* 64 | 65 | [embedmd]:#(operator/examples/basic.yaml) 66 | ```yaml 67 | apiVersion: metalmatze.de/v1alpha1 68 | kind: CockroachDB 69 | metadata: 70 | name: basic 71 | namespace: default 72 | labels: 73 | app.kubernetes.io/component: database 74 | app.kubernetes.io/instance: basic 75 | spec: 76 | replicas: 3 77 | image: cockroachdb/cockroach:v20.1.5 78 | ``` 79 | 80 | ## Monitoring 81 | 82 | By default a *Service Monitor* Custom Resource for the [Prometheus Operator](https://github.com/coreos/prometheus-operator) is generated. This gives an out-of-the-box monitoring experience when running a Prometheus Operator on the same cluster. 83 | This project tries its best to be compatible with the [kube-prometheus](https://github.com/coreos/kube-prometheus/) project. Once you have setup monitoring with kube-prometheus its Prometheus is going to automatically start scraping the deployed CockroachDB instances. 84 | 85 | ### Prometheus Alerting and Recording Rules & Grafana Dashboards 86 | 87 | *I've created an Issue to discuss the addition of Prometheus recording & alerting rules and Grafana dashboards: [#2](https://github.com/metalmatze/kube-cockroachdb/issues/2).* 88 | 89 | ## Backups 90 | 91 | We want backups to be as easy as possible, so that as many users as possible make use of them. 92 | For a start we want to add a Kubernetes CronJob that is configured to create a SQL dump and then save it to an S3 compatible object storage. 93 | 94 | *This feature isn't part of the YAML or Operator yet, but I've created an [issue to discuss its addition](https://github.com/metalmatze/kube-cockroachdb/issues/1)*. 95 | 96 | ## Operator 97 | 98 | ### Installation 99 | 100 | The Operator comes as a pre-build container images: [quay.io/metalmatze/kube-cockroachdb](https://quay.io/metalmatze/kube-cockroachdb). 101 | 102 | The `operator/deployment/` contains a file that you can use to the deploy an instance of the Operator to deploy it to the `cockroachdb` namespace in your cluster. It'll start watching all `CockroachDB` Custom Resource Definitions (CRDs) in all namespaces by default. 103 | 104 | ```bash 105 | # Operator currently depends on the ServiceMonitor CRD 106 | kubectl apply -f https://raw.githubusercontent.com/coreos/kube-prometheus/main/manifests/setup/prometheus-operator-0servicemonitorCustomResourceDefinition.yaml 107 | # CockroachDB Custom Resource Definition 108 | kubectl apply -f https://raw.githubusercontent.com/metalmatze/kube-cockroachdb/main/operator/metalmatze.de_cockroachdbs.yaml 109 | # We'll deploy the operator to this namespace 110 | kubectl create namespace kube-cockroachdb 111 | # Deploy the actual operator 112 | kubectl apply -f https://raw.githubusercontent.com/metalmatze/kube-cockroachdb/main/operator/deployment.yaml 113 | # Optionally deploy a example instance 114 | kubectl apply -f https://raw.githubusercontent.com/metalmatze/kube-cockroachdb/main/operator/examples/basic.yaml 115 | ``` 116 | 117 | *If you want an Operator deployment that's only able to read and write it's own namespace, please open an issue. I'm happy to add this upon request.* 118 | 119 | ### Operator-only features 120 | 121 | #### Autoscaling 122 | 123 | The CockroachDB Operator is able to automatically scale the CockroachDB clusters by supporting decommissioning and recommissioning of nodes. 124 | 125 | The easiest way to try this, is by running a cluster with the CockroachDB Operator and then running the *kubectl scale* command: 126 | 127 | ```bash 128 | kubectl scale cockroachdbs basic --replicas 5 # scaling up from 3 129 | kubectl scale cockroachdbs basic --replicas 3 # scaling down from 5 (decommission of pod 3 and 4) 130 | kubectl scale cockroachdbs basic --replicas 5 # scaling up from 3 (recommission of pod 3 and 4, due earlier decommission) 131 | ``` 132 | 133 | It means, that nodes that will be removed from the cluster due to scaling down, are going to "transfer all range replicas on the node to other nodes". Please read the official [Decommission Nodes](https://www.cockroachlabs.com/docs/stable/remove-nodes.html) documentation to learn more. The Operator is implemented to run `cockroach node decommission 3 4` against `pod-0` in the cluster: [operator/actions/decommission.go](https://github.com/metalmatze/kube-cockroachdb/blob/main/operator/actions/decommission.go). Only if the decommission command succeeds the StatefulSet is actually scaled down removing the Pods going forward. 134 | 135 | Similarly, once nodes have been decommissioned they need to be recommissioned to be used again, essentially telling the CockroachDB cluster to send range replicas to those nodes again. The Operator is implemented to run `cockroach node recommission 3 4` after scaling up a StatefulSet. 136 | 137 | #### Autoscaling using the Horizontal Pod Autoscaler 138 | 139 | It's possible and I've used the `cockroachdb_sql_selects_total` query to scale up and down based on the number of incoming select requests. Let me know if you're interested, then I'll write about it in depth. *Need to write the rest of the README first :slightly_smiling_face:* 140 | -------------------------------------------------------------------------------- /examples/basic/basic.jsonnet: -------------------------------------------------------------------------------- 1 | local kubernetes = import '../../kubernetes.libsonnet'; 2 | 3 | local objects = kubernetes({ 4 | name: 'example', 5 | replicas: 3, 6 | }); 7 | 8 | // Let's generate a List containing all Kubernetes objects 9 | { 10 | apiVersion: 'v1', 11 | kind: 'List', 12 | items: 13 | [objects[name] for name in std.objectFields(objects)], 14 | } 15 | -------------------------------------------------------------------------------- /examples/basic/basic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: batch/v1 4 | kind: Job 5 | metadata: 6 | labels: 7 | app.kubernetes.io/component: database 8 | app.kubernetes.io/instance: example 9 | app.kubernetes.io/name: cockroachdb-example 10 | name: cockroachdb-example 11 | namespace: default 12 | spec: 13 | template: 14 | metadata: 15 | labels: 16 | app.kubernetes.io/component: database 17 | app.kubernetes.io/instance: example 18 | app.kubernetes.io/name: cockroachdb-example 19 | spec: 20 | containers: 21 | - command: 22 | - /cockroach/cockroach 23 | - init 24 | - --insecure 25 | - --host=cockroachdb-example-0.cockroachdb-example.default 26 | image: cockroachdb/cockroach:v20.1.5 27 | name: cluster-init 28 | restartPolicy: OnFailure 29 | - apiVersion: policy/v1 30 | kind: PodDisruptionBudget 31 | metadata: 32 | labels: 33 | app.kubernetes.io/component: database 34 | app.kubernetes.io/instance: example 35 | app.kubernetes.io/name: cockroachdb-example 36 | name: cockroachdb-example 37 | namespace: default 38 | spec: 39 | maxUnavailable: 1 40 | selector: 41 | matchLabels: 42 | app.kubernetes.io/component: database 43 | app.kubernetes.io/instance: example 44 | app.kubernetes.io/name: cockroachdb-example 45 | - apiVersion: v1 46 | kind: Service 47 | metadata: 48 | annotations: 49 | service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" 50 | labels: 51 | app.kubernetes.io/component: database 52 | app.kubernetes.io/instance: example 53 | app.kubernetes.io/name: cockroachdb-example 54 | name: cockroachdb-example 55 | namespace: default 56 | spec: 57 | clusterIP: None 58 | ports: 59 | - name: grpc 60 | port: 26257 61 | targetPort: 26257 62 | - name: http 63 | port: 8080 64 | targetPort: 8080 65 | publishNotReadyAddresses: true 66 | selector: 67 | app.kubernetes.io/component: database 68 | app.kubernetes.io/instance: example 69 | app.kubernetes.io/name: cockroachdb-example 70 | - apiVersion: monitoring.coreos.com/v1 71 | kind: ServiceMonitor 72 | metadata: 73 | labels: 74 | app.kubernetes.io/component: database 75 | app.kubernetes.io/instance: example 76 | app.kubernetes.io/name: cockroachdb-example 77 | prometheus: k8s 78 | name: cockroachdb-example 79 | namespace: default 80 | spec: 81 | endpoints: 82 | - metricRelabelings: 83 | - replacement: cockroachdb_${1} 84 | sourceLabels: 85 | - __name__ 86 | targetLabel: __name__ 87 | path: /_status/vars 88 | port: http 89 | namespaceSelector: 90 | matchNames: 91 | - default 92 | selector: 93 | matchLabels: 94 | app.kubernetes.io/component: database 95 | app.kubernetes.io/instance: example 96 | app.kubernetes.io/name: cockroachdb-example 97 | - apiVersion: v1 98 | kind: Service 99 | metadata: 100 | labels: 101 | app.kubernetes.io/component: database 102 | app.kubernetes.io/instance: example 103 | app.kubernetes.io/name: cockroachdb-example-public 104 | name: cockroachdb-example-public 105 | namespace: default 106 | spec: 107 | ports: 108 | - name: grpc 109 | port: 26257 110 | targetPort: 26257 111 | - name: http 112 | port: 8080 113 | targetPort: 8080 114 | selector: 115 | app.kubernetes.io/component: database 116 | app.kubernetes.io/instance: example 117 | app.kubernetes.io/name: cockroachdb-example 118 | - apiVersion: apps/v1 119 | kind: StatefulSet 120 | metadata: 121 | labels: 122 | app.kubernetes.io/component: database 123 | app.kubernetes.io/instance: example 124 | app.kubernetes.io/name: cockroachdb-example 125 | name: cockroachdb-example 126 | namespace: default 127 | spec: 128 | podManagementPolicy: Parallel 129 | replicas: 3 130 | selector: 131 | matchLabels: 132 | app.kubernetes.io/component: database 133 | app.kubernetes.io/instance: example 134 | app.kubernetes.io/name: cockroachdb-example 135 | serviceName: cockroachdb-example 136 | template: 137 | metadata: 138 | labels: 139 | app.kubernetes.io/component: database 140 | app.kubernetes.io/instance: example 141 | app.kubernetes.io/name: cockroachdb-example 142 | name: cockroachdb-example 143 | namespace: default 144 | spec: 145 | affinity: 146 | podAntiAffinity: 147 | preferredDuringSchedulingIgnoredDuringExecution: 148 | - podAffinityTerm: 149 | labelSelector: 150 | matchExpressions: 151 | - key: app.kubernetes.io/name 152 | operator: In 153 | values: 154 | - cockroachdb-example 155 | namespaces: 156 | - default 157 | topologyKey: kubernetes.io/hostname 158 | weight: 100 159 | containers: 160 | - command: 161 | - /bin/bash 162 | - -ecx 163 | - exec /cockroach/cockroach start --logtostderr=WARNING --insecure --advertise-host=$(hostname -f) --http-host=0.0.0.0 --join=cockroachdb-example-0.cockroachdb-example.default.svc --cache=25% --max-sql-memory=25% 164 | env: 165 | - name: COCKROACH_CHANNEL 166 | value: kubernetes-insecure 167 | image: cockroachdb/cockroach:v20.1.5 168 | imagePullPolicy: IfNotPresent 169 | livenessProbe: 170 | httpGet: 171 | path: /health 172 | port: http 173 | scheme: HTTP 174 | initialDelaySeconds: 30 175 | periodSeconds: 5 176 | name: cockroachdb 177 | ports: 178 | - containerPort: 26257 179 | name: grpc 180 | - containerPort: 8080 181 | name: http 182 | readinessProbe: 183 | failureThreshold: 2 184 | httpGet: 185 | path: /health?ready=1 186 | port: http 187 | scheme: HTTP 188 | initialDelaySeconds: 10 189 | periodSeconds: 5 190 | resources: {} 191 | securityContext: 192 | runAsUser: 65534 193 | volumeMounts: 194 | - mountPath: /cockroach/cockroach-data 195 | name: datadir 196 | terminationGracePeriodSeconds: 60 197 | volumes: 198 | - emptyDir: {} 199 | name: datadir 200 | volumeClaimTemplates: [] 201 | kind: List 202 | -------------------------------------------------------------------------------- /examples/storage/storage.jsonnet: -------------------------------------------------------------------------------- 1 | local kubernetes = import '../../kubernetes.libsonnet'; 2 | 3 | local objects = kubernetes({ 4 | name: 'example', 5 | replicas: 3, 6 | 7 | storage: { 8 | // emptyDir: {}, 9 | volumeClaimTemplate: { 10 | apiVersion: 'v1', 11 | kind: 'PersistentVolumeClaim', 12 | spec: { 13 | accessModes: [ 14 | 'ReadWriteOnce', 15 | ], 16 | resources: { 17 | requests: { 18 | storage: '25Gi', 19 | }, 20 | }, 21 | storageClassName: 'standard', 22 | }, 23 | }, 24 | }, 25 | }); 26 | 27 | // Let's generate a List containing all Kubernetes objects 28 | { 29 | apiVersion: 'v1', 30 | kind: 'List', 31 | items: 32 | [objects[name] for name in std.objectFields(objects)], 33 | } 34 | -------------------------------------------------------------------------------- /examples/storage/storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: batch/v1 4 | kind: Job 5 | metadata: 6 | labels: 7 | app.kubernetes.io/component: database 8 | app.kubernetes.io/instance: example 9 | app.kubernetes.io/name: cockroachdb-example 10 | name: cockroachdb-example 11 | namespace: default 12 | spec: 13 | template: 14 | metadata: 15 | labels: 16 | app.kubernetes.io/component: database 17 | app.kubernetes.io/instance: example 18 | app.kubernetes.io/name: cockroachdb-example 19 | spec: 20 | containers: 21 | - command: 22 | - /cockroach/cockroach 23 | - init 24 | - --insecure 25 | - --host=cockroachdb-example-0.cockroachdb-example.default 26 | image: cockroachdb/cockroach:v20.1.5 27 | name: cluster-init 28 | restartPolicy: OnFailure 29 | - apiVersion: policy/v1 30 | kind: PodDisruptionBudget 31 | metadata: 32 | labels: 33 | app.kubernetes.io/component: database 34 | app.kubernetes.io/instance: example 35 | app.kubernetes.io/name: cockroachdb-example 36 | name: cockroachdb-example 37 | namespace: default 38 | spec: 39 | maxUnavailable: 1 40 | selector: 41 | matchLabels: 42 | app.kubernetes.io/component: database 43 | app.kubernetes.io/instance: example 44 | app.kubernetes.io/name: cockroachdb-example 45 | - apiVersion: v1 46 | kind: Service 47 | metadata: 48 | annotations: 49 | service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" 50 | labels: 51 | app.kubernetes.io/component: database 52 | app.kubernetes.io/instance: example 53 | app.kubernetes.io/name: cockroachdb-example 54 | name: cockroachdb-example 55 | namespace: default 56 | spec: 57 | clusterIP: None 58 | ports: 59 | - name: grpc 60 | port: 26257 61 | targetPort: 26257 62 | - name: http 63 | port: 8080 64 | targetPort: 8080 65 | publishNotReadyAddresses: true 66 | selector: 67 | app.kubernetes.io/component: database 68 | app.kubernetes.io/instance: example 69 | app.kubernetes.io/name: cockroachdb-example 70 | - apiVersion: monitoring.coreos.com/v1 71 | kind: ServiceMonitor 72 | metadata: 73 | labels: 74 | app.kubernetes.io/component: database 75 | app.kubernetes.io/instance: example 76 | app.kubernetes.io/name: cockroachdb-example 77 | prometheus: k8s 78 | name: cockroachdb-example 79 | namespace: default 80 | spec: 81 | endpoints: 82 | - metricRelabelings: 83 | - replacement: cockroachdb_${1} 84 | sourceLabels: 85 | - __name__ 86 | targetLabel: __name__ 87 | path: /_status/vars 88 | port: http 89 | namespaceSelector: 90 | matchNames: 91 | - default 92 | selector: 93 | matchLabels: 94 | app.kubernetes.io/component: database 95 | app.kubernetes.io/instance: example 96 | app.kubernetes.io/name: cockroachdb-example 97 | - apiVersion: v1 98 | kind: Service 99 | metadata: 100 | labels: 101 | app.kubernetes.io/component: database 102 | app.kubernetes.io/instance: example 103 | app.kubernetes.io/name: cockroachdb-example-public 104 | name: cockroachdb-example-public 105 | namespace: default 106 | spec: 107 | ports: 108 | - name: grpc 109 | port: 26257 110 | targetPort: 26257 111 | - name: http 112 | port: 8080 113 | targetPort: 8080 114 | selector: 115 | app.kubernetes.io/component: database 116 | app.kubernetes.io/instance: example 117 | app.kubernetes.io/name: cockroachdb-example 118 | - apiVersion: apps/v1 119 | kind: StatefulSet 120 | metadata: 121 | labels: 122 | app.kubernetes.io/component: database 123 | app.kubernetes.io/instance: example 124 | app.kubernetes.io/name: cockroachdb-example 125 | name: cockroachdb-example 126 | namespace: default 127 | spec: 128 | podManagementPolicy: Parallel 129 | replicas: 3 130 | selector: 131 | matchLabels: 132 | app.kubernetes.io/component: database 133 | app.kubernetes.io/instance: example 134 | app.kubernetes.io/name: cockroachdb-example 135 | serviceName: cockroachdb-example 136 | template: 137 | metadata: 138 | labels: 139 | app.kubernetes.io/component: database 140 | app.kubernetes.io/instance: example 141 | app.kubernetes.io/name: cockroachdb-example 142 | name: cockroachdb-example 143 | namespace: default 144 | spec: 145 | affinity: 146 | podAntiAffinity: 147 | preferredDuringSchedulingIgnoredDuringExecution: 148 | - podAffinityTerm: 149 | labelSelector: 150 | matchExpressions: 151 | - key: app.kubernetes.io/name 152 | operator: In 153 | values: 154 | - cockroachdb-example 155 | namespaces: 156 | - default 157 | topologyKey: kubernetes.io/hostname 158 | weight: 100 159 | containers: 160 | - command: 161 | - /bin/bash 162 | - -ecx 163 | - exec /cockroach/cockroach start --logtostderr=WARNING --insecure --advertise-host=$(hostname -f) --http-host=0.0.0.0 --join=cockroachdb-example-0.cockroachdb-example.default.svc --cache=25% --max-sql-memory=25% 164 | env: 165 | - name: COCKROACH_CHANNEL 166 | value: kubernetes-insecure 167 | image: cockroachdb/cockroach:v20.1.5 168 | imagePullPolicy: IfNotPresent 169 | livenessProbe: 170 | httpGet: 171 | path: /health 172 | port: http 173 | scheme: HTTP 174 | initialDelaySeconds: 30 175 | periodSeconds: 5 176 | name: cockroachdb 177 | ports: 178 | - containerPort: 26257 179 | name: grpc 180 | - containerPort: 8080 181 | name: http 182 | readinessProbe: 183 | failureThreshold: 2 184 | httpGet: 185 | path: /health?ready=1 186 | port: http 187 | scheme: HTTP 188 | initialDelaySeconds: 10 189 | periodSeconds: 5 190 | resources: {} 191 | securityContext: 192 | runAsUser: 65534 193 | volumeMounts: 194 | - mountPath: /cockroach/cockroach-data 195 | name: datadir 196 | terminationGracePeriodSeconds: 60 197 | volumes: 198 | - name: datadir 199 | persistentVolumeClaim: 200 | claimName: datadir 201 | volumeClaimTemplates: 202 | - apiVersion: v1 203 | kind: PersistentVolumeClaim 204 | metadata: 205 | name: datadir 206 | namespace: default 207 | spec: 208 | accessModes: 209 | - ReadWriteOnce 210 | resources: 211 | requests: 212 | storage: 25Gi 213 | storageClassName: standard 214 | kind: List 215 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/metalmatze/kube-cockroachdb 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/brancz/locutus v0.2.0-rc.4.0.20230627132800-aa1e6baa6119 7 | github.com/go-kit/kit v0.12.0 8 | github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a 9 | github.com/oklog/run v1.1.0 10 | github.com/prometheus/client_golang v1.16.0 11 | k8s.io/api v0.27.3 12 | k8s.io/apimachinery v0.27.3 13 | k8s.io/client-go v0.27.3 14 | sigs.k8s.io/controller-runtime v0.15.0 15 | ) 16 | 17 | require ( 18 | github.com/beorn7/perks v1.0.1 // indirect 19 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 20 | github.com/cockroachdb/cockroach-go/v2 v2.3.5 // indirect 21 | github.com/davecgh/go-spew v1.1.1 // indirect 22 | github.com/emicklei/go-restful/v3 v3.10.2 // indirect 23 | github.com/go-kit/log v0.2.1 // indirect 24 | github.com/go-logfmt/logfmt v0.6.0 // indirect 25 | github.com/go-logr/logr v1.2.4 // indirect 26 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 27 | github.com/go-openapi/jsonreference v0.20.2 // indirect 28 | github.com/go-openapi/swag v0.22.4 // indirect 29 | github.com/gogo/protobuf v1.3.2 // indirect 30 | github.com/golang/protobuf v1.5.3 // indirect 31 | github.com/google/gnostic v0.6.9 // indirect 32 | github.com/google/go-cmp v0.5.9 // indirect 33 | github.com/google/go-jsonnet v0.20.0 // indirect 34 | github.com/google/gofuzz v1.2.0 // indirect 35 | github.com/google/uuid v1.3.0 // indirect 36 | github.com/hashicorp/errwrap v1.1.0 // indirect 37 | github.com/hashicorp/go-multierror v1.1.1 // indirect 38 | github.com/imdario/mergo v0.3.16 // indirect 39 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 40 | github.com/jackc/pgconn v1.14.0 // indirect 41 | github.com/jackc/pgio v1.0.0 // indirect 42 | github.com/jackc/pgpassfile v1.0.0 // indirect 43 | github.com/jackc/pgproto3/v2 v2.3.2 // indirect 44 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 45 | github.com/jackc/pgtype v1.14.0 // indirect 46 | github.com/jackc/pgx/v4 v4.18.1 // indirect 47 | github.com/jackc/puddle v1.3.0 // indirect 48 | github.com/josharian/intern v1.0.0 // indirect 49 | github.com/json-iterator/go v1.1.12 // indirect 50 | github.com/mailru/easyjson v0.7.7 // indirect 51 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 52 | github.com/moby/spdystream v0.2.0 // indirect 53 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 54 | github.com/modern-go/reflect2 v1.0.2 // indirect 55 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 56 | github.com/pkg/errors v0.9.1 // indirect 57 | github.com/prometheus/client_model v0.4.0 // indirect 58 | github.com/prometheus/common v0.44.0 // indirect 59 | github.com/prometheus/procfs v0.11.0 // indirect 60 | github.com/spf13/pflag v1.0.5 // indirect 61 | golang.org/x/crypto v0.10.0 // indirect 62 | golang.org/x/net v0.11.0 // indirect 63 | golang.org/x/oauth2 v0.9.0 // indirect 64 | golang.org/x/sys v0.9.0 // indirect 65 | golang.org/x/term v0.9.0 // indirect 66 | golang.org/x/text v0.10.0 // indirect 67 | golang.org/x/time v0.3.0 // indirect 68 | google.golang.org/appengine v1.6.7 // indirect 69 | google.golang.org/protobuf v1.31.0 // indirect 70 | gopkg.in/inf.v0 v0.9.1 // indirect 71 | gopkg.in/yaml.v2 v2.4.0 // indirect 72 | gopkg.in/yaml.v3 v3.0.1 // indirect 73 | k8s.io/klog/v2 v2.100.1 // indirect 74 | k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect 75 | k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect 76 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 77 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect 78 | sigs.k8s.io/yaml v1.3.0 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= 5 | github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 6 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 7 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 8 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 9 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 10 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 11 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 12 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 13 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 14 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 15 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 16 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 17 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 18 | github.com/brancz/locutus v0.2.0-rc.4.0.20230627132800-aa1e6baa6119 h1:8LL21VXQQ+cTOre+2QQl2qYUoGsgFQPxl2qgZDum4n8= 19 | github.com/brancz/locutus v0.2.0-rc.4.0.20230627132800-aa1e6baa6119/go.mod h1:9bMdzYn8u2wbGcF3HHTeiVLtfJj73qUGmgetd7a/ILA= 20 | github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= 21 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 22 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 23 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 24 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 25 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 26 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 27 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 28 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 29 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 30 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 31 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 32 | github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= 33 | github.com/cockroachdb/cockroach-go/v2 v2.3.5/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= 34 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 35 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 36 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 37 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 38 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 39 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 40 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 41 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 42 | github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= 43 | github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 44 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 45 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 46 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 47 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 48 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 49 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 50 | github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= 51 | github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= 52 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 53 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 54 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 55 | github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= 56 | github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= 57 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 58 | github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= 59 | github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 60 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 61 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 62 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 63 | github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= 64 | github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 65 | github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 66 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 67 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 68 | github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= 69 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 70 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 71 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 72 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 73 | github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= 74 | github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 75 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 76 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 77 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 78 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 79 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 80 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 81 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 82 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 83 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 84 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 85 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 86 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 88 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 89 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 90 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 91 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 92 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 93 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 94 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 95 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 96 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 97 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 98 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 99 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 100 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 101 | github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= 102 | github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= 103 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 104 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 105 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 106 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 107 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 108 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 110 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 111 | github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g= 112 | github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA= 113 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 114 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 115 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 116 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= 117 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 118 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 119 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 120 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 121 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 122 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 123 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 124 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 125 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 126 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 127 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 128 | github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= 129 | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 130 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 131 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 132 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 133 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 134 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 135 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 136 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 137 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 138 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 139 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 140 | github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= 141 | github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 142 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 143 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 144 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 145 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 146 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 147 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 148 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 149 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 150 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 151 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 152 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 153 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 154 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 155 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 156 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 157 | github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= 158 | github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 159 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 160 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 161 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 162 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 163 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 164 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 165 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 166 | github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= 167 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 168 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 169 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 170 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 171 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 172 | github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= 173 | github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= 174 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 175 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 176 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 177 | github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= 178 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 179 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 180 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 181 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 182 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 183 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 184 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 185 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 186 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 187 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 188 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 189 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 190 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 191 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 192 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 193 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 194 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 195 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 196 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 197 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 198 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 199 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 200 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 201 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 202 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 203 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 204 | github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= 205 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 206 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 207 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 208 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 209 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 210 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 211 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 212 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 213 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 214 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 215 | github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a h1:0usWxe5SGXKQovz3p+BiQ81Jy845xSMu2CWKuXsXuUM= 216 | github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a/go.mod h1:3OETvrxfELvGsU2RoGGWercfeZ4bCL3+SOwzIWtJH/Q= 217 | github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= 218 | github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 219 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 220 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 221 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 222 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 223 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 224 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 225 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 226 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 227 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 228 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 229 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 230 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 231 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= 232 | github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= 233 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 234 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 235 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 236 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 237 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 238 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 239 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 240 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 241 | github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 242 | github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= 243 | github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= 244 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 245 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 246 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 247 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 248 | github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= 249 | github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= 250 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 251 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 252 | github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= 253 | github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= 254 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 255 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 256 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 257 | github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= 258 | github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 259 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 260 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 261 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 262 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 263 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 264 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 265 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 266 | github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= 267 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 268 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 269 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 270 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 271 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 272 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 273 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 274 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 275 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 276 | github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= 277 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 278 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 279 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 280 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 281 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 282 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 283 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 284 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 285 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 286 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 287 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 288 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 289 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 290 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 291 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 292 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 293 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 294 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 295 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 296 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 297 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 298 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 299 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 300 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 301 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 302 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 303 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 304 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 305 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 306 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 307 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 308 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 309 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 310 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 311 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 312 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 313 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 314 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 315 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 316 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 317 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 318 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 319 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 320 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 321 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 322 | golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= 323 | golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= 324 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 325 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 326 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 327 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 328 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 329 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 330 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 331 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 332 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 333 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 334 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 335 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 336 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 337 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 338 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 339 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 340 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 341 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 342 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 343 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 344 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 345 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 346 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 347 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 348 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 349 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 350 | golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 351 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 352 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 353 | golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= 354 | golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= 355 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 356 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 357 | golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= 358 | golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= 359 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 364 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 365 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 366 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 367 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 368 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 369 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 370 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 371 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 382 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 383 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 384 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 385 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 386 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 387 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 388 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 389 | golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= 390 | golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 391 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 392 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 393 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 394 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 395 | golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= 396 | golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 397 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 398 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 399 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 400 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 401 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 402 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 403 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 404 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 405 | golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= 406 | golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 407 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 408 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 409 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 410 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 411 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 412 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 413 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 414 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 415 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 416 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 417 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 418 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 419 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 420 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 421 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 422 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 423 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 424 | golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 425 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 426 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 427 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 428 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 429 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 430 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 431 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 432 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 433 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 434 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 435 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 436 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 437 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 438 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 439 | google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 440 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 441 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 442 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 443 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 444 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 445 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 446 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= 447 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 448 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 449 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 450 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 451 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 452 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 453 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 454 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 455 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 456 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 457 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 458 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 459 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 460 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 461 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 462 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 463 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 464 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 465 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 466 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 467 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 468 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 469 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 470 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 471 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 472 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 473 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 474 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 475 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 476 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 477 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 478 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 479 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 480 | gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 481 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 482 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 483 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 484 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 485 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 486 | k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= 487 | k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= 488 | k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= 489 | k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= 490 | k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= 491 | k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= 492 | k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= 493 | k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= 494 | k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= 495 | k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= 496 | k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= 497 | k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 498 | sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= 499 | sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= 500 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= 501 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 502 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= 503 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= 504 | sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= 505 | sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= 506 | -------------------------------------------------------------------------------- /kubernetes.libsonnet: -------------------------------------------------------------------------------- 1 | function(params) { 2 | local cockroachdb = 3 | { 4 | local defaults = self, 5 | name: 'cockroachdb', 6 | 7 | metadata: { 8 | name: 'cockroachdb-' + defaults.name, 9 | namespace: 'default', 10 | labels: { 11 | 'app.kubernetes.io/name': 'cockroachdb-' + defaults.name, 12 | 'app.kubernetes.io/instance': defaults.name, 13 | 'app.kubernetes.io/component': 'database', 14 | }, 15 | }, 16 | image: 'cockroachdb/cockroach:v20.1.5', 17 | replicas: 1, 18 | expose: { 19 | http: 8080, 20 | grpc: 26257, 21 | }, 22 | resources: {}, 23 | storage: {}, 24 | } 25 | + params, // this merges your parameters with default ones 26 | 27 | // We now generate all Kubernetes objects based on the cockroach configuration created above 28 | 29 | servicePublic: { 30 | apiVersion: 'v1', 31 | kind: 'Service', 32 | metadata: cockroachdb.metadata { 33 | name: cockroachdb.metadata.name + '-public', 34 | labels+: { 35 | 'app.kubernetes.io/name': cockroachdb.metadata.name + '-public', 36 | }, 37 | }, 38 | spec: { 39 | ports: [ 40 | { name: name, port: cockroachdb.expose[name], targetPort: cockroachdb.expose[name] } 41 | for name in std.objectFields(cockroachdb.expose) 42 | ], 43 | selector: cockroachdb.metadata.labels, 44 | }, 45 | }, 46 | service: { 47 | apiVersion: 'v1', 48 | kind: 'Service', 49 | metadata: cockroachdb.metadata { 50 | annotations: { 51 | 'service.alpha.kubernetes.io/tolerate-unready-endpoints': 'true', 52 | }, 53 | }, 54 | spec: { 55 | clusterIP: 'None', 56 | publishNotReadyAddresses: true, 57 | ports: [ 58 | { name: name, port: cockroachdb.expose[name], targetPort: cockroachdb.expose[name] } 59 | for name in std.objectFields(cockroachdb.expose) 60 | ], 61 | selector: cockroachdb.metadata.labels, 62 | }, 63 | }, 64 | statefulSet: { 65 | apiVersion: 'apps/v1', 66 | kind: 'StatefulSet', 67 | metadata: cockroachdb.metadata, 68 | spec: { 69 | podManagementPolicy: 'Parallel', 70 | replicas: cockroachdb.replicas, 71 | selector: { 72 | matchLabels: cockroachdb.metadata.labels, 73 | }, 74 | serviceName: cockroachdb.metadata.name, 75 | template: { 76 | metadata: cockroachdb.metadata, 77 | spec: { 78 | containers: [ 79 | { 80 | name: 'cockroachdb', 81 | image: cockroachdb.image, 82 | imagePullPolicy: 'IfNotPresent', 83 | podManagementPolicy: 'Parallel', 84 | args:: [ 85 | 'start', 86 | '--logtostderr=WARNING', 87 | '--insecure', 88 | '--advertise-host=$(hostname -f)', 89 | '--http-host=0.0.0.0', 90 | '--join=%s-0.%s.%s.svc' % [ 91 | cockroachdb.metadata.name, 92 | cockroachdb.metadata.name, 93 | cockroachdb.metadata.namespace, 94 | ], 95 | '--cache=25%', 96 | '--max-sql-memory=25%', 97 | ], 98 | command: [ 99 | '/bin/bash', 100 | '-ecx', 101 | 'exec /cockroach/cockroach %s' % std.join(' ', self.args), 102 | ], 103 | securityContext: { 104 | runAsUser: 65534, 105 | runAsGroup: 65534, 106 | runAsNonRoot: true, 107 | allowPrivilegeEscalation: false, 108 | seccompProfile: { 109 | type: 'RuntimeDefault', 110 | }, 111 | capabilities: { 112 | drop: ['ALL'], 113 | }, 114 | }, 115 | env: [ 116 | { 117 | name: 'COCKROACH_CHANNEL', 118 | value: 'kubernetes-insecure', 119 | }, 120 | ], 121 | ports: [ 122 | { name: name, containerPort: cockroachdb.expose[name] } 123 | for name in std.objectFields(cockroachdb.expose) 124 | ], 125 | resources: cockroachdb.resources, 126 | livenessProbe: { 127 | httpGet: { 128 | path: '/health', 129 | port: 'http', 130 | scheme: 'HTTP', 131 | }, 132 | initialDelaySeconds: 30, 133 | periodSeconds: 5, 134 | }, 135 | readinessProbe: { 136 | failureThreshold: 2, 137 | httpGet: { 138 | path: '/health?ready=1', 139 | port: 'http', 140 | scheme: 'HTTP', 141 | }, 142 | initialDelaySeconds: 10, 143 | periodSeconds: 5, 144 | }, 145 | volumeMounts: [ 146 | { mountPath: '/cockroach/cockroach-data', name: 'datadir' }, 147 | ], 148 | }, 149 | ], 150 | terminationGracePeriodSeconds: 60, 151 | affinity: { 152 | podAntiAffinity: { 153 | preferredDuringSchedulingIgnoredDuringExecution: [ 154 | { 155 | podAffinityTerm: { 156 | labelSelector: { 157 | matchExpressions: [ 158 | { key: 'app.kubernetes.io/name', operator: 'In', values: [cockroachdb.metadata.name] }, 159 | ], 160 | }, 161 | namespaces: [cockroachdb.metadata.namespace], 162 | topologyKey: 'kubernetes.io/hostname', 163 | }, 164 | weight: 100, 165 | }, 166 | ], 167 | }, 168 | }, 169 | volumes: if std.objectHas(cockroachdb.storage, 'volumeClaimTemplate') then [ 170 | { name: 'datadir', persistentVolumeClaim: { claimName: 'datadir' } }, 171 | ] else [ 172 | { name: 'datadir', emptyDir: {} }, 173 | ], 174 | }, 175 | }, 176 | volumeClaimTemplates: if std.objectHas(cockroachdb.storage, 'volumeClaimTemplate') then [ 177 | cockroachdb.storage.volumeClaimTemplate { 178 | metadata+: { 179 | name: 'datadir', 180 | namespace: cockroachdb.metadata.namespace, 181 | }, 182 | spec+:{ 183 | accessModes: ['ReadWriteOnce'], 184 | }, 185 | }, 186 | ] else [], 187 | }, 188 | }, 189 | podDisruptionBudget: { 190 | apiVersion: 'policy/v1', 191 | kind: 'PodDisruptionBudget', 192 | metadata: cockroachdb.metadata, 193 | spec: { 194 | maxUnavailable: // (n-1)/2 if n>1 195 | if cockroachdb.replicas > 1 then 196 | std.floor((cockroachdb.replicas - 1) / 2) 197 | else 1, 198 | selector: { 199 | matchLabels: cockroachdb.metadata.labels, 200 | }, 201 | }, 202 | }, 203 | 204 | jobInitialize: { 205 | apiVersion: 'batch/v1', 206 | kind: 'Job', 207 | metadata: cockroachdb.metadata, 208 | spec: { 209 | template: { 210 | metadata: { 211 | labels: cockroachdb.metadata.labels, 212 | }, 213 | spec: { 214 | containers: [ 215 | { 216 | name: 'cluster-init', 217 | image: cockroachdb.image, 218 | command: [ 219 | '/cockroach/cockroach', 220 | 'init', 221 | '--insecure', 222 | '--host=%s-0.%s.%s' % [ 223 | cockroachdb.metadata.name, 224 | cockroachdb.metadata.name, 225 | cockroachdb.metadata.namespace, 226 | ], 227 | ], 228 | }, 229 | ], 230 | restartPolicy: 'OnFailure', 231 | }, 232 | }, 233 | }, 234 | }, 235 | 236 | serviceMonitor: { 237 | apiVersion: 'monitoring.coreos.com/v1', 238 | kind: 'ServiceMonitor', 239 | metadata: cockroachdb.metadata { 240 | labels+: { 241 | prometheus: 'k8s', 242 | }, 243 | }, 244 | spec: { 245 | endpoints: [ 246 | { 247 | port: 'http', 248 | path: '/_status/vars', 249 | metricRelabelings: [ 250 | { 251 | // prefix all metric names with cockroachdb_ 252 | sourceLabels: ['__name__'], 253 | targetLabel: '__name__', 254 | replacement: 'cockroachdb_${1}', 255 | }, 256 | ], 257 | }, 258 | ], 259 | namespaceSelector: { 260 | matchNames: [cockroachdb.metadata.namespace], 261 | }, 262 | selector: { 263 | matchLabels: cockroachdb.metadata.labels, 264 | }, 265 | }, 266 | }, 267 | 268 | // TODO: Add backups to object storage via CronJob and Minio 269 | } 270 | -------------------------------------------------------------------------------- /monitoring/alerts/cockroachdb.libsonnet: -------------------------------------------------------------------------------- 1 | { 2 | prometheusAlerts+:: { 3 | groups+: [ 4 | { 5 | name: 'cockroachdb', 6 | rules: [ 7 | { 8 | alert: 'CockroachInstanceFlapping', 9 | expr: ||| 10 | resets(cockroachdb_sys_uptime{%(cockroachdbSelector)s}[10m]) > 5 11 | ||| % $._config, 12 | 'for': '1m', 13 | labels: { 14 | severity: 'warning', 15 | }, 16 | annotations: { 17 | summary: 'CockroachDB instances have restarted in the last 10 minutes.', 18 | description: '{{ $labels.instance }} for cluster {{ $labels.cluster }} restarted {{ $value }} time(s) in 10m.', 19 | }, 20 | }, 21 | { 22 | alert: 'CockroachLivenessMismatch', 23 | expr: ||| 24 | (cockroachdb_liveness_livenodes{%(cockroachdbSelector)s}) 25 | != 26 | ignoring(instance) group_left() (count by(cluster, job) (up{%(cockroachdbSelector)s} == 1)) 27 | ||| % $._config, 28 | 'for': '5m', 29 | labels: { 30 | severity: 'warning', 31 | }, 32 | annotations: { 33 | summary: 'CockroachDB has liveness mismatches.', 34 | description: 'Liveness mismatch for {{ $labels.instance }}', 35 | }, 36 | }, 37 | { 38 | alert: 'CockroachVersionMismatch', 39 | expr: ||| 40 | count by(cluster) (count_values by(tag, cluster) ("version", cockroachdb_build_timestamp{%(cockroachdbSelector)s})) > 1 41 | ||| % $._config, 42 | 'for': '1h', 43 | labels: { 44 | severity: 'warning', 45 | }, 46 | annotations: { 47 | summary: 'CockroachDB cluster is running different versions.', 48 | description: 'Cluster {{ $labels.cluster }} running {{ $value }} different versions', 49 | }, 50 | }, 51 | { 52 | alert: 'CockroachStoreDiskLow', 53 | // TODO: use predict_linear 54 | expr: ||| 55 | :cockroachdb_capacity_available:ratio{%(cockroachdbSelector)s} < 0.15 56 | ||| % $._config, 57 | 'for': '30m', 58 | labels: { 59 | severity: 'critical', 60 | }, 61 | annotations: { 62 | summary: 'CockroachDB is at low disk capacity.', 63 | description: 'Store {{ $labels.store }} on node {{ $labels.instance }} at {{ $value }} available disk fraction', 64 | }, 65 | }, 66 | { 67 | alert: 'CockroachClusterDiskLow', 68 | // TODO: use predict_linear 69 | expr: ||| 70 | cluster:cockroachdb_capacity_available:ratio{%(cockroachdbSelector)s} < 0.2 71 | ||| % $._config, 72 | 'for': '30m', 73 | labels: { 74 | severity: 'critical', 75 | }, 76 | annotations: { 77 | summary: 'CockroachDB cluster is at critically low disk capacity.', 78 | description: 'Cluster {{ $labels.cluster }} at {{ $value }} available disk fraction', 79 | }, 80 | }, 81 | // { 82 | // alert: 'CockroachZeroSQLQps', 83 | // expr: ||| 84 | // cockroachdb_sql_conns{%(cockroachdbSelector)s} > 0 and rate(cockroachdb_sql_query_count{%(cockroachdbSelector)s}[5m]) == 0 85 | // ||| % $._config, 86 | // 'for': '10m', 87 | // labels: { 88 | // severity: 'critical', 89 | // }, 90 | // annotations: { 91 | // message: 'Instance {{ $labels.instance }} has SQL connections but no queries', 92 | // }, 93 | // }, 94 | { 95 | alert: 'CockroachUnavailableRanges', 96 | expr: ||| 97 | (sum by(instance, cluster) (cockroachdb_ranges_unavailable{%(cockroachdbSelector)s})) > 0 98 | ||| % $._config, 99 | 'for': '10m', 100 | labels: { 101 | severity: 'critical', 102 | }, 103 | annotations: { 104 | summary: 'CockroachDB has unavailable ranges.', 105 | description: 'Instance {{ $labels.instance }} has {{ $value }} unavailable ranges', 106 | }, 107 | }, 108 | { 109 | alert: 'CockroachNoLeaseRanges', 110 | expr: ||| 111 | (sum by(instance, cluster) (cockroachdb_replicas_leaders_not_leaseholders{%(cockroachdbSelector)s})) > 0 112 | ||| % $._config, 113 | 'for': '10m', 114 | labels: { 115 | severity: 'warning', 116 | }, 117 | annotations: { 118 | summary: 'CockroachDB has ranges without leases.', 119 | description: 'Instance {{ $labels.instance }} has {{ $value }} ranges without leases', 120 | }, 121 | }, 122 | { 123 | alert: 'CockroachHighOpenFDCount', 124 | expr: ||| 125 | cockroachdb_sys_fd_open{%(cockroachdbSelector)s} / cockroachdb_sys_fd_softlimit{%(cockroachdbSelector)s} > 0.8 126 | ||| % $._config, 127 | 'for': '10m', 128 | labels: { 129 | severity: 'warning', 130 | }, 131 | annotations: { 132 | summary: 'CockroachDB has too many open file descriptors.', 133 | description: 'Too many open file descriptors on {{ $labels.instance }}: {{ $value }} fraction used', 134 | }, 135 | }, 136 | ], 137 | }, 138 | ], 139 | }, 140 | } 141 | -------------------------------------------------------------------------------- /monitoring/config.libsonnet: -------------------------------------------------------------------------------- 1 | { 2 | _config+::{ 3 | cockroachdbSelector: 'job="cockroachdb-public"', 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /monitoring/examples.jsonnet: -------------------------------------------------------------------------------- 1 | local k = (import 'mixin.libsonnet'); 2 | 3 | { 4 | groups: k.prometheusAlerts.groups + k.prometheusRules.groups, 5 | } 6 | -------------------------------------------------------------------------------- /monitoring/examples/prometheus.yaml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: cockroachdb 3 | rules: 4 | - alert: CockroachInstanceFlapping 5 | annotations: 6 | message: '{{ $labels.instance }} for cluster {{ $labels.cluster }} restarted {{ $value }} time(s) in 10m' 7 | expr: | 8 | resets(cockroachdb_sys_uptime{job="cockroachdb-public"}[10m]) > 5 9 | for: 1m 10 | labels: 11 | severity: warning 12 | - alert: CockroachLivenessMismatch 13 | annotations: 14 | message: Liveness mismatch for {{ $labels.instance }} 15 | expr: | 16 | (cockroachdb_liveness_livenodes{job="cockroachdb-public"}) 17 | != 18 | ignoring(instance) group_left() (count by(cluster, job) (up{job="cockroachdb-public"} == 1)) 19 | for: 5m 20 | labels: 21 | severity: warning 22 | - alert: CockroachVersionMismatch 23 | annotations: 24 | message: Cluster {{ $labels.cluster }} running {{ $value }} different versions 25 | expr: | 26 | count by(cluster) (count_values by(tag, cluster) ("version", cockroachdb_build_timestamp{job="cockroachdb-public"})) > 1 27 | for: 1h 28 | labels: 29 | severity: warning 30 | - alert: CockroachStoreDiskLow 31 | annotations: 32 | message: Store {{ $labels.store }} on node {{ $labels.instance }} at {{ $value }} available disk fraction 33 | expr: | 34 | :cockroachdb_capacity_available:ratio{job="cockroachdb-public"} < 0.15 35 | for: 30m 36 | labels: 37 | severity: critical 38 | - alert: CockroachClusterDiskLow 39 | annotations: 40 | message: Cluster {{ $labels.cluster }} at {{ $value }} available disk fraction 41 | expr: | 42 | cluster:cockroachdb_capacity_available:ratio{job="cockroachdb-public"} < 0.2 43 | for: 30m 44 | labels: 45 | severity: critical 46 | - alert: CockroachUnavailableRanges 47 | annotations: 48 | message: Instance {{ $labels.instance }} has {{ $value }} unavailable ranges 49 | expr: | 50 | (sum by(instance, cluster) (cockroachdb_ranges_unavailable{job="cockroachdb-public"})) > 0 51 | for: 10m 52 | labels: 53 | severity: critical 54 | - alert: CockroachNoLeaseRanges 55 | annotations: 56 | message: Instance {{ $labels.instance }} has {{ $value }} ranges without leases 57 | expr: | 58 | (sum by(instance, cluster) (cockroachdb_replicas_leaders_not_leaseholders{job="cockroachdb-public"})) > 0 59 | for: 10m 60 | labels: 61 | severity: warning 62 | - alert: CockroachHighOpenFDCount 63 | annotations: 64 | message: 'Too many open file descriptors on {{ $labels.instance }}: {{ $value }} fraction used' 65 | expr: | 66 | cockroachdb_sys_fd_open{job="cockroachdb-public"} / cockroachdb_sys_fd_softlimit{job="cockroachdb-public"} > 0.8 67 | for: 10m 68 | labels: 69 | severity: warning 70 | - name: cockroachdb.rules 71 | rules: 72 | - expr: | 73 | sum without(store) (cockroachdb_capacity{job="cockroachdb-public"}) 74 | record: node:cockroachdb_capacity:sum 75 | - expr: | 76 | sum without(instance) (node:cockroachdb_capacity:sum{job="cockroachdb-public"}) 77 | record: cluster:cockroachdb_capacity:sum 78 | - expr: | 79 | sum without(store) (cockroachdb_capacity_available{job="cockroachdb-public"}) 80 | record: node:cockroachdb_capacity_available:sum 81 | - expr: | 82 | sum without(instance) (node:cockroachdb_capacity_available:sum{job="cockroachdb-public"}) 83 | record: cluster:cockroachdb_capacity_available:sum 84 | - expr: | 85 | cockroachdb_capacity_available{job="cockroachdb-public"} / cockroachdb_capacity{job="cockroachdb-public"} 86 | record: :cockroachdb_capacity_available:ratio 87 | - expr: | 88 | node:cockroachdb_capacity_available:sum{job="cockroachdb-public"} / node:cockroachdb_capacity:sum{job="cockroachdb-public"} 89 | record: node:cockroachdb_capacity_available:ratio 90 | - expr: | 91 | cluster:cockroachdb_capacity_available:sum{job="cockroachdb-public"} / cluster:cockroachdb_capacity:sum{job="cockroachdb-public"} 92 | record: cluster:cockroachdb_capacity_available:ratio 93 | -------------------------------------------------------------------------------- /monitoring/mixin.libsonnet: -------------------------------------------------------------------------------- 1 | (import 'alerts/cockroachdb.libsonnet') + 2 | (import 'rules/cockroachdb.libsonnet') + 3 | (import 'config.libsonnet') 4 | -------------------------------------------------------------------------------- /monitoring/rules/cockroachdb.libsonnet: -------------------------------------------------------------------------------- 1 | { 2 | prometheusRules+:: { 3 | groups+: [ 4 | { 5 | name: 'cockroachdb.rules', 6 | rules: [ 7 | { 8 | record: 'node:cockroachdb_capacity:sum', 9 | expr: ||| 10 | sum without(store) (cockroachdb_capacity{%(cockroachdbSelector)s}) 11 | ||| % $._config, 12 | }, 13 | { 14 | record: 'cluster:cockroachdb_capacity:sum', 15 | expr: ||| 16 | sum without(instance) (node:cockroachdb_capacity:sum{%(cockroachdbSelector)s}) 17 | ||| % $._config, 18 | }, 19 | { 20 | record: 'node:cockroachdb_capacity_available:sum', 21 | expr: ||| 22 | sum without(store) (cockroachdb_capacity_available{%(cockroachdbSelector)s}) 23 | ||| % $._config, 24 | }, 25 | { 26 | record: 'cluster:cockroachdb_capacity_available:sum', 27 | expr: ||| 28 | sum without(instance) (node:cockroachdb_capacity_available:sum{%(cockroachdbSelector)s}) 29 | ||| % $._config, 30 | }, 31 | { 32 | record: ':cockroachdb_capacity_available:ratio', 33 | expr: ||| 34 | cockroachdb_capacity_available{%(cockroachdbSelector)s} / cockroachdb_capacity{%(cockroachdbSelector)s} 35 | ||| % $._config, 36 | }, 37 | { 38 | record: 'node:cockroachdb_capacity_available:ratio', 39 | expr: ||| 40 | node:cockroachdb_capacity_available:sum{%(cockroachdbSelector)s} / node:cockroachdb_capacity:sum{%(cockroachdbSelector)s} 41 | ||| % $._config, 42 | }, 43 | { 44 | record: 'cluster:cockroachdb_capacity_available:ratio', 45 | expr: ||| 46 | cluster:cockroachdb_capacity_available:sum{%(cockroachdbSelector)s} / cluster:cockroachdb_capacity:sum{%(cockroachdbSelector)s} 47 | ||| % $._config, 48 | }, 49 | ], 50 | }, 51 | ], 52 | }, 53 | } 54 | -------------------------------------------------------------------------------- /operator/actions/decommission.go: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sort" 7 | "strings" 8 | "time" 9 | 10 | "github.com/brancz/locutus/client" 11 | "github.com/go-kit/kit/log" 12 | "github.com/go-kit/kit/log/level" 13 | apierrors "k8s.io/apimachinery/pkg/api/errors" 14 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 | "k8s.io/client-go/kubernetes" 17 | "k8s.io/client-go/rest" 18 | ) 19 | 20 | type DecommissionNodeAction struct { 21 | Konfig *rest.Config 22 | Klient *kubernetes.Clientset 23 | Logger log.Logger 24 | } 25 | 26 | func (a *DecommissionNodeAction) Name() string { 27 | return "DecommissionNode" 28 | } 29 | 30 | func (a *DecommissionNodeAction) Execute(ctx context.Context, rc *client.ResourceClient, u *unstructured.Unstructured) error { 31 | obj, err := rc.Get(ctx, u.GetName(), metav1.GetOptions{}) 32 | if apierrors.IsNotFound(err) { 33 | // There's no statefulset yet (during rollout), 34 | // so we don't need to check for decommissioning any pods. 35 | return nil 36 | } 37 | if err != nil { 38 | return err 39 | } 40 | 41 | currReplicas, ok, err := unstructured.NestedInt64(obj.Object, "spec", "replicas") 42 | if !ok || err != nil { 43 | return fmt.Errorf("failed to get current replicas while decommissioning node: %w", err) 44 | } 45 | 46 | newReplicas, ok, err := unstructured.NestedFloat64(u.Object, "spec", "replicas") 47 | if !ok || err != nil { 48 | return fmt.Errorf("failed to get new replicas while decommissioning node: %w", err) 49 | } 50 | 51 | // replica count doesn't decrease, not need to decommission 52 | if int64(newReplicas) >= currReplicas { 53 | level.Debug(a.Logger).Log("msg", "no nodes to decommission") 54 | return nil 55 | } 56 | 57 | podName := fmt.Sprintf("%s-%d", obj.GetName(), 0) 58 | podNamespace := obj.GetNamespace() 59 | 60 | nodeStatuses, err := getStatus(a.Konfig, a.Klient, podNamespace, podName) 61 | if err != nil { 62 | return fmt.Errorf("failed to get node statuses for decommission: %w", err) 63 | } 64 | 65 | sort.Slice(nodeStatuses, func(i, j int) bool { 66 | return nodeStatuses[i].Address < nodeStatuses[j].Address 67 | }) 68 | 69 | var decommissionIDs []int64 70 | for i := currReplicas - 1; i >= int64(newReplicas); i-- { 71 | decommissionIDs = append(decommissionIDs, nodeStatuses[i].ID) 72 | } 73 | 74 | if len(decommissionIDs) == 0 { 75 | fmt.Println("No nodes to decommission") 76 | return nil 77 | } 78 | 79 | command := []string{"cockroach", "node", "decommission", "--insecure", "--self"} 80 | for _, id := range decommissionIDs { 81 | command = append(command, fmt.Sprintf("%d", id)) 82 | } 83 | level.Debug(a.Logger).Log("msg", "decommissioning nodes", "command", strings.Join(command, " ")) 84 | start := time.Now() 85 | _, _, err = podExec(a.Konfig, a.Klient, podNamespace, podName, command) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | level.Info(a.Logger).Log( 91 | "msg", "successfully decommissioned nodes", 92 | "command", strings.Join(command, " "), 93 | "duration", time.Since(start), 94 | ) 95 | 96 | return nil 97 | } 98 | -------------------------------------------------------------------------------- /operator/actions/initialize.go: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | "time" 10 | 11 | "github.com/brancz/locutus/client" 12 | "github.com/go-kit/kit/log" 13 | "github.com/go-kit/kit/log/level" 14 | v1 "k8s.io/api/core/v1" 15 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 16 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 17 | "k8s.io/client-go/kubernetes" 18 | "k8s.io/client-go/kubernetes/scheme" 19 | "k8s.io/client-go/rest" 20 | "k8s.io/client-go/tools/remotecommand" 21 | ) 22 | 23 | type InitializeCockroachDBAction struct { 24 | Konfig *rest.Config 25 | Klient *kubernetes.Clientset 26 | Logger log.Logger 27 | } 28 | 29 | func (a *InitializeCockroachDBAction) Name() string { 30 | return "InitializeIfNot" 31 | } 32 | 33 | func (a *InitializeCockroachDBAction) Execute(ctx context.Context, rc *client.ResourceClient, u *unstructured.Unstructured) error { 34 | obj, err := rc.Get(ctx, u.GetName(), metav1.GetOptions{}) 35 | if err != nil { 36 | return fmt.Errorf("failed to get resource while initializing CockroachDB") 37 | } 38 | 39 | // Get number of replicas in StatefulSet 40 | replicas, ok, err := unstructured.NestedInt64(obj.Object, "spec", "replicas") 41 | if !ok || err != nil { 42 | return fmt.Errorf("failed to get replicas while initializing CockroachDB: %w", err) 43 | } 44 | 45 | if replicas < 1 { 46 | return nil 47 | } 48 | 49 | podNamespace := obj.GetNamespace() 50 | podName := fmt.Sprintf("%s-%d", obj.GetName(), 0) 51 | 52 | command := []string{"cockroach", "init", "--insecure", fmt.Sprintf("--host=%s.%s.%s", podName, obj.GetName(), podNamespace)} 53 | level.Debug(a.Logger).Log("msg", "initializing CockroachDB cluster", "command", strings.Join(command, " ")) 54 | start := time.Now() 55 | _, stderr, err := podExec(a.Konfig, a.Klient, podNamespace, podName, command) 56 | if stderr != nil { 57 | if strings.Contains(stderr.String(), "cluster has already been initialized") { 58 | return nil 59 | } 60 | } 61 | if err != nil { 62 | fmt.Println(err, stderr.String()) 63 | // TODO: Should be made a transient error 64 | return nil 65 | } 66 | 67 | level.Info(a.Logger).Log( 68 | "msg", "successfully initialized CockroachDB cluster", 69 | "command", strings.Join(command, " "), 70 | "duration", time.Since(start), 71 | ) 72 | 73 | return nil 74 | } 75 | 76 | func podExec(konfig *rest.Config, klient *kubernetes.Clientset, namespace string, name string, command []string) (*bytes.Buffer, *bytes.Buffer, error) { 77 | ctx := context.TODO() 78 | pod, err := klient.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{}) 79 | if err != nil { 80 | return nil, nil, err 81 | } 82 | 83 | req := klient.CoreV1().RESTClient(). 84 | Post(). 85 | Resource("pods"). 86 | Name(pod.GetName()). 87 | Namespace(pod.GetNamespace()). 88 | SubResource("exec"). 89 | Timeout(10*time.Second). 90 | VersionedParams(&v1.PodExecOptions{ 91 | Container: pod.Spec.Containers[0].Name, 92 | Command: command, 93 | Stdout: true, 94 | Stderr: true, 95 | }, scheme.ParameterCodec) 96 | 97 | exec, err := remotecommand.NewSPDYExecutor(konfig, "POST", req.URL()) 98 | if err != nil { 99 | return nil, nil, fmt.Errorf("failed to create executor: %w", err) 100 | } 101 | 102 | stdout := &bytes.Buffer{} 103 | stderr := &bytes.Buffer{} 104 | err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{ 105 | Stdout: stdout, 106 | Stderr: stderr, 107 | Tty: false, 108 | }) 109 | 110 | if err != nil { 111 | return stdout, stderr, fmt.Errorf("failed to execute: %w", err) 112 | } 113 | 114 | return stdout, stderr, nil 115 | } 116 | 117 | type NodeStatus struct { 118 | ID int64 119 | Address string 120 | Available bool 121 | Live bool 122 | Decommissioning bool 123 | Draining bool 124 | } 125 | 126 | func getStatus(konfig *rest.Config, klient *kubernetes.Clientset, namespace string, name string) ([]NodeStatus, error) { 127 | command := []string{"cockroach", "node", "status", "--insecure", "--all", "--format=tsv"} 128 | stdout, _, err := podExec(konfig, klient, namespace, name, command) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return parseStatus(stdout.String()) 133 | } 134 | 135 | func parseStatus(stdout string) ([]NodeStatus, error) { 136 | var status []NodeStatus 137 | 138 | parseBool := func(in string) bool { 139 | if in == "true" { 140 | return true 141 | } 142 | return false 143 | } 144 | 145 | stdout = strings.TrimSpace(stdout) 146 | lines := strings.Split(stdout, "\n") 147 | 148 | idColumnIndex := -1 149 | addressColumnIndex := -1 150 | isAvailableColumnIndex := -1 151 | isLiveColumnIndex := -1 152 | isDecommissioningColumnIndex := -1 153 | isDrainingColumnIndex := -1 154 | 155 | headerLine := lines[0] 156 | columns := strings.Split(headerLine, "\t") 157 | for i, column := range columns { 158 | switch column { 159 | case "id": 160 | idColumnIndex = i 161 | case "address": 162 | addressColumnIndex = i 163 | case "is_available": 164 | isAvailableColumnIndex = i 165 | case "is_live": 166 | isLiveColumnIndex = i 167 | case "is_decommissioning": 168 | isDecommissioningColumnIndex = i 169 | case "is_draining": 170 | isDrainingColumnIndex = i 171 | } 172 | } 173 | 174 | if idColumnIndex == -1 { 175 | return nil, fmt.Errorf("failed to find id column in status output") 176 | } 177 | 178 | if addressColumnIndex == -1 { 179 | return nil, fmt.Errorf("failed to find address column in status output") 180 | } 181 | 182 | if isAvailableColumnIndex == -1 { 183 | return nil, fmt.Errorf("failed to find is_available column in status output") 184 | } 185 | 186 | if isLiveColumnIndex == -1 { 187 | return nil, fmt.Errorf("failed to find is_live column in status output") 188 | } 189 | 190 | if isDecommissioningColumnIndex == -1 { 191 | return nil, fmt.Errorf("failed to find is_decommissioning column in status output") 192 | } 193 | 194 | if isDrainingColumnIndex == -1 { 195 | return nil, fmt.Errorf("failed to find is_draining column in status output") 196 | } 197 | 198 | // Exclude the header line. 199 | for _, line := range lines[1:] { 200 | columns := strings.Split(line, "\t") 201 | id, err := strconv.ParseInt(columns[idColumnIndex], 10, 64) 202 | if err != nil { 203 | return status, err 204 | } 205 | 206 | status = append(status, NodeStatus{ 207 | ID: id, 208 | Address: columns[addressColumnIndex], 209 | Available: parseBool(columns[isAvailableColumnIndex]), 210 | Live: parseBool(columns[isLiveColumnIndex]), 211 | Decommissioning: parseBool(columns[isDecommissioningColumnIndex]), 212 | Draining: parseBool(columns[isDrainingColumnIndex]), 213 | }) 214 | } 215 | 216 | return status, nil 217 | } 218 | -------------------------------------------------------------------------------- /operator/actions/initialize_test.go: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func Test_parseStatus(t *testing.T) { 9 | cases := []struct { 10 | name string 11 | input string 12 | expected []NodeStatus 13 | }{ 14 | { 15 | name: "standard", 16 | input: ` 17 | id address sql_address build started_at updated_at locality is_available is_live replicas_leaders replicas_leaseholders ranges ranges_unavailable ranges_underreplicated live_bytes key_bytes value_bytes intent_bytes system_bytes gossiped_replicas is_decommissioning is_draining 18 | 1 cockroachdb-basic-0.cockroachdb-basic.default.svc.cluster.local:26257 cockroachdb-basic-0.cockroachdb-basic.default.svc.cluster.local:26257 v20.1.0 2020-05-23 17:48:27.739773+00:00 2020-05-23 18:37:57.762499+00:00 true true 13 13 32 0 0 32256729 216458 32160705 0 35098 32 false false 19 | 2 cockroachdb-basic-1.cockroachdb-basic.default.svc.cluster.local:26257 cockroachdb-basic-1.cockroachdb-basic.default.svc.cluster.local:26257 v20.1.0 2020-05-23 17:48:27.963981+00:00 2020-05-23 18:37:58.53086+00:00 true true 8 8 32 0 0 32290475 216470 32194474 0 35098 32 false false 20 | 3 cockroachdb-basic-2.cockroachdb-basic.default.svc.cluster.local:26257 cockroachdb-basic-2.cockroachdb-basic.default.svc.cluster.local:26257 v20.1.0 2020-05-23 17:48:28.726112+00:00 2020-05-23 18:37:58.769248+00:00 true true 11 11 32 0 0 32324221 216482 32228243 0 35098 32 false false 21 | 4 cockroachdb-basic-4.cockroachdb-basic.default.svc.cluster.local:26257 cockroachdb-basic-4.cockroachdb-basic.default.svc.cluster.local:26257 v20.1.0 2020-05-23 18:35:14.928669+00:00 2020-05-23 18:37:57.549187+00:00 true true 0 0 0 0 0 0 0 0 0 0 0 true false 22 | 8 cockroachdb-basic-3.cockroachdb-basic.default.svc.cluster.local:26257 cockroachdb-basic-3.cockroachdb-basic.default.svc.cluster.local:26257 v20.1.0 2020-05-23 18:35:14.923954+00:00 2020-05-23 18:37:57.571877+00:00 true true 0 0 0 0 0 0 0 0 0 0 0 true false 23 | `, 24 | expected: []NodeStatus{{ 25 | ID: 1, 26 | Address: "cockroachdb-basic-0", 27 | Available: true, 28 | Live: true, 29 | Decommissioning: false, 30 | Draining: false, 31 | }, { 32 | ID: 2, 33 | Address: "cockroachdb-basic-1", 34 | Available: true, 35 | Live: true, 36 | Decommissioning: false, 37 | Draining: false, 38 | }, { 39 | ID: 3, 40 | Address: "cockroachdb-basic-2", 41 | Available: true, 42 | Live: true, 43 | Decommissioning: false, 44 | Draining: false, 45 | }, { 46 | ID: 4, 47 | Address: "cockroachdb-basic-4", 48 | Available: true, 49 | Live: true, 50 | Decommissioning: true, 51 | Draining: false, 52 | }, { 53 | ID: 8, 54 | Address: "cockroachdb-basic-3", 55 | Available: true, 56 | Live: true, 57 | Decommissioning: true, 58 | Draining: false, 59 | }}, 60 | }, 61 | { 62 | name: "v20", 63 | // v20 added the "membership" column. 64 | input: ` 65 | id address sql_address build started_at updated_at locality is_available is_live replicas_leaders replicas_leaseholders ranges ranges_unavailable ranges_underreplicated live_bytes key_bytes value_bytes intent_bytes system_bytes gossiped_replicas is_decommissioning membership is_draining 66 | 1 cockroachdb-cockroachdb-0.cockroachdb-cockroachdb.db.svc.cluster.local:26257 cockroachdb-cockroachdb-0.cockroachdb-cockroachdb.db.svc.cluster.local:26257 v20.2.3 2022-02-13 09:19:31.671654+00:00 2022-02-13 10:11:03.488379+00:00 true true 41 41 72 0 72 3082237972 41254331 3047032980 1612 158761 72 false active false 67 | 2 cockroachdb-cockroachdb-1.cockroachdb-cockroachdb.db.svc.cluster.local:26257 cockroachdb-cockroachdb-1.cockroachdb-cockroachdb.db.svc.cluster.local:26257 v20.2.3 2022-02-13 09:56:12.448711+00:00 2022-02-13 10:01:25.746251+00:00 false false 26 24 72 0 0 3080325214 41696315 3045892113 1640 447868 0 false active false 68 | 3 cockroachdb-cockroachdb-2.cockroachdb-cockroachdb.db.svc.cluster.local:26257 cockroachdb-cockroachdb-2.cockroachdb-cockroachdb.db.svc.cluster.local:26257 v20.2.3 2022-02-13 09:28:00.398419+00:00 2022-02-13 10:10:59.203639+00:00 true true 31 31 72 0 0 3082237972 41254331 3047032980 1612 158761 72 false active false 69 | `, 70 | expected: []NodeStatus{{ 71 | ID: 1, 72 | Address: "cockroachdb-cockroachdb-0.cockroachdb-cockroachdb.db.svc.cluster.local:26257", 73 | Available: true, 74 | Live: true, 75 | Decommissioning: false, 76 | Draining: false, 77 | }, { 78 | ID: 2, 79 | Address: "cockroachdb-cockroachdb-1.cockroachdb-cockroachdb.db.svc.cluster.local:26257", 80 | Available: false, 81 | Live: false, 82 | Decommissioning: false, 83 | Draining: false, 84 | }, { 85 | ID: 3, 86 | Address: "cockroachdb-cockroachdb-2.cockroachdb-cockroachdb.db.svc.cluster.local:26257", 87 | Available: true, 88 | Live: true, 89 | Decommissioning: false, 90 | Draining: false, 91 | }}, 92 | }, 93 | } 94 | 95 | for _, testCase := range cases { 96 | t.Run(testCase.name, func(t *testing.T) { 97 | status, err := parseStatus(testCase.input) 98 | if err != nil { 99 | t.Errorf("failed to parse status: %v", err) 100 | } 101 | 102 | if len(status) != len(testCase.expected) { 103 | t.Fatalf("expected %d nodes, got %d", len(testCase.expected), len(status)) 104 | } 105 | 106 | for i, expected := range testCase.expected { 107 | if status[i].ID != expected.ID { 108 | t.Errorf("row %d ID was parsed incorrectly, expected %d got %d", i, expected.ID, status[i].ID) 109 | } 110 | if !strings.HasPrefix(status[i].Address, expected.Address) { 111 | t.Errorf("row %d address was parsed incorrectly, expected %s got %s", i, expected.Address, status[i].Address) 112 | } 113 | if status[i].Available != expected.Available { 114 | t.Errorf("row %d available was parsed incorrectly, expected %v got %v", i, expected.Available, status[i].Available) 115 | } 116 | if status[i].Live != expected.Live { 117 | t.Errorf("row %d live was parsed incorrectly, expected %v got %v", i, expected.Live, status[i].Live) 118 | } 119 | if status[i].Decommissioning != expected.Decommissioning { 120 | t.Errorf("row %d decommissioning was parsed incorrectly, expected %v got %v", i, expected.Decommissioning, status[i].Decommissioning) 121 | } 122 | if status[i].Draining != expected.Draining { 123 | t.Errorf("row %d draining was parsed incorrectly, expected %v got %v", i, expected.Draining, status[i].Draining) 124 | } 125 | } 126 | }) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /operator/actions/recommission.go: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | "time" 8 | 9 | "github.com/brancz/locutus/client" 10 | "github.com/go-kit/kit/log" 11 | "github.com/go-kit/kit/log/level" 12 | apierrors "k8s.io/apimachinery/pkg/api/errors" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 15 | "k8s.io/client-go/kubernetes" 16 | "k8s.io/client-go/rest" 17 | ) 18 | 19 | type RecommissionNodeAction struct { 20 | Konfig *rest.Config 21 | Klient *kubernetes.Clientset 22 | Logger log.Logger 23 | } 24 | 25 | func (a *RecommissionNodeAction) Name() string { 26 | return "RecommissionNode" 27 | } 28 | 29 | func (a *RecommissionNodeAction) Execute(ctx context.Context, rc *client.ResourceClient, u *unstructured.Unstructured) error { 30 | obj, err := rc.Get(ctx, u.GetName(), metav1.GetOptions{}) 31 | if apierrors.IsNotFound(err) { 32 | // There's no statefulset yet (during rollout), 33 | // so we don't need to check for decommissioning any pods. 34 | return nil 35 | } 36 | if err != nil { 37 | return err 38 | } 39 | 40 | podName := fmt.Sprintf("%s-%d", obj.GetName(), 0) 41 | podNamespace := obj.GetNamespace() 42 | 43 | nodeStatuses, err := getStatus(a.Konfig, a.Klient, podNamespace, podName) 44 | if err != nil { 45 | return fmt.Errorf("failed to get node statuses for recommission: %w", err) 46 | } 47 | 48 | var recommissionIDs []int64 49 | for _, ns := range nodeStatuses { 50 | if ns.Live && ns.Available && !ns.Draining && ns.Decommissioning { 51 | recommissionIDs = append(recommissionIDs, ns.ID) 52 | } 53 | } 54 | 55 | if len(recommissionIDs) == 0 { 56 | level.Debug(a.Logger).Log("msg", "no nodes to recommission") 57 | return nil 58 | } 59 | 60 | start := time.Now() 61 | 62 | command := []string{"cockroach", "node", "recommission", "--insecure", "--self"} 63 | level.Debug(a.Logger).Log("msg", "recommissioning nodes", "command", strings.Join(command, " ")) 64 | for _, id := range recommissionIDs { 65 | command = append(command, fmt.Sprintf("%d", id)) 66 | } 67 | _, _, err = podExec(a.Konfig, a.Klient, podNamespace, podName, command) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | level.Info(a.Logger).Log( 73 | "msg", "successfully recommissioned nodes", 74 | "command", strings.Join(command, " "), 75 | "duration", time.Since(start), 76 | ) 77 | 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /operator/api/v1alphav1/cockroachdb_types.go: -------------------------------------------------------------------------------- 1 | /* 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 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package v1alpha1 17 | 18 | import ( 19 | corev1 "k8s.io/api/core/v1" 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | func init() { 24 | SchemeBuilder.Register(&CockroachDB{}, &CockroachDBList{}) 25 | } 26 | 27 | // CockroachDBList contains a list of CockroachDB 28 | // +kubebuilder:object:root=true 29 | type CockroachDBList struct { 30 | metav1.TypeMeta `json:",inline"` 31 | metav1.ListMeta `json:"metadata,omitempty"` 32 | Items []CockroachDB `json:"items"` 33 | } 34 | 35 | // CockroachDB is the Schema for the a CockroachDB instance managed by the Operator. 36 | // +kubebuilder:object:root=true 37 | // +kubebuilder:subresource:status 38 | // +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.selector 39 | // +kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.replicas" 40 | // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" 41 | type CockroachDB struct { 42 | metav1.TypeMeta `json:",inline"` 43 | metav1.ObjectMeta `json:"metadata,omitempty"` 44 | 45 | Spec CockroachDBSpec `json:"spec,omitempty"` 46 | Status *CockroachDBStatus `json:"status,omitempty"` 47 | } 48 | 49 | // CockroachDBSpec defines the desired state of a CockroachDB instance. 50 | type CockroachDBSpec struct { 51 | Image *string `json:"image"` 52 | 53 | // Define the number of replicas to run for the CockroachDB cluster. 54 | Replicas *uint32 `json:"replicas,omitempty"` 55 | 56 | // Define resources requests and limits for single Pods. 57 | Resources corev1.ResourceRequirements `json:"resources,omitempty"` 58 | 59 | // Storage spec to specify how storage shall be used. 60 | Storage *StorageSpec `json:"storage,omitempty"` 61 | } 62 | 63 | // Storage parts are taken from the Prometheus Operator: 64 | 65 | // StorageSpec defines the configured storage for a group CockroachDB clusters. 66 | // If neither `emptyDir` nor `volumeClaimTemplate` is specified, then by default an [EmptyDir](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir) will be used. 67 | // +k8s:openapi-gen=true 68 | type StorageSpec struct { 69 | // EmptyDirVolumeSource to be used by the CockroachDB StatefulSets. If specified, used in place of any volumeClaimTemplate. More 70 | // info: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir 71 | EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"` 72 | // A PVC spec to be used by the CockroachDB StatefulSets. 73 | VolumeClaimTemplate EmbeddedPersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"` 74 | } 75 | 76 | // EmbeddedPersistentVolumeClaim is an embedded version of k8s.io/api/core/v1.PersistentVolumeClaim. 77 | // It contains TypeMeta and a reduced ObjectMeta. 78 | type EmbeddedPersistentVolumeClaim struct { 79 | metav1.TypeMeta `json:",inline"` 80 | 81 | // EmbeddedMetadata contains metadata relevant to an EmbeddedResource. 82 | EmbeddedObjectMetadata `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` 83 | 84 | // Spec defines the desired characteristics of a volume requested by a pod author. 85 | // More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims 86 | // +optional 87 | Spec corev1.PersistentVolumeClaimSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` 88 | 89 | // Status represents the current information/status of a persistent volume claim. 90 | // Read-only. 91 | // More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims 92 | // +optional 93 | Status corev1.PersistentVolumeClaimStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` 94 | } 95 | 96 | // EmbeddedObjectMetadata contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta 97 | // Only fields which are relevant to embedded resources are included. 98 | type EmbeddedObjectMetadata struct { 99 | // Name must be unique within a namespace. Is required when creating resources, although 100 | // some resources may allow a client to request the generation of an appropriate name 101 | // automatically. Name is primarily intended for creation idempotence and configuration 102 | // definition. 103 | // Cannot be updated. 104 | // More info: http://kubernetes.io/docs/user-guide/identifiers#names 105 | // +optional 106 | Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` 107 | 108 | // Map of string keys and values that can be used to organize and categorize 109 | // (scope and select) objects. May match selectors of replication controllers 110 | // and services. 111 | // More info: http://kubernetes.io/docs/user-guide/labels 112 | // +optional 113 | Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"` 114 | 115 | // Annotations is an unstructured key value map stored with a resource that may be 116 | // set by external tools to store and retrieve arbitrary metadata. They are not 117 | // queryable and should be preserved when modifying objects. 118 | // More info: http://kubernetes.io/docs/user-guide/annotations 119 | // +optional 120 | Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"` 121 | } 122 | 123 | type CockroachDBStatus struct{} 124 | -------------------------------------------------------------------------------- /operator/api/v1alphav1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha1 contains API Schema definitions for the CockroachDB v1alpha1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=metalmatze.de 4 | package v1alpha1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "metalmatze.de", Version: "v1alpha1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /operator/api/v1alphav1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | // Code generated by controller-gen. DO NOT EDIT. 5 | 6 | package v1alpha1 7 | 8 | import ( 9 | "k8s.io/api/core/v1" 10 | runtime "k8s.io/apimachinery/pkg/runtime" 11 | ) 12 | 13 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 14 | func (in *CockroachDB) DeepCopyInto(out *CockroachDB) { 15 | *out = *in 16 | out.TypeMeta = in.TypeMeta 17 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 18 | in.Spec.DeepCopyInto(&out.Spec) 19 | if in.Status != nil { 20 | in, out := &in.Status, &out.Status 21 | *out = new(CockroachDBStatus) 22 | **out = **in 23 | } 24 | } 25 | 26 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CockroachDB. 27 | func (in *CockroachDB) DeepCopy() *CockroachDB { 28 | if in == nil { 29 | return nil 30 | } 31 | out := new(CockroachDB) 32 | in.DeepCopyInto(out) 33 | return out 34 | } 35 | 36 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 37 | func (in *CockroachDB) DeepCopyObject() runtime.Object { 38 | if c := in.DeepCopy(); c != nil { 39 | return c 40 | } 41 | return nil 42 | } 43 | 44 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 45 | func (in *CockroachDBList) DeepCopyInto(out *CockroachDBList) { 46 | *out = *in 47 | out.TypeMeta = in.TypeMeta 48 | in.ListMeta.DeepCopyInto(&out.ListMeta) 49 | if in.Items != nil { 50 | in, out := &in.Items, &out.Items 51 | *out = make([]CockroachDB, len(*in)) 52 | for i := range *in { 53 | (*in)[i].DeepCopyInto(&(*out)[i]) 54 | } 55 | } 56 | } 57 | 58 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CockroachDBList. 59 | func (in *CockroachDBList) DeepCopy() *CockroachDBList { 60 | if in == nil { 61 | return nil 62 | } 63 | out := new(CockroachDBList) 64 | in.DeepCopyInto(out) 65 | return out 66 | } 67 | 68 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 69 | func (in *CockroachDBList) DeepCopyObject() runtime.Object { 70 | if c := in.DeepCopy(); c != nil { 71 | return c 72 | } 73 | return nil 74 | } 75 | 76 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 77 | func (in *CockroachDBSpec) DeepCopyInto(out *CockroachDBSpec) { 78 | *out = *in 79 | if in.Image != nil { 80 | in, out := &in.Image, &out.Image 81 | *out = new(string) 82 | **out = **in 83 | } 84 | if in.Replicas != nil { 85 | in, out := &in.Replicas, &out.Replicas 86 | *out = new(uint32) 87 | **out = **in 88 | } 89 | in.Resources.DeepCopyInto(&out.Resources) 90 | if in.Storage != nil { 91 | in, out := &in.Storage, &out.Storage 92 | *out = new(StorageSpec) 93 | (*in).DeepCopyInto(*out) 94 | } 95 | } 96 | 97 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CockroachDBSpec. 98 | func (in *CockroachDBSpec) DeepCopy() *CockroachDBSpec { 99 | if in == nil { 100 | return nil 101 | } 102 | out := new(CockroachDBSpec) 103 | in.DeepCopyInto(out) 104 | return out 105 | } 106 | 107 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 108 | func (in *CockroachDBStatus) DeepCopyInto(out *CockroachDBStatus) { 109 | *out = *in 110 | } 111 | 112 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CockroachDBStatus. 113 | func (in *CockroachDBStatus) DeepCopy() *CockroachDBStatus { 114 | if in == nil { 115 | return nil 116 | } 117 | out := new(CockroachDBStatus) 118 | in.DeepCopyInto(out) 119 | return out 120 | } 121 | 122 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 123 | func (in *EmbeddedObjectMetadata) DeepCopyInto(out *EmbeddedObjectMetadata) { 124 | *out = *in 125 | if in.Labels != nil { 126 | in, out := &in.Labels, &out.Labels 127 | *out = make(map[string]string, len(*in)) 128 | for key, val := range *in { 129 | (*out)[key] = val 130 | } 131 | } 132 | if in.Annotations != nil { 133 | in, out := &in.Annotations, &out.Annotations 134 | *out = make(map[string]string, len(*in)) 135 | for key, val := range *in { 136 | (*out)[key] = val 137 | } 138 | } 139 | } 140 | 141 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmbeddedObjectMetadata. 142 | func (in *EmbeddedObjectMetadata) DeepCopy() *EmbeddedObjectMetadata { 143 | if in == nil { 144 | return nil 145 | } 146 | out := new(EmbeddedObjectMetadata) 147 | in.DeepCopyInto(out) 148 | return out 149 | } 150 | 151 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 152 | func (in *EmbeddedPersistentVolumeClaim) DeepCopyInto(out *EmbeddedPersistentVolumeClaim) { 153 | *out = *in 154 | out.TypeMeta = in.TypeMeta 155 | in.EmbeddedObjectMetadata.DeepCopyInto(&out.EmbeddedObjectMetadata) 156 | in.Spec.DeepCopyInto(&out.Spec) 157 | in.Status.DeepCopyInto(&out.Status) 158 | } 159 | 160 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EmbeddedPersistentVolumeClaim. 161 | func (in *EmbeddedPersistentVolumeClaim) DeepCopy() *EmbeddedPersistentVolumeClaim { 162 | if in == nil { 163 | return nil 164 | } 165 | out := new(EmbeddedPersistentVolumeClaim) 166 | in.DeepCopyInto(out) 167 | return out 168 | } 169 | 170 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 171 | func (in *StorageSpec) DeepCopyInto(out *StorageSpec) { 172 | *out = *in 173 | if in.EmptyDir != nil { 174 | in, out := &in.EmptyDir, &out.EmptyDir 175 | *out = new(v1.EmptyDirVolumeSource) 176 | (*in).DeepCopyInto(*out) 177 | } 178 | in.VolumeClaimTemplate.DeepCopyInto(&out.VolumeClaimTemplate) 179 | } 180 | 181 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSpec. 182 | func (in *StorageSpec) DeepCopy() *StorageSpec { 183 | if in == nil { 184 | return nil 185 | } 186 | out := new(StorageSpec) 187 | in.DeepCopyInto(out) 188 | return out 189 | } 190 | -------------------------------------------------------------------------------- /operator/config.yaml: -------------------------------------------------------------------------------- 1 | mainResource: CockroachDB 2 | resources: 3 | - name: CockroachDB 4 | kind: CockroachDB 5 | apiVersion: metalmatze.de/v1alpha1 6 | - name: statefulSet 7 | kind: StatefulSet 8 | apiVersion: apps/v1 9 | keyTransformations: 10 | - action: keep 11 | regex: '(.*)\/cockroachdb-(.*)' 12 | - regex: '(.*)\/cockroachdb-(.*)' 13 | replacement: "$1/$2" 14 | - name: service 15 | kind: Service 16 | apiVersion: v1 17 | keyTransformations: 18 | - action: keep 19 | regex: '(.*)\/cockroachdb-(.*)' 20 | - regex: '(.*)\/cockroachdb-(.*)' 21 | replacement: "$1/$2" 22 | - name: servicePublic 23 | kind: Service 24 | apiVersion: v1 25 | keyTransformations: 26 | - action: keep 27 | regex: '(.*)\/cockroachdb-(.*)-public$' 28 | - regex: '(.*)\/cockroachdb-(.*)-public$' 29 | replacement: "$1/$2" 30 | - name: podDisruptionBudget 31 | kind: PodDisruptionBudget 32 | apiVersion: policy/v1 33 | keyTransformations: 34 | - action: keep 35 | regex: '(.*)\/cockroachdb-(.*)' 36 | - regex: '(.*)\/cockroachdb-(.*)' 37 | replacement: "$1/$2" 38 | - name: serviceMonitor 39 | kind: ServiceMonitor 40 | apiVersion: monitoring.coreos.com/v1 41 | keyTransformations: 42 | - action: keep 43 | regex: '(.*)\/cockroachdb-(.*)' 44 | - regex: '(.*)\/cockroachdb-(.*)' 45 | replacement: "$1/$2" 46 | -------------------------------------------------------------------------------- /operator/deployment.jsonnet: -------------------------------------------------------------------------------- 1 | local objects = { 2 | metadata:: { 3 | name: 'kube-cockroachdb', 4 | namespace: 'kube-cockroachdb', 5 | labels: { 6 | 'app.kubernetes.io/name': 'kube-cockroachdb', 7 | 'app.kubernetes.io/component': 'operator', 8 | }, 9 | }, 10 | 11 | deployment: { 12 | kind: 'Deployment', 13 | apiVersion: 'apps/v1', 14 | metadata: $.metadata, 15 | spec: { 16 | selector: { 17 | matchLabels: $.deployment.metadata.labels, 18 | }, 19 | template: { 20 | metadata: { 21 | labels: $.deployment.metadata.labels, 22 | }, 23 | spec: { 24 | serviceAccountName: $.serviceAccount.metadata.name, 25 | containers: [ 26 | { 27 | name: 'kube-cockroachdb', 28 | image: 'quay.io/metalmatze/kube-cockroachdb', 29 | imagePullPolicy: 'Always', 30 | args: [ 31 | '--jsonnet.main=main.jsonnet', 32 | '--trigger.config=config.yaml', 33 | ], 34 | }, 35 | ], 36 | }, 37 | }, 38 | }, 39 | }, 40 | serviceAccount: { 41 | apiVersion: 'v1', 42 | kind: 'ServiceAccount', 43 | metadata: $.metadata, 44 | }, 45 | clusterRole: { 46 | apiVersion: 'rbac.authorization.k8s.io/v1', 47 | kind: 'ClusterRole', 48 | metadata: $.metadata, 49 | rules: [ 50 | { apiGroups: ['metalmatze.de'], resources: ['cockroachdbs'], verbs: ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'] }, 51 | { apiGroups: ['metalmatze.de'], resources: ['cockroachdbs/status'], verbs: ['get', 'patch', 'update'] }, 52 | { apiGroups: [''], resources: ['services'], verbs: ['list', 'watch', 'get', 'create', 'update'] }, 53 | { apiGroups: [''], resources: ['pods'], verbs: ['get'] }, 54 | { apiGroups: [''], resources: ['pods/exec'], verbs: ['create'] }, 55 | { apiGroups: ['apps'], resources: ['statefulsets'], verbs: ['list', 'watch', 'get', 'create', 'update'] }, 56 | { apiGroups: ['policy'], resources: ['poddisruptionbudgets'], verbs: ['list', 'watch'] }, 57 | { apiGroups: ['monitoring.coreos.com'], resources: ['servicemonitors', 'prometheusrules'], verbs: ['list', 'watch', 'get', 'create', 'update'] }, 58 | ], 59 | }, 60 | clusterRoleBinding: { 61 | apiVersion: 'rbac.authorization.k8s.io/v1', 62 | kind: 'ClusterRoleBinding', 63 | metadata: $.metadata, 64 | roleRef: { 65 | apiGroup: 'rbac.authorization.k8s.io', 66 | kind: 'ClusterRole', 67 | name: $.clusterRole.metadata.name, 68 | }, 69 | subjects: [ 70 | { 71 | kind: 'ServiceAccount', 72 | name: $.serviceAccount.metadata.name, 73 | namespace: $.serviceAccount.metadata.namespace, 74 | }, 75 | ], 76 | }, 77 | // podMonitor: {} 78 | }; 79 | 80 | { 81 | apiVersion: 'v1', 82 | kind: 'List', 83 | items: 84 | [objects[name] for name in std.objectFields(objects)], 85 | } 86 | -------------------------------------------------------------------------------- /operator/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | labels: 7 | app.kubernetes.io/component: operator 8 | app.kubernetes.io/name: kube-cockroachdb 9 | name: kube-cockroachdb 10 | namespace: kube-cockroachdb 11 | rules: 12 | - apiGroups: 13 | - metalmatze.de 14 | resources: 15 | - cockroachdbs 16 | verbs: 17 | - create 18 | - delete 19 | - get 20 | - list 21 | - patch 22 | - update 23 | - watch 24 | - apiGroups: 25 | - metalmatze.de 26 | resources: 27 | - cockroachdbs/status 28 | verbs: 29 | - get 30 | - patch 31 | - update 32 | - apiGroups: 33 | - "" 34 | resources: 35 | - services 36 | verbs: 37 | - list 38 | - watch 39 | - get 40 | - create 41 | - update 42 | - apiGroups: 43 | - "" 44 | resources: 45 | - pods 46 | verbs: 47 | - get 48 | - apiGroups: 49 | - "" 50 | resources: 51 | - pods/exec 52 | verbs: 53 | - create 54 | - apiGroups: 55 | - apps 56 | resources: 57 | - statefulsets 58 | verbs: 59 | - list 60 | - watch 61 | - get 62 | - create 63 | - update 64 | - apiGroups: 65 | - policy 66 | resources: 67 | - poddisruptionbudgets 68 | verbs: 69 | - list 70 | - watch 71 | - apiGroups: 72 | - monitoring.coreos.com 73 | resources: 74 | - servicemonitors 75 | - prometheusrules 76 | verbs: 77 | - list 78 | - watch 79 | - get 80 | - create 81 | - update 82 | - apiVersion: rbac.authorization.k8s.io/v1 83 | kind: ClusterRoleBinding 84 | metadata: 85 | labels: 86 | app.kubernetes.io/component: operator 87 | app.kubernetes.io/name: kube-cockroachdb 88 | name: kube-cockroachdb 89 | namespace: kube-cockroachdb 90 | roleRef: 91 | apiGroup: rbac.authorization.k8s.io 92 | kind: ClusterRole 93 | name: kube-cockroachdb 94 | subjects: 95 | - kind: ServiceAccount 96 | name: kube-cockroachdb 97 | namespace: kube-cockroachdb 98 | - apiVersion: apps/v1 99 | kind: Deployment 100 | metadata: 101 | labels: 102 | app.kubernetes.io/component: operator 103 | app.kubernetes.io/name: kube-cockroachdb 104 | name: kube-cockroachdb 105 | namespace: kube-cockroachdb 106 | spec: 107 | selector: 108 | matchLabels: 109 | app.kubernetes.io/component: operator 110 | app.kubernetes.io/name: kube-cockroachdb 111 | template: 112 | metadata: 113 | labels: 114 | app.kubernetes.io/component: operator 115 | app.kubernetes.io/name: kube-cockroachdb 116 | spec: 117 | containers: 118 | - args: 119 | - --jsonnet.main=main.jsonnet 120 | - --trigger.config=config.yaml 121 | image: quay.io/metalmatze/kube-cockroachdb 122 | imagePullPolicy: Always 123 | name: kube-cockroachdb 124 | serviceAccountName: kube-cockroachdb 125 | - apiVersion: v1 126 | kind: ServiceAccount 127 | metadata: 128 | labels: 129 | app.kubernetes.io/component: operator 130 | app.kubernetes.io/name: kube-cockroachdb 131 | name: kube-cockroachdb 132 | namespace: kube-cockroachdb 133 | kind: List 134 | -------------------------------------------------------------------------------- /operator/examples/basic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metalmatze.de/v1alpha1 2 | kind: CockroachDB 3 | metadata: 4 | name: basic 5 | namespace: default 6 | labels: 7 | app.kubernetes.io/component: database 8 | app.kubernetes.io/instance: basic 9 | spec: 10 | replicas: 3 11 | image: cockroachdb/cockroach:v20.1.5 12 | -------------------------------------------------------------------------------- /operator/examples/groovelink.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metalmatze.de/v1alpha1 2 | kind: CockroachDB 3 | metadata: 4 | name: groovelink 5 | namespace: groovelink-dev 6 | labels: 7 | app.kubernetes.io/component: database 8 | spec: 9 | replicas: 3 10 | image: cockroachdb/cockroach:v20.1.0 11 | resources: 12 | requests: 13 | cpu: '500m' 14 | memory: 1Gi 15 | limits: 16 | cpu: '2' 17 | memory: 4Gi 18 | --- 19 | kind: HorizontalPodAutoscaler 20 | apiVersion: autoscaling/v2beta2 21 | metadata: 22 | name: cockroachdb-groovelink 23 | namespace: groovelink-dev 24 | spec: 25 | scaleTargetRef: 26 | apiVersion: metalmatze.de/v1alpha1 27 | kind: CockroachDB 28 | name: groovelink 29 | minReplicas: 3 30 | maxReplicas: 9 31 | metrics: 32 | - type: Pods 33 | pods: 34 | metric: 35 | name: cockroachdb_sql_select_count 36 | selector: 37 | matchLabels: 38 | service: cockroachdb-groovelink 39 | target: 40 | type: Value 41 | averageValue: 15 42 | --- 43 | kind: HorizontalPodAutoscaler 44 | apiVersion: autoscaling/v2beta2 45 | metadata: 46 | name: groovelink-frontend 47 | namespace: groovelink-dev 48 | spec: 49 | scaleTargetRef: 50 | apiVersion: apps/v1 51 | kind: Deployment 52 | name: groovelink-frontend 53 | minReplicas: 1 54 | maxReplicas: 15 55 | metrics: 56 | - type: Pods 57 | pods: 58 | metric: 59 | name: groovelink_http_requests 60 | selector: 61 | matchLabels: 62 | service: groovelink-frontend 63 | target: 64 | type: AverageValue 65 | averageValue: 10 66 | -------------------------------------------------------------------------------- /operator/examples/storage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: metalmatze.de/v1alpha1 2 | kind: CockroachDB 3 | metadata: 4 | name: basic 5 | namespace: default 6 | labels: 7 | app.kubernetes.io/component: database 8 | app.kubernetes.io/instance: basic 9 | spec: 10 | replicas: 3 11 | image: cockroachdb/cockroach:v20.1.5 12 | storage: 13 | volumeClaimTemplate: 14 | apiVersion: v1 15 | kind: PersistentVolumeClaim 16 | spec: 17 | accessModes: 18 | - ReadWriteOnce 19 | resources: 20 | requests: 21 | storage: 5Gi 22 | storageClassName: standard 23 | -------------------------------------------------------------------------------- /operator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | stdlog "log" 7 | "net/http" 8 | "os" 9 | "strings" 10 | 11 | "github.com/brancz/locutus/client" 12 | "github.com/brancz/locutus/config" 13 | "github.com/brancz/locutus/render/jsonnet" 14 | "github.com/brancz/locutus/rollout" 15 | "github.com/brancz/locutus/rollout/checks" 16 | "github.com/brancz/locutus/trigger/resource" 17 | "github.com/go-kit/kit/log" 18 | "github.com/go-kit/kit/log/level" 19 | "github.com/metalmatze/signal/healthcheck" 20 | "github.com/metalmatze/signal/internalserver" 21 | "github.com/oklog/run" 22 | "github.com/prometheus/client_golang/prometheus" 23 | "k8s.io/client-go/kubernetes" 24 | "k8s.io/client-go/tools/clientcmd" 25 | 26 | "github.com/metalmatze/kube-cockroachdb/operator/actions" 27 | ) 28 | 29 | func main() { 30 | var ( 31 | kubeconfigPath string 32 | jsonnetPath string 33 | renderConfigPath string 34 | triggerConfigPath string 35 | loggerLevel string 36 | ) 37 | flag.StringVar(&kubeconfigPath, "kubeconfig", "", "Path to kubeconfig") 38 | flag.StringVar(&jsonnetPath, "jsonnet.main", "", "Path to the main jsonnet file to render") 39 | flag.StringVar(&renderConfigPath, "render.config", "", "Path to the render configuration") 40 | flag.StringVar(&triggerConfigPath, "trigger.config", "", "Path to the trigger configuration") 41 | flag.StringVar(&loggerLevel, "log.level", "info", "Change the verbosity of the logger (debug,info,warn,error)") 42 | flag.Parse() 43 | 44 | ctx := context.Background() 45 | reg := prometheus.NewRegistry() 46 | healthchecks := healthcheck.NewMetricsHandler(healthcheck.NewHandler(), reg) 47 | 48 | logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) 49 | logger = log.WithPrefix(logger, "ts", log.DefaultTimestampUTC) 50 | logger = log.WithPrefix(logger, "caller", log.DefaultCaller) 51 | 52 | switch strings.ToLower(loggerLevel) { 53 | case "debug": 54 | logger = level.NewFilter(logger, level.AllowDebug()) 55 | case "info": 56 | logger = level.NewFilter(logger, level.AllowInfo()) 57 | case "warn": 58 | logger = level.NewFilter(logger, level.AllowWarn()) 59 | case "error": 60 | logger = level.NewFilter(logger, level.AllowError()) 61 | } 62 | 63 | level.Info(logger).Log("msg", "initializing CockroachDB Operator") 64 | 65 | var gr run.Group 66 | { 67 | konfig, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) 68 | if err != nil { 69 | stdlog.Fatalf("error building kubeconfig: %v", err) 70 | } 71 | 72 | klient, err := kubernetes.NewForConfig(konfig) 73 | if err != nil { 74 | stdlog.Fatalf("error building kubernetes clientset: %v", err) 75 | } 76 | 77 | cl := client.NewClient(konfig, klient) 78 | cl.SetUpdatePreparations([]client.UpdatePreparation{ 79 | client.UpdatePreparationFunc(client.PrepareServiceForUpdate), 80 | //client.UpdatePreparationFunc(client.PrepareStatefulsetForUpdate), 81 | }) 82 | 83 | renderer, err := jsonnet.NewRenderer( 84 | logger, 85 | "main.jsonnet", 86 | map[string]func(context.Context) ([]byte, error){}, 87 | []string{}, 88 | []string{jsonnetPath}, 89 | ) 90 | if err != nil { 91 | stdlog.Fatalf("error creating jsonnet renderer: %v", err) 92 | } 93 | 94 | c, err := checks.NewChecks(logger, cl, nil, nil) 95 | 96 | runner := rollout.NewRunner(reg, log.With(logger, "component", "rollout-runner"), cl, renderer, c, false) 97 | runner.SetObjectActions([]rollout.ObjectAction{ 98 | &rollout.CreateOrUpdateObjectAction{}, 99 | &rollout.CreateIfNotExistObjectAction{}, 100 | &actions.InitializeCockroachDBAction{Konfig: konfig, Klient: klient, Logger: logger}, 101 | &actions.DecommissionNodeAction{Konfig: konfig, Klient: klient, Logger: logger}, 102 | &actions.RecommissionNodeAction{Konfig: konfig, Klient: klient, Logger: logger}, 103 | }) 104 | 105 | trigger, err := resource.NewTrigger(ctx, logger, cl, triggerConfigPath, true) 106 | if err != nil { 107 | stdlog.Fatalf("error creating resource trigger: %v", err) 108 | } 109 | trigger.Register(config.NewFileConfigPasser(renderConfigPath, runner)) 110 | 111 | ctx, shutdown := context.WithCancel(context.Background()) 112 | gr.Add(func() error { 113 | level.Info(logger).Log("msg", "running CockroachDB Operator") 114 | return trigger.Run(ctx) 115 | }, func(err error) { 116 | shutdown() 117 | }) 118 | } 119 | { 120 | h := internalserver.NewHandler( 121 | internalserver.WithName("CockroachDB Operator"), 122 | internalserver.WithPrometheusRegistry(reg), 123 | internalserver.WithHealthchecks(healthchecks), 124 | internalserver.WithPProf(), 125 | ) 126 | 127 | s := http.Server{Addr: ":8081", Handler: h} 128 | 129 | gr.Add(func() error { 130 | level.Info(logger).Log("msg", "running internal server", "addr", s.Addr) 131 | return s.ListenAndServe() 132 | }, func(err error) { 133 | _ = s.Shutdown(context.Background()) 134 | }) 135 | } 136 | 137 | if err := gr.Run(); err != nil { 138 | stdlog.Fatalf("failed to run controller: %v", err) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /operator/main.jsonnet: -------------------------------------------------------------------------------- 1 | local kubernetes = import '../kubernetes.libsonnet'; 2 | local config = import 'generic-operator/config'; 3 | 4 | { 5 | objects: std.mapWithKey( 6 | // inject owner references into all Kubernetes objects 7 | function(k, v) v { 8 | metadata+: { 9 | ownerReferences: [{ 10 | apiVersion: config.apiVersion, 11 | blockOwnerdeletion: true, 12 | controller: true, 13 | kind: config.kind, 14 | name: config.metadata.name, 15 | uid: config.metadata.uid, 16 | }], 17 | }, 18 | }, 19 | // Generate kubernetes objects in kubernetes function for give params 20 | kubernetes({ 21 | name: config.metadata.name, 22 | metadata+: { 23 | namespace: config.metadata.namespace, 24 | }, 25 | image: config.spec.image, 26 | replicas: config.spec.replicas, 27 | resources: if std.objectHas(config.spec, 'resources') then config.spec.resources else {}, 28 | storage: if std.objectHas(config.spec, 'storage') then config.spec.storage else {}, 29 | }) 30 | ), 31 | rollout: { 32 | apiVersion: 'workflow.kubernetes.io/v1alpha1', 33 | kind: 'Rollout', 34 | metadata: { 35 | name: 'jsonnet', 36 | }, 37 | spec: { 38 | groups: [ 39 | { 40 | name: 'Rollout CockroachDB', 41 | steps: [ 42 | { 43 | action: 'DecommissionNode', 44 | object: 'statefulSet', 45 | }, 46 | { 47 | action: 'CreateOrUpdate', 48 | object: 'statefulSet', 49 | success: [ 50 | { 51 | fieldComparisons: { 52 | timeout: '10s', 53 | progressTimeout: '1m', 54 | pollInterval: '15s', 55 | expectedValues: [ 56 | { 57 | name: 'Generation correct', 58 | path: '{.metadata.generation}', 59 | value: { 60 | path: '{.status.observedGeneration}', 61 | }, 62 | }, 63 | { 64 | name: 'All replicas updated', 65 | path: '{.status.replicas}', 66 | value: { 67 | path: '{.status.updatedReplicas}', 68 | }, 69 | }, 70 | { 71 | name: 'No replica unavailable', 72 | path: '{.status.unavailableReplicas}', 73 | default: 0, 74 | value: { 75 | static: 0, 76 | }, 77 | }, 78 | ], 79 | }, 80 | }, 81 | ], 82 | }, 83 | { 84 | action: 'CreateOrUpdate', 85 | object: 'service', 86 | }, 87 | { 88 | action: 'CreateOrUpdate', 89 | object: 'servicePublic', 90 | }, 91 | { 92 | action: 'CreateOrUpdate', 93 | object: 'serviceMonitor', 94 | }, 95 | { 96 | action: 'InitializeIfNot', 97 | object: 'statefulSet', 98 | }, 99 | { 100 | action: 'RecommissionNode', 101 | object: 'statefulSet', 102 | }, 103 | ], 104 | }, 105 | ], 106 | }, 107 | }, 108 | } 109 | -------------------------------------------------------------------------------- /operator/metalmatze.de_cockroachdbs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.8.0 7 | creationTimestamp: null 8 | name: cockroachdbs.metalmatze.de 9 | spec: 10 | group: metalmatze.de 11 | names: 12 | kind: CockroachDB 13 | listKind: CockroachDBList 14 | plural: cockroachdbs 15 | singular: cockroachdb 16 | scope: Namespaced 17 | versions: 18 | - additionalPrinterColumns: 19 | - jsonPath: .spec.replicas 20 | name: Replicas 21 | type: integer 22 | - jsonPath: .metadata.creationTimestamp 23 | name: Age 24 | type: date 25 | name: v1alpha1 26 | schema: 27 | openAPIV3Schema: 28 | description: CockroachDB is the Schema for the a CockroachDB instance managed 29 | by the Operator. 30 | properties: 31 | apiVersion: 32 | description: 'APIVersion defines the versioned schema of this representation 33 | of an object. Servers should convert recognized schemas to the latest 34 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 35 | type: string 36 | kind: 37 | description: 'Kind is a string value representing the REST resource this 38 | object represents. Servers may infer this from the endpoint the client 39 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 40 | type: string 41 | metadata: 42 | type: object 43 | spec: 44 | description: CockroachDBSpec defines the desired state of a CockroachDB 45 | instance. 46 | properties: 47 | image: 48 | type: string 49 | replicas: 50 | description: Define the number of replicas to run for the CockroachDB 51 | cluster. 52 | format: int32 53 | type: integer 54 | resources: 55 | description: Define resources requests and limits for single Pods. 56 | properties: 57 | limits: 58 | additionalProperties: 59 | anyOf: 60 | - type: integer 61 | - type: string 62 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 63 | x-kubernetes-int-or-string: true 64 | description: 'Limits describes the maximum amount of compute resources 65 | allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' 66 | type: object 67 | requests: 68 | additionalProperties: 69 | anyOf: 70 | - type: integer 71 | - type: string 72 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 73 | x-kubernetes-int-or-string: true 74 | description: 'Requests describes the minimum amount of compute 75 | resources required. If Requests is omitted for a container, 76 | it defaults to Limits if that is explicitly specified, otherwise 77 | to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' 78 | type: object 79 | type: object 80 | storage: 81 | description: Storage spec to specify how storage shall be used. 82 | properties: 83 | emptyDir: 84 | description: 'EmptyDirVolumeSource to be used by the CockroachDB 85 | StatefulSets. If specified, used in place of any volumeClaimTemplate. 86 | More info: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir' 87 | properties: 88 | medium: 89 | description: 'What type of storage medium should back this 90 | directory. The default is "" which means to use the node''s 91 | default medium. Must be an empty string (default) or Memory. 92 | More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' 93 | type: string 94 | sizeLimit: 95 | anyOf: 96 | - type: integer 97 | - type: string 98 | description: 'Total amount of local storage required for this 99 | EmptyDir volume. The size limit is also applicable for memory 100 | medium. The maximum usage on memory medium EmptyDir would 101 | be the minimum value between the SizeLimit specified here 102 | and the sum of memory limits of all containers in a pod. 103 | The default is nil which means that the limit is undefined. 104 | More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' 105 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 106 | x-kubernetes-int-or-string: true 107 | type: object 108 | volumeClaimTemplate: 109 | description: A PVC spec to be used by the CockroachDB StatefulSets. 110 | properties: 111 | apiVersion: 112 | description: 'APIVersion defines the versioned schema of this 113 | representation of an object. Servers should convert recognized 114 | schemas to the latest internal value, and may reject unrecognized 115 | values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 116 | type: string 117 | kind: 118 | description: 'Kind is a string value representing the REST 119 | resource this object represents. Servers may infer this 120 | from the endpoint the client submits requests to. Cannot 121 | be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 122 | type: string 123 | metadata: 124 | description: EmbeddedMetadata contains metadata relevant to 125 | an EmbeddedResource. 126 | properties: 127 | annotations: 128 | additionalProperties: 129 | type: string 130 | description: 'Annotations is an unstructured key value 131 | map stored with a resource that may be set by external 132 | tools to store and retrieve arbitrary metadata. They 133 | are not queryable and should be preserved when modifying 134 | objects. More info: http://kubernetes.io/docs/user-guide/annotations' 135 | type: object 136 | labels: 137 | additionalProperties: 138 | type: string 139 | description: 'Map of string keys and values that can be 140 | used to organize and categorize (scope and select) objects. 141 | May match selectors of replication controllers and services. 142 | More info: http://kubernetes.io/docs/user-guide/labels' 143 | type: object 144 | name: 145 | description: 'Name must be unique within a namespace. 146 | Is required when creating resources, although some resources 147 | may allow a client to request the generation of an appropriate 148 | name automatically. Name is primarily intended for creation 149 | idempotence and configuration definition. Cannot be 150 | updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names' 151 | type: string 152 | type: object 153 | spec: 154 | description: 'Spec defines the desired characteristics of 155 | a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' 156 | properties: 157 | accessModes: 158 | description: 'AccessModes contains the desired access 159 | modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' 160 | items: 161 | type: string 162 | type: array 163 | dataSource: 164 | description: 'This field can be used to specify either: 165 | * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot 166 | - Beta) * An existing PVC (PersistentVolumeClaim) * 167 | An existing custom resource/object that implements data 168 | population (Alpha) In order to use VolumeSnapshot object 169 | types, the appropriate feature gate must be enabled 170 | (VolumeSnapshotDataSource or AnyVolumeDataSource) If 171 | the provisioner or an external controller can support 172 | the specified data source, it will create a new volume 173 | based on the contents of the specified data source. 174 | If the specified data source is not supported, the volume 175 | will not be created and the failure will be reported 176 | as an event. In the future, we plan to support more 177 | data source types and the behavior of the provisioner 178 | may change.' 179 | properties: 180 | apiGroup: 181 | description: APIGroup is the group for the resource 182 | being referenced. If APIGroup is not specified, 183 | the specified Kind must be in the core API group. 184 | For any other third-party types, APIGroup is required. 185 | type: string 186 | kind: 187 | description: Kind is the type of resource being referenced 188 | type: string 189 | name: 190 | description: Name is the name of resource being referenced 191 | type: string 192 | required: 193 | - kind 194 | - name 195 | type: object 196 | resources: 197 | description: 'Resources represents the minimum resources 198 | the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' 199 | properties: 200 | limits: 201 | additionalProperties: 202 | anyOf: 203 | - type: integer 204 | - type: string 205 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 206 | x-kubernetes-int-or-string: true 207 | description: 'Limits describes the maximum amount 208 | of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' 209 | type: object 210 | requests: 211 | additionalProperties: 212 | anyOf: 213 | - type: integer 214 | - type: string 215 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 216 | x-kubernetes-int-or-string: true 217 | description: 'Requests describes the minimum amount 218 | of compute resources required. If Requests is omitted 219 | for a container, it defaults to Limits if that is 220 | explicitly specified, otherwise to an implementation-defined 221 | value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' 222 | type: object 223 | type: object 224 | selector: 225 | description: A label query over volumes to consider for 226 | binding. 227 | properties: 228 | matchExpressions: 229 | description: matchExpressions is a list of label selector 230 | requirements. The requirements are ANDed. 231 | items: 232 | description: A label selector requirement is a selector 233 | that contains values, a key, and an operator that 234 | relates the key and values. 235 | properties: 236 | key: 237 | description: key is the label key that the selector 238 | applies to. 239 | type: string 240 | operator: 241 | description: operator represents a key's relationship 242 | to a set of values. Valid operators are In, 243 | NotIn, Exists and DoesNotExist. 244 | type: string 245 | values: 246 | description: values is an array of string values. 247 | If the operator is In or NotIn, the values 248 | array must be non-empty. If the operator is 249 | Exists or DoesNotExist, the values array must 250 | be empty. This array is replaced during a 251 | strategic merge patch. 252 | items: 253 | type: string 254 | type: array 255 | required: 256 | - key 257 | - operator 258 | type: object 259 | type: array 260 | matchLabels: 261 | additionalProperties: 262 | type: string 263 | description: matchLabels is a map of {key,value} pairs. 264 | A single {key,value} in the matchLabels map is equivalent 265 | to an element of matchExpressions, whose key field 266 | is "key", the operator is "In", and the values array 267 | contains only "value". The requirements are ANDed. 268 | type: object 269 | type: object 270 | storageClassName: 271 | description: 'Name of the StorageClass required by the 272 | claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' 273 | type: string 274 | volumeMode: 275 | description: volumeMode defines what type of volume is 276 | required by the claim. Value of Filesystem is implied 277 | when not included in claim spec. 278 | type: string 279 | volumeName: 280 | description: VolumeName is the binding reference to the 281 | PersistentVolume backing this claim. 282 | type: string 283 | type: object 284 | status: 285 | description: 'Status represents the current information/status 286 | of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' 287 | properties: 288 | accessModes: 289 | description: 'AccessModes contains the actual access modes 290 | the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' 291 | items: 292 | type: string 293 | type: array 294 | capacity: 295 | additionalProperties: 296 | anyOf: 297 | - type: integer 298 | - type: string 299 | pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ 300 | x-kubernetes-int-or-string: true 301 | description: Represents the actual resources of the underlying 302 | volume. 303 | type: object 304 | conditions: 305 | description: Current Condition of persistent volume claim. 306 | If underlying persistent volume is being resized then 307 | the Condition will be set to 'ResizeStarted'. 308 | items: 309 | description: PersistentVolumeClaimCondition contails 310 | details about state of pvc 311 | properties: 312 | lastProbeTime: 313 | description: Last time we probed the condition. 314 | format: date-time 315 | type: string 316 | lastTransitionTime: 317 | description: Last time the condition transitioned 318 | from one status to another. 319 | format: date-time 320 | type: string 321 | message: 322 | description: Human-readable message indicating details 323 | about last transition. 324 | type: string 325 | reason: 326 | description: Unique, this should be a short, machine 327 | understandable string that gives the reason for 328 | condition's last transition. If it reports "ResizeStarted" 329 | that means the underlying persistent volume is 330 | being resized. 331 | type: string 332 | status: 333 | type: string 334 | type: 335 | description: PersistentVolumeClaimConditionType 336 | is a valid value of PersistentVolumeClaimCondition.Type 337 | type: string 338 | required: 339 | - status 340 | - type 341 | type: object 342 | type: array 343 | phase: 344 | description: Phase represents the current phase of PersistentVolumeClaim. 345 | type: string 346 | type: object 347 | type: object 348 | type: object 349 | required: 350 | - image 351 | type: object 352 | status: 353 | type: object 354 | type: object 355 | served: true 356 | storage: true 357 | subresources: 358 | scale: 359 | labelSelectorPath: .status.selector 360 | specReplicasPath: .spec.replicas 361 | statusReplicasPath: .status.replicas 362 | status: {} 363 | status: 364 | acceptedNames: 365 | kind: "" 366 | plural: "" 367 | conditions: [] 368 | storedVersions: [] 369 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/metalmatze/kube-cockroachdb/9844a476811b71cb97aed00910fc9fefa9eb1fab/screenshot.png --------------------------------------------------------------------------------