├── .dockerignore ├── .gitignore ├── .goreleaser.yml ├── .travis.yml ├── Dockerfile ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── Makefile ├── README.md ├── cmd └── kube-tasks.go └── pkg ├── kubetasks └── kubetasks.go └── utils ├── general.go ├── kube.go └── path.go /.dockerignore: -------------------------------------------------------------------------------- 1 | **/*gitlab* 2 | *.md 3 | *.git 4 | *.yml 5 | vendor/ 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # dep/glide 15 | vendor/ 16 | bin/ 17 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # .goreleaser.yml 2 | builds: 3 | - main: cmd/kube-tasks.go 4 | binary: kube-tasks 5 | goos: 6 | - windows 7 | - darwin 8 | - linux 9 | goarch: 10 | - amd64 11 | env: 12 | - CGO_ENABLED=0 13 | ldflags: 14 | - -s -w -X main.GitTag={{.Tag}} -X main.GitCommit={{.ShortCommit}} 15 | archive: 16 | name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Os }}" 17 | format: tar.gz 18 | wrap_in_directory: true 19 | format_overrides: 20 | - goos: windows 21 | format: zip 22 | replacements: 23 | darwin: macos 24 | dockers: 25 | - binary: kube-tasks 26 | dockerfile: Dockerfile 27 | image_templates: 28 | - "maorfr/{{.ProjectName}}:{{ .Tag }}" 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # .travis.yml 2 | language: go 3 | go: 4 | - 1.11.2 5 | 6 | services: 7 | - docker 8 | 9 | deploy: 10 | - provider: script 11 | skip_cleanup: true 12 | script: curl -sL https://git.io/goreleaser | bash 13 | on: 14 | tags: true 15 | condition: $TRAVIS_OS_NAME = linux -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.8 2 | RUN apk --no-cache add ca-certificates 3 | COPY kube-tasks /usr/local/bin/kube-tasks 4 | RUN addgroup -g 1001 -S kube-tasks \ 5 | && adduser -u 1001 -D -S -G kube-tasks kube-tasks 6 | USER kube-tasks 7 | WORKDIR /home/kube-tasks 8 | CMD ["kube-tasks"] 9 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:8eec992db9a5900142f171337f0e2c5f7cd7c98a8fc375e408f71600d4ef93bd" 6 | name = "cloud.google.com/go" 7 | packages = [ 8 | "compute/metadata", 9 | "iam", 10 | "internal", 11 | "internal/optional", 12 | "internal/trace", 13 | "internal/version", 14 | "storage", 15 | ] 16 | pruneopts = "UT" 17 | revision = "fcb9a2d5f791d07be64506ab54434de65989d370" 18 | version = "v0.37.4" 19 | 20 | [[projects]] 21 | digest = "1:279540310125d2b219920588d7e2edb2a85b3317b528839166e896ce6b6f211c" 22 | name = "github.com/Azure/azure-pipeline-go" 23 | packages = ["pipeline"] 24 | pruneopts = "UT" 25 | revision = "55fedc85a614dcd0e942a66f302ae3efb83d563c" 26 | version = "v0.1.9" 27 | 28 | [[projects]] 29 | digest = "1:c4a5edf3b0f38e709a78dcc945997678a364c2b5adfd48842a3dd349c352f833" 30 | name = "github.com/Azure/azure-storage-blob-go" 31 | packages = ["azblob"] 32 | pruneopts = "UT" 33 | revision = "5152f14ace1c6db66bd9cb57840703a8358fa7bc" 34 | version = "0.3.0" 35 | 36 | [[projects]] 37 | digest = "1:4cbfa54eb7ec3d4d33926c2ae3be713215e29c43b499dba386750ea933af2a55" 38 | name = "github.com/aws/aws-sdk-go" 39 | packages = [ 40 | "aws", 41 | "aws/awserr", 42 | "aws/awsutil", 43 | "aws/client", 44 | "aws/client/metadata", 45 | "aws/corehandlers", 46 | "aws/credentials", 47 | "aws/credentials/ec2rolecreds", 48 | "aws/credentials/endpointcreds", 49 | "aws/credentials/processcreds", 50 | "aws/credentials/stscreds", 51 | "aws/csm", 52 | "aws/defaults", 53 | "aws/ec2metadata", 54 | "aws/endpoints", 55 | "aws/request", 56 | "aws/session", 57 | "aws/signer/v4", 58 | "internal/ini", 59 | "internal/s3err", 60 | "internal/sdkio", 61 | "internal/sdkmath", 62 | "internal/sdkrand", 63 | "internal/sdkuri", 64 | "internal/shareddefaults", 65 | "private/protocol", 66 | "private/protocol/eventstream", 67 | "private/protocol/eventstream/eventstreamapi", 68 | "private/protocol/json/jsonutil", 69 | "private/protocol/query", 70 | "private/protocol/query/queryutil", 71 | "private/protocol/rest", 72 | "private/protocol/restxml", 73 | "private/protocol/xml/xmlutil", 74 | "service/s3", 75 | "service/s3/s3iface", 76 | "service/s3/s3manager", 77 | "service/sts", 78 | "service/sts/stsiface", 79 | ] 80 | pruneopts = "UT" 81 | revision = "d57c8d96f72d9475194ccf18d2ba70ac294b0cb3" 82 | version = "v1.23.13" 83 | 84 | [[projects]] 85 | digest = "1:49ea765e340f3907fc52ab1cc23bf9698d2d4fa9db14673710879d2ae2d22a9a" 86 | name = "github.com/djherbis/buffer" 87 | packages = [ 88 | ".", 89 | "limio", 90 | "wrapio", 91 | ] 92 | pruneopts = "UT" 93 | revision = "4972e2bf4a27dbd7c43140c959b85e8c1d2f5536" 94 | version = "v1.0" 95 | 96 | [[projects]] 97 | branch = "master" 98 | digest = "1:ecdc8e0fe3bc7d549af1c9c36acf3820523b707d6c071b6d0c3860882c6f7b42" 99 | name = "github.com/docker/spdystream" 100 | packages = [ 101 | ".", 102 | "spdy", 103 | ] 104 | pruneopts = "UT" 105 | revision = "6480d4af844c189cf5dd913db24ddd339d3a4f85" 106 | 107 | [[projects]] 108 | digest = "1:4d02824a56d268f74a6b6fdd944b20b58a77c3d70e81008b3ee0c4f1a6777340" 109 | name = "github.com/gogo/protobuf" 110 | packages = [ 111 | "proto", 112 | "sortkeys", 113 | ] 114 | pruneopts = "UT" 115 | revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c" 116 | version = "v1.2.1" 117 | 118 | [[projects]] 119 | digest = "1:7ade0e7351787347d3dd3456abb365fd71ea75c19a859505e6a427064e1f9e42" 120 | name = "github.com/golang/protobuf" 121 | packages = [ 122 | "proto", 123 | "protoc-gen-go/descriptor", 124 | "ptypes", 125 | "ptypes/any", 126 | "ptypes/duration", 127 | "ptypes/timestamp", 128 | ] 129 | pruneopts = "UT" 130 | revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" 131 | version = "v1.3.2" 132 | 133 | [[projects]] 134 | digest = "1:0bfbe13936953a98ae3cfe8ed6670d396ad81edf069a806d2f6515d7bb6950df" 135 | name = "github.com/google/btree" 136 | packages = ["."] 137 | pruneopts = "UT" 138 | revision = "4030bb1f1f0c35b30ca7009e9ebd06849dd45306" 139 | version = "v1.0.0" 140 | 141 | [[projects]] 142 | digest = "1:a6181aca1fd5e27103f9a920876f29ac72854df7345a39f3b01e61c8c94cc8af" 143 | name = "github.com/google/gofuzz" 144 | packages = ["."] 145 | pruneopts = "UT" 146 | revision = "f140a6486e521aad38f5917de355cbf147cc0496" 147 | version = "v1.0.0" 148 | 149 | [[projects]] 150 | digest = "1:766102087520f9d54f2acc72bd6637045900ac735b4a419b128d216f0c5c4876" 151 | name = "github.com/googleapis/gax-go" 152 | packages = ["v2"] 153 | pruneopts = "UT" 154 | revision = "bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2" 155 | version = "v2.0.5" 156 | 157 | [[projects]] 158 | digest = "1:ca4524b4855ded427c7003ec903a5c854f37e7b1e8e2a93277243462c5b753a8" 159 | name = "github.com/googleapis/gnostic" 160 | packages = [ 161 | "OpenAPIv2", 162 | "compiler", 163 | "extensions", 164 | ] 165 | pruneopts = "UT" 166 | revision = "ab0dd09aa10e2952b28e12ecd35681b20463ebab" 167 | version = "v0.3.1" 168 | 169 | [[projects]] 170 | branch = "master" 171 | digest = "1:5fc0e23b254a1bd7d8d2d42fa093ba33471d08f52fe04afd3713adabb5888dc3" 172 | name = "github.com/gregjones/httpcache" 173 | packages = [ 174 | ".", 175 | "diskcache", 176 | ] 177 | pruneopts = "UT" 178 | revision = "901d90724c7919163f472a9812253fb26761123d" 179 | 180 | [[projects]] 181 | digest = "1:7fae9ec96d10b2afce0da23c378c8b3389319b7f92fa092f2621bba3078cfb4b" 182 | name = "github.com/hashicorp/golang-lru" 183 | packages = ["simplelru"] 184 | pruneopts = "UT" 185 | revision = "7f827b33c0f158ec5dfbba01bb0b14a4541fd81d" 186 | version = "v0.5.3" 187 | 188 | [[projects]] 189 | digest = "1:a0cefd27d12712af4b5018dc7046f245e1e3b5760e2e848c30b171b570708f9b" 190 | name = "github.com/imdario/mergo" 191 | packages = ["."] 192 | pruneopts = "UT" 193 | revision = "7c29201646fa3de8506f701213473dd407f19646" 194 | version = "v0.3.7" 195 | 196 | [[projects]] 197 | digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" 198 | name = "github.com/inconshreveable/mousetrap" 199 | packages = ["."] 200 | pruneopts = "UT" 201 | revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" 202 | version = "v1.0" 203 | 204 | [[projects]] 205 | digest = "1:bb81097a5b62634f3e9fec1014657855610c82d19b9a40c17612e32651e35dca" 206 | name = "github.com/jmespath/go-jmespath" 207 | packages = ["."] 208 | pruneopts = "UT" 209 | revision = "c2b33e84" 210 | 211 | [[projects]] 212 | digest = "1:709cd2a2c29cc9b89732f6c24846bbb9d6270f28ef5ef2128cc73bd0d6d7bff9" 213 | name = "github.com/json-iterator/go" 214 | packages = ["."] 215 | pruneopts = "UT" 216 | revision = "27518f6661eba504be5a7a9a9f6d9460d892ade3" 217 | version = "v1.1.7" 218 | 219 | [[projects]] 220 | digest = "1:48addb4ab030c6885899bb73b20a86dad347a58e6fc74f20bdafab1efb26a36a" 221 | name = "github.com/maorfr/skbn" 222 | packages = [ 223 | "pkg/skbn", 224 | "pkg/utils", 225 | ] 226 | pruneopts = "UT" 227 | revision = "3d8a7af7e6207a77c0532cf8c9128ef8ab1a2597" 228 | version = "0.5.0" 229 | 230 | [[projects]] 231 | digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" 232 | name = "github.com/modern-go/concurrent" 233 | packages = ["."] 234 | pruneopts = "UT" 235 | revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" 236 | version = "1.0.3" 237 | 238 | [[projects]] 239 | digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" 240 | name = "github.com/modern-go/reflect2" 241 | packages = ["."] 242 | pruneopts = "UT" 243 | revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" 244 | version = "1.0.1" 245 | 246 | [[projects]] 247 | branch = "master" 248 | digest = "1:89da0f0574bc94cfd0ac8b59af67bf76cdd110d503df2721006b9f0492394333" 249 | name = "github.com/petar/GoLLRB" 250 | packages = ["llrb"] 251 | pruneopts = "UT" 252 | revision = "33fb24c13b99c46c93183c291836c573ac382536" 253 | 254 | [[projects]] 255 | digest = "1:a8c2725121694dfbf6d552fb86fe6b46e3e7135ea05db580c28695b916162aad" 256 | name = "github.com/peterbourgon/diskv" 257 | packages = ["."] 258 | pruneopts = "UT" 259 | revision = "0be1b92a6df0e4f5cb0a5d15fb7f643d0ad93ce6" 260 | version = "v3.0.0" 261 | 262 | [[projects]] 263 | digest = "1:e096613fb7cf34743d49af87d197663cfccd61876e2219853005a57baedfa562" 264 | name = "github.com/spf13/cobra" 265 | packages = ["."] 266 | pruneopts = "UT" 267 | revision = "f2b07da1e2c38d5f12845a4f607e2e1018cbb1f5" 268 | version = "v0.0.5" 269 | 270 | [[projects]] 271 | digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" 272 | name = "github.com/spf13/pflag" 273 | packages = ["."] 274 | pruneopts = "UT" 275 | revision = "298182f68c66c05229eb03ac171abe6e309ee79a" 276 | version = "v1.0.3" 277 | 278 | [[projects]] 279 | digest = "1:74055050ea547bb04600be79cc501965cb3de8988018262f2ca430f0a0b48ec3" 280 | name = "go.opencensus.io" 281 | packages = [ 282 | ".", 283 | "internal", 284 | "internal/tagencoding", 285 | "metric/metricdata", 286 | "metric/metricproducer", 287 | "plugin/ochttp", 288 | "plugin/ochttp/propagation/b3", 289 | "resource", 290 | "stats", 291 | "stats/internal", 292 | "stats/view", 293 | "tag", 294 | "trace", 295 | "trace/internal", 296 | "trace/propagation", 297 | "trace/tracestate", 298 | ] 299 | pruneopts = "UT" 300 | revision = "9c377598961b706d1542bd2d84d538b5094d596e" 301 | version = "v0.22.0" 302 | 303 | [[projects]] 304 | branch = "master" 305 | digest = "1:bbe51412d9915d64ffaa96b51d409e070665efc5194fcf145c4a27d4133107a4" 306 | name = "golang.org/x/crypto" 307 | packages = ["ssh/terminal"] 308 | pruneopts = "UT" 309 | revision = "9756ffdc24725223350eb3266ffb92590d28f278" 310 | 311 | [[projects]] 312 | branch = "master" 313 | digest = "1:e93fe09ca93cf16f8b2dc48053f56c2f91ed4f3fd16bfaf9596b6548c7b48a7f" 314 | name = "golang.org/x/net" 315 | packages = [ 316 | "context", 317 | "context/ctxhttp", 318 | "http/httpguts", 319 | "http2", 320 | "http2/hpack", 321 | "idna", 322 | "internal/timeseries", 323 | "trace", 324 | ] 325 | pruneopts = "UT" 326 | revision = "ba9fcec4b297b415637633c5a6e8fa592e4a16c3" 327 | 328 | [[projects]] 329 | branch = "master" 330 | digest = "1:31e33f76456ccf54819ab4a646cf01271d1a99d7712ab84bf1a9e7b61cd2031b" 331 | name = "golang.org/x/oauth2" 332 | packages = [ 333 | ".", 334 | "google", 335 | "internal", 336 | "jws", 337 | "jwt", 338 | ] 339 | pruneopts = "UT" 340 | revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33" 341 | 342 | [[projects]] 343 | branch = "master" 344 | digest = "1:db4d094dcdda93745779828d4f7536085eae66f9ebcba842bda762883db08800" 345 | name = "golang.org/x/sys" 346 | packages = [ 347 | "unix", 348 | "windows", 349 | ] 350 | pruneopts = "UT" 351 | revision = "1e83adbbebd0f5dc971915fd7e5db032c3d2b731" 352 | 353 | [[projects]] 354 | digest = "1:8d8faad6b12a3a4c819a3f9618cb6ee1fa1cfc33253abeeea8b55336721e3405" 355 | name = "golang.org/x/text" 356 | packages = [ 357 | "collate", 358 | "collate/build", 359 | "internal/colltab", 360 | "internal/gen", 361 | "internal/language", 362 | "internal/language/compact", 363 | "internal/tag", 364 | "internal/triegen", 365 | "internal/ucd", 366 | "language", 367 | "secure/bidirule", 368 | "transform", 369 | "unicode/bidi", 370 | "unicode/cldr", 371 | "unicode/norm", 372 | "unicode/rangetable", 373 | ] 374 | pruneopts = "UT" 375 | revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" 376 | version = "v0.3.2" 377 | 378 | [[projects]] 379 | branch = "master" 380 | digest = "1:9fdc2b55e8e0fafe4b41884091e51e77344f7dc511c5acedcfd98200003bff90" 381 | name = "golang.org/x/time" 382 | packages = ["rate"] 383 | pruneopts = "UT" 384 | revision = "9d24e82272b4f38b78bc8cff74fa936d31ccd8ef" 385 | 386 | [[projects]] 387 | digest = "1:a9536711cdfc5fbe1071ccdbb767318cd5e61724d3f7fd2d0af03c608a7db4ae" 388 | name = "google.golang.org/api" 389 | packages = [ 390 | "gensupport", 391 | "googleapi", 392 | "googleapi/internal/uritemplates", 393 | "googleapi/transport", 394 | "internal", 395 | "iterator", 396 | "option", 397 | "storage/v1", 398 | "transport/http", 399 | "transport/http/internal/propagation", 400 | ] 401 | pruneopts = "UT" 402 | revision = "feb0267beb8644f5088a03be4d5ec3f8c7020152" 403 | version = "v0.9.0" 404 | 405 | [[projects]] 406 | digest = "1:498b722d33dde4471e7d6e5d88a5e7132d2a8306fea5ff5ee82d1f418b4f41ed" 407 | name = "google.golang.org/appengine" 408 | packages = [ 409 | ".", 410 | "internal", 411 | "internal/app_identity", 412 | "internal/base", 413 | "internal/datastore", 414 | "internal/log", 415 | "internal/modules", 416 | "internal/remote_api", 417 | "internal/urlfetch", 418 | "urlfetch", 419 | ] 420 | pruneopts = "UT" 421 | revision = "5f2a59506353b8d5ba8cbbcd9f3c1f41f1eaf079" 422 | version = "v1.6.2" 423 | 424 | [[projects]] 425 | branch = "master" 426 | digest = "1:fab9710d9a0a1e9fe6ee772785abb950bb00b3c31a97c3266fb8e58e4786e51b" 427 | name = "google.golang.org/genproto" 428 | packages = [ 429 | "googleapis/api/annotations", 430 | "googleapis/iam/v1", 431 | "googleapis/rpc/code", 432 | "googleapis/rpc/status", 433 | "googleapis/type/expr", 434 | ] 435 | pruneopts = "UT" 436 | revision = "24fa4b261c55da65468f2abfdae2b024eef27dfb" 437 | 438 | [[projects]] 439 | digest = "1:3b97661db2e5d4c87f7345e875ea28f911e54c715ba0a74be08e1649d67e05cd" 440 | name = "google.golang.org/grpc" 441 | packages = [ 442 | ".", 443 | "balancer", 444 | "balancer/base", 445 | "balancer/roundrobin", 446 | "binarylog/grpc_binarylog_v1", 447 | "codes", 448 | "connectivity", 449 | "credentials", 450 | "credentials/internal", 451 | "encoding", 452 | "encoding/proto", 453 | "grpclog", 454 | "internal", 455 | "internal/backoff", 456 | "internal/balancerload", 457 | "internal/binarylog", 458 | "internal/channelz", 459 | "internal/envconfig", 460 | "internal/grpcrand", 461 | "internal/grpcsync", 462 | "internal/syscall", 463 | "internal/transport", 464 | "keepalive", 465 | "metadata", 466 | "naming", 467 | "peer", 468 | "resolver", 469 | "resolver/dns", 470 | "resolver/passthrough", 471 | "serviceconfig", 472 | "stats", 473 | "status", 474 | "tap", 475 | ] 476 | pruneopts = "UT" 477 | revision = "6eaf6f47437a6b4e2153a190160ef39a92c7eceb" 478 | version = "v1.23.0" 479 | 480 | [[projects]] 481 | digest = "1:5aebed51f26a49cb77a0c34d1328a0d29839e7a1204b14ff8e26a3df8ec61736" 482 | name = "gopkg.in/djherbis/nio.v2" 483 | packages = ["."] 484 | pruneopts = "UT" 485 | revision = "824ca9017eeb2a422fdda7ae9dba05d3ab019dfa" 486 | version = "v2.0.3" 487 | 488 | [[projects]] 489 | digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" 490 | name = "gopkg.in/inf.v0" 491 | packages = ["."] 492 | pruneopts = "UT" 493 | revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf" 494 | version = "v0.9.1" 495 | 496 | [[projects]] 497 | digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" 498 | name = "gopkg.in/yaml.v2" 499 | packages = ["."] 500 | pruneopts = "UT" 501 | revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" 502 | version = "v2.2.2" 503 | 504 | [[projects]] 505 | digest = "1:0d299a04c6472e4458461d7034c76d014cc6f632a3262cbf21d123b19ce13e65" 506 | name = "k8s.io/api" 507 | packages = [ 508 | "admissionregistration/v1alpha1", 509 | "admissionregistration/v1beta1", 510 | "apps/v1", 511 | "apps/v1beta1", 512 | "apps/v1beta2", 513 | "auditregistration/v1alpha1", 514 | "authentication/v1", 515 | "authentication/v1beta1", 516 | "authorization/v1", 517 | "authorization/v1beta1", 518 | "autoscaling/v1", 519 | "autoscaling/v2beta1", 520 | "autoscaling/v2beta2", 521 | "batch/v1", 522 | "batch/v1beta1", 523 | "batch/v2alpha1", 524 | "certificates/v1beta1", 525 | "coordination/v1beta1", 526 | "core/v1", 527 | "events/v1beta1", 528 | "extensions/v1beta1", 529 | "networking/v1", 530 | "policy/v1beta1", 531 | "rbac/v1", 532 | "rbac/v1alpha1", 533 | "rbac/v1beta1", 534 | "scheduling/v1alpha1", 535 | "scheduling/v1beta1", 536 | "settings/v1alpha1", 537 | "storage/v1", 538 | "storage/v1alpha1", 539 | "storage/v1beta1", 540 | ] 541 | pruneopts = "UT" 542 | revision = "89a74a8d264df0e993299876a8cde88379b940ee" 543 | version = "kubernetes-1.13.0" 544 | 545 | [[projects]] 546 | branch = "release-1.13" 547 | digest = "1:93b99119e553ad1e154713e3b35f8c2148fe2988e26569c26150109d7ff34ae0" 548 | name = "k8s.io/apimachinery" 549 | packages = [ 550 | "pkg/api/errors", 551 | "pkg/api/meta", 552 | "pkg/api/resource", 553 | "pkg/apis/meta/v1", 554 | "pkg/apis/meta/v1/unstructured", 555 | "pkg/apis/meta/v1beta1", 556 | "pkg/conversion", 557 | "pkg/conversion/queryparams", 558 | "pkg/fields", 559 | "pkg/labels", 560 | "pkg/runtime", 561 | "pkg/runtime/schema", 562 | "pkg/runtime/serializer", 563 | "pkg/runtime/serializer/json", 564 | "pkg/runtime/serializer/protobuf", 565 | "pkg/runtime/serializer/recognizer", 566 | "pkg/runtime/serializer/streaming", 567 | "pkg/runtime/serializer/versioning", 568 | "pkg/selection", 569 | "pkg/types", 570 | "pkg/util/clock", 571 | "pkg/util/errors", 572 | "pkg/util/framer", 573 | "pkg/util/httpstream", 574 | "pkg/util/httpstream/spdy", 575 | "pkg/util/intstr", 576 | "pkg/util/json", 577 | "pkg/util/naming", 578 | "pkg/util/net", 579 | "pkg/util/remotecommand", 580 | "pkg/util/runtime", 581 | "pkg/util/sets", 582 | "pkg/util/validation", 583 | "pkg/util/validation/field", 584 | "pkg/util/yaml", 585 | "pkg/version", 586 | "pkg/watch", 587 | "third_party/forked/golang/netutil", 588 | "third_party/forked/golang/reflect", 589 | ] 590 | pruneopts = "UT" 591 | revision = "bf4de9df677cd119930fa83483bad158956cdfcf" 592 | 593 | [[projects]] 594 | digest = "1:89965ed41772270efa6a201c54382ca28bba606bfb364fb6cd0c1252c57bf1a2" 595 | name = "k8s.io/client-go" 596 | packages = [ 597 | "discovery", 598 | "kubernetes", 599 | "kubernetes/scheme", 600 | "kubernetes/typed/admissionregistration/v1alpha1", 601 | "kubernetes/typed/admissionregistration/v1beta1", 602 | "kubernetes/typed/apps/v1", 603 | "kubernetes/typed/apps/v1beta1", 604 | "kubernetes/typed/apps/v1beta2", 605 | "kubernetes/typed/auditregistration/v1alpha1", 606 | "kubernetes/typed/authentication/v1", 607 | "kubernetes/typed/authentication/v1beta1", 608 | "kubernetes/typed/authorization/v1", 609 | "kubernetes/typed/authorization/v1beta1", 610 | "kubernetes/typed/autoscaling/v1", 611 | "kubernetes/typed/autoscaling/v2beta1", 612 | "kubernetes/typed/autoscaling/v2beta2", 613 | "kubernetes/typed/batch/v1", 614 | "kubernetes/typed/batch/v1beta1", 615 | "kubernetes/typed/batch/v2alpha1", 616 | "kubernetes/typed/certificates/v1beta1", 617 | "kubernetes/typed/coordination/v1beta1", 618 | "kubernetes/typed/core/v1", 619 | "kubernetes/typed/events/v1beta1", 620 | "kubernetes/typed/extensions/v1beta1", 621 | "kubernetes/typed/networking/v1", 622 | "kubernetes/typed/policy/v1beta1", 623 | "kubernetes/typed/rbac/v1", 624 | "kubernetes/typed/rbac/v1alpha1", 625 | "kubernetes/typed/rbac/v1beta1", 626 | "kubernetes/typed/scheduling/v1alpha1", 627 | "kubernetes/typed/scheduling/v1beta1", 628 | "kubernetes/typed/settings/v1alpha1", 629 | "kubernetes/typed/storage/v1", 630 | "kubernetes/typed/storage/v1alpha1", 631 | "kubernetes/typed/storage/v1beta1", 632 | "pkg/apis/clientauthentication", 633 | "pkg/apis/clientauthentication/v1alpha1", 634 | "pkg/apis/clientauthentication/v1beta1", 635 | "pkg/version", 636 | "plugin/pkg/client/auth/exec", 637 | "plugin/pkg/client/auth/gcp", 638 | "rest", 639 | "rest/watch", 640 | "third_party/forked/golang/template", 641 | "tools/auth", 642 | "tools/clientcmd", 643 | "tools/clientcmd/api", 644 | "tools/clientcmd/api/latest", 645 | "tools/clientcmd/api/v1", 646 | "tools/metrics", 647 | "tools/reference", 648 | "tools/remotecommand", 649 | "transport", 650 | "transport/spdy", 651 | "util/cert", 652 | "util/connrotation", 653 | "util/exec", 654 | "util/flowcontrol", 655 | "util/homedir", 656 | "util/integer", 657 | "util/jsonpath", 658 | ] 659 | pruneopts = "UT" 660 | revision = "e64494209f554a6723674bd494d69445fb76a1d4" 661 | version = "v10.0.0" 662 | 663 | [[projects]] 664 | digest = "1:ccb9be4c583b6ec848eb98aa395a4e8c8f8ad9ebb823642c0dd1c1c45939a5bb" 665 | name = "k8s.io/klog" 666 | packages = ["."] 667 | pruneopts = "UT" 668 | revision = "3ca30a56d8a775276f9cdae009ba326fdc05af7f" 669 | version = "v0.4.0" 670 | 671 | [[projects]] 672 | digest = "1:7719608fe0b52a4ece56c2dde37bedd95b938677d1ab0f84b8a7852e4c59f849" 673 | name = "sigs.k8s.io/yaml" 674 | packages = ["."] 675 | pruneopts = "UT" 676 | revision = "fd68e9863619f6ec2fdd8625fe1f02e7c877e480" 677 | version = "v1.1.0" 678 | 679 | [solve-meta] 680 | analyzer-name = "dep" 681 | analyzer-version = 1 682 | input-imports = [ 683 | "github.com/maorfr/skbn/pkg/skbn", 684 | "github.com/spf13/cobra", 685 | "k8s.io/apimachinery/pkg/apis/meta/v1", 686 | ] 687 | solver-name = "gps-cdcl" 688 | solver-version = 1 689 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | [[constraint]] 2 | name = "github.com/maorfr/skbn" 3 | version = "0.5.0" 4 | 5 | [[constraint]] 6 | name = "github.com/spf13/cobra" 7 | version = "0.0.3" 8 | 9 | [[constraint]] 10 | branch = "release-1.13" 11 | name = "k8s.io/apimachinery" 12 | 13 | [prune] 14 | go-tests = true 15 | unused-packages = true 16 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HAS_DEP := $(shell command -v dep;) 2 | DEP_VERSION := v0.5.0 3 | GIT_TAG := $(shell git describe --tags --always) 4 | GIT_COMMIT := $(shell git rev-parse --short HEAD) 5 | LDFLAGS := "-X main.GitTag=${GIT_TAG} -X main.GitCommit=${GIT_COMMIT}" 6 | DIST := $(CURDIR)/dist 7 | DOCKER_USER := $(shell printenv DOCKER_USER) 8 | DOCKER_PASSWORD := $(shell printenv DOCKER_PASSWORD) 9 | TRAVIS := $(shell printenv TRAVIS) 10 | 11 | all: bootstrap build docker push 12 | 13 | fmt: 14 | go fmt ./pkg/... ./cmd/... 15 | 16 | vet: 17 | go vet ./pkg/... ./cmd/... 18 | 19 | # Build kube-tasks binary 20 | build: fmt vet 21 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o bin/kube-tasks cmd/kube-tasks.go 22 | 23 | # Build kube-tasks docker image 24 | docker: fmt vet 25 | cp bin/kube-tasks kube-tasks 26 | docker build -t maorfr/kube-tasks:latest . 27 | rm kube-tasks 28 | 29 | 30 | # Push will only happen in travis ci 31 | push: 32 | ifdef TRAVIS 33 | ifdef DOCKER_USER 34 | ifdef DOCKER_PASSWORD 35 | docker login -u $(DOCKER_USER) -p $(DOCKER_PASSWORD) 36 | docker push maorfr/kube-tasks:latest 37 | endif 38 | endif 39 | endif 40 | 41 | bootstrap: 42 | ifndef HAS_DEP 43 | wget -q -O $(GOPATH)/bin/dep https://github.com/golang/dep/releases/download/$(DEP_VERSION)/dep-linux-amd64 44 | chmod +x $(GOPATH)/bin/dep 45 | endif 46 | dep ensure 47 | 48 | dist: 49 | mkdir -p $(DIST) 50 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o kube-tasks cmd/kube-tasks.go 51 | tar -zcvf $(DIST)/kube-tasks-linux-$(GIT_TAG).tgz kube-tasks 52 | rm kube-tasks 53 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags $(LDFLAGS) -o kube-tasks cmd/kube-tasks.go 54 | tar -zcvf $(DIST)/kube-tasks-macos-$(GIT_TAG).tgz kube-tasks 55 | rm kube-tasks 56 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags $(LDFLAGS) -o kube-tasks.exe cmd/kube-tasks.go 57 | tar -zcvf $(DIST)/kube-tasks-windows-$(GIT_TAG).tgz kube-tasks.exe 58 | rm kube-tasks.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Release](https://img.shields.io/github/release/maorfr/kube-tasks.svg)](https://github.com/maorfr/kube-tasks/releases) 2 | [![Travis branch](https://img.shields.io/travis/maorfr/kube-tasks/master.svg)](https://travis-ci.org/maorfr/kube-tasks) 3 | [![Docker Pulls](https://img.shields.io/docker/pulls/maorfr/kube-tasks.svg)](https://hub.docker.com/r/maorfr/kube-tasks/) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/maorfr/kube-tasks)](https://goreportcard.com/report/github.com/maorfr/kube-tasks) 5 | [![license](https://img.shields.io/github/license/maorfr/kube-tasks.svg)](https://github.com/maorfr/kube-tasks/blob/master/LICENSE) 6 | 7 | # Kube tasks 8 | 9 | A tool to perform simple Kubernetes related actions 10 | 11 | ## Commands 12 | 13 | ### Simple Backup 14 | 15 | Usage: 16 | ``` 17 | Backup files to cloud storage 18 | 19 | Usage: 20 | kube-tasks simple-backup [flags] 21 | 22 | Flags: 23 | -b, --buffer-size float in-memory buffer size (in MB) to use for files copy (buffer per file) (default 6.75) 24 | -c, --container string container name to act on 25 | --dst string destination to backup to. Example: s3://bucket/backup 26 | -n, --namespace string namespace to find pods 27 | -p, --parallel int number of files to copy in parallel. set this flag to 0 for full parallelism (default 1) 28 | --path string path to act on 29 | -l, --selector string selector to filter on 30 | --tag string tag to backup to. Default is Now (yyMMddHHmmss) 31 | ``` 32 | 33 | Example: Backup Jenkins 34 | ``` 35 | kube-tasks simple-backup -n default -l release=jenkins -c jenkins --path /var/jenkins_home --dst s3://maorfr-jenkins-data 36 | ``` 37 | 38 | ### Wait for Pods 39 | 40 | Usage: 41 | ``` 42 | Wait for a given number of ready pods 43 | 44 | Usage: 45 | kube-tasks wait-for-pods [flags] 46 | 47 | Flags: 48 | -n, --namespace string namespace to find pods 49 | -r, --replicas int number of ready replicas to wait for (default 1) 50 | -l, --selector string selector to filter on 51 | ``` 52 | 53 | Example: Cassandra Repairer init container 54 | ``` 55 | kube-tasks wait-for-pods -n prod -l release=prod-cassandra --replicas=3 56 | ``` 57 | 58 | ### Execute 59 | 60 | Usage: 61 | ``` 62 | Execute a command in a container. Only executes the command in the first pod 63 | 64 | Usage: 65 | kube-tasks execute [flags] 66 | 67 | Flags: 68 | --command string command to execute in container 69 | -c, --container string container name to act on 70 | -n, --namespace string namespace to find pods 71 | -l, --selector string selector to filter on 72 | ``` 73 | 74 | Example: Cassandra Repairer 75 | ``` 76 | kube-tasks execute -n prod -l release=prod-cassandra --command "nodetool repair --full" 77 | ``` 78 | -------------------------------------------------------------------------------- /cmd/kube-tasks.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | 9 | "github.com/maorfr/kube-tasks/pkg/kubetasks" 10 | "github.com/maorfr/kube-tasks/pkg/utils" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func main() { 15 | cmd := NewRootCmd(os.Args[1:]) 16 | if err := cmd.Execute(); err != nil { 17 | log.Fatal("Failed to execute command") 18 | } 19 | } 20 | 21 | // NewRootCmd represents the base command when called without any subcommands 22 | func NewRootCmd(args []string) *cobra.Command { 23 | cmd := &cobra.Command{ 24 | Use: "kube-tasks", 25 | Short: "", 26 | Long: ``, 27 | } 28 | 29 | out := cmd.OutOrStdout() 30 | 31 | cmd.AddCommand(NewSimpleBackupCmd(out)) 32 | cmd.AddCommand(NewWaitForPodsCmd(out)) 33 | cmd.AddCommand(NewExecuteCmd(out)) 34 | cmd.AddCommand(NewVersionCmd(out)) 35 | 36 | return cmd 37 | } 38 | 39 | type simpleBackupCmd struct { 40 | namespace string 41 | selector string 42 | container string 43 | path string 44 | dst string 45 | parallel int 46 | tag string 47 | bufferSize float64 48 | 49 | out io.Writer 50 | } 51 | 52 | // NewSimpleBackupCmd performs a backup 53 | func NewSimpleBackupCmd(out io.Writer) *cobra.Command { 54 | b := &simpleBackupCmd{out: out} 55 | 56 | cmd := &cobra.Command{ 57 | Use: "simple-backup", 58 | Short: "Backup files to cloud storage", 59 | Long: ``, 60 | Run: func(cmd *cobra.Command, args []string) { 61 | if _, err := kubetasks.SimpleBackup(b.namespace, b.selector, b.container, b.path, b.dst, b.parallel, b.tag, b.bufferSize); err != nil { 62 | log.Fatal(err) 63 | } 64 | }, 65 | } 66 | f := cmd.Flags() 67 | 68 | f.StringVarP(&b.namespace, "namespace", "n", "", "namespace to find pods") 69 | f.StringVarP(&b.selector, "selector", "l", "", "selector to filter on") 70 | f.StringVarP(&b.container, "container", "c", "", "container name to act on") 71 | f.StringVar(&b.path, "path", "", "path to act on") 72 | f.StringVar(&b.dst, "dst", "", "destination to backup to. Example: s3://bucket/backup") 73 | f.IntVarP(&b.parallel, "parallel", "p", 1, "number of files to copy in parallel. set this flag to 0 for full parallelism") 74 | f.StringVar(&b.tag, "tag", utils.GetTimeStamp(), "tag to backup to. Default is Now (yyMMddHHmmss)") 75 | f.Float64VarP(&b.bufferSize, "buffer-size", "b", 6.75, "in-memory buffer size (in MB) to use for files copy (buffer per file)") 76 | 77 | return cmd 78 | } 79 | 80 | type waitForPods struct { 81 | namespace string 82 | selector string 83 | container string 84 | replicas int 85 | 86 | out io.Writer 87 | } 88 | 89 | // NewWaitForPodsCmd waits for a given number of replicas 90 | func NewWaitForPodsCmd(out io.Writer) *cobra.Command { 91 | w := &waitForPods{out: out} 92 | 93 | cmd := &cobra.Command{ 94 | Use: "wait-for-pods", 95 | Short: "Wait for a given number of ready pods", 96 | Long: ``, 97 | Run: func(cmd *cobra.Command, args []string) { 98 | if err := kubetasks.WaitForPods(w.namespace, w.selector, w.replicas); err != nil { 99 | log.Fatal(err) 100 | } 101 | }, 102 | } 103 | f := cmd.Flags() 104 | 105 | f.StringVarP(&w.namespace, "namespace", "n", "", "namespace to find pods") 106 | f.StringVarP(&w.selector, "selector", "l", "", "selector to filter on") 107 | f.IntVarP(&w.replicas, "replicas", "r", 1, "number of ready replicas to wait for") 108 | 109 | return cmd 110 | } 111 | 112 | type execCmd struct { 113 | namespace string 114 | selector string 115 | container string 116 | command string 117 | 118 | out io.Writer 119 | } 120 | 121 | // NewExecuteCmd executes a simple command in a container 122 | func NewExecuteCmd(out io.Writer) *cobra.Command { 123 | e := &execCmd{out: out} 124 | 125 | cmd := &cobra.Command{ 126 | Use: "execute", 127 | Short: "Execute a command in a container. Only executes the command in the first pod", 128 | Long: ``, 129 | Run: func(cmd *cobra.Command, args []string) { 130 | if err := kubetasks.Execute(e.namespace, e.selector, e.container, e.command); err != nil { 131 | log.Fatal(err) 132 | } 133 | }, 134 | } 135 | f := cmd.Flags() 136 | 137 | f.StringVarP(&e.namespace, "namespace", "n", "", "namespace to find pods") 138 | f.StringVarP(&e.selector, "selector", "l", "", "selector to filter on") 139 | f.StringVarP(&e.container, "container", "c", "", "container name to act on") 140 | f.StringVar(&e.command, "command", "", "command to execute in container") 141 | 142 | return cmd 143 | } 144 | 145 | var ( 146 | // GitTag stands for a git tag 147 | GitTag string 148 | // GitCommit stands for a git commit hash 149 | GitCommit string 150 | ) 151 | 152 | // NewVersionCmd prints version information 153 | func NewVersionCmd(out io.Writer) *cobra.Command { 154 | cmd := &cobra.Command{ 155 | Use: "version", 156 | Short: "Print version information", 157 | Long: ``, 158 | Run: func(cmd *cobra.Command, args []string) { 159 | fmt.Printf("Version %s (git-%s)\n", GitTag, GitCommit) 160 | }, 161 | } 162 | 163 | return cmd 164 | } 165 | -------------------------------------------------------------------------------- /pkg/kubetasks/kubetasks.go: -------------------------------------------------------------------------------- 1 | package kubetasks 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | "github.com/maorfr/kube-tasks/pkg/utils" 12 | "github.com/maorfr/skbn/pkg/skbn" 13 | ) 14 | 15 | // SimpleBackup performs backup 16 | func SimpleBackup(namespace, selector, container, path, dst string, parallel int, tag string, bufferSize float64) (string, error) { 17 | log.Println("Backup started!") 18 | dstPrefix, dstPath := utils.SplitInTwo(dst, "://") 19 | dstPath = filepath.Join(dstPath, tag) 20 | 21 | log.Println("Getting clients") 22 | k8sClient, dstClient, err := skbn.GetClients("k8s", dstPrefix, "", dstPath) 23 | if err != nil { 24 | return "", err 25 | } 26 | 27 | log.Println("Getting pods") 28 | pods, err := utils.GetReadyPods(k8sClient, namespace, selector) 29 | if err != nil { 30 | return "", err 31 | } 32 | 33 | if len(pods) == 0 { 34 | return "", fmt.Errorf("No pods were found in namespace %s by selector %s", namespace, selector) 35 | } 36 | 37 | log.Println("Calculating paths. This may take a while...") 38 | fromToPathsAllPods, err := utils.GetFromAndToPathsFromK8s(k8sClient, pods, namespace, container, path, dstPath) 39 | if err != nil { 40 | return "", err 41 | } 42 | 43 | log.Println("Starting files copy to tag: " + tag) 44 | if err := skbn.PerformCopy(k8sClient, dstClient, "k8s", dstPrefix, fromToPathsAllPods, parallel, bufferSize); err != nil { 45 | return "", err 46 | } 47 | 48 | log.Println("All done!") 49 | return tag, nil 50 | } 51 | 52 | // WaitForPods waits for a given number of pods 53 | func WaitForPods(namespace, selector string, desiredReplicas int) error { 54 | log.Println("Getting clients") 55 | k8sClient, err := skbn.GetClientToK8s() 56 | if err != nil { 57 | return err 58 | } 59 | 60 | readyPods := -1 61 | log.Printf("Waiting for %d ready pods", desiredReplicas) 62 | for readyPods != desiredReplicas { 63 | pods, err := utils.GetReadyPods(k8sClient, namespace, selector) 64 | if err != nil { 65 | return err 66 | } 67 | readyPods = len(pods) 68 | log.Printf("Currently %d/%d ready pods", readyPods, desiredReplicas) 69 | if readyPods == desiredReplicas { 70 | break 71 | } 72 | time.Sleep(10 * time.Second) 73 | } 74 | return nil 75 | } 76 | 77 | // Execute executes simple commands in a container 78 | func Execute(namespace, selector, container, command string) error { 79 | log.Println("Getting clients") 80 | k8sClient, err := skbn.GetClientToK8s() 81 | if err != nil { 82 | return err 83 | } 84 | 85 | log.Println("Getting pods") 86 | pods, err := utils.GetReadyPods(k8sClient, namespace, selector) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | commandArray := strings.Fields(command) 92 | stdout := new(bytes.Buffer) 93 | stderr, err := skbn.Exec(*k8sClient, namespace, pods[0], container, commandArray, nil, stdout) 94 | if len(stderr) != 0 { 95 | return fmt.Errorf("STDERR: " + (string)(stderr)) 96 | } 97 | if err != nil { 98 | return err 99 | } 100 | 101 | printOutput(stdout.String(), pods[0]) 102 | return nil 103 | } 104 | 105 | func printOutput(output, pod string) { 106 | for _, line := range strings.Split(output, "\n") { 107 | if line != "" { 108 | log.Println(pod, line) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /pkg/utils/general.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // SplitInTwo splits a string to two parts by a delimeter 13 | func SplitInTwo(s, sep string) (string, string) { 14 | if !strings.Contains(s, sep) { 15 | log.Fatal(s, "does not contain", sep) 16 | } 17 | split := strings.Split(s, sep) 18 | return split[0], split[1] 19 | } 20 | 21 | // GetRandString returns a randon string 22 | func GetRandString() string { 23 | randNum := strconv.Itoa((rand.New(rand.NewSource(time.Now().UnixNano()))).Int()) 24 | b := make([]byte, 6) 25 | for i := range b { 26 | b[i] = randNum[rand.Intn(len(randNum))] 27 | } 28 | return string(b) 29 | } 30 | 31 | // GetTimeStamp returns time stamp 32 | func GetTimeStamp() string { 33 | return time.Now().Format("20060102150405") 34 | } 35 | 36 | // MapKeysToSlice converts a map to a slice using the keys as the values 37 | func MapKeysToSlice(m map[string]string) []string { 38 | var slice []string 39 | for k := range m { 40 | slice = append(slice, k) 41 | } 42 | return slice 43 | } 44 | 45 | // SliceContainsSlice verifies that outer slice contains inner slice 46 | func SliceContainsSlice(outers, inners []string) error { 47 | for _, outer := range outers { 48 | found := false 49 | for _, inner := range inners { 50 | if outer == inner { 51 | found = true 52 | break 53 | } 54 | } 55 | if !found { 56 | return fmt.Errorf("%s not contained in %s", outer, inners) 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | // Contains checks if a slice contains a given value 63 | func Contains(s []string, e string) bool { 64 | for _, a := range s { 65 | if a == e { 66 | return true 67 | } 68 | } 69 | return false 70 | } 71 | -------------------------------------------------------------------------------- /pkg/utils/kube.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/maorfr/skbn/pkg/skbn" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | // GetReadyPods gets a list of all ready ports according to defined namespace and selector 9 | func GetReadyPods(iClient interface{}, namespace, selector string) ([]string, error) { 10 | 11 | k8sClient := *iClient.(*skbn.K8sClient) 12 | pods, err := k8sClient.ClientSet.CoreV1().Pods(namespace).List(metav1.ListOptions{ 13 | LabelSelector: selector, 14 | }) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | var podList []string 20 | for _, pod := range pods.Items { 21 | ready := true 22 | for _, condition := range pod.Status.Conditions { 23 | if condition.Status != "True" { 24 | ready = false 25 | break 26 | } 27 | } 28 | if ready { 29 | podList = append(podList, pod.Name) 30 | } 31 | } 32 | 33 | return podList, nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/utils/path.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "path/filepath" 5 | 6 | "github.com/maorfr/skbn/pkg/skbn" 7 | ) 8 | 9 | // GetFromAndToPathsFromK8s aggregates paths from all pods 10 | func GetFromAndToPathsFromK8s(iClient interface{}, pods []string, namespace, container, path, dstBasePath string) ([]skbn.FromToPair, error) { 11 | k8sClient := iClient.(*skbn.K8sClient) 12 | var fromToPathsAllPods []skbn.FromToPair 13 | for _, pod := range pods { 14 | 15 | fromToPaths, err := GetFromAndToPathsK8sToDst(k8sClient, namespace, pod, container, path, dstBasePath) 16 | if err != nil { 17 | return nil, err 18 | } 19 | fromToPathsAllPods = append(fromToPathsAllPods, fromToPaths...) 20 | } 21 | 22 | return fromToPathsAllPods, nil 23 | } 24 | 25 | // GetFromAndToPathsK8sToDst performs a path mapping between Kubernetes and a destination 26 | func GetFromAndToPathsK8sToDst(k8sClient interface{}, namespace, pod, container, path, dstBasePath string) ([]skbn.FromToPair, error) { 27 | var fromToPaths []skbn.FromToPair 28 | 29 | pathPrfx := filepath.Join(namespace, pod, container, path) 30 | 31 | relativePaths, err := skbn.GetListOfFilesFromK8s(k8sClient, pathPrfx, "f", "*") 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | for _, relativePath := range relativePaths { 37 | 38 | fromPath := filepath.Join(pathPrfx, relativePath) 39 | toPath := filepath.Join(dstBasePath, relativePath) 40 | 41 | fromToPaths = append(fromToPaths, skbn.FromToPair{FromPath: fromPath, ToPath: toPath}) 42 | } 43 | 44 | return fromToPaths, nil 45 | } 46 | --------------------------------------------------------------------------------