Compare commits

...

63 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
92 changed files with 7311 additions and 4587 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)

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

@ -11,7 +11,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.20.5
# Checkout to the latest commit
# On specific directory/path
@ -19,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

View File

@ -13,7 +13,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.20.5
# Checkout to the latest commit
# On specific directory/path
@ -38,7 +38,7 @@ jobs:
- name: Get tag
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF##*/})"
run: echo "branch=$(echo ${GITHUB_REF##*/})" >> $GITHUB_OUTPUT
id: tag
- name: Building litmusctl
@ -55,4 +55,4 @@ jobs:
- name: Copy binaries to the litmusctl s3 bucket
run: |
aws s3 sync platforms-${{ steps.tag.outputs.branch }} s3://${{ secrets.AWS_S3_BUCKET }}
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 }}

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! 🚀🔥

335
README.md
View File

@ -1,4 +1,5 @@
# Litmusctl
[![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)
@ -7,10 +8,11 @@
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 v0.12.0 or latest:
* Non-Interactive mode: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage.md">Click here</a>
* Interactive mode: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_interactive.md">Click here</a>
* 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
@ -18,8 +20,7 @@ For more information including a complete list of litmusctl operations, see the
The litmusctl CLI requires the following things:
- 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)
- 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
@ -29,86 +30,90 @@ To check compatibility of litmusctl with Chaos Center
<th>litmusctl version</th>
<th>Lowest Chaos Center supported version</th>
<th>Highest Chaos Center supported version</th>
<tr>
<td>0.7.0</td>
<td>2.4.0</td>
<td>2.8.0</td>
</tr>
<tr>
<td>0.8.0</td>
<td>2.4.0</td>
<td>2.8.0</td>
</tr>
<tr>
<td>0.9.0</td>
<td>2.4.0</td>
<td>2.8.0</td>
</tr>
<tr>
<td>0.10.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.11.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.12.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.13.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.14.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.15.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.16.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.17.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.18.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.19.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.20.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
</tr>
<tr>
<td>0.21.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
<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>0.22.0</td>
<td>2.9.0</td>
<td>3.0.0-beta8</td>
<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>
@ -118,134 +123,134 @@ To install the latest version of litmusctl follow the below steps:
<table>
<th>Platforms</th>
<th>0.22.0</th>
<th>0.21.0</th>
<th>0.20.0</th>
<th>0.19.0</th>
<th>0.18.0</th>
<th>0.17.0</th>
<th>0.16.0</th>
<th>0.15.0</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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-0.15.0.tar.gz">Click here</a></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-0.22.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.21.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.20.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.19.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.18.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.17.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.16.0.tar.gz">Click here</a></td>
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-0.15.0.tar.gz">Click here</a></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>
### Linux/MacOS
* Extract the binary
- Extract the binary
```shell
tar -zxvf litmusctl-<OS>-<ARCH>-<VERSION>.tar.gz
```
* Provide necessary permissions
- Provide necessary permissions
```shell
chmod +x litmusctl
```
* Move the litmusctl binary to /usr/local/bin/litmusctl. Note: Make sure to use root user or use sudo as a prefix
- Move the litmusctl binary to /usr/local/bin/litmusctl. Note: Make sure to use root user or use sudo as a prefix
```shell
mv litmusctl /usr/local/bin/litmusctl
```
* You can run the litmusctl command in Linux/macOS:
- You can run the litmusctl command in Linux/macOS:
```shell
litmusctl <command> <subcommand> <subcommand> [options and parameters]
@ -253,18 +258,22 @@ litmusctl <command> <subcommand> <subcommand> [options and parameters]
### Windows
* Extract the binary from the zip using WinZip or any other extraction tool.
- Extract the binary from the zip using WinZip or any other extraction tool.
* You can run the litmusctl command in windows:
- You can run the litmusctl command in windows:
```shell
litmusctl.exe <command> <subcommand> <subcommand> [options and parameters]
```
* To check the version of the litmusctl:
- To check the version of the litmusctl:
```shell
litmusctl version
```
----
## Development Guide
You can find the local setup guide for **`litmusctl`** [here](DEVELOPMENT.md).
---

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`.

View File

@ -167,15 +167,12 @@ Service Account: litmus (new)
Installation Mode: cluster
🤷 Do you want to continue with the above details? [Y/N]: Y
👍 Continuing Chaos Delegate connection!!
Applying YAML:
https://preview.litmuschaos.io/api/file/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbHVzdGVyX2lkIjoiMDUyZmFlN2UtZGM0MS00YmU4LWJiYTgtMmM4ZTYyNDFkN2I0In0.i31QQDG92X5nD6P_-7TfeAAarZqLvUTFfnAghJYXPiM.yaml
👍 Continuing Chaos Infrastructure connection!!
💡 Connecting Chaos Delegate to ChaosCenter.
💡 Connecting Chaos Infrastructure to ChaosCenter.
🏃 Chaos Delegate is running!!
🚀 Chaos Delegate Connection Successful!! 🎉
👉 Litmus Chaos Delegates can be accessed here: https://preview.litmuschaos.io/targets
🚀 Chaos Infrastructure Connection Successful!! 🎉
```
#### Verify the new Chaos Delegate Connection\*\*
@ -203,6 +200,18 @@ litmusctl create chaos-scenario -f custom-chaos-scenario.yml --project-id="" --c
### 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

92
go.mod
View File

@ -1,33 +1,97 @@
module github.com/litmuschaos/litmusctl
go 1.16
go 1.21
require (
github.com/argoproj/argo-workflows/v3 v3.3.1
github.com/fatih/color v1.13.0
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-20221010164339-e91b0109a875
github.com/litmuschaos/litmus/litmus-portal/graphql-server v0.0.0-20221019142834-cbc3e089e654
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/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
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.23.3
k8s.io/apimachinery v0.23.3
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.3.0
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.21.2
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.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.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

3109
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ 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,

View File

@ -1,288 +0,0 @@
/*
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 agent
import (
"fmt"
"os"
"strconv"
"strings"
"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 PrintExistingAgents(agent apis.AgentData) {
utils.Red.Println("\nChaos Delegate with the given name already exists.")
// Print Chaos Delegate list if existing Chaos Delegate name is entered twice
utils.White_B.Println("\nConnected Chaos Delegates list:")
for i := range agent.Data.GetAgent {
utils.White_B.Println("-", agent.Data.GetAgent[i].AgentName)
}
utils.White_B.Println("\n❗ Please enter a different name.")
}
// 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 Delegate 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
}
// GetAgentDetails take details of Chaos Delegate as input
func GetAgentDetails(mode string, pid string, c types.Credentials, kubeconfig *string) (types.Agent, error) {
var newAgent types.Agent
// Get agent name as input
utils.White_B.Println("\nEnter the details of the Chaos Delegate")
// Label for goto statement in case of invalid Chaos Delegate name
AGENT_NAME:
utils.White_B.Print("\nChaos Delegate Name: ")
newAgent.AgentName = utils.Scanner()
if newAgent.AgentName == "" {
utils.Red.Println("⛔ Chaos Delegate name cannot be empty. Please enter a valid name.")
goto AGENT_NAME
}
// Check if Chaos Delegate with the given name already exists
agent, err := apis.GetAgentList(c, pid)
if err != nil {
return types.Agent{}, err
}
var isAgentExist = false
for i := range agent.Data.GetAgent {
if newAgent.AgentName == agent.Data.GetAgent[i].AgentName {
utils.White_B.Println(agent.Data.GetAgent[i].AgentName)
isAgentExist = true
}
}
if isAgentExist {
PrintExistingAgents(agent)
goto AGENT_NAME
}
// Get agent description as input
utils.White_B.Print("\nChaos Delegate Description: ")
newAgent.Description = utils.Scanner()
utils.White_B.Print("\nDo you want Chaos Delegate to skip SSL/TLS check (Y/N) (Default: N): ")
skipSSLDescision := utils.Scanner()
if strings.ToLower(skipSSLDescision) == "y" {
newAgent.SkipSSL = true
} else {
newAgent.SkipSSL = false
}
utils.White_B.Print("\nDo you want NodeSelector to be added in the Chaos Delegate 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): ")
newAgent.NodeSelector = utils.Scanner()
if ok := utils.CheckKeyValueFormat(newAgent.NodeSelector); !ok {
os.Exit(1)
}
}
utils.White_B.Print("\nDo you want Tolerations to be added in the Chaos Delegate 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 != "" {
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 += " }"
}
str += "]"
newAgent.Tolerations = str
}
// Get platform name as input
newAgent.PlatformName = GetPlatformName(kubeconfig)
// Set agent type
newAgent.ClusterType = utils.AgentType
// Set project id
newAgent.ProjectId = pid
// Get namespace
newAgent.Namespace, newAgent.NsExists = k8s.ValidNs(mode, utils.ChaosAgentLabel, kubeconfig)
return newAgent, 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 Delegate...")
}
// Summary display the agent details based on input
func Summary(agent types.Agent, kubeconfig *string) {
utils.White_B.Printf("\n📌 Summary \nChaos Delegate Name: %s\nChaos Delegate Description: %s\nChaos Delegate SSL/TLS Skip: %t\nPlatform Name: %s\n", agent.AgentName, agent.Description, agent.SkipSSL, agent.PlatformName)
if ok, _ := k8s.NsExists(agent.Namespace, kubeconfig); ok {
utils.White_B.Println("Namespace: ", agent.Namespace)
} else {
utils.White_B.Println("Namespace: ", agent.Namespace, "(new)")
}
if k8s.SAExists(k8s.SAExistsParams{Namespace: agent.Namespace, Serviceaccount: agent.ServiceAccount}, kubeconfig) {
utils.White_B.Println("Service Account: ", agent.ServiceAccount)
} else {
utils.White_B.Println("Service Account: ", agent.ServiceAccount, "(new)")
}
utils.White_B.Printf("\nInstallation Mode: %s\n", agent.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 Delegate connection!!")
} else {
utils.Red.Println("✋ Exiting Chaos Delegate 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
}

View File

@ -1,219 +0,0 @@
/*
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"
"fmt"
"io/ioutil"
"net/http"
"github.com/litmuschaos/litmusctl/pkg/utils"
types "github.com/litmuschaos/litmusctl/pkg/types"
)
type AgentData struct {
Data AgentList `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type AgentDetails struct {
AgentName string `json:"clusterName"`
IsActive bool `json:"isActive"`
IsRegistered bool `json:"isRegistered"`
ClusterID string `json:"clusterID"`
}
type AgentList struct {
GetAgent []AgentDetails `json:"listClusters"`
}
// GetAgentList lists the Chaos Delegate connected to the specified project
func GetAgentList(c types.Credentials, pid string) (AgentData, error) {
query := `{"query":"query{\n listClusters(projectID: \"` + pid + `\"){\n clusterID clusterName isActive isRegistered\n }\n}"}`
resp, err := SendRequest(SendRequestParams{Endpoint: c.Endpoint + utils.GQLAPIPath, Token: c.Token}, []byte(query), string(types.Post))
if err != nil {
return AgentData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return AgentData{}, err
}
if resp.StatusCode == http.StatusOK {
var agent AgentData
err = json.Unmarshal(bodyBytes, &agent)
if err != nil {
return AgentData{}, err
}
if len(agent.Errors) > 0 {
return AgentData{}, errors.New(agent.Errors[0].Message)
}
return agent, nil
} else {
return AgentData{}, err
}
}
type AgentConnectionData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data AgentConnect `json:"data"`
}
type Errors struct {
Message string `json:"message"`
Path []string `json:"path"`
}
type AgentConnect struct {
UserAgentReg UserAgentReg `json:"registerCluster"`
}
type UserAgentReg struct {
ClusterID string `json:"clusterID"`
ClusterName string `json:"clusterName"`
Token string `json:"token"`
}
// ConnectAgent connects the agent with the given details
func ConnectAgent(agent types.Agent, cred types.Credentials) (AgentConnectionData, error) {
query := `{"query":"mutation {\n registerCluster(request: \n { \n clusterName: \"` + agent.AgentName + `\", \n description: \"` + agent.Description + `\",\n \tplatformName: \"` + agent.PlatformName + `\",\n projectID: \"` + agent.ProjectId + `\",\n clusterType: \"` + agent.ClusterType + `\",\n agentScope: \"` + agent.Mode + `\",\n agentNamespace: \"` + agent.Namespace + `\",\n serviceAccount: \"` + agent.ServiceAccount + `\",\n skipSsl: ` + fmt.Sprintf("%t", agent.SkipSSL) + `,\n agentNsExists: ` + fmt.Sprintf("%t", agent.NsExists) + `,\n agentSaExists: ` + fmt.Sprintf("%t", agent.SAExists) + `,\n }){\n clusterID\n clusterName\n token\n }\n}"}`
if agent.NodeSelector != "" {
query = `{"query":"mutation {\n registerCluster(request: \n { \n clusterName: \"` + agent.AgentName + `\", \n description: \"` + agent.Description + `\",\n nodeSelector: \"` + agent.NodeSelector + `\",\n \tplatformName: \"` + agent.PlatformName + `\",\n projectID: \"` + agent.ProjectId + `\",\n clusterType: \"` + agent.ClusterType + `\",\n agentScope: \"` + agent.Mode + `\",\n agentNamespace: \"` + agent.Namespace + `\",\n skipSsl: ` + fmt.Sprintf("%t", agent.SkipSSL) + `,\n serviceAccount: \"` + agent.ServiceAccount + `\",\n agentNsExists: ` + fmt.Sprintf("%t", agent.NsExists) + `,\n agentSaExists: ` + fmt.Sprintf("%t", agent.SAExists) + `,\n }){\n clusterID\n clusterName\n token\n }\n}"}`
}
if agent.Tolerations != "" {
query = `{"query":"mutation {\n registerCluster(request: \n { \n clusterName: \"` + agent.AgentName + `\", \n description: \"` + agent.Description + `\",\n \tplatformName: \"` + agent.PlatformName + `\",\n projectID: \"` + agent.ProjectId + `\",\n clusterType: \"` + agent.ClusterType + `\",\n agentScope: \"` + agent.Mode + `\",\n agentNamespace: \"` + agent.Namespace + `\",\n serviceAccount: \"` + agent.ServiceAccount + `\",\n skipSsl: ` + fmt.Sprintf("%t", agent.SkipSSL) + `,\n agentNsExists: ` + fmt.Sprintf("%t", agent.NsExists) + `,\n agentSaExists: ` + fmt.Sprintf("%t", agent.SAExists) + `,\n tolerations: ` + agent.Tolerations + ` }){\n clusterID\n clusterName\n token\n }\n}"}`
}
if agent.NodeSelector != "" && agent.Tolerations != "" {
query = `{"query":"mutation {\n registerCluster(request: \n { \n clusterName: \"` + agent.AgentName + `\", \n description: \"` + agent.Description + `\",\n nodeSelector: \"` + agent.NodeSelector + `\",\n \tplatformName: \"` + agent.PlatformName + `\",\n projectID: \"` + agent.ProjectId + `\",\n clusterType: \"` + agent.ClusterType + `\",\n agentScope: \"` + agent.Mode + `\",\n agentNamespace: \"` + agent.Namespace + `\",\n skipSsl: ` + fmt.Sprintf("%t", agent.SkipSSL) + `,\n serviceAccount: \"` + agent.ServiceAccount + `\",\n agentNsExists: ` + fmt.Sprintf("%t", agent.NsExists) + `,\n agentSaExists: ` + fmt.Sprintf("%t", agent.SAExists) + `,\n tolerations: ` + agent.Tolerations + ` }){\n clusterID\n clusterName\n token\n }\n}"}`
}
resp, err := SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(query), string(types.Post))
if err != nil {
return AgentConnectionData{}, errors.New("Error in registering Chaos Delegate: " + err.Error())
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return AgentConnectionData{}, errors.New("Error in registering Chaos Delegate: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var connectAgent AgentConnectionData
err = json.Unmarshal(bodyBytes, &connectAgent)
if err != nil {
return AgentConnectionData{}, errors.New("Error in registering Chaos Delegate: " + err.Error())
}
if len(connectAgent.Errors) > 0 {
return AgentConnectionData{}, errors.New(connectAgent.Errors[0].Message)
}
return connectAgent, nil
} else {
return AgentConnectionData{}, err
}
}
type DisconnectAgentData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DisconnectAgentDetails `json:"data"`
}
type DisconnectAgentDetails struct {
Message string `json:"deleteClusters"`
}
type DisconnectAgentGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
ClusterIDs []*string `json:"clusterIDs"`
} `json:"variables"`
}
// DisconnectAgent sends GraphQL API request for disconnecting Chaos Delegate(s).
func DisconnectAgent(projectID string, clusterIDs []*string, cred types.Credentials) (DisconnectAgentData, error) {
var gqlReq DisconnectAgentGraphQLRequest
var err error
gqlReq.Query = `mutation deleteClusters($projectID: String!, $clusterIDs: [String]!) {
deleteClusters(
projectID: $projectID
clusterIDs: $clusterIDs
)
}`
gqlReq.Variables.ProjectID = projectID
gqlReq.Variables.ClusterIDs = clusterIDs
query, err := json.Marshal(gqlReq)
if err != nil {
return DisconnectAgentData{}, err
}
resp, err := SendRequest(
SendRequestParams{
Endpoint: cred.Endpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DisconnectAgentData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DisconnectAgentData{}, err
}
if resp.StatusCode == http.StatusOK {
var disconnectAgentData DisconnectAgentData
err = json.Unmarshal(bodyBytes, &disconnectAgentData)
if err != nil {
return DisconnectAgentData{}, err
}
if len(disconnectAgentData.Errors) > 0 {
return DisconnectAgentData{}, errors.New(disconnectAgentData.Errors[0].Message)
}
return disconnectAgentData, nil
} else {
return DisconnectAgentData{}, err
}
}

View File

@ -5,7 +5,7 @@ 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,
@ -18,7 +18,7 @@ package apis
import (
"encoding/json"
"errors"
"io/ioutil"
"io"
"net/http"
"github.com/litmuschaos/litmusctl/pkg/utils"
@ -47,7 +47,7 @@ func Auth(input types.AuthInput) (types.AuthResponse, error) {
return types.AuthResponse{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return types.AuthResponse{}, err
}

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"`
}

View File

@ -5,7 +5,7 @@ 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,
@ -18,7 +18,7 @@ package apis
import (
"encoding/json"
"errors"
"io/ioutil"
"io"
"net/http"
"github.com/golang-jwt/jwt"
@ -28,10 +28,10 @@ import (
"github.com/litmuschaos/litmusctl/pkg/types"
)
type createProjectResponse struct {
type CreateProjectResponse struct {
Data struct {
Name string `json:"name"`
ID string `json:"id"`
ID string `json:"projectID"`
} `json:"data"`
Errors []struct {
Message string `json:"message"`
@ -40,52 +40,56 @@ type createProjectResponse struct {
}
type createProjectPayload struct {
ProjectName string `json:"project_name"`
ProjectName string `json:"projectName"`
}
func CreateProjectRequest(projectName string, cred types.Credentials) (createProjectResponse, error) {
func CreateProjectRequest(projectName string, cred types.Credentials) (CreateProjectResponse, error) {
payloadBytes, err := json.Marshal(createProjectPayload{
ProjectName: projectName,
})
if err != nil {
return createProjectResponse{}, err
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
return CreateProjectResponse{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return createProjectResponse{}, err
return CreateProjectResponse{}, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
var project createProjectResponse
var project CreateProjectResponse
err = json.Unmarshal(bodyBytes, &project)
if err != nil {
return createProjectResponse{}, err
return CreateProjectResponse{}, err
}
if len(project.Errors) > 0 {
return createProjectResponse{}, errors.New(project.Errors[0].Message)
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))
return CreateProjectResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
}
}
type listProjectResponse struct {
Data []struct {
ID string `json:"ID"`
Name string `json:"Name"`
CreatedAt string `json:"CreatedAt"`
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"`
@ -100,11 +104,10 @@ func ListProject(cred types.Credentials) (listProjectResponse, error) {
return listProjectResponse{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return listProjectResponse{}, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
@ -112,6 +115,7 @@ func ListProject(cred types.Credentials) (listProjectResponse, error) {
err = json.Unmarshal(bodyBytes, &data)
if err != nil {
return listProjectResponse{}, err
}
if len(data.Errors) > 0 {
@ -139,14 +143,14 @@ type Data struct {
type Member struct {
Role string `json:"Role"`
UserID string `json:"UserID"`
UserName string `json:"UserName"`
UserID string `json:"userID"`
UserName string `json:"username"`
}
type Project struct {
ID string `json:"ID"`
ID string `json:"ProjectID"`
Name string `json:"Name"`
CreatedAt string `json:"CreatedAt"`
CreatedAt int64 `json:"CreatedAt"`
Members []Member `json:"Members"`
}
@ -162,7 +166,7 @@ func GetProjectDetails(c types.Credentials) (ProjectDetails, error) {
return ProjectDetails{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ProjectDetails{}, err

View File

@ -5,7 +5,7 @@ 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,
@ -32,6 +32,7 @@ func SendRequest(params SendRequestParams, payload []byte, method string) (*http
}
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 {

View File

@ -6,7 +6,8 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"github.com/sirupsen/logrus"
"io"
"net/http"
"os"
@ -26,64 +27,63 @@ type manifestData struct {
}
type data struct {
GetManifest string `json:"getManifest"`
GetManifest string `json:"getInfraManifest"`
}
type ClusterData struct {
Data GetAgentDetails `json:"data"`
type GetInfraResponse struct {
Data GetInfraData `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type GetAgentDetails struct {
GetAgentDetails ClusterDetails `json:"getAgentDetails"`
type GetInfraData struct {
GetInfraDetails InfraDetails `json:"getInfraDetails"`
}
type ClusterDetails struct {
ClusterID string `json:"clusterID"`
AccessKey string `json:"accessKey"`
AgentNamespace *string `json:"agentNamespace"`
type InfraDetails struct {
InfraID string `json:"infraID"`
InfraNamespace *string `json:"infraNamespace"`
}
func UpgradeAgent(c context.Context, cred types.Credentials, projectID string, clusterID string, kubeconfig string) (string, error) {
func UpgradeInfra(c context.Context, cred types.Credentials, projectID string, infraID string, kubeconfig string) (string, error) {
// Query to fetch agent details from server
query := `{"query":"query {\n getAgentDetails(clusterID : \"` + clusterID + `\", \n projectID : \"` + projectID + `\"){\n agentNamespace accessKey clusterID \n}}"}`
// 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 := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
defer resp.Body.Close()
var agent ClusterData
var infra GetInfraResponse
if resp.StatusCode == http.StatusOK {
err = json.Unmarshal(bodyBytes, &agent)
err = json.Unmarshal(bodyBytes, &infra)
if err != nil {
return "", err
}
if len(agent.Errors) > 0 {
return "", errors.New(agent.Errors[0].Message)
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 getManifest(projectID : \"` + projectID + `\",\n clusterID : \"` + agent.Data.GetAgentDetails.ClusterID + `\",\n accessKey :\"` + agent.Data.GetAgentDetails.AccessKey + `\")}"}`
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 = ioutil.ReadAll(resp.Body)
bodyBytes, err = io.ReadAll(resp.Body)
if err != nil {
return "", err
}
@ -102,14 +102,8 @@ func UpgradeAgent(c context.Context, cred types.Credentials, projectID string, c
return "", errors.New(manifest.Errors[0].Message)
}
// To write the manifest data into a temporary file
err = ioutil.WriteFile("chaos-delegate-manifest.yaml", []byte(manifest.Data.GetManifest), 0644)
if err != nil {
return "", err
}
// Fetching agent-config from the subscriber
configData, err := k8s.GetConfigMap(c, "agent-config", *agent.Data.GetAgentDetails.AgentNamespace)
// Fetching subscriber-config from the subscriber
configData, err := k8s.GetConfigMap(c, "subscriber-config", *infra.Data.GetInfraDetails.InfraNamespace)
if err != nil {
return "", err
}
@ -117,13 +111,13 @@ func UpgradeAgent(c context.Context, cred types.Credentials, projectID string, c
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", "agent-config", "namespace", *agent.Data.GetAgentDetails.AgentNamespace, "data")
"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_CLUSTER_CONFIRMED" {
} 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)
@ -132,33 +126,25 @@ func UpgradeAgent(c context.Context, cred types.Credentials, projectID string, c
}
yamlOutput, err := k8s.ApplyYaml(k8s.ApplyYamlPrams{
Token: cred.Token,
Endpoint: cred.Endpoint,
YamlPath: "chaos-delegate-manifest.yaml",
}, kubeconfig, true)
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)
err = os.Remove("chaos-delegate-manifest.yaml")
if err != nil {
return "Error removing Chaos Delegate manifest: ", err
}
// Creating a backup for current agent-config in the SUBSCRIBER
// Creating a backup for current subscriber-config in the SUBSCRIBER
home, err := homedir.Dir()
cobra.CheckErr(err)
configMapString = metadata.String() + configMapString
err = ioutil.WriteFile(home+"/backupAgentConfig.yaml", []byte(configMapString), 0644)
err = os.WriteFile(home+"/backupSubscriberConfig.yaml", []byte(configMapString), 0644)
if err != nil {
return "Error creating backup for agent config: ", err
return "Error creating backup for subscriber config: ", err
}
utils.White_B.Print("\n ** A backup of agent-config configmap has been saved in your system's home directory as backupAgentConfig.yaml **\n")
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 {

View File

@ -1,419 +0,0 @@
/*
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/ioutil"
"net/http"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
types "github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
)
type ChaosWorkflowCreationData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data CreatedChaosWorkflow `json:"data"`
}
type CreatedChaosWorkflow struct {
CreateChaosWorkflow model.ChaosWorkFlowResponse `json:"createChaosWorkFlow"`
}
type CreateChaosWorkFlowGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
CreateChaosWorkFlowRequest model.ChaosWorkFlowRequest `json:"request"`
} `json:"variables"`
}
// CreateWorkflow sends GraphQL API request for creating a workflow
func CreateWorkflow(requestData model.ChaosWorkFlowRequest, cred types.Credentials) (ChaosWorkflowCreationData, error) {
var gqlReq CreateChaosWorkFlowGraphQLRequest
gqlReq.Query = `mutation createChaosWorkFlow($request: ChaosWorkFlowRequest!) {
createChaosWorkFlow(request: $request) {
workflowID
cronSyntax
workflowName
workflowDescription
isCustomWorkflow
}
}`
gqlReq.Variables.CreateChaosWorkFlowRequest = requestData
query, err := json.Marshal(gqlReq)
if err != nil {
return ChaosWorkflowCreationData{}, err
}
resp, err := SendRequest(
SendRequestParams{
Endpoint: cred.Endpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return ChaosWorkflowCreationData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return ChaosWorkflowCreationData{}, errors.New("Error in creating Chaos Scenario: " + err.Error())
}
if resp.StatusCode == http.StatusOK {
var createdWorkflow ChaosWorkflowCreationData
err = json.Unmarshal(bodyBytes, &createdWorkflow)
if err != nil {
return ChaosWorkflowCreationData{}, errors.New("Error in creating Chaos Scenario: " + err.Error())
}
// Errors present
if len(createdWorkflow.Errors) > 0 {
return ChaosWorkflowCreationData{}, errors.New(createdWorkflow.Errors[0].Message)
}
return createdWorkflow, nil
} else {
return ChaosWorkflowCreationData{}, errors.New("graphql schema error")
}
}
type WorkflowListData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data WorkflowList `json:"data"`
}
type WorkflowList struct {
ListWorkflowDetails model.ListWorkflowsResponse `json:"listWorkflows"`
}
type GetChaosWorkFlowsGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
GetChaosWorkFlowsRequest model.ListWorkflowsRequest `json:"request"`
} `json:"variables"`
}
// GetWorkflowList sends GraphQL API request for fetching a list of workflows.
func GetWorkflowList(in model.ListWorkflowsRequest, cred types.Credentials) (WorkflowListData, error) {
var gqlReq GetChaosWorkFlowsGraphQLRequest
var err error
gqlReq.Query = `query listWorkflows($request: ListWorkflowsRequest!) {
listWorkflows(request: $request) {
totalNoOfWorkflows
workflows {
workflowID
workflowManifest
cronSyntax
clusterName
workflowName
workflowDescription
weightages {
experimentName
weightage
}
isCustomWorkflow
updatedAt
createdAt
projectID
clusterID
clusterType
isRemoved
lastUpdatedBy
}
}
}`
gqlReq.Variables.GetChaosWorkFlowsRequest = in
query, err := json.Marshal(gqlReq)
if err != nil {
return WorkflowListData{}, err
}
resp, err := SendRequest(
SendRequestParams{
Endpoint: cred.Endpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return WorkflowListData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return WorkflowListData{}, err
}
if resp.StatusCode == http.StatusOK {
var workflowList WorkflowListData
err = json.Unmarshal(bodyBytes, &workflowList)
if err != nil {
return WorkflowListData{}, err
}
if len(workflowList.Errors) > 0 {
return WorkflowListData{}, errors.New(workflowList.Errors[0].Message)
}
return workflowList, nil
} else {
return WorkflowListData{}, errors.New("Error while fetching the Chaos Scenarios")
}
}
type WorkflowRunsListData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data WorkflowRunsList `json:"data"`
}
type WorkflowRunsList struct {
ListWorkflowRunsDetails model.ListWorkflowRunsResponse `json:"listWorkflowRuns"`
}
type GetChaosWorkFlowRunsGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
GetChaosWorkFlowRunsRequest model.ListWorkflowRunsRequest `json:"request"`
} `json:"variables"`
}
// GetWorkflowRunsList sends GraphQL API request for fetching a list of workflow runs.
func GetWorkflowRunsList(in model.ListWorkflowRunsRequest, cred types.Credentials) (WorkflowRunsListData, error) {
var gqlReq GetChaosWorkFlowRunsGraphQLRequest
var err error
gqlReq.Query = `query listWorkflowRuns($request: ListWorkflowRunsRequest!) {
listWorkflowRuns(request: $request) {
totalNoOfWorkflowRuns
workflowRuns {
workflowRunID
workflowID
clusterName
workflowName
projectID
clusterID
clusterType
isRemoved
lastUpdated
phase
resiliencyScore
experimentsPassed
experimentsFailed
experimentsAwaited
experimentsStopped
experimentsNa
totalExperiments
executedBy
}
}
}`
gqlReq.Variables.GetChaosWorkFlowRunsRequest = in
query, err := json.Marshal(gqlReq)
if err != nil {
return WorkflowRunsListData{}, err
}
resp, err := SendRequest(
SendRequestParams{
Endpoint: cred.Endpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return WorkflowRunsListData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return WorkflowRunsListData{}, err
}
if resp.StatusCode == http.StatusOK {
var workflowRunsList WorkflowRunsListData
err = json.Unmarshal(bodyBytes, &workflowRunsList)
if err != nil {
return WorkflowRunsListData{}, err
}
if len(workflowRunsList.Errors) > 0 {
return WorkflowRunsListData{}, errors.New(workflowRunsList.Errors[0].Message)
}
return workflowRunsList, nil
} else {
return WorkflowRunsListData{}, errors.New("Error while fetching the Chaos Scenario runs")
}
}
type DeleteChaosWorkflowData struct {
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
Data DeleteChaosWorkflowDetails `json:"data"`
}
type DeleteChaosWorkflowDetails struct {
IsDeleted bool `json:"deleteChaosWorkflow"`
}
type DeleteChaosWorkflowGraphQLRequest struct {
Query string `json:"query"`
Variables struct {
ProjectID string `json:"projectID"`
WorkflowID *string `json:"workflowID"`
WorkflowRunID *string `json:"workflowRunID"`
} `json:"variables"`
}
// DeleteChaosWorkflow sends GraphQL API request for deleting a given Chaos Workflow.
func DeleteChaosWorkflow(projectID string, workflowID *string, cred types.Credentials) (DeleteChaosWorkflowData, error) {
var gqlReq DeleteChaosWorkflowGraphQLRequest
var err error
gqlReq.Query = `mutation deleteChaosWorkflow($projectID: String!, $workflowID: String, $workflowRunID: String) {
deleteChaosWorkflow(
projectID: $projectID
workflowID: $workflowID
workflowRunID: $workflowRunID
)
}`
gqlReq.Variables.ProjectID = projectID
gqlReq.Variables.WorkflowID = workflowID
var workflow_run_id string = ""
gqlReq.Variables.WorkflowRunID = &workflow_run_id
query, err := json.Marshal(gqlReq)
if err != nil {
return DeleteChaosWorkflowData{}, err
}
resp, err := SendRequest(
SendRequestParams{
Endpoint: cred.Endpoint + utils.GQLAPIPath,
Token: cred.Token,
},
query,
string(types.Post),
)
if err != nil {
return DeleteChaosWorkflowData{}, err
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return DeleteChaosWorkflowData{}, err
}
if resp.StatusCode == http.StatusOK {
var deletedWorkflow DeleteChaosWorkflowData
err = json.Unmarshal(bodyBytes, &deletedWorkflow)
if err != nil {
return DeleteChaosWorkflowData{}, err
}
if len(deletedWorkflow.Errors) > 0 {
return DeleteChaosWorkflowData{}, errors.New(deletedWorkflow.Errors[0].Message)
}
return deletedWorkflow, nil
} else {
return DeleteChaosWorkflowData{}, errors.New("Error while deleting the Chaos Scenario")
}
}
type ServerVersionResponse struct {
Data ServerVersionData `json:"data"`
Errors []struct {
Message string `json:"message"`
Path []string `json:"path"`
} `json:"errors"`
}
type ServerVersionData struct {
GetServerVersion GetServerVersionData `json:"getServerVersion"`
}
type GetServerVersionData struct {
Key string `json:"key"`
Value string `json:"value"`
}
// GetServerVersion fetches the GQL server version
func GetServerVersion(endpoint string) (ServerVersionResponse, error) {
query := `{"query":"query{\n getServerVersion{\n key value\n }\n}"}`
resp, err := SendRequest(
SendRequestParams{
Endpoint: endpoint + utils.GQLAPIPath,
},
[]byte(query),
string(types.Post),
)
if err != nil {
return ServerVersionResponse{}, err
}
bodyBytes, err := ioutil.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

@ -5,7 +5,7 @@ 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,

View File

@ -5,7 +5,7 @@ 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,

View File

@ -5,7 +5,7 @@ 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,19 +16,23 @@ 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"
"golang.org/x/term"
)
// setAccountCmd represents the setAccount command
@ -47,20 +51,54 @@ var setAccountCmd = &cobra.Command{
err error
)
authInput.Endpoint, err = cmd.Flags().GetString("endpoint")
nonInteractive, err := cmd.Flags().GetBool("non-interactive")
utils.PrintError(err)
authInput.Username, err = cmd.Flags().GetString("username")
utils.PrintError(err)
if nonInteractive {
authInput.Endpoint, err = cmd.Flags().GetString("endpoint")
utils.PrintError(err)
authInput.Password, err = cmd.Flags().GetString("password")
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)
for authInput.Endpoint == "" {
if authInput.Endpoint == "" {
utils.Red.Println("\n⛔ Host URL can't be empty!!")
os.Exit(1)
}
@ -72,27 +110,6 @@ var setAccountCmd = &cobra.Command{
authInput.Endpoint = newUrl.String()
}
if authInput.Username == "" {
utils.White_B.Print("\nUsername [Default: ", utils.DefaultUsername, "]: ")
fmt.Scanln(&authInput.Username)
if authInput.Username == "" {
authInput.Username = utils.DefaultUsername
}
}
if authInput.Password == "" {
utils.White_B.Print("\nPassword: ")
pass, err := term.ReadPassword(0)
utils.PrintError(err)
if pass == nil {
utils.Red.Println("\n⛔ Password cannot be empty!")
os.Exit(1)
}
authInput.Password = string(pass)
}
if authInput.Endpoint != "" && authInput.Username != "" && authInput.Password != "" {
exists := config.FileExists(configFilePath)
var lgt int
@ -120,8 +137,9 @@ var setAccountCmd = &cobra.Command{
users = append(users, user)
var account = types.Account{
Endpoint: authInput.Endpoint,
Users: users,
Endpoint: authInput.Endpoint,
Users: users,
ServerEndpoint: authInput.Endpoint,
}
// If config file doesn't exist or length of the file is zero.
@ -141,7 +159,6 @@ var setAccountCmd = &cobra.Command{
err := config.CreateNewLitmusCtlConfig(configFilePath, litmuCtlConfig)
utils.PrintError(err)
os.Exit(0)
} else {
// checking syntax
err = config.ConfigSyntaxCheck(configFilePath)
@ -151,48 +168,79 @@ var setAccountCmd = &cobra.Command{
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))
serverResp, err := apis.GetServerVersion(authInput.Endpoint)
var isCompatible bool
credentials, err := utils.GetCredentials(cmd)
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
}
}
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)
}
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! ")
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
}
}
} else {
utils.Red.Println("\nError: some flags are missing. Run 'litmusctl config set-account --help' for usage. ")
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

@ -5,7 +5,7 @@ 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,
@ -79,7 +79,9 @@ var useAccountCmd = &cobra.Command{
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)

View File

@ -5,7 +5,7 @@ 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,
@ -17,7 +17,6 @@ package config
import (
"fmt"
"io/ioutil"
"github.com/litmuschaos/litmusctl/pkg/utils"
@ -41,7 +40,7 @@ var viewCmd = &cobra.Command{
os.Exit(1)
}
data, err := ioutil.ReadFile(configFilePath)
data, err := os.ReadFile(configFilePath)
utils.PrintError(err)
//Printing the config map

View File

@ -1,298 +0,0 @@
/*
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 (
"encoding/json"
"fmt"
"os"
"github.com/litmuschaos/litmusctl/pkg/agent"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/k8s"
"github.com/litmuschaos/litmusctl/pkg/types"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// agentCmd represents the Chaos Delegate command
var agentCmd = &cobra.Command{
Use: "chaos-delegate",
Short: `Connect an external Chaos Delegate.
Example(s):
#connect a Chaos Delegate
litmusctl connect chaos-delegate --name="new-chaos-delegate" --non-interactive
#connect a Chaos Delegate within a project
litmusctl connect chaos-delegate --name="new-chaos-delegate" --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 newAgent types.Agent
newAgent.ProjectId, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if newAgent.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...")
newAgent.ProjectId = agent.CreateRandomProject(credentials)
}
}
if nonInteractive {
newAgent.Mode, err = cmd.Flags().GetString("installation-mode")
utils.PrintError(err)
if newAgent.Mode == "" {
utils.Red.Print("Error: --installation-mode flag is empty")
os.Exit(1)
}
newAgent.AgentName, err = cmd.Flags().GetString("name")
utils.PrintError(err)
newAgent.SkipSSL, err = cmd.Flags().GetBool("skip-ssl")
utils.PrintError(err)
if newAgent.AgentName == "" {
utils.Red.Print("Error: --name flag is empty")
os.Exit(1)
}
newAgent.Description, err = cmd.Flags().GetString("description")
utils.PrintError(err)
newAgent.PlatformName, err = cmd.Flags().GetString("platform-name")
utils.PrintError(err)
if newAgent.PlatformName == "" {
utils.Red.Print("Error: --platform-name flag is empty")
os.Exit(1)
}
newAgent.ClusterType, err = cmd.Flags().GetString("chaos-delegate-type")
utils.PrintError(err)
if newAgent.ClusterType == "" {
utils.Red.Print("Error: --chaos-delegate-type flag is empty")
os.Exit(1)
}
newAgent.NodeSelector, err = cmd.Flags().GetString("node-selector")
utils.PrintError(err)
if newAgent.NodeSelector != "" {
if ok := utils.CheckKeyValueFormat(newAgent.NodeSelector); !ok {
os.Exit(1)
}
}
toleration, err := cmd.Flags().GetString("tolerations")
utils.PrintError(err)
if toleration != "" {
var tolerations []types.Toleration
err := json.Unmarshal([]byte(toleration), &tolerations)
utils.PrintError(err)
str := "["
for _, tol := range tolerations {
str += "{"
if tol.TolerationSeconds > 0 {
str += "tolerationSeconds: " + fmt.Sprint(tol.TolerationSeconds) + " "
}
if tol.Effect != "" {
str += "effect: \\\"" + tol.Effect + "\\\" "
}
if tol.Key != "" {
str += "key: \\\"" + tol.Key + "\\\" "
}
if tol.Value != "" {
str += "value: \\\"" + tol.Value + "\\\" "
}
if tol.Operator != "" {
str += "operator : \\\"" + tol.Operator + "\\\" "
}
str += " }"
}
str += "]"
newAgent.Tolerations = str
}
newAgent.Namespace, err = cmd.Flags().GetString("namespace")
utils.PrintError(err)
newAgent.ServiceAccount, err = cmd.Flags().GetString("service-account")
utils.PrintError(err)
newAgent.NsExists, err = cmd.Flags().GetBool("ns-exists")
utils.PrintError(err)
newAgent.SAExists, err = cmd.Flags().GetBool("sa-exists")
utils.PrintError(err)
if newAgent.Mode == "" {
newAgent.Mode = utils.DefaultMode
}
if newAgent.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....")
agent.ValidateSAPermissions(newAgent.Namespace, newAgent.Mode, &kubeconfig)
agents, err := apis.GetAgentList(credentials, newAgent.ProjectId)
utils.PrintError(err)
// Duplicate agent check
var isAgentExist = false
for i := range agents.Data.GetAgent {
if newAgent.AgentName == agents.Data.GetAgent[i].AgentName {
utils.White_B.Print(agents.Data.GetAgent[i].AgentName)
isAgentExist = true
}
}
if isAgentExist {
agent.PrintExistingAgents(agents)
os.Exit(1)
}
} else {
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)
if newAgent.ProjectId == "" {
// Fetch project id
newAgent.ProjectId = agent.GetProjectID(userDetails)
}
modeType := agent.GetModeType()
// Check if user has sufficient permissions based on mode
utils.White_B.Print("\n🏃 Running prerequisites check....")
agent.ValidateSAPermissions(newAgent.Namespace, modeType, &kubeconfig)
newAgent, err = agent.GetAgentDetails(modeType, newAgent.ProjectId, credentials, &kubeconfig)
utils.PrintError(err)
newAgent.ServiceAccount, newAgent.SAExists = k8s.ValidSA(newAgent.Namespace, &kubeconfig)
newAgent.Mode = modeType
}
agent.Summary(newAgent, &kubeconfig)
if !nonInteractive {
agent.ConfirmInstallation()
}
agent, err := apis.ConnectAgent(newAgent, credentials)
if err != nil {
utils.Red.Println("\n❌ Chaos Delegate connection failed: " + err.Error() + "\n")
os.Exit(1)
}
if agent.Data.UserAgentReg.Token == "" {
utils.Red.Println("\n❌ failed to get the agent registration token: " + "\n")
os.Exit(1)
}
path := fmt.Sprintf("%s/%s/%s.yaml", credentials.Endpoint, utils.ChaosYamlPath, agent.Data.UserAgentReg.Token)
utils.White_B.Print("Applying YAML:\n", path)
// Print error message in case Data field is null in response
if (agent.Data == apis.AgentConnect{}) {
utils.White_B.Print("\n🚫 Chaos Delegate connection failed: " + agent.Errors[0].Message + "\n")
os.Exit(1)
}
//Apply agent connection yaml
yamlOutput, err := k8s.ApplyYaml(k8s.ApplyYamlPrams{
Token: agent.Data.UserAgentReg.Token,
Endpoint: credentials.Endpoint,
YamlPath: utils.ChaosYamlPath,
}, kubeconfig, false)
if err != nil {
utils.Red.Print("\n❌ Failed in applying connection yaml: \n" + err.Error() + "\n")
utils.White_B.Print("\n Error: \n" + err.Error())
os.Exit(1)
}
utils.White_B.Print("\n", yamlOutput)
// Watch subscriber pod status
k8s.WatchPod(k8s.WatchPodParams{Namespace: newAgent.Namespace, Label: utils.ChaosAgentLabel}, &kubeconfig)
utils.White_B.Println("\n🚀 Chaos Delegate connection successful!! 🎉")
utils.White_B.Println("👉 Litmus Chaos Delegates can be accessed here: " + fmt.Sprintf("%s/%s", credentials.Endpoint, utils.ChaosAgentPath))
},
}
func init() {
ConnectCmd.AddCommand(agentCmd)
agentCmd.Flags().BoolP("non-interactive", "n", false, "Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean")
agentCmd.Flags().StringP("kubeconfig", "k", "", "Set to pass kubeconfig file if it is not in the default location ($HOME/.kube/config)")
agentCmd.Flags().String("tolerations", "", "Set the tolerations for Chaos Delegate components | Format: '[{\"key\":\"key1\",\"value\":\"value1\",\"operator\":\"Exist\",\"effect\":\"NoSchedule\",\"tolerationSeconds\":30}]'")
agentCmd.Flags().String("project-id", "", "Set the project-id to install Chaos Delegate for the particular project. To see the projects, apply litmusctl get projects")
agentCmd.Flags().String("installation-mode", "cluster", "Set the installation mode for the kind of Chaos Delegate | Supported=cluster/namespace")
agentCmd.Flags().String("name", "", "Set the Chaos Delegate name")
agentCmd.Flags().String("description", "---", "Set the Chaos Delegate description")
agentCmd.Flags().String("platform-name", "Others", "Set the platform name. Supported- AWS/GKE/Openshift/Rancher/Others")
agentCmd.Flags().String("chaos-delegate-type", "external", "Set the chaos-delegate-type to external for external Chaos Delegates | Supported=external/internal")
agentCmd.Flags().String("node-selector", "", "Set the node-selector for Chaos Delegate components | Format: \"key1=value1,key2=value2\")")
agentCmd.Flags().String("namespace", "litmus", "Set the namespace for the Chaos Delegate installation")
agentCmd.Flags().String("service-account", "litmus", "Set the service account to be used by the Chaos Delegate")
agentCmd.Flags().Bool("skip-ssl", false, "Set whether Chaos Delegate will skip ssl/tls check (can be used for self-signed certs, if cert is not provided in portal)")
agentCmd.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")
agentCmd.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")
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,14 +22,13 @@ import (
// connectCmd represents the connect command
var ConnectCmd = &cobra.Command{
Use: "connect",
Short: `Connect resources for LitmusChaos agent plane.
Short: `Connect resources for LitmusChaos Execution plane.
Examples:
#connect a Chaos Delegate
litmusctl connect chaos-delegate --name="new-chaos-delegate" --non-interactive
#connect a chaos-delegate within a project
litmusctl connect chaos-delegate --name="new-chaos-delegate" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
#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")
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,14 +22,16 @@ import (
// createCmd represents the create command
var CreateCmd = &cobra.Command{
Use: "create",
Short: `Create resources for LitmusChaos agent plane.
Short: `Create resources for LitmusChaos Execution plane.
Examples:
#create a project
litmusctl create project --name new-proj
#create a Chaos Scenario from a file
litmusctl create chaos-scenario -f chaos-scenario.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-delegate-id="d861b650-1549-4574-b2ba-ab754058dd04"
#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")
}

View File

@ -5,7 +5,7 @@ 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,
@ -21,6 +21,7 @@ import (
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
@ -40,14 +41,30 @@ var projectCmd = &cobra.Command{
projectName, err := cmd.Flags().GetString("name")
utils.PrintError(err)
if projectName == "" {
utils.White_B.Print("\nEnter a project name: ")
fmt.Scanln(&projectName)
}
// prompt to ask project name
prompt := promptui.Prompt{
Label: "Enter a project name",
AllowEdit: true,
}
_, err = apis.CreateProjectRequest(projectName, credentials)
utils.PrintError(err)
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)
}
},
}

View File

@ -1,141 +0,0 @@
/*
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"
"time"
"github.com/gorhill/cronexpr"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
"github.com/spf13/cobra"
)
// workflowCmd represents the project command
var workflowCmd = &cobra.Command{
Use: "chaos-scenario",
Short: `Create a Chaos Scenario
Example:
#create a Chaos Scenario
litmusctl create chaos-scenario -f chaos-scenario.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-delegate-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 chaosWorkFlowRequest model.ChaosWorkFlowRequest
workflowManifest, err := cmd.Flags().GetString("file")
utils.PrintError(err)
chaosWorkFlowRequest.ProjectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
// Handle blank input for project ID
if chaosWorkFlowRequest.ProjectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&chaosWorkFlowRequest.ProjectID)
if chaosWorkFlowRequest.ProjectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
chaosWorkFlowRequest.ClusterID, err = cmd.Flags().GetString("chaos-delegate-id")
utils.PrintError(err)
// Handle blank input for Chaos Delegate ID
if chaosWorkFlowRequest.ClusterID == "" {
utils.White_B.Print("\nEnter the Chaos Delegate ID: ")
fmt.Scanln(&chaosWorkFlowRequest.ClusterID)
if chaosWorkFlowRequest.ClusterID == "" {
utils.Red.Println("⛔ Chaos Delegate 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 == chaosWorkFlowRequest.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)
}
// Parse workflow manifest and populate chaosWorkFlowInput
err = utils.ParseWorkflowManifest(workflowManifest, &chaosWorkFlowRequest)
if err != nil {
utils.Red.Println("❌ Error parsing Chaos Scenario manifest: " + err.Error())
os.Exit(1)
}
// Make API call
createdWorkflow, err := apis.CreateWorkflow(chaosWorkFlowRequest, credentials)
if err != nil {
if (createdWorkflow.Data == apis.CreatedChaosWorkflow{}) {
if strings.Contains(err.Error(), "multiple write errors") {
utils.Red.Println("\n❌ Chaos Scenario/" + chaosWorkFlowRequest.WorkflowName + " already exists")
os.Exit(1)
} else {
utils.White_B.Print("\n❌ Chaos Scenario/" + chaosWorkFlowRequest.WorkflowName + " failed to be created: " + err.Error())
os.Exit(1)
}
}
}
// Successful creation
utils.White_B.Println("\n🚀 Chaos Scenario/" + createdWorkflow.Data.CreateChaosWorkflow.WorkflowName + " successfully created 🎉")
if createdWorkflow.Data.CreateChaosWorkflow.CronSyntax == "" {
utils.White_B.Println("\nThe next run of this Chaos Scenario will be scheduled immediately.")
} else {
utils.White_B.Println(
"\nThe next run of this Chaos Scenario will be scheduled at " +
cronexpr.MustParse(createdWorkflow.Data.CreateChaosWorkflow.CronSyntax).Next(time.Now()).Format("January 2nd 2006, 03:04:05 pm"))
}
},
}
func init() {
CreateCmd.AddCommand(workflowCmd)
workflowCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Scenario for the particular project. To see the projects, apply litmusctl get projects")
workflowCmd.Flags().String("chaos-delegate-id", "", "Set the chaos-delegate-id to create Chaos Scenario for the particular Chaos Delegate. To see the Chaos Delegates, apply litmusctl get chaos-delegates")
workflowCmd.Flags().StringP("file", "f", "", "The manifest file for the Chaos Scenario")
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,10 +22,16 @@ import (
// deleteCmd represents the delete command
var DeleteCmd = &cobra.Command{
Use: "delete",
Short: `Delete resources for LitmusChaos agent plane.
Short: `Delete resources for LitmusChaos Execution plane.
Examples:
#delete a Chaos Scenario
litmusctl delete chaos-scenario c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
#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

@ -1,111 +0,0 @@
/*
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 (
"fmt"
"os"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// workflowCmd represents the Chaos Scenario command
var workflowCmd = &cobra.Command{
Use: "chaos-scenario",
Short: `Delete a Chaos Scenario
Example:
#delete a Chaos Scenario
litmusctl delete chaos-scenario 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
`,
Args: cobra.ExactArgs(1),
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)
}
}
workflowID := args[0]
// Handle blank input for Chaos Scenario ID
if workflowID == "" {
utils.White_B.Print("\nEnter the Chaos Scenario ID: ")
fmt.Scanln(&workflowID)
if workflowID == "" {
utils.Red.Println("⛔ Chaos Scenario 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
deletedWorkflow, err := apis.DeleteChaosWorkflow(projectID, &workflowID, credentials)
if err != nil {
utils.Red.Println("\n❌ Error in deleting Chaos Scenario: ", err.Error())
os.Exit(1)
}
if deletedWorkflow.Data.IsDeleted {
utils.White_B.Println("\n🚀 Chaos Scenario successfully deleted.")
} else {
utils.White_B.Println("\n❌ Failed to delete Chaos Scenario. Please check if the ID is correct or not.")
}
},
}
func init() {
DeleteCmd.AddCommand(workflowCmd)
workflowCmd.Flags().String("project-id", "", "Set the project-id to create Chaos Scenario for the particular project. To see the projects, apply litmusctl get projects")
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,10 +22,13 @@ import (
// DescribeCmd represents the describe command
var DescribeCmd = &cobra.Command{
Use: "describe",
Short: `Describe resources for LitmusChaos agent plane.
Short: `Describe resources for LitmusChaos Execution plane.
Examples:
#describe a Chaos Scenario
litmusctl describe chaos-scenario d861b650-1549-4574-b2ba-ab754058dd04 --project-id="d861b650-1549-4574-b2ba-ab754058dd04"
#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

@ -1,90 +0,0 @@
/*
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 (
"fmt"
"os"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)
// workflowCmd represents the Chaos Scenario command
var workflowCmd = &cobra.Command{
Use: "chaos-scenario",
Short: "Describe a Chaos Scenario within the project",
Long: `Describe a Chaos Scenario within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var describeWorkflowRequest model.ListWorkflowsRequest
describeWorkflowRequest.ProjectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if describeWorkflowRequest.ProjectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&describeWorkflowRequest.ProjectID)
for describeWorkflowRequest.ProjectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
var workflowID string
if len(args) == 0 {
utils.White_B.Print("\nEnter the Chaos Scenario ID: ")
fmt.Scanln(&workflowID)
} else {
workflowID = args[0]
}
// Handle blank input for Chaos Scenario ID
if workflowID == "" {
utils.Red.Println("⛔ Chaos Scenario ID can't be empty!!")
os.Exit(1)
}
describeWorkflowRequest.WorkflowIDs = append(describeWorkflowRequest.WorkflowIDs, &workflowID)
workflow, err := apis.GetWorkflowList(describeWorkflowRequest, credentials)
utils.PrintError(err)
if len(workflow.Data.ListWorkflowDetails.Workflows) == 0 {
utils.Red.Println("⛔ No chaos scenarios found with ID: ", workflowID)
os.Exit(1)
}
yamlManifest, err := yaml.JSONToYAML([]byte(workflow.Data.ListWorkflowDetails.Workflows[0].WorkflowManifest))
if err != nil {
utils.Red.Println("❌ Error parsing Chaos Scenario manifest: " + err.Error())
os.Exit(1)
}
utils.PrintInYamlFormat(string(yamlManifest))
},
}
func init() {
DescribeCmd.AddCommand(workflowCmd)
workflowCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Scenarios from the particular project. To see the projects, apply litmusctl get projects")
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,10 +22,10 @@ import (
// disconnectCmd represents the disconnect command
var DisconnectCmd = &cobra.Command{
Use: "disconnect",
Short: `Disconnect resources for LitmusChaos agent plane.
Short: `Disconnect resources for LitmusChaos Execution plane.
Examples:
#disconnect a Chaos Delegate
litmusctl disconnect chaos-delegate c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
#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
`,

View File

@ -5,7 +5,7 @@ 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,
@ -20,19 +20,21 @@ import (
"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"
)
// agentCmd represents the agent command
var agentCmd = &cobra.Command{
Use: "chaos-delegate",
Short: `Disconnect a Chaos Delegate
// infraCmd represents the infra command
var infraCmd = &cobra.Command{
Use: "chaos-infra",
Short: `Disconnect a Chaos Infrastructure
Example:
#disconnect a Chaos Delegate
litmusctl disconnect chaos-delegate c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
#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
`,
@ -56,16 +58,16 @@ var agentCmd = &cobra.Command{
}
}
var agentID string
var infraID string
if len(args) == 0 {
utils.White_B.Print("\nEnter the Agent ID: ")
fmt.Scanln(&agentID)
utils.White_B.Print("\nEnter the Infra ID: ")
fmt.Scanln(&infraID)
} else {
agentID = args[0]
infraID = args[0]
}
// Handle blank input for agent ID
if agentID == "" {
utils.Red.Println("⛔ Chaos Delegate ID can't be empty!!")
// Handle blank input for Infra ID
if infraID == "" {
utils.Red.Println("⛔ Chaos Infra ID can't be empty!!")
os.Exit(1)
}
@ -90,24 +92,27 @@ var agentCmd = &cobra.Command{
}
// Make API call
var agentIDs []*string
agentIDs = append(agentIDs, &agentID)
disconnectedAgent, err := apis.DisconnectAgent(projectID, agentIDs, credentials)
disconnectedInfra, err := infrastructure.DisconnectInfra(projectID, infraID, credentials)
if err != nil {
utils.Red.Println("\n❌ Error in disconnecting Chaos Delegate: ", err.Error())
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.Red.Println("\n❌ Error in disconnecting Chaos Infrastructure: ", err.Error())
os.Exit(1)
}
}
if strings.Contains(disconnectedAgent.Data.Message, "Successfully deleted clusters") {
utils.White_B.Println("\n🚀 Chaos Delegate successfully disconnected.")
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 Delegate. Please check if the ID is correct or not.")
utils.White_B.Println("\n❌ Failed to disconnect Chaos Infrastructure. Please check if the ID is correct or not.")
}
},
}
func init() {
DisconnectCmd.AddCommand(agentCmd)
DisconnectCmd.AddCommand(infraCmd)
agentCmd.Flags().String("project-id", "", "Set the project-id to disconnect Chaos Delegate for the particular project. To see the projects, apply litmusctl get projects")
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")
}

View File

@ -1,95 +0,0 @@
/*
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"
"text/tabwriter"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// agentsCmd represents the agents command
var agentsCmd = &cobra.Command{
Use: "chaos-delegates",
Short: "Display list of Chaos Delegates within the project",
Long: `Display list of Chaos Delegates 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)
}
}
agents, err := apis.GetAgentList(credentials, projectID)
utils.PrintError(err)
output, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch output {
case "json":
utils.PrintInJsonFormat(agents.Data)
case "yaml":
utils.PrintInYamlFormat(agents.Data)
case "":
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS DELEGATE ID \tCHAOS DELEGATE NAME\tSTATUS\tREGISTRATION\t")
for _, agent := range agents.Data.GetAgent {
var status string
if agent.IsActive {
status = "ACTIVE"
} else {
status = "INACTIVE"
}
var isRegistered string
if agent.IsRegistered {
isRegistered = "REGISTERED"
} else {
isRegistered = "NOT REGISTERED"
}
utils.White.Fprintln(writer, agent.ClusterID+"\t"+agent.AgentName+"\t"+status+"\t"+isRegistered+"\t")
}
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(agentsCmd)
agentsCmd.Flags().String("project-id", "", "Set the project-id. To retrieve projects. Apply `litmusctl get projects`")
agentsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

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")
}

View File

@ -5,7 +5,7 @@ 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,
@ -26,14 +26,20 @@ var GetCmd = &cobra.Command{
#get list of projects accessed by the user
litmusctl get projects
#get list of Chaos Delegates within the project
litmusctl get chaos-delegates --project-id=""
#get list of Chaos Infrastructure within the project
litmusctl get chaos-infra --project-id=""
#get list of chaos Chaos Scenarios
litmusctl get chaos-scenarios --project-id=""
#get list of chaos Chaos Experiments
litmusctl get chaos-experiments --project-id=""
#get list of Chaos Scenario runs
litmusctl get chaos-scenario-runs --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)
}
}

View File

@ -5,7 +5,7 @@ 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,
@ -17,12 +17,12 @@ package get
import (
"os"
"strconv"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)
@ -35,31 +35,63 @@ var projectsCmd = &cobra.Command{
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
outputFormat, _ := cmd.Flags().GetString("output")
projects, err := apis.ListProject(credentials)
utils.PrintError(err)
output, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch output {
switch outputFormat {
case "json":
utils.PrintInJsonFormat(projects.Data)
case "yaml":
utils.PrintInYamlFormat(projects.Data)
utils.PrintInYamlFormat(projects.Data.Projects)
case "":
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 {
intTime, err := strconv.ParseInt(project.CreatedAt, 10, 64)
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)
humanTime := time.Unix(intTime, 0)
utils.White.Fprintln(writer, project.ID+"\t"+project.Name+"\t"+humanTime.String()+"\t")
if userInput == "q" {
break
} else {
page++
}
}
writer.Flush()
}
},
}

View File

@ -1,113 +0,0 @@
/*
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"
"text/tabwriter"
"time"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// workflowRunsCmd represents the Chaos Scenario runs command
var workflowRunsCmd = &cobra.Command{
Use: "chaos-scenario-runs",
Short: "Display list of Chaos Scenario runs within the project",
Long: `Display list of Chaos Scenario runs within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var listWorkflowRunsRequest model.ListWorkflowRunsRequest
listWorkflowRunsRequest.ProjectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if listWorkflowRunsRequest.ProjectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&listWorkflowRunsRequest.ProjectID)
for listWorkflowRunsRequest.ProjectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
listAllWorkflowRuns, _ := cmd.Flags().GetBool("all")
if !listAllWorkflowRuns {
listWorkflowRunsRequest.Pagination = &model.Pagination{}
listWorkflowRunsRequest.Pagination.Limit, _ = cmd.Flags().GetInt("count")
}
workflowRuns, err := apis.GetWorkflowRunsList(listWorkflowRunsRequest, credentials)
utils.PrintError(err)
output, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch output {
case "json":
utils.PrintInJsonFormat(workflowRuns.Data)
case "yaml":
utils.PrintInYamlFormat(workflowRuns.Data)
case "":
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS SCENARIO RUN ID\tSTATUS\tRESILIENCY SCORE\tCHAOS SCENARIO ID\tCHAOS SCENARIO NAME\tTARGET CHAOS DELEGATE\tLAST RUN\tEXECUTED BY")
for _, workflowRun := range workflowRuns.Data.ListWorkflowRunsDetails.WorkflowRuns {
var lastUpdated string
unixSecondsInt, err := strconv.ParseInt(workflowRun.LastUpdated, 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,
workflowRun.WorkflowRunID+"\t"+workflowRun.Phase+"\t"+strconv.FormatFloat(*workflowRun.ResiliencyScore, 'f', 2, 64)+"\t"+workflowRun.WorkflowID+"\t"+workflowRun.WorkflowName+"\t"+workflowRun.ClusterName+"\t"+lastUpdated+"\t"+workflowRun.ExecutedBy)
}
if listAllWorkflowRuns || (workflowRuns.Data.ListWorkflowRunsDetails.TotalNoOfWorkflowRuns <= listWorkflowRunsRequest.Pagination.Limit) {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Scenario runs", workflowRuns.Data.ListWorkflowRunsDetails.TotalNoOfWorkflowRuns, workflowRuns.Data.ListWorkflowRunsDetails.TotalNoOfWorkflowRuns))
} else {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Scenario runs", listWorkflowRunsRequest.Pagination.Limit, workflowRuns.Data.ListWorkflowRunsDetails.TotalNoOfWorkflowRuns))
}
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(workflowRunsCmd)
workflowRunsCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Scenarios from the particular project. To see the projects, apply litmusctl get projects")
workflowRunsCmd.Flags().Int("count", 30, "Set the count of Chaos Scenario runs to display. Default value is 30")
workflowRunsCmd.Flags().BoolP("all", "A", false, "Set to true to display all Chaos Scenario runs")
workflowRunsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

View File

@ -1,115 +0,0 @@
/*
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"
"text/tabwriter"
"time"
"github.com/gorhill/cronexpr"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
"github.com/spf13/cobra"
)
// workflowsCmd represents the Chaos Scenarios command
var workflowsCmd = &cobra.Command{
Use: "chaos-scenarios",
Short: "Display list of Chaos Scenarios within the project",
Long: `Display list of Chaos Scenarios within the project`,
Run: func(cmd *cobra.Command, args []string) {
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)
var listWorkflowsRequest model.ListWorkflowsRequest
listWorkflowsRequest.ProjectID, err = cmd.Flags().GetString("project-id")
utils.PrintError(err)
if listWorkflowsRequest.ProjectID == "" {
utils.White_B.Print("\nEnter the Project ID: ")
fmt.Scanln(&listWorkflowsRequest.ProjectID)
for listWorkflowsRequest.ProjectID == "" {
utils.Red.Println("⛔ Project ID can't be empty!!")
os.Exit(1)
}
}
listAllWorkflows, _ := cmd.Flags().GetBool("all")
if !listAllWorkflows {
listWorkflowsRequest.Pagination = &model.Pagination{}
listWorkflowsRequest.Pagination.Limit, _ = cmd.Flags().GetInt("count")
}
listWorkflowsRequest.Filter = &model.WorkflowFilterInput{}
agentName, err := cmd.Flags().GetString("chaos-delegate")
utils.PrintError(err)
listWorkflowsRequest.Filter.ClusterName = &agentName
workflows, err := apis.GetWorkflowList(listWorkflowsRequest, credentials)
utils.PrintError(err)
output, err := cmd.Flags().GetString("output")
utils.PrintError(err)
switch output {
case "json":
utils.PrintInJsonFormat(workflows.Data)
case "yaml":
utils.PrintInYamlFormat(workflows.Data)
case "":
writer := tabwriter.NewWriter(os.Stdout, 4, 8, 1, '\t', 0)
utils.White_B.Fprintln(writer, "CHAOS SCENARIO ID\tCHAOS SCENARIO NAME\tCHAOS SCENARIO TYPE\tNEXT SCHEDULE\tCHAOS DELEGATE ID\tCHAOS DELEGATE NAME\tLAST UPDATED BY")
for _, workflow := range workflows.Data.ListWorkflowDetails.Workflows {
if workflow.CronSyntax != "" {
utils.White.Fprintln(
writer,
workflow.WorkflowID+"\t"+workflow.WorkflowName+"\tCron Chaos Scenario\t"+cronexpr.MustParse(workflow.CronSyntax).Next(time.Now()).Format("January 2 2006, 03:04:05 pm")+"\t"+workflow.ClusterID+"\t"+workflow.ClusterName+"\t"+*workflow.LastUpdatedBy)
} else {
utils.White.Fprintln(
writer,
workflow.WorkflowID+"\t"+workflow.WorkflowName+"\tNon Cron Chaos Scenario\tNone\t"+workflow.ClusterID+"\t"+workflow.ClusterName+"\t"+*workflow.LastUpdatedBy)
}
}
if listAllWorkflows || (workflows.Data.ListWorkflowDetails.TotalNoOfWorkflows <= listWorkflowsRequest.Pagination.Limit) {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Scenarios", workflows.Data.ListWorkflowDetails.TotalNoOfWorkflows, workflows.Data.ListWorkflowDetails.TotalNoOfWorkflows))
} else {
utils.White_B.Fprintln(writer, fmt.Sprintf("\nShowing %d of %d Chaos Scenarios", listWorkflowsRequest.Pagination.Limit, workflows.Data.ListWorkflowDetails.TotalNoOfWorkflows))
}
writer.Flush()
}
},
}
func init() {
GetCmd.AddCommand(workflowsCmd)
workflowsCmd.Flags().String("project-id", "", "Set the project-id to list Chaos Scenarios from the particular project. To see the projects, apply litmusctl get projects")
workflowsCmd.Flags().Int("count", 30, "Set the count of Chaos Scenarios to display. Default value is 30")
workflowsCmd.Flags().Bool("all", false, "Set to true to display all Chaos Scenarios")
workflowsCmd.Flags().StringP("chaos-delegate", "A", "", "Set the Chaos Delegate name to display all Chaos Scenarios targeted towards that particular Chaos Delegate.")
workflowsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
}

View File

@ -5,7 +5,7 @@ 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,
@ -19,10 +19,13 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"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"
@ -70,6 +73,9 @@ func init() {
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,
@ -101,7 +107,7 @@ func initConfig() {
if config2.SkipSSLVerify {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else if config2.CACert != "" {
caCert, err := ioutil.ReadFile(config2.CACert)
caCert, err := os.ReadFile(config2.CACert)
cobra.CheckErr(err)
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

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
`,
}

View File

@ -5,7 +5,7 @@ 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,
@ -19,6 +19,7 @@ import (
"context"
"fmt"
"os"
"strings"
"github.com/litmuschaos/litmusctl/pkg/apis"
"github.com/litmuschaos/litmusctl/pkg/utils"
@ -26,9 +27,9 @@ import (
)
// createCmd represents the create command
var agentCmd = &cobra.Command{
Use: "chaos-delegate",
Short: `Upgrades the LitmusChaos agent plane.`,
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)
@ -41,20 +42,24 @@ var agentCmd = &cobra.Command{
fmt.Scanln(&projectID)
}
cluster_id, err := cmd.Flags().GetString("chaos-delegate-id")
infraID, err := cmd.Flags().GetString("chaos-infra-id")
utils.PrintError(err)
if cluster_id == "" {
utils.White_B.Print("\nEnter the Chaos Delegate ID: ")
fmt.Scanln(&cluster_id)
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.UpgradeAgent(context.Background(), credentials, projectID, cluster_id, kubeconfig)
output, err := apis.UpgradeInfra(context.Background(), credentials, projectID, infraID, kubeconfig)
if err != nil {
utils.Red.Print("\n❌ Failed upgrading Chaos Delegate: \n" + err.Error() + "\n")
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)
@ -62,8 +67,8 @@ var agentCmd = &cobra.Command{
}
func init() {
UpgradeCmd.AddCommand(agentCmd)
agentCmd.Flags().String("project-id", "", "Enter the project ID")
agentCmd.Flags().String("kubeconfig", "", "Enter the kubeconfig path(default: $HOME/.kube/config))")
agentCmd.Flags().String("chaos-delegate-id", "", "Enter the Chaos Delegate ID")
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

@ -5,7 +5,7 @@ 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,
@ -23,8 +23,8 @@ import (
var UpgradeCmd = &cobra.Command{
Use: "upgrade",
Short: `Examples:
#upgrade version of your Chaos Delegate
litmusctl upgrade chaos-delegate --chaos-delegate-id="4cc25543-36c8-4373-897b-2e5dbbe87bcf" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
#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

@ -5,7 +5,7 @@ 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,10 +16,14 @@ limitations under the License.
package version
import (
"io"
"net/http"
"os"
"os/exec"
"runtime"
"github.com/litmuschaos/litmusctl/pkg/utils"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
)
@ -29,7 +33,12 @@ var VersionCmd = &cobra.Command{
Short: "Displays the version of litmusctl",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
compatibilityArr := utils.CompatibilityMatrix[os.Getenv("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("[ ")
@ -39,3 +48,92 @@ var VersionCmd = &cobra.Command{
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

@ -5,7 +5,7 @@ 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,
@ -17,7 +17,6 @@ package config
import (
"errors"
"io/ioutil"
"os"
"github.com/litmuschaos/litmusctl/pkg/types"
@ -41,7 +40,7 @@ func CreateNewLitmusCtlConfig(filename string, config types.LitmuCtlConfig) erro
return err
}
err = ioutil.WriteFile(filename, configByte, 0644)
err = os.WriteFile(filename, configByte, 0644)
if err != nil {
return err
}
@ -59,7 +58,7 @@ func FileExists(filename string) bool {
}
func GetFileLength(filename string) (int, error) {
data, err := ioutil.ReadFile(filename)
data, err := os.ReadFile(filename)
if err != nil {
return -1, err
}
@ -68,7 +67,7 @@ func GetFileLength(filename string) (int, error) {
}
func YamltoObject(filename string) (types.LitmuCtlConfig, error) {
data, err := ioutil.ReadFile(filename)
data, err := os.ReadFile(filename)
if err != nil {
return types.LitmuCtlConfig{}, errors.New("File reading error " + err.Error())
}
@ -106,6 +105,7 @@ func UpdateLitmusCtlConfig(litmusconfig types.UpdateLitmusCtlConfig, filename st
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
@ -165,7 +165,7 @@ func writeObjToFile(obj types.LitmuCtlConfig, filename string) error {
return err
}
err = ioutil.WriteFile(filename, byteObj, 0644)
err = os.WriteFile(filename, byteObj, 0644)
if err != nil {
return err
}

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
}

View File

@ -5,7 +5,7 @@ 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,
@ -13,7 +13,7 @@ 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 agent
package infra_ops
import (
"context"
@ -72,15 +72,16 @@ func DiscoverPlatform(kubeconfig *string) string {
// 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"
// }
// }
// }
//
// {
// "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 {
@ -100,15 +101,16 @@ func IsAWSPlatform(kubeconfig *string) (bool, error) {
// by checking the ProviderID inside node spec
//
// Sample node custom resource of an GKE node
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": ""
// }
// }
// }
//
// {
// "apiVersion": "v1",
// "kind": "Node",
// ....
// "spec": {
// "providerID": ""
// }
// }
// }
func IsGKEPlatform(kubeconfig *string) (bool, error) {
clientset, err := k8s.ClientSet(kubeconfig)
if err != nil {
@ -129,16 +131,17 @@ func IsGKEPlatform(kubeconfig *string) (bool, error) {
// label on the nodes
//
// Sample node custom resource of an Openshift node
// {
// "apiVersion": "v1",
// "kind": "Node",
// "metadata": {
// "labels": {
// "node.openshift.io/os_id": "rhcos"
// }
// }
// ....
// }
//
// {
// "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 {

View File

@ -5,7 +5,7 @@ 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,

View File

@ -5,7 +5,7 @@ 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,
@ -18,23 +18,31 @@ package k8s
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"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"
)
@ -156,7 +164,7 @@ start:
}
if ok {
if podExists(podExistsParams{namespace, label}, kubeconfig) {
utils.Red.Println("\n🚫 There is a Chaos Delegate already present in this namespace. Please enter a different namespace")
utils.Red.Println("\n🚫 There is a Chaos Infra already present in this namespace. Please enter a different namespace")
goto start
} else {
nsExists = true
@ -195,9 +203,9 @@ func WatchPod(params WatchPodParams, kubeconfig *string) {
if !ok {
log.Fatal("unexpected type")
}
utils.White_B.Println("💡 Connecting Chaos Delegate to ChaosCenter.")
utils.White_B.Println("💡 Connecting Chaos Infra to ChaosCenter.")
if p.Status.Phase == "Running" {
utils.White_B.Println("🏃 Chaos Delegate is running!!")
utils.White_B.Println("🏃 Chaos Infra is running!!")
watch.Stop()
break
}
@ -271,66 +279,129 @@ func ValidSA(namespace string, kubeconfig *string) (string, bool) {
return sa, false
}
// Token: Authorization token
// EndPoint: Endpoint in .litmusconfig
// YamlPath: Path of yaml file
type ApplyYamlPrams struct {
Token string
Endpoint string
YamlPath string
}
// ApplyManifest applies the provided manifest and kubeconfig with the help of client-go library.
func ApplyManifest(manifest []byte, kubeconfig string) (string, error) {
func ApplyYaml(params ApplyYamlPrams, kubeconfig string, isLocal bool) (output string, err error) {
path := params.YamlPath
if !isLocal {
path = fmt.Sprintf("%s/%s/%s.yaml", params.Endpoint, params.YamlPath, params.Token)
req, err := http.NewRequest("GET", path, nil)
if err != nil {
return "", err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
err = ioutil.WriteFile("chaos-delegate-manifest.yaml", resp_body, 0644)
if err != nil {
return "", err
}
path = "chaos-delegate-manifest.yaml"
}
args := []string{"kubectl", "apply", "-f", path}
if kubeconfig != "" {
args = append(args, []string{"--kubeconfig", kubeconfig}...)
} else {
args = []string{"kubectl", "apply", "-f", path}
}
cmd := exec.Command(args[0], args[1:]...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err = cmd.Run()
outStr, errStr := stdout.String(), stderr.String()
// err, can have exit status 1
// Get Kubernetes and dynamic clients along with the configuration.
_, kubeClient, dynamicClient, err := getClientAndConfig(kubeconfig)
if err != nil {
// if we get standard error then, return the same
if errStr != "" {
return "", fmt.Errorf(errStr)
}
// if not standard error found, return error
return "", err
}
// If no error found, return standard output
return outStr, nil
// 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

View File

@ -5,7 +5,7 @@ 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,
@ -23,19 +23,27 @@ const (
)
type AuthResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
AccessToken string `json:"accessToken"`
ExpiresIn int64 `json:"expiresIn"`
Type string `json:"type"`
}
type AuthInput struct {
Endpoint string
Username string
Password string
Endpoint string
Username string
Password string
ServerEndpoint string
}
type Credentials struct {
Username string
Token string
Endpoint string
Username string
Token string
Endpoint string
ServerEndpoint string
}
type UpdatePasswordInput struct {
Username string `json:"username,omitempty"`
OldPassword string
NewPassword string
}

View File

@ -5,7 +5,7 @@ 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,
@ -22,8 +22,9 @@ type User struct {
}
type Account struct {
Users []User `yaml:"users" json:"users"`
Endpoint string `yaml:"endpoint" json:"endpoint"`
Users []User `yaml:"users" json:"users"`
Endpoint string `yaml:"endpoint" json:"endpoint"`
ServerEndpoint string `yaml:"serverEndpoint" json:"serverEndpoint"`
}
type LitmuCtlConfig struct {
@ -43,4 +44,5 @@ type UpdateLitmusCtlConfig struct {
CurrentAccount string `yaml:"current-account" json:"current-account"`
CurrentUser string `yaml:"current-user" json:"current-user"`
Account Account `yaml:"account" json:"account"`
ServerEndpoint string `yaml:"serverEndpoint" json:"serverEndpoint"`
}

View File

@ -5,7 +5,7 @@ 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,13 +15,14 @@ limitations under the License.
*/
package types
type Agent struct {
AgentName string `json:"clusterName"`
type Infra struct {
InfraName string `json:"infraName"`
Mode string
Description string `json:"description,omitempty"`
PlatformName string `json:"platformName"`
EnvironmentID string `json:"environmentID"`
ProjectId string `json:"projectID"`
ClusterType string `json:"clusterType"`
InfraType string `json:"infraType"`
NodeSelector string `json:"nodeSelector"`
Tolerations string
Namespace string

View File

@ -5,7 +5,7 @@ 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,
@ -24,6 +24,7 @@ import (
"fmt"
"math/big"
"os"
"regexp"
"strings"
"github.com/fatih/color"
@ -82,8 +83,10 @@ func GetCredentials(cmd *cobra.Command) (types.Credentials, error) {
}
var token string
var serverEndpoint string
for _, account := range obj.Accounts {
if account.Endpoint == obj.CurrentAccount {
serverEndpoint = account.ServerEndpoint
for _, user := range account.Users {
if user.Username == obj.CurrentUser {
token = user.Token
@ -93,9 +96,10 @@ func GetCredentials(cmd *cobra.Command) (types.Credentials, error) {
}
return types.Credentials{
Username: obj.CurrentUser,
Token: token,
Endpoint: obj.CurrentAccount,
Username: obj.CurrentUser,
Token: token,
Endpoint: obj.CurrentAccount,
ServerEndpoint: serverEndpoint,
}, nil
}
@ -119,6 +123,9 @@ func PrintInYamlFormat(inf interface{}) {
}
func GenerateRandomString(n int) (string, error) {
if n <= 0 {
return "", fmt.Errorf("length should not be negative")
}
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
ret := make([]byte, n)
for i := 0; i < n; i++ {
@ -149,3 +156,17 @@ func CheckKeyValueFormat(str string) bool {
}
return true
}
func GenerateNameID(in string) string {
// Replace spaces and special characters with underscore
reg := regexp.MustCompile(`[^a-zA-Z0-9]+`)
replaced := reg.ReplaceAllString(in, "_")
// Remove hyphens
noHyphens := strings.ReplaceAll(replaced, "-", "")
// Convert everything to lowercase
nameID := strings.ToLower(noHyphens)
return nameID
}

View File

@ -19,5 +19,25 @@ var (
"0.19.0": {"2.9.0", "2.10.0", "2.11.0", "2.12.0", "2.13.0", "2.14.0", "3.0-beta1", "3.0.0-beta2", "3.0.0-beta3", "3.0.0-beta4", "3.0.0-beta5", "3.0.0-beta6", "3.0.0-beta7", "3.0.0-beta8"},
"0.20.0": {"2.9.0", "2.10.0", "2.11.0", "2.12.0", "2.13.0", "2.14.0", "3.0-beta1", "3.0.0-beta2", "3.0.0-beta3", "3.0.0-beta4", "3.0.0-beta5", "3.0.0-beta6", "3.0.0-beta7", "3.0.0-beta8"},
"0.21.0": {"2.9.0", "2.10.0", "2.11.0", "2.12.0", "2.13.0", "2.14.0", "3.0-beta1", "3.0.0-beta2", "3.0.0-beta3", "3.0.0-beta4", "3.0.0-beta5", "3.0.0-beta6", "3.0.0-beta7", "3.0.0-beta8"},
"0.22.0": {"2.9.0", "2.10.0", "2.11.0", "2.12.0", "2.13.0", "2.14.0", "3.0-beta1", "3.0.0-beta2", "3.0.0-beta3", "3.0.0-beta4", "3.0.0-beta5", "3.0.0-beta6", "3.0.0-beta7", "3.0.0-beta8"},
"0.23.0": {"3.0.0-beta9", "3.0.0-beta10", "3.0.0-beta11", "3.0.0-beta12"},
"0.24.0": {"3.0.0-beta9", "3.0.0-beta10", "3.0.0-beta11", "3.0.0-beta12"},
"1.0.0": {"3.0.0", "3.1.0"},
"1.1.0": {"3.0.0", "3.1.0", "3.2.0"},
"1.2.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0"},
"1.3.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0"},
"1.4.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0"},
"1.5.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0"},
"1.6.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0"},
"1.7.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0"},
"1.8.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1"},
"1.9.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0"},
"1.10.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0"},
"1.11.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0"},
"1.12.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0", "3.13.0"},
"1.13.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0", "3.13.0", "3.14.0"},
"1.14.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0", "3.13.0", "3.14.0", "3.15.0"},
"1.15.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0", "3.13.0", "3.14.0", "3.15.0", "3.16.0", "3.17.0", "3.18.0"},
"1.16.0": {"3.0.0", "3.1.0", "3.2.0", "3.3.0", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "3.8.0", "3.9.0", "3.9.1", "3.10.0", "3.11.0", "3.12.0", "3.13.0", "3.14.0", "3.15.0", "3.16.0", "3.17.0", "3.18.0", "3.19.0"},
}
)

View File

@ -5,7 +5,7 @@ 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,
@ -39,22 +39,28 @@ const (
// Default platform name
DefaultPlatform = "Others"
// Label of subscriber agent being deployed
ChaosAgentLabel = "app=subscriber"
// Label of subscriber infra being deployed
ChaosInfraLabel = "app=subscriber"
// Agent type is "external" for agents connected via litmusctl
AgentType = "external"
// infra type is "external" for infras connected via litmusctl
InfraTypeKubernetes = "Kubernetes"
// Default namespace for agent installation
// member owner role
MemberOwnerRole = "Owner"
// member editor role
MemberEditorRole = "Editor"
// Default namespace for infra installation
DefaultNs = "litmus"
// Default service account used for agent installation
// Default service account used for infra installation
DefaultSA = "litmus"
// Chaos agent connection yaml path
ChaosYamlPath = "api/file"
// Chaos infra connection yaml path
ChaosYamlPath = "/api/file"
ChaosAgentPath = "targets"
ChaosInfraPath = "targets"
// Graphql server api path
GQLAPIPath = "/api/query"

View File

@ -5,7 +5,7 @@ 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,
@ -19,9 +19,9 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/url"
"os"
"regexp"
"strconv"
"strings"
@ -29,14 +29,14 @@ import (
"github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
chaosTypes "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1"
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"sigs.k8s.io/yaml"
)
// ParseWorkflowManifest reads the manifest that is passed as an argument and
// populates the payload for the CreateChaosWorkflow API request. The manifest
// ParseExperimentManifest reads the manifest that is passed as an argument and
// populates the payload for the Message API request. The manifest
// can be either a local file or a remote file.
func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlowRequest) error {
func ParseExperimentManifest(file string, chaosWorkFlowRequest *model.SaveChaosExperimentRequest) error {
var body []byte
var err error
@ -44,7 +44,7 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
// Read the manifest file.
parsedURL, ok := url.ParseRequestURI(file)
if ok != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
body, err = ioutil.ReadFile(file)
body, err = os.ReadFile(file)
} else {
body, err = ReadRemoteFile(file)
}
@ -66,13 +66,13 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
}
if len(workflow.ObjectMeta.Name) > 0 {
chaosWorkFlowRequest.WorkflowName = workflow.ObjectMeta.Name
chaosWorkFlowRequest.Name = workflow.ObjectMeta.Name
} else if len(workflow.ObjectMeta.GenerateName) > 0 {
workflow.ObjectMeta.Name = workflow.ObjectMeta.GenerateName + generateRandomString()
workflow.ObjectMeta.GenerateName = "TOBEDELETED"
chaosWorkFlowRequest.WorkflowName = workflow.ObjectMeta.Name
chaosWorkFlowRequest.Name = workflow.ObjectMeta.Name
} else {
return errors.New("No name or generateName provided for the Chaos scenario.")
return errors.New("No name or generateName provided for the Chaos experiment.")
}
// Marshal the workflow back to JSON for API payload.
@ -81,8 +81,8 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
return ok
}
chaosWorkFlowRequest.WorkflowManifest = strings.Replace(string(workflowStr), "\"generateName\":\"TOBEDELETED\",", "", 1)
chaosWorkFlowRequest.IsCustomWorkflow = true
chaosWorkFlowRequest.Manifest = strings.Replace(string(workflowStr), "\"generateName\":\"TOBEDELETED\",", "", 1)
//chaosWorkFlowRequest.IsCustomWorkflow = true
// Fetch the weightages for experiments present in the spec.
err = FetchWeightages(chaosWorkFlowRequest, workflow.Spec.Templates)
@ -97,16 +97,16 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
return err
}
chaosWorkFlowRequest.WorkflowName = cronWorkflow.ObjectMeta.Name
chaosWorkFlowRequest.Name = cronWorkflow.ObjectMeta.Name
if len(cronWorkflow.ObjectMeta.Name) > 0 {
chaosWorkFlowRequest.WorkflowName = cronWorkflow.ObjectMeta.Name
chaosWorkFlowRequest.Name = cronWorkflow.ObjectMeta.Name
} else if len(cronWorkflow.ObjectMeta.GenerateName) > 0 {
cronWorkflow.ObjectMeta.Name = cronWorkflow.ObjectMeta.GenerateName + generateRandomString()
cronWorkflow.ObjectMeta.GenerateName = "TOBEDELETED"
chaosWorkFlowRequest.WorkflowName = cronWorkflow.ObjectMeta.Name
chaosWorkFlowRequest.Name = cronWorkflow.ObjectMeta.Name
} else {
return errors.New("No name or generateName provided for the Chaos scenario.")
return errors.New("No name or generateName provided for the Chaos experiment.")
}
// Marshal the workflow back to JSON for API payload.
@ -115,11 +115,11 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
return ok
}
chaosWorkFlowRequest.WorkflowManifest = strings.Replace(string(workflowStr), "\"generateName\":\"TOBEDELETED\",", "", 1)
chaosWorkFlowRequest.IsCustomWorkflow = true
chaosWorkFlowRequest.Manifest = strings.Replace(string(workflowStr), "\"generateName\":\"TOBEDELETED\",", "", 1)
//chaosWorkFlowRequest.IsCustomWorkflow = true
// Set the schedule for the workflow
chaosWorkFlowRequest.CronSyntax = cronWorkflow.Spec.Schedule
//chaosWorkFlowRequest.CronSyntax = cronWorkflow.Spec.Schedule
// Fetch the weightages for experiments present in the spec.
err = FetchWeightages(chaosWorkFlowRequest, cronWorkflow.Spec.WorkflowSpec.Templates)
@ -133,23 +133,14 @@ func ParseWorkflowManifest(file string, chaosWorkFlowRequest *model.ChaosWorkFlo
return nil
}
// Helper function to check the presence of a string in a slice
func sliceContains(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
// Helper function to generate a random 8 char string - used for workflow name postfix
func generateRandomString() string {
rand.Seed(time.Now().UnixNano())
source := rand.NewSource(time.Now().UnixNano())
randomGenerator := rand.New(source)
var letters = []rune("abcdefghijklmnopqrstuvxyz0123456789")
b := make([]rune, 5)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
b[i] = letters[randomGenerator.Intn(len(letters))]
}
return string(b)
}
@ -157,7 +148,7 @@ func generateRandomString() string {
// FetchWeightages takes in the templates present in the workflow spec and
// assigns weightage to each of the experiments present in them. It can parse
// both artifacts and remote experiment specs.
func FetchWeightages(chaosWorkFlowRequest *model.ChaosWorkFlowRequest, templates []v1alpha1.Template) error {
func FetchWeightages(chaosWorkFlowRequest *model.SaveChaosExperimentRequest, templates []v1alpha1.Template) error {
for _, t := range templates {
@ -186,41 +177,41 @@ func FetchWeightages(chaosWorkFlowRequest *model.ChaosWorkFlowRequest, templates
if strings.ToLower(chaosEngine.Kind) == "chaosengine" {
var weightageInput model.WeightagesInput
weightageInput.ExperimentName = chaosEngine.ObjectMeta.GenerateName
weightageInput.FaultName = chaosEngine.ObjectMeta.GenerateName
if len(weightageInput.ExperimentName) == 0 {
if len(weightageInput.FaultName) == 0 {
return errors.New("empty chaos experiment name")
}
if len(chaosEngine.Spec.Experiments) == 0 {
return errors.New("no experiments specified in chaosengine - " + weightageInput.ExperimentName)
return errors.New("no experiments specified in chaosengine - " + weightageInput.FaultName)
}
w, ok := t.Metadata.Labels["weight"]
if !ok {
White.Println("Weightage for ChaosExperiment/" + weightageInput.ExperimentName + " not provided, defaulting to 10.")
White.Println("Weightage for ChaosFault/" + weightageInput.FaultName + " not provided, defaulting to 10.")
w = "10"
}
weightageInput.Weightage, err = strconv.Atoi(w)
if err != nil {
return errors.New("Invalid weightage for ChaosExperiment/" + weightageInput.ExperimentName + ".")
return errors.New("Invalid weightage for ChaosExperiment/" + weightageInput.FaultName + ".")
}
chaosWorkFlowRequest.Weightages = append(chaosWorkFlowRequest.Weightages, &weightageInput)
//chaosWorkFlowRequest. = append(chaosWorkFlowRequest.Weightages, &weightageInput)
}
}
}
}
// If no experiments are present in the workflow, adds a 0 to the Weightages array so it doesn't fail (same behavior as the UI)
if len(chaosWorkFlowRequest.Weightages) == 0 {
White.Println("No experiments found in the chaos scenario, defaulting experiments weightage to 0.")
var weightageInput model.WeightagesInput
weightageInput.ExperimentName = ""
weightageInput.Weightage = 0
chaosWorkFlowRequest.Weightages = append(chaosWorkFlowRequest.Weightages, &weightageInput)
}
//if len(chaosWorkFlowRequest.Weightages) == 0 {
// White.Println("No experiments found in the chaos scenario, defaulting experiments weightage to 0.")
// var weightageInput model.WeightagesInput
// weightageInput.ExperimentName = ""
// weightageInput.Weightage = 0
// chaosWorkFlowRequest.Weightages = append(chaosWorkFlowRequest.Weightages, &weightageInput)
//}
return nil
}

View File

@ -5,7 +5,7 @@ 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,
@ -17,7 +17,7 @@ package utils
import (
"encoding/json"
"io/ioutil"
"io"
"net/http"
"sigs.k8s.io/yaml"
@ -45,7 +45,7 @@ func ReadRemoteFile(url string) ([]byte, error) {
resp, err := http.Get(url)
var body []byte
if err == nil {
body, err = ioutil.ReadAll(resp.Body)
body, err = io.ReadAll(resp.Body)
resp.Body.Close()
}
return body, err

View File

@ -0,0 +1,2 @@
go test fuzz v1
int(-82)

View File

@ -0,0 +1,41 @@
package utils
import (
"strings"
"testing"
)
func isValidString(s string) bool {
// Define the set of valid characters
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"
// Iterate over each character in the string
for _, char := range s {
// Check if the character is not in the set of valid characters
if !strings.ContainsRune(validChars, char) {
return false
}
}
return true
}
func FuzzGenerateRandomString(f *testing.F) {
f.Add(10)
f.Fuzz(func(t *testing.T, n int) {
randomString, err := GenerateRandomString(n)
if err != nil && n > 0 {
t.Errorf("Error generating random string: %v", err)
}
// Perform checks on the generated string
// Check if the length matches the expected length
if n >= 0 && len(randomString) != n {
t.Errorf("Generated string length doesn't match expected length")
}
// Check if the string contains only valid characters
if !isValidString(randomString) {
t.Errorf("Generated string contains invalid characters")
}
})
}