Compare commits

...

161 Commits

Author SHA1 Message Date
Sarthak Jain 9f36304f0d
added changes for 1.16 release (#274)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2025-05-21 14:08:23 +05:30
Sarthak Jain 3ce353dcfc
added changes for 1.15.x release (#272)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2025-04-17 14:55:42 +05:30
Baalekshan 9c4c3351ac
docs: added usage for ` update password ` command (#258)
* docs: added usage for update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

* revert changes

* Revert "revert changes"

This reverts commit 8aeb2d1fd0.

---------

Signed-off-by: Baalekshan <baalekshan@gmail.com>
2025-04-17 12:37:38 +05:30
Baalekshan 094d1ec037
feat: add initial login check (#263)
Signed-off-by: Baalekshan <baalekshan@gmail.com>
2025-04-17 12:37:20 +05:30
Ayush Sharma b1dba98cfb
fix: update password (#271)
Signed-off-by: Ayush Sharma <kshitij3160@gmail.com>
2025-04-10 17:55:02 +05:30
Sarthak Jain f06f4f9d52
added changes for 1.14.x release (#269)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2025-01-21 16:22:00 +05:30
Ayush Sharma 734ea7dcb3
fix: error on fetching projects (#268)
Signed-off-by: Ayush Sharma <kshitij3160@gmail.com>
2025-01-21 16:09:19 +05:30
Sarthak Jain 902cab3da2
added changes for 1.13.x release (#266)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-12-16 15:42:16 +05:30
Sarthak Jain 1780d64f2f
added changes for 1.12.x release (#265)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-11-18 18:05:11 +05:30
Sarthak Jain 32f32320d6
addde changes for 1.11.0 release (#261)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-10-15 22:28:10 +05:30
Sarthak Jain ff915a9b2e
added changes for 1.10.0 release (#256)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-09-18 00:28:16 +05:30
Baalekshan 0980230d37
feat: added `update password` command (#255)
* feat: added update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

* feat: added update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

* feat: added update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

* feat: added update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

* feat: added update password command

Signed-off-by: Baalekshan <baalekshan@gmail.com>

---------

Signed-off-by: Baalekshan <baalekshan@gmail.com>
2024-09-17 22:53:45 +05:30
Sarthak Jain 4f498493a6
fixed refere header issue, used client-go for chaos infra connection instead of kubectl apply (#254)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-09-16 23:36:35 +05:30
Shivam Purohit 5b4f3cd6c8
Fix for new schema changes (#253)
* get-project:fix for new schema

Signed-off-by: shivam <shivampurohit900@gmail.com>

* remove comments and unnecessary code

Signed-off-by: shivam <shivampurohit900@gmail.com>

* gofmt fix

Signed-off-by: shivam <shivampurohit900@gmail.com>

---------

Signed-off-by: shivam <shivampurohit900@gmail.com>
2024-09-10 22:54:25 +05:30
Shivam Purohit 327ffaa710
Automate homebrew version updation with new release (#248)
* add action for bump-formula homebrew

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* Update trigger for stable release

* Update release.yml

* Update release.yml

* Update release.yml

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-08-14 17:56:42 +05:30
Sarthak Jain 86377b15a1
added changes for 1.9.0 release (#251)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-08-14 17:51:28 +05:30
Sarthak Jain ca5e75b3df
added changes for 1.8.0 release (#246)
* added changes for 1.8.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* added changes for 1.8.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-07-15 17:19:01 +05:30
Sarthak Jain a422a8a7a5
added chaos experiment ID in save experiment command (#245)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-07-15 17:01:03 +05:30
Sarthak Jain fd531ae0e0
1.7.0 release changes (#242)
* added changes for 1.7.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* added changes for 1.7.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-06-17 12:02:03 +05:30
dependabot[bot] 06bfd1a21c
--- (#232)
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 13:26:55 +05:30
dependabot[bot] f8426a7006
--- (#233)
updated-dependencies:
- dependency-name: github.com/fatih/color
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 12:42:05 +05:30
dependabot[bot] c34fbbc134
Bump github.com/spf13/cobra from 1.7.0 to 1.8
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 12:41:31 +05:30
Raj Das 34a2702e07
Adding dependabot.yml to litmusctl (#230) 2024-05-21 21:37:42 +05:30
Sarthak Jain d5b1d839cd
added changes for 1.6.0 release (#228)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-05-16 12:27:21 +05:30
Shivam Purohit a6b5157f7e
non-interactive output for get experiment (#219)
Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-05-15 18:15:12 +05:30
Shivam Purohit 5d8e1be588
Probes describe command (#215)
* describe probe command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add output flag for exp describe

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* describe probe: add option to change output type

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* remove comments

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-05-15 18:14:35 +05:30
Sarthak Jain d304f7d7a4
added changes for 1.5.0 release (#220)
* added changes for 1.5.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* added changes for 1.5.0 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-04-16 19:14:22 +05:30
Shivam Purohit 025938a208
Probe delete command (#207)
* delete probe command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* delete probe: add warning

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-04-16 18:52:20 +05:30
Kartikay 2ff8c1fa5d
update command merged into version (#217)
Signed-off-by: Kartikay <120778728+kartikaysaxena@users.noreply.github.com>
2024-04-16 16:53:18 +05:30
Shivam Purohit 47ba363e58
Merge get/list env command (#206)
* Merge get/list env command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add environment details flag

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* improve logic for list environment

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-04-15 12:21:05 +05:30
Shivam Purohit 6b276da3af
add get/list probe command (#204)
* fix

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add get/list probe command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* get-probes:add non-interactivte flag support

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:non-iteractive mode

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add probe-id flag for probe details

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* improve logic for probe-id flag

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:filter query on listprobe command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* get probe:add more details

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* refactor get probe command into smaller funcs

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:double printing of break statement get probe

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* remove toolchain

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix swap created by/at

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* refactor the get probe cmd

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* remove unnecessary fields from get probe query

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add referencedBy in output get probe

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-03-29 17:25:59 +05:30
Sarthak Jain 1a69cc5d24
Added changes for 1.4.0 release (#213)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-03-17 00:09:25 +05:30
Nageshbansal 6c628850a7
chores(deps): Bumps for vulnerability scan (#209)
Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2024-03-15 19:50:52 +05:30
Saranya Jena 49ebc8b936
Added fuzzer in Litmusctl (#210)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2024-03-14 19:34:16 +05:30
Aryan Bhokare 95fad15b01
Pre commit Hook added. (#203)
* Added Workflow script.

Signed-off-by: Aryan <aryan1bhokare@gmail.com>

* Added pre commit script.

Signed-off-by: Aryan <aryan1bhokare@gmail.com>

* Modified build with pre-commit hook.

Signed-off-by: Aryan <aryan1bhokare@gmail.com>

* Removed check.

Signed-off-by: Aryan Bhokare <92683836+aryan-bhokare@users.noreply.github.com>

* Added pre-commit prerequisite.

Signed-off-by: Aryan Bhokare <92683836+aryan-bhokare@users.noreply.github.com>

---------

Signed-off-by: Aryan <aryan1bhokare@gmail.com>
Signed-off-by: Aryan Bhokare <92683836+aryan-bhokare@users.noreply.github.com>
2024-02-28 12:00:46 +05:30
Shivam Purohit 9fe2a37e07
feat:delete environment command (#189)
* Feat:delete environment command

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* add check for chaos-infras

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* modify the description messages

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* Add command example for delete environment

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* Add new command usage in docs

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* change example for delete environment

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* update: Usage_0.23.0.md

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-02-23 17:50:07 +05:30
Sarthak Jain 05634fb60d
added changes for 1.3.0 release (#200)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-02-16 11:20:20 +05:30
Kartikay 2c252f845b
fix: added missing environmentID flag (#198)
* added missing environmentID flag

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* removed example in a case

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

---------

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>
2024-02-15 17:50:23 +05:30
Kartikay 8a139b00fb
fix staticcheck warnings (#191)
* fix dependencies

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* go mod tidy

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* some more fixes

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* revert deleted function

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* fixed lint

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* fix

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

---------

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>
2024-02-15 16:48:02 +05:30
Shivam Purohit 8bfa459dde
fix : output flag for get projects (#199)
* add get project output flag

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* remove unused import

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-02-15 16:21:03 +05:30
Kartikay ec74df7d75
Added a new command to change version (#185)
* added update command to change version

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* added homedir.Dir()

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

* added homedir

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>

---------

Signed-off-by: Kartikay <kartikay_2101ce32@iitp.ac.in>
2024-02-15 15:00:44 +05:30
Nageshbansal a6c26e09a8
Fixes getServerVersion gql (#193)
Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2024-02-15 11:51:13 +05:30
Shivam Purohit be72d8eeaf
add check for version env variable (#182)
Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-02-12 12:15:24 +05:30
Shivam Purohit e44abf3387
fix:index out of bound for get projects cmd (#184)
Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-02-12 12:07:13 +05:30
Deep Poharkar bed5b0c775
Added the local development setup guide (#168)
* added the local dev setup guide

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* fixed the link

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* removed unwanted settings for litmusconfig

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

---------

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>
2024-02-10 18:17:16 +05:30
Deep Poharkar 3bac6d7aae
Using client-go to Upgrade Infra (#166)
* removed kubectl dependency and applied manifest with client-go

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* made the code modular

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* removed the kubectl dependency completely from applyYaml func and reverted the local setup changes

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* fixed one local change

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* added comments to improve understandability of the code

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* added comments to improve understandability of the code

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* made the code less repetitive

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* removed unwanted print statements

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

---------

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>
2024-01-19 17:31:07 +05:30
Sarthak Jain 5a7722d618
added changes for 1.2.0 release (#178)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2024-01-17 12:53:15 +05:30
Nageshbansal 9794b0b391
Fixes connect chaos-infra for infrastructureType (#177)
Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2024-01-16 11:56:55 +05:30
Shivam Purohit cf8c33a62b
Get chaos environments command (#170)
* add get/list env commands to docs

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* modify print for error message

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

* fix:fmt error

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>

---------

Signed-off-by: Shivam Purohit <shivampurohit900@gmail.com>
2024-01-15 18:19:27 +05:30
Saranya Jena 486349b494
Updated compatibility matrix for 1.1 release (#172)
* updated compatibility matirx for 1.1 release

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

---------

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2023-12-15 15:02:51 +05:30
Nageshbansal 19b2c5bd9d
Fixes non-interactive mode in set-account command (#171)
Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2023-12-15 15:02:25 +05:30
Sarthak Jain 29997566fc
Added changes for 1.0.0 release (#165)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-11-15 20:36:21 +05:30
Deep Poharkar 6b68c4d1ca
UX Changes in litmusctl using promptui library. (#156)
* added the ux changes

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* removed local setup changes

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* removed commented code and fixed casing

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* updated readme and create project now shows id

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* typo fixed

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

---------

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>
2023-11-15 16:33:25 +05:30
dependabot[bot] 28f474437d
Bump golang.org/x/net from 0.7.0 to 0.17.0 (#159)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.7.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.7.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 11:59:35 +05:30
Sarthak Jain 9b468e1f08
added changes to add server endpoint (#160)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-11-10 11:48:51 +05:30
Sarthak Jain 49fc5b0ec7
Added changes for 0.24 release (#154)
* Added changes for 0.24 release

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* minor changes

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-09-15 17:53:26 +05:30
Nageshbansal 2a448d4f5a
Update Upgrade Command (#144)
* Update Upgrade Command

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Rename ops/ and types/agent_types.go

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Add Usage_0.23.0.md link in Readme

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

---------

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2023-09-14 10:32:23 +05:30
Jongwoo Han 30cb3ca9c5
Replace deprecated command with environment file (#142)
Signed-off-by: jongwooo <jongwooo.han@gmail.com>
2023-09-14 10:26:31 +05:30
Deep Poharkar 34a718c21b
fixed AuthResponse (#153)
* fixed the compatibility matrix

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

* fixed AuthResponse snake case to camelCase

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>

---------

Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>
2023-09-13 15:24:09 +05:30
Deep Poharkar 77e53623bf
fixed the compatibility matrix (#151)
Signed-off-by: deep-poharkar <deeppoharkar21@gmail.com>
2023-09-11 18:21:02 +05:30
Sarthak Jain c3db8e7002
Updated readme and compatibility matrix (#145)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-08-16 19:19:46 +05:30
dependabot[bot] 374d168123
Bump github.com/emicklei/go-restful (#143)
Bumps [github.com/emicklei/go-restful](https://github.com/emicklei/go-restful) from 2.15.0+incompatible to 2.16.0+incompatible.
- [Release notes](https://github.com/emicklei/go-restful/releases)
- [Changelog](https://github.com/emicklei/go-restful/blob/v3/CHANGES.md)
- [Commits](https://github.com/emicklei/go-restful/compare/v2.15.0...v2.16.0)

---
updated-dependencies:
- dependency-name: github.com/emicklei/go-restful
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-14 19:10:53 +05:30
Nageshbansal c0bf38685e
Upgrade Litmusctl for 3.0.0 Chaoscenter compatibility (#140)
* Migrate GetAgentList to GetInfraList

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Migrate ConnectAgent to ConnectInfra

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Migrate UpdateAgent to UpdateInfra

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Migrate GetWorkflowList to GetExperimentList

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Migrate GetWorkflowRunsList to GetExperimentRunsList

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Migrate DeleteChaosWorkflow to DeleteChaosExperiment

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update get agent cmd

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update get workflowruns cmd

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update get chaos-workflows cmd

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Fixes get commands

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update Connect Command

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update Disconnect Command

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Add Experiments Related Commands

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update delete and describe command

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Rename delete/workflow and describe/workflow

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Add run and save commands

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Add create environment command

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update Terminology

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update api directory str

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update logs

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Adds more Error Handling cases

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update go.mod

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Update .github/workflows

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

* Fix gofmt

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>

---------

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2023-08-14 18:43:02 +05:30
Saranya Jena ed34eb5209
updated readme and compatibility matrix for 0.22.0 release (#138)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2023-06-20 10:54:29 +05:30
Sarthak Jain 75bf6863ae
updated readme and compatibility matrix for 0.21.0 release (#135)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-05-17 15:44:46 +05:30
Sarthak Jain 98edb045c0
update compatibility matrix and readme for 0.20.0 (#133)
* update compatibility matrix and readme for 0.20.0

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* updated compatibility matrix

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-04-18 18:30:17 +05:30
Sarthak Jain d471d1866c
updated readme (#130)
* updated readme

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* Updated download link

---------

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-03-15 13:22:07 +05:30
Saranya Jena 12ec3977a6
updated compatibility matrix for 0.19.0 (#131)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2023-03-15 12:58:46 +05:30
Sarthak Jain 7d43952a5a
Updated compatibility matrix and readme (#127)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2023-02-15 18:12:08 +05:30
Saranya Jena 2668f59ab6
updated compatibility matrix and readme for 3.0.0-beta3 (#124)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2023-01-13 17:56:43 +05:30
Sarthak Jain 83345b696a
Updated compatibility matrix and added download link for 0.16.0 (#120)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-12-20 10:13:16 +05:30
Saranya Jena ef4f78a864
Updated Compatibility matrix for `3.0.0-beta2` (#118)
* Updated Compatibility matrix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* Added next version in the matrix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-12-15 19:50:59 +05:30
Nico J 8b99793189
Litmusctl checks existence of workflow name before scheduling & add the same if not present already (#117)
* Draft on handling scenario name vs generatename attributes

Signed-off-by: Nico J <nicolasj@microsoft.com>

* rollback build.sh changes made by mistake

Signed-off-by: Nico J <nicolasj@microsoft.com>

* Random string cannot have uppercase and remove the generateName from the json manifest

Signed-off-by: Nico J <nicolasj@microsoft.com>

Signed-off-by: Nico J <nicolasj@microsoft.com>
2022-12-15 16:47:25 +05:30
Nico J 5ae06c1fc6
litmsuctl create scenario command independent of install-chaos-experiments step (#114)
* Fixing FetchWeightages. Defaulting to 10

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* new pseudocode for calculating FetchWeightages

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* New pseudocode for handling weightage

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* re-implementing FetchWeightages for properly handling weightages

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* Update pkg/utils/workflow.go

Co-authored-by: Saranya Jena <saranya.jena@harness.io>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* Fixing FetchWeightages. Defaulting to 10

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Signed-off-by: Nico J <nicolasj@microsoft.com>

* signing all using gpg commits

Signed-off-by: Nico J <nicolasj@microsoft.com>

Signed-off-by: Nico J <nicolasj@microsoft.com>
Signed-off-by: iamnicoj <nicolasj@outlook.com>
Co-authored-by: Saranya Jena <saranya.jena@harness.io>
2022-12-12 10:53:45 +05:30
Sarthak Jain e59872117d
Updated compatibility matrix and added download link for 0.15.0 (#111)
* Updated compatibility matrix and added download link for 0.15.0

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* Updated 2.15.0 to 3.0-beta1

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* Removed download link for 0.8.0

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-11-21 17:43:06 +05:30
Caleb Xu cc8c5cec25
Fix tolerations help text in ChaosDelegate connection (#108)
Signed-off-by: Caleb Xu <calebcenter@live.com>

Signed-off-by: Caleb Xu <calebcenter@live.com>
2022-11-07 11:04:01 +05:30
Sarthak Jain eaf8030d76
Updated readme with 0.14.0 release (#106)
* Updated readme with 0.14.0 release

* Removed 0.6.0 download link

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-10-20 00:08:48 +05:30
Vedant Shrotria 830949e454
Added changes for 2.14.0 (#105)
Signed-off-by: Vedant <vedant.srotria@harness.io>

Signed-off-by: Vedant <vedant.srotria@harness.io>
Co-authored-by: Vedant <vedant.srotria@harness.io>
2022-10-19 23:17:20 +05:30
Fernando Alexandre 9535949dad
Typo in get.go (#104)
Correcting a typo in the help
2022-10-05 20:55:59 +05:30
Caleb Xu 61c2b740c4
Avoid panic during platform discovery with no nodes (#103)
Signed-off-by: Caleb Xu <calebcenter@live.com>

Signed-off-by: Caleb Xu <calebcenter@live.com>
2022-10-03 17:35:27 +05:30
Saranya Jena 16b4c617df
Updated compatibility and installation matrix in readme (#102)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-09-21 15:12:16 +05:30
Saranya Jena acacbde5b3
updated compatibility matrix (#101)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-09-15 18:55:01 +05:30
Sarthak Jain 5cd6548eb4
Updated chaos-operator version (#100)
* Updated chaos-operator version

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* Upgraded graphql-server version

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-09-15 13:48:56 +05:30
Sarthak Jain 573b812f7a
Updated argo and chaos-operator versions (#98)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-09-14 19:20:35 +05:30
Caleb Xu 0ca98c0aef
Add .exe extension to binaries on Windows (#96)
Signed-off-by: Caleb Xu <calebcenter@live.com>

Signed-off-by: Caleb Xu <calebcenter@live.com>
2022-09-13 15:19:39 +05:30
Saranya Jena 6c8aea4344
Added changes for error handling in chaosctl apply manifest logic (#97)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-09-13 15:06:42 +05:30
Sarthak Jain dd2a89e6d5
Update README.md (#95)
* Update README.md

Removed v0.12.0 download link

* Updated 0.12.0 download link

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-08-17 23:00:01 +05:30
Vedant Shrotria 36ec9cbc82
Added minor fix (#93)
Signed-off-by: Vedant <vedant.srotria@harness.io>

Signed-off-by: Vedant <vedant.srotria@harness.io>
Co-authored-by: Vedant <vedant.srotria@harness.io>
2022-08-17 12:33:44 +05:30
Vedant Shrotria 3e40de731e
Added changes for litmusctl readme update (#92)
* Added changes for litmusctl readme update

Signed-off-by: Vedant <vedant.srotria@harness.io>

* Added download link for v0.8.0 binary

Signed-off-by: Vedant <vedant.srotria@harness.io>

Signed-off-by: Vedant <vedant.srotria@harness.io>
Co-authored-by: Vedant <vedant.srotria@harness.io>
2022-08-17 10:39:44 +05:30
Sarthak Jain f44e8840ea
Fixed "index out of bound issue" in describe chaos-scenario command (#90)
* Fixed out of bound issue in describe chaos-scenario command

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* reverted compatibility matrix version changes

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-08-16 15:15:59 +05:30
Saranya Jena 7f837acb80
Added kubeconfig flag in upgrade command (#89)
* Added kubeconfig flag in all commands

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fixes

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fixes

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor change in compatibility matrix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fixes

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-08-12 22:59:38 +05:30
Sarthak Jain e6ad58e849
Added error handling for describe workflow and disconnect agent command (#81)
Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-08-12 22:55:22 +05:30
Saranya Jena 8cfd8de579
updated compatibility matrix (#88)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-08-12 22:05:50 +05:30
Saranya Jena 72bc309f57
Improved error handling logic for upgrade agent (#87)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-08-12 13:31:00 +05:30
Saranya Jena 3f5ac08325
Added compatibility matrix and API to fetch GQL server version (#84)
* Added getServerVersion and compatibility checks

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor linting fix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* Updated commend and endpoint

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* minor fix

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* Added a minor enhancement in logic

Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-08-09 12:00:50 +05:30
Sarthak Jain 35afa81773
Update commands and logs with new terminology (#83)
* Update commands and logs with new terminology

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>

* Added compatibility matrix in readme

Signed-off-by: Sarthak Jain <sarthak.jain@harness.io>
2022-08-09 12:00:19 +05:30
Raj Das 92277b257d
Passing namespace in the SelfSubjectAccessReviews function (#82)
Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
2022-07-29 14:54:15 +05:30
Prayag Savsani 19bf342353
docs(agents|workflows): Update usage documentation (#78)
* docs(agents): Refactor `create agent` -> `connect agent`

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* docs(workflows): Add the newly added commands in Usage

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* docs(agents): Add `disconnect agent` in Usage

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* docs(agent): Add `REGISTRATION` field in `get agents`

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* Update Usage.md

* Update Usage_interactive.md

Co-authored-by: Raj Das <mail.rajdas@gmail.com>
2022-06-14 15:11:08 +05:30
Prayag Savsani c0e2912f67
feat(workflows): Add `delete workflow` and `describe workflow` commands (#77)
* feat(workflows): Add `delete workflow` command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Add `describe workflow` command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): gofmt fixes

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* Added error message

Co-authored-by: Saranya Jena <saranya.jena@harness.io>
2022-06-14 12:26:07 +05:30
Prayag Savsani 5bb28a4cc2
feat(workflows): Add `get workflows` command (#75)
* feat(workflows): Add `get workflows` command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* Added error messages

Co-authored-by: Saranya Jena <saranya.jena@harness.io>
2022-06-14 12:06:45 +05:30
Prayag Savsani b4f7fe9cf8
feat(workflows): Add `get workflowruns` command (#76)
* feat(workflows): Add `get workflowruns` command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* Added error message

Co-authored-by: Saranya Jena <saranya.jena@harness.io>
2022-06-14 11:38:42 +05:30
Saranya Jena eb54bb66ec
Added error log in agent-connect (#79)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-06-13 12:24:24 +05:30
Prayag Savsani 8646502b22
feat(agents): Add delete agent command and refactor other agent commands (#73)
* feat(agents): Add delete agent command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(agents): Fix imports (gofmt)

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(agents): delete -> disconnect

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(agents): Add isRegistered field in `get agents`

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(agents): `create agent` -> `connect agent`

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(agents): resolve merge conflicts

Signed-off-by: PrayagS <prayagsavsani@gmail.com>
2022-05-25 14:58:04 +05:30
Prayag Savsani a683816084
Reword field names to be consistent (#72)
Signed-off-by: PrayagS <prayagsavsani@gmail.com>
2022-05-24 16:17:53 +05:30
Prayag Savsani 1dd51bb4ba
feat(workflows): Add create workflow command (#64)
* feat(workflows): Init create workflow command

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Add project ID flag

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Handle project ID empty value

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Add cluster ID flag

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Parsing of experiment weightages

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Add RBAC check for edit access

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* chore(workflows): Add code comments

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Exit if invalid RBAC access

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Add utils to parse YAML manifests

This also enables reading remote files.

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Improve weightage assignment

Also moved it to utils

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Improve handling of API response

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* docs(workflows): Add usage example

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Rename type names

Names of types now reflect their operation.
Eg. ChaosWorkFlowInput -> CreateChaosWorkFlowInput

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Support CronWorkflows

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Consistent error/success messages

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Show schedule after creation

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Uncommit duplicate code from api/workflows.go

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Fix type in workflow_types.go

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Reword error messages

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Reword types which store API response

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Handle error in sending API request

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* feat(workflows): Import API types from upstream GraphQL model

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Reword workflow -> chaos workflow

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Reword cluster -> agent

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Update API types and queries

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Update go.mod

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* refactor(workflows): Reword variable names

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Handle error in marshaling of API payload

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Update experiment weight assignment logic

Signed-off-by: PrayagS <prayagsavsani@gmail.com>

* fix(workflows): Use ChaosEngine GenerateName for weight name

Signed-off-by: PrayagS <prayagsavsani@gmail.com>
2022-05-24 16:17:20 +05:30
Adarshkumar14 14a7207bb1
Updating readme for litmusctl-0.10.0 (#71)
Signed-off-by: Adarshkumar14 <adarsh.kumar@harness.io>
2022-05-17 16:02:15 +05:30
Saranya Jena 5e8f261a86
Added proper error messages for upgrade agent (#69)
Signed-off-by: Saranya-jena <saranya.jena@harness.io>
2022-05-16 17:20:32 +05:30
Saranya Jena 7ea4b77753
Updated APIs as per new schema changes in litmus (#67)
* Fixed API naming, updated LitmusPortal -> ChaosCenter

Signed-off-by: Vansh Bhatia <vansh.bhatia@harness.io>

* Fixed some schema changes in projects and agents side

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

* Changed Chaos Center to ChaosCenter

Signed-off-by: Saranya-jena <saranya.jena@harness.io>

Co-authored-by: Vansh Bhatia <vansh.bhatia@harness.io>
2022-05-12 15:00:24 +05:30
Adarshkumar14 bc205aa96f
Adding download link for litmusctl v0.9.0 in readme (#59)
* Adding download link of litmusctl 0.9.0 in readme

Signed-off-by: Adarshkumar14 <1829034@kiit.ac.in>

* Adding download link of litmusctl 0.9.0 in readme

Signed-off-by: Adarshkumar14 <1829034@kiit.ac.in>
2022-01-18 13:12:52 +05:30
Soumya Ghosh Dastidar 5681620af9
Updated docs for litmusctl (#58)
* update non-interactive docs

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* updated interactive docs

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>
2022-01-17 17:58:24 +05:30
Soumya Ghosh Dastidar ee5ae8c214
Updated litmusctl docs (#57)
* updated doc for litmusctl

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

* updated non-interactive docs

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>
2022-01-15 12:09:04 +05:30
Soumya Ghosh Dastidar a3f1a94afc
added ability to use custom cert in cli (#54)
* Added changes for Skipping SSL verify (#49)

Signed-off-by: Jonsy13 <vedant.shrotria@chaosnative.com>

* Added some fixes for litmusctl (#50)

Signed-off-by: Jonsy13 <vedant.shrotria@chaosnative.com>

* added ability to use custom cert in cli

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>

Co-authored-by: Vedant Shrotria <40681425+Jonsy13@users.noreply.github.com>
2022-01-11 19:09:17 +05:30
Raj Babu Das 49f75e33fd
Update README.md (#52) 2021-12-15 14:32:11 +05:30
Sarthak Jain e38cf5a5fc
Fixed API path for auth APIs (#44)
Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>
2021-11-15 13:42:05 +05:30
Sarthak Jain 7ec6f3f867
Api migration (#43)
* Migrating project associated APIs to REST APIs

Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>

* Added constants

Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>
2021-11-15 09:22:17 +05:30
Raj Babu Das a338254204
adding codeowners (#41)
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-21 15:08:01 +05:30
Raj Babu Das 2fcc35ec5d
minor fix for tolerations (#39)
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-21 14:02:32 +05:30
Raj Babu Das a0b9618f68
Update README.md (#38) 2021-10-17 02:24:19 +05:30
Raj Babu Das 5a6ca23872
removing duplicate conditions (#35)
* removing duplicate conditions

Signed-off-by: rajdas98 <mail.rajdas@gmail.com>

* gofmt

Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-14 16:40:51 +05:30
Raj Babu Das f06886fa8b
Merge pull request #34 from litmuschaos/add-toleration
Adding k8s  toleration support to litmusctl
2021-10-12 10:49:19 +05:30
rajdas98 60483b0d58 fixing govet
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-12 01:43:02 +05:30
Raj Babu Das 587c6439b3
Merge pull request #33 from SarthakJain26/agent-upgrade
Implementation of upgrading Agent
2021-10-08 11:47:08 +05:30
Saranya-jena f7d946ab41 Added error check in api.upgradeAgent
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-07 17:41:27 +05:30
Saranya-jena 7d77349d7e Merge branch 'master' of https://github.com/litmuschaos/litmusctl into agent-upgrade
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-07 17:28:32 +05:30
Saranya-jena 5eff84f8bf minor fixes and code cleanup
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-07 17:03:36 +05:30
Saranya-jena a130b15801 Added conditions for applying the agent manifest
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-06 15:43:27 +05:30
rajdas98 4bdc1f0db3 Adding toleration
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-06 00:13:15 +05:30
rajdas98 b7ceb417fa Adding toleration
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-05 22:06:48 +05:30
rajdas98 376e068058 Adding toleration
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-10-05 22:05:41 +05:30
Saranya-jena 2f2e12e95d Updated flag name
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-05 18:55:17 +05:30
SarthakJain26 3d5cf51b9c Code cleanup
Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>
2021-10-05 12:52:07 +05:30
Saranya-jena c0c8460fb7 Added logic for config map backup, changed user input
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-10-01 13:44:41 +05:30
Saranya-jena db93a17783 Added comments and optimized the code
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-09-30 12:49:06 +05:30
Saranya-jena bdfd0e048e Added logic for deleting the manifest file
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-09-30 11:43:54 +05:30
Saranya-jena 69bc32a9e5 Added logic for replacing manifest agent-config with cluster agent-config
Signed-off-by: Saranya-jena <saranya@chaosnative.com>
2021-09-29 18:17:37 +05:30
SarthakJain26 c3d03f7683 Processing data
Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>
2021-09-28 16:03:45 +05:30
SarthakJain26 bebdd14f4b Fetching manifest and config data
Signed-off-by: SarthakJain26 <sarthak@chaosnative.com>
2021-09-28 14:42:49 +05:30
Raj Babu Das db39e0958f
Merge pull request #31 from litmuschaos/upgrade-version
Adding litmusctl v0.4.0 version to readme
2021-09-24 11:05:12 +05:30
Raj Babu Das 9018663a6e
Adding litmusctl v0.4.0 version to readme 2021-09-24 10:54:46 +05:30
Vedant Shrotria fe00c067d7
Added logic for parsing username from token (#29)
* Added logic for parsing username from token

Signed-off-by: Jonsy13 <vedant.shrotria@chaosnative.com>

* Removed error, as token check is present

Signed-off-by: Jonsy13 <vedant.shrotria@chaosnative.com>
2021-09-23 20:50:24 +05:30
Raj Babu Das e515dc4fff
update-bucket link (#27)
* update-bucket link

Signed-off-by: rajdas98 <mail.rajdas@gmail.com>

* Update README.md
2021-08-18 11:00:58 +05:30
Raj Babu Das ffba121903
Update README.md (#26) 2021-08-17 11:57:25 +05:30
Raj Babu Das fbd7960119
Adding interactive usage doc for litmusctl (#25)
* Adding interactive usage doc for litmusctl

Signed-off-by: rajdas98 <mail.rajdas@gmail.com>

* minor fix

Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-08-15 13:55:52 +05:30
Raj Babu Das 9add806abc
Create codeql-analysis.yml (#23) 2021-07-26 16:59:19 +05:30
Raj Babu Das 5c6a334d85
adding compatible versions (#22)
Signed-off-by: rajdas98 <mail.rajdas@gmail.com>
2021-07-25 14:53:05 +05:30
Raj Babu Das ea4c62c3e7
litmusctl version fix (#20)
Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
2021-07-21 20:18:14 +05:30
Raj Babu Das a58bc763a6
litmusctl version fix (#19)
Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
2021-07-21 18:20:22 +05:30
Raj Babu Das 887406a2e2
Adding nodeselector flag in create agent cmd and refactored the litmusctl doc (#18)
* time format

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* Refactoring Readme

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
2021-07-21 16:29:42 +05:30
Raj Babu Das 9e98b273a8
Minor Bug fixes in litmusctl (#17)
* fixing minor issue in litmusctl

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* fixing minor issue in litmusctl

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* fixing minor issue in litmusctl

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>

* fixing minor issue in litmusctl

Signed-off-by: Raj Babu Das <mail.rajdas@gmail.com>
2021-07-19 12:06:00 +05:30
Raj Babu Das 72546de78f
Completely revamped the litmusctl workflow (#16)
* Refactored litmusctl

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* Refactored litmusctl

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* litmusctl refactor

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* litmusctl refactor

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* adding few commands

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* gofmt

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* adding config flag in litmusctl

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* goimport fix

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* add version cmd

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* add version cmd

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* removing old pkg and adding help doc

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* go mod tidy

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* Code changes

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* fixing bch

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* adding license, improving bch and gofmt

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* adding help doc

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* Adding auto project creation

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* fixing lint issues

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* fixing lint issues

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* lint fix

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* minor changes in help doc

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* fix minor issue in GetModeType()

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* fixing review dog

Signed-off-by: Raj Das <mail.rajdas@gmail.com>
2021-07-08 12:58:26 +05:30
Raj Babu Das 92c04195a0
version upgrade to v0.2.0 (#14)
* version upgrade

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* Update README.md
2021-06-15 22:29:57 +05:30
Raj Babu Das df171479ac
Adding auto build version to the litmusctl (#13)
* Adding auto version set in CLI

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* Adding auto version set in CLI

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* goimport fix

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* changes in the cmd prompt

Signed-off-by: Raj Das <mail.rajdas@gmail.com>
2021-06-15 04:24:57 +05:30
Raj Babu Das bcf1791705
Add kubeconfig flag to litmusctl (#11)
* Add kubeconfig flag

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* go fmt

Signed-off-by: Raj Das <mail.rajdas@gmail.com>

* changing logic from recursion function to goto statement

Signed-off-by: Raj Das <mail.rajdas@gmail.com>
2021-06-01 16:30:09 +05:30
Arkajyoti Mukherjee 67053298d9
modified README.md (#12)
Signed-off-by: GitHub <noreply@github.com>
2021-05-25 18:30:07 +05:30
Arkajyoti Mukherjee ea0ee8e30b
changed command from agent register to agent connect (#10)
* changed command from agent register to agent connect

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>

* removed main binary file

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>
2021-04-01 12:27:05 +05:30
Raj Babu Das de2d07fbaa
Update the readme for new litmusctl binaries (#9)
Signed-off-by: Raj Das <mail.rajdas@gmail.com>
2021-03-28 14:16:26 +05:30
Raj Babu Das 46c8e00150
Deploying litmusctl bins to s3 bucket (#8)
Signed-off-by: Raj Das <mail.rajdas@gmail.com>
2021-03-28 13:45:52 +05:30
Raj Babu Das 0ee4b998ef
Update README.md 2021-03-18 21:39:46 +05:30
litmuschaos-bot 4c136b252f Updating litmusctl
Signed-off-by: litmuschaos-bot <litmuschaos-bot@users.noreply.github.com>
2021-03-18 15:56:32 +00:00
Arkajyoti Mukherjee 7e7e8ddd6b
added more platforms types for litmusctl (#7)
* added more platforms types for litmusctl

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>

* removed other build envs and added darwin/amd64

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>

* updated README.md to include other platform versions (MacOS and windows) as well

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>

* reduced version to 0.1.0

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>
2021-03-18 21:18:28 +05:30
Arkajyoti Mukherjee dc43fa9aea
fixed the selection of installation mode logic (#6)
* fixed the selection of installation mode logic

Signed-off-by: arkajyotiMukherjee <arkajyoti.mukherjee@mayadata.io>

* Updating litmusctl

Signed-off-by: litmuschaos-bot <litmuschaos-bot@users.noreply.github.com>

Co-authored-by: litmuschaos-bot <litmuschaos-bot@users.noreply.github.com>
2021-03-18 20:55:20 +05:30
114 changed files with 11343 additions and 2158 deletions

31
.github-workflow-script Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""Extract steps containing special env variable COMMIT_HOOKS from Github workflow"""
import yaml
import argparse
# Parse command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument("workflow_file",
help="Path to the GitHub Actions workflow file")
args = parser.parse_args()
# Load the GitHub Actions workflow file as YAML
with open(args.workflow_file, "r") as file:
data = yaml.safe_load(file)
print("#!/bin/bash")
# Loop over all jobs
for job_name, job in data["jobs"].items():
# Loop over all steps in the job
for step in job["steps"]:
# Check if the step has a KCIDB_HOOKS environment variable
if "env" in step and step["env"].get("COMMIT_HOOKS") == "pre-commit":
# Print the name of the step
print(f"\n# {step['name']}")
# Extract the command(s) from the step
command = step.get("run", [])
# Print the command(s)
for line in command if isinstance(command, list) else [command]:
print(line)

5
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,5 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
# These owners will be the default owners for everything in the repo.
* @rajdas98 @arkajyotiMukherjee @Jonsy13 @Saranya-jena @SarthakJain26

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"

View File

@ -1,11 +1,8 @@
name: Build
name: build-pipeline
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
build:
@ -14,7 +11,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.14
go-version: 1.20.5
# Checkout to the latest commit
# On specific directory/path
@ -22,6 +19,8 @@ jobs:
uses: actions/checkout@v2
- name: gofmt check
env:
COMMIT_HOOKS: pre-commit
run: |
if [ "$(gofmt -s -l . | wc -l)" -ne 0 ]
then

71
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '44 2 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,13 +1,10 @@
name: Push-or-Release
name: push-release-pipeline
on:
push:
branches:
- master
tags-ignore:
- '**'
create:
tags:
- '**'
- '*'
jobs:
build-and-push:
@ -16,7 +13,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.14
go-version: 1.20.5
# Checkout to the latest commit
# On specific directory/path
@ -27,9 +24,9 @@ jobs:
run: |
if [ "$(gofmt -s -l . | wc -l)" -ne 0 ]
then
echo "The following files were found to be not go formatted:"
gofmt -s -l .
exit 1
echo "The following files were found to be not go formatted:"
gofmt -s -l .
exit 1
fi
- name: golangci-lint
@ -39,23 +36,23 @@ jobs:
run: |
make unused-package-check
- name: Get tag
shell: bash
run: echo "branch=$(echo ${GITHUB_REF##*/})" >> $GITHUB_OUTPUT
id: tag
- name: Building litmusctl
run: |
git checkout ${GITHUB_REF##*/}
make build
make TAG=${{ steps.tag.outputs.branch }} build
- name: Push litmusctl to repo
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Copy binaries to the litmusctl s3 bucket
run: |
git config --global user.name 'litmuschaos-bot'
git config --global user.email 'litmuschaos-bot@users.noreply.github.com'
git remote -v
if [[ `git status --porcelain` ]]; then
echo "changes"
git add .
git commit -s -m "Updating litmusctl"
git push
exit 0;
else
echo "no changes"
exit 0;
fi
aws s3 sync platforms-${{ steps.tag.outputs.branch }} s3://${{ secrets.AWS_S3_BUCKET }}

23
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: release-pipeline
on:
create:
tags:
- '**'
jobs:
homebrew:
name: Bump Homebrew formula
runs-on: ubuntu-latest
steps:
- name: Extract version
id: extract-version
run: |
echo "tag-name=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- uses: mislav/bump-homebrew-formula-action@v3
with:
formula-name: litmusctl
tag-name: ${{ steps.extract-version.outputs.tag-name }}
download-url: https://github.com/litmuschaos/litmusctl/archive/refs/tags/${{ steps.extract-version.outputs.tag-name }}.tar.gz
env:
COMMITTER_TOKEN: ${{ secrets.COMMITTER_TOKEN }}

29
.gitignore vendored
View File

@ -1,11 +1,22 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
main
litmusctl
agent-manifest.yaml
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
vendor/
platforms*
#IDE
.idea
main

View File

@ -1,25 +0,0 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
- go mod download
builds:
- env:
- GO111MODULE=on
- CGO_ENABLED=0
archives:
- name_template: '{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

7
.pre-commit Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# If there are whitespace errors, print the offending file names and fail.
# git diff-index --check --cached $against --
# Execute steps extracted from GitHub Actions
. <(./.github-workflow-script .github/workflows/build.yml)

97
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,97 @@
# Litmusctl Local Development Setup Guide
## Introduction
Welcome to the local development setup guide for **`litmusctl`**. This guide will walk you through the steps required to set up and run **`litmusctl`** on your local machine.
## Important Note
Before running **`litmusctl`**, make sure you have a Chaos Centre running. Ensure that the Chaos Centre version is compatible with the **`litmusctl`** version you are using.
## Prerequisites
Before you begin, ensure that you have the following prerequisites installed on your machine:
- [Go programming language](https://golang.org/doc/install) (version or later)
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- Kubeconfig - `litmusctl` needs the kubeconfig of the k8s cluster where we need to connect litmus Chaos Delegates. The CLI currently uses the default path of kubeconfig i.e. `~/.kube/config`.
## Clone the Repository
```bash
git clone https://github.com/litmuschaos/litmusctl.git
cd litmusctl
```
## **Install Dependencies**
```bash
go mod download
```
## **Configuration**
Before running **`litmusctl`**, update the following configuration paths in the **`pkg/utils/constants.go`**
From this
```go
// Graphql server API path
GQLAPIPath = "/api/query"
// Auth server API path
AuthAPIPath = "/auth"
```
To this
```go
// Graphql server API path
GQLAPIPath = "/query"
// Auth server API path
AuthAPIPath = ""
```
## **Running `litmusctl`**
Execute the following command to run **`litmusctl`** locally:
```bash
go run main.go <command> <subcommand> <subcommand> [options and parameters]
```
## **Testing `litmusctl`**
To run tests, use the following command:
```bash
go test ./...
```
## **Contributing Prerequisites**
Setting up pre-commit:
Execute the following command to create a symbolic link named `pre-commit` in the `.git/hooks` directory that points to the `.pre-commit`.
```bash
ln -s ../../.pre-commit .git/hooks/pre-commit
```
## **Contributing Guidelines**
If you wish to contribute to **`litmusctl`**, please follow our [contributing guidelines](https://github.com/litmuschaos/litmus/blob/master/CONTRIBUTING.md). Your contributions are valuable, and adhering to these guidelines ensures a smooth and collaborative development process.
## **Troubleshooting**
If you encounter any issues during setup, refer to our [troubleshooting guide](https://docs.litmuschaos.io/docs/troubleshooting) or reach out to our community for assistance. We're here to help you overcome any obstacles and ensure a successful setup.
## **Additional Information**
For more details on using **`litmusctl`**, refer to our [official documentation](https://docs.litmuschaos.io/). This documentation provides comprehensive information to help you make the most out of **`litmusctl`**.
Thank you for setting up **`litmusctl`** locally! Feel free to explore and contribute to the project. Your involvement is crucial to the success of the **`litmusctl`** community.
Let the chaos begin! 🚀🔥

View File

@ -1,3 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

View File

@ -1,11 +1,7 @@
PROJECT_NAME := "litmusctl"
PKG := "github.com/litmuschaos/$(PROJECT_NAME)"
all: build
build: ## Build the binary file
@bash scripts/build.sh main.go
@bash scripts/build.sh main.go $(TAG)
.PHONY: unused-package-check
unused-package-check:

357
README.md
View File

@ -1,154 +1,279 @@
# Litmusctl
Litmusctl is a command line interface to manage LitmusPortal services.
[![BCH compliance](https://bettercodehub.com/edge/badge/litmuschaos/litmusctl?branch=master)](https://bettercodehub.com/)
![GitHub Workflow](https://github.com/litmuschaos/litmusctl/actions/workflows/push.yml/badge.svg?branch=master)
[![GitHub stars](https://img.shields.io/github/stars/litmuschaos/litmusctl?style=social)](https://github.com/litmuschaos/litmusctl/stargazers)
[![GitHub Release](https://img.shields.io/github/release/litmuschaos/litmusctl.svg?style=flat)]()
The Litmuschaos command-line tool, litmusctl, allows you to manage litmuschaos's agent plane. You can use litmusctl to connect Chaos Delegates, create project, schedule Chaos Scenarios, disconnect Chaos Delegates and manage multiple litmuschaos accounts.
## Usage
For more information including a complete list of litmusctl operations, see the litmusctl reference documentation.
* For 0.23.0 or latest: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_0.23.0.md">Click here</a>
* For v0.12.0 to v0.22.0: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_interactive.md">Click here</a>
* For v0.2.0 or earlier && compatible with Litmus-2.0.0-Beta8 or earlier: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_v0.2.0.md">Click here</a>
## Requirements
The litmusctl CLI requires the following things:
- Kubeconfig - litmusctl needs the kubeconfig of the k8s cluster where we need to connect litmus agents. The CLI currently uses the default path of kubeconfig i.e. `~/.kube/config`.
- kubeconfig - litmusctl needs the kubeconfig of the k8s cluster where we need to connect litmus Chaos Delegates. The CLI currently uses the default path of kubeconfig i.e. `~/.kube/config`.
- kubectl- litmusctl is using kubectl under the hood to apply the manifest. To install kubectl, follow: [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
## Compatibility matrix
To check compatibility of litmusctl with Chaos Center
<table>
<th>litmusctl version</th>
<th>Lowest Chaos Center supported version</th>
<th>Highest Chaos Center supported version</th>
<tr>
<td>1.16.0</td>
<td>3.0.0</td>
<td>3.19.0</td>
</tr>
<tr>
<td>1.15.0</td>
<td>3.0.0</td>
<td>3.18.0</td>
</tr>
<tr>
<td>1.14.0</td>
<td>3.0.0</td>
<td>3.15.0</td>
</tr>
<tr>
<td>1.13.0</td>
<td>3.0.0</td>
<td>3.14.0</td>
</tr>
<tr>
<td>1.12.0</td>
<td>3.0.0</td>
<td>3.13.0</td>
</tr>
<tr>
<td>1.11.0</td>
<td>3.0.0</td>
<td>3.12.0</td>
</tr>
<tr>
<td>1.10.0</td>
<td>3.0.0</td>
<td>3.11.0</td>
</tr>
<tr>
<td>1.9.0</td>
<td>3.0.0</td>
<td>3.10.0</td>
</tr>
<tr>
<td>1.8.0</td>
<td>3.0.0</td>
<td>3.9.1</td>
</tr>
<tr>
<td>1.7.0</td>
<td>3.0.0</td>
<td>3.8.0</td>
</tr>
<tr>
<td>1.6.0</td>
<td>3.0.0</td>
<td>3.7.0</td>
</tr>
<tr>
<td>1.5.0</td>
<td>3.0.0</td>
<td>3.6.0</td>
</tr>
<tr>
<td>1.4.0</td>
<td>3.0.0</td>
<td>3.5.0</td>
</tr>
<tr>
<td>1.3.0</td>
<td>3.0.0</td>
<td>3.4.0</td>
</tr>
<tr>
<td>1.2.0</td>
<td>3.0.0</td>
<td>3.3.0</td>
</tr>
<tr>
<td>1.1.0</td>
<td>3.0.0</td>
<td>3.2.0</td>
</tr>
<tr>
<td>1.0.0</td>
<td>3.0.0</td>
<td>3.1.0</td>
</tr>
</table>
## Installation
**Linux**
To install the latest version of litmusctl follow the below steps:
- Download the latest litmusctl binary from -
<table>
<th>Platforms</th>
<th>1.16.0</th>
<th>1.15.0</th>
<th>1.14.0</th>
<th>1.13.0</th>
<th>1.12.0</th>
<th>1.11.0</th>
<th>1.10.0</th>
<th>1.9.0</th>
<th>master(Unreleased)</th>
<tr>
<td>litmusctl-darwin-amd64 (MacOS)</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-linux-386</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-linux-amd64</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-linux-arm</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-linux-arm64</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-windows-386</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-windows-amd64</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-master.tar.gz">Click here</a></td>
</tr>
<tr>
<td>litmusctl-windows-arm</td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.15.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.14.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.13.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.12.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.11.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.10.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.9.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-master.tar.gz">Click here</a></td>
</tr>
</table>
| Platforms | Download Link |
|-----------------------|-------------------------------------------------------------------------------------------------------------|
| litmusctl-linux-amd64 | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-amd64?raw=true) |
| litmusctl-linux-arm | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-arm?raw=true) |
| litmusctl-linux-arm64 | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-arm64?raw=true) |
### Linux/MacOS
<br>
- Extract the binary
```shell
tar -zxvf litmusctl-<OS>-<ARCH>-<VERSION>.tar.gz
```
- Provide necessary permissions
```shell
$ chmod +x <filename>
chmod +x litmusctl
```
- Move the litmusctl binary to /usr/local/bin/litmusctl
- Move the litmusctl binary to /usr/local/bin/litmusctl. Note: Make sure to use root user or use sudo as a prefix
```shell
$ sudo mv <filename> /usr/local/bin/litmusctl
mv litmusctl /usr/local/bin/litmusctl
```
## Basic Commands
litmusctl CLI command has the following structure:
- You can run the litmusctl command in Linux/macOS:
```shell
$ litmusctl <command> <subcommand> <subcommand> [options and parameters]
litmusctl <command> <subcommand> <subcommand> [options and parameters]
```
To get the version of the litmusctl CLI:
### Windows
- Extract the binary from the zip using WinZip or any other extraction tool.
- You can run the litmusctl command in windows:
```shell
$ litmusctl version
litmusctl.exe <command> <subcommand> <subcommand> [options and parameters]
```
### Registering an agent
To register Litmus Chaos agent:
- To check the version of the litmusctl:
```shell
$ litmusctl agent register
litmusctl version
```
Next, you need to enter LitmusPortal details to login into your LitmusPortal account. Fields to be filled in:
## Development Guide
**LimtusPortal UI URL:** Enter the URL used to access the Litmus Portal UI.
Example, http://172.17.0.2:31696/
You can find the local setup guide for **`litmusctl`** [here](DEVELOPMENT.md).
**Username:** Enter your LitmusPortal username.
**Password:** Enter your LitmusPortal password.
```shell
🔥 Registering LitmusChaos agent
📶 Please enter LitmusChaos details --
👉 Host URL where litmus is installed: http://172.17.0.2:31696/
🤔 Username [admin]: admin
🙈 Password:
✅ Login Successful!
```
Upon successful login, there will be a list of exiting projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
```shell
✨ Projects List:
1. abc
🔎 Select Project: 1
```
Next, select the installation mode. In case the selected mode was a Cluster there will be a prerequisites check to verify ClusterRole and ClusterRoleBinding.
```shell
🔌 Installation Modes:
1. Cluster
2. Namespace
👉 Select Mode [cluster]: 1
🏃 Running prerequisites check....
🔑 clusterrole - ✅
🔑 clusterrolebinding - ✅
🌟 Sufficient permissions. Registering Agent
```
Next, enter the details of the new agent.
Fields to filled in:
**Agent Name:** Enter the name for the new agent.
**Agent Description:** Fill in details about the agent.
**Platform Name:** Enter the platform name on which this agent is hosted. For example, AWS, GCP, Rancher etc.
**Enter the namespace:** You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, LimtusPortal creates it for you.
**Enter service account:** Enter a name for your service account.
```shell
🔗 Enter the details of the agent ----
🤷 Agent Name: my-agent
📘 Agent Description: This is a new agent.
📦 Platform List
1. AWS
2. GKE
3. Openshift
4. Rancher
5. Others
🔎 Select Platform [Others]: 5
📁 Enter the namespace (new or existing) [litmus]: litmus
🔑 Enter service account [litmus]: litmus
```
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
After verification of these details, you can proceed with the registration of the agent by entering Y. The process of registration might take up to a few seconds.
```shell
📌 Summary --------------------------
Agent Name: my-agent
Agent Description: This is a new agent.
Platform Name: Others
Namespace: litmus
Service Account: litmus
Installation Mode: cluster
-------------------------------------
🤷 Do you want to continue with the above details? [Y/N]: Y
💡 Connecting agent to Litmus Portal.
🏃 Agents running!!
🚀 Agent Registration Successful!! 🎉
👉 Litmus agents can be accessed here: http://172.17.0.2:31696/targets
```
To verify, if the registration process was successful you can view the list of connected agents from the Targets section on your LitmusPortal and ensure that the connected agent is in Active State.
---

309
Usage_0.22.0.md Normal file
View File

@ -0,0 +1,309 @@
### litmusctl Syntax
`litmusctl` has a syntax to use as follows:
```shell
litmusctl [command] [TYPE] [flags]
```
* Command: refers to what you do want to perform (connect, create, get and config)
* Type: refers to the feature type you are performing a command against (chaos-delegate, project etc.)
* Flags: It takes some additional information for resource operations. For example, `--installation-mode` allows you to specify an installation mode.
Litmusctl is using the `.litmusconfig` config file to manage multiple accounts
1. If the --config flag is set, then only the given file is loaded. The flag may only be set once and no merging takes place.
2. Otherwise, the ${HOME}/.litmusconfig file is used, and no merging takes place.
Litmusctl supports both interactive and non-interactive(flag based) modes.
> Only `litmusctl connect chaos-delegate` command needs --non-interactive flag, other commands don't need this flag to be in non-interactive mode. If mandatory flags aren't passed, then litmusctl takes input in an interactive mode.
### Installation modes
Litmusctl can install a Chaos Delegate in two different modes.
* cluster mode: With this mode, the Chaos Delegate can run the chaos in any namespace. It installs appropriate cluster roles and cluster role bindings to achieve this mode. It can be enabled by passing a flag `--installation-mode=cluster`
* namespace mode: With this mode, the Chaos Delegate can run the chaos in its namespace. It installs appropriate roles and role bindings to achieve this mode. It can be enabled by passing a flag `--installation-mode=namespace`
Note: With namespace mode, the user needs to create the namespace to install the Chaos Delegate as a prerequisite.
### Minimal steps to connect a Chaos Delegate
* To setup an account with litmusctl
```shell
litmusctl config set-account --endpoint="" --username="" --password=""
```
* To create an Chaos Delegate with an existing project
> Note: To get `project-id`. Apply `litmusctl get projects`
```shell
litmusctl connect chaos-delegate --name="" --project-id="" --non-interactive
```
### Flags for `connect chaos-delegate` command
<table>
<tr>
<th>Flag</th>
<th>Short Flag</th>
<th>Type</th>
<th>Description</th>
<tr>
<td>--description</td>
<td></td>
<td>String</td>
<td>Set the Chaos Delegate description (default "---")</td>
</tr>
<tr>
<td>--name</td>
<td></td>
<td>String</td>
<td>Set the name of Chaos Delegate which should be unique</td>
</tr>
<tr>
<td>--skip-ssl</td>
<td></td>
<td>Boolean</td>
<td>Set whether Chaos Delegate will skip ssl/tls check (can be used for self-signed certs, if cert is not provided in portal) (default false)</td>
</tr>
<tr>
<td>--chaos-delegate-type</td>
<td></td>
<td>String</td>
<td>Set the chaos-delegate-type to external for external Chaos Delegates | Supported=external/internal (default "external")</td>
</tr>
<tr>
<td>--installation-mode</td>
<td></td>
<td>String</td>
<td>Set the installation mode for the kind of Chaos Delegate | Supported=cluster/namespace (default "cluster")</td>
</tr>
<tr>
<td>--kubeconfig</td>
<td>-k</td>
<td>String</td>
<td>Set to pass kubeconfig file if it is not in the default location ($HOME/.kube/config)</td>
</tr>
<tr>
<td>--namespace</td>
<td></td>
<td>String</td>
<td>Set the namespace for the Chaos Delegate installation (default "litmus")</td>
</tr>
<tr>
<td>--node-selector</td>
<td></td>
<td>String</td>
<td>Set the node-selector for Chaos Delegate components | Format: key1=value1,key2=value2)</td>
</tr>
<tr>
<td>--non-interactive</td>
<td>-n</td>
<td>String</td>
<td>Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean</td>
</tr>
<tr>
<td>--ns-exists</td>
<td></td>
<td>Boolean</td>
<td>Set the --ns-exists=false if the namespace mentioned in the --namespace flag is not existed else set it to --ns-exists=true | Note: Always set the boolean flag as --ns-exists=Boolean</td>
</tr>
<tr>
<td>--platform-name</td>
<td></td>
<td>String</td>
<td>Set the platform name. Supported- AWS/GKE/Openshift/Rancher/Others (default "Others")</td>
</tr>
<tr>
<td>--sa-exists</td>
<td></td>
<td>Boolean</td>
<td>Set the --sa-exists=false if the service-account mentioned in the --service-account flag is not existed else set it to --sa-exists=true | Note: Always set the boolean flag as --sa-exists=Boolean"</td>
</tr>
<tr>
<td>--service-account</td>
<td></td>
<td>String</td>
<td>Set the service account to be used by the Chaos Delegate (default "litmus")</td>
</tr>
<tr>
<td>--config</td>
<td></td>
<td>String</td>
<td>config file (default is $HOME/.litmusctl)</td>
</tr>
</table>
---
### Steps to create a chaos scenaro
* To setup an account with litmusctl
```shell
litmusctl config set-account --endpoint="" --username="" --password=""
```
* To create a Chaos Scenario by passing a manifest file
> Note:
> * To get `project-id`, apply `litmusctl get projects`
> * To get `chaos-delegate-id`, apply `litmusctl get chaos-delegates --project-id=""`
```shell
litmusctl create chaos-scenario -f custom-chaos-scenario.yml --project-id="" --chaos-delegate-id=""
```
---
### Additional commands
* To view the current configuration of `.litmusconfig`, type:
```shell
litmusctl config view
```
**Output:**
```
accounts:
- users:
- expires_in: "1626897027"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY4OTcwMjcsInJvbGUiOiJhZG1pbiIsInVpZCI6ImVlODZkYTljLTNmODAtNGRmMy04YzQyLTExNzlhODIzOTVhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.O_hFcIhxP4rhyUN9NEVlQmWesoWlpgHpPFL58VbJHnhvJllP5_MNPbrRMKyFvzW3hANgXK2u8437u
username: admin
- expires_in: "1626944602"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY5NDQ2MDIsInJvbGUiOiJ1c2VyIiwidWlkIjoiNjFmMDY4M2YtZWY0OC00MGE1LWIzMjgtZTU2ZDA2NjM1MTE4IiwidXNlcm5hbWUiOiJyYWoifQ.pks7xjkFdJD649RjCBwQuPF1_QMoryDWixSKx4tPAqXI75ns4sc-yGhMdbEvIZ3AJSvDaqTa47XTC6c8R
username: litmus-user
endpoint: https://preview.litmuschaos.io
apiVersion: v1
current-account: https://preview.litmuschaos.io
current-user: litmus-user
kind: Config
```
* To get an overview of the accounts available within `.litmusconfig`, use the `config get-accounts` command:
```shell
litmusctl config get-accounts
```
**Output:**
```
CURRENT ENDPOINT USERNAME EXPIRESIN
https://preview.litmuschaos.io admin 2021-07-22 01:20:27 +0530 IST
* https://preview.litmuschaos.io raj 2021-07-22 14:33:22 +0530 IST
```
* To alter the current account use the `use-account` command with the --endpoint and --username flags:
```shell
litmusctl config use-account --endpoint="" --username=""
```
* To create a project, apply the following command with the `--name` flag:
```shell
litmusctl create project --name=""
```
* To view all the projects with the user, use the `get projects` command.
```shell
litmusctl get projects
```
**Output:**
```
PROJECT ID PROJECT NAME CREATEDAT
50addd40-8767-448c-a91a-5071543a2d8e Developer Project 2021-07-21 14:38:51 +0530 IST
7a4a259a-1ae5-4204-ae83-89a8838eaec3 DevOps Project 2021-07-21 14:39:14 +0530 IST
```
* To get an overview of the Chaos Delegates available within a project, issue the following command.
```shell
litmusctl get chaos-delegates --project-id=""
```
**Output:**
```
CHAOS DELEGATE ID CHAOS DELEGATE NAME STATUS REGISTRATION
55ecc7f2-2754-43aa-8e12-6903e4c6183a chaos-delegate-1 ACTIVE REGISTERED
13dsf3d1-5324-54af-4g23-5331g5v2364f chaos-delegate-2 INACTIVE NOT REGISTERED
```
* To disconnect a Chaos Delegate, issue the following command..
```shell
litmusctl disconnect chaos-delegate <chaos-delegate-id> --project-id=""
```
**Output:**
```
🚀 Chaos Delegate successfully disconnected.
```
* To list the created Chaos Scenarios within a project, issue the following command.
```shell
litmusctl get chaos-scenarios --project-id=""
```
**Output:**
```
CHAOS SCENARIO ID CHAOS SCENARIO NAME CHAOS SCENARIO TYPE NEXT SCHEDULE CHAOS DELEGATE ID CHAOS DELEGATE NAME LAST UPDATED BY
9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Non Cron Chaos Scenario None f9799723-29f1-454c-b830-ae8ba7ee4c30 Self-Chaos-delegate admin
Showing 1 of 1 Chaos Scenarios
```
* To list all the Chaos Scenario runs within a project, issue the following command.
```shell
litmusctl get chaos-scenario-runs --project-id=""
```
**Output:**
```
CHAOS SCENARIO RUN ID STATUS RESILIENCY SCORE CHAOS SCENARIO ID CHAOS SCENARIO NAME TARGET CHAOS DELEGATE LAST RUN EXECUTED BY
8ceb712c-1ed4-40e6-adc4-01f78d281506 Running 0.00 9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Self-Chaos-Delegate June 1 2022, 10:28:02 pm admin
Showing 1 of 1 Chaos Scenario runs
```
* To describe a particular Chaos Scenario, issue the following command.
```shell
litmusctl describe chaos-scenario <chaos-scenario-id> --project-id=""
```
**Output:**
```
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
creationTimestamp: null
labels:
cluster_id: f9799723-29f1-454c-b830-ae8ba7ee4c30
subject: custom-chaos-scenario_litmus
workflow_id: 9433b48c-4ab7-4544-8dab-4a7237619e09
workflows.argoproj.io/controller-instanceid: f9799723-29f1-454c-b830-ae8ba7ee4c30
name: custom-chaos-scenario-1627980541
namespace: litmus
spec:
...
```
* To delete a particular Chaos Scenario, issue the following command.
```shell
litmusctl delete chaos-scenario <chaos-scenario-id> --project-id=""
```
**Output:**
```
🚀 Chaos Scenario successfully deleted.
```
For more information related to flags, Use `litmusctl --help`.
----

585
Usage_0.23.0.md Normal file
View File

@ -0,0 +1,585 @@
> Notes:
>
> - For litmusctl v0.23.0 or latest
### litmusctl Syntax
`litmusctl` has a syntax to use as follows:
```shell
litmusctl [command] [TYPE] [flags]
```
- Command: refers to what you do want to perform (connect, create, get and config)
- Type: refers to the feature type you are performing a command against (chaos-infra, project etc.)
- Flags: It takes some additional information for resource operations. For example, `--installation-mode` allows you to specify an installation mode.
Litmusctl is using the `.litmusconfig` config file to manage multiple accounts
1. If the --config flag is set, then only the given file is loaded. The flag may only be set once and no merging takes place.
2. Otherwise, the ${HOME}/.litmusconfig file is used, and no merging takes place.
Litmusctl supports both interactive and non-interactive(flag based) modes.
> Only `litmusctl connect chaos-infra` command needs --non-interactive flag, other commands don't need this flag to be in non-interactive mode. If mandatory flags aren't passed, then litmusctl takes input in an interactive mode.
### Steps to connect a Chaos Infrastucture
- To setup an account with litmusctl
```shell
litmusctl config set-account
```
Next, you need to enter ChaosCenter details to login into your ChaosCenter account. Fields to be filled in:
**ChaosCenter URL:** Enter the URL used to access the ChaosCenter.
> Example, https://preview.litmuschaos.io/
**Username:** Enter your ChaosCenter username.
**Password:** Enter your ChaosCenter password.
```
Host endpoint where litmus is installed: https://preview.litmuschaos.io/
Username [Default: admin]: admin
Password:
account.username/admin configured
```
- To connect a Chaos Infrastructure in a cluster mode
```shell
litmusctl connect chaos-infra
```
There will be a list of existing projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
```
Project list:
1. Project-Admin
Select a project [Range: 1-1]: 1
```
Next, select the installation mode based on your requirement by entering the sequence number indicated against it.
Litmusctl can install a Chaos Infrastructure in two different modes.
- cluster mode: With this mode, the Chaos Infrastructure can run the chaos in any namespace. It installs appropriate cluster roles and cluster role bindings to achieve this mode.
- namespace mode: With this mode, the Chaos Infrastructure can run the chaos in its namespace. It installs appropriate roles and role bindings to achieve this mode.
Note: With namespace mode, the user needs to create the namespace to install the Chaos Infrastructure as a prerequisite.
```
Installation Modes:
1. Cluster
2. Namespace
Select Mode [Default: cluster] [Range: 1-2]: 1
🏃 Running prerequisites check....
🔑 clusterrole ✅
🔑 clusterrolebinding ✅
🌟 Sufficient permissions. Installing the Chaos Infrastructure...
```
Next, enter the details of the new Chaos infrastructure.
Fields to be filled in <br />
<table>
<th>Field</th>
<th>Description</th>
<tr>
<td>Chaos Infrastructure Name:</td>
<td>Enter a name of the Chaos Infrastructure which needs to be unique across the project</td>
</tr>
<tr>
<td>Chaos Infrastructure Description:</td>
<td>Fill in details about the Chaos Infrastructure</td>
</tr>
<tr>
<td>Chaos EnvironmentID :</td>
<td>Fill in details about the Chaos Environment ID. The Environment Should be already existing.</td>
</tr>
<tr>
<td>Skip SSL verification</td>
<td>Choose whether Chaos Infrastructure will skip SSL/TLS verification</td>
</tr>
<tr>
<td>Node Selector:</td>
<td>To deploy the Chaos Infrastructure on a particular node based on the node selector labels</td>
</tr>
<tr>
<td>Platform Name:</td>
<td>Enter the platform name on which this Chaos Infrastructure is hosted. For example, AWS, GCP, Rancher etc.</td>
</tr>
<tr>
<td>Enter the namespace:</td>
<td>You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, litmusctl creates it for you</td>
</tr>
<tr>
<td>Enter service account:</td>
<td>You can either enter an existing or new service account</td>
</tr>
</table>
```
Enter the details of the Chaos Infrastructure:
Chaos Infrastructure Name: New-Chaos-infrastructure
Chaos Infrastructure Description: This is a new Chaos Infrastructure
Chaos EnvironmentID: test-infra-environment
Do you want Chaos Infrastructure to skip SSL/TLS check (Y/N) (Default: N): n
Do you want NodeSelector to be added in the Chaos Infrastructure deployments (Y/N) (Default: N): N
Platform List:
1. AWS
2. GKE
3. Openshift
4. Rancher
5. Others
Select a platform [Default: Others] [Range: 1-5]: 5
Enter the namespace (new or existing namespace) [Default: litmus]:
👍 Continuing with litmus namespace
```
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
After verification of these details, you can proceed with the connection of the Chaos infra by entering Y. The process of connection might take up to a few seconds.
```
Enter service account [Default: litmus]:
📌 Summary
Chaos Infra Name: test4
Chaos EnvironmentID: test
Chaos Infra Description:
Chaos Infra SSL/TLS Skip: false
Platform Name: Others
Namespace: litmuwrq (new)
Service Account: litmus (new)
Installation Mode: cluster
🤷 Do you want to continue with the above details? [Y/N]: Y
👍 Continuing Chaos Infrastructure connection!!
💡 Connecting Chaos Infrastructure to ChaosCenter.
🏃 Chaos Infrastructure is running!!
🚀 Chaos Infrastructure Connection Successful!! 🎉
```
#### Verify the new Chaos Infrastructure Connection\*\*
To verify, if the connection process was successful you can view the list of connected Chaos Infrastructures from the Targets section on your ChaosCenter and ensure that the connected Chaos Infrastructure is in Active State.
---
### Steps to create a Chaos Experiment
- To setup an account with litmusctl
```shell
litmusctl config set-account --endpoint="" --username="" --password=""
```
- To create a Chaos Experiment by passing a manifest file
> Note:
>
> - To get `project-id`, apply `litmusctl get projects`
> - To get `chaos-infra-id`, apply `litmusctl get chaos-infra --project-id=""`
```shell
litmusctl create chaos-experiment -f custom-chaos-experiment.yml --project-id="" --chaos-infra-id=""
```
- To Save the Chaos Experiment:
```shell
litmusctl save chaos-experiment -f custom-litmus-experiment.yaml
```
> Note:
>
> - Experiment Name can also be passed through the Manifest file
```shell
Enter the Project ID: eb7fc0a0-5878-4454-a9db-b67d283713bc
Enter the Chaos Infra ID: e7eb0386-085c-49c2-b550-8d85b58fd
Experiment Description:
🚀 Chaos Experiment/experiment-1 successfully created 🎉
```
- To Run a chaos Experiment:
```shell
litmusctl run chaos-experiment
Enter the Project ID: eb7fc0a0-5878-4454-a9db-b67d283713bc
Enter the Chaos Experiment ID: test_exp
🚀 Chaos Experiment running successfully 🎉
```
### Additional commands
- To change the ChaosCenter account's password use the `update password` command:
```shell
litmusctl update password
✓ Username: admin
✓ Old Password: ********
✓ New Password: ********
✓ Confirm Password: ********
Password updated successfully!
```
- To view the current configuration of `.litmusconfig`, type:
```shell
litmusctl config view
```
**Output:**
```
accounts:
- users:
- expires_in: "1626897027"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY4OTcwMjcsInJvbGUiOiJhZG1pbiIsInVpZCI6ImVlODZkYTljLTNmODAtNGRmMy04YzQyLTExNzlhODIzOTVhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.O_hFcIhxP4rhyUN9NEVlQmWesoWlpgHpPFL58VbJHnhvJllP5_MNPbrRMKyFvzW3hANgXK2u8437u
username: admin
- expires_in: "1626944602"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY5NDQ2MDIsInJvbGUiOiJ1c2VyIiwidWlkIjoiNjFmMDY4M2YtZWY0OC00MGE1LWIzMjgtZTU2ZDA2NjM1MTE4IiwidXNlcm5hbWUiOiJyYWoifQ.pks7xjkFdJD649RjCBwQuPF1_QMoryDWixSKx4tPAqXI75ns4sc-yGhMdbEvIZ3AJSvDaqTa47XTC6c8R
username: litmus-user
endpoint: https://preview.litmuschaos.io
serverEndpoint: https://preview.litmuschaos.io
apiVersion: v1
current-account: https://preview.litmuschaos.io
current-user: litmus-user
kind: Config
```
- To get an overview of the accounts available within `.litmusconfig`, use the `config get-accounts` command:
```shell
litmusctl config get-accounts
```
**Output:**
```
CURRENT ENDPOINT USERNAME EXPIRESIN
https://preview.litmuschaos.io admin 2021-07-22 01:20:27 +0530 IST
* https://preview.litmuschaos.io raj 2021-07-22 14:33:22 +0530 IST
```
- To alter the current account use the `use-account` command:
```shell
litmusctl config use-account
Host endpoint where litmus is installed: https://preview.litmuschaos.io
Username: admin
✅ Successfully set the current account to 'account-name' at 'URL'
```
- To create a project, apply the following command :
```shell
litmusctl create project
Enter a project name: new
Project 'project-name' created successfully!🎉
```
- To create a new Environment, apply the following command :
```shell
litmusctl create environment
Enter the Project ID: eb7fc0a0-5878-4454-a9db-b67d283713bc
Enter the Environment Name: test2
🚀 New Chaos Environment creation successful!! 🎉
```
- To delete an Environment, apply the following command :
```shell
litmusctl delete chaos-environment
Enter the Project ID: eb7fc0a0-5878-4454-a9db-b67d283713bc
Enter the Environment ID: testenv
Are you sure you want to delete this Chaos Environment? (y/n):y
🚀 Chaos Environment deleted successfully.
```
- To view all the projects with the user, use the `get projects` command.
```shell
litmusctl get projects
```
**Output:**
```
PROJECT ID PROJECT NAME CREATEDAT
50addd40-8767-448c-a91a-5071543a2d8e Developer Project 2021-07-21 14:38:51 +0530 IST
7a4a259a-1ae5-4204-ae83-89a8838eaec3 DevOps Project 2021-07-21 14:39:14 +0530 IST
Press Enter to show the next page (or type 'q' to quit): q
```
- To get an overview of the Chaos Infrastructures available within a project, issue the following command.
```shell
litmusctl get chaos-infra
Enter the Project ID: 50addd40-8767-448c-a91a-5071543a2d8e
```
**Output:**
```
CHAOS Infrastructure ID CHAOS Infrastructure NAME STATUS
55ecc7f2-2754-43aa-8e12-6903e4c6183a chaos-infra-1 ACTIVE
13dsf3d1-5324-54af-4g23-5331g5v2364f chaos-infra-2 INACTIVE
```
- To disconnect an Chaos Infrastructure, issue the following command..
```shell
litmusctl disconnect chaos-infra <chaos-infra-id> --project-id=""
```
**Output:**
```
🚀 Chaos Infrastructure successfully disconnected.
```
- To list the created Chaos Experiments within a project, issue the following command.
Using Flag :
```shell
litmusctl get chaos-experiment --project-id=""
```
Using UI :
```shell
Enter the Project ID: "project-id"
Select an output format:
table
json
yaml
```
**Output:**
```
CHAOS Experiment ID CHAOS Experiment NAME CHAOS Experiment TYPE NEXT SCHEDULE CHAOS INFRASTRUCTURE ID CHAOS Experiment NAME LAST UPDATED BY
9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-experiment-1627980541 Non Cron Chaos Experiment None f9799723-29f1-454c-b830-ae8ba7ee4c30 Self-infra-infra admin
Showing 1 of 1 Chaos Experiments
```
- To list all the Chaos Experiment runs within a project, issue the following command.
```shell
litmusctl get chaos-experiment-runs --project-id=""
```
- To list all the Chaos Experiment runs within a specific experiment, issue the following command.
```shell
litmusctl get chaos-experiment-runs --project-id="" --experiment-id=""
```
- To list the Chaos Experiment run with a specific experiment-run-id , issue the following command.
```shell
litmusctl get chaos-experiment-runs --project-id="" --experiment-run-id=""
```
**Output:**
```
CHAOS EXPERIMENT RUN ID STATUS RESILIENCY SCORE CHAOS EXPERIMENT ID CHAOS EXPERIMENT NAME TARGET CHAOS INFRA UPDATED AT UPDATED BY
8ceb712c-1ed4-40e6-adc4-01f78d281506 Running 0.00 9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-experiment-1627980541 Self-Chaos-Infra June 1 2022, 10:28:02 pm admin
Showing 1 of 1 Chaos Experiments runs
```
- To describe a particular Chaos Experiment, issue the following command.
Using Flag :
```shell
litmusctl describe chaos-experiment <chaos-experiment-id> --project-id=""
```
Using UI :
```shell
litmusctl describe chaos-experiment
Enter the Project ID: "project-id"
Enter the Chaos Experiment ID: "chaos-experiment-id"
Select an output format :
yaml
json
```
**Output:**
```
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
creationTimestamp: null
labels:
cluster_id: f9799723-29f1-454c-b830-ae8ba7ee4c30
subject: custom-chaos-experiment-1627980541
workflow_id: 9433b48c-4ab7-4544-8dab-4a7237619e09
workflows.argoproj.io/controller-instanceid: f9799723-29f1-454c-b830-ae8ba7ee4c30
name: custom-chaos-experiment-1627980541
namespace: litmus
spec:
...
```
- To delete a particular Chaos Experiment, issue the following commands.
Using Flag :
```shell
litmusctl delete chaos-experiment <chaos-experiment-id> --project-id=""
```
Using UI :
```shell
litmusctl delete chaos-experiment
Enter the Project ID: "project-id"
Enter the Chaos Experiment ID: "chaos-experiment-id"
Are you sure you want to delete this Chaos Experiment? (y/n): y
```
**Output:**
```
🚀 Chaos Experiment successfully deleted.
```
- To get the Chaos Environment, issue the following command.
Using Flag :
```shell
litmusctl get chaos-environment --project-id="" --environment-id=""
```
Using UI :
```shell
litmusctl get chaos-environment
Enter the Project ID: "project-id"
Enter the Environment ID: "chaos-experiment-id"
```
**Output:**
```
CHAOS ENVIRONMENT ID shivamenv
CHAOS ENVIRONMENT NAME shivamenv
CHAOS ENVIRONMENT Type NON_PROD
CREATED AT 55908-04-03 16:42:51 +0530 IST
CREATED BY admin
UPDATED AT 55908-04-03 16:42:51 +0530 IST
UPDATED BY admin
CHAOS INFRA IDs d99c7d14-56ef-4836-8537-423f28ceac4e
```
- To list the Chaos Environments, issue the following command.
Using Flag :
```shell
litmusctl list chaos-environments --project-id=""
```
Using UI :
```shell
litmusctl list chaos-environment
Enter the Project ID: "project-id"
```
**Output:**
```
CHAOS ENVIRONMENT ID CHAOS ENVIRONMENT NAME CREATED AT CREATED BY
testenv testenv 55985-01-15 01:42:33 +0530 IST admin
shivamnewenv shivamnewenv 55962-10-01 15:05:45 +0530 IST admin
newenvironmenttest newenvironmenttest 55912-12-01 10:55:23 +0530 IST admin
shivamenv shivamenv 55908-04-03 16:42:51 +0530 IST admin
```
---
## Flag details
<table>
<th>Flag</th>
<th>Short Flag</th>
<th>Type</th>
<th>Description</th>
<tr>
<td>--cacert</td>
<td></td>
<td>String</td>
<td>custom ca certificate used by litmusctl for communicating with portal</td>
</tr>
<tr>
<td>--config</td>
<td></td>
<td>String</td>
<td>config file (default is $HOME/.litmusctl)</td>
</tr>
<tr>
<td>--skipSSL</td>
<td></td>
<td>Boolean</td>
<td>litmusctl will skip ssl/tls verification while communicating with portal</td>
</tr>
<tr>
<td>--help</td>
<td>-h</td>
<td></td>
<td>help for litmusctl</td>
</tr>
</table>
For more information related to flags, Use `litmusctl --help`.

414
Usage_interactive.md Normal file
View File

@ -0,0 +1,414 @@
# Usage: Litmusctl v0.12.0 (Interactive mode)
> Notes:
>
> - For litmusctl v0.12.0 or latest
### litmusctl Syntax
`litmusctl` has a syntax to use as follows:
```shell
litmusctl [command] [TYPE] [flags]
```
- Command: refers to what you do want to perform (connect, create, get and config)
- Type: refers to the feature type you are performing a command against (chaos-delegate, project etc.)
- Flags: It takes some additional information for resource operations. For example, `--installation-mode` allows you to specify an installation mode.
Litmusctl is using the `.litmusconfig` config file to manage multiple accounts
1. If the --config flag is set, then only the given file is loaded. The flag may only be set once and no merging takes place.
2. Otherwise, the ${HOME}/.litmusconfig file is used, and no merging takes place.
Litmusctl supports both interactive and non-interactive(flag based) modes.
> Only `litmusctl connect chaos-delegate` command needs --non-interactive flag, other commands don't need this flag to be in non-interactive mode. If mandatory flags aren't passed, then litmusctl takes input in an interactive mode.
### Steps to connect a Chaos Delegate
- To setup an account with litmusctl
```shell
litmusctl config set-account
```
Next, you need to enter ChaosCenter details to login into your ChaosCenter account. Fields to be filled in:
**ChaosCenter URL:** Enter the URL used to access the ChaosCenter.
> Example, https://preview.litmuschaos.io/
**Username:** Enter your ChaosCenter username. <br />
**Password:** Enter your ChaosCenter password.
```
Host endpoint where litmus is installed: https://preview.litmuschaos.io/
Username [Default: admin]: admin
Password:
account.username/admin configured
```
- To connect a Chaos Delegate in a cluster mode
```shell
litmusctl connect chaos-delegate
```
There will be a list of existing projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
```
Project list:
1. Project-Admin
Select a project [Range: 1-1]: 1
```
Next, select the installation mode based on your requirement by entering the sequence number indicated against it.
Litmusctl can install a Chaos Delegate in two different modes.
- cluster mode: With this mode, the Chaos Delegate can run the chaos in any namespace. It installs appropriate cluster roles and cluster role bindings to achieve this mode.
- namespace mode: With this mode, the Chaos Delegate can run the chaos in its namespace. It installs appropriate roles and role bindings to achieve this mode.
Note: With namespace mode, the user needs to create the namespace to install the Chaos Delegate as a prerequisite.
```
Installation Modes:
1. Cluster
2. Namespace
Select Mode [Default: cluster] [Range: 1-2]: 1
🏃 Running prerequisites check....
🔑 clusterrole ✅
🔑 clusterrolebinding ✅
🌟 Sufficient permissions. Installing the Chaos Delegate...
```
Next, enter the details of the new Chaos Delegate.
Fields to be filled in <br />
<table>
<th>Field</th>
<th>Description</th>
<tr>
<td>Chaos Delegate Name:</td>
<td>Enter a name of the Chaos Delegate which needs to be unique across the project</td>
</tr>
<tr>
<td>Chaos Delegate Description:</td>
<td>Fill in details about the Chaos Delegate</td>
</tr>
<tr>
<td>Skip SSL verification</td>
<td>Choose whether Chaos Delegate will skip SSL/TLS verification</td>
</tr>
<tr>
<td>Node Selector:</td>
<td>To deploy the Chaos Delegate on a particular node based on the node selector labels</td>
</tr>
<tr>
<td>Platform Name:</td>
<td>Enter the platform name on which this Chaos Delegate is hosted. For example, AWS, GCP, Rancher etc.</td>
</tr>
<tr>
<td>Enter the namespace:</td>
<td>You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, litmusctl creates it for you</td>
</tr>
<tr>
<td>Enter service account:</td>
<td>You can either enter an existing or new service account</td>
</tr>
</table>
```
Enter the details of the Chaos Delegate
Chaos Delegate Name: New-Chaos-Delegate
Chaos Delegate Description: This is a new Chaos Delegate
Do you want Chaos Delegate to skip SSL/TLS check (Y/N) (Default: N): n
Do you want NodeSelector to be added in the Chaos Delegate deployments (Y/N) (Default: N): N
Platform List:
1. AWS
2. GKE
3. Openshift
4. Rancher
5. Others
Select a platform [Default: Others] [Range: 1-5]: 5
Enter the namespace (new or existing namespace) [Default: litmus]:
👍 Continuing with litmus namespace
```
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
After verification of these details, you can proceed with the connection of the Chaos Delegate by entering Y. The process of connection might take up to a few seconds.
```
Enter service account [Default: litmus]:
📌 Summary
Chaos Delegate Name: New-Chaos-Delegate
Chaos Delegate Description: This is a new Chaos Delegate
Chaos Delegate SSL/TLS Skip: false
Platform Name: Others
Namespace: litmus
Service Account: litmus (new)
Installation Mode: cluster
🤷 Do you want to continue with the above details? [Y/N]: Y
👍 Continuing Chaos Infrastructure connection!!
💡 Connecting Chaos Infrastructure to ChaosCenter.
🏃 Chaos Delegate is running!!
🚀 Chaos Infrastructure Connection Successful!! 🎉
```
#### Verify the new Chaos Delegate Connection\*\*
To verify, if the connection process was successful you can view the list of connected Chaos Delegates from the Targets section on your ChaosCenter and ensure that the connected Chaos Delegate is in Active State.
---
### Steps to create a Chaos Scenario
* To setup an account with litmusctl
```shell
litmusctl config set-account --endpoint="" --username="" --password=""
```
* To create a Chaos Scenario by passing a manifest file
> Note:
> * To get `project-id`, apply `litmusctl get projects`
> * To get `chaos-delegate-id`, apply `litmusctl get chaos-delegates --project-id=""`
```shell
litmusctl create chaos-scenario -f custom-chaos-scenario.yml --project-id="" --chaos-delegate-id=""
```
---
### Additional commands
- To change the ChaosCenter account's password use the `update password` command:
```shell
litmusctl update password
✓ Username: admin
✓ Old Password: ********
✓ New Password: ********
✓ Confirm Password: ********
Password updated successfully!
```
- To view the current configuration of `.litmusconfig`, type:
```shell
litmusctl config view
```
**Output:**
```
accounts:
- users:
- expires_in: "1626897027"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY4OTcwMjcsInJvbGUiOiJhZG1pbiIsInVpZCI6ImVlODZkYTljLTNmODAtNGRmMy04YzQyLTExNzlhODIzOTVhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.O_hFcIhxP4rhyUN9NEVlQmWesoWlpgHpPFL58VbJHnhvJllP5_MNPbrRMKyFvzW3hANgXK2u8437u
username: admin
- expires_in: "1626944602"
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY5NDQ2MDIsInJvbGUiOiJ1c2VyIiwidWlkIjoiNjFmMDY4M2YtZWY0OC00MGE1LWIzMjgtZTU2ZDA2NjM1MTE4IiwidXNlcm5hbWUiOiJyYWoifQ.pks7xjkFdJD649RjCBwQuPF1_QMoryDWixSKx4tPAqXI75ns4sc-yGhMdbEvIZ3AJSvDaqTa47XTC6c8R
username: litmus-user
endpoint: https://preview.litmuschaos.io
apiVersion: v1
current-account: https://preview.litmuschaos.io
current-user: litmus-user
kind: Config
```
- To get an overview of the accounts available within `.litmusconfig`, use the `config get-accounts` command:
```shell
litmusctl config get-accounts
```
**Output:**
```
CURRENT ENDPOINT USERNAME EXPIRESIN
https://preview.litmuschaos.io admin 2021-07-22 01:20:27 +0530 IST
* https://preview.litmuschaos.io raj 2021-07-22 14:33:22 +0530 IST
```
- To alter the current account use the `use-account` command:
```shell
litmusctl config use-account
Host endpoint where litmus is installed: https://preview.litmuschaos.io
Username: admin
```
- To create a project, apply the following command :
```shell
litmusctl create project
Enter a project name: new
```
- To view all the projects with the user, use the `get projects` command.
```shell
litmusctl get projects
```
**Output:**
```
PROJECT ID PROJECT NAME CREATEDAT
50addd40-8767-448c-a91a-5071543a2d8e Developer Project 2021-07-21 14:38:51 +0530 IST
7a4a259a-1ae5-4204-ae83-89a8838eaec3 DevOps Project 2021-07-21 14:39:14 +0530 IST
```
- To get an overview of the Chaos Delegates available within a project, issue the following command.
```shell
litmusctl get chaos-delegates
Enter the Project ID: 50addd40-8767-448c-a91a-5071543a2d8e
```
**Output:**
```
CHAOS DELEGATE ID CHAOS DELEGATE NAME STATUS REGISTRATION
55ecc7f2-2754-43aa-8e12-6903e4c6183a chaos-delegate-1 ACTIVE REGISTERED
13dsf3d1-5324-54af-4g23-5331g5v2364f chaos-delegate-2 INACTIVE NOT REGISTERED
```
* To disconnect an Chaos Delegate, issue the following command..
```shell
litmusctl disconnect chaos-delegate <chaos-delegate-id> --project-id=""
```
**Output:**
```
🚀 Chaos Delegate successfully disconnected.
```
* To list the created Chaos Scenarios within a project, issue the following command.
```shell
litmusctl get chaos-scenarios --project-id=""
```
**Output:**
```
CHAOS SCENARIO ID CHAOS SCENARIO NAME CHAOS SCENARIO TYPE NEXT SCHEDULE CHAOS DELEGATE ID CHAOS DELEGATE NAME LAST UPDATED BY
9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Non Cron Chaos Scenario None f9799723-29f1-454c-b830-ae8ba7ee4c30 Self-Chaos-delegate admin
Showing 1 of 1 Chaos Scenarios
```
* To list all the Chaos Scenario runs within a project, issue the following command.
```shell
litmusctl get chaos-scenario-runs --project-id=""
```
**Output:**
```
CHAOS SCENARIO RUN ID STATUS RESILIENCY SCORE CHAOS SCENARIO ID CHAOS SCENARIO NAME TARGET CHAOS DELEGATE LAST RUN EXECUTED BY
8ceb712c-1ed4-40e6-adc4-01f78d281506 Running 0.00 9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Self-Chaos-Delegate June 1 2022, 10:28:02 pm admin
Showing 1 of 1 Chaos Scenario runs
```
* To describe a particular Chaos Scenario, issue the following command.
```shell
litmusctl describe chaos-scenario <chaos-scenario-id> --project-id=""
```
**Output:**
```
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
creationTimestamp: null
labels:
cluster_id: f9799723-29f1-454c-b830-ae8ba7ee4c30
subject: custom-chaos-scenario_litmus
workflow_id: 9433b48c-4ab7-4544-8dab-4a7237619e09
workflows.argoproj.io/controller-instanceid: f9799723-29f1-454c-b830-ae8ba7ee4c30
name: custom-chaos-scenario-1627980541
namespace: litmus
spec:
...
```
* To delete a particular Chaos Scenario, issue the following command.
```shell
litmusctl delete chaos-scenario <chaos-scenario-id> --project-id=""
```
**Output:**
```
🚀 Chaos Scenario successfully deleted.
```
---
## Flag details
<table>
<th>Flag</th>
<th>Short Flag</th>
<th>Type</th>
<th>Description</th>
<tr>
<td>--cacert</td>
<td></td>
<td>String</td>
<td>custom ca certificate used by litmusctl for communicating with portal</td>
</tr>
<tr>
<td>--config</td>
<td></td>
<td>String</td>
<td>config file (default is $HOME/.litmusctl)</td>
</tr>
<tr>
<td>--skipSSL</td>
<td></td>
<td>Boolean</td>
<td>litmusctl will skip ssl/tls verification while communicating with portal</td>
</tr>
<tr>
<td>--help</td>
<td>-h</td>
<td></td>
<td>help for litmusctl</td>
</tr>
</table>
For more information related to flags, Use `litmusctl --help`.

110
Usage_v0.2.0.md Normal file
View File

@ -0,0 +1,110 @@
# Usage: Litmusctl v0.2.0
> Notes:
>
> - For litmusctl v0.3.0 or earlier
> - Compatible with Litmus 2.0.0-Beta8 or earlier
### Connecting an agent
To connect Litmus Chaos agent:
```shell
litmusctl agent connect
```
Next, you need to enter ChaosCenter details to login into your ChaosCenter account. Fields to be filled in:
**ChaosCenter UI URL:** Enter the URL used to access the ChaosCenter UI.
Example, http://172.17.0.2:31696/
**Username:** Enter your ChaosCenter username.
**Password:** Enter your ChaosCenter password.
```shell
🔥 Connecting LitmusChaos agent
📶 Please enter LitmusChaos details --
👉 Host URL where litmus is installed: http://172.17.0.2:31696/
🤔 Username [admin]: admin
🙈 Password:
✅ Login Successful!
```
Upon successful login, there will be a list of exiting projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
```shell
✨ Projects List:
1. abc
🔎 Select Project: 1
```
Next, select the installation mode. In case the selected mode was a Cluster there will be a prerequisites check to verify ClusterRole and ClusterRoleBinding.
```shell
🔌 Installation Modes:
1. Cluster
2. Namespace
👉 Select Mode [cluster]: 1
🏃 Running prerequisites check....
🔑 clusterrole - ✅
🔑 clusterrolebinding - ✅
🌟 Sufficient permissions. Connecting Agent
```
Next, enter the details of the new agent.
Fields to filled in:
**Agent Name:** Enter the name of the new agent.
**Agent Description:** Fill in details about the agent.
**Platform Name:** Enter the platform name on which this agent is hosted. For example, AWS, GCP, Rancher etc.
**Enter the namespace:** You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, ChaosCenter creates it for you.
**Enter service account:** Enter a name for your service account.
```shell
🔗 Enter the details of the agent ----
🤷 Agent Name: my-agent
📘 Agent Description: This is a new agent.
📦 Platform List
1. AWS
2. GKE
3. Openshift
4. Rancher
5. Others
🔎 Select Platform [Others]: 5
📁 Enter the namespace (new or existing) [litmus]: litmus
🔑 Enter service account [litmus]: litmus
```
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
After verification of these details, you can proceed with the connection of the agent by entering Y. The process of connection might take up to a few seconds.
```shell
📌 Summary --------------------------
Agent Name: my-agent
Agent Description: This is a new agent.
Platform Name: Others
Namespace: litmus
Service Account: litmus
Installation Mode: cluster
-------------------------------------
🤷 Do you want to continue with the above details? [Y/N]: Y
💡 Connecting agent to ChaosCenter.
🏃 Agents running!!
🚀 Agent Connection Successful!! 🎉
👉 Litmus agents can be accessed here: http://172.17.0.2:31696/targets
```
To verify, if the connection process was successful you can view the list of connected agents from the Targets section on your ChaosCenter and ensure that the connected agent is in Active State.

117
go.mod
View File

@ -1,17 +1,112 @@
module github.com/litmuschaos/litmusctl
go 1.14
go 1.21
require (
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/argoproj/argo v2.5.2+incompatible
github.com/go-resty/resty/v2 v2.5.0
github.com/imdario/mergo v0.3.11 // indirect
github.com/spf13/cobra v1.1.3
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
github.com/argoproj/argo-workflows/v3 v3.5.5
github.com/fatih/color v1.17.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
github.com/litmuschaos/chaos-operator v0.0.0-20230718113617-6819a4be12e4
github.com/litmuschaos/litmus/chaoscenter/graphql/server v0.0.0-20240115142759-7a29dc1eb1d8
github.com/manifoldco/promptui v0.9.0
github.com/mitchellh/go-homedir v1.1.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.20.4
k8s.io/apimachinery v0.20.4
k8s.io/client-go v0.20.4
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 // indirect
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/client-go v12.0.0+incompatible
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/chzyer/readline v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic v0.7.0 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
sigs.k8s.io/controller-runtime v0.11.1 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)
// Pinned to kubernetes-1.21.2
replace (
k8s.io/api => k8s.io/api v0.27.3
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.2
k8s.io/apimachinery => k8s.io/apimachinery v0.27.3
k8s.io/apiserver => k8s.io/apiserver v0.21.2
k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.2
k8s.io/client-go => k8s.io/client-go v0.27.3
k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.2
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.21.2
k8s.io/code-generator => k8s.io/code-generator v0.21.2
k8s.io/component-base => k8s.io/component-base v0.21.2
k8s.io/cri-api => k8s.io/cri-api v0.21.2
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.2
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.2
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.2
k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.2
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.2
k8s.io/kubectl => k8s.io/kubectl v0.21.2
k8s.io/kubelet => k8s.io/kubelet v0.21.2
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.2
k8s.io/metrics => k8s.io/metrics v0.21.2
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.2
)
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm

1823
go.sum

File diff suppressed because it is too large Load Diff

20
main.go
View File

@ -1,11 +1,11 @@
/*
Copyright © 2020 NAME HERE <EMAIL ADDRESS>
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@ -15,8 +15,20 @@ limitations under the License.
*/
package main
import cmd "github.com/litmuschaos/litmusctl/pkg/cmd/litmusctl"
import (
"fmt"
"os"
rootCmd "github.com/litmuschaos/litmusctl/pkg/cmd/root"
)
var CLIVersion string
func main() {
cmd.Execute()
err := os.Setenv("CLIVersion", CLIVersion)
if err != nil {
fmt.Println("Failed to fetched CLIVersion")
}
rootCmd.Execute()
}

66
pkg/apis/auth.go Normal file
View File

@ -0,0 +1,66 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/litmuschaos/litmusctl/pkg/types"
)
type Payload struct {
Username string `json:"username"`
Password string `json:"password"`
}
func Auth(input types.AuthInput) (types.AuthResponse, error) {
payloadBytes, err := json.Marshal(Payload{
Username: input.Username,
Password: input.Password,
})
if err != nil {
return types.AuthResponse{}, err
}
// Sending token as empty because auth server doesn't need Authorization token to validate.
resp, err := SendRequest(SendRequestParams{input.Endpoint + utils.AuthAPIPath + "/login", ""}, payloadBytes, string(types.Post))
if err != nil {
return types.AuthResponse{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return types.AuthResponse{}, err
}
if resp.StatusCode == http.StatusOK {
var authResponse types.AuthResponse
err = json.Unmarshal(bodyBytes, &authResponse)
if err != nil {
return types.AuthResponse{}, err
}
return authResponse, nil
} else {
return types.AuthResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}

View File

@ -0,0 +1,183 @@
package environment
import (
"encoding/json"
"errors"
"io"
"net/http"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
// CreateEnvironment connects the Infra with the given details
func CreateEnvironment(pid string, request models.CreateEnvironmentRequest, cred types.Credentials) (CreateEnvironmentResponse, error) {
var gqlReq CreateEnvironmentGQLRequest
gqlReq.Query = CreateEnvironmentQuery
gqlReq.Variables.ProjectId = pid
gqlReq.Variables.Request = request
query, err := json.Marshal(gqlReq)
if err != nil {
return CreateEnvironmentResponse{}, errors.New("Error in Creating Chaos Infrastructure: " + err.Error())
}
resp, err := apis.SendRequest(apis.SendRequestParams{Endpoint: cred.ServerEndpoint + utils.GQLAPIPath, Token: cred.Token}, query, string(types.Post))
if err != nil {
return CreateEnvironmentResponse{}, errors.New("Error in Creating Chaos Infrastructure: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return CreateEnvironmentResponse{}, errors.New("Error in Creating Chaos Environment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var connectEnvironment CreateEnvironmentResponse
err = json.Unmarshal(bodyBytes, &connectEnvironment)
if err != nil {
return CreateEnvironmentResponse{}, errors.New("Error in Creating Chaos Environment: " + err.Error())
}
if len(connectEnvironment.Errors) > 0 {
return CreateEnvironmentResponse{}, errors.New(connectEnvironment.Errors[0].Message)
}
return connectEnvironment, nil
} else {
return CreateEnvironmentResponse{}, err
}
}
func ListChaosEnvironments(pid string, cred types.Credentials) (ListEnvironmentData, error) {
var err error
var gqlReq CreateEnvironmentListGQLRequest
gqlReq.Query = ListEnvironmentQuery
gqlReq.Variables.Request = models.ListEnvironmentRequest{}
gqlReq.Variables.ProjectID = pid
query, err := json.Marshal(gqlReq)
if err != nil {
return ListEnvironmentData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return ListEnvironmentData{}, errors.New("Error in Getting Chaos Environment List: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ListEnvironmentData{}, errors.New("Error in Getting Chaos Environment List: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var listEnvironment ListEnvironmentData
err = json.Unmarshal(bodyBytes, &listEnvironment)
if err != nil {
return ListEnvironmentData{}, errors.New("Error in Getting Chaos Environment List: " + err.Error())
}
if len(listEnvironment.Errors) > 0 {
return ListEnvironmentData{}, errors.New(listEnvironment.Errors[0].Message)
}
return listEnvironment, nil
} else {
return ListEnvironmentData{}, err
}
}
func GetChaosEnvironment(pid string, envid string, cred types.Credentials) (GetEnvironmentData, error) {
var err error
var gqlReq CreateEnvironmentGetGQLRequest
gqlReq.Query = GetEnvironmentQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.EnvironmentID = envid
query, err := json.Marshal(gqlReq)
if err != nil {
return GetEnvironmentData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return GetEnvironmentData{}, errors.New("Error in Getting Chaos Environment: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return GetEnvironmentData{}, errors.New("Error in Getting Chaos Environment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var getEnvironment GetEnvironmentData
err = json.Unmarshal(bodyBytes, &getEnvironment)
if err != nil {
return GetEnvironmentData{}, errors.New("Error in Getting Chaos Environment: " + err.Error())
}
if len(getEnvironment.Errors) > 0 {
return GetEnvironmentData{}, errors.New(getEnvironment.Errors[0].Message)
}
return getEnvironment, nil
} else {
return GetEnvironmentData{}, err
}
}
func DeleteEnvironment(pid string, envid string, cred types.Credentials) (DeleteChaosEnvironmentData, error) {
var err error
var gqlReq CreateEnvironmentDeleteGQLRequest
gqlReq.Query = DeleteEnvironmentQuery
gqlReq.Variables.EnvironmentID = envid
gqlReq.Variables.ProjectID = pid
query, err := json.Marshal(gqlReq)
if err != nil {
return DeleteChaosEnvironmentData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DeleteChaosEnvironmentData{}, errors.New("Error in Deleting Chaos Environment: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DeleteChaosEnvironmentData{}, errors.New("Error in Deleting Chaos Environment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var deletedEnvironment DeleteChaosEnvironmentData
err = json.Unmarshal(bodyBytes, &deletedEnvironment)
if err != nil {
return DeleteChaosEnvironmentData{}, err
}
if len(deletedEnvironment.Errors) > 0 {
return DeleteChaosEnvironmentData{}, errors.New(deletedEnvironment.Errors[0].Message)
}
return deletedEnvironment, nil
} else {
return DeleteChaosEnvironmentData{}, errors.New("Error while deleting the Chaos Environment")
}
}

View File

@ -0,0 +1,57 @@
package environment
const (
CreateEnvironmentQuery = `mutation createEnvironment($projectID: ID!, $request: CreateEnvironmentRequest!) {
createEnvironment(
projectID: $projectID
request: $request
) {
environmentID
name
}
}
`
GetEnvironmentQuery = `query getEnvironment($projectID: ID!, $environmentID : ID!) {
getEnvironment(projectID: $projectID,environmentID: $environmentID){
environmentID
name
createdAt
updatedAt
createdBy{
username
}
updatedBy{
username
}
infraIDs
type
tags
}
}`
ListEnvironmentQuery = `query listEnvironments($projectID: ID!, $request: ListEnvironmentRequest) {
listEnvironments(projectID: $projectID,request: $request){
environments {
environmentID
name
createdAt
updatedAt
createdBy{
username
}
updatedBy{
username
}
infraIDs
type
}
}
}`
DeleteEnvironmentQuery = `mutation deleteEnvironment($projectID: ID!, $environmentID: ID!) {
deleteEnvironment(
projectID: $projectID
environmentID: $environmentID
)
}`
)

View File

@ -0,0 +1,83 @@
package environment
import model "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
type CreateEnvironmentGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectId string `json:"projectID"`
Request model.CreateEnvironmentRequest `json:"request"`
} `json:"variables"`
}
type CreateEnvironmentResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data CreateEnvironmentData `json:"data"`
}
type CreateEnvironmentData struct {
EnvironmentDetails model.Environment `json:"createEnvironment"`
}
type GetEnvironmentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data GetEnvironment `json:"data"`
}
type GetEnvironment struct {
EnvironmentDetails model.Environment `json:"getEnvironment"`
}
type CreateEnvironmentGetGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
EnvironmentID string `json:"environmentID"`
}
}
type ListEnvironmentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data EnvironmentsList `json:"data"`
}
type EnvironmentsList struct {
ListEnvironmentDetails model.ListEnvironmentResponse `json:"listEnvironments"`
}
type CreateEnvironmentListGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
Request model.ListEnvironmentRequest `json:"request"`
}
}
type CreateEnvironmentDeleteGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
EnvironmentID string `json:"environmentID"`
}
}
type DeleteChaosEnvironmentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DeleteChaosEnvironmentDetails `json:"data"`
}
type DeleteChaosEnvironmentDetails struct {
DeleteChaosEnvironment string `json:"deleteChaosExperiment"`
}

View File

@ -0,0 +1,348 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package experiment
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
// CreateExperiment sends GraphQL API request for creating a Experiment
func CreateExperiment(pid string, requestData model.SaveChaosExperimentRequest, cred types.Credentials) (RunExperimentResponse, error) {
// Query to Save the Experiment
var gqlReq SaveChaosExperimentGraphQLRequest
gqlReq.Query = SaveExperimentQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.SaveChaosExperimentRequest = requestData
query, err := json.Marshal(gqlReq)
if err != nil {
return RunExperimentResponse{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return RunExperimentResponse{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return RunExperimentResponse{}, errors.New("Error in saving Chaos Experiment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var savedExperiment SaveExperimentData
err = json.Unmarshal(bodyBytes, &savedExperiment)
if err != nil {
return RunExperimentResponse{}, errors.New("Error in saving Chaos Experiment: " + err.Error())
}
// Errors present
if len(savedExperiment.Errors) > 0 {
return RunExperimentResponse{}, errors.New(savedExperiment.Errors[0].Message)
}
} else {
return RunExperimentResponse{}, errors.New("error in saving Chaos Experiment")
}
// Query to Run the Chaos Experiment
runQuery := `{"query":"mutation{ \n runChaosExperiment(experimentID: \"` + requestData.ID + `\", projectID: \"` + pid + `\"){\n notifyID \n}}"}`
resp, err = apis.SendRequest(apis.SendRequestParams{Endpoint: cred.ServerEndpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(runQuery), string(types.Post))
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
bodyBytes, err = io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var runExperiment RunExperimentResponse
err = json.Unmarshal(bodyBytes, &runExperiment)
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
if len(runExperiment.Errors) > 0 {
return RunExperimentResponse{}, errors.New(runExperiment.Errors[0].Message)
}
return runExperiment, nil
} else {
return RunExperimentResponse{}, err
}
}
func SaveExperiment(pid string, requestData model.SaveChaosExperimentRequest, cred types.Credentials) (SaveExperimentData, error) {
// Query to Save the Experiment
var gqlReq SaveChaosExperimentGraphQLRequest
gqlReq.Query = SaveExperimentQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.SaveChaosExperimentRequest = requestData
query, err := json.Marshal(gqlReq)
if err != nil {
return SaveExperimentData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return SaveExperimentData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return SaveExperimentData{}, errors.New("Error in saving Chaos Experiment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var savedExperiment SaveExperimentData
err = json.Unmarshal(bodyBytes, &savedExperiment)
if err != nil {
return SaveExperimentData{}, errors.New("Error in saving Chaos Experiment: " + err.Error())
}
// Errors present
if len(savedExperiment.Errors) > 0 {
return SaveExperimentData{}, errors.New(savedExperiment.Errors[0].Message)
}
return savedExperiment, nil
} else {
return SaveExperimentData{}, errors.New("error in saving Chaos Experiment")
}
}
func RunExperiment(pid string, eid string, cred types.Credentials) (RunExperimentResponse, error) {
var err error
runQuery := `{"query":"mutation{ \n runChaosExperiment(experimentID: \"` + eid + `\", projectID: \"` + pid + `\"){\n notifyID \n}}"}`
resp, err := apis.SendRequest(apis.SendRequestParams{Endpoint: cred.ServerEndpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(runQuery), string(types.Post))
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var runExperiment RunExperimentResponse
err = json.Unmarshal(bodyBytes, &runExperiment)
if err != nil {
return RunExperimentResponse{}, errors.New("Error in Running Chaos Experiment: " + err.Error())
}
if len(runExperiment.Errors) > 0 {
return RunExperimentResponse{}, errors.New(runExperiment.Errors[0].Message)
}
return runExperiment, nil
} else {
return RunExperimentResponse{}, err
}
}
// GetExperimentList sends GraphQL API request for fetching a list of experiments.
func GetExperimentList(pid string, in model.ListExperimentRequest, cred types.Credentials) (ExperimentListData, error) {
var gqlReq GetChaosExperimentsGraphQLRequest
var err error
gqlReq.Query = ListExperimentQuery
gqlReq.Variables.GetChaosExperimentRequest = in
gqlReq.Variables.ProjectID = pid
query, err := json.Marshal(gqlReq)
if err != nil {
return ExperimentListData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return ExperimentListData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ExperimentListData{}, err
}
if resp.StatusCode == http.StatusOK {
var experimentList ExperimentListData
err = json.Unmarshal(bodyBytes, &experimentList)
if err != nil {
return ExperimentListData{}, err
}
if len(experimentList.Errors) > 0 {
return ExperimentListData{}, errors.New(experimentList.Errors[0].Message)
}
return experimentList, nil
} else {
return ExperimentListData{}, errors.New("Error while fetching the Chaos Experiments")
}
}
// GetExperimentRunsList sends GraphQL API request for fetching a list of experiment runs.
func GetExperimentRunsList(pid string, in model.ListExperimentRunRequest, cred types.Credentials) (ExperimentRunListData, error) {
var gqlReq GetChaosExperimentRunGraphQLRequest
var err error
gqlReq.Query = ListExperimentRunsQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.GetChaosExperimentRunRequest = in
query, err := json.Marshal(gqlReq)
if err != nil {
return ExperimentRunListData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return ExperimentRunListData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ExperimentRunListData{}, err
}
if resp.StatusCode == http.StatusOK {
var experimentRunList ExperimentRunListData
err = json.Unmarshal(bodyBytes, &experimentRunList)
if err != nil {
return ExperimentRunListData{}, err
}
if len(experimentRunList.Errors) > 0 {
return ExperimentRunListData{}, errors.New(experimentRunList.Errors[0].Message)
}
return experimentRunList, nil
} else {
return ExperimentRunListData{}, errors.New("Error while fetching the Chaos Experiment runs")
}
}
// DeleteChaosExperiment sends GraphQL API request for deleting a given Chaos Experiment.
func DeleteChaosExperiment(projectID string, experimentID *string, cred types.Credentials) (DeleteChaosExperimentData, error) {
var gqlReq DeleteChaosExperimentGraphQLRequest
var err error
gqlReq.Query = DeleteExperimentQuery
gqlReq.Variables.ProjectID = projectID
gqlReq.Variables.ExperimentID = experimentID
//var experiment_run_id string = ""
//gqlReq.Variables.ExperimentRunID = &experiment_run_id
query, err := json.Marshal(gqlReq)
if err != nil {
return DeleteChaosExperimentData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DeleteChaosExperimentData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DeleteChaosExperimentData{}, err
}
if resp.StatusCode == http.StatusOK {
var deletedExperiment DeleteChaosExperimentData
err = json.Unmarshal(bodyBytes, &deletedExperiment)
if err != nil {
return DeleteChaosExperimentData{}, err
}
if len(deletedExperiment.Errors) > 0 {
return DeleteChaosExperimentData{}, errors.New(deletedExperiment.Errors[0].Message)
}
return deletedExperiment, nil
} else {
return DeleteChaosExperimentData{}, errors.New("Error while deleting the Chaos Experiment")
}
}

View File

@ -0,0 +1,54 @@
package experiment
const (
SaveExperimentQuery = `mutation saveChaosExperiment($projectID: ID!, $request: SaveChaosExperimentRequest!) {
saveChaosExperiment(projectID: $projectID, request: $request)
}`
ListExperimentQuery = `query listExperiment($projectID: ID!, $request: ListExperimentRequest!) {
listExperiment(projectID: $projectID, request: $request) {
totalNoOfExperiments
experiments {
experimentID
experimentManifest
cronSyntax
name
infra {
name
infraID
}
updatedBy{
username
email
}
}
}
}`
ListExperimentRunsQuery = `query listExperimentRuns($projectID: ID!, $request: ListExperimentRunRequest!) {
listExperimentRun(projectID: $projectID, request: $request) {
totalNoOfExperimentRuns
experimentRuns {
experimentRunID
experimentID
experimentName
infra {
name
}
updatedAt
updatedBy{
username
}
phase
resiliencyScore
}
}
}`
DeleteExperimentQuery = `mutation deleteChaosExperiment($projectID: ID!, $experimentID: String!, $experimentRunID: String) {
deleteChaosExperiment(
projectID: $projectID
experimentID: $experimentID
experimentRunID: $experimentRunID
)
}`
)

View File

@ -0,0 +1,96 @@
package experiment
import "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
type SaveExperimentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data SavedExperimentDetails `json:"data"`
}
type SavedExperimentDetails struct {
Message string `json:"saveChaosExperiment"`
}
type SaveChaosExperimentGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
SaveChaosExperimentRequest model.SaveChaosExperimentRequest `json:"request"`
} `json:"variables"`
}
type RunExperimentResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data RunExperimentData `json:"data"`
}
type RunExperimentData struct {
RunExperimentDetails model.RunChaosExperimentResponse `json:"runChaosExperiment"`
}
type ExperimentListData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data ExperimentList `json:"data"`
}
type ExperimentList struct {
ListExperimentDetails model.ListExperimentResponse `json:"listExperiment"`
}
type GetChaosExperimentsGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
GetChaosExperimentRequest model.ListExperimentRequest `json:"request"`
ProjectID string `json:"projectID"`
} `json:"variables"`
}
type ExperimentRunListData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data ExperimentRunsList `json:"data"`
}
type ExperimentRunsList struct {
ListExperimentRunDetails model.ListExperimentRunResponse `json:"listExperimentRun"`
}
type GetChaosExperimentRunGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
GetChaosExperimentRunRequest model.ListExperimentRunRequest `json:"request"`
} `json:"variables"`
}
type DeleteChaosExperimentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DeleteChaosExperimentDetails `json:"data"`
}
type DeleteChaosExperimentDetails struct {
IsDeleted bool `json:"deleteChaosExperiment"`
}
type DeleteChaosExperimentGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
ExperimentID *string `json:"experimentID"`
ExperimentRunID *string `json:"experimentRunID"`
} `json:"variables"`
}

View File

@ -0,0 +1,234 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a1 copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package infrastructure
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/types"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
// GetInfraList lists the Chaos Infrastructure connected to the specified project
func GetInfraList(c types.Credentials, pid string, request models.ListInfraRequest) (InfraData, error) {
var gplReq ListInfraGraphQLRequest
gplReq.Query = ListInfraQuery
gplReq.Variables.ProjectID = pid
gplReq.Variables.ListInfraRequest = request
query, err := json.Marshal(gplReq)
if err != nil {
return InfraData{}, err
}
resp, err := apis.SendRequest(apis.SendRequestParams{Endpoint: c.ServerEndpoint + utils.GQLAPIPath, Token: c.Token}, query, string(types.Post))
if err != nil {
return InfraData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return InfraData{}, err
}
if resp.StatusCode == http.StatusOK {
var Infra InfraData
err = json.Unmarshal(bodyBytes, &Infra)
if err != nil {
return InfraData{}, err
}
if len(Infra.Errors) > 0 {
return InfraData{}, errors.New(Infra.Errors[0].Message)
}
return Infra, nil
} else {
return InfraData{}, fmt.Errorf("error getting detais from server")
}
}
// ConnectInfra connects the Infra with the given details
func ConnectInfra(infra types.Infra, cred types.Credentials) (InfraConnectionData, error) {
var gqlReq RegisterInfraGqlRequest
gqlReq.Query = RegisterInfraQuery
gqlReq.Variables.ProjectId = infra.ProjectId
gqlReq.Variables.RegisterInfraRequest = CreateRegisterInfraRequest(infra)
if infra.NodeSelector != "" {
gqlReq.Variables.RegisterInfraRequest.NodeSelector = &infra.NodeSelector
}
if infra.Tolerations != "" {
var toleration []*models.Toleration
err := json.Unmarshal([]byte(infra.Tolerations), &toleration)
utils.PrintError(err)
gqlReq.Variables.RegisterInfraRequest.Tolerations = toleration
}
if infra.NodeSelector != "" && infra.Tolerations != "" {
gqlReq.Variables.RegisterInfraRequest.NodeSelector = &infra.NodeSelector
var toleration []*models.Toleration
err := json.Unmarshal([]byte(infra.Tolerations), &toleration)
utils.PrintError(err)
gqlReq.Variables.RegisterInfraRequest.Tolerations = toleration
}
query, err := json.Marshal(gqlReq)
if err != nil {
return InfraConnectionData{}, errors.New("Error in registering Chaos Infrastructure: " + err.Error())
}
resp, err := apis.SendRequest(apis.SendRequestParams{Endpoint: cred.ServerEndpoint + utils.GQLAPIPath, Token: cred.Token}, query, string(types.Post))
if err != nil {
return InfraConnectionData{}, errors.New("Error in registering Chaos Infrastructure: " + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return InfraConnectionData{}, errors.New("Error in registering Chaos Infrastructure: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var connectInfra InfraConnectionData
err = json.Unmarshal(bodyBytes, &connectInfra)
if err != nil {
return InfraConnectionData{}, errors.New("Error in registering Chaos Infrastructure: " + err.Error())
}
if len(connectInfra.Errors) > 0 {
return InfraConnectionData{}, errors.New(connectInfra.Errors[0].Message)
}
return connectInfra, nil
} else {
return InfraConnectionData{}, err
}
}
func CreateRegisterInfraRequest(infra types.Infra) (request models.RegisterInfraRequest) {
return models.RegisterInfraRequest{
Name: infra.InfraName,
InfraScope: infra.Mode,
Description: &infra.Description,
PlatformName: infra.PlatformName,
EnvironmentID: infra.EnvironmentID,
InfrastructureType: models.InfrastructureTypeKubernetes,
InfraNamespace: &infra.Namespace,
ServiceAccount: &infra.ServiceAccount,
InfraNsExists: &infra.NsExists,
InfraSaExists: &infra.SAExists,
SkipSsl: &infra.SkipSSL,
}
}
// DisconnectInfra sends GraphQL API request for disconnecting Chaos Infra(s).
func DisconnectInfra(projectID string, infraID string, cred types.Credentials) (DisconnectInfraData, error) {
var gqlReq DisconnectInfraGraphQLRequest
var err error
gqlReq.Query = DisconnectInfraQuery
gqlReq.Variables.ProjectID = projectID
gqlReq.Variables.InfraID = infraID
query, err := json.Marshal(gqlReq)
if err != nil {
return DisconnectInfraData{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DisconnectInfraData{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DisconnectInfraData{}, err
}
if resp.StatusCode == http.StatusOK {
var disconnectInfraData DisconnectInfraData
err = json.Unmarshal(bodyBytes, &disconnectInfraData)
if err != nil {
return DisconnectInfraData{}, err
}
if len(disconnectInfraData.Errors) > 0 {
return DisconnectInfraData{}, errors.New(disconnectInfraData.Errors[0].Message)
}
return disconnectInfraData, nil
} else {
return DisconnectInfraData{}, err
}
}
func GetServerVersion(endpoint string) (ServerVersionResponse, error) {
var gqlReq ServerVersionRequest
var err error
gqlReq.Query = ServerVersionQuery
query, err := json.Marshal(gqlReq)
if err != nil {
return ServerVersionResponse{}, err
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: endpoint + utils.GQLAPIPath,
},
query,
string(types.Post),
)
if err != nil {
return ServerVersionResponse{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ServerVersionResponse{}, err
}
if resp.StatusCode == http.StatusOK {
var version ServerVersionResponse
err = json.Unmarshal(bodyBytes, &version)
if err != nil {
return ServerVersionResponse{}, err
}
if len(version.Errors) > 0 {
return ServerVersionResponse{}, errors.New(version.Errors[0].Message)
}
return version, nil
} else {
return ServerVersionResponse{}, errors.New(resp.Status)
}
}

View File

@ -0,0 +1,41 @@
package infrastructure
const (
DisconnectInfraQuery = `mutation deleteInfra($projectID: ID!, $infraID: String!) {
deleteInfra(
projectID: $projectID
infraID: $infraID
)
}`
RegisterInfraQuery = `mutation registerInfra($projectID: ID!, $request: RegisterInfraRequest!) {
registerInfra(
projectID: $projectID
request: $request
) {
infraID
name
token
manifest
}
}
`
ListInfraQuery = `query listInfras($projectID: ID!, $request: ListInfraRequest!){
listInfras(projectID: $projectID, request: $request){
totalNoOfInfras
infras {
infraID
name
isActive
environmentID
}
}
}`
ServerVersionQuery = `query getServerVersion{
getServerVersion{
key
value
}
}`
)

View File

@ -0,0 +1,84 @@
package infrastructure
import models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
type InfraData struct {
Data InfraList `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type InfraList struct {
ListInfraDetails models.ListInfraResponse `json:"listInfras"`
}
type ListInfraGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
ListInfraRequest models.ListInfraRequest `json:"request"`
} `json:"variables"`
}
type Errors struct {
Message string `json:"message"`
Path []string `json:"path"`
}
type RegisterInfraGqlRequest struct {
Query string `json:"query"`
Variables struct {
ProjectId string `json:"projectID"`
RegisterInfraRequest models.RegisterInfraRequest `json:"request"`
} `json:"variables"`
}
type InfraConnectionData struct {
Data RegisterInfra `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type RegisterInfra struct {
RegisterInfraDetails models.RegisterInfraResponse `json:"registerInfra"`
}
type DisconnectInfraData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DisconnectInfraDetails `json:"data"`
}
type DisconnectInfraDetails struct {
Message string `json:"deleteInfra"`
}
type DisconnectInfraGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
InfraID string `json:"infraID"`
} `json:"variables"`
}
type ServerVersionRequest struct {
Query string `json:"query"`
}
type ServerVersionResponse struct {
Data ServerVersionData `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type ServerVersionData struct {
GetServerVersion models.ServerVersionResponse `json:"getServerVersion"`
}

197
pkg/apis/probe/probe.go Normal file
View File

@ -0,0 +1,197 @@
package probe
import (
"encoding/json"
"errors"
"io"
"net/http"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
func GetProbeRequest(pid string, probeID string, cred types.Credentials) (GetProbeResponse, error) {
var gqlReq GetProbeGQLRequest
gqlReq.Query = GetProbeQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.ProbeName = probeID
query, err := json.Marshal(gqlReq)
if err != nil {
return GetProbeResponse{}, errors.New("Error in getting requested probe" + err.Error())
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return GetProbeResponse{}, errors.New("Error in getting requested probe" + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return GetProbeResponse{}, errors.New("Error in getting requested probe" + err.Error())
}
if resp.StatusCode == http.StatusOK {
var getProbeResponse GetProbeResponse
err = json.Unmarshal(bodyBytes, &getProbeResponse)
if err != nil {
return GetProbeResponse{}, errors.New("Error in getting requested probe" + err.Error())
}
if len(getProbeResponse.Errors) > 0 {
return GetProbeResponse{}, errors.New(getProbeResponse.Errors[0].Message)
}
return getProbeResponse, nil
} else {
return GetProbeResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
func ListProbeRequest(pid string, probetypes []*models.ProbeType, cred types.Credentials) (ListProbeResponse, error) {
var gqlReq ListProbeGQLRequest
gqlReq.Query = ListProbeQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.Filter = models.ProbeFilterInput{
Type: probetypes,
}
query, err := json.Marshal(gqlReq)
if err != nil {
return ListProbeResponse{}, errors.New("Error in listing probes" + err.Error())
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return ListProbeResponse{}, errors.New("Error in listing probes" + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ListProbeResponse{}, errors.New("Error in listing probes" + err.Error())
}
if resp.StatusCode == http.StatusOK {
var listProbeResponse ListProbeResponse
err = json.Unmarshal(bodyBytes, &listProbeResponse)
if err != nil {
return ListProbeResponse{}, errors.New("Error in listing probes" + err.Error())
}
if len(listProbeResponse.Errors) > 0 {
return ListProbeResponse{}, errors.New(listProbeResponse.Errors[0].Message)
}
return listProbeResponse, nil
} else {
return ListProbeResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
func DeleteProbeRequest(pid string, probeid string, cred types.Credentials) (DeleteProbeResponse, error) {
var gqlReq DeleteProbeGQLRequest
gqlReq.Query = DeleteProbeQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.ProbeName = probeid
query, err := json.Marshal(gqlReq)
if err != nil {
return DeleteProbeResponse{}, errors.New("Error in deleting probe" + err.Error())
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DeleteProbeResponse{}, errors.New("Error in deleting probe" + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DeleteProbeResponse{}, errors.New("Error in deleting probe" + err.Error())
}
if resp.StatusCode == http.StatusOK {
var deleteProbeResponse DeleteProbeResponse
err = json.Unmarshal(bodyBytes, &deleteProbeResponse)
if err != nil {
return DeleteProbeResponse{}, errors.New("Error in deleting probe" + err.Error())
}
if len(deleteProbeResponse.Errors) > 0 {
return DeleteProbeResponse{}, errors.New(deleteProbeResponse.Errors[0].Message)
}
return deleteProbeResponse, nil
} else {
return DeleteProbeResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
func GetProbeYAMLRequest(pid string, request models.GetProbeYAMLRequest, cred types.Credentials) (GetProbeYAMLResponse, error) {
var gqlReq GetProbeYAMLGQLRequest
gqlReq.Query = GetProbeYAMLQuery
gqlReq.Variables.ProjectID = pid
gqlReq.Variables.Request = request
query, err := json.Marshal(gqlReq)
if err != nil {
return GetProbeYAMLResponse{}, errors.New("Error in getting probe details" + err.Error())
}
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: cred.ServerEndpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return GetProbeYAMLResponse{}, errors.New("Error in getting probe details" + err.Error())
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return GetProbeYAMLResponse{}, errors.New("Error in getting probe details" + err.Error())
}
if resp.StatusCode == http.StatusOK {
var getProbeYAMLResponse GetProbeYAMLResponse
err = json.Unmarshal(bodyBytes, &getProbeYAMLResponse)
if err != nil {
return GetProbeYAMLResponse{}, errors.New("Error in getting probes details" + err.Error())
}
if len(getProbeYAMLResponse.Errors) > 0 {
return GetProbeYAMLResponse{}, errors.New(getProbeYAMLResponse.Errors[0].Message)
}
return getProbeYAMLResponse, nil
} else {
return GetProbeYAMLResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}

82
pkg/apis/probe/query.go Normal file
View File

@ -0,0 +1,82 @@
package probe
const (
ListProbeQuery = `query ListProbes($projectID: ID!, $probeNames: [ID!], $filter: ProbeFilterInput) {
listProbes(projectID: $projectID, probeNames: $probeNames, filter: $filter) {
name
type
createdAt
createdBy{
username
}
}
}
`
GetProbeQuery = `query getProbe($projectID: ID!, $probeName: ID!) {
getProbe(projectID: $projectID, probeName: $probeName) {
name
description
type
infrastructureType
kubernetesHTTPProperties{
probeTimeout
interval
retry
attempt
probePollingInterval
initialDelay
evaluationTimeout
stopOnFailure
}
kubernetesCMDProperties{
probeTimeout
interval
retry
attempt
probePollingInterval
initialDelay
evaluationTimeout
stopOnFailure
}
k8sProperties {
probeTimeout
interval
retry
attempt
probePollingInterval
initialDelay
evaluationTimeout
stopOnFailure
}
promProperties {
probeTimeout
interval
retry
attempt
probePollingInterval
initialDelay
evaluationTimeout
stopOnFailure
}
createdAt
createdBy{
username
}
updatedAt
updatedBy{
username
}
tags
}
}
`
GetProbeYAMLQuery = `query getProbeYAML($projectID: ID!, $request: GetProbeYAMLRequest!) {
getProbeYAML(projectID: $projectID, request: $request)
}
`
DeleteProbeQuery = `mutation deleteProbe($probeName: ID!, $projectID: ID!) {
deleteProbe(probeName: $probeName, projectID: $projectID)
}
`
)

82
pkg/apis/probe/types.go Normal file
View File

@ -0,0 +1,82 @@
package probe
import model "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
type GetProbeGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
ProbeName string `json:"probeName"`
} `json:"variables"`
}
type GetProbeResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data GetProbeResponseData `json:"data"`
}
type GetProbeResponseData struct {
GetProbe model.Probe `json:"getProbe"`
}
type ListProbeGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
Filter model.ProbeFilterInput `json:"filter"`
} `json:"variables"`
}
type ListProbeResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data ListProbeResponseData `json:"data"`
}
type ListProbeResponseData struct {
Probes []model.Probe `json:"listProbes"`
}
type DeleteProbeGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProbeName string `json:"probeName"`
ProjectID string `json:"projectID"`
} `json:"variables"`
}
type DeleteProbeResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DeleteProbeResponseData `json:"data"`
}
type DeleteProbeResponseData struct {
DeleteProbe bool `json:"deleteProbe"`
}
type GetProbeYAMLGQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
Request model.GetProbeYAMLRequest `json:"request"`
} `json:"variables"`
}
type GetProbeYAMLResponse struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data GetProbeYAMLResponseData `json:"data"`
}
type GetProbeYAMLResponseData struct {
GetProbeYAML string `json:"getProbeYAML"`
}

189
pkg/apis/project.go Normal file
View File

@ -0,0 +1,189 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/golang-jwt/jwt"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/litmuschaos/litmusctl/pkg/types"
)
type CreateProjectResponse struct {
Data struct {
Name string `json:"name"`
ID string `json:"projectID"`
} `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type createProjectPayload struct {
ProjectName string `json:"projectName"`
}
func CreateProjectRequest(projectName string, cred types.Credentials) (CreateProjectResponse, error) {
payloadBytes, err := json.Marshal(createProjectPayload{
ProjectName: projectName,
})
if err != nil {
return CreateProjectResponse{}, err
}
resp, err := SendRequest(SendRequestParams{cred.Endpoint + utils.AuthAPIPath + "/create_project", "Bearer " + cred.Token}, payloadBytes, string(types.Post))
if err != nil {
return CreateProjectResponse{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return CreateProjectResponse{}, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var project CreateProjectResponse
err = json.Unmarshal(bodyBytes, &project)
if err != nil {
return CreateProjectResponse{}, err
}
if len(project.Errors) > 0 {
return CreateProjectResponse{}, errors.New(project.Errors[0].Message)
}
utils.White_B.Println("project/" + project.Data.Name + " created")
return project, nil
} else {
return CreateProjectResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
type listProjectResponse struct {
Message string `json:"message"`
Data struct {
Projects []struct {
ID string `json:"projectID"` // Adjusted field name
Name string `json:"name"`
CreatedAt int64 `json:"createdAt"`
} `json:"projects"`
TotalNumberOfProjects int `json:"totalNumberOfProjects"`
} `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
func ListProject(cred types.Credentials) (listProjectResponse, error) {
resp, err := SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.AuthAPIPath + "/list_projects", Token: "Bearer " + cred.Token}, []byte{}, string(types.Get))
if err != nil {
return listProjectResponse{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return listProjectResponse{}, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var data listProjectResponse
err = json.Unmarshal(bodyBytes, &data)
if err != nil {
return listProjectResponse{}, err
}
if len(data.Errors) > 0 {
return listProjectResponse{}, errors.New(data.Errors[0].Message)
}
return data, nil
} else {
return listProjectResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
type ProjectDetails struct {
Data Data `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type Data struct {
ID string `json:"ID"`
Projects []Project `json:"Projects"`
}
type Member struct {
Role string `json:"Role"`
UserID string `json:"userID"`
UserName string `json:"username"`
}
type Project struct {
ID string `json:"ProjectID"`
Name string `json:"Name"`
CreatedAt int64 `json:"CreatedAt"`
Members []Member `json:"Members"`
}
// GetProjectDetails fetches details of the input user
func GetProjectDetails(c types.Credentials) (ProjectDetails, error) {
token, _ := jwt.Parse(c.Token, nil)
if token == nil {
return ProjectDetails{}, nil
}
Username, _ := token.Claims.(jwt.MapClaims)["username"].(string)
resp, err := SendRequest(SendRequestParams{Endpoint: c.Endpoint + utils.AuthAPIPath + "/get_user_with_project/" + Username, Token: "Bearer " + c.Token}, []byte{}, string(types.Get))
if err != nil {
return ProjectDetails{}, err
}
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ProjectDetails{}, err
}
if resp.StatusCode == http.StatusOK {
var project ProjectDetails
err = json.Unmarshal(bodyBytes, &project)
if err != nil {
return ProjectDetails{}, err
}
if len(project.Errors) > 0 {
return ProjectDetails{}, errors.New(project.Errors[0].Message)
}
return project, nil
} else {
return ProjectDetails{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}

43
pkg/apis/request.go Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apis
import (
"bytes"
"net/http"
)
type SendRequestParams struct {
Endpoint string
Token string
}
func SendRequest(params SendRequestParams, payload []byte, method string) (*http.Response, error) {
req, err := http.NewRequest(method, params.Endpoint, bytes.NewBuffer(payload))
if err != nil {
return &http.Response{}, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", params.Token)
req.Header.Set("Referer", params.Endpoint)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return &http.Response{}, err
}
return resp, nil
}

153
pkg/apis/upgrade.go Normal file
View File

@ -0,0 +1,153 @@
package apis
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"io"
"net/http"
"os"
"github.com/litmuschaos/litmusctl/pkg/k8s"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
)
type manifestData struct {
Data data `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type data struct {
GetManifest string `json:"getInfraManifest"`
}
type GetInfraResponse struct {
Data GetInfraData `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type GetInfraData struct {
GetInfraDetails InfraDetails `json:"getInfraDetails"`
}
type InfraDetails struct {
InfraID string `json:"infraID"`
InfraNamespace *string `json:"infraNamespace"`
}
func UpgradeInfra(c context.Context, cred types.Credentials, projectID string, infraID string, kubeconfig string) (string, error) {
// Query to fetch Infra details from server
query := `{"query":"query {\n getInfraDetails(infraID : \"` + infraID + `\", \n projectID : \"` + projectID + `\"){\n infraNamespace infraID \n}}"}`
resp, err := SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(query), string(types.Post))
if err != nil {
return "", err
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
var infra GetInfraResponse
if resp.StatusCode == http.StatusOK {
err = json.Unmarshal(bodyBytes, &infra)
if err != nil {
return "", err
}
if len(infra.Errors) > 0 {
return "", errors.New(infra.Errors[0].Message)
}
} else {
return "", errors.New(resp.Status)
}
// Query to fetch upgraded manifest from the server
query = `{"query":"query {\n getInfraManifest(projectID : \"` + projectID + `\",\n infraID : \"` + infra.Data.GetInfraDetails.InfraID + `\", \n upgrade: true)}"}`
resp, err = SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(query), string(types.Post))
if err != nil {
return "", err
}
bodyBytes, err = io.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
// Checks if status code is OK(200)
if resp.StatusCode == http.StatusOK {
var manifest manifestData
err = json.Unmarshal(bodyBytes, &manifest)
if err != nil {
return "", err
}
if len(manifest.Errors) > 0 {
return "", errors.New(manifest.Errors[0].Message)
}
// Fetching subscriber-config from the subscriber
configData, err := k8s.GetConfigMap(c, "subscriber-config", *infra.Data.GetInfraDetails.InfraNamespace)
if err != nil {
return "", err
}
var configMapString string
metadata := new(bytes.Buffer)
fmt.Fprintf(metadata, "\n%s: %s\n%s: %s\n%s: \n %s: %s\n %s: %s\n%s:\n", "apiVersion", "v1",
"kind", "ConfigMap", "metadata", "name", "subscriber-config", "namespace", *infra.Data.GetInfraDetails.InfraNamespace, "data")
for k, v := range configData {
b := new(bytes.Buffer)
if k == "COMPONENTS" {
fmt.Fprintf(b, " %s: |\n %s", k, v)
} else if k == "START_TIME" || k == "IS_INFRA_CONFIRMED" {
fmt.Fprintf(b, " %s: \"%s\"\n", k, v)
} else {
fmt.Fprintf(b, " %s: %s\n", k, v)
}
configMapString = configMapString + b.String()
}
yamlOutput, err := k8s.ApplyManifest([]byte(manifest.Data.GetManifest), kubeconfig)
if err != nil {
return "", err
}
logrus.Println("🚀 Successfully Upgraded Chaos Infrastructure")
utils.White.Print("\n", yamlOutput)
// Creating a backup for current subscriber-config in the SUBSCRIBER
home, err := homedir.Dir()
cobra.CheckErr(err)
configMapString = metadata.String() + configMapString
err = os.WriteFile(home+"/backupSubscriberConfig.yaml", []byte(configMapString), 0644)
if err != nil {
return "Error creating backup for subscriber config: ", err
}
utils.White_B.Print("\n ** A backup of subscriber-config configmap has been saved in your system's home directory as backupSubscriberConfig.yaml **\n")
return "Manifest applied successfully", nil
} else {
return "GQL error: ", errors.New("Unmatched status code:" + string(bodyBytes))
}
}

View File

@ -1,12 +0,0 @@
package agent
import (
"github.com/spf13/cobra"
)
// agentCmd represents the agent command
var AgentCmd = &cobra.Command{
Use: "agent",
Short: "Litmus Agent",
Long: `agent is used to manage Litmus backup and restore agents`,
}

View File

@ -1,38 +0,0 @@
package register
import (
"fmt"
"os"
utils "github.com/litmuschaos/litmusctl/pkg/common"
chaos "github.com/litmuschaos/litmusctl/pkg/common/chaos"
"github.com/spf13/cobra"
)
// registerCmd represents the register command
var RegisterCmd = &cobra.Command{
Use: "register",
Short: "Register LitmusChaos agent",
Long: `Register registers the agent to LitmusChaos`,
Run: func(cmd *cobra.Command, args []string) {
var c utils.Credentials
var pErr error
fmt.Println("🔥 Registering LitmusChaos agent")
fmt.Println("\n📶 Please enter LitmusChaos details --")
// Get LitmusChaos URL as input
c.Host, pErr = utils.GetPortalURL()
if pErr != nil {
fmt.Printf("\n❌ URL parsing failed: [%s]", pErr.Error())
os.Exit(1)
}
// Get username as input
c.Username = utils.GetUsername()
// Get password as input
c.Password = utils.GetPassword()
// Fetch authorization token
t := utils.Login(c, "auth/login")
chaos.Register(t, c)
},
}

41
pkg/cmd/config/config.go Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"github.com/spf13/cobra"
)
// configCmd represents the config command
var ConfigCmd = &cobra.Command{
Use: "config",
Short: `It manages multiple ChaosCenter accounts within a system.
Examples(s)
#set a new account
litmusctl config set-account --endpoint "" --password "" --username ""
#use an existing account from the config file
litmusctl config use-account --endpoint "" --username ""
#get all accounts in the config file
litmusctl config get-accounts
#view the config file
litmusctl config view
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

View File

@ -0,0 +1,62 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"os"
"strconv"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/config"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// getAccountsCmd represents the getAccounts command
var getAccountsCmd = &cobra.Command{
Use: "get-accounts",
Short: "Display accounts defined in the litmusconfig",
Long: `Display accounts defined in the litmusconfig`,
Run: func(cmd *cobra.Command, args []string) {
configFilePath := utils.GetLitmusConfigPath(cmd)
obj, err := config.YamltoObject(configFilePath)
utils.PrintError(err)
writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight)
utils.White_B.Fprintln(writer, "CURRENT\tENDPOINT\tUSERNAME\tEXPIRESIN")
for _, account := range obj.Accounts {
for _, user := range account.Users {
intTime, err := strconv.ParseInt(user.ExpiresIn, 10, 64)
utils.PrintError(err)
humanTime := time.Unix(intTime, 0)
if obj.CurrentUser == user.Username && obj.CurrentAccount == account.Endpoint {
utils.White.Fprintln(writer, "*"+"\t"+account.Endpoint+"\t"+user.Username+"\t"+humanTime.String())
} else {
utils.White.Fprintln(writer, ""+"\t"+account.Endpoint+"\t"+user.Username+"\t"+humanTime.String())
}
}
}
writer.Flush()
},
}
func init() {
ConfigCmd.AddCommand(getAccountsCmd)
}

View File

@ -0,0 +1,247 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"encoding/json"
"fmt"
"io"
"net/url"
"os"
"strings"
"time"
infra "github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
"github.com/golang-jwt/jwt"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/config"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
// setAccountCmd represents the setAccount command
var setAccountCmd = &cobra.Command{
Use: "set-account",
Short: `Sets an account entry in litmusconfig.
Examples(s)
#set a new account
litmusctl config set-account --endpoint "" --password "" --username ""
`,
Run: func(cmd *cobra.Command, args []string) {
configFilePath := utils.GetLitmusConfigPath(cmd)
var (
authInput types.AuthInput
err error
)
nonInteractive, err := cmd.Flags().GetBool("non-interactive")
utils.PrintError(err)
if nonInteractive {
authInput.Endpoint, err = cmd.Flags().GetString("endpoint")
utils.PrintError(err)
authInput.Username, err = cmd.Flags().GetString("username")
utils.PrintError(err)
authInput.Password, err = cmd.Flags().GetString("password")
utils.PrintError(err)
} else {
// prompts for account details
promptEndpoint := promptui.Prompt{
Label: "Host endpoint where litmus is installed",
}
authInput.Endpoint, err = promptEndpoint.Run()
utils.PrintError(err)
promptUsername := promptui.Prompt{
Label: "Username [Default: " + utils.DefaultUsername + "]",
Default: utils.DefaultUsername,
}
authInput.Username, err = promptUsername.Run()
utils.PrintError(err)
promptPassword := promptui.Prompt{
Label: "Password",
Mask: '*',
}
pass, err := promptPassword.Run()
utils.PrintError(err)
authInput.Password = pass
}
// Validate and format the endpoint URL
ep := strings.TrimRight(authInput.Endpoint, "/")
newURL, err := url.Parse(ep)
utils.PrintError(err)
authInput.Endpoint = newURL.String()
if authInput.Endpoint == "" {
utils.White_B.Print("\nHost endpoint where litmus is installed: ")
fmt.Scanln(&authInput.Endpoint)
if authInput.Endpoint == "" {
utils.Red.Println("\n⛔ Host URL can't be empty!!")
os.Exit(1)
}
ep := strings.TrimRight(authInput.Endpoint, "/")
newUrl, err := url.Parse(ep)
utils.PrintError(err)
authInput.Endpoint = newUrl.String()
}
if authInput.Endpoint != "" && authInput.Username != "" && authInput.Password != "" {
exists := config.FileExists(configFilePath)
var lgt int
if exists {
lgt, err = config.GetFileLength(configFilePath)
utils.PrintError(err)
}
resp, err := apis.Auth(authInput)
utils.PrintError(err)
// Decoding token
token, _ := jwt.Parse(resp.AccessToken, nil)
if token == nil {
os.Exit(1)
}
claims, _ := token.Claims.(jwt.MapClaims)
var user = types.User{
ExpiresIn: fmt.Sprint(time.Now().Add(time.Second * time.Duration(resp.ExpiresIn)).Unix()),
Token: resp.AccessToken,
Username: claims["username"].(string),
}
var users []types.User
users = append(users, user)
var account = types.Account{
Endpoint: authInput.Endpoint,
Users: users,
ServerEndpoint: authInput.Endpoint,
}
// If config file doesn't exist or length of the file is zero.
if !exists || lgt == 0 {
var accounts []types.Account
accounts = append(accounts, account)
var litmuCtlConfig = types.LitmuCtlConfig{
APIVersion: "v1",
Kind: "Config",
CurrentAccount: authInput.Endpoint,
CurrentUser: claims["username"].(string),
Accounts: accounts,
}
err := config.CreateNewLitmusCtlConfig(configFilePath, litmuCtlConfig)
utils.PrintError(err)
} else {
// checking syntax
err = config.ConfigSyntaxCheck(configFilePath)
utils.PrintError(err)
var updateLitmusCtlConfig = types.UpdateLitmusCtlConfig{
Account: account,
CurrentAccount: authInput.Endpoint,
CurrentUser: claims["username"].(string),
ServerEndpoint: authInput.Endpoint,
}
err = config.UpdateLitmusCtlConfig(updateLitmusCtlConfig, configFilePath)
utils.PrintError(err)
}
utils.White_B.Printf("\naccount.username/%s configured", claims["username"].(string))
credentials, err := utils.GetCredentials(cmd)
if err != nil {
utils.PrintError(err)
}
endpoint := credentials.Endpoint + utils.AuthAPIPath + "/get_user/" + claims["uid"].(string)
userResp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: endpoint,
Token: "Bearer " + credentials.Token,
},
nil,
string(types.Get),
)
if err != nil {
utils.PrintError(err)
}
bodyBytes, err := io.ReadAll(userResp.Body)
if err != nil {
utils.PrintError(err)
}
var userResponse map[string]interface{}
err = json.Unmarshal(bodyBytes, &userResponse)
if err != nil {
utils.PrintError(err)
}
isInitialLogin := userResponse["isInitialLogin"].(bool)
if isInitialLogin {
utils.White_B.Println("\n❗ This is your first time login. Update your default password to perform further operations.")
utils.White.Println(fmt.Sprintf("Use '%s' to update your password.", utils.White_B.Sprint("litmusctl update password")))
}
} else {
utils.Red.Println("\nError: some flags are missing. Run 'litmusctl config set-account --help' for usage. ")
}
serverResp, err := infra.GetServerVersion(authInput.Endpoint)
var isCompatible bool
if err != nil {
utils.Red.Println("\nError: ", err)
} else {
compatibilityArr := utils.CompatibilityMatrix[os.Getenv("CLIVersion")]
for _, v := range compatibilityArr {
if v == serverResp.Data.GetServerVersion.Value {
isCompatible = true
break
}
}
if !isCompatible {
utils.Red.Println("\n🚫 ChaosCenter version: " + serverResp.Data.GetServerVersion.Value + " is not compatible with the installed LitmusCTL version: " + os.Getenv("CLIVersion"))
utils.White_B.Println("Compatible ChaosCenter versions are: ")
utils.White_B.Print("[ ")
for _, v := range compatibilityArr {
utils.White_B.Print("'" + v + "' ")
}
utils.White_B.Print("]\n")
} else {
utils.White_B.Println("\n✅ Installed versions of ChaosCenter and LitmusCTL are compatible! ")
}
}
},
}
func init() {
ConfigCmd.AddCommand(setAccountCmd)
setAccountCmd.Flags().BoolP("non-interactive", "n", false, "Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean")
setAccountCmd.Flags().StringP("endpoint", "e", "", "Account endpoint. Mandatory")
setAccountCmd.Flags().StringP("username", "u", "", "Account username. Mandatory")
setAccountCmd.Flags().StringP("password", "p", "", "Account password. Mandatory")
}

View File

@ -0,0 +1,100 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"os"
"github.com/litmuschaos/litmusctl/pkg/config"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// useAccountCmd represents the useAccount command
var useAccountCmd = &cobra.Command{
Use: "use-account",
Short: "Sets the current-account and current-username in a litmusconfig file",
Long: `Sets the current-account and current-username in a litmusconfig file`,
Run: func(cmd *cobra.Command, args []string) {
configFilePath := utils.GetLitmusConfigPath(cmd)
endpoint, err := cmd.Flags().GetString("endpoint")
utils.PrintError(err)
if endpoint == "" {
utils.White_B.Print("\nHost endpoint where litmus is installed: ")
fmt.Scanln(&endpoint)
for endpoint == "" {
utils.Red.Println("\n⛔ Host URL can't be empty!!")
os.Exit(1)
}
}
username, err := cmd.Flags().GetString("username")
utils.PrintError(err)
if username == "" {
utils.White_B.Print("\nUsername: ")
fmt.Scanln(&username)
for username == "" {
utils.Red.Println("\n⛔ Username cannot be empty!!")
os.Exit(1)
}
}
if username == "" || endpoint == "" {
utils.Red.Println("endpoint or username is not set")
os.Exit(1)
}
exists := config.FileExists(configFilePath)
err = config.ConfigSyntaxCheck(configFilePath)
utils.PrintError(err)
if exists {
litmusconfig, err := config.YamltoObject(configFilePath)
utils.PrintError(err)
isAccountExist := config.IsAccountExists(litmusconfig, username, endpoint)
if isAccountExist {
err = config.UpdateCurrent(types.Current{
CurrentAccount: endpoint,
CurrentUser: username,
}, configFilePath)
utils.PrintError(err)
fmt.Printf("\n✅ Successfully set the current account to '%s' at '%s'\n", username, endpoint)
} else {
utils.Red.Println("\n⛔ Account not exists")
os.Exit(1)
}
} else {
utils.Red.Println("\n⛔ File not exists")
os.Exit(1)
}
},
}
func init() {
ConfigCmd.AddCommand(useAccountCmd)
useAccountCmd.Flags().StringP("username", "u", "", "Help message for toggle")
useAccountCmd.Flags().StringP("endpoint", "e", "", "Help message for toggle")
}

53
pkg/cmd/config/view.go Normal file
View File

@ -0,0 +1,53 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"github.com/litmuschaos/litmusctl/pkg/utils"
"os"
"github.com/litmuschaos/litmusctl/pkg/config"
"github.com/spf13/cobra"
)
// viewCmd represents the view command
var viewCmd = &cobra.Command{
Use: "view",
Short: "Display litmusconfig settings or a specified litmusconfig file",
Long: `Display litmusconfig settings or a specified litmusconfig file. `,
Run: func(cmd *cobra.Command, args []string) {
configFilePath := utils.GetLitmusConfigPath(cmd)
exists := config.FileExists(configFilePath)
if !exists {
utils.Red.Println("File reading error open ", configFilePath, ": no such file or directory. Use --config or -c flag to point the configfile")
os.Exit(1)
}
data, err := os.ReadFile(configFilePath)
utils.PrintError(err)
//Printing the config map
fmt.Print(string(data))
},
}
func init() {
ConfigCmd.AddCommand(viewCmd)
}

View File

@ -0,0 +1,34 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package connect
import (
"github.com/spf13/cobra"
)
// connectCmd represents the connect command
var ConnectCmd = &cobra.Command{
Use: "connect",
Short: `Connect resources for LitmusChaos Execution plane.
Examples:
#connect a Chaos Infrastructure
litmusctl connect chaos-infra --name="new-chaos-infra" --non-interactive
#connect a chaos-infrastructure within a project
litmusctl connect chaos-infra --name="new-chaos-infra" --environment-id="my-environment-id" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

280
pkg/cmd/connect/infra.go Normal file
View File

@ -0,0 +1,280 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package connect
import (
"os"
"github.com/sirupsen/logrus"
"github.com/litmuschaos/litmusctl/pkg/apis/environment"
"github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/infra_ops"
"github.com/litmuschaos/litmusctl/pkg/k8s"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// infraCmd represents the Chaos infra command
var infraCmd = &cobra.Command{
Use: "chaos-infra",
Short: `Connect an external Chaos infra.
Example(s):
#connect a Chaos infra
litmusctl connect chaos-infra --name="new-chaos-infra" --non-interactive
#connect a Chaos infra within a project
litmusctl connect chaos-infra --name="new-chaos-infra" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
nonInteractive, err := cmd.Flags().GetBool("non-interactive")
utils.PrintError(err)
kubeconfig, err := cmd.Flags().GetString("kubeconfig")
utils.PrintError(err)
var newInfra types.Infra
newInfra.ProjectId, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if newInfra.ProjectId == "" {
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var (
userID = userDetails.Data.ID
projectExists = false
)
outerloop:
for _, project := range userDetails.Data.Projects {
for _, member := range project.Members {
if (member.UserID == userID) && (member.Role == "Owner" || member.Role == "Editor") {
projectExists = true
break outerloop
}
}
}
if !projectExists {
utils.White_B.Print("Creating a random project...")
newInfra.ProjectId = infra_ops.CreateRandomProject(credentials)
}
}
if nonInteractive {
newInfra.Mode, err = cmd.Flags().GetString("installation-mode")
utils.PrintError(err)
if newInfra.Mode == "" {
utils.Red.Print("Error: --installation-mode flag is empty")
os.Exit(1)
}
newInfra.InfraName, err = cmd.Flags().GetString("name")
utils.PrintError(err)
newInfra.SkipSSL, err = cmd.Flags().GetBool("skip-ssl")
utils.PrintError(err)
if newInfra.InfraName == "" {
utils.Red.Print("Error: --name flag is empty")
os.Exit(1)
}
newInfra.EnvironmentID, _ = cmd.Flags().GetString("environment-id")
if newInfra.EnvironmentID == "" {
utils.Red.Print("Error: --environment flag is empty")
os.Exit(1)
}
newInfra.Description, err = cmd.Flags().GetString("description")
utils.PrintError(err)
newInfra.PlatformName, err = cmd.Flags().GetString("platform-name")
utils.PrintError(err)
if newInfra.PlatformName == "" {
utils.Red.Print("Error: --platform-name flag is empty")
os.Exit(1)
}
newInfra.InfraType, err = cmd.Flags().GetString("chaos-infra-type")
utils.PrintError(err)
if newInfra.InfraType == "" {
utils.Red.Print("Error: --chaos-infra-type flag is empty")
os.Exit(1)
}
newInfra.NodeSelector, err = cmd.Flags().GetString("node-selector")
utils.PrintError(err)
if newInfra.NodeSelector != "" {
if ok := utils.CheckKeyValueFormat(newInfra.NodeSelector); !ok {
os.Exit(1)
}
}
toleration, err := cmd.Flags().GetString("tolerations")
utils.PrintError(err)
if toleration != "" {
newInfra.Tolerations = toleration
}
newInfra.Namespace, err = cmd.Flags().GetString("namespace")
utils.PrintError(err)
newInfra.ServiceAccount, err = cmd.Flags().GetString("service-account")
utils.PrintError(err)
newInfra.NsExists, err = cmd.Flags().GetBool("ns-exists")
utils.PrintError(err)
newInfra.SAExists, err = cmd.Flags().GetBool("sa-exists")
utils.PrintError(err)
if newInfra.Mode == "" {
newInfra.Mode = utils.DefaultMode
}
if newInfra.ProjectId == "" {
utils.Red.Println("Error: --project-id flag is empty")
os.Exit(1)
}
// Check if user has sufficient permissions based on mode
utils.White_B.Print("\n🏃 Running prerequisites check....")
infra_ops.ValidateSAPermissions(newInfra.Namespace, newInfra.Mode, &kubeconfig)
// Check if infra already exists
isInfraExist, err, infraList := infra_ops.ValidateInfraNameExists(newInfra.InfraName, newInfra.ProjectId, credentials)
utils.PrintError(err)
if isInfraExist {
infra_ops.PrintExistingInfra(infraList)
os.Exit(1)
}
envIDs, err := environment.ListChaosEnvironments(newInfra.ProjectId, credentials)
utils.PrintError(err)
// Check if Environment exists
var isEnvExist = false
for i := range envIDs.Data.ListEnvironmentDetails.Environments {
if newInfra.EnvironmentID == envIDs.Data.ListEnvironmentDetails.Environments[i].EnvironmentID {
utils.White_B.Print(envIDs.Data.ListEnvironmentDetails.Environments[i].EnvironmentID)
isEnvExist = true
break
}
}
if !isEnvExist {
utils.Red.Println("\nChaos Environment with the given ID doesn't exists.")
infra_ops.PrintExistingEnvironments(envIDs)
utils.White_B.Println("\n❗ Please enter a name from the List or Create a new environment using `litmusctl create chaos-environment`")
os.Exit(1)
}
} else {
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
if newInfra.ProjectId == "" {
// Fetch project id
newInfra.ProjectId = infra_ops.GetProjectID(userDetails)
}
modeType := infra_ops.GetModeType()
// Check if user has sufficient permissions based on mode
utils.White_B.Print("\n🏃 Running prerequisites check....")
infra_ops.ValidateSAPermissions(newInfra.Namespace, modeType, &kubeconfig)
newInfra, err = infra_ops.GetInfraDetails(modeType, newInfra.ProjectId, credentials, &kubeconfig)
utils.PrintError(err)
newInfra.ServiceAccount, newInfra.SAExists = k8s.ValidSA(newInfra.Namespace, &kubeconfig)
newInfra.Mode = modeType
}
infra_ops.Summary(newInfra, &kubeconfig)
if !nonInteractive {
infra_ops.ConfirmInstallation()
}
infra, err := infrastructure.ConnectInfra(newInfra, credentials)
if err != nil {
utils.Red.Println("\n❌ Chaos Infra connection failed: " + err.Error() + "\n")
os.Exit(1)
}
if infra.Data.RegisterInfraDetails.Token == "" {
utils.Red.Println("\n❌ failed to get the Infra registration token: ")
os.Exit(1)
}
// Print error message in case Data field is null in response
if (infra.Data == infrastructure.RegisterInfra{}) {
utils.White_B.Print("\n🚫 Chaos new infrastructure connection failed: " + infra.Errors[0].Message + "\n")
os.Exit(1)
}
yamlOutput, err := k8s.ApplyManifest([]byte(infra.Data.RegisterInfraDetails.Manifest), kubeconfig)
if err != nil {
utils.Red.Println("\n❌ failed to apply infra manifest, error: " + err.Error())
os.Exit(1)
}
logrus.Println("🚀 Successfully Connected Chaos Infrastructure")
utils.White.Print("\n", yamlOutput)
// Watch subscriber pod status
k8s.WatchPod(k8s.WatchPodParams{Namespace: newInfra.Namespace, Label: utils.ChaosInfraLabel}, &kubeconfig)
utils.White_B.Println("\n🚀 Chaos new infrastructure connection successful!! 🎉")
},
}
func init() {
ConnectCmd.AddCommand(infraCmd)
infraCmd.Flags().BoolP("non-interactive", "n", false, "Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean")
infraCmd.Flags().StringP("kubeconfig", "k", "", "Set to pass kubeconfig file if it is not in the default location ($HOME/.kube/config)")
infraCmd.Flags().String("tolerations", "", "Set the tolerations for Chaos infra components | Format: '[{\"key\":\"key1\",\"value\":\"value1\",\"operator\":\"Exist\",\"effect\":\"NoSchedule\",\"tolerationSeconds\":30}]'")
infraCmd.Flags().String("environment-id", "", "Set the environmentID for the Chaos infra installation")
infraCmd.Flags().String("project-id", "", "Set the project-id to install Chaos infra for the particular project. To see the projects, apply litmusctl get projects")
infraCmd.Flags().String("installation-mode", "cluster", "Set the installation mode for the kind of Chaos infra | Supported=cluster/namespace")
infraCmd.Flags().String("name", "", "Set the Chaos infra name")
infraCmd.Flags().String("description", "---", "Set the Chaos infra description")
infraCmd.Flags().String("platform-name", "Others", "Set the platform name. Supported- AWS/GKE/Openshift/Rancher/Others")
infraCmd.Flags().String("chaos-infra-type", "Kubernetes", "Set the chaos-infra-type to external for external Chaos infras | Supported=external/internal")
infraCmd.Flags().String("node-selector", "", "Set the node-selector for Chaos infra components | Format: \"key1=value1,key2=value2\")")
infraCmd.Flags().String("namespace", "litmus", "Set the namespace for the Chaos infra installation")
infraCmd.Flags().String("service-account", "litmus", "Set the service account to be used by the Chaos infra")
infraCmd.Flags().Bool("skip-ssl", false, "Set whether Chaos infra will skip ssl/tls check (can be used for self-signed certs, if cert is not provided in portal)")
infraCmd.Flags().Bool("ns-exists", false, "Set the --ns-exists=false if the namespace mentioned in the --namespace flag is not existed else set it to --ns-exists=true | Note: Always set the boolean flag as --ns-exists=Boolean")
infraCmd.Flags().Bool("sa-exists", false, "Set the --sa-exists=false if the service-account mentioned in the --service-account flag is not existed else set it to --sa-exists=true | Note: Always set the boolean flag as --sa-exists=Boolean\"\n")
}

37
pkg/cmd/create/create.go Normal file
View File

@ -0,0 +1,37 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package create
import (
"github.com/spf13/cobra"
)
// createCmd represents the create command
var CreateCmd = &cobra.Command{
Use: "create",
Short: `Create resources for LitmusChaos Execution plane.
Examples:
#create a project
litmusctl create project --name new-proj
#create a Chaos Experiment from a file
litmusctl create chaos-experiment -f chaos-experiment.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-infra-id="d861b650-1549-4574-b2ba-ab754058dd04"
#create a Chaos Environment
litmusctl create chaos-environment --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --name="new-chaos-environment"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

View File

@ -0,0 +1,165 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package create
import (
"fmt"
"os"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/apis/environment"
"github.com/litmuschaos/litmusctl/pkg/infra_ops"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// environmentCmd represents the Chaos infra command
var environmentCmd = &cobra.Command{
Use: "chaos-environment",
Short: `Create an Environment.
Example(s):
#create a Chaos Environment
litmusctl create chaos-environment --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --name="new-chaos-environment"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var newEnvironment models.CreateEnvironmentRequest
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if pid == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&pid)
if pid == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
envName, err := cmd.Flags().GetString("name")
utils.PrintError(err)
// Handle blank input for project ID
if envName == "" {
utils.White_B.Print("\nEnter the Environment Name: ")
fmt.Scanln(&envName)
if envName == "" {
utils.Red.Println("⛔ Environment Name can't be empty!!")
os.Exit(1)
}
}
newEnvironment.Name = envName
description, err := cmd.Flags().GetString("description")
utils.PrintError(err)
newEnvironment.Description = &description
envType, err := cmd.Flags().GetString("type")
if err != nil {
utils.PrintError(err)
}
// Handle blank input for project ID
if envType == "" {
utils.White_B.Print("\nEnter the Environment Type: ")
fmt.Scanln(&envType)
if envType == "" {
utils.Red.Println("⛔ Environment Type can't be empty!!")
os.Exit(1)
}
}
newEnvironment.Type = models.EnvironmentType(envType)
envs, err := environment.ListChaosEnvironments(pid, credentials)
if err != nil {
utils.PrintError(err)
}
// Generate EnvironmentID from Environment Name
envID := utils.GenerateNameID(newEnvironment.Name)
newEnvironment.EnvironmentID = envID
// Check if Environment exists
var isEnvExist = false
for i := range envs.Data.ListEnvironmentDetails.Environments {
if envID == envs.Data.ListEnvironmentDetails.Environments[i].EnvironmentID {
utils.White_B.Print(envs.Data.ListEnvironmentDetails.Environments[i].EnvironmentID)
isEnvExist = true
break
}
}
if isEnvExist {
utils.Red.Println("\nChaos Environment with the given ID already exists, try with a different name")
infra_ops.PrintExistingEnvironments(envs)
os.Exit(1)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
if err != nil {
utils.PrintError(err)
}
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == pid {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
newEnv, err := environment.CreateEnvironment(pid, newEnvironment, credentials)
if err != nil {
utils.Red.Println("\n❌ Chaos Environment connection failed: " + err.Error() + "\n")
os.Exit(1)
}
//TODO: add the nil checker for the response(newEnv.Data)
//Print error message in case Data field is null in response
//if (newEnv.Data == environment.CreateEnvironmentData{}) {
// utils.White_B.Print("\n🚫 Chaos newInfra connection failed: " + newEnv.Errors[0].Message + "\n")
// os.Exit(1)
//}
utils.White_B.Println("\n🚀 New Chaos Environment creation successful!! 🎉")
utils.White_B.Println("EnvironmentID: " + newEnv.Data.EnvironmentDetails.EnvironmentID)
},
}
func init() {
CreateCmd.AddCommand(environmentCmd)
environmentCmd.Flags().String("project-id", "", "Set the project-id to install Chaos infra for the particular project. To see the projects, apply litmusctl get projects")
environmentCmd.Flags().String("type", "NON_PROD", "Set the installation mode for the kind of Chaos infra | Supported=cluster/namespace")
environmentCmd.Flags().String("name", "", "Set the Chaos infra name")
environmentCmd.Flags().String("description", "---", "Set the Chaos infra description")
}

View File

@ -0,0 +1,149 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package create
import (
"fmt"
"os"
"strings"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// experimentCmd represents the project command
var experimentCmd = &cobra.Command{
Use: "chaos-experiment",
Short: `Create a Chaos Experiment
Example:
#create a Chaos Experiment
litmusctl create chaos-experiment -f chaos-experiment.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-infra-id="1c9c5801-8789-4ac9-bf5f-32649b707a5c"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var chaosExperimentRequest models.SaveChaosExperimentRequest
workflowManifest, err := cmd.Flags().GetString("file")
utils.PrintError(err)
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if pid == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&pid)
if pid == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
chaosExperimentRequest.InfraID, err = cmd.Flags().GetString("chaos-infra-id")
utils.PrintError(err)
// Handle blank input for Chaos Infra ID
if chaosExperimentRequest.InfraID == "" {
utils.White_B.Print("\nEnter the Chaos Infra ID: ")
fmt.Scanln(&chaosExperimentRequest.InfraID)
if chaosExperimentRequest.InfraID == "" {
utils.Red.Println("⛔ Chaos Infra ID can't be empty!!")
os.Exit(1)
}
}
chaosExperimentRequest.Description, err = cmd.Flags().GetString("description")
utils.PrintError(err)
if chaosExperimentRequest.Description == "" {
utils.White_B.Print("\nExperiment Description: ")
fmt.Scanln(&chaosExperimentRequest.Description)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == pid {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// Parse experiment manifest and populate chaosExperimentInput
err = utils.ParseExperimentManifest(workflowManifest, &chaosExperimentRequest)
if err != nil {
utils.Red.Println("❌ Error parsing Chaos Experiment manifest: " + err.Error())
os.Exit(1)
}
// Generate ExperimentID from ExperimentName
chaosExperimentRequest.ID = utils.GenerateNameID(chaosExperimentRequest.Name)
// Make API call
createExperiment, err := experiment.CreateExperiment(pid, chaosExperimentRequest, credentials)
if err != nil {
if (createExperiment.Data == experiment.RunExperimentData{}) {
if strings.Contains(err.Error(), "multiple write errors") {
utils.Red.Println("\n❌ Chaos Experiment/" + chaosExperimentRequest.Name + " already exists")
os.Exit(1)
}
if strings.Contains(err.Error(), "no documents in result") {
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
os.Exit(1)
}
if strings.Contains(err.Error(), "multiple run errors") {
utils.Red.Println("\n❌ Chaos Experiment already exists")
os.Exit(1)
}
} else {
utils.White_B.Print("\n❌ Chaos Experiment/" + chaosExperimentRequest.Name + " failed to be created: " + err.Error())
os.Exit(1)
}
}
//Successful creation
utils.White_B.Println("\n🚀 Chaos Experiment successfully created and experiment run is scheduled 🎉")
},
}
func init() {
CreateCmd.AddCommand(experimentCmd)
experimentCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Experiment for the particular project. To see the projects, apply litmusctl get projects")
experimentCmd.Flags().String("chaos-infra-id", "", "Set the chaos-infra-id to create Chaos Experiment for the particular Chaos Infrastructure. To see the Chaos Infrastructures, apply litmusctl get chaos-infra")
experimentCmd.Flags().StringP("file", "f", "", "The manifest file for the Chaos Experiment")
experimentCmd.Flags().StringP("description", "d", "", "The Description for the Chaos Experiment")
}

74
pkg/cmd/create/project.go Normal file
View File

@ -0,0 +1,74 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package create
import (
"fmt"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
// projectCmd represents the project command
var projectCmd = &cobra.Command{
Use: "project",
Short: `Create a project
Example:
#create a project
litmusctl create project --name new-proj
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectName, err := cmd.Flags().GetString("name")
utils.PrintError(err)
if projectName == "" {
// prompt to ask project name
prompt := promptui.Prompt{
Label: "Enter a project name",
AllowEdit: true,
}
result, err := prompt.Run()
if err != nil {
utils.Red.Printf("Error: %v\n", err)
return
}
projectName = result
}
var response apis.CreateProjectResponse
response, err = apis.CreateProjectRequest(projectName, credentials)
if err != nil {
utils.Red.Printf("❌ Error creating project: %v\n", err)
} else {
fmt.Printf("Response: %+v\n", response)
projectID := response.Data.ID
utils.White_B.Printf("Project '%s' created successfully with project ID - '%s'!🎉\n", projectName, projectID)
}
},
}
func init() {
CreateCmd.AddCommand(projectCmd)
projectCmd.Flags().String("name", "", "Set the project name to create it")
}

38
pkg/cmd/delete/delete.go Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package delete
import (
"github.com/spf13/cobra"
)
// deleteCmd represents the delete command
var DeleteCmd = &cobra.Command{
Use: "delete",
Short: `Delete resources for LitmusChaos Execution plane.
Examples:
#delete a Chaos Experiment
litmusctl delete chaos-experiment c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
#delete a Chaos Environment
litmusctl delete chaos-environment --project-id=8adf62d5-64f8-4c66-ab53-63729db9dd9a --environment-id=environmentexample
#delete a Probe
litmusctl delete probe --project-id=8adf62d5-64f8-4c66-ab53-63729db9dd9a --probe-id=exampleprobe
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

View File

@ -0,0 +1,156 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package delete
import (
"os"
"github.com/litmuschaos/litmusctl/pkg/apis/environment"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"strings"
)
// experimentCmd represents the Chaos Experiment command
var environmentCmd = &cobra.Command{
Use: "chaos-environment",
Short: `Delete a Chaos environment
Example:
#delete a Chaos Environment
litmusctl delete chaos-environment --project-id=8adf62d5-64f8-4c66-ab53-63729db9dd9a --environment-id=environmentexample
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
environmentID, err := cmd.Flags().GetString("environment-id")
utils.PrintError(err)
// Handle blank input for project ID
if projectID == "" {
prompt := promptui.Prompt{
Label: "Enter the Project ID",
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
projectID = result
}
if environmentID == "" {
prompt := promptui.Prompt{
Label: "Enter the Environment ID",
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
environmentID = result
}
// Handle blank input for Chaos Environment ID
if environmentID == "" {
utils.Red.Println("⛔ Chaos Environment ID can't be empty!!")
os.Exit(1)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == projectID {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
environmentGet, err := environment.GetChaosEnvironment(projectID, environmentID, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ You don't have enough permissions to delete an environment.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
environmentGetData := environmentGet.Data.EnvironmentDetails
if len(environmentGetData.InfraIDs) > 0 {
utils.Red.Println("Chaos Infras present in the Chaos Environment" +
", delete the Chaos Infras first to delete the Environment")
os.Exit(1)
}
// confirm before deletion
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Chaos Environment? (y/n)",
AllowEdit: true,
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
if result != "y" {
utils.White_B.Println("\n❌ Chaos Environment was not deleted.")
os.Exit(0)
}
// Make API call
_, err = environment.DeleteEnvironment(projectID, environmentID, credentials)
if err != nil {
utils.Red.Println("\n❌ Error in deleting Chaos Environment: ", err.Error())
os.Exit(1)
}
utils.White_B.Println("\n🚀 Chaos Environment successfully deleted.")
},
}
func init() {
DeleteCmd.AddCommand(environmentCmd)
environmentCmd.Flags().String("project-id", "", "Set the project-id to delete Chaos Environment for the particular project. To see the projects, apply litmusctl get projects")
environmentCmd.Flags().String("environment-id", "", "Set the environment-id to delete the particular Chaos Environment.")
}

View File

@ -0,0 +1,141 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package delete
import (
"os"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
// experimentCmd represents the Chaos Experiment command
var experimentCmd = &cobra.Command{
Use: "chaos-experiment",
Short: `Delete a Chaos experiment
Example:
#delete a Chaos Experiment
litmusctl delete chaos-experiment c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
experimentID := ""
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if projectID == "" {
prompt := promptui.Prompt{
Label: "Enter the Project ID",
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
projectID = result
}
if len(args) == 0 {
prompt := promptui.Prompt{
Label: "Enter the Chaos Experiment ID",
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
experimentID = result
} else {
experimentID = args[0]
}
// Handle blank input for Chaos Experiment ID
if experimentID == "" {
utils.Red.Println("⛔ Chaos Experiment ID can't be empty!!")
os.Exit(1)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == projectID {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// confirm before deletion
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this Chaos Experiment? (y/n)",
AllowEdit: true,
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
if result != "y" {
utils.White_B.Println("\n❌ Chaos Experiment was not deleted.")
os.Exit(0)
}
// Make API call
deleteExperiment, err := experiment.DeleteChaosExperiment(projectID, &experimentID, credentials)
if err != nil {
utils.Red.Println("\n❌ Error in deleting Chaos Experiment: ", err.Error())
os.Exit(1)
}
if deleteExperiment.Data.IsDeleted {
utils.White_B.Println("\n🚀 Chaos Experiment successfully deleted.")
} else {
utils.White_B.Println("\n❌ Failed to delete Chaos Experiment. Please check if the ID is correct or not.")
}
},
}
func init() {
DeleteCmd.AddCommand(experimentCmd)
experimentCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Experiment for the particular project. To see the projects, apply litmusctl get projects")
}

129
pkg/cmd/delete/probe.go Normal file
View File

@ -0,0 +1,129 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.W
See the License for the specific language governing permissions and
limitations under the License.
*/
package delete
import (
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/apis/probe"
"github.com/litmuschaos/litmusctl/pkg/utils"
"os"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
var probeCmd = &cobra.Command{
Use: "probe",
Short: `Delete a Probe
Example:
#delete a Probe
litmusctl delete probe --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b --probe-id="example"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if projectID == "" {
prompt := promptui.Prompt{
Label: "Enter the Project ID",
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
projectID = result
}
probeID, err := cmd.Flags().GetString("probe-id")
// Handle blank input for Probe ID
if probeID == "" {
prompt := promptui.Prompt{
Label: "Enter the Probe ID",
}
IDinput, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
probeID = IDinput
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == projectID {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// confirm before deletion
prompt := promptui.Prompt{
Label: "Are you sure you want to delete this probe and all the associations with experiment runs from the chaos control plane (y/n)",
AllowEdit: true,
}
result, err := prompt.Run()
if err != nil {
utils.Red.Println("⛔ Error:", err)
os.Exit(1)
}
if result != "y" {
utils.White_B.Println("\n❌ Probe was not deleted.")
os.Exit(0)
}
// Make API call
deleteProbe, err := probe.DeleteProbeRequest(projectID, probeID, credentials)
if err != nil {
utils.Red.Println("\n❌ Error in deleting Probe: ", err.Error())
os.Exit(1)
}
if deleteProbe.Data.DeleteProbe {
utils.White_B.Println("\n🚀 Probe was successfully deleted.")
} else {
utils.White_B.Println("\n❌ Failed to delete Probe. Please check if the ID is correct or not.")
}
},
}
func init() {
DeleteCmd.AddCommand(probeCmd)
probeCmd.Flags().String("project-id", "", "Set the project-id to delete Probe for the particular project. To see the projects, apply litmusctl get projects")
probeCmd.Flags().String("probe-id", "", "Set the probe-id to delete that particular probe. To see the probes, apply litmusctl get probes")
}

View File

@ -0,0 +1,35 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package describe
import (
"github.com/spf13/cobra"
)
// DescribeCmd represents the describe command
var DescribeCmd = &cobra.Command{
Use: "describe",
Short: `Describe resources for LitmusChaos Execution plane.
Examples:
#describe a Chaos Experiment
litmusctl describe chaos-experiment d861b650-1549-4574-b2ba-ab754058dd04 --project-id="d861b650-1549-4574-b2ba-ab754058dd04"
#describe a Probe
litmusctl describe probe --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --probe-id="exampleProbe"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

View File

@ -0,0 +1,153 @@
// /*
// Copyright © 2021 The LitmusChaos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// */
package describe
import (
"bytes"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)
// experimentCmd represents the Chaos Experiment command
var experimentCmd = &cobra.Command{
Use: "chaos-experiment",
Short: "Describe a Chaos Experiment within the project",
Long: `Describe a Chaos Experiment within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var describeExperimentRequest model.ListExperimentRequest
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
if pid == "" {
prompt := promptui.Prompt{
Label: "Enter the Project ID",
}
result, err := prompt.Run()
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
pid = result
}
var experimentID string
if len(args) == 0 {
prompt := promptui.Prompt{
Label: "Enter the Chaos Experiment ID",
}
result, err := prompt.Run()
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
experimentID = result
} else {
experimentID = args[0]
}
// Handle blank input for Chaos Experiment ID
if experimentID == "" {
utils.Red.Println("⛔ Chaos Experiment ID can't be empty!!")
os.Exit(1)
}
describeExperimentRequest.ExperimentIDs = append(describeExperimentRequest.ExperimentIDs, &experimentID)
experiment, err := experiment.GetExperimentList(pid, describeExperimentRequest, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ The specified Project ID doesn't exist.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
if len(experiment.Data.ListExperimentDetails.Experiments) == 0 {
utils.Red.Println("⛔ No chaos experiment found with ID: ", experimentID)
os.Exit(1)
}
yamlManifest, err := yaml.JSONToYAML([]byte(experiment.Data.ListExperimentDetails.Experiments[0].ExperimentManifest))
if err != nil {
utils.Red.Println("❌ Error parsing Chaos Experiment manifest: " + err.Error())
os.Exit(1)
}
expOutput, err := cmd.Flags().GetString("output")
utils.PrintError(err)
// Add an output format prompt
if expOutput == "" {
prompt := promptui.Select{
Label: "Select an output format",
Items: []string{"yaml", "json"},
}
_, value, err := prompt.Run()
expOutput = value
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
}
switch expOutput {
case "yaml":
// Output as YAML (default)
utils.PrintInYamlFormat(string(yamlManifest))
case "json":
// Output as JSON
jsonData, err := yaml.YAMLToJSON(yamlManifest)
if err != nil {
utils.Red.Println("❌ Error converting YAML to JSON: " + err.Error())
os.Exit(1)
}
var prettyJSON bytes.Buffer
err = json.Indent(&prettyJSON, jsonData, "", " ") // Adjust the indentation as needed
if err != nil {
utils.Red.Println("❌ Error formatting JSON: " + err.Error())
os.Exit(1)
}
fmt.Println(prettyJSON.String())
default:
utils.Red.Println("❌ Invalid output format selected")
os.Exit(1)
}
},
}
func init() {
DescribeCmd.AddCommand(experimentCmd)
experimentCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Experiments from the particular project. To see the projects, apply litmusctl get projects")
experimentCmd.Flags().String("output", "", "Set the output format for the experiment")
}

126
pkg/cmd/describe/probe.go Normal file
View File

@ -0,0 +1,126 @@
// /*
// Copyright © 2021 The LitmusChaos Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// */
package describe
import (
"bytes"
"encoding/json"
"fmt"
"os"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
apis "github.com/litmuschaos/litmusctl/pkg/apis/probe"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)
var probeCmd = &cobra.Command{
Use: "probe",
Short: "Describe a Probe within the project",
Long: `Describe a Probe within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var getProbeYAMLRequest model.GetProbeYAMLRequest
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
if pid == "" {
prompt := promptui.Prompt{
Label: "Enter the Project ID",
}
result, err := prompt.Run()
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
pid = result
}
var probeID string
probeID, err = cmd.Flags().GetString("probe-id")
utils.PrintError(err)
// Handle blank input for Probe ID
if probeID == "" {
utils.White_B.Print("\nEnter the Probe ID: ")
fmt.Scanln(&probeID)
if probeID == "" {
utils.Red.Println("⛔ Probe ID can't be empty!!")
os.Exit(1)
}
}
getProbeYAMLRequest.ProbeName = probeID
probeMode, err := cmd.Flags().GetString("mode")
utils.PrintError(err)
if probeMode == "" {
prompt := promptui.Select{
Label: "Please select the probe mode ?",
Items: []string{"SOT", "EOT", "Edge", "Continuous", "OnChaos"},
}
_, option, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
probeMode = option
fmt.Printf("You chose %q\n", option)
}
getProbeYAMLRequest.Mode = model.Mode(probeMode)
getProbeYAML, err := apis.GetProbeYAMLRequest(pid, getProbeYAMLRequest, credentials)
if err != nil {
utils.Red.Println(err)
os.Exit(1)
}
getProbeYAMLData := getProbeYAML.Data.GetProbeYAML
probeOutput, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch probeOutput {
case "json":
jsonData, _ := yaml.YAMLToJSON([]byte(getProbeYAMLData))
var prettyJSON bytes.Buffer
err = json.Indent(&prettyJSON, jsonData, "", " ")
if err != nil {
utils.Red.Println("❌ Error formatting JSON: " + err.Error())
os.Exit(1)
}
fmt.Println(prettyJSON.String())
default:
utils.PrintInYamlFormat(getProbeYAMLData)
}
},
}
func init() {
DescribeCmd.AddCommand(probeCmd)
probeCmd.Flags().String("project-id", "", "Set the project-id to get Probe details from the particular project. To see the projects, apply litmusctl get projects")
probeCmd.Flags().String("probe-id", "", "Set the probe-id to get the Probe details in Yaml format")
probeCmd.Flags().String("mode", "", "Set the mode for the probes from SOT/EOT/Edge/Continuous/OnChaos ")
probeCmd.Flags().String("output", "", "Set the output format for the probe")
}

View File

@ -0,0 +1,32 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package disconnect
import (
"github.com/spf13/cobra"
)
// disconnectCmd represents the disconnect command
var DisconnectCmd = &cobra.Command{
Use: "disconnect",
Short: `Disconnect resources for LitmusChaos Execution plane.
Examples:
#disconnect a Chaos Infra
litmusctl disconnect chaos-infra c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

118
pkg/cmd/disconnect/infra.go Normal file
View File

@ -0,0 +1,118 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package disconnect
import (
"fmt"
"os"
"strings"
"github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// infraCmd represents the infra command
var infraCmd = &cobra.Command{
Use: "chaos-infra",
Short: `Disconnect a Chaos Infrastructure
Example:
#disconnect a Chaos Infrastructure
litmusctl disconnect chaos-infra c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if projectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&projectID)
if projectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
var infraID string
if len(args) == 0 {
utils.White_B.Print("\nEnter the Infra ID: ")
fmt.Scanln(&infraID)
} else {
infraID = args[0]
}
// Handle blank input for Infra ID
if infraID == "" {
utils.Red.Println("⛔ Chaos Infra ID can't be empty!!")
os.Exit(1)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == projectID {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// Make API call
disconnectedInfra, err := infrastructure.DisconnectInfra(projectID, infraID, credentials)
if err != nil {
if strings.Contains(err.Error(), "no documents in result") {
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
os.Exit(1)
} else {
utils.Red.Println("\n❌ Error in disconnecting Chaos Infrastructure: ", err.Error())
os.Exit(1)
}
}
if strings.Contains(disconnectedInfra.Data.Message, "infra deleted successfully") {
utils.White_B.Println("\n🚀 Chaos Infrastructure successfully disconnected.")
} else {
utils.White_B.Println("\n❌ Failed to disconnect Chaos Infrastructure. Please check if the ID is correct or not.")
}
},
}
func init() {
DisconnectCmd.AddCommand(infraCmd)
infraCmd.Flags().String("project-id", "", "Set the project-id to disconnect Chaos Infrastructure for the particular project. To see the projects, apply litmusctl get projects")
}

164
pkg/cmd/get/environments.go Normal file
View File

@ -0,0 +1,164 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"os"
"strconv"
"strings"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/apis/environment"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
var GetChaosEnvironmentsCmd = &cobra.Command{
Use: "chaos-environments",
Short: "Get Chaos Environments within the project",
Long: `Display the Chaos Environments within the project with the targeted id `,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
if projectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&projectID)
for projectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
environmentID, err := cmd.Flags().GetString("environment-id")
utils.PrintError(err)
if environmentID == "" {
environmentList, err := environment.ListChaosEnvironments(projectID, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ You don't have enough permissions to access this resource.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
environmentListData := environmentList.Data.ListEnvironmentDetails.Environments
itemsPerPage := 5
page := 1
totalEnvironments := len(environmentListData)
writer := tabwriter.NewWriter(os.Stdout, 30, 8, 0, '\t', tabwriter.AlignRight)
utils.White_B.Fprintln(writer, "CHAOS ENVIRONMENT ID\tCHAOS ENVIRONMENT NAME\tCREATED AT\tCREATED BY")
for {
writer.Flush()
// calculating the start and end indices for the current page
start := (page - 1) * itemsPerPage
if start >= totalEnvironments {
writer.Flush()
utils.Red.Println("No more environments to display")
break
}
end := start + itemsPerPage
if end > totalEnvironments {
end = totalEnvironments
}
for _, environment := range environmentListData[start:end] {
intTime, err := strconv.ParseInt(environment.CreatedAt, 10, 64)
if err != nil {
fmt.Println("Error converting CreatedAt to int64:", err)
continue
}
humanTime := time.Unix(intTime, 0)
utils.White.Fprintln(
writer,
environment.EnvironmentID+"\t"+environment.Name+"\t"+humanTime.String()+"\t"+environment.CreatedBy.Username,
)
}
writer.Flush()
// Check if it's the last item or if user wants to see more
paginationPrompt := promptui.Prompt{
Label: "Press Enter to show more environments (or type 'q' to quit)",
AllowEdit: true,
Default: "",
}
userInput, err := paginationPrompt.Run()
utils.PrintError(err)
if userInput == "q" {
break
}
// Move to the next page
page++
}
} else {
environmentGet, err := environment.GetChaosEnvironment(projectID, environmentID, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ You don't have enough permissions to access this resource.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
environmentGetData := environmentGet.Data.EnvironmentDetails
writer := tabwriter.NewWriter(os.Stdout, 30, 8, 0, '\t', tabwriter.AlignRight)
writer.Flush()
intUpdateTime, err := strconv.ParseInt(environmentGetData.UpdatedAt, 10, 64)
if err != nil {
utils.Red.Println("Error converting UpdatedAt to int64:", err)
}
updatedTime := time.Unix(intUpdateTime, 0).String()
intCreatedTime, err := strconv.ParseInt(environmentGetData.CreatedAt, 10, 64)
if err != nil {
utils.Red.Println("Error converting CreatedAt to int64:", err)
}
createdTime := time.Unix(intCreatedTime, 0).String()
writer.Flush()
utils.White_B.Fprintln(writer, "CHAOS ENVIRONMENT DETAILS")
utils.White.Fprintln(writer, "CHAOS ENVIRONMENT ID\t", environmentGetData.EnvironmentID)
utils.White.Fprintln(writer, "CHAOS ENVIRONMENT NAME\t", environmentGetData.Name)
utils.White.Fprintln(writer, "CHAOS ENVIRONMENT Type\t", environmentGetData.Type)
utils.White.Fprintln(writer, "CREATED AT\t", createdTime)
utils.White.Fprintln(writer, "CREATED BY\t", environmentGetData.CreatedBy.Username)
utils.White.Fprintln(writer, "UPDATED AT\t", updatedTime)
utils.White.Fprintln(writer, "UPDATED BY\t", environmentGetData.UpdatedBy.Username)
utils.White.Fprintln(writer, "CHAOS INFRA IDs\t", strings.Join(environmentGetData.InfraIDs, ", "))
utils.White.Fprintln(writer, "TAGS\t", strings.Join(environmentGetData.Tags, ", "))
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(GetChaosEnvironmentsCmd)
GetChaosEnvironmentsCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Environments from a particular project.")
GetChaosEnvironmentsCmd.Flags().String("environment-id", "", "Set the environment-id to get details about a Chaos Environment.")
}

View File

@ -0,0 +1,143 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"os"
"strconv"
"strings"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// experimentRunsCmd represents the Chaos Experiments runs command
var experimentRunsCmd = &cobra.Command{
Use: "chaos-experiment-runs",
Short: "Display list of Chaos Experiments runs within the project",
Long: `Display list of Chaos Experiments runs within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var listExperimentRunsRequest model.ListExperimentRunRequest
var projectID string
projectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if projectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&projectID)
for projectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
// experiment ID flag
experimentID, err := cmd.Flags().GetString("experiment-id")
utils.PrintError(err)
if experimentID != "" {
listExperimentRunsRequest.ExperimentIDs = []*string{&experimentID}
}
// experiment run ID flag
experimentRunID, err := cmd.Flags().GetString("experiment-run-id")
utils.PrintError(err)
if experimentRunID != "" {
listExperimentRunsRequest.ExperimentRunIDs = []*string{&experimentRunID}
}
listAllExperimentRuns, _ := cmd.Flags().GetBool("all")
if !listAllExperimentRuns {
listExperimentRunsRequest.Pagination = &model.Pagination{}
listExperimentRunsRequest.Pagination.Limit, _ = cmd.Flags().GetInt("count")
}
experimentRuns, err := experiment.GetExperimentRunsList(projectID, listExperimentRunsRequest, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ The specified Project ID doesn't exist.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
output, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch output {
case "json":
utils.PrintInJsonFormat(experimentRuns.Data)
case "yaml":
utils.PrintInYamlFormat(experimentRuns.Data)
case "":
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS EXPERIMENT RUN ID\tSTATUS\tRESILIENCY SCORE\tCHAOS EXPERIMENT ID\tCHAOS EXPERIMENT NAME\tTARGET CHAOS INFRA\tUPDATED AT\tUPDATED BY")
for _, experimentRun := range experimentRuns.Data.ListExperimentRunDetails.ExperimentRuns {
var lastUpdated string
unixSecondsInt, err := strconv.ParseInt(experimentRun.UpdatedAt, 10, 64)
if err != nil {
lastUpdated = "None"
} else {
lastUpdated = time.Unix(unixSecondsInt, 0).Format("January 2 2006, 03:04:05 pm")
}
utils.White.Fprintln(
writer,
experimentRun.ExperimentRunID+"\t"+experimentRun.Phase.String()+"\t"+strconv.FormatFloat(*experimentRun.ResiliencyScore, 'f', 2, 64)+"\t"+experimentRun.ExperimentID+"\t"+experimentRun.ExperimentName+"\t"+experimentRun.Infra.Name+"\t"+lastUpdated+"\t"+experimentRun.UpdatedBy.Username)
}
if listAllExperimentRuns || (experimentRuns.Data.ListExperimentRunDetails.TotalNoOfExperimentRuns <= listExperimentRunsRequest.Pagination.Limit) {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Experiment runs", experimentRuns.Data.ListExperimentRunDetails.TotalNoOfExperimentRuns, experimentRuns.Data.ListExperimentRunDetails.TotalNoOfExperimentRuns))
} else {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Experiment runs", listExperimentRunsRequest.Pagination.Limit, experimentRuns.Data.ListExperimentRunDetails.TotalNoOfExperimentRuns))
}
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(experimentRunsCmd)
experimentRunsCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Experiments from the particular project. To see the projects, apply litmusctl get projects")
experimentRunsCmd.Flags().Int("count", 30, "Set the count of Chaos Experiments runs to display. Default value is 30")
experimentRunsCmd.Flags().BoolP("all", "A", false, "Set to true to display all Chaos Experiments runs")
experimentRunsCmd.Flags().String("experiment-id", "", "Set the experiment ID to list experiment runs within a specific experiment")
experimentRunsCmd.Flags().String("experiment-run-id", "", "Set the experiment run ID to list a specific experiment run")
experimentRunsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

152
pkg/cmd/get/experiments.go Normal file
View File

@ -0,0 +1,152 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/gorhill/cronexpr"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
// experimentsCmd represents the Chaos experiments command
var experimentsCmd = &cobra.Command{
Use: "chaos-experiments",
Short: "Display list of Chaos Experiments within the project",
Long: `Display list of Chaos Experiments within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var listExperimentRequest model.ListExperimentRequest
var pid string
pid, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if pid == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&pid)
for pid == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
listAllExperiments, _ := cmd.Flags().GetBool("all")
if !listAllExperiments {
listExperimentRequest.Pagination = &model.Pagination{}
listExperimentRequest.Pagination.Limit, _ = cmd.Flags().GetInt("count")
}
listExperimentRequest.Filter = &model.ExperimentFilterInput{}
infraName, err := cmd.Flags().GetString("chaos-infra")
utils.PrintError(err)
listExperimentRequest.Filter.InfraName = &infraName
experiments, err := experiment.GetExperimentList(pid, listExperimentRequest, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ The specified Project ID doesn't exist.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
outputFormat, err := cmd.Flags().GetString("output")
utils.PrintError(err)
if outputFormat == "" {
outputPrompt := promptui.Select{
Label: "Select an output format",
Items: []string{"table", "json", "yaml"},
}
_, outputFormat, err = outputPrompt.Run()
utils.PrintError(err)
}
switch outputFormat {
case "json":
utils.PrintInJsonFormat(experiments.Data)
case "yaml":
utils.PrintInYamlFormat(experiments.Data)
case "table":
itemsPerPage := 5
page := 1
totalExperiments := len(experiments.Data.ListExperimentDetails.Experiments)
for {
// calculating the start and end indices for the current page
start := (page - 1) * itemsPerPage
end := start + itemsPerPage
if end > totalExperiments {
end = totalExperiments
}
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS EXPERIMENT ID\tCHAOS EXPERIMENT NAME\tCHAOS EXPERIMENT TYPE\tNEXT SCHEDULE\tCHAOS INFRASTRUCTURE ID\tCHAOS INFRASTRUCTURE NAME\tLAST UPDATED By")
for _, experiment := range experiments.Data.ListExperimentDetails.Experiments[start:end] {
if experiment.CronSyntax != "" {
utils.White.Fprintln(
writer,
experiment.ExperimentID+"\t"+experiment.Name+"\tCron Chaos Experiment\t"+cronexpr.MustParse(experiment.CronSyntax).Next(time.Now()).Format("January 2 2006, 03:04:05 pm")+"\t"+experiment.Infra.InfraID+"\t"+experiment.Infra.Name+"\t"+experiment.UpdatedBy.Username)
} else {
utils.White.Fprintln(
writer,
experiment.ExperimentID+"\t"+experiment.Name+"\tNon Cron Chaos Experiment\tNone\t"+experiment.Infra.InfraID+"\t"+experiment.Infra.Name+"\t"+experiment.UpdatedBy.Username)
}
}
writer.Flush()
paginationPrompt := promptui.Prompt{
Label: "Press Enter to show the next page (or type 'q' to quit)",
AllowEdit: true,
Default: "",
}
userInput, err := paginationPrompt.Run()
utils.PrintError(err)
if userInput == "q" {
break
} else {
page++
}
}
}
},
}
func init() {
GetCmd.AddCommand(experimentsCmd)
experimentsCmd.Flags().String("project-id", "", "Set the project-id to list Chaos experiments from the particular project. To see the projects, apply litmusctl get projects")
experimentsCmd.Flags().Int("count", 30, "Set the count of Chaos experiments to display. Default value is 30")
experimentsCmd.Flags().Bool("all", false, "Set to true to display all Chaos experiments")
experimentsCmd.Flags().StringP("chaos-infra", "A", "", "Set the Chaos Infrastructure name to display all Chaos experiments targeted towards that particular Chaos Infrastructure.")
experimentsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

46
pkg/cmd/get/get.go Normal file
View File

@ -0,0 +1,46 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"github.com/spf13/cobra"
)
// getCmd represents the get command
var GetCmd = &cobra.Command{
Use: "get",
Short: `Examples:
#get list of projects accessed by the user
litmusctl get projects
#get list of Chaos Infrastructure within the project
litmusctl get chaos-infra --project-id=""
#get list of chaos Chaos Experiments
litmusctl get chaos-experiments --project-id=""
#get list of Chaos Experiment runs
litmusctl get chaos-experiment-runs --project-id=""
#get list of Chaos Environments
litmusctl get chaos-environments --project-id=""
#get list of Probes in a Project
litmusctl get probes --project-id=""
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

98
pkg/cmd/get/infra.go Normal file
View File

@ -0,0 +1,98 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"os"
"strings"
"text/tabwriter"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// InfraCmd represents the Infra command
var InfraCmd = &cobra.Command{
Use: "chaos-infra",
Short: "Display list of Chaos Infrastructures within the project",
Long: `Display list of Chaos Infrastructures within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
if projectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&projectID)
for projectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
infras, err := infrastructure.GetInfraList(credentials, projectID, models.ListInfraRequest{})
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ you don't have enough permissions to access this project")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
output, _ := cmd.Flags().GetString("output")
switch output {
case "json":
utils.PrintInJsonFormat(infras.Data)
case "yaml":
utils.PrintInYamlFormat(infras.Data)
case "":
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS INFRASTRUCTURE ID \tCHAOS INFRASTRUCTURE NAME\tSTATUS\tCHAOS ENVIRONMENT ID\t")
for _, infra := range infras.Data.ListInfraDetails.Infras {
var status string
if infra.IsActive {
status = "ACTIVE"
} else {
status = "INACTIVE"
}
utils.White.Fprintln(writer, infra.InfraID+"\t"+infra.Name+"\t"+status+"\t"+infra.EnvironmentID+"\t")
}
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(InfraCmd)
InfraCmd.Flags().String("project-id", "", "Set the project-id. To retrieve projects. Apply `litmusctl get projects`")
InfraCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

294
pkg/cmd/get/probe.go Normal file
View File

@ -0,0 +1,294 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"fmt"
"os"
"strconv"
"strings"
"text/tabwriter"
"time"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
apis "github.com/litmuschaos/litmusctl/pkg/apis/probe"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
var probesCmd = &cobra.Command{
Use: "probes",
Short: "Display list of probes",
Long: `Display list of probes`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var projectID string
projectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if projectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&projectID)
if projectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
ProbeID, err := cmd.Flags().GetString("probe-id")
if ProbeID == "" {
getProbeList(projectID, cmd, credentials)
} else {
getProbeDetails(projectID, ProbeID, credentials)
}
},
}
func init() {
GetCmd.AddCommand(probesCmd)
probesCmd.Flags().String("project-id", "", "Set the project-id to get Probe from a particular project.")
probesCmd.Flags().BoolP("non-interactive", "n", false, "Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean")
probesCmd.Flags().String("probe-types", "", "Set the probe-types as comma separated values to filter the probes")
probesCmd.Flags().String("probe-id", "", "Set the probe-details to the ID of probe for getting all the details related to the probe.")
}
func getProbeList(projectID string, cmd *cobra.Command, credentials types.Credentials) {
// calls the probeList endpoint for the list of probes
var selectedItems []*models.ProbeType
NoninteractiveMode, err := cmd.Flags().GetBool("non-interactive")
utils.PrintError(err)
if NoninteractiveMode == false {
prompt := promptui.Select{
Label: "Do you want to enable advance filter probes?",
Items: []string{"Yes", "No"},
}
_, option, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}
fmt.Printf("You chose %q\n", option)
if option == "Yes" {
items := []models.ProbeType{"httpProbe", "cmdProbe", "promProbe", "k8sProbe", "done"}
for {
prompt := promptui.Select{
Label: "Select ProbeType",
Items: items,
Templates: &promptui.SelectTemplates{
Active: `{{ . | cyan }}`,
Inactive: ` {{ . | white }}`,
Selected: `{{ "✔" | green }} {{ . | bold }}`,
},
}
selectedIndex, result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
os.Exit(1)
}
if items[selectedIndex] == "done" {
break
}
final := models.ProbeType(result)
selectedItems = append(selectedItems, &final)
items = append(items[:selectedIndex], items[selectedIndex+1:]...)
}
fmt.Printf("Selected Probe Types: %v\n", selectedItems)
}
} else {
var probeTypes string
probeTypes, err = cmd.Flags().GetString("probe-types")
values := strings.Split(probeTypes, ",")
for _, value := range values {
probeType := models.ProbeType(value)
selectedItems = append(selectedItems, &probeType)
}
}
probes_get, _ := apis.ListProbeRequest(projectID, selectedItems, credentials)
probes_data := probes_get.Data.Probes
itemsPerPage := 5
page := 1
totalProbes := len(probes_data)
writer := tabwriter.NewWriter(os.Stdout, 8, 8, 8, '\t', tabwriter.AlignRight)
utils.White_B.Fprintln(writer, "PROBE ID\t PROBE TYPE\t REFERENCED BY\t CREATED BY\t CREATED AT")
for {
writer.Flush()
start := (page - 1) * itemsPerPage
if start >= totalProbes {
utils.Red.Println("No more probes to display")
break
}
end := start + itemsPerPage
if end > totalProbes {
end = totalProbes
}
for _, probe := range probes_data[start:end] {
intTime, err := strconv.ParseInt(probe.CreatedAt, 10, 64)
if err != nil {
fmt.Println("Error converting CreatedAt to int64:", err)
continue
}
humanTime := time.Unix(intTime, 0)
var probeReferencedBy int
if probe.ReferencedBy != nil {
probeReferencedBy = *probe.ReferencedBy
}
utils.White.Fprintln(writer, probe.Name+"\t"+fmt.Sprintf("%v", probe.Type)+"\t"+fmt.Sprintf("%d", probeReferencedBy)+"\t"+probe.CreatedBy.Username+"\t"+humanTime.String())
}
writer.Flush()
paginationPrompt := promptui.Prompt{
Label: "Press Enter to show more probes (or type 'q' to quit)",
AllowEdit: true,
Default: "",
}
userInput, err := paginationPrompt.Run()
utils.PrintError(err)
if userInput == "q" {
break
}
page++
}
}
func getProbeDetails(projectID, ProbeID string, credentials types.Credentials) {
//call the probe get endpoint to get the probes details
probeGet, err := apis.GetProbeRequest(projectID, ProbeID, credentials)
if err != nil {
if strings.Contains(err.Error(), "permission_denied") {
utils.Red.Println("❌ You don't have enough permissions to access this resource.")
os.Exit(1)
} else {
utils.PrintError(err)
os.Exit(1)
}
}
probeGetData := probeGet.Data.GetProbe
writer := tabwriter.NewWriter(os.Stdout, 30, 8, 2, '\t', tabwriter.AlignRight)
intUpdateTime, err := strconv.ParseInt(probeGetData.UpdatedAt, 10, 64)
if err != nil {
utils.Red.Println("Error converting UpdatedAt to int64:", err)
}
updatedTime := time.Unix(intUpdateTime, 0).String()
intCreatedTime, err := strconv.ParseInt(probeGetData.CreatedAt, 10, 64)
if err != nil {
utils.Red.Println("Error converting CreatedAt to int64:", err)
}
createdTime := time.Unix(intCreatedTime, 0).String()
utils.White_B.Fprintln(writer, "PROBE DETAILS")
utils.White.Fprintln(writer, "PROBE ID \t", probeGetData.Name)
utils.White.Fprintln(writer, "PROBE DESCRIPTION \t", *probeGetData.Description)
utils.White.Fprintln(writer, "PROBE TYPE \t", probeGetData.Type)
utils.White.Fprintln(writer, "PROBE INFRASTRUCTURE TYPE \t", probeGetData.InfrastructureType)
switch probeGetData.Type {
case "httpProbe":
printHTTPProbeDetails(writer, probeGetData)
case "cmdProbe":
printCmdProbeDetails(writer, probeGetData)
case "k8sProbe":
printK8sProbeDetails(writer, probeGetData)
case "promProbe":
printPromProbeDetails(writer, probeGetData)
}
utils.White.Fprintln(writer, "CREATED AT\t", createdTime)
utils.White.Fprintln(writer, "CREATED BY\t", probeGetData.CreatedBy.Username)
utils.White.Fprintln(writer, "UPDATED AT\t", updatedTime)
utils.White.Fprintln(writer, "UPDATED BY\t", probeGetData.UpdatedBy.Username)
utils.White.Fprintln(writer, "TAGS\t", strings.Join(probeGetData.Tags, ", "))
if probeGetData.ReferencedBy != nil {
utils.White.Fprintln(writer, "REFERENCED BY\t", *probeGetData.ReferencedBy)
}
writer.Flush()
}
func printHTTPProbeDetails(writer *tabwriter.Writer, probeData models.Probe) {
utils.White.Fprintln(writer, "TIMEOUT \t", probeData.KubernetesHTTPProperties.ProbeTimeout)
utils.White.Fprintln(writer, "INTERVAL \t", probeData.KubernetesHTTPProperties.Interval)
printOptionalIntProperty(writer, "ATTEMPT", probeData.KubernetesHTTPProperties.Attempt)
printOptionalIntProperty(writer, "RETRY", probeData.KubernetesHTTPProperties.Retry)
printOptionalProperty(writer, "POLLING INTERVAL", probeData.KubernetesHTTPProperties.ProbePollingInterval)
printOptionalProperty(writer, "INITIAL DELAY", probeData.KubernetesHTTPProperties.InitialDelay)
printOptionalProperty(writer, "EVALUATION TIMEOUT", probeData.KubernetesHTTPProperties.EvaluationTimeout)
printOptionalBoolProperty(writer, "STOP ON FAILURE", probeData.KubernetesHTTPProperties.StopOnFailure)
}
func printCmdProbeDetails(writer *tabwriter.Writer, probeData models.Probe) {
utils.White.Fprintln(writer, "TIMEOUT \t", probeData.KubernetesCMDProperties.ProbeTimeout)
utils.White.Fprintln(writer, "INTERVAL \t", probeData.KubernetesCMDProperties.Interval)
printOptionalIntProperty(writer, "ATTEMPT", probeData.KubernetesCMDProperties.Attempt)
printOptionalIntProperty(writer, "RETRY", probeData.KubernetesCMDProperties.Retry)
printOptionalProperty(writer, "POLLING INTERVAL", probeData.KubernetesCMDProperties.ProbePollingInterval)
printOptionalProperty(writer, "INITIAL DELAY", probeData.KubernetesCMDProperties.InitialDelay)
printOptionalProperty(writer, "EVALUATION TIMEOUT", probeData.KubernetesCMDProperties.EvaluationTimeout)
printOptionalBoolProperty(writer, "STOP ON FAILURE", probeData.KubernetesCMDProperties.StopOnFailure)
}
func printK8sProbeDetails(writer *tabwriter.Writer, probeData models.Probe) {
utils.White.Fprintln(writer, "TIMEOUT \t", probeData.K8sProperties.ProbeTimeout)
utils.White.Fprintln(writer, "INTERVAL \t", probeData.K8sProperties.Interval)
printOptionalIntProperty(writer, "ATTEMPT", probeData.K8sProperties.Attempt)
printOptionalIntProperty(writer, "RETRY", probeData.K8sProperties.Retry)
printOptionalProperty(writer, "POLLING INTERVAL", probeData.K8sProperties.ProbePollingInterval)
printOptionalProperty(writer, "INITIAL DELAY", probeData.K8sProperties.InitialDelay)
printOptionalProperty(writer, "EVALUATION TIMEOUT", probeData.K8sProperties.EvaluationTimeout)
printOptionalBoolProperty(writer, "STOP ON FAILURE", probeData.K8sProperties.StopOnFailure)
}
func printPromProbeDetails(writer *tabwriter.Writer, probeData models.Probe) {
utils.White.Fprintln(writer, "TIMEOUT \t", probeData.PromProperties.ProbeTimeout)
utils.White.Fprintln(writer, "INTERVAL \t", probeData.PromProperties.Interval)
printOptionalIntProperty(writer, "ATTEMPT", probeData.PromProperties.Attempt)
printOptionalIntProperty(writer, "RETRY", probeData.PromProperties.Retry)
printOptionalProperty(writer, "POLLING INTERVAL", probeData.PromProperties.ProbePollingInterval)
printOptionalProperty(writer, "INITIAL DELAY", probeData.PromProperties.InitialDelay)
printOptionalProperty(writer, "EVALUATION TIMEOUT", probeData.PromProperties.EvaluationTimeout)
printOptionalBoolProperty(writer, "STOP ON FAILURE", probeData.PromProperties.StopOnFailure)
}
func printOptionalProperty(writer *tabwriter.Writer, name string, value *string) {
if value != nil {
utils.White.Fprintln(writer, name+"\t", *value)
}
}
func printOptionalIntProperty(writer *tabwriter.Writer, name string, value *int) {
if value != nil {
utils.White.Fprintln(writer, name+"\t", *value)
}
}
func printOptionalBoolProperty(writer *tabwriter.Writer, name string, value *bool) {
if value != nil {
utils.White.Fprintln(writer, name+"\t", *value)
}
}

103
pkg/cmd/get/projects.go Normal file
View File

@ -0,0 +1,103 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"os"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
// projectCmd represents the project command
var projectsCmd = &cobra.Command{
Use: "projects",
Short: "Display list of projects",
Long: `Display list of projects`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
outputFormat, _ := cmd.Flags().GetString("output")
projects, err := apis.ListProject(credentials)
utils.PrintError(err)
switch outputFormat {
case "json":
utils.PrintInJsonFormat(projects.Data)
case "yaml":
utils.PrintInYamlFormat(projects.Data.Projects)
case "":
itemsPerPage := 5
page := 1
totalProjects := len(projects.Data.Projects)
for {
// calculating the start and end indices for the current page
start := (page - 1) * itemsPerPage
end := start + itemsPerPage
if end > totalProjects {
end = totalProjects
}
// check if there are no more projects to display
if start >= totalProjects {
utils.Red.Println("No more projects to display")
break
}
// displaying the projects for the current page
writer := tabwriter.NewWriter(os.Stdout, 8, 8, 8, '\t', tabwriter.AlignRight)
utils.White_B.Fprintln(writer, "PROJECT ID\tPROJECT NAME\tCREATED AT")
for _, project := range projects.Data.Projects[start:end] {
intTime := project.CreatedAt
humanTime := time.Unix(intTime/1000, 0) // Convert milliseconds to second
utils.White.Fprintln(writer, project.ID+"\t"+project.Name+"\t"+humanTime.String()+"\t")
}
writer.Flush()
// pagination prompt
paginationPrompt := promptui.Prompt{
Label: "Press Enter to show the next page (or type 'q' to quit)",
AllowEdit: true,
Default: "",
}
userInput, err := paginationPrompt.Run()
utils.PrintError(err)
if userInput == "q" {
break
} else {
page++
}
}
}
},
}
func init() {
GetCmd.AddCommand(projectsCmd)
projectsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

View File

@ -1,40 +0,0 @@
package litmusctl
import (
"fmt"
"os"
"github.com/litmuschaos/litmusctl/pkg/cmd/version"
chaosAgent "github.com/litmuschaos/litmusctl/pkg/cmd/agent"
chaosRegister "github.com/litmuschaos/litmusctl/pkg/cmd/agent/register"
"github.com/spf13/cobra"
)
// rootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "litmusctl",
Short: "A brief description of your application",
Long: `Litmusctl is a cli for managing Litmus resources.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
RootCmd.AddCommand(version.VersionCmd)
RootCmd.AddCommand(chaosAgent.AgentCmd)
chaosAgent.AgentCmd.AddCommand(chaosRegister.RegisterCmd)
// Create a persistent flag for kubeconfig
// RootCmd.PersistentFlags().StringP("kubeconfig", "k", "", "absolute path to the kubeconfig file")
}

121
pkg/cmd/root/root.go Normal file
View File

@ -0,0 +1,121 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rootCmd
import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"github.com/litmuschaos/litmusctl/pkg/cmd/run"
"github.com/litmuschaos/litmusctl/pkg/cmd/save"
"github.com/litmuschaos/litmusctl/pkg/cmd/update"
"github.com/litmuschaos/litmusctl/pkg/cmd/connect"
"github.com/litmuschaos/litmusctl/pkg/cmd/delete"
"github.com/litmuschaos/litmusctl/pkg/cmd/describe"
"github.com/litmuschaos/litmusctl/pkg/cmd/disconnect"
"github.com/litmuschaos/litmusctl/pkg/cmd/upgrade"
"github.com/litmuschaos/litmusctl/pkg/cmd/version"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/litmuschaos/litmusctl/pkg/cmd/config"
"github.com/litmuschaos/litmusctl/pkg/cmd/create"
"github.com/litmuschaos/litmusctl/pkg/cmd/get"
config2 "github.com/litmuschaos/litmusctl/pkg/config"
"github.com/spf13/cobra"
"github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
var cfgFile string
//var kubeconfig string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "litmusctl",
Short: "Litmusctl controls the litmuschaos agent plane",
Long: `Litmusctl controls the litmuschaos agent plane. ` + "\n" + ` Find more information at: https://github.com/litmuschaos/litmusctl`,
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.AddCommand(config.ConfigCmd)
rootCmd.AddCommand(create.CreateCmd)
rootCmd.AddCommand(get.GetCmd)
rootCmd.AddCommand(connect.ConnectCmd)
rootCmd.AddCommand(disconnect.DisconnectCmd)
rootCmd.AddCommand(delete.DeleteCmd)
rootCmd.AddCommand(describe.DescribeCmd)
rootCmd.AddCommand(version.VersionCmd)
rootCmd.AddCommand(upgrade.UpgradeCmd)
rootCmd.AddCommand(save.SaveCmd)
rootCmd.AddCommand(run.RunCmd)
rootCmd.AddCommand(update.UpdateCmd)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.litmusctl)")
//rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "kubeconfig file (default is $HOME/.kube/config")
rootCmd.PersistentFlags().BoolVar(&config2.SkipSSLVerify, "skipSSL", false, "skipSSL, litmusctl will skip ssl/tls verification while communicating with portal")
rootCmd.PersistentFlags().StringVar(&config2.CACert, "cacert", "", "cacert <path_to_crt_file> , custom ca certificate used for communicating with portal")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
cobra.CheckErr(err)
// Search config in home directory with name ".litmusconfig" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(utils.DefaultFileName)
}
viper.AutomaticEnv() // read in environment variables that match
if config2.SkipSSLVerify {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else if config2.CACert != "" {
caCert, err := os.ReadFile(config2.CACert)
cobra.CheckErr(err)
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{RootCAs: caCertPool}
}
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}

122
pkg/cmd/run/experiment.go Normal file
View File

@ -0,0 +1,122 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package run
import (
"fmt"
"os"
"strings"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// experimentCmd represents the project command
var experimentCmd = &cobra.Command{
Use: "chaos-experiment",
Short: `Create a Chaos Experiment
Example:
#Save a Chaos Experiment
litmusctl run chaos-experiment --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --experiment-id="1c9c5801-8789-4ac9-bf5f-32649b707a5c"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if pid == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&pid)
if pid == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
eid, err := cmd.Flags().GetString("experiment-id")
utils.PrintError(err)
// Handle blank input for Chaos Experiment ID
if eid == "" {
utils.White_B.Print("\nEnter the Chaos Experiment ID: ")
fmt.Scanln(&eid)
if eid == "" {
utils.Red.Println("⛔ Chaos Experiment ID can't be empty!!")
os.Exit(1)
}
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == pid {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == utils.MemberOwnerRole || member.Role == utils.MemberEditorRole) {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// Make API call
runExperiment, err := experiment.RunExperiment(pid, eid, credentials)
if err != nil {
if (runExperiment.Data == experiment.RunExperimentData{}) {
if strings.Contains(err.Error(), "multiple run errors") {
utils.Red.Println("\n❌ Chaos Experiment already exists")
os.Exit(1)
}
if strings.Contains(err.Error(), "no documents in result") {
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
os.Exit(1)
} else {
utils.White_B.Print("\n❌ Failed to run chaos experiment: " + err.Error())
os.Exit(1)
}
}
}
//Successful run
utils.White_B.Println("\n🚀 Chaos Experiment running successfully 🎉")
},
}
func init() {
RunCmd.AddCommand(experimentCmd)
experimentCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Experiment for the particular project. To see the projects, apply litmusctl get projects")
experimentCmd.Flags().String("experiment-id", "", "Set the environment-id to create Chaos Experiment for the particular Chaos Infrastructure. To see the Chaos Infrastructures, apply litmusctl get chaos-infra")
}

18
pkg/cmd/run/run.go Normal file
View File

@ -0,0 +1,18 @@
package run
import (
"github.com/spf13/cobra"
)
// createCmd represents the create command
var RunCmd = &cobra.Command{
Use: "run",
Short: `Runs Experiment for LitmusChaos Execution plane.
Examples:
#Run a Chaos Experiment
litmusctl run chaos-experiment --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --experiment-id="ab754058dd04"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

149
pkg/cmd/save/experiment.go Normal file
View File

@ -0,0 +1,149 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package save
import (
"fmt"
"os"
"strings"
models "github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis/experiment"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// experimentCmd represents the project command
var experimentCmd = &cobra.Command{
Use: "chaos-experiment",
Short: `Create a Chaos Experiment
Example:
#Save a Chaos Experiment
litmusctl save chaos-experiment -f chaos-experiment.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-infra-id="1c9c5801-8789-4ac9-bf5f-32649b707a5c"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
Run: func(cmd *cobra.Command, args []string) {
// Fetch user credentials
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var chaosExperimentRequest models.SaveChaosExperimentRequest
experimentManifest, err := cmd.Flags().GetString("file")
utils.PrintError(err)
pid, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if pid == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&pid)
if pid == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
chaosExperimentRequest.InfraID, err = cmd.Flags().GetString("chaos-infra-id")
utils.PrintError(err)
// Handle blank input for Chaos Infra ID
if chaosExperimentRequest.InfraID == "" {
utils.White_B.Print("\nEnter the Chaos Infra ID: ")
fmt.Scanln(&chaosExperimentRequest.InfraID)
if chaosExperimentRequest.InfraID == "" {
utils.Red.Println("⛔ Chaos Infra ID can't be empty!!")
os.Exit(1)
}
}
chaosExperimentRequest.Description, err = cmd.Flags().GetString("description")
utils.PrintError(err)
if chaosExperimentRequest.Description == "" {
utils.White_B.Print("\nExperiment Description: ")
fmt.Scanln(&chaosExperimentRequest.Description)
}
// Perform authorization
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
var editAccess = false
var project apis.Project
for _, p := range userDetails.Data.Projects {
if p.ID == pid {
project = p
}
}
for _, member := range project.Members {
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
editAccess = true
}
}
if !editAccess {
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
os.Exit(1)
}
// Parse experiment manifest and populate chaosExperimentInput
err = utils.ParseExperimentManifest(experimentManifest, &chaosExperimentRequest)
if err != nil {
utils.Red.Println("❌ Error parsing Chaos Experiment manifest: " + err.Error())
os.Exit(1)
}
// Generate ExperimentID from the ExperimentName
chaosExperimentRequest.ID = utils.GenerateNameID(chaosExperimentRequest.Name)
// Make API call
saveExperiment, err := experiment.SaveExperiment(pid, chaosExperimentRequest, credentials)
if err != nil {
if (saveExperiment.Data == experiment.SavedExperimentDetails{}) {
if strings.Contains(err.Error(), "multiple write errors") {
utils.Red.Println("\n❌ Chaos Experiment " + chaosExperimentRequest.Name + " already exists")
os.Exit(1)
}
if strings.Contains(err.Error(), "no documents in result") {
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
os.Exit(1)
} else {
utils.White_B.Print("\n❌ Chaos Experiment " + chaosExperimentRequest.Name + " failed to be created: " + err.Error())
os.Exit(1)
}
}
}
//Successful creation
utils.White_B.Println("\n🚀 Chaos Experiment " + chaosExperimentRequest.Name + " successfully saved 🎉")
utils.White_B.Println("\nChaos Experiment ID: " + chaosExperimentRequest.ID)
},
}
func init() {
SaveCmd.AddCommand(experimentCmd)
experimentCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Experiment for the particular project. To see the projects, apply litmusctl get projects")
experimentCmd.Flags().String("chaos-infra-id", "", "Set the chaos-infra-id to create Chaos Experiment for the particular Chaos Infrastructure. To see the Chaos Infrastructures, apply litmusctl get chaos-infra")
experimentCmd.Flags().StringP("file", "f", "", "The manifest file for the Chaos Experiment")
experimentCmd.Flags().StringP("description", "d", "", "The Description for the Chaos Experiment")
}

33
pkg/cmd/save/save.go Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package save
import (
"github.com/spf13/cobra"
)
// createCmd represents the create command
var SaveCmd = &cobra.Command{
Use: "save",
Short: `Save Experiment for LitmusChaos Execution plane.
Examples:
#Save a Chaos Experiment from a file
litmusctl save chaos-experiment -f chaos-experiment.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-infra-id="d861b650-1549-4574-b2ba-ab754058dd04"
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

111
pkg/cmd/update/password.go Normal file
View File

@ -0,0 +1,111 @@
package update
import (
"encoding/json"
"errors"
"io"
"net/http"
"os"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
var PasswordCmd = &cobra.Command{
Use: "password",
Short: `Updates an account's password.
Examples(s)
#update a user's password
litmusctl update password
`,
Run: func(cmd *cobra.Command, args []string) {
var (
updatePasswordRequest types.UpdatePasswordInput
)
credentials, err := utils.GetCredentials(cmd)
if err != nil {
utils.PrintError(err)
}
promptUsername := promptui.Prompt{
Label: "Username",
}
updatePasswordRequest.Username, err = promptUsername.Run()
if err != nil {
utils.PrintError(err)
}
promptOldPassword := promptui.Prompt{
Label: "Old Password",
Mask: '*',
}
updatePasswordRequest.OldPassword, err = promptOldPassword.Run()
if err != nil {
utils.PrintError(err)
}
NEW_PASSWORD:
promptNewPassword := promptui.Prompt{
Label: "New Password",
Mask: '*',
}
updatePasswordRequest.NewPassword, err = promptNewPassword.Run()
if err != nil {
utils.PrintError(err)
}
promptConfirmPassword := promptui.Prompt{
Label: "Confirm New Password",
Mask: '*',
}
confirmPassword, err := promptConfirmPassword.Run()
if err != nil {
utils.PrintError(err)
}
if updatePasswordRequest.NewPassword != confirmPassword {
utils.Red.Println("\nPasswords do not match. Please try again.")
goto NEW_PASSWORD
}
payloadBytes, _ := json.Marshal(updatePasswordRequest)
resp, err := apis.SendRequest(
apis.SendRequestParams{
Endpoint: credentials.Endpoint + utils.AuthAPIPath + "/update/password",
Token: "Bearer " + credentials.Token,
},
payloadBytes,
string(types.Post),
)
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
utils.PrintError(err)
os.Exit(1)
}
if resp.StatusCode == http.StatusOK {
utils.White_B.Println("\nPassword updated successfully!")
} else {
err := errors.New("Unmatched status code: " + string(bodyBytes))
if err != nil {
utils.Red.Println("\nError updating password: ", err)
os.Exit(1)
}
}
},
}
func init() {
UpdateCmd.AddCommand(PasswordCmd)
}

15
pkg/cmd/update/update.go Normal file
View File

@ -0,0 +1,15 @@
package update
import (
"github.com/spf13/cobra"
)
var UpdateCmd = &cobra.Command{
Use: "update",
Short: `It updates ChaosCenter account's details.
Examples
#update password
litmusctl update password
`,
}

74
pkg/cmd/upgrade/infra.go Normal file
View File

@ -0,0 +1,74 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package upgrade
import (
"context"
"fmt"
"os"
"strings"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// createCmd represents the create command
var infraCmd = &cobra.Command{
Use: "chaos-infra",
Short: `Upgrades the LitmusChaos Execution plane.`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
projectID, err := cmd.Flags().GetString("project-id")
utils.PrintError(err)
if projectID == "" {
utils.White_B.Print("\nEnter the project ID: ")
fmt.Scanln(&projectID)
}
infraID, err := cmd.Flags().GetString("chaos-infra-id")
utils.PrintError(err)
if infraID == "" {
utils.White_B.Print("\nEnter the Chaos Infra ID: ")
fmt.Scanln(&infraID)
}
kubeconfig, err := cmd.Flags().GetString("kubeconfig")
utils.PrintError(err)
output, err := apis.UpgradeInfra(context.Background(), credentials, projectID, infraID, kubeconfig)
if err != nil {
if strings.Contains(err.Error(), "no documents in result") {
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
os.Exit(1)
}
utils.Red.Print("\n❌ Failed upgrading Chaos Infrastructure: \n" + err.Error() + "\n")
os.Exit(1)
}
utils.White_B.Print("\n", output)
},
}
func init() {
UpgradeCmd.AddCommand(infraCmd)
infraCmd.Flags().String("project-id", "", "Enter the project ID")
infraCmd.Flags().String("kubeconfig", "", "Enter the kubeconfig path(default: $HOME/.kube/config))")
infraCmd.Flags().String("chaos-infra-id", "", "Enter the Chaos Infrastructure ID")
}

View File

@ -0,0 +1,31 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package upgrade
import (
"github.com/spf13/cobra"
)
// UpgradeCmd represents the upgrade command
var UpgradeCmd = &cobra.Command{
Use: "upgrade",
Short: `Examples:
#upgrade version of your Chaos Infrastructure
litmusctl upgrade chaos-infra --chaos-infra-id="4cc25543-36c8-4373-897b-2e5dbbe87bcf" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
`,
}

View File

@ -1,11 +1,11 @@
/*
Copyright © 2020 NAME HERE <EMAIL ADDRESS>
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@ -16,18 +16,124 @@ limitations under the License.
package version
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"runtime"
"github.com/litmuschaos/litmusctl/pkg/constants"
"github.com/litmuschaos/litmusctl/pkg/utils"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
)
// versionCmd represents the version command
var VersionCmd = &cobra.Command{
Use: "version",
Short: "Litmusctl version",
Long: `Litmusctl cli version`,
Short: "Displays the version of litmusctl",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Litmusctl version: ", constants.CLIVersion)
cliVersion := os.Getenv("CLIVersion")
if cliVersion == "" {
utils.Red.Println("Error: CLIVersion environment variable is not set.")
return
}
compatibilityArr := utils.CompatibilityMatrix[cliVersion]
utils.White_B.Println("Litmusctl version: ", os.Getenv("CLIVersion"))
utils.White_B.Println("Compatible ChaosCenter versions: ")
utils.White_B.Print("[ ")
for _, v := range compatibilityArr {
utils.White_B.Print("'" + v + "' ")
}
utils.White_B.Print("]\n")
},
}
var UpdateCmd = &cobra.Command{
Use: "update",
Short: "Changes the version of litmusctl",
Args: cobra.ExactArgs(1),
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
updateVersion := args[0]
homeDir, err := homedir.Dir()
if err != nil {
utils.PrintError(err)
}
var assetURL string = "https://litmusctl-production-bucket.s3.amazonaws.com/"
var binaryName string = "litmusctl"
switch runtime.GOOS {
case "windows":
if runtime.GOARCH == "386" {
binaryName += "-windows-386-" + updateVersion + ".tar.gz"
assetURL += binaryName
} else if runtime.GOARCH == "amd64" {
binaryName += "-windows-amd64-" + updateVersion + ".tar.gz"
assetURL += binaryName
} else {
binaryName += "-windows-arm64-" + updateVersion + ".tar.gz"
assetURL += binaryName
}
case "linux":
if runtime.GOARCH == "arm64" {
binaryName += "-linux-arm64-" + updateVersion + ".tar.gz"
assetURL += binaryName
} else if runtime.GOARCH == "amd64" {
binaryName += "-linux-amd64-" + updateVersion + ".tar.gz"
assetURL += binaryName
} else if runtime.GOARCH == "arm" {
binaryName += "-linux-arm-" + updateVersion + ".tar.gz"
assetURL += binaryName
} else {
binaryName += "-linux-386-" + updateVersion + ".tar.gz"
assetURL += binaryName
}
case "darwin":
if runtime.GOARCH == "amd64" {
binaryName += "-darwin-amd64-" + updateVersion + ".tar.gz"
assetURL += binaryName
}
}
utils.White.Print("Downloading:\n")
resp2, err := http.Get(assetURL)
if err != nil {
utils.PrintError(err)
}
tempFile, err := os.CreateTemp("", binaryName)
if err != nil {
utils.PrintError(err)
return
}
defer os.Remove(tempFile.Name())
_, err = io.Copy(tempFile, resp2.Body)
if err != nil {
utils.PrintError(err)
return
}
utils.White_B.Print("OK\n")
tempFile.Close()
tarCmd := exec.Command("tar", "xzf", tempFile.Name(), "-C", homeDir)
tarCmd.Stdout = os.Stdout
tarCmd.Stderr = os.Stderr
utils.White_B.Print("Extracting binary...\n")
err = tarCmd.Run()
if err != nil {
utils.PrintError(err)
return
}
utils.White_B.Print("Binary extracted successfully\n")
},
}
func init() {
VersionCmd.AddCommand(UpdateCmd)
}

View File

@ -1,14 +0,0 @@
package common
type Agent struct {
AgentName string `json:"cluster_name"`
Mode string
Description string `json:"description,omitempty"`
PlatformName string `json:"platform_name"`
ProjectId string `json:"project_id"`
ClusterType string `json:"cluster_type"`
Namespace string
ServiceAccount string
NsExists bool
SAExists bool
}

View File

@ -1,16 +0,0 @@
package common
import (
"fmt"
"os/exec"
)
func ApplyYaml(token string, cred Credentials, yamlPath string) (output string, err error) {
path := fmt.Sprintf("%s/%s/%s.yaml", cred.Host, yamlPath, token)
args := []string{"kubectl", "apply", "-f", path}
stdout, err := exec.Command(args[0], args[1:]...).CombinedOutput()
if err != nil {
err = fmt.Errorf("Error: %v", err)
}
return string(stdout), err
}

View File

@ -1,171 +0,0 @@
package chaos
import (
"fmt"
util "github.com/litmuschaos/litmusctl/pkg/common"
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
resty "github.com/go-resty/resty/v2"
"github.com/litmuschaos/litmusctl/pkg/constants"
)
type AgentRegistrationData struct {
Errors []Errors `json:"errors"`
Data AgentRegister `json:"data"`
}
type Errors struct {
Message string `json:"message"`
Path []string `json:"path"`
}
type AgentRegister struct {
UserAgentReg UserAgentReg `json:"userClusterReg"`
}
type UserAgentReg struct {
ClusterID string `json:"cluster_id"`
ClusterName string `json:"cluster_name"`
Token string `json:"token"`
}
// GetAgentDetails take details of agent as input
func GetAgentDetails(pid string, t util.Token, cred util.Credentials) util.Agent {
var newAgent util.Agent
// Get agent name as input
fmt.Println("\n🔗 Enter the details of the agent ----")
// Label for goto statement in case of invalid agent name
AGENT_NAME:
fmt.Print("🤷 Agent Name: ")
newAgent.AgentName = util.Scanner()
if newAgent.AgentName == "" {
fmt.Println("⛔ Agent name cannot be empty. Please enter a valid name.")
goto AGENT_NAME
}
// Check if agent with the given name already exists
if AgentExists(pid, newAgent.AgentName, t, cred) {
fmt.Println("🚫 Agent with the given name already exists.")
// Print agent list if existing agent name is entered twice
GetAgentList(pid, t, cred)
fmt.Println("❗ Please enter a different name.")
goto AGENT_NAME
}
// Get agent description as input
fmt.Print("📘 Agent Description: ")
newAgent.Description = util.Scanner()
// Get platform name as input
newAgent.PlatformName = util.GetPlatformName()
// Set agent type
newAgent.ClusterType = constants.AgentType
// Set project id
newAgent.ProjectId = pid
// Get namespace
newAgent.Namespace, newAgent.NsExists = k8s.ValidNs(constants.ChaosAgentLabel)
return newAgent
}
type AgentData struct {
Data AgentList `json:"data"`
}
type AgentDetails struct {
AgentName string `json:"cluster_name"`
IsActive bool `json:"is_active"`
IsRegistered bool `json:"is_registered"`
ClusterID string `json:"cluster_id"`
}
type AgentList struct {
GetAgent []AgentDetails `json:"getCluster"`
}
// AgentExists checks if an agent of the given name already exists
func AgentExists(pid, agentName string, t util.Token, cred util.Credentials) bool {
var agents AgentData
client := resty.New()
bodyData := `{"query":"query{\n getCluster(project_id: \"` + fmt.Sprintf("%s", pid) + `\"){\n cluster_name\n }\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(bodyData).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&agents).
Post(
fmt.Sprintf(
"%s/api/query",
cred.Host,
),
)
if err != nil || !resp.IsSuccess() {
fmt.Println("Error getting agent names: ", err)
return true
}
for i := range agents.Data.GetAgent {
if agentName == agents.Data.GetAgent[i].AgentName {
fmt.Println(agents.Data.GetAgent[i].AgentName)
return true
}
}
return false
}
// GetAgentList lists the agent connected to the specified project
func GetAgentList(pid string, t util.Token, cred util.Credentials) {
var agents AgentData
client := resty.New()
bodyData := `{"query":"query{\n getCluster(project_id: \"` + fmt.Sprintf("%s", pid) + `\"){\n cluster_name\n }\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(bodyData).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&agents).
Post(
fmt.Sprintf(
"%s/api/query",
cred.Host,
),
)
if err != nil || !resp.IsSuccess() {
fmt.Println("Error in geting agent list: ", err)
}
fmt.Print("\n📘 Registered agents list -----------\n\n")
for i := range agents.Data.GetAgent {
fmt.Println("-", agents.Data.GetAgent[i].AgentName)
}
fmt.Println("\n-------------------------------------")
}
// RegisterAgent registers the agent with the given details
func RegisterAgent(c util.Agent, t util.Token, cred util.Credentials) (AgentRegistrationData, error) {
var cr AgentRegistrationData
client := resty.New()
bodyData := `{"query":"mutation {\n userClusterReg(clusterInput: \n { \n cluster_name: \"` + fmt.Sprintf("%s", c.AgentName) + `\", \n description: \"` + fmt.Sprintf("%s", c.Description) + `\",\n \tplatform_name: \"` + fmt.Sprintf("%s", c.PlatformName) + `\",\n project_id: \"` + fmt.Sprintf("%s", c.ProjectId) + `\",\n cluster_type: \"` + fmt.Sprintf("%s", c.ClusterType) + `\",\n agent_scope: \"` + fmt.Sprintf("%s", c.Mode) + `\",\n agent_namespace: \"` + fmt.Sprintf("%s", c.Namespace) + `\",\n serviceaccount: \"` + fmt.Sprintf("%s", c.ServiceAccount) + `\",\n agent_ns_exists: ` + fmt.Sprintf("%t", c.NsExists) + `,\n agent_sa_exists: ` + fmt.Sprintf("%t", c.SAExists) + `,\n }){\n cluster_id\n cluster_name\n token\n }\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(bodyData).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&cr).
Post(
fmt.Sprintf(
"%s/api/query",
cred.Host,
),
)
if err != nil || !resp.IsSuccess() {
fmt.Println(err)
fmt.Println(resp.IsSuccess())
return AgentRegistrationData{}, err
}
return cr, nil
}

View File

@ -1,27 +0,0 @@
package chaos
import (
"fmt"
"github.com/litmuschaos/litmusctl/pkg/constants"
)
// GetMode gets mode of agent installation as input
func GetMode() string {
var mode int
fmt.Println("\n🔌 Installation Modes:\n1. Cluster\n2. Namespace")
fmt.Print("\n👉 Select Mode [", constants.DefaultMode, "]: ")
fmt.Scanln(&mode)
if mode == 0 {
return "namespace"
}
for mode < 1 || mode > 2 {
fmt.Println("🚫 Invalid mode. Please enter the correct mode")
fmt.Print("👉 Select Mode [", constants.DefaultMode, "]: ")
fmt.Scanln(&mode)
}
if mode == 1 {
return "cluster"
}
return constants.DefaultMode
}

View File

@ -1,68 +0,0 @@
package chaos
import (
"fmt"
util "github.com/litmuschaos/litmusctl/pkg/common"
resty "github.com/go-resty/resty/v2"
)
type ProjectDetails struct {
Data Data `json:"data"`
}
type Data struct {
GetUser GetUser `json:"getUser"`
}
type GetUser struct {
Projects []Project `json:"projects"`
}
type Project struct {
ID string `json:"id"`
Name string `json:"name"`
}
// GetProjectDetails fetches details of the input user
func GetProjectDetails(t util.Token, c util.Credentials) (ProjectDetails, interface{}) {
var user ProjectDetails
client := resty.New()
bodyData := `{"query":"query {\n getUser(username: \"` + fmt.Sprintf("%s", c.Username) + `\"){\n projects{\n id\n name\n}\n}\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(bodyData).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&user).
Post(
fmt.Sprintf(
"%s/api/query",
c.Host,
),
)
if err != nil || !resp.IsSuccess() {
return ProjectDetails{}, err
}
return user, nil
}
// GetProject display list of projects and returns the project id based on input
func GetProject(u ProjectDetails) string {
var pid int
fmt.Println("\n✨ Projects List:")
for index := range u.Data.GetUser.Projects {
projectNo := index + 1
fmt.Printf("%d. %s\n", projectNo, u.Data.GetUser.Projects[index].Name)
}
fmt.Print("\n🔎 Select Project: ")
fmt.Scanln(&pid)
for pid < 1 || pid > len(u.Data.GetUser.Projects) {
fmt.Println("❗ Invalid Project. Please select a correct one.")
fmt.Print("\n🔎 Select Project: ")
fmt.Scanln(&pid)
}
pid = pid - 1
return u.Data.GetUser.Projects[pid].ID
}

View File

@ -1,63 +0,0 @@
package chaos
import (
"fmt"
"os"
"github.com/litmuschaos/litmusctl/pkg/common"
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
"github.com/litmuschaos/litmusctl/pkg/constants"
)
func Register(t common.Token, c common.Credentials) {
// Fetch project details
user, uErr := GetProjectDetails(t, c)
if uErr != nil {
fmt.Printf("\n❌ Fetching project details failed: [%s]", uErr)
os.Exit(1)
}
// Fetch project id
pid := GetProject(user)
// Get mode of installation as input
mode := GetMode()
// Check if user has sufficient permissions based on mode
fmt.Println("\n🏃 Running prerequisites check....")
k8s.ValidateSAPermissions(mode)
// Get agent details as input
newAgent := GetAgentDetails(pid, t, c)
newAgent.Mode = mode
// Get service account as input
newAgent.ServiceAccount, newAgent.SAExists = k8s.ValidSA(newAgent.Namespace)
// Display details of agent to be connected
common.Summary(newAgent, "chaos")
// Confirm before connecting the agent
common.Confirm()
// Register agent
agent, cerror := RegisterAgent(newAgent, t, c)
if cerror != nil {
fmt.Printf("\n❌ Agent registration failed: [%s]\n", cerror.Error())
os.Exit(1)
}
path := fmt.Sprintf("%s/%s/%s.yaml", c.Host, constants.ChaosYamlPath, agent.Data.UserAgentReg.Token)
fmt.Println("Applying YAML:\n", path)
// Print error message in case Data field is null in response
if (agent.Data == AgentRegister{}) {
fmt.Printf("\n🚫 Agent registration failed: [%s]\n", agent.Errors[0].Message)
os.Exit(1)
}
// Apply agent registration yaml
yamlOutput, yerror := common.ApplyYaml(agent.Data.UserAgentReg.Token, c, constants.ChaosYamlPath)
if yerror != nil {
fmt.Printf("\n❌ Failed in applying registration yaml: [%s]\n", yerror.Error())
os.Exit(1)
}
fmt.Println("\n", yamlOutput)
// Watch subscriber pod status
k8s.WatchPod(newAgent.Namespace, constants.ChaosAgentLabel)
fmt.Println("\n🚀 Agent Registration Successful!! 🎉")
fmt.Println("👉 Litmus agents can be accessed here: " + fmt.Sprintf("%s/%s", c.Host, constants.ChaosAgentPath))
}

View File

@ -1,314 +0,0 @@
package chaos
import (
"fmt"
"log"
"net/url"
"github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
"github.com/go-resty/resty/v2"
ymlparser "gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
)
type ListPkgData struct {
Data struct {
ListHubPkgData []PackageData `json:"ListHubPkgData"`
} `json:"data"`
}
type PackageData struct {
Experiments []string `json:"Experiments"`
ChartName string `json:"chartName"`
}
type YAMLData struct {
Data struct {
GetYAMLData string `json:"getYAMLData"`
} `json:"data"`
}
type GenerateWorkflowInputs struct {
HubName string
ProjectID string
ChartName string
ExperimentName *string
AccessToken string
FileType *string
URL *url.URL
WorkName string
WorkNamespace string
ClusterID string
Packages []*PackageData
}
type GetClusters struct {
Data struct {
GetCluster []struct {
ClusterID string `json:"cluster_id"`
ClusterName string `json:"cluster_name"`
} `json:"getCluster"`
} `json:"data"`
}
type GetHubStatus struct {
Data struct {
GetHubStatus []struct {
ID string `json:"id"`
HubName string `json:"HubName"`
} `json:"getHubStatus"`
} `json:"data"`
}
func GetYamlData(inputs GenerateWorkflowInputs) (YAMLData, error) {
client := resty.New()
var yamlDataResponse YAMLData
gql_query := `{"query":"query {\n getYAMLData(experimentInput: {\n ProjectID: \"` + inputs.ProjectID + `\"\n HubName: \"` + inputs.HubName + `\"\n ChartName: \"` + inputs.ChartName + `\"\n ExperimentName: \"` + *inputs.ExperimentName + `\"\n FileType: \"` + *inputs.FileType + `\"\n \n })\n}"}`
response, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", inputs.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(gql_query).
SetResult(&yamlDataResponse).
Post(
fmt.Sprintf(
"%s/api/query",
inputs.URL,
),
)
if err != nil || !response.IsSuccess() {
return YAMLData{}, err
}
return yamlDataResponse, nil
}
func GenerateWorkflow(wf_inputs GenerateWorkflowInputs) ([]byte, error) {
var yaml v1alpha1.Workflow
yaml.APIVersion = "argoproj.io/v1alpha1"
yaml.Kind = "Workflow"
yaml.ObjectMeta.Name = wf_inputs.WorkName
yaml.ObjectMeta.Namespace = wf_inputs.WorkNamespace
yaml.ObjectMeta.Labels = map[string]string{
"cluster_id": wf_inputs.ClusterID,
}
var pram v1alpha1.Parameter
pram.Name = "adminModeNamespace"
pram.Value = &wf_inputs.WorkNamespace
yaml.Spec.Arguments.Parameters = append(yaml.Spec.Arguments.Parameters, pram)
//
yaml.Spec.Entrypoint = "custom-chaos"
var b = true
var i int64 = 1000
yaml.Spec.SecurityContext = &v1.PodSecurityContext{
RunAsNonRoot: &b,
RunAsUser: &i,
}
var (
custom_chaos v1alpha1.Template
install_experiments v1alpha1.Template
engines []v1alpha1.Template
revert_chaos v1alpha1.Template
)
custom_chaos.Name = "custom-chaos"
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
{
Name: "install-chaos-experiments",
Template: "install-chaos-experiments",
},
}})
install_experiments.Name = "install-chaos-experiments"
install_experiments.Container = &v1.Container{
Image: "lachlanevenson/k8s-kubectl",
Command: []string{"sh", "-c"},
Args: []string{""},
}
revert_chaos.Name = "revert-chaos"
revert_chaos.Container = &v1.Container{
Image: "lachlanevenson/k8s-kubectl",
Command: []string{"sh", "-c"},
Args: []string{"kubectl delete chaosengine "},
}
for _, pkg := range wf_inputs.Packages {
for _, experiment := range pkg.Experiments {
wf_inputs.ExperimentName = &experiment
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
{
Name: experiment,
Template: experiment,
},
}})
//
var file_type = "experiment"
wf_inputs.FileType = &file_type
yamlData, err := GetYamlData(wf_inputs)
if err != nil {
log.Print(err)
}
install_experiments.Inputs.Artifacts = append(install_experiments.Inputs.Artifacts,
v1alpha1.Artifact{
Name: experiment,
Path: "/tmp/" + experiment + ".yaml",
ArtifactLocation: v1alpha1.ArtifactLocation{
Raw: &v1alpha1.RawArtifact{
Data: fmt.Sprint(yamlData.Data.GetYAMLData),
},
},
})
install_experiments.Container.Args[0] += "kubectl apply -f /tmp/" + experiment + ".yaml" + "-n {{workflow.parameters.adminModeNamespace}} | "
revert_chaos.Container.Args[0] += experiment + " "
file_type = "engine"
wf_inputs.FileType = &file_type
yamlData, err = GetYamlData(wf_inputs)
if err != nil {
log.Print(err)
}
var engine v1alpha1.Template
engine.Name = experiment
engine.Container = &v1.Container{
Args: []string{
`-file=/tmp/chaosengine-` + experiment + `.yaml`,
"-saveName=/tmp/engine-name",
},
Image: "litmuschaos/litmus-checker:latest",
}
engine.Inputs.Artifacts = append(engine.Inputs.Artifacts, v1alpha1.Artifact{
Name: experiment,
Path: "/tmp/chaosengine-" + experiment + ".yaml",
ArtifactLocation: v1alpha1.ArtifactLocation{
Raw: &v1alpha1.RawArtifact{
Data: fmt.Sprint(yamlData.Data.GetYAMLData),
},
},
})
engines = append(engines, engine)
}
}
// Custom chaos
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
{
Name: "revert-chaos",
Template: "revert-chaos",
},
}})
yaml.Spec.Templates = append(yaml.Spec.Templates, custom_chaos)
// Install experiments
install_experiments.Container.Args[0] += "sleep 30"
yaml.Spec.Templates = append(yaml.Spec.Templates, install_experiments)
//
// Install engines
yaml.Spec.Templates = append(yaml.Spec.Templates, engines...)
//
// Revert Chaos
revert_chaos.Container.Args[0] += "-n {{workflow.parameters.adminModeNamespace}}"
yaml.Spec.Templates = append(yaml.Spec.Templates, revert_chaos)
yamlByte, err := ymlparser.Marshal(yaml)
if err != nil {
return nil, err
}
return yamlByte, nil
}
func GetClustersQuery(project_id string, access_token string, url *url.URL) (GetClusters, error) {
client := resty.New()
var getClusters GetClusters
gql_query := `{"query":"query {\n getCluster(project_id: \"` + project_id + `\"){\n cluster_id\n cluster_name\n }\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(gql_query).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&getClusters).
Post(
fmt.Sprintf(
"%s/api/query",
url,
),
)
if err != nil || !resp.IsSuccess() {
return GetClusters{}, err
}
return getClusters, nil
}
func GetHubStatusQuery(project_id string, access_token string, url *url.URL) (GetHubStatus, error) {
client := resty.New()
var getHubStatus GetHubStatus
gql_query := `{"query":"query {\n getHubStatus(projectID: \"` + project_id + `\"){\n id\n HubName \n }\n}"}`
response, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(gql_query).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&getHubStatus).
Post(
fmt.Sprintf(
"%s/api/query",
url,
),
)
if err != nil || !response.IsSuccess() {
return GetHubStatus{}, nil
}
return getHubStatus, nil
}
func ListPkgDataQuery(project_id string, hub_id string, access_token string, url *url.URL) (ListPkgData, error) {
var pkgdata ListPkgData
client := resty.New()
gql_query := `{"query":"query {\n ListHubPkgData(projectID: \"` + project_id + `\", hubID: \"` + hub_id + `\"){\n Experiments\n chartName\n }\n}"}`
response, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(gql_query).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&pkgdata).
Post(
fmt.Sprintf(
"%s/api/query",
url,
),
)
if err != nil || !response.IsSuccess() {
log.Print(err)
}
return pkgdata, nil
}

View File

@ -1,6 +0,0 @@
package common
type Errors struct {
Message string `json:"message"`
Path []string `json:"path"`
}

View File

@ -1,147 +0,0 @@
package common
import (
"bufio"
"fmt"
"net/url"
"os"
"strings"
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
"github.com/litmuschaos/litmusctl/pkg/constants"
"golang.org/x/crypto/ssh/terminal"
)
func GetUsername() string {
var username string
fmt.Print("🤔 Username [", constants.DefaultUsername, "]: ")
fmt.Scanln(&username)
if username == "" {
username = constants.DefaultUsername
}
return username
}
func GetPortalURL() (*url.URL, error) {
var host string
fmt.Print("👉 Host URL where litmus is installed: ")
fmt.Scanln(&host)
for host == "" {
fmt.Println("⛔ Host URL can't be empty!!")
fmt.Print("👉 Host URL: ")
fmt.Scanln(&host)
}
host = strings.TrimRight(host, "/")
newUrl, err := url.Parse(host)
if err != nil {
return &url.URL{}, err
}
return newUrl, nil
}
func GetPassword() []byte {
var pass []byte
fmt.Print("🙈 Password: ")
pass, _ = terminal.ReadPassword(0)
if pass == nil {
fmt.Println("\n⛔ Password cannot be empty!")
os.Exit(1)
}
return pass
}
// GetMode gets mode of agent installation as input
func GetMode() string {
var mode int
fmt.Println("\n🔌 Installation Modes:\n1. Cluster\n2. Namespace")
fmt.Print("\n👉 Select Mode [", constants.DefaultMode, "]: ")
fmt.Scanln(&mode)
if mode == 0 {
return "namespace"
}
for mode < 1 || mode > 2 {
fmt.Println("🚫 Invalid mode. Please enter the correct mode")
fmt.Print("👉 Select Mode [", constants.DefaultMode, "]: ")
fmt.Scanln(&mode)
}
if mode == 1 {
return "cluster"
}
return constants.DefaultMode
}
func Confirm() {
var wish string
fmt.Print("\n🤷 Do you want to continue with the above details? [Y/N]: ")
fmt.Scanln(&wish)
if wish == "Y" || wish == "Yes" || wish == "yes" || wish == "y" {
fmt.Println("👍 Continuing agent registration!!")
} else {
fmt.Println("✋ Exiting agent registration!!")
os.Exit(1)
}
}
// getPlatformName displays a list of platforms, takes the
// platform name as input and returns the selected platform
//
// TODO --
// - Entering any character other than numbers returns 0. Input validation need to be done.
// - If input is given as "123abc", "abc" will be used for next user input. Buffer need to be read completely.
// - String literals like "AWS" are used at multiple places. Need to be changed to constants.
func GetPlatformName() string {
var platform int
discoveredPlatform := DiscoverPlatform()
fmt.Println("📦 Platform List")
fmt.Println(constants.PlatformList)
fmt.Print("🔎 Select Platform [", discoveredPlatform, "]: ")
fmt.Scanln(&platform)
switch platform {
case 0:
return discoveredPlatform
case 1:
return "AWS"
case 2:
return "GKE"
case 3:
return "Openshift"
case 4:
return "Rancher"
default:
return constants.DefaultPlatform
}
}
func Scanner() string {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
return scanner.Text()
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
return ""
}
// Summary display the agent details based on input
func Summary(agent Agent, product string) {
fmt.Println("\n📌 Summary --------------------------")
fmt.Println("\nAgent Name: ", agent.AgentName)
fmt.Println("Agent Description: ", agent.Description)
fmt.Println("Platform Name: ", agent.PlatformName)
if ok, _ := k8s.NsExists(agent.Namespace); ok {
fmt.Println("Namespace: ", agent.Namespace)
} else {
fmt.Println("Namespace: ", agent.Namespace, "(new)")
}
if product == "chaos" {
if k8s.SAExists(agent.Namespace, agent.ServiceAccount) {
fmt.Println("Service Account: ", agent.ServiceAccount)
} else {
fmt.Println("Service Account: ", agent.ServiceAccount, "(new)")
}
fmt.Println("Installation Mode: ", agent.Mode)
}
fmt.Println("\n-------------------------------------")
}

View File

@ -1,109 +0,0 @@
package k8s
import (
"context"
"fmt"
"log"
"os"
authorizationv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
discovery "k8s.io/client-go/discovery"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
)
type CanIOptions struct {
NoHeaders bool
Namespace string
AuthClient authorizationv1client.AuthorizationV1Interface
DiscoveryClient discovery.DiscoveryInterface
Verb string
Resource schema.GroupVersionResource
Subresource string
ResourceName string
}
func CheckSAPermissions(verb, resource string, print bool) (bool, error) {
var o CanIOptions
o.Verb = verb
o.Resource.Resource = resource
client, err := ClientSet()
if err != nil {
log.Fatal(err)
}
AuthClient := client.AuthorizationV1()
var sar *authorizationv1.SelfSubjectAccessReview
sar = &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: o.Namespace,
Verb: o.Verb,
Group: o.Resource.Group,
Resource: o.Resource.Resource,
Subresource: o.Subresource,
Name: o.ResourceName,
},
},
}
response, err := AuthClient.SelfSubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
if err != nil {
return false, err
}
if response.Status.Allowed {
if print {
fmt.Println("🔑 ", resource, "- ✅")
}
} else {
if print {
fmt.Println("🔑 ", resource, "- ❌")
}
if len(response.Status.Reason) > 0 {
fmt.Println(response.Status.Reason)
}
if len(response.Status.EvaluationError) > 0 {
fmt.Println(response.Status.EvaluationError)
}
}
return response.Status.Allowed, nil
}
func ValidateSAPermissions(mode string) {
var pems [2]bool
var err error
if mode == "cluster" {
resources := [2]string{"clusterrole", "clusterrolebinding"}
i := 0
for _, resource := range resources {
pems[i], err = CheckSAPermissions("create", resource, true)
if err != nil {
fmt.Println(err)
}
i++
}
} else {
resources := [2]string{"role", "rolebinding"}
i := 0
for _, resource := range resources {
pems[i], err = CheckSAPermissions("create", resource, true)
if err != nil {
fmt.Println(err)
}
i++
}
}
for _, pem := range pems {
if !pem {
fmt.Println("\n🚫 You don't have sufficient permissions.\n🙄 Please use a service account with sufficient permissions.")
os.Exit(1)
}
}
fmt.Println("\n🌟 Sufficient permissions. Registering Agent")
}

View File

@ -1,37 +0,0 @@
package k8s
import (
"fmt"
"os"
"path/filepath"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
// Returns a new kubernetes client set
func ClientSet() (*kubernetes.Clientset, error) {
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kcfg := filepath.Join(home, ".kube", "config")
kubeconfig = &kcfg
} else {
fmt.Println("ERROR: Clientset generation failed!")
os.Exit(1)
}
// create the config
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
fmt.Println("ERROR: ", err.Error())
os.Exit(1)
}
// create the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Println("ERROR: ", err.Error())
os.Exit(1)
}
return clientset, err
}

View File

@ -1,78 +0,0 @@
package k8s
import (
"context"
"fmt"
"log"
"os"
"github.com/litmuschaos/litmusctl/pkg/constants"
v1 "k8s.io/api/core/v1"
k8serror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NsExists checks if the given namespace already exists
func NsExists(namespace string) (bool, error) {
clientset, err := ClientSet()
if err != nil {
return false, err
}
ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
if k8serror.IsNotFound(err) {
return false, nil
}
if err == nil && ns != nil {
return true, nil
}
return false, err
}
// ValidNs takes a valid namespace as input from user
func ValidNs(label string) (string, bool) {
var namespace string
var nsExists bool
fmt.Print("📁 Enter the namespace (new or existing) [", constants.DefaultNs, "]: ")
fmt.Scanln(&namespace)
if namespace == "" {
namespace = constants.DefaultNs
}
ok, err := NsExists(namespace)
if err != nil {
fmt.Printf("\n Namespace existence check failed: {%s}\n", err.Error())
os.Exit(1)
}
if ok {
if PodExists(namespace, label) {
fmt.Println("🚫 Subscriber already present. Please enter a different namespace")
namespace, nsExists = ValidNs(label)
} else {
nsExists = true
fmt.Println("👍 Continuing with", namespace, "namespace")
}
} else {
if val, _ := CheckSAPermissions("create", "namespace", false); !val {
fmt.Println("🚫 You don't have permissions to create a namespace.\n🙄 Please enter an existing namespace.")
namespace, nsExists = ValidNs(label)
}
nsExists = false
}
return namespace, nsExists
}
// CreateNs creates the given namespace
func CreateNs(namespace string) {
clientset, err := ClientSet()
if err != nil {
log.Fatal(err)
}
nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s", namespace)}}
_, newErr := clientset.CoreV1().Namespaces().Create(context.TODO(), nsSpec, metav1.CreateOptions{})
if newErr != nil {
log.Fatal(err.Error())
}
fmt.Println(namespace, "namespace created successfully")
}

View File

@ -1,58 +0,0 @@
package k8s
import (
"context"
"fmt"
"log"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// WatchPod watches for the pod status
func WatchPod(namespace, label string) {
clientset, err := ClientSet()
if err != nil {
log.Fatal(err)
}
watch, err := clientset.CoreV1().Pods(namespace).Watch(context.TODO(), metav1.ListOptions{
LabelSelector: label,
})
if err != nil {
log.Fatal(err.Error())
}
for event := range watch.ResultChan() {
p, ok := event.Object.(*v1.Pod)
if !ok {
log.Fatal("unexpected type")
}
fmt.Println("💡 Connecting agent to Litmus Portal.")
if p.Status.Phase == "Running" {
fmt.Println("🏃 Agents running!!")
watch.Stop()
break
}
}
}
type PodList struct {
Items []string
}
// PodExists checks if the pod with the given label already exists in the given namespace
func PodExists(namespace, label string) bool {
clientset, err := ClientSet()
if err != nil {
log.Fatal(err)
}
watch, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: label,
})
if err != nil {
log.Fatal(err.Error())
}
if len(watch.Items) >= 1 {
return true
}
return false
}

View File

@ -1,42 +0,0 @@
package k8s
import (
"context"
"fmt"
"log"
"github.com/litmuschaos/litmusctl/pkg/constants"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SAExists checks if the given service account exists in the given namespace
func SAExists(namespace, serviceaccount string) bool {
clientset, err := ClientSet()
if err != nil {
log.Fatal(err)
}
msg := fmt.Sprintf("serviceaccounts \"%s\" not found", serviceaccount)
_, newErr := clientset.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), serviceaccount, metav1.GetOptions{})
if newErr != nil {
if newErr.Error() == msg {
return false
}
log.Fatal(newErr)
}
return true
}
// ValidSA gets a valid service account as input
func ValidSA(namespace string) (string, bool) {
var sa string
fmt.Print("🔑 Enter service account [", constants.DefaultSA, "]: ")
fmt.Scanln(&sa)
if sa == "" {
sa = constants.DefaultSA
}
if SAExists(namespace, sa) {
fmt.Println("👍 Using the existing service account")
return sa, true
}
return sa, false
}

View File

@ -1,115 +0,0 @@
package common
import (
"context"
"strings"
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
"github.com/litmuschaos/litmusctl/pkg/constants"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// discoverPlatform determines the host platform and returns it
func DiscoverPlatform() string {
if ok, _ := IsAWSPlatform(); ok {
return "AWS"
}
if ok, _ := IsGKEPlatform(); ok {
return "GKE"
}
if ok, _ := IsOpenshiftPlatform(); ok {
return "Openshift"
}
if ok, _ := k8s.NsExists("cattle-system"); ok {
return "Rancher"
}
return constants.DefaultPlatform
}
// IsAWSPlatform determines if the host platform is AWS
// by checking the ProviderID inside node spec
//
// Sample node custom resource of an AWS node
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": "aws:///us-east-2b/i-0bf24d83f4b993738"
// }
// }
// }
func IsAWSPlatform() (bool, error) {
clientset, err := k8s.ClientSet()
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil {
return false, err
}
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, constants.AWSIdentifier) {
return true, nil
}
return false, nil
}
// IsGKEPlatform determines if the host platform is GKE
// by checking the ProviderID inside node spec
//
// Sample node custom resource of an GKE node
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": ""
// }
// }
// }
func IsGKEPlatform() (bool, error) {
clientset, err := k8s.ClientSet()
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil {
return false, err
}
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, constants.GKEIdentifier) {
return true, nil
}
return false, nil
}
// IsOpenshiftPlatform determines if the host platform
// is Openshift by checking "node.openshift.io/os_id"
// label on the nodes
//
// Sample node custom resource of an Openshift node
// {
// "apiVersion": "v1",
// "kind": "Node",
// "metadata": {
// "labels": {
// "node.openshift.io/os_id": "rhcos"
// }
// }
// ....
// }
func IsOpenshiftPlatform() (bool, error) {
clientset, err := k8s.ClientSet()
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{
LabelSelector: constants.OpenshiftIdentifier,
})
if err != nil {
return false, err
}
if len(nodeList.Items) > 0 {
return true, nil
}
return false, nil
}

View File

@ -1,40 +0,0 @@
package common
import (
"fmt"
resty "github.com/go-resty/resty/v2"
)
type LaunchProductResponse struct {
Data LaunchProductData `json:"data"`
}
type LaunchProductData struct {
LaunchProduct string `json:"launchProduct"`
}
// GetUserDetails fetches details of the input user
func LaunchProduct(t Token, c Credentials, Product string) (LaunchProductResponse, error) {
var new LaunchProductResponse
client := resty.New()
bodyData := `{"query":"query{\n launchProduct(type: ` + fmt.Sprintf("%s", Product) + `)\n}"}`
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
SetHeader("Accept-Encoding", "gzip, deflate, br").
SetBody(bodyData).
// SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&new).
Post(
fmt.Sprintf(
"%s/api/query",
c.Host,
),
)
if err != nil || !resp.IsSuccess() {
return LaunchProductResponse{}, err
}
return new, nil
}

View File

@ -1,74 +0,0 @@
package common
import (
"fmt"
"net/url"
"os"
resty "github.com/go-resty/resty/v2"
)
type Cred struct {
Username string
Token string
}
type Credentials struct {
Host *url.URL
Username string
Password []byte
}
type Token struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
}
type AuthError struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}
// getToken fetches JWT token for the entered user credentials
func getToken(c Credentials, path string) (Token, AuthError) {
var authErr AuthError
client := resty.New()
token := Token{}
bodyData := map[string]interface{}{
"username": fmt.Sprintf("%s", c.Username),
"password": fmt.Sprintf("%s", c.Password),
}
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(bodyData).
//SetResult automatic unmarshalling for the request,
// if response status code is between 200 and 299
SetResult(&token).
SetError(&authErr).
Post(
fmt.Sprintf(
"%s/%s",
c.Host,
path,
),
)
if err != nil || !resp.IsSuccess() {
return Token{}, authErr
}
return token, AuthError{}
}
func Login(c Credentials, path string) Token {
t, err := getToken(c, path)
if err.Error != "" || t.AccessToken == "" {
fmt.Println("\nError: ", err.ErrorDescription, ":", err.Error)
fmt.Println("❌ Login Failed!!")
os.Exit(1)
}
fmt.Println("\n✅ Login Successful!")
return t
}

188
pkg/config/ops.go Normal file
View File

@ -0,0 +1,188 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"errors"
"os"
"github.com/litmuschaos/litmusctl/pkg/types"
"gopkg.in/yaml.v2"
)
var (
SkipSSLVerify bool = false
CACert string = ""
)
func CreateNewLitmusCtlConfig(filename string, config types.LitmuCtlConfig) error {
configByte, err := yaml.Marshal(config)
if err != nil {
return err
}
_, err = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
err = os.WriteFile(filename, configByte, 0644)
if err != nil {
return err
}
return nil
}
func FileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func GetFileLength(filename string) (int, error) {
data, err := os.ReadFile(filename)
if err != nil {
return -1, err
}
return len(string(data)), nil
}
func YamltoObject(filename string) (types.LitmuCtlConfig, error) {
data, err := os.ReadFile(filename)
if err != nil {
return types.LitmuCtlConfig{}, errors.New("File reading error " + err.Error())
}
obj := &types.LitmuCtlConfig{}
err = yaml.Unmarshal(data, obj)
if err != nil {
return types.LitmuCtlConfig{}, errors.New("File format not correct " + err.Error())
}
return *obj, nil
}
func ConfigSyntaxCheck(filename string) error {
obj, err := YamltoObject(filename)
if err != nil {
return err
}
if obj.APIVersion != "v1" || obj.Kind != "Config" {
return errors.New("File format not correct")
}
return nil
}
func UpdateLitmusCtlConfig(litmusconfig types.UpdateLitmusCtlConfig, filename string) error {
obj, err := YamltoObject(filename)
if err != nil {
return err
}
var outerflag = false
for i, act := range obj.Accounts {
if act.Endpoint == litmusconfig.Account.Endpoint {
var innerflag = false
obj.Accounts[i].ServerEndpoint = litmusconfig.ServerEndpoint
for j, user := range act.Users {
if user.Username == litmusconfig.Account.Users[0].Username {
obj.Accounts[i].Users[j].Username = litmusconfig.Account.Users[0].Username
obj.Accounts[i].Users[j].Token = litmusconfig.Account.Users[0].Token
obj.Accounts[i].Users[j].ExpiresIn = litmusconfig.Account.Users[0].ExpiresIn
innerflag, outerflag = true, true
}
}
if !innerflag {
obj.Accounts[i].Users = append(obj.Accounts[i].Users, litmusconfig.Account.Users[0])
outerflag = true
}
}
}
if !outerflag {
obj.Accounts = append(obj.Accounts, litmusconfig.Account)
}
obj.CurrentAccount = litmusconfig.CurrentAccount
obj.CurrentUser = litmusconfig.CurrentUser
err = writeObjToFile(obj, filename)
if err != nil {
return err
}
return nil
}
func UpdateCurrent(current types.Current, filename string) error {
obj, err := YamltoObject(filename)
if err != nil {
return err
}
obj.CurrentUser = current.CurrentUser
obj.CurrentAccount = current.CurrentAccount
err = writeObjToFile(obj, filename)
if err != nil {
return err
}
return nil
}
func writeObjToFile(obj types.LitmuCtlConfig, filename string) error {
_, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
byteObj, err := yaml.Marshal(obj)
if err != nil {
return err
}
err = os.WriteFile(filename, byteObj, 0644)
if err != nil {
return err
}
return nil
}
func IsAccountExists(obj types.LitmuCtlConfig, username string, endpoint string) bool {
for _, account := range obj.Accounts {
if account.Endpoint == endpoint {
for _, user := range account.Users {
if username == user.Username {
return true
}
}
}
}
return false
}

View File

@ -1,46 +0,0 @@
package constants
const (
// CLI version
CLIVersion = "v0.2.0"
// Default username
DefaultUsername = "admin"
// Default installation mode
DefaultMode = "cluster"
// Platform list
PlatformList = "1. AWS\n2. GKE\n3. Openshift\n4. Rancher\n5. Others"
// AWS identifier
AWSIdentifier = "aws://"
// GKE identifier
GKEIdentifier = "gce://"
// Openshift identifier
OpenshiftIdentifier = "node.openshift.io/os_id"
// Default platform name
DefaultPlatform = "Others"
// Label of subscriber agent being deployed
ChaosAgentLabel = "app=subscriber"
// Agent type is "external" for agents connected via litmusctl
AgentType = "external"
// Default namespace for agent installation
DefaultNs = "litmus"
// Default service account used for agent installation
DefaultSA = "litmus"
// Chaos agent registration yaml path
//ChaosYamlPath = "chaos/api/graphql/file"
ChaosYamlPath = "api/file"
ChaosAgentPath = "targets"
)

343
pkg/infra_ops/ops.go Normal file
View File

@ -0,0 +1,343 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package infra_ops
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis/environment"
"github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/k8s"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
func PrintExistingInfra(infra infrastructure.InfraData) {
utils.Red.Println("\nChaos Infrastructure with the given name already exists.")
// Print Chaos Infra list if existing Chaos Infra name is entered twice
utils.White_B.Println("\nConnected Chaos Infrastructure list:")
for i := range infra.Data.ListInfraDetails.Infras {
utils.White_B.Println("-", infra.Data.ListInfraDetails.Infras[i].Name)
}
utils.White_B.Println("\n❗ Please enter a different name.")
}
func PrintExistingEnvironments(env environment.ListEnvironmentData) {
// Print Chaos EnvironmentID list if Given ID doesn't exist
utils.White_B.Println("\nExisting Chaos Environments list:")
for i := range env.Data.ListEnvironmentDetails.Environments {
utils.White_B.Println("-", env.Data.ListEnvironmentDetails.Environments[i].EnvironmentID)
}
}
// GetProjectID display list of projects and returns the project id based on input
func GetProjectID(u apis.ProjectDetails) string {
var pid int
utils.White_B.Println("Project list:")
for index := range u.Data.Projects {
utils.White_B.Printf("%d. %s\n", index+1, u.Data.Projects[index].Name)
}
repeat:
utils.White_B.Printf("\nSelect a project [Range: 1-%s]: ", fmt.Sprint(len(u.Data.Projects)))
fmt.Scanln(&pid)
for pid < 1 || pid > len(u.Data.Projects) {
utils.Red.Println("❗ Invalid Project. Please select a correct one.")
goto repeat
}
return u.Data.Projects[pid-1].ID
}
// GetModeType gets mode of Chaos Infrastructure installation as input
func GetModeType() string {
repeat:
var (
cluster_no = 1
namespace_no = 2
mode = cluster_no
)
utils.White_B.Println("\nInstallation Modes:\n1. Cluster\n2. Namespace")
utils.White_B.Print("\nSelect Mode [Default: ", utils.DefaultMode, "] [Range: 1-2]: ")
fmt.Scanln(&mode)
if mode == 1 {
return "cluster"
}
if mode == 2 {
return "namespace"
}
if (mode != cluster_no) || (mode != namespace_no) {
utils.Red.Println("🚫 Invalid mode. Please enter the correct mode")
goto repeat
}
return utils.DefaultMode
}
// GetInfraDetails take details of Chaos Infrastructure as input
func GetInfraDetails(mode string, pid string, c types.Credentials, kubeconfig *string) (types.Infra, error) {
var newInfra types.Infra
// Get Infra name as input
utils.White_B.Println("\nEnter the details of the Chaos Infrastructure")
// Label for goto statement in case of invalid Chaos Infra name
INFRA_NAME:
utils.White_B.Print("\nChaos Infra Name: ")
newInfra.InfraName = utils.Scanner()
if newInfra.InfraName == "" {
utils.Red.Println("⛔ Chaos Infra name cannot be empty. Please enter a valid name.")
goto INFRA_NAME
}
// Check if Chaos Infra with the given name already exists
isInfraExist, _, infra := ValidateInfraNameExists(newInfra.InfraName, pid, c)
if isInfraExist {
PrintExistingInfra(infra)
goto INFRA_NAME
}
// Get Infra description as input
utils.White_B.Print("\nChaos Infrastructure Description: ")
newInfra.Description = utils.Scanner()
ENVIRONMENT:
utils.White_B.Print("\nChaos EnvironmentID: ")
newInfra.EnvironmentID = utils.Scanner()
if newInfra.EnvironmentID == "" {
utils.Red.Println("⛔ Chaos Environment ID cannot be empty. Please enter a valid Environment.")
goto ENVIRONMENT
}
// Check if Chaos Environment with the given name exists
Env, err := environment.ListChaosEnvironments(pid, c)
if err != nil {
return types.Infra{}, err
}
var isEnvExist = false
for i := range Env.Data.ListEnvironmentDetails.Environments {
if newInfra.EnvironmentID == Env.Data.ListEnvironmentDetails.Environments[i].EnvironmentID {
utils.White_B.Println(Env.Data.ListEnvironmentDetails.Environments[i].EnvironmentID)
isEnvExist = true
break
}
}
if !isEnvExist {
utils.Red.Println("\nChaos Environment with the given ID doesn't exists.")
PrintExistingEnvironments(Env)
utils.White_B.Println("\n❗ Please enter a name from the List.")
goto ENVIRONMENT
}
utils.White_B.Print("\nDo you want Chaos Infrastructure to skip SSL/TLS check (Y/N) (Default: N): ")
skipSSLDescision := utils.Scanner()
if strings.ToLower(skipSSLDescision) == "y" {
newInfra.SkipSSL = true
} else {
newInfra.SkipSSL = false
}
utils.White_B.Print("\nDo you want NodeSelector to be added in the Chaos Infrastructure deployments (Y/N) (Default: N): ")
nodeSelectorDescision := utils.Scanner()
if strings.ToLower(nodeSelectorDescision) == "y" {
utils.White_B.Print("\nEnter the NodeSelector (Format: key1=value1,key2=value2): ")
newInfra.NodeSelector = utils.Scanner()
if ok := utils.CheckKeyValueFormat(newInfra.NodeSelector); !ok {
os.Exit(1)
}
}
utils.White_B.Print("\nDo you want Tolerations to be added in the Chaos Infrastructure deployments? (Y/N) (Default: N): ")
tolerationDescision := utils.Scanner()
if strings.ToLower(tolerationDescision) == "y" {
utils.White_B.Print("\nHow many tolerations? ")
no_of_tolerations := utils.Scanner()
nts, err := strconv.Atoi(no_of_tolerations)
utils.PrintError(err)
str := "["
for tol := 0; tol < nts; tol++ {
str += "{"
utils.White_B.Print("\nToleration count: ", tol+1)
utils.White_B.Print("\nTolerationSeconds: (Press Enter to ignore)")
ts := utils.Scanner()
utils.White_B.Print("\nOperator: ")
operator := utils.Scanner()
if operator != "" {
str += "\"operator\" : \"" + operator + "\" ,"
}
utils.White_B.Print("\nEffect: ")
effect := utils.Scanner()
if effect != "" {
str += "\"effect\": \"" + effect + "\" ,"
}
if ts != "" {
// check whether if effect is "NoSchedule" then tolerationsSeconds should be 0
if effect != "NoSchedule" {
str += "\"tolerationSeconds\": " + ts + " ,"
}
}
utils.White_B.Print("\nKey: ")
key := utils.Scanner()
if key != "" {
str += "\"key\": \"" + key + "\" ,"
}
utils.White_B.Print("\nValue: ")
value := utils.Scanner()
if key != "" {
str += "\"value\": \"" + value + "\""
}
str += " },"
}
if nts > 0 {
str = str[:len(str)-1]
}
str += "]"
newInfra.Tolerations = str
}
// Get platform name as input
newInfra.PlatformName = GetPlatformName(kubeconfig)
// Set Infra type
newInfra.InfraType = utils.InfraTypeKubernetes
// Set project id
newInfra.ProjectId = pid
// Get namespace
newInfra.Namespace, newInfra.NsExists = k8s.ValidNs(mode, utils.ChaosInfraLabel, kubeconfig)
return newInfra, nil
}
func ValidateSAPermissions(namespace string, mode string, kubeconfig *string) {
var (
pems [2]bool
err error
resources [2]string
)
if mode == "cluster" {
resources = [2]string{"clusterrole", "clusterrolebinding"}
} else {
resources = [2]string{"role", "rolebinding"}
}
for i, resource := range resources {
pems[i], err = k8s.CheckSAPermissions(k8s.CheckSAPermissionsParams{Verb: "create", Resource: resource, Print: true, Namespace: namespace}, kubeconfig)
if err != nil {
utils.Red.Println(err)
}
}
for _, pem := range pems {
if !pem {
utils.Red.Println("\n🚫 You don't have sufficient permissions.\n🙄 Please use a service account with sufficient permissions.")
os.Exit(1)
}
}
utils.White_B.Println("\n🌟 Sufficient permissions. Installing the Chaos Infra...")
}
// ValidateInfraNameExists checks if an infrastructure already exists
func ValidateInfraNameExists(infraName string, pid string, c types.Credentials) (bool, error, infrastructure.InfraData) {
infra, err := infrastructure.GetInfraList(c, pid, model.ListInfraRequest{})
if err != nil {
return false, err, infrastructure.InfraData{}
}
for i := range infra.Data.ListInfraDetails.Infras {
if infraName == infra.Data.ListInfraDetails.Infras[i].Name {
utils.White_B.Println(infra.Data.ListInfraDetails.Infras[i].Name)
return true, nil, infra
}
}
return false, nil, infrastructure.InfraData{}
}
// Summary display the Infra details based on input
func Summary(infra types.Infra, kubeconfig *string) {
utils.White_B.Printf("\n📌 Summary \nChaos Infra Name: %s\nChaos EnvironmentID: %s\nChaos Infra Description: %s\nChaos Infra SSL/TLS Skip: %t\nPlatform Name: %s\n", infra.InfraName, infra.EnvironmentID, infra.Description, infra.SkipSSL, infra.PlatformName)
if ok, _ := k8s.NsExists(infra.Namespace, kubeconfig); ok {
utils.White_B.Println("Namespace: ", infra.Namespace)
} else {
utils.White_B.Println("Namespace: ", infra.Namespace, "(new)")
}
if k8s.SAExists(k8s.SAExistsParams{Namespace: infra.Namespace, Serviceaccount: infra.ServiceAccount}, kubeconfig) {
utils.White_B.Println("Service Account: ", infra.ServiceAccount)
} else {
utils.White_B.Println("Service Account: ", infra.ServiceAccount, "(new)")
}
utils.White_B.Printf("\nInstallation Mode: %s\n", infra.Mode)
}
func ConfirmInstallation() {
var descision string
utils.White_B.Print("\n🤷 Do you want to continue with the above details? [Y/N]: ")
fmt.Scanln(&descision)
if strings.ToLower(descision) == "yes" || strings.ToLower(descision) == "y" {
utils.White_B.Println("👍 Continuing Chaos Infrastructure connection!!")
} else {
utils.Red.Println("✋ Exiting Chaos Infrastructure connection!!")
os.Exit(1)
}
}
func CreateRandomProject(cred types.Credentials) string {
rand, err := utils.GenerateRandomString(10)
utils.PrintError(err)
projectName := cred.Username + "-" + rand
project, err := apis.CreateProjectRequest(projectName, cred)
utils.PrintError(err)
return project.Data.ID
}

160
pkg/infra_ops/platform.go Normal file
View File

@ -0,0 +1,160 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package infra_ops
import (
"context"
"fmt"
"strings"
"github.com/litmuschaos/litmusctl/pkg/k8s"
"github.com/litmuschaos/litmusctl/pkg/utils"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// - Entering any character other than numbers returns 0. Input validation need to be done.
// - If input is given as "123abc", "abc" will be used for next user input. Buffer need to be read completely.
// - String literals like "AWS" are used at multiple places. Need to be changed to constants.
func GetPlatformName(kubeconfig *string) string {
var platform int
discoveredPlatform := DiscoverPlatform(kubeconfig)
utils.White_B.Println("\nPlatform List: ")
utils.White_B.Println(utils.PlatformList)
utils.White_B.Print("\nSelect a platform [Default: ", discoveredPlatform, "] [Range: 1-5]: ")
fmt.Scanln(&platform)
switch platform {
case 0:
return discoveredPlatform
case 1:
return "AWS"
case 2:
return "GKE"
case 3:
return "Openshift"
case 4:
return "Rancher"
default:
return utils.DefaultPlatform
}
}
// discoverPlatform determines the host platform and returns it
func DiscoverPlatform(kubeconfig *string) string {
if ok, _ := IsAWSPlatform(kubeconfig); ok {
return "AWS"
}
if ok, _ := IsGKEPlatform(kubeconfig); ok {
return "GKE"
}
if ok, _ := IsOpenshiftPlatform(kubeconfig); ok {
return "Openshift"
}
if ok, _ := k8s.NsExists("cattle-system", kubeconfig); ok {
return "Rancher"
}
return utils.DefaultPlatform
}
// IsAWSPlatform determines if the host platform is AWS
// by checking the ProviderID inside node spec
//
// Sample node custom resource of an AWS node
//
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": "aws:///us-east-2b/i-0bf24d83f4b993738"
// }
// }
// }
func IsAWSPlatform(kubeconfig *string) (bool, error) {
clientset, err := k8s.ClientSet(kubeconfig)
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil || len(nodeList.Items) == 0 {
return false, err
}
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, utils.AWSIdentifier) {
return true, nil
}
return false, nil
}
// IsGKEPlatform determines if the host platform is GKE
// by checking the ProviderID inside node spec
//
// Sample node custom resource of an GKE node
//
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": ""
// }
// }
// }
func IsGKEPlatform(kubeconfig *string) (bool, error) {
clientset, err := k8s.ClientSet(kubeconfig)
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
if err != nil || len(nodeList.Items) == 0 {
return false, err
}
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, utils.GKEIdentifier) {
return true, nil
}
return false, nil
}
// IsOpenshiftPlatform determines if the host platform
// is Openshift by checking "node.openshift.io/os_id"
// label on the nodes
//
// Sample node custom resource of an Openshift node
//
// {
// "apiVersion": "v1",
// "kind": "Node",
// "metadata": {
// "labels": {
// "node.openshift.io/os_id": "rhcos"
// }
// }
// ....
// }
func IsOpenshiftPlatform(kubeconfig *string) (bool, error) {
clientset, err := k8s.ClientSet(kubeconfig)
if err != nil {
return false, err
}
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{
LabelSelector: utils.OpenshiftIdentifier,
})
if err != nil {
return false, err
}
if len(nodeList.Items) > 0 {
return true, nil
}
return false, nil
}

56
pkg/k8s/client.go Normal file
View File

@ -0,0 +1,56 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package k8s
import (
"os"
"path/filepath"
"github.com/litmuschaos/litmusctl/pkg/utils"
"k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
// Returns a new kubernetes client set
func ClientSet(kubeconfig *string) (*kubernetes.Clientset, error) {
if *kubeconfig == "" {
if home := homedir.HomeDir(); home != "" {
kcfg := filepath.Join(home, ".kube", "config")
kubeconfig = &kcfg
} else {
utils.Red.Println("ERROR: Clientset generation failed!")
os.Exit(1)
}
}
// create the config
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
utils.Red.Println("ERROR: ", err.Error())
os.Exit(1)
}
// create the clientset
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
utils.Red.Println("ERROR: ", err.Error())
os.Exit(1)
}
return clientset, err
}

428
pkg/k8s/operations.go Normal file
View File

@ -0,0 +1,428 @@
/*
Copyright © 2021 The LitmusChaos Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package k8s
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/restmapper"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/sirupsen/logrus"
authorizationv1 "k8s.io/api/authorization/v1"
v1 "k8s.io/api/core/v1"
k8serror "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/discovery"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
)
type CanIOptions struct {
NoHeaders bool
Namespace string
AuthClient authorizationv1client.AuthorizationV1Interface
DiscoveryClient discovery.DiscoveryInterface
Verb string
Resource schema.GroupVersionResource
Subresource string
ResourceName string
}
// NsExists checks if the given namespace already exists
func NsExists(namespace string, kubeconfig *string) (bool, error) {
clientset, err := ClientSet(kubeconfig)
if err != nil {
return false, err
}
ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
if k8serror.IsNotFound(err) {
return false, nil
}
if err == nil && ns != nil {
return true, nil
}
return false, err
}
type CheckSAPermissionsParams struct {
Verb string
Resource string
Print bool
Namespace string
}
func CheckSAPermissions(params CheckSAPermissionsParams, kubeconfig *string) (bool, error) {
var o CanIOptions
o.Verb = params.Verb
o.Resource.Resource = params.Resource
o.Namespace = params.Namespace
client, err := ClientSet(kubeconfig)
if err != nil {
return false, err
}
AuthClient := client.AuthorizationV1()
sar := &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: o.Namespace,
Verb: o.Verb,
Group: o.Resource.Group,
Resource: o.Resource.Resource,
Subresource: o.Subresource,
Name: o.ResourceName,
},
},
}
response, err := AuthClient.SelfSubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
if err != nil {
return false, err
}
if response.Status.Allowed {
if params.Print {
utils.White_B.Print("\n🔑 ", params.Resource, " ✅")
}
} else {
if params.Print {
utils.White_B.Print("\n🔑 ", params.Resource, " ❌")
}
if len(response.Status.Reason) > 0 {
utils.White_B.Println(response.Status.Reason)
}
if len(response.Status.EvaluationError) > 0 {
utils.Red.Println(response.Status.EvaluationError)
}
}
return response.Status.Allowed, nil
}
// ValidNs takes a valid namespace as input from user
func ValidNs(mode string, label string, kubeconfig *string) (string, bool) {
start:
var (
namespace string
nsExists bool
)
if mode == "namespace" {
utils.White_B.Print("\nEnter the namespace (existing namespace) [Default: ", utils.DefaultNs, "]: ")
fmt.Scanln(&namespace)
} else if mode == "cluster" {
utils.White_B.Print("\nEnter the namespace (new or existing namespace) [Default: ", utils.DefaultNs, "]: ")
fmt.Scanln(&namespace)
} else {
utils.Red.Printf("\n 🚫 No mode selected \n")
os.Exit(1)
}
if namespace == "" {
namespace = utils.DefaultNs
}
ok, err := NsExists(namespace, kubeconfig)
if err != nil {
utils.Red.Printf("\n 🚫 Namespace existence check failed: {%s}\n", err.Error())
os.Exit(1)
}
if ok {
if podExists(podExistsParams{namespace, label}, kubeconfig) {
utils.Red.Println("\n🚫 There is a Chaos Infra already present in this namespace. Please enter a different namespace")
goto start
} else {
nsExists = true
utils.White_B.Println("👍 Continuing with", namespace, "namespace")
}
} else {
if val, _ := CheckSAPermissions(CheckSAPermissionsParams{"create", "namespace", false, namespace}, kubeconfig); !val {
utils.Red.Println("🚫 You don't have permissions to create a namespace.\n Please enter an existing namespace.")
goto start
}
nsExists = false
}
return namespace, nsExists
}
type WatchPodParams struct {
Namespace string
Label string
}
// WatchPod watches for the pod status
func WatchPod(params WatchPodParams, kubeconfig *string) {
clientset, err := ClientSet(kubeconfig)
if err != nil {
log.Fatal(err)
}
watch, err := clientset.CoreV1().Pods(params.Namespace).Watch(context.TODO(), metav1.ListOptions{
LabelSelector: params.Label,
})
if err != nil {
log.Fatal(err.Error())
}
for event := range watch.ResultChan() {
p, ok := event.Object.(*v1.Pod)
if !ok {
log.Fatal("unexpected type")
}
utils.White_B.Println("💡 Connecting Chaos Infra to ChaosCenter.")
if p.Status.Phase == "Running" {
utils.White_B.Println("🏃 Chaos Infra is running!!")
watch.Stop()
break
}
}
}
type PodList struct {
Items []string
}
type podExistsParams struct {
Namespace string
Label string
}
// PodExists checks if the pod with the given label already exists in the given namespace
func podExists(params podExistsParams, kubeconfig *string) bool {
clientset, err := ClientSet(kubeconfig)
if err != nil {
log.Fatal(err)
return false
}
watch, err := clientset.CoreV1().Pods(params.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: params.Label,
})
if err != nil {
log.Fatal(err.Error())
return false
}
if len(watch.Items) >= 1 {
return true
}
return false
}
type SAExistsParams struct {
Namespace string
Serviceaccount string
}
// SAExists checks if the given service account exists in the given namespace
func SAExists(params SAExistsParams, kubeconfig *string) bool {
clientset, err := ClientSet(kubeconfig)
if err != nil {
log.Fatal(err)
}
msg := fmt.Sprintf("serviceaccounts \"%s\" not found", params.Serviceaccount)
_, newErr := clientset.CoreV1().ServiceAccounts(params.Namespace).Get(context.TODO(), params.Serviceaccount, metav1.GetOptions{})
if newErr != nil {
if newErr.Error() == msg {
return false
}
log.Fatal(newErr)
}
return true
}
// ValidSA gets a valid service account as input
func ValidSA(namespace string, kubeconfig *string) (string, bool) {
var sa string
utils.White_B.Print("\nEnter service account [Default: ", utils.DefaultSA, "]: ")
fmt.Scanln(&sa)
if sa == "" {
sa = utils.DefaultSA
}
if SAExists(SAExistsParams{namespace, sa}, kubeconfig) {
utils.White_B.Print("\n👍 Using the existing service account")
return sa, true
}
return sa, false
}
// ApplyManifest applies the provided manifest and kubeconfig with the help of client-go library.
func ApplyManifest(manifest []byte, kubeconfig string) (string, error) {
// Get Kubernetes and dynamic clients along with the configuration.
_, kubeClient, dynamicClient, err := getClientAndConfig(kubeconfig)
if err != nil {
return "", err
}
// Decode the manifest into a list of Unstructured resources.
resources, err := decodeManifest(manifest)
if err != nil {
return "", err
}
// Apply the decoded resources using the dynamic client and Kubernetes client.
err = applyResources(resources, dynamicClient, kubeClient)
if err != nil {
return "", err
}
return "Success", nil
}
// retrieves the Kubernetes and dynamic clients along with the configuration.
func getClientAndConfig(kubeconfig string) (*rest.Config, *kubernetes.Clientset, dynamic.Interface, error) {
var config *rest.Config
var dynamicClient dynamic.Interface
// If kubeconfig is provided, use it to create the configuration and dynamic client.
if kubeconfig != "" {
var err error
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to build config from flags: %w", err)
}
} else {
// Use the default kubeconfig file at $HOME/.kube/config.
home := homedir.HomeDir()
defaultKubeconfig := filepath.Join(home, ".kube", "config")
if _, err := os.Stat(defaultKubeconfig); !os.IsNotExist(err) {
var err error
config, err = clientcmd.BuildConfigFromFlags("", defaultKubeconfig)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to build config from flags: %w", err)
}
} else {
// Return an error if kubeconfig is not provided and the default file doesn't exist
return nil, nil, nil, errors.New("kubeconfig not provided, and default kubeconfig not found")
}
}
// Create the dynamic client using the configuration.
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create dynamic client: %w", err)
}
// Create a Kubernetes client using the configuration.
kubeClient, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create kube client: %w", err)
}
return config, kubeClient, dynamicClient, nil
}
// decodes the manifest byte slice into a list of Unstructured resources.
func decodeManifest(manifest []byte) ([]*unstructured.Unstructured, error) {
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(manifest), len(manifest))
// Split the resource file into separate YAML documents.
resources := []*unstructured.Unstructured{}
for {
resourcestr := &unstructured.Unstructured{}
if err := decoder.Decode(resourcestr); err != nil {
if err.Error() == "EOF" {
break
}
return nil, fmt.Errorf("error in decoding resource: %w", err)
}
resources = append(resources, resourcestr)
}
return resources, nil
}
// applies the decoded resources using the dynamic client and Kubernetes client.
func applyResources(resources []*unstructured.Unstructured, dynamicClient dynamic.Interface, kubeClient *kubernetes.Clientset) error {
for _, resource := range resources {
logrus.Infof("Applying resource: %s , kind: %s", resource.GetName(), resource.GetKind())
gvk := resource.GroupVersionKind()
// a mapper for REST mapping.
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(kubeClient.Discovery()))
// Map GVK to GVR using the REST mapper.
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return fmt.Errorf("error in resource gvk to gvr mapping: %w", err)
}
var dr dynamic.ResourceInterface
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
// Namespaced resources should specify the namespace
dr = dynamicClient.Resource(mapping.Resource).Namespace(resource.GetNamespace())
} else {
// For cluster-wide resources
dr = dynamicClient.Resource(mapping.Resource)
}
// Apply the resource using the dynamic client.
_, err = dr.Apply(context.TODO(), resource.GetName(), resource, metav1.ApplyOptions{
Force: true,
FieldManager: "application/apply-patch",
})
if err != nil {
return fmt.Errorf("error in applying resource: %w", err)
}
logrus.Info("Resource applied successfully")
}
return nil
}
// GetConfigMap returns config map for a given name and namespace
func GetConfigMap(c context.Context, name string, namespace string) (map[string]string, error) {
var kubeconfig *string
if home := homedir.HomeDir(); home != "" {
kubeconfig = flag.String("configmap", filepath.Join(home, ".kube", "config"), "")
} else {
kubeconfig = flag.String("configmap", "", "")
}
flag.Parse()
clientset, err := ClientSet(kubeconfig)
if err != nil {
return nil, err
}
x, err := clientset.CoreV1().ConfigMaps(namespace).Get(c, name, metav1.GetOptions{})
if err != nil {
return nil, err
}
return x.Data, nil
}

Some files were not shown because too many files have changed in this diff Show More