Compare commits
161 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 | |
|
ed34eb5209 | |
|
75bf6863ae | |
|
98edb045c0 | |
|
d471d1866c | |
|
12ec3977a6 | |
|
7d43952a5a | |
|
2668f59ab6 | |
|
83345b696a | |
|
ef4f78a864 | |
|
8b99793189 | |
|
5ae06c1fc6 | |
|
e59872117d | |
|
cc8c5cec25 | |
|
eaf8030d76 | |
|
830949e454 | |
|
9535949dad | |
|
61c2b740c4 | |
|
16b4c617df | |
|
acacbde5b3 | |
|
5cd6548eb4 | |
|
573b812f7a | |
|
0ca98c0aef | |
|
6c8aea4344 | |
|
dd2a89e6d5 | |
|
36ec9cbc82 | |
|
3e40de731e | |
|
f44e8840ea | |
|
7f837acb80 | |
|
e6ad58e849 | |
|
8cfd8de579 | |
|
72bc309f57 | |
|
3f5ac08325 | |
|
35afa81773 | |
|
92277b257d | |
|
19bf342353 | |
|
c0e2912f67 | |
|
5bb28a4cc2 | |
|
b4f7fe9cf8 | |
|
eb54bb66ec | |
|
8646502b22 | |
|
a683816084 | |
|
1dd51bb4ba | |
|
14a7207bb1 | |
|
5e8f261a86 | |
|
7ea4b77753 | |
|
bc205aa96f | |
|
5681620af9 | |
|
ee5ae8c214 | |
|
a3f1a94afc | |
|
49f75e33fd | |
|
e38cf5a5fc | |
|
7ec6f3f867 | |
|
a338254204 | |
|
2fcc35ec5d | |
|
a0b9618f68 | |
|
5a6ca23872 | |
|
f06886fa8b | |
|
60483b0d58 | |
|
587c6439b3 | |
|
f7d946ab41 | |
|
7d77349d7e | |
|
5eff84f8bf | |
|
a130b15801 | |
|
4bdc1f0db3 | |
|
b7ceb417fa | |
|
376e068058 | |
|
2f2e12e95d | |
|
3d5cf51b9c | |
|
c0c8460fb7 | |
|
db93a17783 | |
|
bdfd0e048e | |
|
69bc32a9e5 | |
|
c3d03f7683 | |
|
bebdd14f4b | |
|
db39e0958f | |
|
9018663a6e | |
|
fe00c067d7 | |
|
e515dc4fff | |
|
ffba121903 | |
|
fbd7960119 | |
|
9add806abc | |
|
5c6a334d85 | |
|
ea4c62c3e7 | |
|
a58bc763a6 | |
|
887406a2e2 | |
|
9e98b273a8 | |
|
72546de78f | |
|
92c04195a0 | |
|
df171479ac | |
|
bcf1791705 | |
|
67053298d9 | |
|
ea0ee8e30b | |
|
de2d07fbaa | |
|
46c8e00150 | |
|
0ee4b998ef | |
|
4c136b252f | |
|
7e7e8ddd6b | |
|
dc43fa9aea |
|
@ -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,5 @@
|
|||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
* @rajdas98 @arkajyotiMukherjee @Jonsy13 @Saranya-jena @SarthakJain26
|
|
@ -0,0 +1,6 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -1,11 +1,8 @@
|
|||
name: Build
|
||||
name: build-pipeline
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -14,7 +11,7 @@ jobs:
|
|||
# Install golang
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
go-version: 1.20.5
|
||||
|
||||
# Checkout to the latest commit
|
||||
# On specific directory/path
|
||||
|
@ -22,6 +19,8 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
|
||||
- name: gofmt check
|
||||
env:
|
||||
COMMIT_HOOKS: pre-commit
|
||||
run: |
|
||||
if [ "$(gofmt -s -l . | wc -l)" -ne 0 ]
|
||||
then
|
|
@ -0,0 +1,71 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '44 2 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
|
@ -1,13 +1,10 @@
|
|||
name: Push-or-Release
|
||||
name: push-release-pipeline
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags-ignore:
|
||||
- '**'
|
||||
create:
|
||||
tags:
|
||||
- '**'
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
|
@ -16,7 +13,7 @@ jobs:
|
|||
# Install golang
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
go-version: 1.20.5
|
||||
|
||||
# Checkout to the latest commit
|
||||
# On specific directory/path
|
||||
|
@ -27,9 +24,9 @@ jobs:
|
|||
run: |
|
||||
if [ "$(gofmt -s -l . | wc -l)" -ne 0 ]
|
||||
then
|
||||
echo "The following files were found to be not go formatted:"
|
||||
gofmt -s -l .
|
||||
exit 1
|
||||
echo "The following files were found to be not go formatted:"
|
||||
gofmt -s -l .
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: golangci-lint
|
||||
|
@ -39,23 +36,23 @@ jobs:
|
|||
run: |
|
||||
make unused-package-check
|
||||
|
||||
- name: Get tag
|
||||
shell: bash
|
||||
run: echo "branch=$(echo ${GITHUB_REF##*/})" >> $GITHUB_OUTPUT
|
||||
id: tag
|
||||
|
||||
- name: Building litmusctl
|
||||
run: |
|
||||
git checkout ${GITHUB_REF##*/}
|
||||
make build
|
||||
make TAG=${{ steps.tag.outputs.branch }} build
|
||||
|
||||
- name: Push litmusctl to repo
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
|
||||
- name: Copy binaries to the litmusctl s3 bucket
|
||||
run: |
|
||||
git config --global user.name 'litmuschaos-bot'
|
||||
git config --global user.email 'litmuschaos-bot@users.noreply.github.com'
|
||||
git remote -v
|
||||
if [[ `git status --porcelain` ]]; then
|
||||
echo "changes"
|
||||
git add .
|
||||
git commit -s -m "Updating litmusctl"
|
||||
git push
|
||||
exit 0;
|
||||
else
|
||||
echo "no changes"
|
||||
exit 0;
|
||||
fi
|
||||
aws s3 sync platforms-${{ steps.tag.outputs.branch }} s3://${{ secrets.AWS_S3_BUCKET }}
|
||||
|
|
|
@ -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 }}
|
|
@ -1,11 +1,22 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
main
|
||||
litmusctl
|
||||
agent-manifest.yaml
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
vendor/
|
||||
platforms*
|
||||
|
||||
#IDE
|
||||
.idea
|
||||
main
|
|
@ -1,25 +0,0 @@
|
|||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- env:
|
||||
- GO111MODULE=on
|
||||
- CGO_ENABLED=0
|
||||
archives:
|
||||
- name_template: '{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
|
@ -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! 🚀🔥
|
1
LICENSE
1
LICENSE
|
@ -1,3 +1,4 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
|
6
Makefile
6
Makefile
|
@ -1,11 +1,7 @@
|
|||
PROJECT_NAME := "litmusctl"
|
||||
PKG := "github.com/litmuschaos/$(PROJECT_NAME)"
|
||||
|
||||
all: build
|
||||
|
||||
build: ## Build the binary file
|
||||
@bash scripts/build.sh main.go
|
||||
|
||||
@bash scripts/build.sh main.go $(TAG)
|
||||
|
||||
.PHONY: unused-package-check
|
||||
unused-package-check:
|
||||
|
|
357
README.md
357
README.md
|
@ -1,154 +1,279 @@
|
|||
# Litmusctl
|
||||
|
||||
Litmusctl is a command line interface to manage LitmusPortal services.
|
||||
[](https://bettercodehub.com/)
|
||||

|
||||
[](https://github.com/litmuschaos/litmusctl/stargazers)
|
||||
[]()
|
||||
|
||||
The Litmuschaos command-line tool, litmusctl, allows you to manage litmuschaos's agent plane. You can use litmusctl to connect Chaos Delegates, create project, schedule Chaos Scenarios, disconnect Chaos Delegates and manage multiple litmuschaos accounts.
|
||||
|
||||
## Usage
|
||||
|
||||
For more information including a complete list of litmusctl operations, see the litmusctl reference documentation.
|
||||
|
||||
* For 0.23.0 or latest: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_0.23.0.md">Click here</a>
|
||||
* For v0.12.0 to v0.22.0: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_interactive.md">Click here</a>
|
||||
* For v0.2.0 or earlier && compatible with Litmus-2.0.0-Beta8 or earlier: <a href="https://github.com/litmuschaos/litmusctl/blob/master/Usage_v0.2.0.md">Click here</a>
|
||||
|
||||
## Requirements
|
||||
|
||||
The litmusctl CLI requires the following things:
|
||||
|
||||
- Kubeconfig - litmusctl needs the kubeconfig of the k8s cluster where we need to connect litmus agents. The CLI currently uses the default path of kubeconfig i.e. `~/.kube/config`.
|
||||
- kubeconfig - litmusctl needs the kubeconfig of the k8s cluster where we need to connect litmus Chaos Delegates. The CLI currently uses the default path of kubeconfig i.e. `~/.kube/config`.
|
||||
- kubectl- litmusctl is using kubectl under the hood to apply the manifest. To install kubectl, follow: [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
|
||||
|
||||
## Compatibility matrix
|
||||
|
||||
To check compatibility of litmusctl with Chaos Center
|
||||
|
||||
<table>
|
||||
<th>litmusctl version</th>
|
||||
<th>Lowest Chaos Center supported version</th>
|
||||
<th>Highest Chaos Center supported version</th>
|
||||
<tr>
|
||||
<td>1.16.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.19.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.15.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.18.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.14.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.15.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.13.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.14.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.12.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.13.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.11.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.12.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.10.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.11.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.9.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.10.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.8.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.9.1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.7.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.8.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.6.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.7.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.5.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.6.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.4.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.5.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.3.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.4.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.2.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.3.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.1.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.2.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1.0.0</td>
|
||||
<td>3.0.0</td>
|
||||
<td>3.1.0</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Installation
|
||||
|
||||
**Linux**
|
||||
|
||||
To install the latest version of litmusctl follow the below steps:
|
||||
|
||||
- Download the latest litmusctl binary from -
|
||||
<table>
|
||||
<th>Platforms</th>
|
||||
<th>1.16.0</th>
|
||||
<th>1.15.0</th>
|
||||
<th>1.14.0</th>
|
||||
<th>1.13.0</th>
|
||||
<th>1.12.0</th>
|
||||
<th>1.11.0</th>
|
||||
<th>1.10.0</th>
|
||||
<th>1.9.0</th>
|
||||
<th>master(Unreleased)</th>
|
||||
<tr>
|
||||
<td>litmusctl-darwin-amd64 (MacOS)</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-darwin-amd64-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-linux-386</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-386-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-linux-amd64</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-amd64-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-linux-arm</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-linux-arm64</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-linux-arm64-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-windows-386</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-386-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-windows-amd64</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-amd64-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>litmusctl-windows-arm</td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.16.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.15.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.14.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.13.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.12.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.11.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.10.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-1.9.0.tar.gz">Click here</a></td>
|
||||
<td><a href="https://litmusctl-production-bucket.s3.amazonaws.com/litmusctl-windows-arm-master.tar.gz">Click here</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
| Platforms | Download Link |
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------|
|
||||
| litmusctl-linux-amd64 | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-amd64?raw=true) |
|
||||
| litmusctl-linux-arm | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-arm?raw=true) |
|
||||
| litmusctl-linux-arm64 | [Click here](https://github.com/litmuschaos/litmusctl/blob/master/platforms/litmusctl-linux-arm64?raw=true) |
|
||||
### Linux/MacOS
|
||||
|
||||
<br>
|
||||
- Extract the binary
|
||||
|
||||
```shell
|
||||
tar -zxvf litmusctl-<OS>-<ARCH>-<VERSION>.tar.gz
|
||||
```
|
||||
|
||||
- Provide necessary permissions
|
||||
|
||||
```shell
|
||||
$ chmod +x <filename>
|
||||
chmod +x litmusctl
|
||||
```
|
||||
|
||||
- Move the litmusctl binary to /usr/local/bin/litmusctl
|
||||
- Move the litmusctl binary to /usr/local/bin/litmusctl. Note: Make sure to use root user or use sudo as a prefix
|
||||
|
||||
```shell
|
||||
$ sudo mv <filename> /usr/local/bin/litmusctl
|
||||
mv litmusctl /usr/local/bin/litmusctl
|
||||
```
|
||||
|
||||
## Basic Commands
|
||||
|
||||
litmusctl CLI command has the following structure:
|
||||
- You can run the litmusctl command in Linux/macOS:
|
||||
|
||||
```shell
|
||||
$ litmusctl <command> <subcommand> <subcommand> [options and parameters]
|
||||
litmusctl <command> <subcommand> <subcommand> [options and parameters]
|
||||
```
|
||||
|
||||
To get the version of the litmusctl CLI:
|
||||
### Windows
|
||||
|
||||
- Extract the binary from the zip using WinZip or any other extraction tool.
|
||||
|
||||
- You can run the litmusctl command in windows:
|
||||
|
||||
```shell
|
||||
$ litmusctl version
|
||||
litmusctl.exe <command> <subcommand> <subcommand> [options and parameters]
|
||||
```
|
||||
|
||||
### Registering an agent
|
||||
To register Litmus Chaos agent:
|
||||
- To check the version of the litmusctl:
|
||||
|
||||
```shell
|
||||
$ litmusctl agent register
|
||||
litmusctl version
|
||||
```
|
||||
|
||||
Next, you need to enter LitmusPortal details to login into your LitmusPortal account. Fields to be filled in:
|
||||
## Development Guide
|
||||
|
||||
**LimtusPortal UI URL:** Enter the URL used to access the Litmus Portal UI.
|
||||
Example, http://172.17.0.2:31696/
|
||||
You can find the local setup guide for **`litmusctl`** [here](DEVELOPMENT.md).
|
||||
|
||||
**Username:** Enter your LitmusPortal username.
|
||||
**Password:** Enter your LitmusPortal password.
|
||||
|
||||
```shell
|
||||
🔥 Registering LitmusChaos agent
|
||||
|
||||
📶 Please enter LitmusChaos details --
|
||||
👉 Host URL where litmus is installed: http://172.17.0.2:31696/
|
||||
🤔 Username [admin]: admin
|
||||
🙈 Password:
|
||||
✅ Login Successful!
|
||||
```
|
||||
|
||||
Upon successful login, there will be a list of exiting projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
|
||||
|
||||
```shell
|
||||
✨ Projects List:
|
||||
1. abc
|
||||
|
||||
🔎 Select Project: 1
|
||||
```
|
||||
|
||||
Next, select the installation mode. In case the selected mode was a Cluster there will be a prerequisites check to verify ClusterRole and ClusterRoleBinding.
|
||||
|
||||
```shell
|
||||
🔌 Installation Modes:
|
||||
1. Cluster
|
||||
2. Namespace
|
||||
|
||||
👉 Select Mode [cluster]: 1
|
||||
|
||||
🏃 Running prerequisites check....
|
||||
🔑 clusterrole - ✅
|
||||
🔑 clusterrolebinding - ✅
|
||||
|
||||
🌟 Sufficient permissions. Registering Agent
|
||||
```
|
||||
|
||||
Next, enter the details of the new agent.
|
||||
|
||||
Fields to filled in:
|
||||
**Agent Name:** Enter the name for the new agent.
|
||||
|
||||
**Agent Description:** Fill in details about the agent.
|
||||
|
||||
**Platform Name:** Enter the platform name on which this agent is hosted. For example, AWS, GCP, Rancher etc.
|
||||
|
||||
**Enter the namespace:** You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, LimtusPortal creates it for you.
|
||||
|
||||
**Enter service account:** Enter a name for your service account.
|
||||
|
||||
```shell
|
||||
🔗 Enter the details of the agent ----
|
||||
🤷 Agent Name: my-agent
|
||||
📘 Agent Description: This is a new agent.
|
||||
📦 Platform List
|
||||
1. AWS
|
||||
2. GKE
|
||||
3. Openshift
|
||||
4. Rancher
|
||||
5. Others
|
||||
🔎 Select Platform [Others]: 5
|
||||
📁 Enter the namespace (new or existing) [litmus]: litmus
|
||||
🔑 Enter service account [litmus]: litmus
|
||||
```
|
||||
|
||||
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
|
||||
After verification of these details, you can proceed with the registration of the agent by entering Y. The process of registration might take up to a few seconds.
|
||||
|
||||
```shell
|
||||
📌 Summary --------------------------
|
||||
|
||||
Agent Name: my-agent
|
||||
Agent Description: This is a new agent.
|
||||
Platform Name: Others
|
||||
Namespace: litmus
|
||||
Service Account: litmus
|
||||
Installation Mode: cluster
|
||||
|
||||
-------------------------------------
|
||||
|
||||
🤷 Do you want to continue with the above details? [Y/N]: Y
|
||||
|
||||
💡 Connecting agent to Litmus Portal.
|
||||
🏃 Agents running!!
|
||||
🚀 Agent Registration Successful!! 🎉
|
||||
👉 Litmus agents can be accessed here: http://172.17.0.2:31696/targets
|
||||
```
|
||||
|
||||
To verify, if the registration process was successful you can view the list of connected agents from the Targets section on your LitmusPortal and ensure that the connected agent is in Active State.
|
||||
---
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
### litmusctl Syntax
|
||||
`litmusctl` has a syntax to use as follows:
|
||||
|
||||
```shell
|
||||
litmusctl [command] [TYPE] [flags]
|
||||
```
|
||||
* Command: refers to what you do want to perform (connect, create, get and config)
|
||||
* Type: refers to the feature type you are performing a command against (chaos-delegate, project etc.)
|
||||
* Flags: It takes some additional information for resource operations. For example, `--installation-mode` allows you to specify an installation mode.
|
||||
|
||||
Litmusctl is using the `.litmusconfig` config file to manage multiple accounts
|
||||
1. If the --config flag is set, then only the given file is loaded. The flag may only be set once and no merging takes place.
|
||||
2. Otherwise, the ${HOME}/.litmusconfig file is used, and no merging takes place.
|
||||
|
||||
Litmusctl supports both interactive and non-interactive(flag based) modes.
|
||||
> Only `litmusctl connect chaos-delegate` command needs --non-interactive flag, other commands don't need this flag to be in non-interactive mode. If mandatory flags aren't passed, then litmusctl takes input in an interactive mode.
|
||||
|
||||
### Installation modes
|
||||
Litmusctl can install a Chaos Delegate in two different modes.
|
||||
* cluster mode: With this mode, the Chaos Delegate can run the chaos in any namespace. It installs appropriate cluster roles and cluster role bindings to achieve this mode. It can be enabled by passing a flag `--installation-mode=cluster`
|
||||
|
||||
* namespace mode: With this mode, the Chaos Delegate can run the chaos in its namespace. It installs appropriate roles and role bindings to achieve this mode. It can be enabled by passing a flag `--installation-mode=namespace`
|
||||
|
||||
Note: With namespace mode, the user needs to create the namespace to install the Chaos Delegate as a prerequisite.
|
||||
|
||||
### Minimal steps to connect a Chaos Delegate
|
||||
|
||||
* To setup an account with litmusctl
|
||||
```shell
|
||||
litmusctl config set-account --endpoint="" --username="" --password=""
|
||||
```
|
||||
|
||||
* To create an Chaos Delegate with an existing project
|
||||
> Note: To get `project-id`. Apply `litmusctl get projects`
|
||||
|
||||
```shell
|
||||
litmusctl connect chaos-delegate --name="" --project-id="" --non-interactive
|
||||
```
|
||||
|
||||
### Flags for `connect chaos-delegate` command
|
||||
<table>
|
||||
<tr>
|
||||
<th>Flag</th>
|
||||
<th>Short Flag</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<tr>
|
||||
<td>--description</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the Chaos Delegate description (default "---")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--name</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the name of Chaos Delegate which should be unique</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--skip-ssl</td>
|
||||
<td></td>
|
||||
<td>Boolean</td>
|
||||
<td>Set whether Chaos Delegate will skip ssl/tls check (can be used for self-signed certs, if cert is not provided in portal) (default false)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--chaos-delegate-type</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the chaos-delegate-type to external for external Chaos Delegates | Supported=external/internal (default "external")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--installation-mode</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the installation mode for the kind of Chaos Delegate | Supported=cluster/namespace (default "cluster")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--kubeconfig</td>
|
||||
<td>-k</td>
|
||||
<td>String</td>
|
||||
<td>Set to pass kubeconfig file if it is not in the default location ($HOME/.kube/config)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--namespace</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the namespace for the Chaos Delegate installation (default "litmus")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--node-selector</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the node-selector for Chaos Delegate components | Format: key1=value1,key2=value2)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--non-interactive</td>
|
||||
<td>-n</td>
|
||||
<td>String</td>
|
||||
<td>Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--ns-exists</td>
|
||||
<td></td>
|
||||
<td>Boolean</td>
|
||||
<td>Set the --ns-exists=false if the namespace mentioned in the --namespace flag is not existed else set it to --ns-exists=true | Note: Always set the boolean flag as --ns-exists=Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--platform-name</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the platform name. Supported- AWS/GKE/Openshift/Rancher/Others (default "Others")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--sa-exists</td>
|
||||
<td></td>
|
||||
<td>Boolean</td>
|
||||
<td>Set the --sa-exists=false if the service-account mentioned in the --service-account flag is not existed else set it to --sa-exists=true | Note: Always set the boolean flag as --sa-exists=Boolean"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--service-account</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>Set the service account to be used by the Chaos Delegate (default "litmus")</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--config</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>config file (default is $HOME/.litmusctl)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
---
|
||||
|
||||
### Steps to create a chaos scenaro
|
||||
|
||||
* To setup an account with litmusctl
|
||||
```shell
|
||||
litmusctl config set-account --endpoint="" --username="" --password=""
|
||||
```
|
||||
|
||||
* To create a Chaos Scenario by passing a manifest file
|
||||
> Note:
|
||||
> * To get `project-id`, apply `litmusctl get projects`
|
||||
> * To get `chaos-delegate-id`, apply `litmusctl get chaos-delegates --project-id=""`
|
||||
```shell
|
||||
litmusctl create chaos-scenario -f custom-chaos-scenario.yml --project-id="" --chaos-delegate-id=""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Additional commands
|
||||
|
||||
* To view the current configuration of `.litmusconfig`, type:
|
||||
```shell
|
||||
litmusctl config view
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```
|
||||
accounts:
|
||||
- users:
|
||||
- expires_in: "1626897027"
|
||||
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY4OTcwMjcsInJvbGUiOiJhZG1pbiIsInVpZCI6ImVlODZkYTljLTNmODAtNGRmMy04YzQyLTExNzlhODIzOTVhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.O_hFcIhxP4rhyUN9NEVlQmWesoWlpgHpPFL58VbJHnhvJllP5_MNPbrRMKyFvzW3hANgXK2u8437u
|
||||
username: admin
|
||||
- expires_in: "1626944602"
|
||||
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY5NDQ2MDIsInJvbGUiOiJ1c2VyIiwidWlkIjoiNjFmMDY4M2YtZWY0OC00MGE1LWIzMjgtZTU2ZDA2NjM1MTE4IiwidXNlcm5hbWUiOiJyYWoifQ.pks7xjkFdJD649RjCBwQuPF1_QMoryDWixSKx4tPAqXI75ns4sc-yGhMdbEvIZ3AJSvDaqTa47XTC6c8R
|
||||
username: litmus-user
|
||||
endpoint: https://preview.litmuschaos.io
|
||||
apiVersion: v1
|
||||
current-account: https://preview.litmuschaos.io
|
||||
current-user: litmus-user
|
||||
kind: Config
|
||||
```
|
||||
|
||||
* To get an overview of the accounts available within `.litmusconfig`, use the `config get-accounts` command:
|
||||
|
||||
```shell
|
||||
litmusctl config get-accounts
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CURRENT ENDPOINT USERNAME EXPIRESIN
|
||||
https://preview.litmuschaos.io admin 2021-07-22 01:20:27 +0530 IST
|
||||
* https://preview.litmuschaos.io raj 2021-07-22 14:33:22 +0530 IST
|
||||
```
|
||||
|
||||
* To alter the current account use the `use-account` command with the --endpoint and --username flags:
|
||||
```shell
|
||||
litmusctl config use-account --endpoint="" --username=""
|
||||
```
|
||||
|
||||
* To create a project, apply the following command with the `--name` flag:
|
||||
```shell
|
||||
litmusctl create project --name=""
|
||||
```
|
||||
|
||||
* To view all the projects with the user, use the `get projects` command.
|
||||
```shell
|
||||
litmusctl get projects
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
PROJECT ID PROJECT NAME CREATEDAT
|
||||
50addd40-8767-448c-a91a-5071543a2d8e Developer Project 2021-07-21 14:38:51 +0530 IST
|
||||
7a4a259a-1ae5-4204-ae83-89a8838eaec3 DevOps Project 2021-07-21 14:39:14 +0530 IST
|
||||
```
|
||||
|
||||
|
||||
* To get an overview of the Chaos Delegates available within a project, issue the following command.
|
||||
```shell
|
||||
litmusctl get chaos-delegates --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS DELEGATE ID CHAOS DELEGATE NAME STATUS REGISTRATION
|
||||
55ecc7f2-2754-43aa-8e12-6903e4c6183a chaos-delegate-1 ACTIVE REGISTERED
|
||||
13dsf3d1-5324-54af-4g23-5331g5v2364f chaos-delegate-2 INACTIVE NOT REGISTERED
|
||||
```
|
||||
|
||||
|
||||
* To disconnect a Chaos Delegate, issue the following command..
|
||||
```shell
|
||||
litmusctl disconnect chaos-delegate <chaos-delegate-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
🚀 Chaos Delegate successfully disconnected.
|
||||
```
|
||||
|
||||
|
||||
* To list the created Chaos Scenarios within a project, issue the following command.
|
||||
```shell
|
||||
litmusctl get chaos-scenarios --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS SCENARIO ID CHAOS SCENARIO NAME CHAOS SCENARIO TYPE NEXT SCHEDULE CHAOS DELEGATE ID CHAOS DELEGATE NAME LAST UPDATED BY
|
||||
9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Non Cron Chaos Scenario None f9799723-29f1-454c-b830-ae8ba7ee4c30 Self-Chaos-delegate admin
|
||||
|
||||
Showing 1 of 1 Chaos Scenarios
|
||||
```
|
||||
|
||||
|
||||
* To list all the Chaos Scenario runs within a project, issue the following command.
|
||||
```shell
|
||||
litmusctl get chaos-scenario-runs --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS SCENARIO RUN ID STATUS RESILIENCY SCORE CHAOS SCENARIO ID CHAOS SCENARIO NAME TARGET CHAOS DELEGATE LAST RUN EXECUTED BY
|
||||
8ceb712c-1ed4-40e6-adc4-01f78d281506 Running 0.00 9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Self-Chaos-Delegate June 1 2022, 10:28:02 pm admin
|
||||
|
||||
Showing 1 of 1 Chaos Scenario runs
|
||||
```
|
||||
|
||||
|
||||
* To describe a particular Chaos Scenario, issue the following command.
|
||||
```shell
|
||||
litmusctl describe chaos-scenario <chaos-scenario-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Workflow
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
cluster_id: f9799723-29f1-454c-b830-ae8ba7ee4c30
|
||||
subject: custom-chaos-scenario_litmus
|
||||
workflow_id: 9433b48c-4ab7-4544-8dab-4a7237619e09
|
||||
workflows.argoproj.io/controller-instanceid: f9799723-29f1-454c-b830-ae8ba7ee4c30
|
||||
name: custom-chaos-scenario-1627980541
|
||||
namespace: litmus
|
||||
spec:
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
* To delete a particular Chaos Scenario, issue the following command.
|
||||
```shell
|
||||
litmusctl delete chaos-scenario <chaos-scenario-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
🚀 Chaos Scenario successfully deleted.
|
||||
```
|
||||
|
||||
|
||||
|
||||
For more information related to flags, Use `litmusctl --help`.
|
||||
|
||||
----
|
|
@ -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`.
|
|
@ -0,0 +1,414 @@
|
|||
# Usage: Litmusctl v0.12.0 (Interactive mode)
|
||||
|
||||
> Notes:
|
||||
>
|
||||
> - For litmusctl v0.12.0 or latest
|
||||
|
||||
### litmusctl Syntax
|
||||
|
||||
`litmusctl` has a syntax to use as follows:
|
||||
|
||||
```shell
|
||||
litmusctl [command] [TYPE] [flags]
|
||||
```
|
||||
|
||||
- Command: refers to what you do want to perform (connect, create, get and config)
|
||||
- Type: refers to the feature type you are performing a command against (chaos-delegate, project etc.)
|
||||
- Flags: It takes some additional information for resource operations. For example, `--installation-mode` allows you to specify an installation mode.
|
||||
|
||||
Litmusctl is using the `.litmusconfig` config file to manage multiple accounts
|
||||
|
||||
1. If the --config flag is set, then only the given file is loaded. The flag may only be set once and no merging takes place.
|
||||
2. Otherwise, the ${HOME}/.litmusconfig file is used, and no merging takes place.
|
||||
|
||||
Litmusctl supports both interactive and non-interactive(flag based) modes.
|
||||
|
||||
> Only `litmusctl connect chaos-delegate` command needs --non-interactive flag, other commands don't need this flag to be in non-interactive mode. If mandatory flags aren't passed, then litmusctl takes input in an interactive mode.
|
||||
|
||||
### Steps to connect a Chaos Delegate
|
||||
|
||||
- To setup an account with litmusctl
|
||||
|
||||
```shell
|
||||
litmusctl config set-account
|
||||
```
|
||||
|
||||
Next, you need to enter ChaosCenter details to login into your ChaosCenter account. Fields to be filled in:
|
||||
|
||||
**ChaosCenter URL:** Enter the URL used to access the ChaosCenter.
|
||||
|
||||
> Example, https://preview.litmuschaos.io/
|
||||
|
||||
**Username:** Enter your ChaosCenter username. <br />
|
||||
**Password:** Enter your ChaosCenter password.
|
||||
|
||||
```
|
||||
Host endpoint where litmus is installed: https://preview.litmuschaos.io/
|
||||
Username [Default: admin]: admin
|
||||
|
||||
Password:
|
||||
account.username/admin configured
|
||||
```
|
||||
|
||||
- To connect a Chaos Delegate in a cluster mode
|
||||
|
||||
```shell
|
||||
litmusctl connect chaos-delegate
|
||||
```
|
||||
|
||||
There will be a list of existing projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
|
||||
|
||||
```
|
||||
Project list:
|
||||
1. Project-Admin
|
||||
|
||||
Select a project [Range: 1-1]: 1
|
||||
```
|
||||
|
||||
Next, select the installation mode based on your requirement by entering the sequence number indicated against it.
|
||||
|
||||
Litmusctl can install a Chaos Delegate in two different modes.
|
||||
|
||||
- cluster mode: With this mode, the Chaos Delegate can run the chaos in any namespace. It installs appropriate cluster roles and cluster role bindings to achieve this mode.
|
||||
|
||||
- namespace mode: With this mode, the Chaos Delegate can run the chaos in its namespace. It installs appropriate roles and role bindings to achieve this mode.
|
||||
|
||||
Note: With namespace mode, the user needs to create the namespace to install the Chaos Delegate as a prerequisite.
|
||||
|
||||
```
|
||||
Installation Modes:
|
||||
1. Cluster
|
||||
2. Namespace
|
||||
|
||||
Select Mode [Default: cluster] [Range: 1-2]: 1
|
||||
|
||||
🏃 Running prerequisites check....
|
||||
🔑 clusterrole ✅
|
||||
🔑 clusterrolebinding ✅
|
||||
🌟 Sufficient permissions. Installing the Chaos Delegate...
|
||||
|
||||
```
|
||||
|
||||
Next, enter the details of the new Chaos Delegate.
|
||||
|
||||
Fields to be filled in <br />
|
||||
|
||||
<table>
|
||||
<th>Field</th>
|
||||
<th>Description</th>
|
||||
<tr>
|
||||
<td>Chaos Delegate Name:</td>
|
||||
<td>Enter a name of the Chaos Delegate which needs to be unique across the project</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Chaos Delegate Description:</td>
|
||||
<td>Fill in details about the Chaos Delegate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Skip SSL verification</td>
|
||||
<td>Choose whether Chaos Delegate will skip SSL/TLS verification</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node Selector:</td>
|
||||
<td>To deploy the Chaos Delegate on a particular node based on the node selector labels</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Platform Name:</td>
|
||||
<td>Enter the platform name on which this Chaos Delegate is hosted. For example, AWS, GCP, Rancher etc.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Enter the namespace:</td>
|
||||
<td>You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, litmusctl creates it for you</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Enter service account:</td>
|
||||
<td>You can either enter an existing or new service account</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
```
|
||||
Enter the details of the Chaos Delegate
|
||||
|
||||
Chaos Delegate Name: New-Chaos-Delegate
|
||||
|
||||
Chaos Delegate Description: This is a new Chaos Delegate
|
||||
|
||||
Do you want Chaos Delegate to skip SSL/TLS check (Y/N) (Default: N): n
|
||||
|
||||
Do you want NodeSelector to be added in the Chaos Delegate deployments (Y/N) (Default: N): N
|
||||
|
||||
Platform List:
|
||||
1. AWS
|
||||
2. GKE
|
||||
3. Openshift
|
||||
4. Rancher
|
||||
5. Others
|
||||
|
||||
Select a platform [Default: Others] [Range: 1-5]: 5
|
||||
|
||||
Enter the namespace (new or existing namespace) [Default: litmus]:
|
||||
👍 Continuing with litmus namespace
|
||||
```
|
||||
|
||||
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
|
||||
After verification of these details, you can proceed with the connection of the Chaos Delegate by entering Y. The process of connection might take up to a few seconds.
|
||||
|
||||
```
|
||||
Enter service account [Default: litmus]:
|
||||
|
||||
📌 Summary
|
||||
Chaos Delegate Name: New-Chaos-Delegate
|
||||
Chaos Delegate Description: This is a new Chaos Delegate
|
||||
Chaos Delegate SSL/TLS Skip: false
|
||||
Platform Name: Others
|
||||
Namespace: litmus
|
||||
Service Account: litmus (new)
|
||||
|
||||
Installation Mode: cluster
|
||||
|
||||
🤷 Do you want to continue with the above details? [Y/N]: Y
|
||||
👍 Continuing Chaos Infrastructure connection!!
|
||||
|
||||
💡 Connecting Chaos Infrastructure to ChaosCenter.
|
||||
🏃 Chaos Delegate is running!!
|
||||
|
||||
🚀 Chaos Infrastructure Connection Successful!! 🎉
|
||||
```
|
||||
|
||||
#### Verify the new Chaos Delegate Connection\*\*
|
||||
|
||||
To verify, if the connection process was successful you can view the list of connected Chaos Delegates from the Targets section on your ChaosCenter and ensure that the connected Chaos Delegate is in Active State.
|
||||
|
||||
---
|
||||
|
||||
### Steps to create a Chaos Scenario
|
||||
|
||||
* To setup an account with litmusctl
|
||||
```shell
|
||||
litmusctl config set-account --endpoint="" --username="" --password=""
|
||||
```
|
||||
|
||||
* To create a Chaos Scenario by passing a manifest file
|
||||
> Note:
|
||||
> * To get `project-id`, apply `litmusctl get projects`
|
||||
> * To get `chaos-delegate-id`, apply `litmusctl get chaos-delegates --project-id=""`
|
||||
```shell
|
||||
litmusctl create chaos-scenario -f custom-chaos-scenario.yml --project-id="" --chaos-delegate-id=""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Additional commands
|
||||
|
||||
- To change the ChaosCenter account's password use the `update password` command:
|
||||
|
||||
```shell
|
||||
litmusctl update password
|
||||
✓ Username: admin
|
||||
✓ Old Password: ********
|
||||
✓ New Password: ********
|
||||
✓ Confirm Password: ********
|
||||
|
||||
Password updated successfully!
|
||||
```
|
||||
|
||||
- To view the current configuration of `.litmusconfig`, type:
|
||||
|
||||
```shell
|
||||
litmusctl config view
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
accounts:
|
||||
- users:
|
||||
- expires_in: "1626897027"
|
||||
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY4OTcwMjcsInJvbGUiOiJhZG1pbiIsInVpZCI6ImVlODZkYTljLTNmODAtNGRmMy04YzQyLTExNzlhODIzOTVhOSIsInVzZXJuYW1lIjoiYWRtaW4ifQ.O_hFcIhxP4rhyUN9NEVlQmWesoWlpgHpPFL58VbJHnhvJllP5_MNPbrRMKyFvzW3hANgXK2u8437u
|
||||
username: admin
|
||||
- expires_in: "1626944602"
|
||||
token: eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjY5NDQ2MDIsInJvbGUiOiJ1c2VyIiwidWlkIjoiNjFmMDY4M2YtZWY0OC00MGE1LWIzMjgtZTU2ZDA2NjM1MTE4IiwidXNlcm5hbWUiOiJyYWoifQ.pks7xjkFdJD649RjCBwQuPF1_QMoryDWixSKx4tPAqXI75ns4sc-yGhMdbEvIZ3AJSvDaqTa47XTC6c8R
|
||||
username: litmus-user
|
||||
endpoint: https://preview.litmuschaos.io
|
||||
apiVersion: v1
|
||||
current-account: https://preview.litmuschaos.io
|
||||
current-user: litmus-user
|
||||
kind: Config
|
||||
```
|
||||
|
||||
- To get an overview of the accounts available within `.litmusconfig`, use the `config get-accounts` command:
|
||||
|
||||
```shell
|
||||
litmusctl config get-accounts
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CURRENT ENDPOINT USERNAME EXPIRESIN
|
||||
https://preview.litmuschaos.io admin 2021-07-22 01:20:27 +0530 IST
|
||||
* https://preview.litmuschaos.io raj 2021-07-22 14:33:22 +0530 IST
|
||||
```
|
||||
|
||||
- To alter the current account use the `use-account` command:
|
||||
|
||||
```shell
|
||||
litmusctl config use-account
|
||||
|
||||
Host endpoint where litmus is installed: https://preview.litmuschaos.io
|
||||
|
||||
Username: admin
|
||||
```
|
||||
|
||||
- To create a project, apply the following command :
|
||||
|
||||
```shell
|
||||
litmusctl create project
|
||||
|
||||
Enter a project name: new
|
||||
```
|
||||
|
||||
- To view all the projects with the user, use the `get projects` command.
|
||||
|
||||
```shell
|
||||
litmusctl get projects
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
PROJECT ID PROJECT NAME CREATEDAT
|
||||
50addd40-8767-448c-a91a-5071543a2d8e Developer Project 2021-07-21 14:38:51 +0530 IST
|
||||
7a4a259a-1ae5-4204-ae83-89a8838eaec3 DevOps Project 2021-07-21 14:39:14 +0530 IST
|
||||
```
|
||||
|
||||
- To get an overview of the Chaos Delegates available within a project, issue the following command.
|
||||
|
||||
```shell
|
||||
litmusctl get chaos-delegates
|
||||
|
||||
Enter the Project ID: 50addd40-8767-448c-a91a-5071543a2d8e
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS DELEGATE ID CHAOS DELEGATE NAME STATUS REGISTRATION
|
||||
55ecc7f2-2754-43aa-8e12-6903e4c6183a chaos-delegate-1 ACTIVE REGISTERED
|
||||
13dsf3d1-5324-54af-4g23-5331g5v2364f chaos-delegate-2 INACTIVE NOT REGISTERED
|
||||
```
|
||||
|
||||
|
||||
* To disconnect an Chaos Delegate, issue the following command..
|
||||
```shell
|
||||
litmusctl disconnect chaos-delegate <chaos-delegate-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
🚀 Chaos Delegate successfully disconnected.
|
||||
```
|
||||
|
||||
|
||||
* To list the created Chaos Scenarios within a project, issue the following command.
|
||||
```shell
|
||||
litmusctl get chaos-scenarios --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS SCENARIO ID CHAOS SCENARIO NAME CHAOS SCENARIO TYPE NEXT SCHEDULE CHAOS DELEGATE ID CHAOS DELEGATE NAME LAST UPDATED BY
|
||||
9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Non Cron Chaos Scenario None f9799723-29f1-454c-b830-ae8ba7ee4c30 Self-Chaos-delegate admin
|
||||
|
||||
Showing 1 of 1 Chaos Scenarios
|
||||
```
|
||||
|
||||
|
||||
* To list all the Chaos Scenario runs within a project, issue the following command.
|
||||
```shell
|
||||
litmusctl get chaos-scenario-runs --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
CHAOS SCENARIO RUN ID STATUS RESILIENCY SCORE CHAOS SCENARIO ID CHAOS SCENARIO NAME TARGET CHAOS DELEGATE LAST RUN EXECUTED BY
|
||||
8ceb712c-1ed4-40e6-adc4-01f78d281506 Running 0.00 9433b48c-4ab7-4544-8dab-4a7237619e09 custom-chaos-scenario-1627980541 Self-Chaos-Delegate June 1 2022, 10:28:02 pm admin
|
||||
|
||||
Showing 1 of 1 Chaos Scenario runs
|
||||
```
|
||||
|
||||
|
||||
* To describe a particular Chaos Scenario, issue the following command.
|
||||
```shell
|
||||
litmusctl describe chaos-scenario <chaos-scenario-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Workflow
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
cluster_id: f9799723-29f1-454c-b830-ae8ba7ee4c30
|
||||
subject: custom-chaos-scenario_litmus
|
||||
workflow_id: 9433b48c-4ab7-4544-8dab-4a7237619e09
|
||||
workflows.argoproj.io/controller-instanceid: f9799723-29f1-454c-b830-ae8ba7ee4c30
|
||||
name: custom-chaos-scenario-1627980541
|
||||
namespace: litmus
|
||||
spec:
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
* To delete a particular Chaos Scenario, issue the following command.
|
||||
```shell
|
||||
litmusctl delete chaos-scenario <chaos-scenario-id> --project-id=""
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```
|
||||
🚀 Chaos Scenario successfully deleted.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flag details
|
||||
|
||||
<table>
|
||||
<th>Flag</th>
|
||||
<th>Short Flag</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<tr>
|
||||
<td>--cacert</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>custom ca certificate used by litmusctl for communicating with portal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--config</td>
|
||||
<td></td>
|
||||
<td>String</td>
|
||||
<td>config file (default is $HOME/.litmusctl)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--skipSSL</td>
|
||||
<td></td>
|
||||
<td>Boolean</td>
|
||||
<td>litmusctl will skip ssl/tls verification while communicating with portal</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--help</td>
|
||||
<td>-h</td>
|
||||
<td></td>
|
||||
<td>help for litmusctl</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
For more information related to flags, Use `litmusctl --help`.
|
|
@ -0,0 +1,110 @@
|
|||
# Usage: Litmusctl v0.2.0
|
||||
|
||||
> Notes:
|
||||
>
|
||||
> - For litmusctl v0.3.0 or earlier
|
||||
> - Compatible with Litmus 2.0.0-Beta8 or earlier
|
||||
|
||||
### Connecting an agent
|
||||
|
||||
To connect Litmus Chaos agent:
|
||||
|
||||
```shell
|
||||
litmusctl agent connect
|
||||
```
|
||||
|
||||
Next, you need to enter ChaosCenter details to login into your ChaosCenter account. Fields to be filled in:
|
||||
|
||||
**ChaosCenter UI URL:** Enter the URL used to access the ChaosCenter UI.
|
||||
Example, http://172.17.0.2:31696/
|
||||
|
||||
**Username:** Enter your ChaosCenter username.
|
||||
**Password:** Enter your ChaosCenter password.
|
||||
|
||||
```shell
|
||||
🔥 Connecting LitmusChaos agent
|
||||
|
||||
📶 Please enter LitmusChaos details --
|
||||
👉 Host URL where litmus is installed: http://172.17.0.2:31696/
|
||||
🤔 Username [admin]: admin
|
||||
🙈 Password:
|
||||
✅ Login Successful!
|
||||
```
|
||||
|
||||
Upon successful login, there will be a list of exiting projects displayed on the terminal. Select the desired project by entering the sequence number indicated against it.
|
||||
|
||||
```shell
|
||||
✨ Projects List:
|
||||
1. abc
|
||||
|
||||
🔎 Select Project: 1
|
||||
```
|
||||
|
||||
Next, select the installation mode. In case the selected mode was a Cluster there will be a prerequisites check to verify ClusterRole and ClusterRoleBinding.
|
||||
|
||||
```shell
|
||||
🔌 Installation Modes:
|
||||
1. Cluster
|
||||
2. Namespace
|
||||
|
||||
👉 Select Mode [cluster]: 1
|
||||
|
||||
🏃 Running prerequisites check....
|
||||
🔑 clusterrole - ✅
|
||||
🔑 clusterrolebinding - ✅
|
||||
|
||||
🌟 Sufficient permissions. Connecting Agent
|
||||
```
|
||||
|
||||
Next, enter the details of the new agent.
|
||||
|
||||
Fields to filled in:
|
||||
**Agent Name:** Enter the name of the new agent.
|
||||
|
||||
**Agent Description:** Fill in details about the agent.
|
||||
|
||||
**Platform Name:** Enter the platform name on which this agent is hosted. For example, AWS, GCP, Rancher etc.
|
||||
|
||||
**Enter the namespace:** You can either enter an existing namespace or enter a new namespace. In cases where the namespace does not exist, ChaosCenter creates it for you.
|
||||
|
||||
**Enter service account:** Enter a name for your service account.
|
||||
|
||||
```shell
|
||||
🔗 Enter the details of the agent ----
|
||||
🤷 Agent Name: my-agent
|
||||
📘 Agent Description: This is a new agent.
|
||||
📦 Platform List
|
||||
1. AWS
|
||||
2. GKE
|
||||
3. Openshift
|
||||
4. Rancher
|
||||
5. Others
|
||||
🔎 Select Platform [Others]: 5
|
||||
📁 Enter the namespace (new or existing) [litmus]: litmus
|
||||
🔑 Enter service account [litmus]: litmus
|
||||
```
|
||||
|
||||
Once, all these steps are implemented you will be able to see a summary of all the entered fields.
|
||||
After verification of these details, you can proceed with the connection of the agent by entering Y. The process of connection might take up to a few seconds.
|
||||
|
||||
```shell
|
||||
📌 Summary --------------------------
|
||||
|
||||
Agent Name: my-agent
|
||||
Agent Description: This is a new agent.
|
||||
Platform Name: Others
|
||||
Namespace: litmus
|
||||
Service Account: litmus
|
||||
Installation Mode: cluster
|
||||
|
||||
-------------------------------------
|
||||
|
||||
🤷 Do you want to continue with the above details? [Y/N]: Y
|
||||
|
||||
💡 Connecting agent to ChaosCenter.
|
||||
🏃 Agents running!!
|
||||
🚀 Agent Connection Successful!! 🎉
|
||||
👉 Litmus agents can be accessed here: http://172.17.0.2:31696/targets
|
||||
```
|
||||
|
||||
To verify, if the connection process was successful you can view the list of connected agents from the Targets section on your ChaosCenter and ensure that the connected agent is in Active State.
|
117
go.mod
117
go.mod
|
@ -1,17 +1,112 @@
|
|||
module github.com/litmuschaos/litmusctl
|
||||
|
||||
go 1.14
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
|
||||
github.com/argoproj/argo v2.5.2+incompatible
|
||||
github.com/go-resty/resty/v2 v2.5.0
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
github.com/spf13/cobra v1.1.3
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
github.com/argoproj/argo-workflows/v3 v3.5.5
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
|
||||
github.com/litmuschaos/chaos-operator v0.0.0-20230718113617-6819a4be12e4
|
||||
github.com/litmuschaos/litmus/chaoscenter/graphql/server v0.0.0-20240115142759-7a29dc1eb1d8
|
||||
github.com/manifoldco/promptui v0.9.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.20.4
|
||||
k8s.io/apimachinery v0.20.4
|
||||
k8s.io/client-go v0.20.4
|
||||
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 // indirect
|
||||
k8s.io/api v0.29.2
|
||||
k8s.io/apimachinery v0.29.2
|
||||
k8s.io/client-go v12.0.0+incompatible
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.120.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.11.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
)
|
||||
|
||||
// Pinned to kubernetes-1.21.2
|
||||
replace (
|
||||
k8s.io/api => k8s.io/api v0.27.3
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.2
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.27.3
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.21.2
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.2
|
||||
k8s.io/client-go => k8s.io/client-go v0.27.3
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.2
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.21.2
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.21.2
|
||||
k8s.io/component-base => k8s.io/component-base v0.21.2
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.21.2
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.2
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.2
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.2
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.2
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.2
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.21.2
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.21.2
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.2
|
||||
k8s.io/metrics => k8s.io/metrics v0.21.2
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.2
|
||||
)
|
||||
|
||||
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm
|
||||
|
|
20
main.go
20
main.go
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
Copyright © 2020 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -15,8 +15,20 @@ limitations under the License.
|
|||
*/
|
||||
package main
|
||||
|
||||
import cmd "github.com/litmuschaos/litmusctl/pkg/cmd/litmusctl"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
rootCmd "github.com/litmuschaos/litmusctl/pkg/cmd/root"
|
||||
)
|
||||
|
||||
var CLIVersion string
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
err := os.Setenv("CLIVersion", CLIVersion)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to fetched CLIVersion")
|
||||
}
|
||||
|
||||
rootCmd.Execute()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package apis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
)
|
||||
|
||||
type Payload struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func Auth(input types.AuthInput) (types.AuthResponse, error) {
|
||||
payloadBytes, err := json.Marshal(Payload{
|
||||
Username: input.Username,
|
||||
Password: input.Password,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return types.AuthResponse{}, err
|
||||
}
|
||||
|
||||
// Sending token as empty because auth server doesn't need Authorization token to validate.
|
||||
resp, err := SendRequest(SendRequestParams{input.Endpoint + utils.AuthAPIPath + "/login", ""}, payloadBytes, string(types.Post))
|
||||
if err != nil {
|
||||
return types.AuthResponse{}, err
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return types.AuthResponse{}, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var authResponse types.AuthResponse
|
||||
err = json.Unmarshal(bodyBytes, &authResponse)
|
||||
if err != nil {
|
||||
return types.AuthResponse{}, err
|
||||
}
|
||||
|
||||
return authResponse, nil
|
||||
} else {
|
||||
return types.AuthResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
|
||||
}
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package apis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
)
|
||||
|
||||
type CreateProjectResponse struct {
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"projectID"`
|
||||
} `json:"data"`
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
type createProjectPayload struct {
|
||||
ProjectName string `json:"projectName"`
|
||||
}
|
||||
|
||||
func CreateProjectRequest(projectName string, cred types.Credentials) (CreateProjectResponse, error) {
|
||||
payloadBytes, err := json.Marshal(createProjectPayload{
|
||||
ProjectName: projectName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return CreateProjectResponse{}, err
|
||||
}
|
||||
resp, err := SendRequest(SendRequestParams{cred.Endpoint + utils.AuthAPIPath + "/create_project", "Bearer " + cred.Token}, payloadBytes, string(types.Post))
|
||||
if err != nil {
|
||||
return CreateProjectResponse{}, err
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return CreateProjectResponse{}, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var project CreateProjectResponse
|
||||
err = json.Unmarshal(bodyBytes, &project)
|
||||
if err != nil {
|
||||
return CreateProjectResponse{}, err
|
||||
}
|
||||
|
||||
if len(project.Errors) > 0 {
|
||||
return CreateProjectResponse{}, errors.New(project.Errors[0].Message)
|
||||
}
|
||||
|
||||
utils.White_B.Println("project/" + project.Data.Name + " created")
|
||||
return project, nil
|
||||
} else {
|
||||
return CreateProjectResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
type listProjectResponse struct {
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
Projects []struct {
|
||||
ID string `json:"projectID"` // Adjusted field name
|
||||
Name string `json:"name"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
} `json:"projects"`
|
||||
TotalNumberOfProjects int `json:"totalNumberOfProjects"`
|
||||
} `json:"data"`
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
func ListProject(cred types.Credentials) (listProjectResponse, error) {
|
||||
|
||||
resp, err := SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.AuthAPIPath + "/list_projects", Token: "Bearer " + cred.Token}, []byte{}, string(types.Get))
|
||||
if err != nil {
|
||||
return listProjectResponse{}, err
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return listProjectResponse{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var data listProjectResponse
|
||||
err = json.Unmarshal(bodyBytes, &data)
|
||||
if err != nil {
|
||||
return listProjectResponse{}, err
|
||||
|
||||
}
|
||||
|
||||
if len(data.Errors) > 0 {
|
||||
return listProjectResponse{}, errors.New(data.Errors[0].Message)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
} else {
|
||||
return listProjectResponse{}, errors.New("Unmatched status code:" + string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
type ProjectDetails struct {
|
||||
Data Data `json:"data"`
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
ID string `json:"ID"`
|
||||
Projects []Project `json:"Projects"`
|
||||
}
|
||||
|
||||
type Member struct {
|
||||
Role string `json:"Role"`
|
||||
UserID string `json:"userID"`
|
||||
UserName string `json:"username"`
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID string `json:"ProjectID"`
|
||||
Name string `json:"Name"`
|
||||
CreatedAt int64 `json:"CreatedAt"`
|
||||
Members []Member `json:"Members"`
|
||||
}
|
||||
|
||||
// GetProjectDetails fetches details of the input user
|
||||
func GetProjectDetails(c types.Credentials) (ProjectDetails, error) {
|
||||
token, _ := jwt.Parse(c.Token, nil)
|
||||
if token == nil {
|
||||
return ProjectDetails{}, nil
|
||||
}
|
||||
Username, _ := token.Claims.(jwt.MapClaims)["username"].(string)
|
||||
resp, err := SendRequest(SendRequestParams{Endpoint: c.Endpoint + utils.AuthAPIPath + "/get_user_with_project/" + Username, Token: "Bearer " + c.Token}, []byte{}, string(types.Get))
|
||||
if err != nil {
|
||||
return ProjectDetails{}, err
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
return ProjectDetails{}, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var project ProjectDetails
|
||||
err = json.Unmarshal(bodyBytes, &project)
|
||||
if err != nil {
|
||||
return ProjectDetails{}, err
|
||||
}
|
||||
if len(project.Errors) > 0 {
|
||||
return ProjectDetails{}, errors.New(project.Errors[0].Message)
|
||||
}
|
||||
|
||||
return project, nil
|
||||
} else {
|
||||
return ProjectDetails{}, errors.New("Unmatched status code:" + string(bodyBytes))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package apis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type SendRequestParams struct {
|
||||
Endpoint string
|
||||
Token string
|
||||
}
|
||||
|
||||
func SendRequest(params SendRequestParams, payload []byte, method string) (*http.Response, error) {
|
||||
req, err := http.NewRequest(method, params.Endpoint, bytes.NewBuffer(payload))
|
||||
if err != nil {
|
||||
return &http.Response{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", params.Token)
|
||||
req.Header.Set("Referer", params.Endpoint)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return &http.Response{}, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package apis
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/k8s"
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type manifestData struct {
|
||||
Data data `json:"data"`
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
type data struct {
|
||||
GetManifest string `json:"getInfraManifest"`
|
||||
}
|
||||
|
||||
type GetInfraResponse struct {
|
||||
Data GetInfraData `json:"data"`
|
||||
Errors []struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
type GetInfraData struct {
|
||||
GetInfraDetails InfraDetails `json:"getInfraDetails"`
|
||||
}
|
||||
|
||||
type InfraDetails struct {
|
||||
InfraID string `json:"infraID"`
|
||||
InfraNamespace *string `json:"infraNamespace"`
|
||||
}
|
||||
|
||||
func UpgradeInfra(c context.Context, cred types.Credentials, projectID string, infraID string, kubeconfig string) (string, error) {
|
||||
|
||||
// Query to fetch Infra details from server
|
||||
query := `{"query":"query {\n getInfraDetails(infraID : \"` + infraID + `\", \n projectID : \"` + projectID + `\"){\n infraNamespace infraID \n}}"}`
|
||||
resp, err := SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(query), string(types.Post))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
var infra GetInfraResponse
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
err = json.Unmarshal(bodyBytes, &infra)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(infra.Errors) > 0 {
|
||||
return "", errors.New(infra.Errors[0].Message)
|
||||
}
|
||||
} else {
|
||||
return "", errors.New(resp.Status)
|
||||
}
|
||||
|
||||
// Query to fetch upgraded manifest from the server
|
||||
query = `{"query":"query {\n getInfraManifest(projectID : \"` + projectID + `\",\n infraID : \"` + infra.Data.GetInfraDetails.InfraID + `\", \n upgrade: true)}"}`
|
||||
resp, err = SendRequest(SendRequestParams{Endpoint: cred.Endpoint + utils.GQLAPIPath, Token: cred.Token}, []byte(query), string(types.Post))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bodyBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Checks if status code is OK(200)
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
var manifest manifestData
|
||||
err = json.Unmarshal(bodyBytes, &manifest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(manifest.Errors) > 0 {
|
||||
return "", errors.New(manifest.Errors[0].Message)
|
||||
}
|
||||
|
||||
// Fetching subscriber-config from the subscriber
|
||||
configData, err := k8s.GetConfigMap(c, "subscriber-config", *infra.Data.GetInfraDetails.InfraNamespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var configMapString string
|
||||
|
||||
metadata := new(bytes.Buffer)
|
||||
fmt.Fprintf(metadata, "\n%s: %s\n%s: %s\n%s: \n %s: %s\n %s: %s\n%s:\n", "apiVersion", "v1",
|
||||
"kind", "ConfigMap", "metadata", "name", "subscriber-config", "namespace", *infra.Data.GetInfraDetails.InfraNamespace, "data")
|
||||
|
||||
for k, v := range configData {
|
||||
b := new(bytes.Buffer)
|
||||
if k == "COMPONENTS" {
|
||||
fmt.Fprintf(b, " %s: |\n %s", k, v)
|
||||
} else if k == "START_TIME" || k == "IS_INFRA_CONFIRMED" {
|
||||
fmt.Fprintf(b, " %s: \"%s\"\n", k, v)
|
||||
} else {
|
||||
fmt.Fprintf(b, " %s: %s\n", k, v)
|
||||
}
|
||||
configMapString = configMapString + b.String()
|
||||
|
||||
}
|
||||
|
||||
yamlOutput, err := k8s.ApplyManifest([]byte(manifest.Data.GetManifest), kubeconfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.Println("🚀 Successfully Upgraded Chaos Infrastructure")
|
||||
|
||||
utils.White.Print("\n", yamlOutput)
|
||||
|
||||
// Creating a backup for current subscriber-config in the SUBSCRIBER
|
||||
home, err := homedir.Dir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
configMapString = metadata.String() + configMapString
|
||||
err = os.WriteFile(home+"/backupSubscriberConfig.yaml", []byte(configMapString), 0644)
|
||||
if err != nil {
|
||||
return "Error creating backup for subscriber config: ", err
|
||||
}
|
||||
|
||||
utils.White_B.Print("\n ** A backup of subscriber-config configmap has been saved in your system's home directory as backupSubscriberConfig.yaml **\n")
|
||||
|
||||
return "Manifest applied successfully", nil
|
||||
} else {
|
||||
return "GQL error: ", errors.New("Unmatched status code:" + string(bodyBytes))
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// agentCmd represents the agent command
|
||||
var AgentCmd = &cobra.Command{
|
||||
Use: "agent",
|
||||
Short: "Litmus Agent",
|
||||
Long: `agent is used to manage Litmus backup and restore agents`,
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package register
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
utils "github.com/litmuschaos/litmusctl/pkg/common"
|
||||
chaos "github.com/litmuschaos/litmusctl/pkg/common/chaos"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// registerCmd represents the register command
|
||||
var RegisterCmd = &cobra.Command{
|
||||
Use: "register",
|
||||
Short: "Register LitmusChaos agent",
|
||||
Long: `Register registers the agent to LitmusChaos`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
var c utils.Credentials
|
||||
var pErr error
|
||||
fmt.Println("🔥 Registering LitmusChaos agent")
|
||||
fmt.Println("\n📶 Please enter LitmusChaos details --")
|
||||
// Get LitmusChaos URL as input
|
||||
c.Host, pErr = utils.GetPortalURL()
|
||||
if pErr != nil {
|
||||
fmt.Printf("\n❌ URL parsing failed: [%s]", pErr.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
// Get username as input
|
||||
c.Username = utils.GetUsername()
|
||||
// Get password as input
|
||||
c.Password = utils.GetPassword()
|
||||
// Fetch authorization token
|
||||
t := utils.Login(c, "auth/login")
|
||||
|
||||
chaos.Register(t, c)
|
||||
},
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// configCmd represents the config command
|
||||
var ConfigCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: `It manages multiple ChaosCenter accounts within a system.
|
||||
Examples(s)
|
||||
#set a new account
|
||||
litmusctl config set-account --endpoint "" --password "" --username ""
|
||||
|
||||
#use an existing account from the config file
|
||||
litmusctl config use-account --endpoint "" --username ""
|
||||
|
||||
#get all accounts in the config file
|
||||
litmusctl config get-accounts
|
||||
|
||||
#view the config file
|
||||
litmusctl config view
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/config"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// getAccountsCmd represents the getAccounts command
|
||||
var getAccountsCmd = &cobra.Command{
|
||||
Use: "get-accounts",
|
||||
Short: "Display accounts defined in the litmusconfig",
|
||||
Long: `Display accounts defined in the litmusconfig`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
configFilePath := utils.GetLitmusConfigPath(cmd)
|
||||
|
||||
obj, err := config.YamltoObject(configFilePath)
|
||||
utils.PrintError(err)
|
||||
|
||||
writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight)
|
||||
utils.White_B.Fprintln(writer, "CURRENT\tENDPOINT\tUSERNAME\tEXPIRESIN")
|
||||
for _, account := range obj.Accounts {
|
||||
for _, user := range account.Users {
|
||||
intTime, err := strconv.ParseInt(user.ExpiresIn, 10, 64)
|
||||
utils.PrintError(err)
|
||||
|
||||
humanTime := time.Unix(intTime, 0)
|
||||
|
||||
if obj.CurrentUser == user.Username && obj.CurrentAccount == account.Endpoint {
|
||||
utils.White.Fprintln(writer, "*"+"\t"+account.Endpoint+"\t"+user.Username+"\t"+humanTime.String())
|
||||
} else {
|
||||
utils.White.Fprintln(writer, ""+"\t"+account.Endpoint+"\t"+user.Username+"\t"+humanTime.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigCmd.AddCommand(getAccountsCmd)
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
infra "github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
|
||||
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis"
|
||||
"github.com/litmuschaos/litmusctl/pkg/config"
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// setAccountCmd represents the setAccount command
|
||||
var setAccountCmd = &cobra.Command{
|
||||
Use: "set-account",
|
||||
Short: `Sets an account entry in litmusconfig.
|
||||
Examples(s)
|
||||
#set a new account
|
||||
litmusctl config set-account --endpoint "" --password "" --username ""
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
configFilePath := utils.GetLitmusConfigPath(cmd)
|
||||
|
||||
var (
|
||||
authInput types.AuthInput
|
||||
err error
|
||||
)
|
||||
|
||||
nonInteractive, err := cmd.Flags().GetBool("non-interactive")
|
||||
utils.PrintError(err)
|
||||
|
||||
if nonInteractive {
|
||||
authInput.Endpoint, err = cmd.Flags().GetString("endpoint")
|
||||
utils.PrintError(err)
|
||||
|
||||
authInput.Username, err = cmd.Flags().GetString("username")
|
||||
utils.PrintError(err)
|
||||
|
||||
authInput.Password, err = cmd.Flags().GetString("password")
|
||||
utils.PrintError(err)
|
||||
|
||||
} else {
|
||||
// prompts for account details
|
||||
promptEndpoint := promptui.Prompt{
|
||||
Label: "Host endpoint where litmus is installed",
|
||||
}
|
||||
authInput.Endpoint, err = promptEndpoint.Run()
|
||||
utils.PrintError(err)
|
||||
|
||||
promptUsername := promptui.Prompt{
|
||||
Label: "Username [Default: " + utils.DefaultUsername + "]",
|
||||
Default: utils.DefaultUsername,
|
||||
}
|
||||
authInput.Username, err = promptUsername.Run()
|
||||
utils.PrintError(err)
|
||||
|
||||
promptPassword := promptui.Prompt{
|
||||
Label: "Password",
|
||||
Mask: '*',
|
||||
}
|
||||
pass, err := promptPassword.Run()
|
||||
utils.PrintError(err)
|
||||
authInput.Password = pass
|
||||
}
|
||||
|
||||
// Validate and format the endpoint URL
|
||||
ep := strings.TrimRight(authInput.Endpoint, "/")
|
||||
newURL, err := url.Parse(ep)
|
||||
utils.PrintError(err)
|
||||
authInput.Endpoint = newURL.String()
|
||||
|
||||
if authInput.Endpoint == "" {
|
||||
utils.White_B.Print("\nHost endpoint where litmus is installed: ")
|
||||
fmt.Scanln(&authInput.Endpoint)
|
||||
|
||||
if authInput.Endpoint == "" {
|
||||
utils.Red.Println("\n⛔ Host URL can't be empty!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ep := strings.TrimRight(authInput.Endpoint, "/")
|
||||
newUrl, err := url.Parse(ep)
|
||||
utils.PrintError(err)
|
||||
|
||||
authInput.Endpoint = newUrl.String()
|
||||
}
|
||||
|
||||
if authInput.Endpoint != "" && authInput.Username != "" && authInput.Password != "" {
|
||||
exists := config.FileExists(configFilePath)
|
||||
var lgt int
|
||||
if exists {
|
||||
lgt, err = config.GetFileLength(configFilePath)
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
resp, err := apis.Auth(authInput)
|
||||
utils.PrintError(err)
|
||||
// Decoding token
|
||||
token, _ := jwt.Parse(resp.AccessToken, nil)
|
||||
if token == nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
claims, _ := token.Claims.(jwt.MapClaims)
|
||||
|
||||
var user = types.User{
|
||||
ExpiresIn: fmt.Sprint(time.Now().Add(time.Second * time.Duration(resp.ExpiresIn)).Unix()),
|
||||
Token: resp.AccessToken,
|
||||
Username: claims["username"].(string),
|
||||
}
|
||||
|
||||
var users []types.User
|
||||
users = append(users, user)
|
||||
|
||||
var account = types.Account{
|
||||
Endpoint: authInput.Endpoint,
|
||||
Users: users,
|
||||
ServerEndpoint: authInput.Endpoint,
|
||||
}
|
||||
|
||||
// If config file doesn't exist or length of the file is zero.
|
||||
if !exists || lgt == 0 {
|
||||
|
||||
var accounts []types.Account
|
||||
accounts = append(accounts, account)
|
||||
|
||||
var litmuCtlConfig = types.LitmuCtlConfig{
|
||||
APIVersion: "v1",
|
||||
Kind: "Config",
|
||||
CurrentAccount: authInput.Endpoint,
|
||||
CurrentUser: claims["username"].(string),
|
||||
Accounts: accounts,
|
||||
}
|
||||
|
||||
err := config.CreateNewLitmusCtlConfig(configFilePath, litmuCtlConfig)
|
||||
utils.PrintError(err)
|
||||
|
||||
} else {
|
||||
// checking syntax
|
||||
err = config.ConfigSyntaxCheck(configFilePath)
|
||||
utils.PrintError(err)
|
||||
|
||||
var updateLitmusCtlConfig = types.UpdateLitmusCtlConfig{
|
||||
Account: account,
|
||||
CurrentAccount: authInput.Endpoint,
|
||||
CurrentUser: claims["username"].(string),
|
||||
ServerEndpoint: authInput.Endpoint,
|
||||
}
|
||||
|
||||
err = config.UpdateLitmusCtlConfig(updateLitmusCtlConfig, configFilePath)
|
||||
utils.PrintError(err)
|
||||
}
|
||||
utils.White_B.Printf("\naccount.username/%s configured", claims["username"].(string))
|
||||
credentials, err := utils.GetCredentials(cmd)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
endpoint := credentials.Endpoint + utils.AuthAPIPath + "/get_user/" + claims["uid"].(string)
|
||||
userResp, err := apis.SendRequest(
|
||||
apis.SendRequestParams{
|
||||
Endpoint: endpoint,
|
||||
Token: "Bearer " + credentials.Token,
|
||||
},
|
||||
nil,
|
||||
string(types.Get),
|
||||
)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
bodyBytes, err := io.ReadAll(userResp.Body)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
var userResponse map[string]interface{}
|
||||
err = json.Unmarshal(bodyBytes, &userResponse)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
isInitialLogin := userResponse["isInitialLogin"].(bool)
|
||||
if isInitialLogin {
|
||||
utils.White_B.Println("\n❗ This is your first time login. Update your default password to perform further operations.")
|
||||
utils.White.Println(fmt.Sprintf("Use '%s' to update your password.", utils.White_B.Sprint("litmusctl update password")))
|
||||
}
|
||||
} else {
|
||||
utils.Red.Println("\nError: some flags are missing. Run 'litmusctl config set-account --help' for usage. ")
|
||||
}
|
||||
|
||||
serverResp, err := infra.GetServerVersion(authInput.Endpoint)
|
||||
var isCompatible bool
|
||||
if err != nil {
|
||||
utils.Red.Println("\nError: ", err)
|
||||
} else {
|
||||
compatibilityArr := utils.CompatibilityMatrix[os.Getenv("CLIVersion")]
|
||||
for _, v := range compatibilityArr {
|
||||
if v == serverResp.Data.GetServerVersion.Value {
|
||||
isCompatible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !isCompatible {
|
||||
utils.Red.Println("\n🚫 ChaosCenter version: " + serverResp.Data.GetServerVersion.Value + " is not compatible with the installed LitmusCTL version: " + os.Getenv("CLIVersion"))
|
||||
utils.White_B.Println("Compatible ChaosCenter versions are: ")
|
||||
utils.White_B.Print("[ ")
|
||||
for _, v := range compatibilityArr {
|
||||
utils.White_B.Print("'" + v + "' ")
|
||||
}
|
||||
utils.White_B.Print("]\n")
|
||||
} else {
|
||||
utils.White_B.Println("\n✅ Installed versions of ChaosCenter and LitmusCTL are compatible! ")
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigCmd.AddCommand(setAccountCmd)
|
||||
setAccountCmd.Flags().BoolP("non-interactive", "n", false, "Set it to true for non interactive mode | Note: Always set the boolean flag as --non-interactive=Boolean")
|
||||
setAccountCmd.Flags().StringP("endpoint", "e", "", "Account endpoint. Mandatory")
|
||||
setAccountCmd.Flags().StringP("username", "u", "", "Account username. Mandatory")
|
||||
setAccountCmd.Flags().StringP("password", "p", "", "Account password. Mandatory")
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/config"
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// useAccountCmd represents the useAccount command
|
||||
var useAccountCmd = &cobra.Command{
|
||||
Use: "use-account",
|
||||
Short: "Sets the current-account and current-username in a litmusconfig file",
|
||||
Long: `Sets the current-account and current-username in a litmusconfig file`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
configFilePath := utils.GetLitmusConfigPath(cmd)
|
||||
|
||||
endpoint, err := cmd.Flags().GetString("endpoint")
|
||||
utils.PrintError(err)
|
||||
|
||||
if endpoint == "" {
|
||||
utils.White_B.Print("\nHost endpoint where litmus is installed: ")
|
||||
fmt.Scanln(&endpoint)
|
||||
|
||||
for endpoint == "" {
|
||||
utils.Red.Println("\n⛔ Host URL can't be empty!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
username, err := cmd.Flags().GetString("username")
|
||||
utils.PrintError(err)
|
||||
|
||||
if username == "" {
|
||||
utils.White_B.Print("\nUsername: ")
|
||||
fmt.Scanln(&username)
|
||||
|
||||
for username == "" {
|
||||
utils.Red.Println("\n⛔ Username cannot be empty!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if username == "" || endpoint == "" {
|
||||
utils.Red.Println("endpoint or username is not set")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
exists := config.FileExists(configFilePath)
|
||||
|
||||
err = config.ConfigSyntaxCheck(configFilePath)
|
||||
utils.PrintError(err)
|
||||
|
||||
if exists {
|
||||
litmusconfig, err := config.YamltoObject(configFilePath)
|
||||
utils.PrintError(err)
|
||||
|
||||
isAccountExist := config.IsAccountExists(litmusconfig, username, endpoint)
|
||||
if isAccountExist {
|
||||
err = config.UpdateCurrent(types.Current{
|
||||
CurrentAccount: endpoint,
|
||||
CurrentUser: username,
|
||||
}, configFilePath)
|
||||
|
||||
utils.PrintError(err)
|
||||
fmt.Printf("\n✅ Successfully set the current account to '%s' at '%s'\n", username, endpoint)
|
||||
} else {
|
||||
utils.Red.Println("\n⛔ Account not exists")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
utils.Red.Println("\n⛔ File not exists")
|
||||
os.Exit(1)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigCmd.AddCommand(useAccountCmd)
|
||||
useAccountCmd.Flags().StringP("username", "u", "", "Help message for toggle")
|
||||
useAccountCmd.Flags().StringP("endpoint", "e", "", "Help message for toggle")
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// viewCmd represents the view command
|
||||
var viewCmd = &cobra.Command{
|
||||
Use: "view",
|
||||
Short: "Display litmusconfig settings or a specified litmusconfig file",
|
||||
Long: `Display litmusconfig settings or a specified litmusconfig file. `,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
configFilePath := utils.GetLitmusConfigPath(cmd)
|
||||
|
||||
exists := config.FileExists(configFilePath)
|
||||
if !exists {
|
||||
utils.Red.Println("File reading error open ", configFilePath, ": no such file or directory. Use --config or -c flag to point the configfile")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configFilePath)
|
||||
utils.PrintError(err)
|
||||
|
||||
//Printing the config map
|
||||
fmt.Print(string(data))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigCmd.AddCommand(viewCmd)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package connect
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// connectCmd represents the connect command
|
||||
var ConnectCmd = &cobra.Command{
|
||||
Use: "connect",
|
||||
Short: `Connect resources for LitmusChaos Execution plane.
|
||||
Examples:
|
||||
#connect a Chaos Infrastructure
|
||||
litmusctl connect chaos-infra --name="new-chaos-infra" --non-interactive
|
||||
|
||||
#connect a chaos-infrastructure within a project
|
||||
litmusctl connect chaos-infra --name="new-chaos-infra" --environment-id="my-environment-id" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package create
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// createCmd represents the create command
|
||||
var CreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: `Create resources for LitmusChaos Execution plane.
|
||||
Examples:
|
||||
#create a project
|
||||
litmusctl create project --name new-proj
|
||||
|
||||
#create a Chaos Experiment from a file
|
||||
litmusctl create chaos-experiment -f chaos-experiment.yaml --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --chaos-infra-id="d861b650-1549-4574-b2ba-ab754058dd04"
|
||||
|
||||
#create a Chaos Environment
|
||||
litmusctl create chaos-environment --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --name="new-chaos-environment"
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package create
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// projectCmd represents the project command
|
||||
var projectCmd = &cobra.Command{
|
||||
Use: "project",
|
||||
Short: `Create a project
|
||||
Example:
|
||||
#create a project
|
||||
litmusctl create project --name new-proj
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
credentials, err := utils.GetCredentials(cmd)
|
||||
utils.PrintError(err)
|
||||
|
||||
projectName, err := cmd.Flags().GetString("name")
|
||||
utils.PrintError(err)
|
||||
if projectName == "" {
|
||||
// prompt to ask project name
|
||||
prompt := promptui.Prompt{
|
||||
Label: "Enter a project name",
|
||||
AllowEdit: true,
|
||||
}
|
||||
|
||||
result, err := prompt.Run()
|
||||
if err != nil {
|
||||
utils.Red.Printf("Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
projectName = result
|
||||
}
|
||||
var response apis.CreateProjectResponse
|
||||
response, err = apis.CreateProjectRequest(projectName, credentials)
|
||||
if err != nil {
|
||||
utils.Red.Printf("❌ Error creating project: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Response: %+v\n", response)
|
||||
projectID := response.Data.ID
|
||||
utils.White_B.Printf("Project '%s' created successfully with project ID - '%s'!🎉\n", projectName, projectID)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
CreateCmd.AddCommand(projectCmd)
|
||||
projectCmd.Flags().String("name", "", "Set the project name to create it")
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package delete
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// deleteCmd represents the delete command
|
||||
var DeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: `Delete resources for LitmusChaos Execution plane.
|
||||
Examples:
|
||||
#delete a Chaos Experiment
|
||||
litmusctl delete chaos-experiment c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
|
||||
|
||||
#delete a Chaos Environment
|
||||
litmusctl delete chaos-environment --project-id=8adf62d5-64f8-4c66-ab53-63729db9dd9a --environment-id=environmentexample
|
||||
|
||||
#delete a Probe
|
||||
litmusctl delete probe --project-id=8adf62d5-64f8-4c66-ab53-63729db9dd9a --probe-id=exampleprobe
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package describe
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// DescribeCmd represents the describe command
|
||||
var DescribeCmd = &cobra.Command{
|
||||
Use: "describe",
|
||||
Short: `Describe resources for LitmusChaos Execution plane.
|
||||
Examples:
|
||||
#describe a Chaos Experiment
|
||||
litmusctl describe chaos-experiment d861b650-1549-4574-b2ba-ab754058dd04 --project-id="d861b650-1549-4574-b2ba-ab754058dd04"
|
||||
|
||||
#describe a Probe
|
||||
litmusctl describe probe --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --probe-id="exampleProbe"
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package disconnect
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// disconnectCmd represents the disconnect command
|
||||
var DisconnectCmd = &cobra.Command{
|
||||
Use: "disconnect",
|
||||
Short: `Disconnect resources for LitmusChaos Execution plane.
|
||||
Examples:
|
||||
#disconnect a Chaos Infra
|
||||
litmusctl disconnect chaos-infra c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package disconnect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis/infrastructure"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// infraCmd represents the infra command
|
||||
var infraCmd = &cobra.Command{
|
||||
Use: "chaos-infra",
|
||||
Short: `Disconnect a Chaos Infrastructure
|
||||
Example:
|
||||
#disconnect a Chaos Infrastructure
|
||||
litmusctl disconnect chaos-infra c520650e-7cb6-474c-b0f0-4df07b2b025b --project-id=c520650e-7cb6-474c-b0f0-4df07b2b025b
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
// Fetch user credentials
|
||||
credentials, err := utils.GetCredentials(cmd)
|
||||
utils.PrintError(err)
|
||||
|
||||
projectID, err := cmd.Flags().GetString("project-id")
|
||||
utils.PrintError(err)
|
||||
|
||||
// Handle blank input for project ID
|
||||
if projectID == "" {
|
||||
utils.White_B.Print("\nEnter the Project ID: ")
|
||||
fmt.Scanln(&projectID)
|
||||
|
||||
if projectID == "" {
|
||||
utils.Red.Println("⛔ Project ID can't be empty!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var infraID string
|
||||
if len(args) == 0 {
|
||||
utils.White_B.Print("\nEnter the Infra ID: ")
|
||||
fmt.Scanln(&infraID)
|
||||
} else {
|
||||
infraID = args[0]
|
||||
}
|
||||
// Handle blank input for Infra ID
|
||||
if infraID == "" {
|
||||
utils.Red.Println("⛔ Chaos Infra ID can't be empty!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Perform authorization
|
||||
userDetails, err := apis.GetProjectDetails(credentials)
|
||||
utils.PrintError(err)
|
||||
var editAccess = false
|
||||
var project apis.Project
|
||||
for _, p := range userDetails.Data.Projects {
|
||||
if p.ID == projectID {
|
||||
project = p
|
||||
}
|
||||
}
|
||||
for _, member := range project.Members {
|
||||
if (member.UserID == userDetails.Data.ID) && (member.Role == "Owner" || member.Role == "Editor") {
|
||||
editAccess = true
|
||||
}
|
||||
}
|
||||
if !editAccess {
|
||||
utils.Red.Println("⛔ User doesn't have edit access to the project!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Make API call
|
||||
disconnectedInfra, err := infrastructure.DisconnectInfra(projectID, infraID, credentials)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no documents in result") {
|
||||
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
utils.Red.Println("\n❌ Error in disconnecting Chaos Infrastructure: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(disconnectedInfra.Data.Message, "infra deleted successfully") {
|
||||
utils.White_B.Println("\n🚀 Chaos Infrastructure successfully disconnected.")
|
||||
} else {
|
||||
utils.White_B.Println("\n❌ Failed to disconnect Chaos Infrastructure. Please check if the ID is correct or not.")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
DisconnectCmd.AddCommand(infraCmd)
|
||||
|
||||
infraCmd.Flags().String("project-id", "", "Set the project-id to disconnect Chaos Infrastructure for the particular project. To see the projects, apply litmusctl get projects")
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package get
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// getCmd represents the get command
|
||||
var GetCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: `Examples:
|
||||
#get list of projects accessed by the user
|
||||
litmusctl get projects
|
||||
|
||||
#get list of Chaos Infrastructure within the project
|
||||
litmusctl get chaos-infra --project-id=""
|
||||
|
||||
#get list of chaos Chaos Experiments
|
||||
litmusctl get chaos-experiments --project-id=""
|
||||
|
||||
#get list of Chaos Experiment runs
|
||||
litmusctl get chaos-experiment-runs --project-id=""
|
||||
|
||||
#get list of Chaos Environments
|
||||
litmusctl get chaos-environments --project-id=""
|
||||
|
||||
#get list of Probes in a Project
|
||||
litmusctl get probes --project-id=""
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package get
|
||||
|
||||
import (
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// projectCmd represents the project command
|
||||
var projectsCmd = &cobra.Command{
|
||||
Use: "projects",
|
||||
Short: "Display list of projects",
|
||||
Long: `Display list of projects`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
credentials, err := utils.GetCredentials(cmd)
|
||||
utils.PrintError(err)
|
||||
|
||||
outputFormat, _ := cmd.Flags().GetString("output")
|
||||
|
||||
projects, err := apis.ListProject(credentials)
|
||||
utils.PrintError(err)
|
||||
|
||||
switch outputFormat {
|
||||
case "json":
|
||||
utils.PrintInJsonFormat(projects.Data)
|
||||
|
||||
case "yaml":
|
||||
utils.PrintInYamlFormat(projects.Data.Projects)
|
||||
|
||||
case "":
|
||||
itemsPerPage := 5
|
||||
page := 1
|
||||
totalProjects := len(projects.Data.Projects)
|
||||
|
||||
for {
|
||||
// calculating the start and end indices for the current page
|
||||
start := (page - 1) * itemsPerPage
|
||||
end := start + itemsPerPage
|
||||
if end > totalProjects {
|
||||
end = totalProjects
|
||||
|
||||
}
|
||||
// check if there are no more projects to display
|
||||
if start >= totalProjects {
|
||||
utils.Red.Println("No more projects to display")
|
||||
break
|
||||
}
|
||||
|
||||
// displaying the projects for the current page
|
||||
writer := tabwriter.NewWriter(os.Stdout, 8, 8, 8, '\t', tabwriter.AlignRight)
|
||||
utils.White_B.Fprintln(writer, "PROJECT ID\tPROJECT NAME\tCREATED AT")
|
||||
for _, project := range projects.Data.Projects[start:end] {
|
||||
intTime := project.CreatedAt
|
||||
humanTime := time.Unix(intTime/1000, 0) // Convert milliseconds to second
|
||||
utils.White.Fprintln(writer, project.ID+"\t"+project.Name+"\t"+humanTime.String()+"\t")
|
||||
}
|
||||
writer.Flush()
|
||||
|
||||
// pagination prompt
|
||||
paginationPrompt := promptui.Prompt{
|
||||
Label: "Press Enter to show the next page (or type 'q' to quit)",
|
||||
AllowEdit: true,
|
||||
Default: "",
|
||||
}
|
||||
|
||||
userInput, err := paginationPrompt.Run()
|
||||
utils.PrintError(err)
|
||||
|
||||
if userInput == "q" {
|
||||
break
|
||||
} else {
|
||||
page++
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
GetCmd.AddCommand(projectsCmd)
|
||||
|
||||
projectsCmd.Flags().StringP("output", "o", "", "Output format. One of:\njson|yaml")
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package litmusctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/version"
|
||||
|
||||
chaosAgent "github.com/litmuschaos/litmusctl/pkg/cmd/agent"
|
||||
chaosRegister "github.com/litmuschaos/litmusctl/pkg/cmd/agent/register"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "litmusctl",
|
||||
Short: "A brief description of your application",
|
||||
Long: `Litmusctl is a cli for managing Litmus resources.`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
// Run: func(cmd *cobra.Command, args []string) { },
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(version.VersionCmd)
|
||||
RootCmd.AddCommand(chaosAgent.AgentCmd)
|
||||
chaosAgent.AgentCmd.AddCommand(chaosRegister.RegisterCmd)
|
||||
|
||||
// Create a persistent flag for kubeconfig
|
||||
// RootCmd.PersistentFlags().StringP("kubeconfig", "k", "", "absolute path to the kubeconfig file")
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package rootCmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/run"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/save"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/update"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/connect"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/delete"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/describe"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/disconnect"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/upgrade"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/version"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/config"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/create"
|
||||
"github.com/litmuschaos/litmusctl/pkg/cmd/get"
|
||||
config2 "github.com/litmuschaos/litmusctl/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
|
||||
//var kubeconfig string
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "litmusctl",
|
||||
Short: "Litmusctl controls the litmuschaos agent plane",
|
||||
Long: `Litmusctl controls the litmuschaos agent plane. ` + "\n" + ` Find more information at: https://github.com/litmuschaos/litmusctl`,
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
cobra.CheckErr(rootCmd.Execute())
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.AddCommand(config.ConfigCmd)
|
||||
rootCmd.AddCommand(create.CreateCmd)
|
||||
rootCmd.AddCommand(get.GetCmd)
|
||||
rootCmd.AddCommand(connect.ConnectCmd)
|
||||
rootCmd.AddCommand(disconnect.DisconnectCmd)
|
||||
rootCmd.AddCommand(delete.DeleteCmd)
|
||||
rootCmd.AddCommand(describe.DescribeCmd)
|
||||
rootCmd.AddCommand(version.VersionCmd)
|
||||
rootCmd.AddCommand(upgrade.UpgradeCmd)
|
||||
rootCmd.AddCommand(save.SaveCmd)
|
||||
rootCmd.AddCommand(run.RunCmd)
|
||||
rootCmd.AddCommand(update.UpdateCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.litmusctl)")
|
||||
//rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "kubeconfig file (default is $HOME/.kube/config")
|
||||
rootCmd.PersistentFlags().BoolVar(&config2.SkipSSLVerify, "skipSSL", false, "skipSSL, litmusctl will skip ssl/tls verification while communicating with portal")
|
||||
rootCmd.PersistentFlags().StringVar(&config2.CACert, "cacert", "", "cacert <path_to_crt_file> , custom ca certificate used for communicating with portal")
|
||||
}
|
||||
|
||||
// initConfig reads in config file and ENV variables if set.
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := homedir.Dir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
// Search config in home directory with name ".litmusconfig" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.SetConfigName(utils.DefaultFileName)
|
||||
}
|
||||
|
||||
viper.AutomaticEnv() // read in environment variables that match
|
||||
|
||||
if config2.SkipSSLVerify {
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
} else if config2.CACert != "" {
|
||||
caCert, err := os.ReadFile(config2.CACert)
|
||||
cobra.CheckErr(err)
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{RootCAs: caCertPool}
|
||||
}
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
|
@ -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
|
||||
`,
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/apis"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// createCmd represents the create command
|
||||
var infraCmd = &cobra.Command{
|
||||
Use: "chaos-infra",
|
||||
Short: `Upgrades the LitmusChaos Execution plane.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
credentials, err := utils.GetCredentials(cmd)
|
||||
utils.PrintError(err)
|
||||
|
||||
projectID, err := cmd.Flags().GetString("project-id")
|
||||
utils.PrintError(err)
|
||||
|
||||
if projectID == "" {
|
||||
utils.White_B.Print("\nEnter the project ID: ")
|
||||
fmt.Scanln(&projectID)
|
||||
}
|
||||
|
||||
infraID, err := cmd.Flags().GetString("chaos-infra-id")
|
||||
utils.PrintError(err)
|
||||
|
||||
if infraID == "" {
|
||||
utils.White_B.Print("\nEnter the Chaos Infra ID: ")
|
||||
fmt.Scanln(&infraID)
|
||||
}
|
||||
|
||||
kubeconfig, err := cmd.Flags().GetString("kubeconfig")
|
||||
utils.PrintError(err)
|
||||
|
||||
output, err := apis.UpgradeInfra(context.Background(), credentials, projectID, infraID, kubeconfig)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no documents in result") {
|
||||
utils.Red.Println("❌ The specified Project ID or Chaos Infrastructure ID doesn't exist.")
|
||||
os.Exit(1)
|
||||
}
|
||||
utils.Red.Print("\n❌ Failed upgrading Chaos Infrastructure: \n" + err.Error() + "\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
utils.White_B.Print("\n", output)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
UpgradeCmd.AddCommand(infraCmd)
|
||||
infraCmd.Flags().String("project-id", "", "Enter the project ID")
|
||||
infraCmd.Flags().String("kubeconfig", "", "Enter the kubeconfig path(default: $HOME/.kube/config))")
|
||||
infraCmd.Flags().String("chaos-infra-id", "", "Enter the Chaos Infrastructure ID")
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package upgrade
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// UpgradeCmd represents the upgrade command
|
||||
var UpgradeCmd = &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: `Examples:
|
||||
#upgrade version of your Chaos Infrastructure
|
||||
litmusctl upgrade chaos-infra --chaos-infra-id="4cc25543-36c8-4373-897b-2e5dbbe87bcf" --project-id="d861b650-1549-4574-b2ba-ab754058dd04" --non-interactive
|
||||
|
||||
Note: The default location of the config file is $HOME/.litmusconfig, and can be overridden by a --config flag
|
||||
`,
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
/*
|
||||
Copyright © 2020 NAME HERE <EMAIL ADDRESS>
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -16,18 +16,124 @@ limitations under the License.
|
|||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
var VersionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Litmusctl version",
|
||||
Long: `Litmusctl cli version`,
|
||||
Short: "Displays the version of litmusctl",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Litmusctl version: ", constants.CLIVersion)
|
||||
cliVersion := os.Getenv("CLIVersion")
|
||||
if cliVersion == "" {
|
||||
utils.Red.Println("Error: CLIVersion environment variable is not set.")
|
||||
return
|
||||
}
|
||||
compatibilityArr := utils.CompatibilityMatrix[cliVersion]
|
||||
utils.White_B.Println("Litmusctl version: ", os.Getenv("CLIVersion"))
|
||||
utils.White_B.Println("Compatible ChaosCenter versions: ")
|
||||
utils.White_B.Print("[ ")
|
||||
for _, v := range compatibilityArr {
|
||||
utils.White_B.Print("'" + v + "' ")
|
||||
}
|
||||
utils.White_B.Print("]\n")
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Changes the version of litmusctl",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
updateVersion := args[0]
|
||||
homeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
var assetURL string = "https://litmusctl-production-bucket.s3.amazonaws.com/"
|
||||
var binaryName string = "litmusctl"
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
if runtime.GOARCH == "386" {
|
||||
binaryName += "-windows-386-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
} else if runtime.GOARCH == "amd64" {
|
||||
binaryName += "-windows-amd64-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
} else {
|
||||
binaryName += "-windows-arm64-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
}
|
||||
case "linux":
|
||||
if runtime.GOARCH == "arm64" {
|
||||
binaryName += "-linux-arm64-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
} else if runtime.GOARCH == "amd64" {
|
||||
binaryName += "-linux-amd64-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
} else if runtime.GOARCH == "arm" {
|
||||
binaryName += "-linux-arm-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
} else {
|
||||
binaryName += "-linux-386-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
}
|
||||
case "darwin":
|
||||
if runtime.GOARCH == "amd64" {
|
||||
binaryName += "-darwin-amd64-" + updateVersion + ".tar.gz"
|
||||
assetURL += binaryName
|
||||
}
|
||||
}
|
||||
|
||||
utils.White.Print("Downloading:\n")
|
||||
|
||||
resp2, err := http.Get(assetURL)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
}
|
||||
|
||||
tempFile, err := os.CreateTemp("", binaryName)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
_, err = io.Copy(tempFile, resp2.Body)
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
return
|
||||
}
|
||||
utils.White_B.Print("OK\n")
|
||||
|
||||
tempFile.Close()
|
||||
|
||||
tarCmd := exec.Command("tar", "xzf", tempFile.Name(), "-C", homeDir)
|
||||
tarCmd.Stdout = os.Stdout
|
||||
tarCmd.Stderr = os.Stderr
|
||||
|
||||
utils.White_B.Print("Extracting binary...\n")
|
||||
err = tarCmd.Run()
|
||||
if err != nil {
|
||||
utils.PrintError(err)
|
||||
return
|
||||
}
|
||||
|
||||
utils.White_B.Print("Binary extracted successfully\n")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
VersionCmd.AddCommand(UpdateCmd)
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package common
|
||||
|
||||
type Agent struct {
|
||||
AgentName string `json:"cluster_name"`
|
||||
Mode string
|
||||
Description string `json:"description,omitempty"`
|
||||
PlatformName string `json:"platform_name"`
|
||||
ProjectId string `json:"project_id"`
|
||||
ClusterType string `json:"cluster_type"`
|
||||
Namespace string
|
||||
ServiceAccount string
|
||||
NsExists bool
|
||||
SAExists bool
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func ApplyYaml(token string, cred Credentials, yamlPath string) (output string, err error) {
|
||||
path := fmt.Sprintf("%s/%s/%s.yaml", cred.Host, yamlPath, token)
|
||||
args := []string{"kubectl", "apply", "-f", path}
|
||||
stdout, err := exec.Command(args[0], args[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error: %v", err)
|
||||
}
|
||||
return string(stdout), err
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
package chaos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
util "github.com/litmuschaos/litmusctl/pkg/common"
|
||||
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
|
||||
|
||||
resty "github.com/go-resty/resty/v2"
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
)
|
||||
|
||||
type AgentRegistrationData struct {
|
||||
Errors []Errors `json:"errors"`
|
||||
Data AgentRegister `json:"data"`
|
||||
}
|
||||
|
||||
type Errors struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
}
|
||||
|
||||
type AgentRegister struct {
|
||||
UserAgentReg UserAgentReg `json:"userClusterReg"`
|
||||
}
|
||||
|
||||
type UserAgentReg struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
ClusterName string `json:"cluster_name"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// GetAgentDetails take details of agent as input
|
||||
func GetAgentDetails(pid string, t util.Token, cred util.Credentials) util.Agent {
|
||||
var newAgent util.Agent
|
||||
// Get agent name as input
|
||||
fmt.Println("\n🔗 Enter the details of the agent ----")
|
||||
// Label for goto statement in case of invalid agent name
|
||||
AGENT_NAME:
|
||||
fmt.Print("🤷 Agent Name: ")
|
||||
newAgent.AgentName = util.Scanner()
|
||||
if newAgent.AgentName == "" {
|
||||
fmt.Println("⛔ Agent name cannot be empty. Please enter a valid name.")
|
||||
goto AGENT_NAME
|
||||
}
|
||||
|
||||
// Check if agent with the given name already exists
|
||||
if AgentExists(pid, newAgent.AgentName, t, cred) {
|
||||
fmt.Println("🚫 Agent with the given name already exists.")
|
||||
// Print agent list if existing agent name is entered twice
|
||||
GetAgentList(pid, t, cred)
|
||||
fmt.Println("❗ Please enter a different name.")
|
||||
goto AGENT_NAME
|
||||
}
|
||||
// Get agent description as input
|
||||
fmt.Print("📘 Agent Description: ")
|
||||
newAgent.Description = util.Scanner()
|
||||
// Get platform name as input
|
||||
newAgent.PlatformName = util.GetPlatformName()
|
||||
// Set agent type
|
||||
newAgent.ClusterType = constants.AgentType
|
||||
// Set project id
|
||||
newAgent.ProjectId = pid
|
||||
// Get namespace
|
||||
newAgent.Namespace, newAgent.NsExists = k8s.ValidNs(constants.ChaosAgentLabel)
|
||||
|
||||
return newAgent
|
||||
}
|
||||
|
||||
type AgentData struct {
|
||||
Data AgentList `json:"data"`
|
||||
}
|
||||
type AgentDetails struct {
|
||||
AgentName string `json:"cluster_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
IsRegistered bool `json:"is_registered"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
}
|
||||
type AgentList struct {
|
||||
GetAgent []AgentDetails `json:"getCluster"`
|
||||
}
|
||||
|
||||
// AgentExists checks if an agent of the given name already exists
|
||||
func AgentExists(pid, agentName string, t util.Token, cred util.Credentials) bool {
|
||||
|
||||
var agents AgentData
|
||||
client := resty.New()
|
||||
bodyData := `{"query":"query{\n getCluster(project_id: \"` + fmt.Sprintf("%s", pid) + `\"){\n cluster_name\n }\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(bodyData).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&agents).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
cred.Host,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
fmt.Println("Error getting agent names: ", err)
|
||||
return true
|
||||
}
|
||||
for i := range agents.Data.GetAgent {
|
||||
if agentName == agents.Data.GetAgent[i].AgentName {
|
||||
fmt.Println(agents.Data.GetAgent[i].AgentName)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAgentList lists the agent connected to the specified project
|
||||
func GetAgentList(pid string, t util.Token, cred util.Credentials) {
|
||||
var agents AgentData
|
||||
client := resty.New()
|
||||
bodyData := `{"query":"query{\n getCluster(project_id: \"` + fmt.Sprintf("%s", pid) + `\"){\n cluster_name\n }\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(bodyData).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&agents).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
cred.Host,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
fmt.Println("Error in geting agent list: ", err)
|
||||
}
|
||||
fmt.Print("\n📘 Registered agents list -----------\n\n")
|
||||
|
||||
for i := range agents.Data.GetAgent {
|
||||
fmt.Println("-", agents.Data.GetAgent[i].AgentName)
|
||||
}
|
||||
fmt.Println("\n-------------------------------------")
|
||||
}
|
||||
|
||||
// RegisterAgent registers the agent with the given details
|
||||
func RegisterAgent(c util.Agent, t util.Token, cred util.Credentials) (AgentRegistrationData, error) {
|
||||
var cr AgentRegistrationData
|
||||
client := resty.New()
|
||||
bodyData := `{"query":"mutation {\n userClusterReg(clusterInput: \n { \n cluster_name: \"` + fmt.Sprintf("%s", c.AgentName) + `\", \n description: \"` + fmt.Sprintf("%s", c.Description) + `\",\n \tplatform_name: \"` + fmt.Sprintf("%s", c.PlatformName) + `\",\n project_id: \"` + fmt.Sprintf("%s", c.ProjectId) + `\",\n cluster_type: \"` + fmt.Sprintf("%s", c.ClusterType) + `\",\n agent_scope: \"` + fmt.Sprintf("%s", c.Mode) + `\",\n agent_namespace: \"` + fmt.Sprintf("%s", c.Namespace) + `\",\n serviceaccount: \"` + fmt.Sprintf("%s", c.ServiceAccount) + `\",\n agent_ns_exists: ` + fmt.Sprintf("%t", c.NsExists) + `,\n agent_sa_exists: ` + fmt.Sprintf("%t", c.SAExists) + `,\n }){\n cluster_id\n cluster_name\n token\n }\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(bodyData).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&cr).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
cred.Host,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
fmt.Println(err)
|
||||
fmt.Println(resp.IsSuccess())
|
||||
return AgentRegistrationData{}, err
|
||||
}
|
||||
return cr, nil
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package chaos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
)
|
||||
|
||||
// GetMode gets mode of agent installation as input
|
||||
func GetMode() string {
|
||||
var mode int
|
||||
fmt.Println("\n🔌 Installation Modes:\n1. Cluster\n2. Namespace")
|
||||
fmt.Print("\n👉 Select Mode [", constants.DefaultMode, "]: ")
|
||||
fmt.Scanln(&mode)
|
||||
if mode == 0 {
|
||||
return "namespace"
|
||||
}
|
||||
for mode < 1 || mode > 2 {
|
||||
fmt.Println("🚫 Invalid mode. Please enter the correct mode")
|
||||
fmt.Print("👉 Select Mode [", constants.DefaultMode, "]: ")
|
||||
fmt.Scanln(&mode)
|
||||
}
|
||||
if mode == 1 {
|
||||
return "cluster"
|
||||
}
|
||||
return constants.DefaultMode
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package chaos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
util "github.com/litmuschaos/litmusctl/pkg/common"
|
||||
|
||||
resty "github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type ProjectDetails struct {
|
||||
Data Data `json:"data"`
|
||||
}
|
||||
type Data struct {
|
||||
GetUser GetUser `json:"getUser"`
|
||||
}
|
||||
type GetUser struct {
|
||||
Projects []Project `json:"projects"`
|
||||
}
|
||||
type Project struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// GetProjectDetails fetches details of the input user
|
||||
func GetProjectDetails(t util.Token, c util.Credentials) (ProjectDetails, interface{}) {
|
||||
var user ProjectDetails
|
||||
|
||||
client := resty.New()
|
||||
bodyData := `{"query":"query {\n getUser(username: \"` + fmt.Sprintf("%s", c.Username) + `\"){\n projects{\n id\n name\n}\n}\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(bodyData).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&user).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
c.Host,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return ProjectDetails{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetProject display list of projects and returns the project id based on input
|
||||
func GetProject(u ProjectDetails) string {
|
||||
var pid int
|
||||
fmt.Println("\n✨ Projects List:")
|
||||
for index := range u.Data.GetUser.Projects {
|
||||
projectNo := index + 1
|
||||
fmt.Printf("%d. %s\n", projectNo, u.Data.GetUser.Projects[index].Name)
|
||||
}
|
||||
fmt.Print("\n🔎 Select Project: ")
|
||||
fmt.Scanln(&pid)
|
||||
for pid < 1 || pid > len(u.Data.GetUser.Projects) {
|
||||
fmt.Println("❗ Invalid Project. Please select a correct one.")
|
||||
fmt.Print("\n🔎 Select Project: ")
|
||||
fmt.Scanln(&pid)
|
||||
}
|
||||
pid = pid - 1
|
||||
return u.Data.GetUser.Projects[pid].ID
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package chaos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/common"
|
||||
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
)
|
||||
|
||||
func Register(t common.Token, c common.Credentials) {
|
||||
// Fetch project details
|
||||
user, uErr := GetProjectDetails(t, c)
|
||||
|
||||
if uErr != nil {
|
||||
fmt.Printf("\n❌ Fetching project details failed: [%s]", uErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
// Fetch project id
|
||||
pid := GetProject(user)
|
||||
|
||||
// Get mode of installation as input
|
||||
mode := GetMode()
|
||||
// Check if user has sufficient permissions based on mode
|
||||
fmt.Println("\n🏃 Running prerequisites check....")
|
||||
k8s.ValidateSAPermissions(mode)
|
||||
// Get agent details as input
|
||||
newAgent := GetAgentDetails(pid, t, c)
|
||||
newAgent.Mode = mode
|
||||
// Get service account as input
|
||||
newAgent.ServiceAccount, newAgent.SAExists = k8s.ValidSA(newAgent.Namespace)
|
||||
// Display details of agent to be connected
|
||||
common.Summary(newAgent, "chaos")
|
||||
// Confirm before connecting the agent
|
||||
common.Confirm()
|
||||
// Register agent
|
||||
agent, cerror := RegisterAgent(newAgent, t, c)
|
||||
if cerror != nil {
|
||||
fmt.Printf("\n❌ Agent registration failed: [%s]\n", cerror.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s/%s.yaml", c.Host, constants.ChaosYamlPath, agent.Data.UserAgentReg.Token)
|
||||
fmt.Println("Applying YAML:\n", path)
|
||||
|
||||
// Print error message in case Data field is null in response
|
||||
if (agent.Data == AgentRegister{}) {
|
||||
fmt.Printf("\n🚫 Agent registration failed: [%s]\n", agent.Errors[0].Message)
|
||||
os.Exit(1)
|
||||
}
|
||||
// Apply agent registration yaml
|
||||
yamlOutput, yerror := common.ApplyYaml(agent.Data.UserAgentReg.Token, c, constants.ChaosYamlPath)
|
||||
if yerror != nil {
|
||||
fmt.Printf("\n❌ Failed in applying registration yaml: [%s]\n", yerror.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("\n", yamlOutput)
|
||||
// Watch subscriber pod status
|
||||
k8s.WatchPod(newAgent.Namespace, constants.ChaosAgentLabel)
|
||||
fmt.Println("\n🚀 Agent Registration Successful!! 🎉")
|
||||
fmt.Println("👉 Litmus agents can be accessed here: " + fmt.Sprintf("%s/%s", c.Host, constants.ChaosAgentPath))
|
||||
}
|
|
@ -1,314 +0,0 @@
|
|||
package chaos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
|
||||
"github.com/go-resty/resty/v2"
|
||||
ymlparser "gopkg.in/yaml.v2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type ListPkgData struct {
|
||||
Data struct {
|
||||
ListHubPkgData []PackageData `json:"ListHubPkgData"`
|
||||
} `json:"data"`
|
||||
}
|
||||
type PackageData struct {
|
||||
Experiments []string `json:"Experiments"`
|
||||
ChartName string `json:"chartName"`
|
||||
}
|
||||
|
||||
type YAMLData struct {
|
||||
Data struct {
|
||||
GetYAMLData string `json:"getYAMLData"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type GenerateWorkflowInputs struct {
|
||||
HubName string
|
||||
ProjectID string
|
||||
ChartName string
|
||||
ExperimentName *string
|
||||
AccessToken string
|
||||
FileType *string
|
||||
URL *url.URL
|
||||
WorkName string
|
||||
WorkNamespace string
|
||||
ClusterID string
|
||||
Packages []*PackageData
|
||||
}
|
||||
|
||||
type GetClusters struct {
|
||||
Data struct {
|
||||
GetCluster []struct {
|
||||
ClusterID string `json:"cluster_id"`
|
||||
ClusterName string `json:"cluster_name"`
|
||||
} `json:"getCluster"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type GetHubStatus struct {
|
||||
Data struct {
|
||||
GetHubStatus []struct {
|
||||
ID string `json:"id"`
|
||||
HubName string `json:"HubName"`
|
||||
} `json:"getHubStatus"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
func GetYamlData(inputs GenerateWorkflowInputs) (YAMLData, error) {
|
||||
client := resty.New()
|
||||
|
||||
var yamlDataResponse YAMLData
|
||||
gql_query := `{"query":"query {\n getYAMLData(experimentInput: {\n ProjectID: \"` + inputs.ProjectID + `\"\n HubName: \"` + inputs.HubName + `\"\n ChartName: \"` + inputs.ChartName + `\"\n ExperimentName: \"` + *inputs.ExperimentName + `\"\n FileType: \"` + *inputs.FileType + `\"\n \n })\n}"}`
|
||||
response, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", inputs.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(gql_query).
|
||||
SetResult(&yamlDataResponse).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
inputs.URL,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil || !response.IsSuccess() {
|
||||
return YAMLData{}, err
|
||||
}
|
||||
|
||||
return yamlDataResponse, nil
|
||||
}
|
||||
|
||||
func GenerateWorkflow(wf_inputs GenerateWorkflowInputs) ([]byte, error) {
|
||||
|
||||
var yaml v1alpha1.Workflow
|
||||
|
||||
yaml.APIVersion = "argoproj.io/v1alpha1"
|
||||
yaml.Kind = "Workflow"
|
||||
yaml.ObjectMeta.Name = wf_inputs.WorkName
|
||||
yaml.ObjectMeta.Namespace = wf_inputs.WorkNamespace
|
||||
yaml.ObjectMeta.Labels = map[string]string{
|
||||
"cluster_id": wf_inputs.ClusterID,
|
||||
}
|
||||
|
||||
var pram v1alpha1.Parameter
|
||||
pram.Name = "adminModeNamespace"
|
||||
pram.Value = &wf_inputs.WorkNamespace
|
||||
yaml.Spec.Arguments.Parameters = append(yaml.Spec.Arguments.Parameters, pram)
|
||||
//
|
||||
yaml.Spec.Entrypoint = "custom-chaos"
|
||||
var b = true
|
||||
var i int64 = 1000
|
||||
yaml.Spec.SecurityContext = &v1.PodSecurityContext{
|
||||
RunAsNonRoot: &b,
|
||||
RunAsUser: &i,
|
||||
}
|
||||
|
||||
var (
|
||||
custom_chaos v1alpha1.Template
|
||||
install_experiments v1alpha1.Template
|
||||
engines []v1alpha1.Template
|
||||
revert_chaos v1alpha1.Template
|
||||
)
|
||||
|
||||
custom_chaos.Name = "custom-chaos"
|
||||
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
|
||||
{
|
||||
Name: "install-chaos-experiments",
|
||||
Template: "install-chaos-experiments",
|
||||
},
|
||||
}})
|
||||
|
||||
install_experiments.Name = "install-chaos-experiments"
|
||||
install_experiments.Container = &v1.Container{
|
||||
Image: "lachlanevenson/k8s-kubectl",
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{""},
|
||||
}
|
||||
|
||||
revert_chaos.Name = "revert-chaos"
|
||||
revert_chaos.Container = &v1.Container{
|
||||
Image: "lachlanevenson/k8s-kubectl",
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{"kubectl delete chaosengine "},
|
||||
}
|
||||
|
||||
for _, pkg := range wf_inputs.Packages {
|
||||
|
||||
for _, experiment := range pkg.Experiments {
|
||||
|
||||
wf_inputs.ExperimentName = &experiment
|
||||
|
||||
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
|
||||
{
|
||||
Name: experiment,
|
||||
Template: experiment,
|
||||
},
|
||||
}})
|
||||
//
|
||||
var file_type = "experiment"
|
||||
wf_inputs.FileType = &file_type
|
||||
yamlData, err := GetYamlData(wf_inputs)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
install_experiments.Inputs.Artifacts = append(install_experiments.Inputs.Artifacts,
|
||||
v1alpha1.Artifact{
|
||||
Name: experiment,
|
||||
Path: "/tmp/" + experiment + ".yaml",
|
||||
ArtifactLocation: v1alpha1.ArtifactLocation{
|
||||
Raw: &v1alpha1.RawArtifact{
|
||||
Data: fmt.Sprint(yamlData.Data.GetYAMLData),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
install_experiments.Container.Args[0] += "kubectl apply -f /tmp/" + experiment + ".yaml" + "-n {{workflow.parameters.adminModeNamespace}} | "
|
||||
|
||||
revert_chaos.Container.Args[0] += experiment + " "
|
||||
|
||||
file_type = "engine"
|
||||
wf_inputs.FileType = &file_type
|
||||
|
||||
yamlData, err = GetYamlData(wf_inputs)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
var engine v1alpha1.Template
|
||||
engine.Name = experiment
|
||||
engine.Container = &v1.Container{
|
||||
Args: []string{
|
||||
`-file=/tmp/chaosengine-` + experiment + `.yaml`,
|
||||
"-saveName=/tmp/engine-name",
|
||||
},
|
||||
Image: "litmuschaos/litmus-checker:latest",
|
||||
}
|
||||
|
||||
engine.Inputs.Artifacts = append(engine.Inputs.Artifacts, v1alpha1.Artifact{
|
||||
Name: experiment,
|
||||
Path: "/tmp/chaosengine-" + experiment + ".yaml",
|
||||
ArtifactLocation: v1alpha1.ArtifactLocation{
|
||||
Raw: &v1alpha1.RawArtifact{
|
||||
Data: fmt.Sprint(yamlData.Data.GetYAMLData),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
engines = append(engines, engine)
|
||||
}
|
||||
}
|
||||
|
||||
// Custom chaos
|
||||
custom_chaos.Steps = append(custom_chaos.Steps, v1alpha1.ParallelSteps{Steps: []v1alpha1.WorkflowStep{
|
||||
{
|
||||
Name: "revert-chaos",
|
||||
Template: "revert-chaos",
|
||||
},
|
||||
}})
|
||||
|
||||
yaml.Spec.Templates = append(yaml.Spec.Templates, custom_chaos)
|
||||
|
||||
// Install experiments
|
||||
install_experiments.Container.Args[0] += "sleep 30"
|
||||
yaml.Spec.Templates = append(yaml.Spec.Templates, install_experiments)
|
||||
//
|
||||
// Install engines
|
||||
yaml.Spec.Templates = append(yaml.Spec.Templates, engines...)
|
||||
//
|
||||
// Revert Chaos
|
||||
revert_chaos.Container.Args[0] += "-n {{workflow.parameters.adminModeNamespace}}"
|
||||
yaml.Spec.Templates = append(yaml.Spec.Templates, revert_chaos)
|
||||
|
||||
yamlByte, err := ymlparser.Marshal(yaml)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return yamlByte, nil
|
||||
}
|
||||
|
||||
func GetClustersQuery(project_id string, access_token string, url *url.URL) (GetClusters, error) {
|
||||
client := resty.New()
|
||||
|
||||
var getClusters GetClusters
|
||||
gql_query := `{"query":"query {\n getCluster(project_id: \"` + project_id + `\"){\n cluster_id\n cluster_name\n }\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(gql_query).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&getClusters).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
url,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return GetClusters{}, err
|
||||
}
|
||||
|
||||
return getClusters, nil
|
||||
}
|
||||
|
||||
func GetHubStatusQuery(project_id string, access_token string, url *url.URL) (GetHubStatus, error) {
|
||||
client := resty.New()
|
||||
|
||||
var getHubStatus GetHubStatus
|
||||
gql_query := `{"query":"query {\n getHubStatus(projectID: \"` + project_id + `\"){\n id\n HubName \n }\n}"}`
|
||||
response, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(gql_query).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&getHubStatus).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
url,
|
||||
),
|
||||
)
|
||||
if err != nil || !response.IsSuccess() {
|
||||
return GetHubStatus{}, nil
|
||||
}
|
||||
|
||||
return getHubStatus, nil
|
||||
}
|
||||
|
||||
func ListPkgDataQuery(project_id string, hub_id string, access_token string, url *url.URL) (ListPkgData, error) {
|
||||
var pkgdata ListPkgData
|
||||
|
||||
client := resty.New()
|
||||
|
||||
gql_query := `{"query":"query {\n ListHubPkgData(projectID: \"` + project_id + `\", hubID: \"` + hub_id + `\"){\n Experiments\n chartName\n }\n}"}`
|
||||
response, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", access_token)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(gql_query).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&pkgdata).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
url,
|
||||
),
|
||||
)
|
||||
if err != nil || !response.IsSuccess() {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
return pkgdata, nil
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package common
|
||||
|
||||
type Errors struct {
|
||||
Message string `json:"message"`
|
||||
Path []string `json:"path"`
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
func GetUsername() string {
|
||||
var username string
|
||||
fmt.Print("🤔 Username [", constants.DefaultUsername, "]: ")
|
||||
fmt.Scanln(&username)
|
||||
if username == "" {
|
||||
username = constants.DefaultUsername
|
||||
}
|
||||
return username
|
||||
}
|
||||
|
||||
func GetPortalURL() (*url.URL, error) {
|
||||
var host string
|
||||
fmt.Print("👉 Host URL where litmus is installed: ")
|
||||
fmt.Scanln(&host)
|
||||
for host == "" {
|
||||
fmt.Println("⛔ Host URL can't be empty!!")
|
||||
fmt.Print("👉 Host URL: ")
|
||||
fmt.Scanln(&host)
|
||||
}
|
||||
host = strings.TrimRight(host, "/")
|
||||
newUrl, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return &url.URL{}, err
|
||||
}
|
||||
return newUrl, nil
|
||||
}
|
||||
|
||||
func GetPassword() []byte {
|
||||
var pass []byte
|
||||
fmt.Print("🙈 Password: ")
|
||||
pass, _ = terminal.ReadPassword(0)
|
||||
if pass == nil {
|
||||
fmt.Println("\n⛔ Password cannot be empty!")
|
||||
os.Exit(1)
|
||||
}
|
||||
return pass
|
||||
}
|
||||
|
||||
// GetMode gets mode of agent installation as input
|
||||
func GetMode() string {
|
||||
var mode int
|
||||
fmt.Println("\n🔌 Installation Modes:\n1. Cluster\n2. Namespace")
|
||||
fmt.Print("\n👉 Select Mode [", constants.DefaultMode, "]: ")
|
||||
fmt.Scanln(&mode)
|
||||
if mode == 0 {
|
||||
return "namespace"
|
||||
}
|
||||
for mode < 1 || mode > 2 {
|
||||
fmt.Println("🚫 Invalid mode. Please enter the correct mode")
|
||||
fmt.Print("👉 Select Mode [", constants.DefaultMode, "]: ")
|
||||
fmt.Scanln(&mode)
|
||||
}
|
||||
if mode == 1 {
|
||||
return "cluster"
|
||||
}
|
||||
return constants.DefaultMode
|
||||
}
|
||||
|
||||
func Confirm() {
|
||||
var wish string
|
||||
fmt.Print("\n🤷 Do you want to continue with the above details? [Y/N]: ")
|
||||
fmt.Scanln(&wish)
|
||||
if wish == "Y" || wish == "Yes" || wish == "yes" || wish == "y" {
|
||||
fmt.Println("👍 Continuing agent registration!!")
|
||||
} else {
|
||||
fmt.Println("✋ Exiting agent registration!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// getPlatformName displays a list of platforms, takes the
|
||||
// platform name as input and returns the selected platform
|
||||
//
|
||||
// TODO --
|
||||
// - Entering any character other than numbers returns 0. Input validation need to be done.
|
||||
// - If input is given as "123abc", "abc" will be used for next user input. Buffer need to be read completely.
|
||||
// - String literals like "AWS" are used at multiple places. Need to be changed to constants.
|
||||
func GetPlatformName() string {
|
||||
var platform int
|
||||
discoveredPlatform := DiscoverPlatform()
|
||||
fmt.Println("📦 Platform List")
|
||||
fmt.Println(constants.PlatformList)
|
||||
fmt.Print("🔎 Select Platform [", discoveredPlatform, "]: ")
|
||||
fmt.Scanln(&platform)
|
||||
switch platform {
|
||||
case 0:
|
||||
return discoveredPlatform
|
||||
case 1:
|
||||
return "AWS"
|
||||
case 2:
|
||||
return "GKE"
|
||||
case 3:
|
||||
return "Openshift"
|
||||
case 4:
|
||||
return "Rancher"
|
||||
default:
|
||||
return constants.DefaultPlatform
|
||||
}
|
||||
}
|
||||
|
||||
func Scanner() string {
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
return scanner.Text()
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "reading standard input:", err)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Summary display the agent details based on input
|
||||
func Summary(agent Agent, product string) {
|
||||
fmt.Println("\n📌 Summary --------------------------")
|
||||
fmt.Println("\nAgent Name: ", agent.AgentName)
|
||||
fmt.Println("Agent Description: ", agent.Description)
|
||||
fmt.Println("Platform Name: ", agent.PlatformName)
|
||||
if ok, _ := k8s.NsExists(agent.Namespace); ok {
|
||||
fmt.Println("Namespace: ", agent.Namespace)
|
||||
} else {
|
||||
fmt.Println("Namespace: ", agent.Namespace, "(new)")
|
||||
}
|
||||
if product == "chaos" {
|
||||
if k8s.SAExists(agent.Namespace, agent.ServiceAccount) {
|
||||
fmt.Println("Service Account: ", agent.ServiceAccount)
|
||||
} else {
|
||||
fmt.Println("Service Account: ", agent.ServiceAccount, "(new)")
|
||||
}
|
||||
fmt.Println("Installation Mode: ", agent.Mode)
|
||||
}
|
||||
fmt.Println("\n-------------------------------------")
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
discovery "k8s.io/client-go/discovery"
|
||||
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||
)
|
||||
|
||||
type CanIOptions struct {
|
||||
NoHeaders bool
|
||||
Namespace string
|
||||
AuthClient authorizationv1client.AuthorizationV1Interface
|
||||
DiscoveryClient discovery.DiscoveryInterface
|
||||
|
||||
Verb string
|
||||
Resource schema.GroupVersionResource
|
||||
Subresource string
|
||||
ResourceName string
|
||||
}
|
||||
|
||||
func CheckSAPermissions(verb, resource string, print bool) (bool, error) {
|
||||
|
||||
var o CanIOptions
|
||||
o.Verb = verb
|
||||
o.Resource.Resource = resource
|
||||
client, err := ClientSet()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
AuthClient := client.AuthorizationV1()
|
||||
|
||||
var sar *authorizationv1.SelfSubjectAccessReview
|
||||
sar = &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: o.Namespace,
|
||||
Verb: o.Verb,
|
||||
Group: o.Resource.Group,
|
||||
Resource: o.Resource.Resource,
|
||||
Subresource: o.Subresource,
|
||||
Name: o.ResourceName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := AuthClient.SelfSubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if response.Status.Allowed {
|
||||
if print {
|
||||
fmt.Println("🔑 ", resource, "- ✅")
|
||||
}
|
||||
} else {
|
||||
if print {
|
||||
fmt.Println("🔑 ", resource, "- ❌")
|
||||
}
|
||||
if len(response.Status.Reason) > 0 {
|
||||
fmt.Println(response.Status.Reason)
|
||||
}
|
||||
if len(response.Status.EvaluationError) > 0 {
|
||||
fmt.Println(response.Status.EvaluationError)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Status.Allowed, nil
|
||||
}
|
||||
|
||||
func ValidateSAPermissions(mode string) {
|
||||
var pems [2]bool
|
||||
var err error
|
||||
if mode == "cluster" {
|
||||
resources := [2]string{"clusterrole", "clusterrolebinding"}
|
||||
i := 0
|
||||
for _, resource := range resources {
|
||||
pems[i], err = CheckSAPermissions("create", resource, true)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
resources := [2]string{"role", "rolebinding"}
|
||||
i := 0
|
||||
for _, resource := range resources {
|
||||
pems[i], err = CheckSAPermissions("create", resource, true)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
for _, pem := range pems {
|
||||
if !pem {
|
||||
fmt.Println("\n🚫 You don't have sufficient permissions.\n🙄 Please use a service account with sufficient permissions.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
fmt.Println("\n🌟 Sufficient permissions. Registering Agent")
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
// Returns a new kubernetes client set
|
||||
func ClientSet() (*kubernetes.Clientset, error) {
|
||||
var kubeconfig *string
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kcfg := filepath.Join(home, ".kube", "config")
|
||||
kubeconfig = &kcfg
|
||||
} else {
|
||||
fmt.Println("ERROR: Clientset generation failed!")
|
||||
os.Exit(1)
|
||||
}
|
||||
// create the config
|
||||
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
// create the clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
fmt.Println("ERROR: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return clientset, err
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
k8serror "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NsExists checks if the given namespace already exists
|
||||
func NsExists(namespace string) (bool, error) {
|
||||
clientset, err := ClientSet()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
|
||||
if k8serror.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err == nil && ns != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// ValidNs takes a valid namespace as input from user
|
||||
func ValidNs(label string) (string, bool) {
|
||||
var namespace string
|
||||
var nsExists bool
|
||||
fmt.Print("📁 Enter the namespace (new or existing) [", constants.DefaultNs, "]: ")
|
||||
fmt.Scanln(&namespace)
|
||||
if namespace == "" {
|
||||
namespace = constants.DefaultNs
|
||||
}
|
||||
ok, err := NsExists(namespace)
|
||||
if err != nil {
|
||||
fmt.Printf("\n Namespace existence check failed: {%s}\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if ok {
|
||||
if PodExists(namespace, label) {
|
||||
fmt.Println("🚫 Subscriber already present. Please enter a different namespace")
|
||||
namespace, nsExists = ValidNs(label)
|
||||
} else {
|
||||
nsExists = true
|
||||
fmt.Println("👍 Continuing with", namespace, "namespace")
|
||||
}
|
||||
} else {
|
||||
if val, _ := CheckSAPermissions("create", "namespace", false); !val {
|
||||
fmt.Println("🚫 You don't have permissions to create a namespace.\n🙄 Please enter an existing namespace.")
|
||||
namespace, nsExists = ValidNs(label)
|
||||
}
|
||||
nsExists = false
|
||||
}
|
||||
return namespace, nsExists
|
||||
}
|
||||
|
||||
// CreateNs creates the given namespace
|
||||
func CreateNs(namespace string) {
|
||||
clientset, err := ClientSet()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
nsSpec := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s", namespace)}}
|
||||
_, newErr := clientset.CoreV1().Namespaces().Create(context.TODO(), nsSpec, metav1.CreateOptions{})
|
||||
if newErr != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
fmt.Println(namespace, "namespace created successfully")
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// WatchPod watches for the pod status
|
||||
func WatchPod(namespace, label string) {
|
||||
clientset, err := ClientSet()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
watch, err := clientset.CoreV1().Pods(namespace).Watch(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: label,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
for event := range watch.ResultChan() {
|
||||
p, ok := event.Object.(*v1.Pod)
|
||||
if !ok {
|
||||
log.Fatal("unexpected type")
|
||||
}
|
||||
fmt.Println("💡 Connecting agent to Litmus Portal.")
|
||||
if p.Status.Phase == "Running" {
|
||||
fmt.Println("🏃 Agents running!!")
|
||||
watch.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PodList struct {
|
||||
Items []string
|
||||
}
|
||||
|
||||
// PodExists checks if the pod with the given label already exists in the given namespace
|
||||
func PodExists(namespace, label string) bool {
|
||||
clientset, err := ClientSet()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
watch, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: label,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if len(watch.Items) >= 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// SAExists checks if the given service account exists in the given namespace
|
||||
func SAExists(namespace, serviceaccount string) bool {
|
||||
clientset, err := ClientSet()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
msg := fmt.Sprintf("serviceaccounts \"%s\" not found", serviceaccount)
|
||||
_, newErr := clientset.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), serviceaccount, metav1.GetOptions{})
|
||||
if newErr != nil {
|
||||
if newErr.Error() == msg {
|
||||
return false
|
||||
}
|
||||
log.Fatal(newErr)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidSA gets a valid service account as input
|
||||
func ValidSA(namespace string) (string, bool) {
|
||||
var sa string
|
||||
fmt.Print("🔑 Enter service account [", constants.DefaultSA, "]: ")
|
||||
fmt.Scanln(&sa)
|
||||
if sa == "" {
|
||||
sa = constants.DefaultSA
|
||||
}
|
||||
if SAExists(namespace, sa) {
|
||||
fmt.Println("👍 Using the existing service account")
|
||||
return sa, true
|
||||
}
|
||||
return sa, false
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/common/k8s"
|
||||
"github.com/litmuschaos/litmusctl/pkg/constants"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// discoverPlatform determines the host platform and returns it
|
||||
func DiscoverPlatform() string {
|
||||
if ok, _ := IsAWSPlatform(); ok {
|
||||
return "AWS"
|
||||
}
|
||||
if ok, _ := IsGKEPlatform(); ok {
|
||||
return "GKE"
|
||||
}
|
||||
if ok, _ := IsOpenshiftPlatform(); ok {
|
||||
return "Openshift"
|
||||
}
|
||||
if ok, _ := k8s.NsExists("cattle-system"); ok {
|
||||
return "Rancher"
|
||||
}
|
||||
return constants.DefaultPlatform
|
||||
}
|
||||
|
||||
// IsAWSPlatform determines if the host platform is AWS
|
||||
// by checking the ProviderID inside node spec
|
||||
//
|
||||
// Sample node custom resource of an AWS node
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// ....
|
||||
// "spec": {
|
||||
// "providerID": "aws:///us-east-2b/i-0bf24d83f4b993738"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func IsAWSPlatform() (bool, error) {
|
||||
clientset, err := k8s.ClientSet()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, constants.AWSIdentifier) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsGKEPlatform determines if the host platform is GKE
|
||||
// by checking the ProviderID inside node spec
|
||||
//
|
||||
// Sample node custom resource of an GKE node
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// ....
|
||||
// "spec": {
|
||||
// "providerID": ""
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func IsGKEPlatform() (bool, error) {
|
||||
clientset, err := k8s.ClientSet()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, constants.GKEIdentifier) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsOpenshiftPlatform determines if the host platform
|
||||
// is Openshift by checking "node.openshift.io/os_id"
|
||||
// label on the nodes
|
||||
//
|
||||
// Sample node custom resource of an Openshift node
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// "metadata": {
|
||||
// "labels": {
|
||||
// "node.openshift.io/os_id": "rhcos"
|
||||
// }
|
||||
// }
|
||||
// ....
|
||||
// }
|
||||
func IsOpenshiftPlatform() (bool, error) {
|
||||
clientset, err := k8s.ClientSet()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{
|
||||
LabelSelector: constants.OpenshiftIdentifier,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(nodeList.Items) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
resty "github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type LaunchProductResponse struct {
|
||||
Data LaunchProductData `json:"data"`
|
||||
}
|
||||
type LaunchProductData struct {
|
||||
LaunchProduct string `json:"launchProduct"`
|
||||
}
|
||||
|
||||
// GetUserDetails fetches details of the input user
|
||||
func LaunchProduct(t Token, c Credentials, Product string) (LaunchProductResponse, error) {
|
||||
var new LaunchProductResponse
|
||||
client := resty.New()
|
||||
bodyData := `{"query":"query{\n launchProduct(type: ` + fmt.Sprintf("%s", Product) + `)\n}"}`
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", fmt.Sprintf("%s", t.AccessToken)).
|
||||
SetHeader("Accept-Encoding", "gzip, deflate, br").
|
||||
SetBody(bodyData).
|
||||
// SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&new).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/api/query",
|
||||
c.Host,
|
||||
),
|
||||
)
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return LaunchProductResponse{}, err
|
||||
}
|
||||
|
||||
return new, nil
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
resty "github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type Cred struct {
|
||||
Username string
|
||||
Token string
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
Host *url.URL
|
||||
Username string
|
||||
Password []byte
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type AuthError struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
// getToken fetches JWT token for the entered user credentials
|
||||
func getToken(c Credentials, path string) (Token, AuthError) {
|
||||
|
||||
var authErr AuthError
|
||||
client := resty.New()
|
||||
token := Token{}
|
||||
bodyData := map[string]interface{}{
|
||||
"username": fmt.Sprintf("%s", c.Username),
|
||||
"password": fmt.Sprintf("%s", c.Password),
|
||||
}
|
||||
resp, err := client.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(bodyData).
|
||||
//SetResult automatic unmarshalling for the request,
|
||||
// if response status code is between 200 and 299
|
||||
SetResult(&token).
|
||||
SetError(&authErr).
|
||||
Post(
|
||||
fmt.Sprintf(
|
||||
"%s/%s",
|
||||
c.Host,
|
||||
path,
|
||||
),
|
||||
)
|
||||
|
||||
if err != nil || !resp.IsSuccess() {
|
||||
return Token{}, authErr
|
||||
}
|
||||
|
||||
return token, AuthError{}
|
||||
}
|
||||
|
||||
func Login(c Credentials, path string) Token {
|
||||
t, err := getToken(c, path)
|
||||
if err.Error != "" || t.AccessToken == "" {
|
||||
fmt.Println("\nError: ", err.ErrorDescription, ":", err.Error)
|
||||
fmt.Println("❌ Login Failed!!")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("\n✅ Login Successful!")
|
||||
return t
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/types"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
SkipSSLVerify bool = false
|
||||
CACert string = ""
|
||||
)
|
||||
|
||||
func CreateNewLitmusCtlConfig(filename string, config types.LitmuCtlConfig) error {
|
||||
|
||||
configByte, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(filename, configByte, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func FileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func GetFileLength(filename string) (int, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
return len(string(data)), nil
|
||||
}
|
||||
|
||||
func YamltoObject(filename string) (types.LitmuCtlConfig, error) {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return types.LitmuCtlConfig{}, errors.New("File reading error " + err.Error())
|
||||
}
|
||||
|
||||
obj := &types.LitmuCtlConfig{}
|
||||
err = yaml.Unmarshal(data, obj)
|
||||
if err != nil {
|
||||
return types.LitmuCtlConfig{}, errors.New("File format not correct " + err.Error())
|
||||
}
|
||||
|
||||
return *obj, nil
|
||||
}
|
||||
|
||||
func ConfigSyntaxCheck(filename string) error {
|
||||
|
||||
obj, err := YamltoObject(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if obj.APIVersion != "v1" || obj.Kind != "Config" {
|
||||
return errors.New("File format not correct")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateLitmusCtlConfig(litmusconfig types.UpdateLitmusCtlConfig, filename string) error {
|
||||
obj, err := YamltoObject(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var outerflag = false
|
||||
for i, act := range obj.Accounts {
|
||||
if act.Endpoint == litmusconfig.Account.Endpoint {
|
||||
var innerflag = false
|
||||
obj.Accounts[i].ServerEndpoint = litmusconfig.ServerEndpoint
|
||||
for j, user := range act.Users {
|
||||
if user.Username == litmusconfig.Account.Users[0].Username {
|
||||
obj.Accounts[i].Users[j].Username = litmusconfig.Account.Users[0].Username
|
||||
obj.Accounts[i].Users[j].Token = litmusconfig.Account.Users[0].Token
|
||||
obj.Accounts[i].Users[j].ExpiresIn = litmusconfig.Account.Users[0].ExpiresIn
|
||||
innerflag, outerflag = true, true
|
||||
}
|
||||
}
|
||||
|
||||
if !innerflag {
|
||||
obj.Accounts[i].Users = append(obj.Accounts[i].Users, litmusconfig.Account.Users[0])
|
||||
outerflag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !outerflag {
|
||||
obj.Accounts = append(obj.Accounts, litmusconfig.Account)
|
||||
}
|
||||
|
||||
obj.CurrentAccount = litmusconfig.CurrentAccount
|
||||
obj.CurrentUser = litmusconfig.CurrentUser
|
||||
|
||||
err = writeObjToFile(obj, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateCurrent(current types.Current, filename string) error {
|
||||
obj, err := YamltoObject(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obj.CurrentUser = current.CurrentUser
|
||||
obj.CurrentAccount = current.CurrentAccount
|
||||
|
||||
err = writeObjToFile(obj, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeObjToFile(obj types.LitmuCtlConfig, filename string) error {
|
||||
_, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
byteObj, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(filename, byteObj, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsAccountExists(obj types.LitmuCtlConfig, username string, endpoint string) bool {
|
||||
for _, account := range obj.Accounts {
|
||||
if account.Endpoint == endpoint {
|
||||
for _, user := range account.Users {
|
||||
if username == user.Username {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package constants
|
||||
|
||||
const (
|
||||
|
||||
// CLI version
|
||||
CLIVersion = "v0.2.0"
|
||||
|
||||
// Default username
|
||||
DefaultUsername = "admin"
|
||||
|
||||
// Default installation mode
|
||||
DefaultMode = "cluster"
|
||||
|
||||
// Platform list
|
||||
PlatformList = "1. AWS\n2. GKE\n3. Openshift\n4. Rancher\n5. Others"
|
||||
|
||||
// AWS identifier
|
||||
AWSIdentifier = "aws://"
|
||||
|
||||
// GKE identifier
|
||||
GKEIdentifier = "gce://"
|
||||
|
||||
// Openshift identifier
|
||||
OpenshiftIdentifier = "node.openshift.io/os_id"
|
||||
|
||||
// Default platform name
|
||||
DefaultPlatform = "Others"
|
||||
|
||||
// Label of subscriber agent being deployed
|
||||
ChaosAgentLabel = "app=subscriber"
|
||||
|
||||
// Agent type is "external" for agents connected via litmusctl
|
||||
AgentType = "external"
|
||||
|
||||
// Default namespace for agent installation
|
||||
DefaultNs = "litmus"
|
||||
|
||||
// Default service account used for agent installation
|
||||
DefaultSA = "litmus"
|
||||
|
||||
// Chaos agent registration yaml path
|
||||
//ChaosYamlPath = "chaos/api/graphql/file"
|
||||
ChaosYamlPath = "api/file"
|
||||
|
||||
ChaosAgentPath = "targets"
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package infra_ops
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/k8s"
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// - Entering any character other than numbers returns 0. Input validation need to be done.
|
||||
// - If input is given as "123abc", "abc" will be used for next user input. Buffer need to be read completely.
|
||||
// - String literals like "AWS" are used at multiple places. Need to be changed to constants.
|
||||
func GetPlatformName(kubeconfig *string) string {
|
||||
var platform int
|
||||
discoveredPlatform := DiscoverPlatform(kubeconfig)
|
||||
utils.White_B.Println("\nPlatform List: ")
|
||||
utils.White_B.Println(utils.PlatformList)
|
||||
utils.White_B.Print("\nSelect a platform [Default: ", discoveredPlatform, "] [Range: 1-5]: ")
|
||||
fmt.Scanln(&platform)
|
||||
switch platform {
|
||||
case 0:
|
||||
return discoveredPlatform
|
||||
case 1:
|
||||
return "AWS"
|
||||
case 2:
|
||||
return "GKE"
|
||||
case 3:
|
||||
return "Openshift"
|
||||
case 4:
|
||||
return "Rancher"
|
||||
default:
|
||||
return utils.DefaultPlatform
|
||||
}
|
||||
}
|
||||
|
||||
// discoverPlatform determines the host platform and returns it
|
||||
func DiscoverPlatform(kubeconfig *string) string {
|
||||
if ok, _ := IsAWSPlatform(kubeconfig); ok {
|
||||
return "AWS"
|
||||
}
|
||||
if ok, _ := IsGKEPlatform(kubeconfig); ok {
|
||||
return "GKE"
|
||||
}
|
||||
if ok, _ := IsOpenshiftPlatform(kubeconfig); ok {
|
||||
return "Openshift"
|
||||
}
|
||||
if ok, _ := k8s.NsExists("cattle-system", kubeconfig); ok {
|
||||
return "Rancher"
|
||||
}
|
||||
return utils.DefaultPlatform
|
||||
}
|
||||
|
||||
// IsAWSPlatform determines if the host platform is AWS
|
||||
// by checking the ProviderID inside node spec
|
||||
//
|
||||
// Sample node custom resource of an AWS node
|
||||
//
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// ....
|
||||
// "spec": {
|
||||
// "providerID": "aws:///us-east-2b/i-0bf24d83f4b993738"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func IsAWSPlatform(kubeconfig *string) (bool, error) {
|
||||
clientset, err := k8s.ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
|
||||
if err != nil || len(nodeList.Items) == 0 {
|
||||
return false, err
|
||||
}
|
||||
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, utils.AWSIdentifier) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsGKEPlatform determines if the host platform is GKE
|
||||
// by checking the ProviderID inside node spec
|
||||
//
|
||||
// Sample node custom resource of an GKE node
|
||||
//
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// ....
|
||||
// "spec": {
|
||||
// "providerID": ""
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func IsGKEPlatform(kubeconfig *string) (bool, error) {
|
||||
clientset, err := k8s.ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{})
|
||||
if err != nil || len(nodeList.Items) == 0 {
|
||||
return false, err
|
||||
}
|
||||
if strings.HasPrefix(nodeList.Items[0].Spec.ProviderID, utils.GKEIdentifier) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsOpenshiftPlatform determines if the host platform
|
||||
// is Openshift by checking "node.openshift.io/os_id"
|
||||
// label on the nodes
|
||||
//
|
||||
// Sample node custom resource of an Openshift node
|
||||
//
|
||||
// {
|
||||
// "apiVersion": "v1",
|
||||
// "kind": "Node",
|
||||
// "metadata": {
|
||||
// "labels": {
|
||||
// "node.openshift.io/os_id": "rhcos"
|
||||
// }
|
||||
// }
|
||||
// ....
|
||||
// }
|
||||
func IsOpenshiftPlatform(kubeconfig *string) (bool, error) {
|
||||
clientset, err := k8s.ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
nodeList, err := clientset.CoreV1().Nodes().List(context.TODO(), v1.ListOptions{
|
||||
LabelSelector: utils.OpenshiftIdentifier,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(nodeList.Items) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
)
|
||||
|
||||
// Returns a new kubernetes client set
|
||||
func ClientSet(kubeconfig *string) (*kubernetes.Clientset, error) {
|
||||
if *kubeconfig == "" {
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kcfg := filepath.Join(home, ".kube", "config")
|
||||
kubeconfig = &kcfg
|
||||
} else {
|
||||
utils.Red.Println("ERROR: Clientset generation failed!")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// create the config
|
||||
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
|
||||
if err != nil {
|
||||
utils.Red.Println("ERROR: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create the clientset
|
||||
clientset, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
utils.Red.Println("ERROR: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return clientset, err
|
||||
}
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
Copyright © 2021 The LitmusChaos Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package k8s
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
|
||||
"github.com/litmuschaos/litmusctl/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
k8serror "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/discovery"
|
||||
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||
)
|
||||
|
||||
type CanIOptions struct {
|
||||
NoHeaders bool
|
||||
Namespace string
|
||||
AuthClient authorizationv1client.AuthorizationV1Interface
|
||||
DiscoveryClient discovery.DiscoveryInterface
|
||||
|
||||
Verb string
|
||||
Resource schema.GroupVersionResource
|
||||
Subresource string
|
||||
ResourceName string
|
||||
}
|
||||
|
||||
// NsExists checks if the given namespace already exists
|
||||
func NsExists(namespace string, kubeconfig *string) (bool, error) {
|
||||
clientset, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ns, err := clientset.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
|
||||
if k8serror.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err == nil && ns != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
type CheckSAPermissionsParams struct {
|
||||
Verb string
|
||||
Resource string
|
||||
Print bool
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func CheckSAPermissions(params CheckSAPermissionsParams, kubeconfig *string) (bool, error) {
|
||||
|
||||
var o CanIOptions
|
||||
o.Verb = params.Verb
|
||||
o.Resource.Resource = params.Resource
|
||||
o.Namespace = params.Namespace
|
||||
client, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
AuthClient := client.AuthorizationV1()
|
||||
|
||||
sar := &authorizationv1.SelfSubjectAccessReview{
|
||||
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||
Namespace: o.Namespace,
|
||||
Verb: o.Verb,
|
||||
Group: o.Resource.Group,
|
||||
Resource: o.Resource.Resource,
|
||||
Subresource: o.Subresource,
|
||||
Name: o.ResourceName,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
response, err := AuthClient.SelfSubjectAccessReviews().Create(context.TODO(), sar, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if response.Status.Allowed {
|
||||
if params.Print {
|
||||
utils.White_B.Print("\n🔑 ", params.Resource, " ✅")
|
||||
}
|
||||
} else {
|
||||
if params.Print {
|
||||
utils.White_B.Print("\n🔑 ", params.Resource, " ❌")
|
||||
}
|
||||
if len(response.Status.Reason) > 0 {
|
||||
utils.White_B.Println(response.Status.Reason)
|
||||
}
|
||||
if len(response.Status.EvaluationError) > 0 {
|
||||
utils.Red.Println(response.Status.EvaluationError)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Status.Allowed, nil
|
||||
}
|
||||
|
||||
// ValidNs takes a valid namespace as input from user
|
||||
func ValidNs(mode string, label string, kubeconfig *string) (string, bool) {
|
||||
start:
|
||||
var (
|
||||
namespace string
|
||||
nsExists bool
|
||||
)
|
||||
|
||||
if mode == "namespace" {
|
||||
utils.White_B.Print("\nEnter the namespace (existing namespace) [Default: ", utils.DefaultNs, "]: ")
|
||||
fmt.Scanln(&namespace)
|
||||
|
||||
} else if mode == "cluster" {
|
||||
utils.White_B.Print("\nEnter the namespace (new or existing namespace) [Default: ", utils.DefaultNs, "]: ")
|
||||
fmt.Scanln(&namespace)
|
||||
} else {
|
||||
utils.Red.Printf("\n 🚫 No mode selected \n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if namespace == "" {
|
||||
namespace = utils.DefaultNs
|
||||
}
|
||||
ok, err := NsExists(namespace, kubeconfig)
|
||||
if err != nil {
|
||||
utils.Red.Printf("\n 🚫 Namespace existence check failed: {%s}\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if ok {
|
||||
if podExists(podExistsParams{namespace, label}, kubeconfig) {
|
||||
utils.Red.Println("\n🚫 There is a Chaos Infra already present in this namespace. Please enter a different namespace")
|
||||
goto start
|
||||
} else {
|
||||
nsExists = true
|
||||
utils.White_B.Println("👍 Continuing with", namespace, "namespace")
|
||||
}
|
||||
} else {
|
||||
if val, _ := CheckSAPermissions(CheckSAPermissionsParams{"create", "namespace", false, namespace}, kubeconfig); !val {
|
||||
utils.Red.Println("🚫 You don't have permissions to create a namespace.\n Please enter an existing namespace.")
|
||||
goto start
|
||||
}
|
||||
nsExists = false
|
||||
}
|
||||
|
||||
return namespace, nsExists
|
||||
}
|
||||
|
||||
type WatchPodParams struct {
|
||||
Namespace string
|
||||
Label string
|
||||
}
|
||||
|
||||
// WatchPod watches for the pod status
|
||||
func WatchPod(params WatchPodParams, kubeconfig *string) {
|
||||
clientset, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
watch, err := clientset.CoreV1().Pods(params.Namespace).Watch(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: params.Label,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
for event := range watch.ResultChan() {
|
||||
p, ok := event.Object.(*v1.Pod)
|
||||
if !ok {
|
||||
log.Fatal("unexpected type")
|
||||
}
|
||||
utils.White_B.Println("💡 Connecting Chaos Infra to ChaosCenter.")
|
||||
if p.Status.Phase == "Running" {
|
||||
utils.White_B.Println("🏃 Chaos Infra is running!!")
|
||||
watch.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PodList struct {
|
||||
Items []string
|
||||
}
|
||||
|
||||
type podExistsParams struct {
|
||||
Namespace string
|
||||
Label string
|
||||
}
|
||||
|
||||
// PodExists checks if the pod with the given label already exists in the given namespace
|
||||
func podExists(params podExistsParams, kubeconfig *string) bool {
|
||||
clientset, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return false
|
||||
}
|
||||
watch, err := clientset.CoreV1().Pods(params.Namespace).List(context.TODO(), metav1.ListOptions{
|
||||
LabelSelector: params.Label,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
return false
|
||||
}
|
||||
if len(watch.Items) >= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type SAExistsParams struct {
|
||||
Namespace string
|
||||
Serviceaccount string
|
||||
}
|
||||
|
||||
// SAExists checks if the given service account exists in the given namespace
|
||||
func SAExists(params SAExistsParams, kubeconfig *string) bool {
|
||||
clientset, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
msg := fmt.Sprintf("serviceaccounts \"%s\" not found", params.Serviceaccount)
|
||||
_, newErr := clientset.CoreV1().ServiceAccounts(params.Namespace).Get(context.TODO(), params.Serviceaccount, metav1.GetOptions{})
|
||||
if newErr != nil {
|
||||
if newErr.Error() == msg {
|
||||
return false
|
||||
}
|
||||
log.Fatal(newErr)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidSA gets a valid service account as input
|
||||
func ValidSA(namespace string, kubeconfig *string) (string, bool) {
|
||||
var sa string
|
||||
utils.White_B.Print("\nEnter service account [Default: ", utils.DefaultSA, "]: ")
|
||||
fmt.Scanln(&sa)
|
||||
if sa == "" {
|
||||
sa = utils.DefaultSA
|
||||
}
|
||||
if SAExists(SAExistsParams{namespace, sa}, kubeconfig) {
|
||||
utils.White_B.Print("\n👍 Using the existing service account")
|
||||
return sa, true
|
||||
}
|
||||
return sa, false
|
||||
}
|
||||
|
||||
// ApplyManifest applies the provided manifest and kubeconfig with the help of client-go library.
|
||||
func ApplyManifest(manifest []byte, kubeconfig string) (string, error) {
|
||||
|
||||
// Get Kubernetes and dynamic clients along with the configuration.
|
||||
_, kubeClient, dynamicClient, err := getClientAndConfig(kubeconfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Decode the manifest into a list of Unstructured resources.
|
||||
resources, err := decodeManifest(manifest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Apply the decoded resources using the dynamic client and Kubernetes client.
|
||||
err = applyResources(resources, dynamicClient, kubeClient)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "Success", nil
|
||||
}
|
||||
|
||||
// retrieves the Kubernetes and dynamic clients along with the configuration.
|
||||
func getClientAndConfig(kubeconfig string) (*rest.Config, *kubernetes.Clientset, dynamic.Interface, error) {
|
||||
var config *rest.Config
|
||||
var dynamicClient dynamic.Interface
|
||||
|
||||
// If kubeconfig is provided, use it to create the configuration and dynamic client.
|
||||
if kubeconfig != "" {
|
||||
var err error
|
||||
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to build config from flags: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Use the default kubeconfig file at $HOME/.kube/config.
|
||||
home := homedir.HomeDir()
|
||||
defaultKubeconfig := filepath.Join(home, ".kube", "config")
|
||||
if _, err := os.Stat(defaultKubeconfig); !os.IsNotExist(err) {
|
||||
var err error
|
||||
config, err = clientcmd.BuildConfigFromFlags("", defaultKubeconfig)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to build config from flags: %w", err)
|
||||
}
|
||||
} else {
|
||||
// Return an error if kubeconfig is not provided and the default file doesn't exist
|
||||
return nil, nil, nil, errors.New("kubeconfig not provided, and default kubeconfig not found")
|
||||
}
|
||||
}
|
||||
|
||||
// Create the dynamic client using the configuration.
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to create dynamic client: %w", err)
|
||||
}
|
||||
|
||||
// Create a Kubernetes client using the configuration.
|
||||
kubeClient, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to create kube client: %w", err)
|
||||
}
|
||||
|
||||
return config, kubeClient, dynamicClient, nil
|
||||
}
|
||||
|
||||
// decodes the manifest byte slice into a list of Unstructured resources.
|
||||
func decodeManifest(manifest []byte) ([]*unstructured.Unstructured, error) {
|
||||
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(manifest), len(manifest))
|
||||
|
||||
// Split the resource file into separate YAML documents.
|
||||
resources := []*unstructured.Unstructured{}
|
||||
for {
|
||||
resourcestr := &unstructured.Unstructured{}
|
||||
if err := decoder.Decode(resourcestr); err != nil {
|
||||
if err.Error() == "EOF" {
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("error in decoding resource: %w", err)
|
||||
}
|
||||
resources = append(resources, resourcestr)
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
// applies the decoded resources using the dynamic client and Kubernetes client.
|
||||
func applyResources(resources []*unstructured.Unstructured, dynamicClient dynamic.Interface, kubeClient *kubernetes.Clientset) error {
|
||||
for _, resource := range resources {
|
||||
logrus.Infof("Applying resource: %s , kind: %s", resource.GetName(), resource.GetKind())
|
||||
|
||||
gvk := resource.GroupVersionKind()
|
||||
// a mapper for REST mapping.
|
||||
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(kubeClient.Discovery()))
|
||||
// Map GVK to GVR using the REST mapper.
|
||||
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in resource gvk to gvr mapping: %w", err)
|
||||
}
|
||||
|
||||
var dr dynamic.ResourceInterface
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
||||
// Namespaced resources should specify the namespace
|
||||
dr = dynamicClient.Resource(mapping.Resource).Namespace(resource.GetNamespace())
|
||||
} else {
|
||||
// For cluster-wide resources
|
||||
dr = dynamicClient.Resource(mapping.Resource)
|
||||
}
|
||||
|
||||
// Apply the resource using the dynamic client.
|
||||
_, err = dr.Apply(context.TODO(), resource.GetName(), resource, metav1.ApplyOptions{
|
||||
Force: true,
|
||||
FieldManager: "application/apply-patch",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in applying resource: %w", err)
|
||||
}
|
||||
|
||||
logrus.Info("Resource applied successfully")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfigMap returns config map for a given name and namespace
|
||||
func GetConfigMap(c context.Context, name string, namespace string) (map[string]string, error) {
|
||||
var kubeconfig *string
|
||||
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
kubeconfig = flag.String("configmap", filepath.Join(home, ".kube", "config"), "")
|
||||
} else {
|
||||
kubeconfig = flag.String("configmap", "", "")
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
clientset, err := ClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x, err := clientset.CoreV1().ConfigMaps(namespace).Get(c, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
return x.Data, nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue