Compare commits
63 Commits
Author | SHA1 | Date |
---|---|---|
|
9f36304f0d | |
|
3ce353dcfc | |
|
9c4c3351ac | |
|
094d1ec037 | |
|
b1dba98cfb | |
|
f06f4f9d52 | |
|
734ea7dcb3 | |
|
902cab3da2 | |
|
1780d64f2f | |
|
32f32320d6 | |
|
ff915a9b2e | |
|
0980230d37 | |
|
4f498493a6 | |
|
5b4f3cd6c8 | |
|
327ffaa710 | |
|
86377b15a1 | |
|
ca5e75b3df | |
|
a422a8a7a5 | |
|
fd531ae0e0 | |
|
06bfd1a21c | |
|
f8426a7006 | |
|
c34fbbc134 | |
|
34a2702e07 | |
|
d5b1d839cd | |
|
a6b5157f7e | |
|
5d8e1be588 | |
|
d304f7d7a4 | |
|
025938a208 | |
|
2ff8c1fa5d | |
|
47ba363e58 | |
|
6b276da3af | |
|
1a69cc5d24 | |
|
6c628850a7 | |
|
49ebc8b936 | |
|
95fad15b01 | |
|
9fe2a37e07 | |
|
05634fb60d | |
|
2c252f845b | |
|
8a139b00fb | |
|
8bfa459dde | |
|
ec74df7d75 | |
|
a6c26e09a8 | |
|
be72d8eeaf | |
|
e44abf3387 | |
|
bed5b0c775 | |
|
3bac6d7aae | |
|
5a7722d618 | |
|
9794b0b391 | |
|
cf8c33a62b | |
|
486349b494 | |
|
19b2c5bd9d | |
|
29997566fc | |
|
6b68c4d1ca | |
|
28f474437d | |
|
9b468e1f08 | |
|
49fc5b0ec7 | |
|
2a448d4f5a | |
|
30cb3ca9c5 | |
|
34a718c21b | |
|
77e53623bf | |
|
c3db8e7002 | |
|
374d168123 | |
|
c0bf38685e |
|
@ -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)
|
|
@ -0,0 +1,6 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -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
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -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 }}
|
|
@ -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)
|
|
@ -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
335
README.md
|
@ -1,4 +1,5 @@
|
|||
# Litmusctl
|
||||
|
||||
[](https://bettercodehub.com/)
|
||||

|
||||
[](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).
|
||||
|
||||
---
|
||||
|
|
|
@ -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`.
|
|
@ -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
92
go.mod
|
@ -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
|
||||
|
|
2
main.go
2
main.go
|
@ -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,
|
||||
|
|
288
pkg/agent/ops.go
288
pkg/agent/ops.go
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}`
|
||||
)
|
|
@ -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"`
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}`
|
||||
)
|
|
@ -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"`
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}`
|
||||
)
|
|
@ -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"`
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
`
|
||||
)
|
|
@ -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"`
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
|
|
|
@ -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.")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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.")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
go test fuzz v1
|
||||
int(-82)
|
|
@ -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")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
Loading…
Reference in New Issue