Compare commits

...

58 Commits

Author SHA1 Message Date
Namkyu Park af8244e53a
update: pinning k8s version to 1.26 (#507)
* update: pinning k8s version to 1.26

Signed-off-by: namkyu1999 <lak9348@gmail.com>

* Fix: add flags to the golangci

Signed-off-by: namkyu1999 <lak9348@gmail.com>

* fix: update base image because of the CVE-2024-3596

Signed-off-by: namkyu1999 <lak9348@gmail.com>

---------

Signed-off-by: namkyu1999 <lak9348@gmail.com>
2025-04-02 11:48:47 +05:30
Shubham Chaudhary 0e85028f68
(chore): Fix the release pipeline (#506)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-10-15 23:40:33 +05:30
Shubham Chaudhary 63281440c1
(chore): Fix the release pipeline (#505)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-10-15 23:07:33 +05:30
Shubham Chaudhary 7e4ac4cc45
(chore): Fix the release pipelines (#504)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-10-15 22:55:25 +05:30
Shubham Chaudhary 9fb5e5a384
chore(go): upgrade the go version (#497)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-09-14 00:11:11 +05:30
Vedant Shrotria 0b10874e04
Merge pull request #499 from dusdjhyeon/ubi-migration
UBI migration of Images - chaos-operator
2024-08-06 12:57:54 +05:30
Vedant Shrotria 77a6191735
Merge branch 'master' into ubi-migration 2024-07-24 15:43:15 +05:30
Shubham Chaudhary bb7ea39bcd
chore(operator): Fix the lint (#500)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-07-16 11:47:00 +05:30
dusdjhyeon 3a8ff7b8fa
feat: migration base image
Signed-off-by: dusdjhyeon <dusdj0813@gmail.com>
2024-07-16 10:47:11 +09:00
Shubham Chaudhary e96a7ee7f1
fix(logs): fix the rank warning in logs (#496)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2024-06-01 12:04:04 +05:30
Vedant Shrotria 31aa78b0cf
Merge pull request #495 from Jonsy13/add-gitleaks
Adding `gitleaks` as PR Check
2024-05-20 10:27:29 +05:30
Vedant Shrotria 9d6dfb2d65
Merge branch 'master' into add-gitleaks 2024-05-17 17:37:17 +05:30
Jonsy13 a4c31076d4
Added gitleaks
Signed-off-by: Jonsy13 <vedant.shrotria@harness.io>
2024-05-17 17:33:41 +05:30
Udit Gaurav 2b2646ea7e
Fix: Adds fixes for pipeline issues (#493)
* Vulnerability: Fix vulnerabilities

---------

Signed-off-by: Udit Gaurav <udit.gaurav@harness.io>
2024-04-26 14:35:49 +05:30
Saranya Jena dc17ee1399
Merge pull request #488 from shovanmaity/fuz-2
add fuzz test for remove string utility function
2024-04-04 08:36:04 +02:00
Shovan Maity fe0bf63328
Update golang ci version
Signed-off-by: Shovan Maity <shovan.maity@harness.io>
2024-04-04 11:48:02 +05:30
Shovan Maity 361a7649cb
add fuzz test for remove string utility function
Signed-off-by: Shovan Maity <shovan.maity@harness.io>
2024-03-14 12:02:36 +05:30
Shovan Maity f1afe838dd
add fuzz test for set env utility function (#485)
* add fuzz test for set env utility function

Signed-off-by: Shovan Maity <shovan.maity@harness.io>
2024-03-08 21:16:51 +05:30
Nageshbansal ba4d2f704c
Support taints and tolerations for source cmd Probe (#483)
* Support tolerations for source cmd Probe

Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2024-03-01 14:25:54 +05:30
Nageshbansal 2d84728732
Adds verbosity property (#481)
Signed-off-by: nagesh bansal <nageshbansal59@gmail.com>
2024-01-04 16:19:15 +05:30
Shubham Chaudhary d864dde90f
fix the go deps (#479)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-12-15 14:14:45 +05:30
Shubham Chaudhary e2641540a9
Integrate the operator image with scarf gateway (#478)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-11-16 11:15:51 +05:30
Adarshkumar14 6819a4be12
Upgrading the go version in build files (#474)
* updating the go version in build files

Signed-off-by: Adarsh kumar <adarsh.kumar@harness.io>

* Updating go version

Signed-off-by: Adarsh kumar <adarsh.kumar@harness.io>

---------

Signed-off-by: Adarsh kumar <adarsh.kumar@harness.io>
2023-07-18 17:06:17 +05:30
Shubham Chaudhary de73ffdd63
fixing the group of the fake clients (#472)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-06-29 09:34:37 +05:30
Shubham Chaudhary 522c4dd8d8
fixing the docker buildx progess argument (#473)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-06-20 11:53:11 +05:30
Shubham Chaudhary d2a310ac30
add the pod security context (#470)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-06-20 00:06:03 +05:30
Vedant Shrotria dc8f3597d3
Merge pull request #469 from uditgaurav/add-initialDelaySeconds
Add initialDelaySeconds for backward compatibility of probes
2023-06-05 16:52:23 +05:30
Udit Gaurav 8909abc5c2 Add initialDelaySeconds for backward compatibility 2023-06-05 16:43:45 +05:30
Vedant Shrotria d019f63af5
Merge pull request #468 from ispeakc0de/k8s-group
Group should be optional incase of k8s probes
2023-06-02 22:30:15 +05:30
Shubham Chaudhary ed0899559a Group should be optional incase of k8s probes
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-06-01 12:35:16 +05:30
Vedant Shrotria 06ff37d414
Merge pull request #464 from Adarshkumar14/master
updating go version to 1.19 for resolving vulnerabilities
2023-05-26 15:01:35 +05:30
Adarsh kumar ccd88854bd fixing gofmt check
Signed-off-by: Adarsh kumar <adarsh.kumar@harness.io>
2023-05-24 00:39:06 +05:30
Adarsh kumar 852d1203d4 updating go version to 1.19
Signed-off-by: Adarsh kumar <adarsh.kumar@harness.io>
2023-05-19 17:32:06 +05:30
Shubham Chaudhary c97b2b90e1
chore(fields): Converting optional fields to pointer type (#461)
* chore(fields): Converting optional fields to pointer type

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

* add sloprobe fields

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

---------

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-04-25 12:00:39 +05:30
Shubham Chaudhary 3a5f576c5b
run workflow on dispatch event and use token from secrets (#460)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-04-20 13:20:54 +05:30
Shubham Chaudhary 5ea32522f0
chore(unit): Adding units to the duration fields (#459)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-04-18 12:51:31 +05:30
Karthik Satchitanand 184224e2d2
(enhancement)schema: additional statuses for result phase (#458)
Signed-off-by: ksatchit <karthik.s@harness.io>
2023-04-13 20:43:51 +05:30
Amit Kumar Das 9cf6a6b9b7
Updated insecure skip verify type (#457)
Signed-off-by: Amit Kumar Das <amit.das@harness.io>
2023-04-08 21:46:28 +05:30
Amit Kumar Das 3998ae9f43
Added insecureSkipVerify in SLO Probe (#456)
Signed-off-by: Amit Kumar Das <amit.das@harness.io>
2023-04-08 09:30:18 +05:30
Amit Kumar Das aa8bea8162
feat: [CHAOS-126]: Added SLO probe schema (#455)
* feat: [CHAOS-126]: Added SLO probe schema

Signed-off-by: Amit Kumar Das <amit.das@harness.io>
2023-04-05 16:36:06 +05:30
Shubham Chaudhary e7f9ae680a
chore(probe): rollback the probe schema (#452)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-03-09 21:15:31 +05:30
Shubham Chaudhary d7645eaac5
updating result phase (#451)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-03-08 14:07:32 +05:30
Shubham Chaudhary 155d6cd729
fix(crd): fix the crd validation for required field (#450)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-03-08 11:20:38 +05:30
Shubham Chaudhary feb7100c35
add the experiment phase as completed with error (#449)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-03-06 15:43:02 +05:30
Neelanjan Manna 6b272cd656
chore(engine): Updates mandatory fields in the engine probe spec (#447)
* updates mandatory fields in the engine spec

Signed-off-by: neelanjan00 <neelanjan.manna@harness.io>
2023-02-22 12:58:19 +05:30
Shubham Chaudhary 054c3e2a97
adding backend compatibility to probe retry (#446)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-02-20 16:09:17 +05:30
Shubham Chaudhary 66266db7fd
chore(probe): updating probe failstep to error reason (#444)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-02-16 17:05:34 +05:30
Shubham Chaudhary a47df5d5c7
chore(probe): updating retries to attempt (#443)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-02-09 16:14:27 +05:30
Shubham Chaudhary de7c74a937
chore(sidecar): adding env and envfrom field in sidecar (#442)
* chore(sidecar): adding envfrom field in sidecar

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

* chore(sidecar): adding env field in sidecar

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-01-09 18:32:22 +05:30
Shubham Chaudhary 00233519bd
chore(sidecar): adding sidecar to the helper pod (#441)
* chore(sidecar): add sidecar image and imagepullpolicy in engine

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2023-01-09 15:52:39 +05:30
Shubham Chaudhary f3873a0417
Adding failstep and error code changes to master (#440)
* feat(errorCode): Adding error code inside chaosresult (#436)

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-12-15 14:34:09 +05:30
Shubham Chaudhary d39bc1fc09
fix(vulrn):fixing the security vulnerabilities (#438)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-11-29 20:00:34 +05:30
Shubham Chaudhary 30feb53ddf
chore(operator): fixing the update conflict issue (#435)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-11-23 19:20:34 +05:30
Shubham Chaudhary e5b440b5cd
feat(selectors): Adding selectors inside the chaosengine (#423) (#434)
* feat(selectors): Adding selectors inside the chaosengine

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

* upading image tag

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-11-21 21:20:30 +05:30
Shubham Chaudhary 463651570a
fix(operator): fixing setup logging (#433) 2022-11-16 15:11:24 +05:30
Shubham Chaudhary 466b8aba39
fix(analytics): fixing analytics (#431)
Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-11-15 11:15:22 +05:30
Soumya Ghosh Dastidar 3d12d34d20
feat: add resource names field in k8sprobe (#430)
* feat: add resource names field in k8sprobe

Signed-off-by: Soumya Ghosh Dastidar <gdsoumya@gmail.com>
2022-11-14 11:25:03 +05:30
Shubham Chaudhary b7f908ff8c
chore(verdict): Adding awaited verdict as enum (#429)
* chore(verdcit): Adding awaited verdict as enum

Signed-off-by: Shubham Chaudhary <shubham.chaudhary@harness.io>
2022-11-07 17:50:46 +05:30
41 changed files with 3628 additions and 5596 deletions

View File

@ -1,10 +0,0 @@
component_depth: 2
languages:
- go
exclude:
- /vendor/.*
- /pkg/client/.*
- /pkg/apis/litmuschaos/v1alpha1/zz_generated.*\.go
test:
include:
- .*/test/.*\.go

2
.github/CODEOWNERS vendored
View File

@ -2,4 +2,4 @@
# Each line is a file pattern followed by one or more owners.
# These owners will be the default owners for everything in the repo.
* @rahulchheda @ksatchit @chandankumar4 @rajdas98
* @ksatchit @ispeakc0de @imrajdas

View File

@ -11,7 +11,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
go-version: 1.22
# Checkout to the latest commit
# On specific directory/path
@ -22,27 +22,13 @@ jobs:
run: make gofmt-check
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v1
uses: reviewdog/action-golangci-lint@v2
with:
golangci_lint_flags: "--timeout=10m"
- name: unused-package check
run: make unused-package-check
security:
container:
image: litmuschaos/snyk:1.0
volumes:
- /home/runner/work/_actions/:/home/runner/work/_actions/
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: snyk/actions/setup@master
- run: snyk auth ${SNYK_TOKEN}
- uses: actions/setup-go@v1
with:
go-version: '1.17'
- name: Snyk monitor
run: snyk test
trivy:
needs: pre-checks
runs-on: ubuntu-latest
@ -65,6 +51,18 @@ jobs:
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
gitleaks-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run GitLeaks
run: |
wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.2/gitleaks_8.18.2_linux_x64.tar.gz && \
tar -zxvf gitleaks_8.18.2_linux_x64.tar.gz && \
sudo mv gitleaks /usr/local/bin && gitleaks detect --source . -v
image-build:
runs-on: ubuntu-latest
needs: pre-checks
@ -85,7 +83,7 @@ jobs:
chmod +x ${{ github.workspace }}/image.tar
- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: myimage
path: |
@ -98,7 +96,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
go-version: 1.22
# Checkout to the latest commit
# On specific directory/path
@ -121,7 +119,7 @@ jobs:
make deps
- name: Download artifact
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: myimage
path: ${{ github.workspace }}
@ -133,4 +131,5 @@ jobs:
- name: Running Go BDD Test
run: |
go mod tidy
make test

View File

@ -19,7 +19,7 @@ jobs:
- uses: actions/setup-go@v2
with:
go-version: '^1.13.1'
go-version: 1.22
- name: Setting up GOPATH
run: |
@ -61,12 +61,14 @@ jobs:
- name: Installing Prerequisites (KinD Cluster)
uses: engineerd/setup-kind@v0.5.0
with:
version: "v0.7.0"
version: "v0.22.0"
- name: Configuring and testing kind Installation
run: |
kubectl cluster-info --context kind-kind
kind get kubeconfig --internal >$HOME/.kube/config
kubectl cluster-info
kubectl get pods -n kube-system
echo "current-context:" $(kubectl config current-context)
echo "environment-kubeconfig:" ${KUBECONFIG}
kubectl get nodes
- name: Load image on the nodes of the cluster
@ -76,7 +78,7 @@ jobs:
- name: Getting litmus-e2e repository
run: |
cd ${GOPATH}/src/github.com/litmuschaos/
git clone https://github.com/litmuschaos/litmus-e2e.git -b generic
git clone https://github.com/litmuschaos/litmus-e2e.git -b master
- name: Install LitmusChaos
run: |
@ -93,7 +95,7 @@ jobs:
run: |
export PATH=$PATH:$(go env GOPATH)/bin
cd ${GOPATH}/src/github.com/litmuschaos/litmus-e2e
go test operator/admin-mode_test.go -v -count=1
go test components/operator/admin-mode_test.go -v -count=1
env:
KUBECONFIG: /home/runner/.kube/config
@ -102,7 +104,7 @@ jobs:
run: |
export PATH=$PATH:$(go env GOPATH)/bin
cd ${GOPATH}/src/github.com/litmuschaos/litmus-e2e
go test operator/reconcile-resiliency_test.go -v -count=1
go test components/operator/reconcile-resiliency_test.go -v -count=1
env:
KUBECONFIG: /home/runner/.kube/config
@ -150,7 +152,7 @@ jobs:
with:
comment-id: "${{ github.event.comment.id }}"
body: |
**Test Result:** No test found
**Test Result:** No test found try /run-e2e-all
**Logs:** [${{ env.RUN_ID }}](https://github.com/litmuschaos/chaos-operator/actions/runs/${{ env.RUN_ID }})
reactions: eyes
env:

View File

@ -13,7 +13,7 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
go-version: 1.22
# Checkout to the latest commit
# On specific directory/path
@ -24,14 +24,13 @@ jobs:
run: make gofmt-check
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v1
uses: reviewdog/action-golangci-lint@v2
- name: unused-package check
run: make unused-package-check
image-build:
runs-on: ubuntu-latest
needs: tests
steps:
# Checkout to the latest commit
# On specific directory/path
@ -60,44 +59,3 @@ jobs:
DNAME: ${{ secrets.DNAME }}
DPASS: ${{ secrets.DPASS }}
run: make push-chaos-operator
tests:
needs: pre-checks
runs-on: ubuntu-latest
steps:
# Checkout to the latest commit
# On specific directory/path
- name: Checkout
uses: actions/checkout@v2
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
#Install and configure a kind cluster
- name: Installing Prerequisites (K3S Cluster)
env:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
run: |
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.20.14-rc1+k3s1 sh -s - --docker --write-kubeconfig-mode 664
kubectl wait node --all --for condition=ready --timeout=90s
mkdir -p $HOME/.kube
cp /etc/rancher/k3s/k3s.yaml $HOME/.kube/config
kubectl get nodes
- name: Dependency checks
run: |
make deps
- name: Build Docker Image
env:
DOCKER_REPO: litmuschaos
DOCKER_IMAGE: chaos-operator
DOCKER_TAG: ci
run: |
make build-amd64
- name: Running Go BDD Test
run: |
make test

View File

@ -11,25 +11,15 @@ jobs:
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
go-version: 1.22
# Checkout to the latest commit
# On specific directory/path
- name: Checkout
uses: actions/checkout@v2
- name: gofmt check
run: make gofmt-check
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v1
- name: unused-package check
run: make unused-package-check
image-build:
runs-on: ubuntu-latest
needs: tests
steps:
# Checkout to the latest commit
# On specific directory/path
@ -73,44 +63,3 @@ jobs:
DNAME: ${{ secrets.DNAME }}
DPASS: ${{ secrets.DPASS }}
run: make push-chaos-operator
tests:
needs: pre-checks
runs-on: ubuntu-latest
steps:
# Checkout to the latest commit
# On specific directory/path
- name: Checkout
uses: actions/checkout@v2
# Install golang
- uses: actions/setup-go@v2
with:
go-version: 1.17.5
#Install and configure a kind cluster
- name: Installing Prerequisites (K3S Cluster)
env:
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
run: |
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.20.14-rc1+k3s1 sh -s - --docker --write-kubeconfig-mode 664
kubectl wait node --all --for condition=ready --timeout=90s
mkdir -p $HOME/.kube
cp /etc/rancher/k3s/k3s.yaml $HOME/.kube/config
kubectl get nodes
- name: Dependency checks
run: |
make deps
- name: Build Docker Image
env:
DOCKER_REPO: litmuschaos
DOCKER_IMAGE: chaos-operator
DOCKER_TAG: ci
run: |
make build-amd64
- name: Running Go BDD Test
run: |
make test

23
.github/workflows/security-scan.yml vendored Normal file
View File

@ -0,0 +1,23 @@
---
name: Security Scan
on:
workflow_dispatch:
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Build an image from Dockerfile
run: |
docker build -f build/Dockerfile -t docker.io/litmuschaos/chaos-operator:${{ github.sha }} . --build-arg TARGETARCH=amd64
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'docker.io/litmuschaos/chaos-operator:${{ github.sha }}'
format: 'table'
exit-code: '1'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'

View File

@ -74,14 +74,14 @@ build-chaos-operator:
@echo "-------------------------"
@echo "--> Build go-runner image"
@echo "-------------------------"
@docker buildx build --file build/Dockerfile --progress plane --no-cache --platform linux/arm64,linux/amd64 --tag $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG) .
@docker buildx build --file build/Dockerfile --progress plain --no-cache --platform linux/arm64,linux/amd64 --tag $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG) .
.PHONY: push-chaos-operator
push-chaos-operator:
@echo "------------------------------"
@echo "--> Pushing image"
@echo "------------------------------"
@docker buildx build --file build/Dockerfile --progress plane --no-cache --push --platform linux/arm64,linux/amd64 --tag $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG) .
@docker buildx build --file build/Dockerfile --progress plain --no-cache --push --platform linux/arm64,linux/amd64 --tag $(DOCKER_REGISTRY)/$(DOCKER_REPO)/$(DOCKER_IMAGE):$(DOCKER_TAG) .
.PHONY: build-amd64
build-amd64:

View File

@ -8,7 +8,6 @@
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2597079b1b5240d3866a6deb4112a2f2)](https://www.codacy.com/manual/litmuschaos/chaos-operator?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=litmuschaos/chaos-operator&amp;utm_campaign=Badge_Grade)
[![Go Report Card](https://goreportcard.com/badge/github.com/litmuschaos/chaos-operator)](https://goreportcard.com/report/github.com/litmuschaos/chaos-operator)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5290/badge)](https://bestpractices.coreinfrastructure.org/projects/5290)
[![BCH compliance](https://bettercodehub.com/edge/badge/litmuschaos/chaos-operator?branch=master)](https://bettercodehub.com/)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Flitmuschaos%2Fchaos-operator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Flitmuschaos%2Fchaos-operator?ref=badge_shield)
[![codecov](https://codecov.io/gh/litmuschaos/chaos-operator/branch/master/graph/badge.svg)](https://codecov.io/gh/litmuschaos/chaos-operator)
[![YouTube Channel](https://img.shields.io/badge/YouTube-Subscribe-red)](https://www.youtube.com/channel/UCa57PMqmz_j0wnteRa9nCaw)

View File

@ -25,13 +25,11 @@ import (
// ChaosEngineSpec describes a user-facing custom resource which is used by developers
// to create a chaos profile
type ChaosEngineSpec struct {
//Appinfo contains deployment details of AUT
//Appinfo contains the AUT details
Appinfo ApplicationParams `json:"appinfo,omitempty"`
//AnnotationCheck defines whether annotation check is allowed or not. It can be true or false
AnnotationCheck string `json:"annotationCheck,omitempty"`
//DefaultHealthCheck defines whether default health checks should be executed or not. It can be true or false
// default value is true
DefaultHealthCheck string `json:"defaultHealthCheck,omitempty"`
DefaultHealthCheck bool `json:"defaultHealthCheck,omitempty"`
//ChaosServiceAccount is the SvcAcc specified for chaos runner pods
ChaosServiceAccount string `json:"chaosServiceAccount"`
//Components contains the image, imagePullPolicy, arguments, and commands of runner
@ -46,6 +44,8 @@ type ChaosEngineSpec struct {
EngineState EngineState `json:"engineState"`
// TerminationGracePeriodSeconds contains terminationGracePeriod for the chaos resources
TerminationGracePeriodSeconds int64 `json:"terminationGracePeriodSeconds,omitempty"`
// Selectors contains the target application details
Selectors *Selector `json:"selectors,omitempty"`
}
// EngineState provides interface for all supported strings in spec.EngineState
@ -119,10 +119,52 @@ type ApplicationParams struct {
AppKind string `json:"appkind,omitempty"`
}
type Selector struct {
Workloads []Workload `json:"workloads,omitempty"`
Pods []Pod `json:"pods,omitempty"`
}
type WorkloadKind string
const (
WorkloadDeployment WorkloadKind = "deployment"
WorkloadStatefulSet WorkloadKind = "statefulset"
WorkloadDaemonSet WorkloadKind = "daemonSet"
WorkloadDeploymentConfig WorkloadKind = "deploymentconfig"
WorkloadRollout WorkloadKind = "rollout"
)
type Workload struct {
Kind WorkloadKind `json:"kind"`
Namespace string `json:"namespace"`
Names string `json:"names,omitempty"`
Labels string `json:"labels,omitempty"`
}
type Pod struct {
Namespace string `json:"namespace"`
Names string `json:"names"`
}
// ComponentParams defines information about the runner
type ComponentParams struct {
//Contains informations of the the runner pod
//Contains information of the runner pod
Runner RunnerInfo `json:"runner"`
// Contains information of the sidecar
Sidecar []Sidecar `json:"sidecar,omitempty"`
}
type Sidecar struct {
//Image of the sidecar container
Image string `json:"image"`
//ImagePullPolicy of the sidecar container
ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"`
// Secrets for the sidecar container
Secrets []Secret `json:"secrets,omitempty"`
// EnvFrom for the sidecar container
EnvFrom []corev1.EnvFromSource `json:"envFrom"`
// ENV contains ENV passed to the sidecar container
ENV []corev1.EnvVar `json:"env,omitempty"`
}
// RunnerInfo defines the information of the runnerinfo pod
@ -166,8 +208,6 @@ type ExperimentList struct {
// ExperimentAttributes defines attributes of experiments
type ExperimentAttributes struct {
//Execution priority of the chaos experiment
Rank uint32 `json:"rank"`
// It contains env, configmaps, secrets, experimentImage, node selector, custom experiment annotation
// which can be provided or overridden from the chaos engine
Components ExperimentComponents `json:"components,omitempty"`
@ -179,22 +219,24 @@ type ExperimentAttributes struct {
// ProbeAttributes contains details of probe, which can be applied on the experiments
type ProbeAttributes struct {
// Name of probe
Name string `json:"name,omitempty"`
Name string `json:"name"`
// Type of probe
Type string `json:"type,omitempty"`
Type string `json:"type"`
// inputs needed for the k8s probe
K8sProbeInputs K8sProbeInputs `json:"k8sProbe/inputs,omitempty"`
K8sProbeInputs *K8sProbeInputs `json:"k8sProbe/inputs,omitempty"`
// inputs needed for the http probe
HTTPProbeInputs HTTPProbeInputs `json:"httpProbe/inputs,omitempty"`
HTTPProbeInputs *HTTPProbeInputs `json:"httpProbe/inputs,omitempty"`
// inputs needed for the cmd probe
CmdProbeInputs CmdProbeInputs `json:"cmdProbe/inputs,omitempty"`
CmdProbeInputs *CmdProbeInputs `json:"cmdProbe/inputs,omitempty"`
// inputs needed for the prometheus probe
PromProbeInputs PromProbeInputs `json:"promProbe/inputs,omitempty"`
PromProbeInputs *PromProbeInputs `json:"promProbe/inputs,omitempty"`
// inputs needed for the SLO probe
SLOProbeInputs *SLOProbeInputs `json:"sloProbe/inputs,omitempty"`
// RunProperty contains timeout, retry and interval for the probe
RunProperties RunProperty `json:"runProperties,omitempty"`
RunProperties RunProperty `json:"runProperties"`
// mode for k8s probe
// it can be SOT, EOT, Edge
Mode string `json:"mode,omitempty"`
Mode string `json:"mode"`
// Data contains the manifest/data for the resource, which need to be created
// it supported for create operation only
Data string `json:"data,omitempty"`
@ -205,9 +247,11 @@ type K8sProbeInputs struct {
// group of the resource
Group string `json:"group,omitempty"`
// apiversion of the resource
Version string `json:"version,omitempty"`
Version string `json:"version"`
// kind of resource
Resource string `json:"resource,omitempty"`
Resource string `json:"resource"`
// ResourceNames to get the resources using their list of comma separated names
ResourceNames string `json:"resourceNames,omitempty"`
// namespace of the resource
Namespace string `json:"namespace,omitempty"`
// fieldselector to get the resource using fields selector
@ -216,24 +260,24 @@ type K8sProbeInputs struct {
LabelSelector string `json:"labelSelector,omitempty"`
// Operation performed by the k8s probe
// it can be create, delete, present, absent
Operation string `json:"operation,omitempty"`
Operation string `json:"operation"`
}
// CmdProbeInputs contains all the inputs required for cmd probe
type CmdProbeInputs struct {
// Command need to be executed for the probe
Command string `json:"command,omitempty"`
Command string `json:"command"`
// Comparator check for the correctness of the probe output
Comparator ComparatorInfo `json:"comparator,omitempty"`
Comparator ComparatorInfo `json:"comparator"`
// The source where we have to run the command
// It will run in inline(inside experiment itself) mode if source is nil
Source SourceDetails `json:"source,omitempty"`
Source *SourceDetails `json:"source,omitempty"`
}
// SourceDetails contains source details of the cmdProbe
type SourceDetails struct {
// Image for the source pod
Image string `json:"image,omitempty"`
Image string `json:"image"`
// HostNetwork define the hostNetwork of the external pod
// it supports boolean values and default value is false
HostNetwork bool `json:"hostNetwork,omitempty"`
@ -256,6 +300,8 @@ type SourceDetails struct {
Privileged bool `json:"privileged,omitempty"`
// NodeSelector for the source pod
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// Tolerations for the source pod
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// Volumes for the source pod
Volumes []corev1.Volume `json:"volumes,omitempty"`
// VolumesMount for the source pod
@ -267,13 +313,54 @@ type SourceDetails struct {
// PromProbeInputs contains all the inputs required for prometheus probe
type PromProbeInputs struct {
// Endpoint for the prometheus probe
Endpoint string `json:"endpoint,omitempty"`
Endpoint string `json:"endpoint"`
// Query to get promethus metrics
Query string `json:"query,omitempty"`
// QueryPath contains filePath, which contains prometheus query
QueryPath string `json:"queryPath,omitempty"`
// Comparator check for the correctness of the probe output
Comparator ComparatorInfo `json:"comparator,omitempty"`
Comparator ComparatorInfo `json:"comparator"`
}
// SLOProbeInputs contains all the inputs required for SLO probe
type SLOProbeInputs struct {
// PlatformEndpoint for the monitoring service endpoint
PlatformEndpoint string `json:"platformEndpoint"`
// SLOIdentifier for fetching the details of the SLO
SLOIdentifier string `json:"sloIdentifier"`
// InsecureSkipVerify flag to skip certificate checks
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
// EvaluationWindow is the time period for which the metrics will be evaluated
EvaluationWindow *EvaluationWindow `json:"evaluationWindow,omitempty"`
// SLOSourceMetadata consists of required metadata details to fetch metric data
SLOSourceMetadata SLOSourceMetadata `json:"sloSourceMetadata"`
// Comparator check for the correctness of the probe output
Comparator ComparatorInfo `json:"comparator"`
}
// EvaluationWindow is the time period for which the SLO probe will work
type EvaluationWindow struct {
// Start time of evaluation
EvaluationStartTime int `json:"evaluationStartTime,omitempty"`
// End time of evaluation
EvaluationEndTime int `json:"evaluationEndTime,omitempty"`
}
type SLOSourceMetadata struct {
// APITokenSecret for authenticating with the platform service
APITokenSecret string `json:"apiTokenSecret"`
// Scope required for fetching details
Scope Identifier `json:"scope"`
}
// Identifier required for fetching details from the Platform APIs
type Identifier struct {
// AccountIdentifier for account ID
AccountIdentifier string `json:"accountIdentifier"`
// OrgIdentifier for organization ID
OrgIdentifier string `json:"orgIdentifier"`
// ProjectIdentifier for project ID
ProjectIdentifier string `json:"projectIdentifier"`
}
// ComparatorInfo contains the comparator details
@ -284,34 +371,34 @@ type ComparatorInfo struct {
// Criteria for matching data
// it supports >=, <=, ==, >, <, != for int and float
// it supports equal, notEqual, contains for string
Criteria string `json:"criteria,omitempty"`
Criteria string `json:"criteria"`
// Value contains relative value for criteria
Value string `json:"value,omitempty"`
Value string `json:"value"`
}
// HTTPProbeInputs contains all the inputs required for http probe
type HTTPProbeInputs struct {
// URL which needs to curl, to check the status
URL string `json:"url,omitempty"`
URL string `json:"url"`
// InsecureSkipVerify flag to skip certificate checks
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
// Method define the http method, it can be get or post
Method HTTPMethod `json:"method,omitempty"`
Method HTTPMethod `json:"method"`
}
// HTTPMethod define the http method details
type HTTPMethod struct {
Get GetMethod `json:"get,omitempty"`
Post PostMethod `json:"post,omitempty"`
Get *GetMethod `json:"get,omitempty"`
Post *PostMethod `json:"post,omitempty"`
}
// GetMethod define the http Get method
type GetMethod struct {
// Criteria for matching data
// it supports == != operations
Criteria string `json:"criteria,omitempty"`
Criteria string `json:"criteria"`
// Value contains relative value for criteria
ResponseCode string `json:"responseCode,omitempty"`
ResponseCode string `json:"responseCode"`
}
// PostMethod define the http Post method
@ -324,24 +411,33 @@ type PostMethod struct {
BodyPath string `json:"bodyPath,omitempty"`
// Criteria for matching data
// it supports == != operations
Criteria string `json:"criteria,omitempty"`
Criteria string `json:"criteria"`
// Value contains relative value for criteria
ResponseCode string `json:"responseCode,omitempty"`
ResponseCode string `json:"responseCode"`
}
// RunProperty contains timeout, retry and interval for the probe
type RunProperty struct {
//ProbeTimeout contains timeout for the probe
ProbeTimeout int `json:"probeTimeout,omitempty"`
// Interval contains the inverval for the probe
Interval int `json:"interval,omitempty"`
ProbeTimeout string `json:"probeTimeout"`
// Interval contains the interval for the probe
Interval string `json:"interval"`
// Retry contains the retry count for the probe
Retry int `json:"retry,omitempty"`
// Attempt contains the total attempt count for the probe
Attempt int `json:"attempt,omitempty"`
//ProbePollingInterval contains time interval, for which continuous probe should be sleep
// after each iteration
ProbePollingInterval int `json:"probePollingInterval,omitempty"`
ProbePollingInterval string `json:"probePollingInterval,omitempty"`
//InitialDelaySeconds time interval for which probe will wait before run
InitialDelaySeconds int `json:"initialDelaySeconds,omitempty"`
//InitialDelay time interval for which probe will wait before run
InitialDelay string `json:"initialDelay,omitempty"`
// EvaluationTimeout is the timeout window in which the SLO metrics
// will be fetched and will be evaluated
EvaluationTimeout string `json:"evaluationTimeout,omitempty"`
// Verbosity contains flags for type of logging while running the Continuous and onChaos Probes
Verbosity string `json:"verbosity,omitempty"`
// StopOnFailure contains flag to stop/continue experiment execution, if probe fails
// it will stop the experiment execution, if provided true
// it will continue the experiment execution, if provided false or not provided(default case)

View File

@ -40,8 +40,14 @@ const (
ResultPhaseRunning ResultPhase = "Running"
// ResultPhaseCompleted is phase of chaosresult which is in completed state
ResultPhaseCompleted ResultPhase = "Completed"
// Retained For Backward Compatibility: ResultPhaseCompletedWithError is phase of chaosresult when probe is failed in 3.0beta5
ResultPhaseCompletedWithError ResultPhase = "Completed_With_Error"
// ResultPhaseCompletedWithProbeFailure is phase of chaosresult when probe is failed from 3.0beta6
ResultPhaseCompletedWithProbeFailure ResultPhase = "Completed_With_Probe_Failure"
// ResultPhaseStopped is phase of chaosresult which is in stopped state
ResultPhaseStopped ResultPhase = "Stopped"
// ResultPhaseError is phase of chaosresult, which indicates that the experiment is terminated due to an error
ResultPhaseError ResultPhase = "Error"
)
// ResultVerdict is typecasted to string for supporting the values below.
@ -54,6 +60,10 @@ const (
ResultVerdictFailed ResultVerdict = "Fail"
// ResultVerdictStopped is verdict of chaosresult when experiment aborted
ResultVerdictStopped ResultVerdict = "Stopped"
// ResultVerdictAwaited is verdict of chaosresult when experiment is yet to evaluated(experiment is in running state)
ResultVerdictAwaited ResultVerdict = "Awaited"
// ResultVerdictError is verdict of chaosresult when experiment is completed because of an error
ResultVerdictError ResultVerdict = "Error"
)
type ProbeVerdict string
@ -116,12 +126,20 @@ type TestStatus struct {
Phase ResultPhase `json:"phase"`
// Verdict defines whether an experiment result is pass or fail
Verdict ResultVerdict `json:"verdict"`
// FailStep defines step where the experiments fails
FailStep string `json:"failStep,omitempty"`
// ErrorOutput defines error message and error code
ErrorOutput *ErrorOutput `json:"errorOutput,omitempty"`
// ProbeSuccessPercentage defines the score of the probes
ProbeSuccessPercentage string `json:"probeSuccessPercentage,omitempty"`
}
// ErrorOutput defines error reason and error code
type ErrorOutput struct {
// ErrorCode defines error code of the experiment
ErrorCode string `json:"errorCode,omitempty"`
// Reason contains the error reason
Reason string `json:"reason,omitempty"`
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// +genclient

View File

@ -113,6 +113,11 @@ func (in *ChaosEngineSpec) DeepCopyInto(out *ChaosEngineSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Selectors != nil {
in, out := &in.Selectors, &out.Selectors
*out = new(Selector)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChaosEngineSpec.
@ -314,7 +319,7 @@ func (in *ChaosResultSpec) DeepCopy() *ChaosResultSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ChaosResultStatus) DeepCopyInto(out *ChaosResultStatus) {
*out = *in
out.ExperimentStatus = in.ExperimentStatus
in.ExperimentStatus.DeepCopyInto(&out.ExperimentStatus)
if in.ProbeStatuses != nil {
in, out := &in.ProbeStatuses, &out.ProbeStatuses
*out = make([]ProbeStatuses, len(*in))
@ -341,7 +346,11 @@ func (in *ChaosResultStatus) DeepCopy() *ChaosResultStatus {
func (in *CmdProbeInputs) DeepCopyInto(out *CmdProbeInputs) {
*out = *in
out.Comparator = in.Comparator
in.Source.DeepCopyInto(&out.Source)
if in.Source != nil {
in, out := &in.Source, &out.Source
*out = new(SourceDetails)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CmdProbeInputs.
@ -373,6 +382,13 @@ func (in *ComparatorInfo) DeepCopy() *ComparatorInfo {
func (in *ComponentParams) DeepCopyInto(out *ComponentParams) {
*out = *in
in.Runner.DeepCopyInto(&out.Runner)
if in.Sidecar != nil {
in, out := &in.Sidecar, &out.Sidecar
*out = make([]Sidecar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ComponentParams.
@ -407,6 +423,36 @@ func (in *ConfigMap) DeepCopy() *ConfigMap {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ErrorOutput) DeepCopyInto(out *ErrorOutput) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrorOutput.
func (in *ErrorOutput) DeepCopy() *ErrorOutput {
if in == nil {
return nil
}
out := new(ErrorOutput)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EvaluationWindow) DeepCopyInto(out *EvaluationWindow) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EvaluationWindow.
func (in *EvaluationWindow) DeepCopy() *EvaluationWindow {
if in == nil {
return nil
}
out := new(EvaluationWindow)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ExperimentAttributes) DeepCopyInto(out *ExperimentAttributes) {
*out = *in
@ -613,8 +659,16 @@ func (in *GetMethod) DeepCopy() *GetMethod {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPMethod) DeepCopyInto(out *HTTPMethod) {
*out = *in
out.Get = in.Get
out.Post = in.Post
if in.Get != nil {
in, out := &in.Get, &out.Get
*out = new(GetMethod)
**out = **in
}
if in.Post != nil {
in, out := &in.Post, &out.Post
*out = new(PostMethod)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMethod.
@ -630,7 +684,7 @@ func (in *HTTPMethod) DeepCopy() *HTTPMethod {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPProbeInputs) DeepCopyInto(out *HTTPProbeInputs) {
*out = *in
out.Method = in.Method
in.Method.DeepCopyInto(&out.Method)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPProbeInputs.
@ -678,6 +732,21 @@ func (in *HostFile) DeepCopy() *HostFile {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Identifier) DeepCopyInto(out *Identifier) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Identifier.
func (in *Identifier) DeepCopy() *Identifier {
if in == nil {
return nil
}
out := new(Identifier)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *K8sProbeInputs) DeepCopyInto(out *K8sProbeInputs) {
*out = *in
@ -693,6 +762,21 @@ func (in *K8sProbeInputs) DeepCopy() *K8sProbeInputs {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Pod) DeepCopyInto(out *Pod) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pod.
func (in *Pod) DeepCopy() *Pod {
if in == nil {
return nil
}
out := new(Pod)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostMethod) DeepCopyInto(out *PostMethod) {
*out = *in
@ -711,10 +795,31 @@ func (in *PostMethod) DeepCopy() *PostMethod {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProbeAttributes) DeepCopyInto(out *ProbeAttributes) {
*out = *in
out.K8sProbeInputs = in.K8sProbeInputs
out.HTTPProbeInputs = in.HTTPProbeInputs
in.CmdProbeInputs.DeepCopyInto(&out.CmdProbeInputs)
out.PromProbeInputs = in.PromProbeInputs
if in.K8sProbeInputs != nil {
in, out := &in.K8sProbeInputs, &out.K8sProbeInputs
*out = new(K8sProbeInputs)
**out = **in
}
if in.HTTPProbeInputs != nil {
in, out := &in.HTTPProbeInputs, &out.HTTPProbeInputs
*out = new(HTTPProbeInputs)
(*in).DeepCopyInto(*out)
}
if in.CmdProbeInputs != nil {
in, out := &in.CmdProbeInputs, &out.CmdProbeInputs
*out = new(CmdProbeInputs)
(*in).DeepCopyInto(*out)
}
if in.PromProbeInputs != nil {
in, out := &in.PromProbeInputs, &out.PromProbeInputs
*out = new(PromProbeInputs)
**out = **in
}
if in.SLOProbeInputs != nil {
in, out := &in.SLOProbeInputs, &out.SLOProbeInputs
*out = new(SLOProbeInputs)
(*in).DeepCopyInto(*out)
}
out.RunProperties = in.RunProperties
}
@ -861,6 +966,44 @@ func (in *RunnerInfo) DeepCopy() *RunnerInfo {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SLOProbeInputs) DeepCopyInto(out *SLOProbeInputs) {
*out = *in
if in.EvaluationWindow != nil {
in, out := &in.EvaluationWindow, &out.EvaluationWindow
*out = new(EvaluationWindow)
**out = **in
}
out.SLOSourceMetadata = in.SLOSourceMetadata
out.Comparator = in.Comparator
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SLOProbeInputs.
func (in *SLOProbeInputs) DeepCopy() *SLOProbeInputs {
if in == nil {
return nil
}
out := new(SLOProbeInputs)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SLOSourceMetadata) DeepCopyInto(out *SLOSourceMetadata) {
*out = *in
out.Scope = in.Scope
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SLOSourceMetadata.
func (in *SLOSourceMetadata) DeepCopy() *SLOSourceMetadata {
if in == nil {
return nil
}
out := new(SLOSourceMetadata)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Secret) DeepCopyInto(out *Secret) {
*out = *in
@ -893,6 +1036,65 @@ func (in *SecurityContext) DeepCopy() *SecurityContext {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Selector) DeepCopyInto(out *Selector) {
*out = *in
if in.Workloads != nil {
in, out := &in.Workloads, &out.Workloads
*out = make([]Workload, len(*in))
copy(*out, *in)
}
if in.Pods != nil {
in, out := &in.Pods, &out.Pods
*out = make([]Pod, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Selector.
func (in *Selector) DeepCopy() *Selector {
if in == nil {
return nil
}
out := new(Selector)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Sidecar) DeepCopyInto(out *Sidecar) {
*out = *in
if in.Secrets != nil {
in, out := &in.Secrets, &out.Secrets
*out = make([]Secret, len(*in))
copy(*out, *in)
}
if in.EnvFrom != nil {
in, out := &in.EnvFrom, &out.EnvFrom
*out = make([]v1.EnvFromSource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.ENV != nil {
in, out := &in.ENV, &out.ENV
*out = make([]v1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sidecar.
func (in *Sidecar) DeepCopy() *Sidecar {
if in == nil {
return nil
}
out := new(Sidecar)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SourceDetails) DeepCopyInto(out *SourceDetails) {
*out = *in
@ -934,6 +1136,13 @@ func (in *SourceDetails) DeepCopyInto(out *SourceDetails) {
(*out)[key] = val
}
}
if in.Tolerations != nil {
in, out := &in.Tolerations, &out.Tolerations
*out = make([]v1.Toleration, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Volumes != nil {
in, out := &in.Volumes, &out.Volumes
*out = make([]v1.Volume, len(*in))
@ -998,6 +1207,11 @@ func (in *TargetDetails) DeepCopy() *TargetDetails {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TestStatus) DeepCopyInto(out *TestStatus) {
*out = *in
if in.ErrorOutput != nil {
in, out := &in.ErrorOutput, &out.ErrorOutput
*out = new(ErrorOutput)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestStatus.
@ -1009,3 +1223,18 @@ func (in *TestStatus) DeepCopy() *TestStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Workload) DeepCopyInto(out *Workload) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workload.
func (in *Workload) DeepCopy() *Workload {
if in == nil {
return nil
}
out := new(Workload)
in.DeepCopyInto(out)
return out
}

View File

@ -17,13 +17,15 @@ RUN go env
RUN CGO_ENABLED=0 go build -buildvcs=false -o /output/chaos-operator -v ./main.go
# Packaging stage
# Image source: https://github.com/litmuschaos/test-tools/blob/master/custom/hardened-alpine/infra/Dockerfile
# The base image is non-root (have litmus user) with default litmus directory.
FROM litmuschaos/infra-alpine
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.5
LABEL maintainer="LitmusChaos"
ENV OPERATOR=/usr/local/bin/chaos-operator
COPY --from=builder /output/chaos-operator ${OPERATOR}
RUN chown 65534:0 ${OPERATOR} && chmod 755 ${OPERATOR}
USER 65534
ENTRYPOINT ["/usr/local/bin/chaos-operator"]

View File

@ -19,12 +19,15 @@ package controllers
import (
"context"
"fmt"
"os"
"reflect"
"strings"
"time"
"github.com/go-logr/logr"
litmuschaosv1alpha1 "github.com/litmuschaos/chaos-operator/api/litmuschaos/v1alpha1"
"github.com/litmuschaos/chaos-operator/pkg/analytics"
dynamicclientset "github.com/litmuschaos/chaos-operator/pkg/client/dynamic"
clientset "github.com/litmuschaos/chaos-operator/pkg/client/kubernetes"
"github.com/litmuschaos/chaos-operator/pkg/resource"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
"github.com/litmuschaos/chaos-operator/pkg/utils"
"github.com/litmuschaos/chaos-operator/pkg/utils/retry"
@ -39,14 +42,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
"os"
"reflect"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"strings"
"time"
)
const finalizer = "chaosengine.litmuschaos.io/finalizer"
@ -104,13 +103,16 @@ func (r *ChaosEngineReconciler) Reconcile(ctx context.Context, request ctrl.Requ
}
// Start the reconcile by setting default values into ChaosEngine
if err := r.initEngine(engine); err != nil {
if requeue, err := r.initEngine(engine); err != nil {
if requeue {
return reconcile.Result{Requeue: true}, nil
}
return reconcile.Result{}, err
}
// Handling of normal execution of ChaosEngine
if engine.Instance.Spec.EngineState == litmuschaosv1alpha1.EngineStateActive && engine.Instance.Status.EngineStatus == litmuschaosv1alpha1.EngineStatusInitialized {
return r.reconcileForCreationAndRunning(engine, reqLogger)
return r.reconcileForCreationAndRunning(engine, *reqLogger)
}
// Handling Graceful completion of ChaosEngine
@ -137,24 +139,16 @@ func (r *ChaosEngineReconciler) Reconcile(ctx context.Context, request ctrl.Requ
}
// getChaosRunnerENV return the env required for chaos-runner
func getChaosRunnerENV(cr *litmuschaosv1alpha1.ChaosEngine, aExList []string, ClientUUID string) []corev1.EnvVar {
appNS := cr.Spec.Appinfo.Appns
if appNS == "" {
appNS = cr.Namespace
}
func getChaosRunnerENV(engine *chaosTypes.EngineInfo, ClientUUID string) []corev1.EnvVar {
var envDetails utils.ENVDetails
envDetails.SetEnv("CHAOSENGINE", cr.Name).
SetEnv("APP_LABEL", cr.Spec.Appinfo.Applabel).
SetEnv("APP_KIND", cr.Spec.Appinfo.AppKind).
SetEnv("APP_NAMESPACE", appNS).
SetEnv("EXPERIMENT_LIST", fmt.Sprint(strings.Join(aExList, ","))).
SetEnv("CHAOS_SVC_ACC", cr.Spec.ChaosServiceAccount).
SetEnv("AUXILIARY_APPINFO", cr.Spec.AuxiliaryAppInfo).
envDetails.SetEnv("CHAOSENGINE", engine.Instance.Name).
SetEnv("TARGETS", engine.Targets).
SetEnv("EXPERIMENT_LIST", fmt.Sprint(strings.Join(engine.AppExperiments, ","))).
SetEnv("CHAOS_SVC_ACC", engine.Instance.Spec.ChaosServiceAccount).
SetEnv("AUXILIARY_APPINFO", engine.Instance.Spec.AuxiliaryAppInfo).
SetEnv("CLIENT_UUID", ClientUUID).
SetEnv("CHAOS_NAMESPACE", cr.Namespace).
SetEnv("ANNOTATION_CHECK", cr.Spec.AnnotationCheck).
SetEnv("ANNOTATION_KEY", resource.GetAnnotationKey())
SetEnv("CHAOS_NAMESPACE", engine.Instance.Namespace)
return envDetails.ENV
}
@ -175,10 +169,15 @@ func getChaosRunnerLabels(cr *litmuschaosv1alpha1.ChaosEngine) map[string]string
// newGoRunnerPodForCR defines a new go-based Runner Pod
func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInfo) (*corev1.Pod, error) {
var experiment litmuschaosv1alpha1.ChaosExperiment
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: engine.Instance.Spec.Experiments[0].Name, Namespace: engine.Instance.Namespace}, &experiment); err != nil {
return nil, err
}
engine.VolumeOpts.VolumeOperations(engine.Instance.Spec.Components.Runner.ConfigMaps, engine.Instance.Spec.Components.Runner.Secrets)
containerForRunner := container.NewBuilder().
WithEnvsNew(getChaosRunnerENV(engine.Instance, engine.AppExperiments, analytics.ClientUUID)).
WithEnvsNew(getChaosRunnerENV(engine, analytics.ClientUUID)).
WithName("chaos-runner").
WithImage(engine.Instance.Spec.Components.Runner.Image).
WithImagePullPolicy(corev1.PullIfNotPresent)
@ -203,6 +202,10 @@ func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInf
containerForRunner.WithResourceRequirements(engine.Instance.Spec.Components.Runner.Resources)
}
if !reflect.DeepEqual(experiment.Spec.Definition.SecurityContext.ContainerSecurityContext, corev1.SecurityContext{}) {
containerForRunner.WithSecurityContext(experiment.Spec.Definition.SecurityContext.ContainerSecurityContext)
}
podForRunner := pod.NewBuilder().
WithName(engine.Instance.Name + "-runner").
WithNamespace(engine.Instance.Namespace).
@ -228,6 +231,10 @@ func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInf
podForRunner.WithImagePullSecrets(engine.Instance.Spec.Components.Runner.ImagePullSecrets)
}
if !reflect.DeepEqual(experiment.Spec.Definition.SecurityContext.PodSecurityContext, corev1.PodSecurityContext{}) {
podForRunner.WithSecurityContext(experiment.Spec.Definition.SecurityContext.PodSecurityContext)
}
runnerPod, err := podForRunner.Build()
if err != nil {
return nil, err
@ -238,29 +245,6 @@ func (r *ChaosEngineReconciler) newGoRunnerPodForCR(engine *chaosTypes.EngineInf
return runnerPod, nil
}
// initializeApplicationInfo to initialize application info
func initializeApplicationInfo(instance *litmuschaosv1alpha1.ChaosEngine, appInfo *chaosTypes.ApplicationInfo) (*chaosTypes.ApplicationInfo, error) {
if instance == nil {
return nil, errors.New("chaosengine is empty")
}
if instance.Spec.Appinfo.Applabel != "" {
appInfo.Label = instance.Spec.Appinfo.Applabel
}
if instance.Spec.Appinfo.Appns != "" {
appInfo.Namespace = instance.Spec.Appinfo.Appns
} else {
appInfo.Namespace = instance.Namespace
}
appInfo.Kind = instance.Spec.Appinfo.AppKind
appInfo.ExperimentList = instance.Spec.Experiments
appInfo.ServiceAccountName = instance.Spec.ChaosServiceAccount
return appInfo, nil
}
// engineRunnerPod to Check if the engineRunner pod already exists, else create
func engineRunnerPod(runnerPod *podEngineRunner) error {
if err := runnerPod.r.Client.Get(context.TODO(), types.NamespacedName{Name: runnerPod.engineRunner.Name, Namespace: runnerPod.engineRunner.Namespace}, runnerPod.pod); err != nil && k8serrors.IsNotFound(err) {
@ -291,30 +275,8 @@ func (r *ChaosEngineReconciler) getChaosEngineInstance(engine *chaosTypes.Engine
return err
}
engine.Instance = instance
return nil
}
// Get application details
func getApplicationDetail(engine *chaosTypes.EngineInfo) error {
applicationInfo := &chaosTypes.ApplicationInfo{}
appInfo, err := initializeApplicationInfo(engine.Instance, applicationInfo)
if err != nil {
return err
}
engine.AppInfo = appInfo
var appExperiments []string
for _, exp := range appInfo.ExperimentList {
appExperiments = append(appExperiments, exp.Name)
}
engine.AppExperiments = appExperiments
chaosTypes.Log.Info("App Label derived from Chaosengine is ", "appLabels", appInfo.Label)
chaosTypes.Log.Info("App NS derived from Chaosengine is ", "appNamespace", appInfo.Namespace)
chaosTypes.Log.Info("Exp list derived from chaosengine is ", "appExpirements", appExperiments)
chaosTypes.Log.Info("Runner image derived from chaosengine is", "runnerImage", engine.Instance.Spec.Components.Runner.Image)
chaosTypes.Log.Info("Annotation check is ", "annotationCheck", engine.Instance.Spec.AnnotationCheck)
engine.AppInfo = instance.Spec.Appinfo
engine.Selectors = instance.Spec.Selectors
return nil
}
@ -357,19 +319,6 @@ func setChaosResourceImage(engine *chaosTypes.EngineInfo) {
}
}
// getAnnotationCheck() checks for annotation on the application
func getAnnotationCheck(engine *chaosTypes.EngineInfo) error {
if engine.Instance.Spec.AnnotationCheck == "" {
engine.Instance.Spec.AnnotationCheck = chaosTypes.DefaultAnnotationCheck
}
if engine.Instance.Spec.AnnotationCheck != "true" && engine.Instance.Spec.AnnotationCheck != "false" {
return fmt.Errorf("annotationCheck '%s', is not supported it should be either true or false", engine.Instance.Spec.AnnotationCheck)
}
return nil
}
// reconcileForDelete reconciles for deletion/force deletion of Chaos Engine
func (r *ChaosEngineReconciler) reconcileForDelete(engine *chaosTypes.EngineInfo, request reconcile.Request) (reconcile.Result, error) {
patch := client.MergeFrom(engine.Instance.DeepCopy())
@ -538,8 +487,11 @@ func (r *ChaosEngineReconciler) reconcileForRestartAfterAbort(engine *chaosTypes
return reconcile.Result{}, err
}
if err := r.updateEngineForRestart(engine); err != nil {
return reconcile.Result{}, nil
if requeue, err := r.updateEngineForRestart(engine); err != nil {
if requeue {
return reconcile.Result{Requeue: true}, nil
}
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
@ -574,7 +526,7 @@ func (r *ChaosEngineReconciler) reconcileForRestartAfterComplete(engine *chaosTy
}
// initEngine initialize Chaos Engine, and add a finalizer to it.
func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) error {
func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) (bool, error) {
if engine.Instance.Spec.EngineState == "" {
engine.Instance.Spec.EngineState = litmuschaosv1alpha1.EngineStateActive
}
@ -587,32 +539,29 @@ func (r *ChaosEngineReconciler) initEngine(engine *chaosTypes.EngineInfo) error
if engine.Instance.ObjectMeta.Finalizers == nil {
engine.Instance.ObjectMeta.Finalizers = append(engine.Instance.ObjectMeta.Finalizers, finalizer)
if err := r.Client.Update(context.TODO(), engine.Instance, &client.UpdateOptions{}); err != nil {
return fmt.Errorf("unable to initialize ChaosEngine, because of Update Error: %v", err)
if k8serrors.IsConflict(err) {
return true, err
}
return false, fmt.Errorf("unable to initialize ChaosEngine, because of Update Error: %v", err)
}
// generate the ChaosEngineInitialized event once finalizer has been added
r.Recorder.Eventf(engine.Instance, corev1.EventTypeNormal, "ChaosEngineInitialized", "Identifying app under test & launching %s", engine.Instance.Name+"-runner")
}
}
return nil
return false, nil
}
// reconcileForCreationAndRunning reconciles for Chaos execution of Chaos Engine
func (r *ChaosEngineReconciler) reconcileForCreationAndRunning(engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) {
if err := r.validateAnnotatedApplication(engine); err != nil {
if stopEngineWithAnnotationErrorMessage := r.updateEngineState(engine, litmuschaosv1alpha1.EngineStateStop); stopEngineWithAnnotationErrorMessage != nil {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos stop) Unable to update chaosengine")
return reconcile.Result{}, fmt.Errorf("unable to Update Engine State: %v", err)
var runner corev1.Pod
if err := r.Client.Get(context.TODO(), types.NamespacedName{Name: engine.Instance.Name + "-runner", Namespace: engine.Instance.Namespace}, &runner); err != nil {
if k8serrors.IsNotFound(err) {
return r.createRunnerPod(engine, reqLogger)
}
return reconcile.Result{}, err
}
// Check if the engineRunner pod already exists, else create
if err := r.checkEngineRunnerPod(engine, reqLogger); err != nil {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos start) Unable to get chaos resources")
return reconcile.Result{}, err
}
isCompleted, err := r.checkRunnerContainerCompletedStatus(engine)
if err != nil {
if k8serrors.IsNotFound(err) {
@ -623,15 +572,103 @@ func (r *ChaosEngineReconciler) reconcileForCreationAndRunning(engine *chaosType
}
if isCompleted {
if err := r.updateEngineForComplete(engine, isCompleted); err != nil {
if requeue, err := r.updateEngineForComplete(engine, isCompleted); err != nil {
if requeue {
return reconcile.Result{Requeue: true}, nil
}
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos completed) Unable to update chaos engine")
return reconcile.Result{}, err
}
}
reqLogger.Info("Skip reconcile: engineRunner Pod already exists", "Pod.Namespace", runner.Namespace, "Pod.Name", runner.Name)
return reconcile.Result{}, nil
}
func (r *ChaosEngineReconciler) createRunnerPod(engine *chaosTypes.EngineInfo, reqLogger logr.Logger) (reconcile.Result, error) {
if err := r.setExperimentDetails(engine); err != nil {
if updateEngineErr := r.updateEngineState(engine, litmuschaosv1alpha1.EngineStateStop); updateEngineErr != nil {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos stop) Unable to update chaosengine")
return reconcile.Result{}, fmt.Errorf("unable to Update Engine State: %v", err)
}
return reconcile.Result{}, err
}
// Check if the engineRunner pod already exists, else create
if err := r.checkEngineRunnerPod(engine, reqLogger); err != nil {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(chaos start) Unable to get chaos resources")
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
func (r *ChaosEngineReconciler) setExperimentDetails(engine *chaosTypes.EngineInfo) error {
// Get the image for runner pod from chaosengine spec,operator env or default values.
setChaosResourceImage(engine)
if engine.Selectors != nil && engine.Selectors.Workloads == nil && engine.Selectors.Pods == nil {
return fmt.Errorf("specify one out of workloads or pods")
}
if (engine.AppInfo.AppKind != "") != (engine.AppInfo.Applabel != "") {
return fmt.Errorf("incomplete appinfo, provide appkind and applabel both")
}
engine.Targets = getTargets(engine)
var appExperiments []string
for _, exp := range engine.Instance.Spec.Experiments {
appExperiments = append(appExperiments, exp.Name)
}
engine.AppExperiments = appExperiments
chaosTypes.Log.Info("Targets derived from Chaosengine is ", "targets", engine.Targets)
chaosTypes.Log.Info("Exp list derived from chaosengine is ", "appExpirements", appExperiments)
chaosTypes.Log.Info("Runner image derived from chaosengine is", "runnerImage", engine.Instance.Spec.Components.Runner.Image)
return nil
}
func getTargets(engine *chaosTypes.EngineInfo) string {
if engine.Selectors == nil && reflect.DeepEqual(engine.AppInfo, litmuschaosv1alpha1.ApplicationParams{}) {
return ""
}
var targets []string
if engine.Selectors != nil {
if engine.Selectors.Workloads != nil {
for _, w := range engine.Selectors.Workloads {
var filter string
if w.Names != "" {
filter = w.Names
} else {
filter = w.Labels
}
target := strings.Join([]string{string(w.Kind), w.Namespace, fmt.Sprintf("[%v]", filter)}, ":")
targets = append(targets, target)
}
return strings.Join(targets, ";")
}
for _, w := range engine.Selectors.Pods {
target := strings.Join([]string{"pod", w.Namespace, fmt.Sprintf("[%v]", w.Names)}, ":")
targets = append(targets, target)
}
return strings.Join(targets, ";")
}
if engine.AppInfo.Appns == "" {
engine.AppInfo.Appns = engine.Instance.Namespace
}
if engine.AppInfo.AppKind == "" {
engine.AppInfo.AppKind = "KIND"
}
return strings.Join([]string{engine.AppInfo.AppKind, engine.AppInfo.Appns, fmt.Sprintf("[%v]", engine.AppInfo.Applabel)}, ":")
}
// updateExperimentStatusesForStop updates ChaosEngine.Status.Experiment with Abort Status.
func updateExperimentStatusesForStop(engine *chaosTypes.EngineInfo) {
for i := range engine.Instance.Status.Experiments {
@ -643,80 +680,41 @@ func updateExperimentStatusesForStop(engine *chaosTypes.EngineInfo) {
}
}
func startReqLogger(request reconcile.Request) logr.Logger {
func startReqLogger(request reconcile.Request) *logr.Logger {
reqLogger := chaosTypes.Log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.Info("Reconciling ChaosEngine")
return reqLogger
return &reqLogger
}
func (r *ChaosEngineReconciler) validateAnnotatedApplication(engine *chaosTypes.EngineInfo) error {
// Get the image for runner pod from chaosengine spec,operator env or default values.
setChaosResourceImage(engine)
clientSet, err := clientset.CreateClientSet()
if err != nil {
return err
}
dynamicClient, err := dynamicclientset.CreateClientSet()
if err != nil {
return err
}
// getAnnotationCheck fetch the annotationCheck from engine spec
if err = getAnnotationCheck(engine); err != nil {
return err
}
// Fetch the app details from ChaosEngine instance. Check if app is present
// Also check, if the app is annotated for chaos & that the labels are unique
if err = getApplicationDetail(engine); err != nil {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(appinfo derivation) Unable to get chaosengine")
return err
}
if engine.Instance.Spec.AnnotationCheck == "true" {
if engine.AppInfo.Label == "" || engine.AppInfo.Namespace == "" || engine.AppInfo.Kind == "" {
return errors.Errorf("incomplete AppInfo inside chaosengine")
}
// Determine whether apps with matching labels have chaos annotation set to true
engine, err = resource.CheckChaosAnnotation(engine, clientSet, *dynamicClient)
if err != nil {
// using an event msg that indicates the app couldn't be identified. By this point in execution,
// if the engine could not be found or accessed, it would already be caught in r.initEngine & getApplicationDetail
r.Recorder.Eventf(engine.Instance, corev1.EventTypeWarning, "ChaosResourcesOperationFailed", "(app indentification) Unable to filter app by specified info")
chaosTypes.Log.Info("Annotation check failed with", "error:", err)
return err
}
}
return nil
}
func (r *ChaosEngineReconciler) updateEngineForComplete(engine *chaosTypes.EngineInfo, isCompleted bool) error {
func (r *ChaosEngineReconciler) updateEngineForComplete(engine *chaosTypes.EngineInfo, isCompleted bool) (bool, error) {
if engine.Instance.Status.EngineStatus != litmuschaosv1alpha1.EngineStatusCompleted {
engine.Instance.Status.EngineStatus = litmuschaosv1alpha1.EngineStatusCompleted
engine.Instance.Spec.EngineState = litmuschaosv1alpha1.EngineStateStop
if err := r.Client.Update(context.TODO(), engine.Instance, &client.UpdateOptions{}); err != nil {
return fmt.Errorf("unable to update ChaosEngine Status, due to update error: %v", err)
if k8serrors.IsConflict(err) {
return true, err
}
return false, fmt.Errorf("unable to update ChaosEngine Status, due to update error: %v", err)
}
r.Recorder.Eventf(engine.Instance, corev1.EventTypeNormal, "ChaosEngineCompleted", "ChaosEngine completed, will delete or retain the resources according to jobCleanUpPolicy")
}
return nil
return false, nil
}
func (r *ChaosEngineReconciler) updateEngineForRestart(engine *chaosTypes.EngineInfo) error {
func (r *ChaosEngineReconciler) updateEngineForRestart(engine *chaosTypes.EngineInfo) (bool, error) {
r.Recorder.Eventf(engine.Instance, corev1.EventTypeNormal, "RestartInProgress", "ChaosEngine is restarted")
engine.Instance.Status.EngineStatus = litmuschaosv1alpha1.EngineStatusInitialized
engine.Instance.Status.Experiments = nil
if err := r.Client.Update(context.TODO(), engine.Instance, &client.UpdateOptions{}); err != nil {
return fmt.Errorf("unable to restart ChaosEngine, due to update error: %v", err)
if k8serrors.IsConflict(err) {
return true, err
}
return false, fmt.Errorf("unable to restart ChaosEngine, due to update error: %v", err)
}
return nil
return false, nil
}
// updateChaosStatus update the chaos status inside the chaosresult
@ -829,7 +827,7 @@ func isResultCRDAvailable() (bool, error) {
Resource: "customresourcedefinitions",
}
resultList, err := (*dynamicClient).Resource(gvr).List(context.Background(), v1.ListOptions{})
resultList, err := dynamicClient.Resource(gvr).List(context.Background(), v1.ListOptions{})
if err != nil {
return false, err
}

View File

@ -34,61 +34,13 @@ import (
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
func TestInitializeApplicationInfo(t *testing.T) {
tests := map[string]struct {
instance *v1alpha1.ChaosEngine
isErr bool
}{
"Test Positive-1": {
instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "test-monitor",
Namespace: "test",
},
Spec: v1alpha1.ChaosEngineSpec{
Appinfo: v1alpha1.ApplicationParams{
Applabel: "key=value",
},
},
},
isErr: false,
},
"Test Negative-1": {
instance: nil,
isErr: true,
},
}
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
appInfo := &chaosTypes.ApplicationInfo{
Namespace: "namespace",
Label: "fake_id=aa",
ExperimentList: []v1alpha1.ExperimentList{
{
Name: "fake_name",
},
},
ServiceAccountName: "fake-service-account-name",
}
_, err := initializeApplicationInfo(mock.instance, appInfo)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
if !mock.isErr && err != nil {
fmt.Println(err)
t.Fatalf("Test %q failed: expected error to be nil", name)
}
})
}
}
func TestGetChaosRunnerENV(t *testing.T) {
fakeEngineName := "Fake Engine"
fakeNameSpace := "Fake NameSpace"
fakeServiceAcc := "Fake Service Account"
fakeTargets := "fakeTargets"
fakeAppLabel := "Fake Label"
fakeAppKind := "Fake Kind"
fakeAnnotationCheck := "Fake Annotation Check"
fakeAnnotationKey := "litmuschaos.io/chaos"
fakeAExList := []string{"fake string"}
fakeAuxilaryAppInfo := "ns1:name=percona,ns2:run=nginx"
fakeClientUUID := "12345678-9012-3456-7890-123456789012"
@ -106,7 +58,6 @@ func TestGetChaosRunnerENV(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: fakeServiceAcc,
AnnotationCheck: fakeAnnotationCheck,
Appinfo: v1alpha1.ApplicationParams{
Applabel: fakeAppLabel,
Appns: fakeNameSpace,
@ -122,16 +73,8 @@ func TestGetChaosRunnerENV(t *testing.T) {
Value: fakeEngineName,
},
{
Name: "APP_LABEL",
Value: fakeAppLabel,
},
{
Name: "APP_KIND",
Value: fakeAppKind,
},
{
Name: "APP_NAMESPACE",
Value: fakeNameSpace,
Name: "TARGETS",
Value: fakeTargets,
},
{
Name: "EXPERIMENT_LIST",
@ -153,23 +96,16 @@ func TestGetChaosRunnerENV(t *testing.T) {
Name: "CHAOS_NAMESPACE",
Value: fakeNameSpace,
},
{
Name: "ANNOTATION_CHECK",
Value: fakeAnnotationCheck,
},
{
Name: "ANNOTATION_KEY",
Value: fakeAnnotationKey,
},
},
},
}
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
actualResult := getChaosRunnerENV(mock.instance, mock.aExList, fakeClientUUID)
println(actualResult)
if len(actualResult) != 11 {
t.Fatalf("Test %q failed: expected array length to be 11", name)
engine := &chaosTypes.EngineInfo{Instance: mock.instance, Targets: fakeTargets, AppExperiments: fakeAExList}
actualResult := getChaosRunnerENV(engine, fakeClientUUID)
println(len(actualResult))
if len(actualResult) != 7 {
t.Fatalf("Test %q failed: expected array length to be 7", name)
}
for index, result := range actualResult {
if result.Value != mock.expectedResult[index].Value {
@ -180,260 +116,6 @@ func TestGetChaosRunnerENV(t *testing.T) {
}
}
func TestGetApplicationDetail(t *testing.T) {
tests := map[string]struct {
engine chaosTypes.EngineInfo
isErr bool
}{
"Test Positive-1": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "test-monitor",
Namespace: "test",
},
Spec: v1alpha1.ChaosEngineSpec{
Appinfo: v1alpha1.ApplicationParams{
Applabel: "key=value",
},
},
},
},
isErr: false,
},
"Test Negative": {
engine: chaosTypes.EngineInfo{
Instance: nil,
},
isErr: true,
},
}
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
err := getApplicationDetail(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
if !mock.isErr && err != nil {
fmt.Println(err)
t.Fatalf("Test %q failed: expected error to be nil", name)
}
})
}
}
func TestGetAnnotationCheck(t *testing.T) {
tests := map[string]struct {
engine chaosTypes.EngineInfo
isErr bool
}{
"Test Positive-1": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "test-runner",
Namespace: "test",
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
},
AnnotationCheck: "true",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
isErr: false,
},
"Test Positive-2": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "test-runner",
Namespace: "test",
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
},
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
isErr: false,
},
"Test Negative-1": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "test-runner",
Namespace: "test",
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "fakeCheck",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
},
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
isErr: true,
},
}
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
err := getAnnotationCheck(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
if !mock.isErr && err != nil {
t.Fatalf("Test %q failed: expected error to be nil", name)
}
})
}
}
func TestValidateAnnontatedApplication(t *testing.T) {
tests := map[string]struct {
engine chaosTypes.EngineInfo
isErr bool
}{
"Test Positive-1": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "validate-annotation-p2",
Namespace: "default",
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
EngineState: "active",
AnnotationCheck: "false",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "app=nginx",
AppKind: "deployment",
},
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
isErr: false,
},
"Test Negetive-1": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "validate-annotation-n1",
Namespace: "default",
},
Spec: v1alpha1.ChaosEngineSpec{
Appinfo: v1alpha1.ApplicationParams{
Applabel: "app=nginx",
AppKind: "deployment",
},
EngineState: "active",
AnnotationCheck: "dummy",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
},
isErr: true,
},
"Test Negetive-2": {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{
Name: "validate-annotation-n2",
Namespace: "default",
},
Spec: v1alpha1.ChaosEngineSpec{
AnnotationCheck: "true",
EngineState: "active",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "app=nginx",
AppKind: "deployment",
},
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
isErr: true,
},
}
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
r := CreateFakeClient(t)
err := r.Client.Create(context.TODO(), mock.engine.Instance)
if err != nil {
fmt.Printf("Unable to create engine: %v", err)
}
err = r.validateAnnotatedApplication(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
if !mock.isErr && err != nil {
t.Fatalf("Test %q failed: expected error to be nil", name)
}
})
}
}
func TestUpdateEngineForComplete(t *testing.T) {
tests := map[string]struct {
engine chaosTypes.EngineInfo
@ -452,7 +134,6 @@ func TestUpdateEngineForComplete(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -484,7 +165,6 @@ func TestUpdateEngineForComplete(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
@ -511,7 +191,6 @@ func TestUpdateEngineForComplete(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
@ -534,7 +213,7 @@ func TestUpdateEngineForComplete(t *testing.T) {
fmt.Printf("Unable to create engine: %v", err)
}
err = r.updateEngineForComplete(&mock.engine, true)
_, err = r.updateEngineForComplete(&mock.engine, true)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
@ -563,7 +242,6 @@ func TestUpdateEngineForRestart(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -595,7 +273,6 @@ func TestUpdateEngineForRestart(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
@ -618,7 +295,7 @@ func TestUpdateEngineForRestart(t *testing.T) {
fmt.Printf("Unable to create engine: %v", err)
}
err = r.updateEngineForRestart(&mock.engine)
_, err = r.updateEngineForRestart(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
@ -652,6 +329,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -678,6 +360,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -695,7 +382,6 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -706,6 +392,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -723,7 +414,6 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "true",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -734,6 +424,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -746,6 +441,13 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ChaosEngineSpec{
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
AppExperiments: []string{"exp-1"},
},
@ -760,6 +462,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -776,6 +483,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -797,6 +509,11 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
Image: "",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "pod-delete",
},
},
},
},
@ -808,6 +525,15 @@ func TestNewGoRunnerPodForCR(t *testing.T) {
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
r := CreateFakeClient(t)
exp := v1alpha1.ChaosExperiment{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-delete",
Namespace: "test",
},
}
if err := r.Client.Create(context.TODO(), &exp); err != nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
_, err := r.newGoRunnerPodForCR(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
@ -836,7 +562,6 @@ func TestInitEngine(t *testing.T) {
Applabel: "app=nginx",
AppKind: "deployment",
},
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -868,7 +593,6 @@ func TestInitEngine(t *testing.T) {
AppKind: "deployment",
},
EngineState: "active",
AnnotationCheck: "false",
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
@ -886,7 +610,7 @@ func TestInitEngine(t *testing.T) {
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
r := CreateFakeClient(t)
err := r.initEngine(&mock.engine)
_, err := r.initEngine(&mock.engine)
if mock.isErr && err == nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
@ -916,7 +640,6 @@ func TestUpdateEngineState(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -949,7 +672,6 @@ func TestUpdateEngineState(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1006,7 +728,6 @@ func TestCheckRunnerPodCompletedStatus(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1038,7 +759,6 @@ func TestCheckRunnerPodCompletedStatus(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1189,7 +909,6 @@ func TestGetChaosEngineInstance(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1227,7 +946,6 @@ func TestGetChaosEngineInstance(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1292,6 +1010,11 @@ func TestCheckEngineRunnerPod(t *testing.T) {
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
@ -1313,6 +1036,11 @@ func TestCheckEngineRunnerPod(t *testing.T) {
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
@ -1330,12 +1058,16 @@ func TestCheckEngineRunnerPod(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
@ -1353,12 +1085,16 @@ func TestCheckEngineRunnerPod(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "true",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
},
},
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
@ -1371,6 +1107,13 @@ func TestCheckEngineRunnerPod(t *testing.T) {
engine: chaosTypes.EngineInfo{
Instance: &v1alpha1.ChaosEngine{
ObjectMeta: metav1.ObjectMeta{},
Spec: v1alpha1.ChaosEngineSpec{
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
AppExperiments: []string{"exp-1"},
@ -1386,6 +1129,11 @@ func TestCheckEngineRunnerPod(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Experiments: []v1alpha1.ExperimentList{
{
Name: "exp-1",
},
},
},
},
@ -1434,6 +1182,15 @@ func TestCheckEngineRunnerPod(t *testing.T) {
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
r := CreateFakeClient(t)
exp := v1alpha1.ChaosExperiment{
ObjectMeta: metav1.ObjectMeta{
Name: "exp-1",
Namespace: "test",
},
}
if err := r.Client.Create(context.TODO(), &exp); err != nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
reqLogger := chaosTypes.Log.WithValues()
err := r.checkEngineRunnerPod(&mock.engine, reqLogger)
if mock.isErr && err == nil {
@ -1465,7 +1222,6 @@ func TestReconcileForDelete(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1503,7 +1259,6 @@ func TestReconcileForDelete(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1568,7 +1323,6 @@ func TestForceRemoveAllChaosPods(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1606,7 +1360,6 @@ func TestForceRemoveAllChaosPods(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
},
},
},
@ -1656,7 +1409,6 @@ func TestGracefullyRemoveDefaultChaosResources(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1694,7 +1446,6 @@ func TestGracefullyRemoveDefaultChaosResources(t *testing.T) {
AppKind: "deployment",
},
EngineState: v1alpha1.EngineStateActive,
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1753,9 +1504,10 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
AnnotationCheck: "false",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1769,6 +1521,12 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
},
},
AppInfo: v1alpha1.ApplicationParams{
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
AppExperiments: []string{"exp-1"},
},
isErr: false,
@ -1782,9 +1540,10 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
},
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
AnnotationCheck: "fakeCheck",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
@ -1794,6 +1553,12 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
},
},
AppInfo: v1alpha1.ApplicationParams{
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
AppExperiments: []string{"exp-1"},
},
isErr: true,
@ -1808,9 +1573,10 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
Spec: v1alpha1.ChaosEngineSpec{
ChaosServiceAccount: "fake-serviceAccount",
Appinfo: v1alpha1.ApplicationParams{
Applabel: "run=nginx",
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
AnnotationCheck: "true",
Components: v1alpha1.ComponentParams{
Runner: v1alpha1.RunnerInfo{
Image: "fake-runner-image",
@ -1818,6 +1584,11 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
},
},
},
AppInfo: v1alpha1.ApplicationParams{
Applabel: "fakeAppLabel",
Appns: "fakeAppNs",
AppKind: "fakeAppKind",
},
AppExperiments: []string{"exp-1"},
},
@ -1827,6 +1598,15 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
for name, mock := range tests {
t.Run(name, func(t *testing.T) {
r := CreateFakeClient(t)
exp := v1alpha1.ChaosExperiment{
ObjectMeta: metav1.ObjectMeta{
Name: "exp-1",
Namespace: "test",
},
}
if err := r.Client.Create(context.TODO(), &exp); err != nil {
t.Fatalf("Test %q failed: expected error not to be nil", name)
}
reqLogger := chaosTypes.Log.WithValues()
_, err := r.reconcileForCreationAndRunning(&mock.engine, reqLogger)
if mock.isErr && err == nil {
@ -1841,7 +1621,7 @@ func TestReconcileForCreationAndRunning(t *testing.T) {
func CreateFakeClient(t *testing.T) *ChaosEngineReconciler {
fakeClient := litmusFakeClientset.NewFakeClient()
fakeClient := litmusFakeClientset.NewClientBuilder().WithRuntimeObjects().Build()
if fakeClient == nil {
fmt.Println("litmusClient is not created")
}
@ -1855,11 +1635,18 @@ func CreateFakeClient(t *testing.T) *ChaosEngineReconciler {
},
}
exp := &v1alpha1.ChaosExperiment{
ObjectMeta: metav1.ObjectMeta{
Labels: make(map[string]string),
Name: "dummyexp",
},
}
chaosResultList := &v1alpha1.ChaosResultList{
Items: []v1alpha1.ChaosResult{},
}
s.AddKnownTypes(v1alpha1.SchemeGroupVersion, engineR, chaosResultList)
s.AddKnownTypes(v1alpha1.SchemeGroupVersion, engineR, chaosResultList, exp)
recorder := record.NewFakeRecorder(1024)

View File

@ -40,12 +40,8 @@ spec:
#oneOf:
# - pattern: '^delete$'
# - pattern: '^retain$'
annotationCheck:
type: string
pattern: ^(true|false)$
defaultHealthCheck:
type: string
pattern: ^(true|false)$
type: boolean
appinfo:
type: object
properties:
@ -56,6 +52,44 @@ spec:
type: string
appns:
type: string
selectors:
type: object
properties:
pods:
items:
properties:
names:
type: string
namespace:
type: string
required:
- names
- namespace
type: object
type: array
workloads:
items:
properties:
kind:
type: string
pattern: ^(^$|deployment|statefulset|daemonset|deploymentconfig|rollout)$
labels:
type: string
names:
type: string
namespace:
type: string
oneOf:
- required: [ names ]
- required: [ labels ]
required:
- kind
- namespace
type: object
type: array
oneOf:
- required: [ pods ]
- required: [ workloads ]
auxiliaryAppInfo:
type: string
engineState:
@ -68,6 +102,180 @@ spec:
components:
type: object
properties:
sidecar:
type: array
items:
type: object
properties:
env:
description: ENV contains ENV passed to the sidecar container
items:
description: EnvVar represents an environment variable
present in a Container.
properties:
name:
description: Name of the environment variable. Must
be a C_IDENTIFIER.
type: string
value:
description: 'Variable references $(VAR_NAME) are
expanded using the previous defined environment
variables in the container and any service environment
variables. If a variable cannot be resolved, the
reference in the input string will be unchanged.
The $(VAR_NAME) syntax can be escaped with a double
$$, ie: $$(VAR_NAME). Escaped references will never
be expanded, regardless of whether the variable
exists or not. Defaults to "".'
type: string
valueFrom:
description: Source for the environment variable's
value. Cannot be used if value is not empty.
properties:
configMapKeyRef:
description: Selects a key of a ConfigMap.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion,
kind, uid?'
type: string
optional:
description: Specify whether the ConfigMap
or its key must be defined
type: boolean
required:
- key
type: object
fieldRef:
description: 'Selects a field of the pod: supports
metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`,
`metadata.annotations[''<KEY>'']`, spec.nodeName,
spec.serviceAccountName, status.hostIP, status.podIP,
status.podIPs.'
properties:
apiVersion:
description: Version of the schema the FieldPath
is written in terms of, defaults to "v1".
type: string
fieldPath:
description: Path of the field to select in
the specified API version.
type: string
required:
- fieldPath
type: object
resourceFieldRef:
description: 'Selects a resource of the container:
only resources limits and requests (limits.cpu,
limits.memory, limits.ephemeral-storage, requests.cpu,
requests.memory and requests.ephemeral-storage)
are currently supported.'
properties:
containerName:
description: 'Container name: required for
volumes, optional for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
description: Specifies the output format of
the exposed resources, defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
description: 'Required: resource to select'
type: string
required:
- resource
type: object
secretKeyRef:
description: Selects a key of a secret in the
pod's namespace
properties:
key:
description: The key of the secret to select
from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion,
kind, uid?'
type: string
optional:
description: Specify whether the Secret or
its key must be defined
type: boolean
required:
- key
type: object
type: object
required:
- name
type: object
type: array
envFrom:
description: EnvFrom for the sidecar container
items:
description: EnvFromSource represents the source of a
set of ConfigMaps
properties:
configMapRef:
description: The ConfigMap to select from
properties:
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap must
be defined
type: boolean
type: object
prefix:
description: An optional identifier to prepend to
each key in the ConfigMap. Must be a C_IDENTIFIER.
type: string
secretRef:
description: The Secret to select from
properties:
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret must be
defined
type: boolean
type: object
type: object
type: array
image:
type: string
imagePullPolicy:
type: string
secrets:
items:
properties:
mountPath:
type: string
name:
type: string
required:
- mountPath
- name
type: object
type: array
runner:
x-kubernetes-preserve-unknown-fields: true
type: object
@ -128,6 +336,9 @@ spec:
items:
type: object
required:
- name
- type
- mode
- runProperties
properties:
name:
@ -135,9 +346,13 @@ spec:
type:
type: string
minLength: 1
pattern: ^(k8sProbe|httpProbe|cmdProbe|promProbe)$
pattern: ^(k8sProbe|httpProbe|cmdProbe|promProbe|sloProbe)$
k8sProbe/inputs:
type: object
required:
- version
- resource
- operation
properties:
group:
type: string
@ -147,6 +362,8 @@ spec:
type: string
namespace:
type: string
resourceNames:
type: string
fieldSelector:
type: string
labelSelector:
@ -157,12 +374,19 @@ spec:
minLength: 1
cmdProbe/inputs:
type: object
required:
- command
- comparator
properties:
command:
type: string
minLength: 1
comparator:
type: object
required:
- type
- criteria
- value
properties:
type:
type: string
@ -175,6 +399,8 @@ spec:
source:
description: The external pod where we have to run the
probe commands. It will run the commands inside the experiment pod itself(inline mode) if source contains a nil value
required:
- image
properties:
annotations:
additionalProperties:
@ -350,6 +576,29 @@ spec:
type: string
description: NodeSelector for the source pod
type: object
tolerations:
description: Tolerations for the source pod
items:
description: The pod with this Toleration tolerates any taint matches the <key,value,effect> using the matching operator <operator>.
properties:
effect:
description: Effect to match. Empty means all effects.
type: string
key:
description: Taint key the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists.
type: string
operator:
description: Operators are Exists or Equal. Defaults to Equal.
type: string
tolerationSeconds:
description: Period of time the toleration tolerates the taint.
format: int64
type: integer
value:
description: If the operator is Exists, the value should be empty, otherwise just a regular string.
type: string
type: object
type: array
privileged:
description: Privileged for the source pod
type: boolean
@ -1958,6 +2207,9 @@ spec:
type: object
httpProbe/inputs:
type: object
required:
- url
- method
properties:
url:
type: string
@ -1970,6 +2222,9 @@ spec:
properties:
get:
type: object
required:
- criteria
- responseCode
properties:
criteria:
type: string
@ -1979,6 +2234,9 @@ spec:
minLength: 1
post:
type: object
required:
- criteria
- responseCode
properties:
contentType:
type: string
@ -1995,6 +2253,9 @@ spec:
minLength: 1
promProbe/inputs:
type: object
required:
- endpoint
- comparator
properties:
endpoint:
type: string
@ -2004,6 +2265,9 @@ spec:
type: string
comparator:
type: object
required:
- criteria
- value
properties:
criteria:
type: string
@ -2016,18 +2280,110 @@ spec:
- probeTimeout
- interval
properties:
evaluationTimeout:
type: string
probeTimeout:
type: integer
type: string
interval:
type: integer
type: string
retry:
type: integer
attempt:
type: integer
probePollingInterval:
type: integer
initialDelaySeconds:
type: integer
type: string
initialDelay:
type: string
verbosity:
type: string
stopOnFailure:
type: boolean
sloProbe/inputs:
description: inputs needed for the SLO probe
required:
- platformEndpoint
- sloIdentifier
- sloSourceMetadata
- comparator
properties:
comparator:
description: Comparator check for the correctness
of the probe output
required:
- criteria
- value
properties:
criteria:
description: Criteria for matching data it
supports >=, <=, ==, >, <, != for int and
float it supports equal, notEqual, contains
for string
type: string
type:
description: Type of data it can be int, float,
string
type: string
value:
description: Value contains relative value
for criteria
type: string
type: object
evaluationWindow:
description: EvaluationWindow is the time period
for which the metrics will be evaluated
properties:
evaluationEndTime:
description: End time of evaluation
type: integer
evaluationStartTime:
description: Start time of evaluation
type: integer
type: object
platformEndpoint:
description: PlatformEndpoint for the monitoring
service endpoint
type: string
insecureSkipVerify:
description: InsecureSkipVerify flag to skip certificate
checks
type: boolean
sloIdentifier:
description: SLOIdentifier for fetching the details
of the SLO
type: string
sloSourceMetadata:
description: SLOSourceMetadata consists of required
metadata details to fetch metric data
required:
- apiTokenSecret
- scope
properties:
apiTokenSecret:
description: APITokenSecret for authenticating
with the platform service
type: string
scope:
description: Scope required for fetching details
required:
- accountIdentifier
- orgIdentifier
- projectIdentifier
properties:
accountIdentifier:
description: AccountIdentifier for account
ID
type: string
orgIdentifier:
description: OrgIdentifier for organization
ID
type: string
projectIdentifier:
description: ProjectIdentifier for project
ID
type: string
type: object
type: object
type: object
mode:
type: string
pattern: ^(SOT|EOT|Edge|Continuous|OnChaos)$

View File

@ -39,12 +39,8 @@ spec:
#oneOf:
# - pattern: '^delete$'
# - pattern: '^retain$'
annotationCheck:
type: string
pattern: ^(true|false)$
defaultHealthCheck:
type: string
pattern: ^(true|false)$
type: boolean
appinfo:
type: object
properties:
@ -55,6 +51,44 @@ spec:
type: string
appns:
type: string
selectors:
type: object
properties:
pods:
items:
properties:
names:
type: string
namespace:
type: string
required:
- names
- namespace
type: object
type: array
workloads:
items:
properties:
kind:
type: string
pattern: ^(^$|deployment|statefulset|daemonset|deploymentconfig|rollout)$
labels:
type: string
names:
type: string
namespace:
type: string
oneOf:
- required: [ names ]
- required: [ labels ]
required:
- kind
- namespace
type: object
type: array
oneOf:
- required: [ pods ]
- required: [ workloads ]
auxiliaryAppInfo:
type: string
engineState:
@ -67,6 +101,180 @@ spec:
components:
type: object
properties:
sidecar:
type: array
items:
type: object
properties:
env:
description: ENV contains ENV passed to the sidecar container
items:
description: EnvVar represents an environment variable
present in a Container.
properties:
name:
description: Name of the environment variable. Must
be a C_IDENTIFIER.
type: string
value:
description: 'Variable references $(VAR_NAME) are
expanded using the previous defined environment
variables in the container and any service environment
variables. If a variable cannot be resolved, the
reference in the input string will be unchanged.
The $(VAR_NAME) syntax can be escaped with a double
$$, ie: $$(VAR_NAME). Escaped references will never
be expanded, regardless of whether the variable
exists or not. Defaults to "".'
type: string
valueFrom:
description: Source for the environment variable's
value. Cannot be used if value is not empty.
properties:
configMapKeyRef:
description: Selects a key of a ConfigMap.
properties:
key:
description: The key to select.
type: string
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion,
kind, uid?'
type: string
optional:
description: Specify whether the ConfigMap
or its key must be defined
type: boolean
required:
- key
type: object
fieldRef:
description: 'Selects a field of the pod: supports
metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`,
`metadata.annotations[''<KEY>'']`, spec.nodeName,
spec.serviceAccountName, status.hostIP, status.podIP,
status.podIPs.'
properties:
apiVersion:
description: Version of the schema the FieldPath
is written in terms of, defaults to "v1".
type: string
fieldPath:
description: Path of the field to select in
the specified API version.
type: string
required:
- fieldPath
type: object
resourceFieldRef:
description: 'Selects a resource of the container:
only resources limits and requests (limits.cpu,
limits.memory, limits.ephemeral-storage, requests.cpu,
requests.memory and requests.ephemeral-storage)
are currently supported.'
properties:
containerName:
description: 'Container name: required for
volumes, optional for env vars'
type: string
divisor:
anyOf:
- type: integer
- type: string
description: Specifies the output format of
the exposed resources, defaults to "1"
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
x-kubernetes-int-or-string: true
resource:
description: 'Required: resource to select'
type: string
required:
- resource
type: object
secretKeyRef:
description: Selects a key of a secret in the
pod's namespace
properties:
key:
description: The key of the secret to select
from. Must be a valid secret key.
type: string
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion,
kind, uid?'
type: string
optional:
description: Specify whether the Secret or
its key must be defined
type: boolean
required:
- key
type: object
type: object
required:
- name
type: object
type: array
envFrom:
description: EnvFrom for the sidecar container
items:
description: EnvFromSource represents the source of a
set of ConfigMaps
properties:
configMapRef:
description: The ConfigMap to select from
properties:
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the ConfigMap must
be defined
type: boolean
type: object
prefix:
description: An optional identifier to prepend to
each key in the ConfigMap. Must be a C_IDENTIFIER.
type: string
secretRef:
description: The Secret to select from
properties:
name:
description: 'Name of the referent. More info:
https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind,
uid?'
type: string
optional:
description: Specify whether the Secret must be
defined
type: boolean
type: object
type: object
type: array
image:
type: string
imagePullPolicy:
type: string
secrets:
items:
properties:
mountPath:
type: string
name:
type: string
required:
- mountPath
- name
type: object
type: array
runner:
x-kubernetes-preserve-unknown-fields: true
type: object
@ -127,6 +335,9 @@ spec:
items:
type: object
required:
- name
- type
- mode
- runProperties
properties:
name:
@ -134,9 +345,13 @@ spec:
type:
type: string
minLength: 1
pattern: ^(k8sProbe|httpProbe|cmdProbe|promProbe)$
pattern: ^(k8sProbe|httpProbe|cmdProbe|promProbe|sloProbe)$
k8sProbe/inputs:
type: object
required:
- version
- resource
- operation
properties:
group:
type: string
@ -146,6 +361,8 @@ spec:
type: string
namespace:
type: string
resourceNames:
type: string
fieldSelector:
type: string
labelSelector:
@ -156,12 +373,19 @@ spec:
minLength: 1
cmdProbe/inputs:
type: object
required:
- command
- comparator
properties:
command:
type: string
minLength: 1
comparator:
type: object
required:
- type
- criteria
- value
properties:
type:
type: string
@ -174,6 +398,8 @@ spec:
source:
description: The external pod where we have to run the
probe commands. It will run the commands inside the experiment pod itself(inline mode) if source contains a nil value
required:
- image
properties:
annotations:
additionalProperties:
@ -349,6 +575,29 @@ spec:
type: string
description: NodeSelector for the source pod
type: object
tolerations:
description: Tolerations for the source pod
items:
description: The pod with this Toleration tolerates any taint matches the <key,value,effect> using the matching operator <operator>.
properties:
effect:
description: Effect to match. Empty means all effects.
type: string
key:
description: Taint key the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists.
type: string
operator:
description: Operators are Exists or Equal. Defaults to Equal.
type: string
tolerationSeconds:
description: Period of time the toleration tolerates the taint.
format: int64
type: integer
value:
description: If the operator is Exists, the value should be empty, otherwise just a regular string.
type: string
type: object
type: array
privileged:
description: Privileged for the source pod
type: boolean
@ -1957,6 +2206,9 @@ spec:
type: object
httpProbe/inputs:
type: object
required:
- url
- method
properties:
url:
type: string
@ -1969,6 +2221,9 @@ spec:
properties:
get:
type: object
required:
- criteria
- responseCode
properties:
criteria:
type: string
@ -1978,6 +2233,9 @@ spec:
minLength: 1
post:
type: object
required:
- criteria
- responseCode
properties:
contentType:
type: string
@ -1994,6 +2252,9 @@ spec:
minLength: 1
promProbe/inputs:
type: object
required:
- endpoint
- comparator
properties:
endpoint:
type: string
@ -2003,6 +2264,9 @@ spec:
type: string
comparator:
type: object
required:
- criteria
- value
properties:
criteria:
type: string
@ -2015,18 +2279,112 @@ spec:
- probeTimeout
- interval
properties:
evaluationTimeout:
type: string
probeTimeout:
type: integer
type: string
interval:
type: integer
type: string
retry:
type: integer
probePollingInterval:
attempt:
type: integer
probePollingInterval:
type: string
initialDelaySeconds:
type: integer
initialDelay:
type: string
verbosity:
type: string
stopOnFailure:
type: boolean
sloProbe/inputs:
description: inputs needed for the SLO probe
required:
- platformEndpoint
- sloIdentifier
- sloSourceMetadata
- comparator
properties:
comparator:
description: Comparator check for the correctness
of the probe output
required:
- criteria
- value
properties:
criteria:
description: Criteria for matching data it
supports >=, <=, ==, >, <, != for int and
float it supports equal, notEqual, contains
for string
type: string
type:
description: Type of data it can be int, float,
string
type: string
value:
description: Value contains relative value
for criteria
type: string
type: object
evaluationWindow:
description: EvaluationWindow is the time period
for which the metrics will be evaluated
properties:
evaluationEndTime:
description: End time of evaluation
type: integer
evaluationStartTime:
description: Start time of evaluation
type: integer
type: object
platformEndpoint:
description: PlatformEndpoint for the monitoring
service endpoint
type: string
insecureSkipVerify:
description: InsecureSkipVerify flag to skip certificate
checks
type: boolean
sloIdentifier:
description: SLOIdentifier for fetching the details
of the SLO
type: string
sloSourceMetadata:
description: SLOSourceMetadata consists of required
metadata details to fetch metric data
required:
- apiTokenSecret
- scope
properties:
apiTokenSecret:
description: APITokenSecret for authenticating
with the platform service
type: string
scope:
description: Scope required for fetching details
required:
- accountIdentifier
- orgIdentifier
- projectIdentifier
properties:
accountIdentifier:
description: AccountIdentifier for account
ID
type: string
orgIdentifier:
description: OrgIdentifier for organization
ID
type: string
projectIdentifier:
description: ProjectIdentifier for project
ID
type: string
type: object
type: object
type: object
mode:
type: string
pattern: ^(SOT|EOT|Edge|Continuous|OnChaos)$

View File

@ -40,7 +40,7 @@ spec:
imagePullPolicy: IfNotPresent
env:
- name: CHAOS_RUNNER_IMAGE
value: "litmuschaos/chaos-runner:ci"
value: "litmuschaos.docker.scarf.sh/litmuschaos/chaos-runner:ci"
- name: WATCH_NAMESPACE
value: ""
- name: POD_NAME

140
go.mod
View File

@ -1,114 +1,108 @@
module github.com/litmuschaos/chaos-operator
go 1.17
go 1.22
require (
cloud.google.com/go v0.81.0 // indirect
github.com/go-logr/logr v0.4.0
github.com/google/go-cmp v0.5.6 // indirect
github.com/go-logr/logr v1.4.2
github.com/google/go-cmp v0.5.9 // indirect
github.com/jpillora/go-ogle-analytics v0.0.0-20161213085824-14b04e0594ef
github.com/litmuschaos/elves v0.0.0-20201107015738-552d74669e3c
github.com/litmuschaos/elves v0.0.0-20230607095010-c7119636b529
github.com/pkg/errors v0.9.1
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b // indirect
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
golang.org/x/tools v0.1.11 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
k8s.io/api v0.22.1
k8s.io/apimachinery v0.22.1
golang.org/x/oauth2 v0.7.0 // indirect
k8s.io/api v0.26.15
k8s.io/apimachinery v0.26.15
k8s.io/client-go v12.0.0+incompatible
sigs.k8s.io/controller-runtime v0.10.0
sigs.k8s.io/controller-runtime v0.14.6
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24
github.com/google/martian v2.1.0+incompatible
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0
github.com/operator-framework/operator-sdk v0.19.0
github.com/stretchr/testify v1.7.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.24.2
github.com/stretchr/testify v1.8.2
k8s.io/klog v1.0.0
)
require (
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-logr/zapr v0.4.0 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-logr/zapr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // 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/nxadm/tail v1.4.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.0 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.26.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.22.1 // indirect
k8s.io/component-base v0.22.2 // indirect
k8s.io/klog/v2 v2.9.0 // indirect
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
k8s.io/apiextensions-apiserver v0.26.1 // indirect
k8s.io/component-base v0.26.15 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
// Pinned to kubernetes-1.21.2
// Pinned to kubernetes-1.26
replace (
k8s.io/api => k8s.io/api v0.21.2
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.2
k8s.io/apimachinery => k8s.io/apimachinery v0.21.2
k8s.io/apiserver => k8s.io/apiserver v0.21.2
k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.2
k8s.io/client-go => k8s.io/client-go v0.21.2
k8s.io/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
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.14.6
github.com/go-logr/logr => github.com/go-logr/logr v1.4.2
k8s.io/api => k8s.io/api v0.26.15
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.26.15
k8s.io/apimachinery => k8s.io/apimachinery v0.26.15
k8s.io/client-go => k8s.io/client-go v0.26.15
k8s.io/cloud-provider => k8s.io/cloud-provider v0.26.15
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.26.15
k8s.io/component-base => k8s.io/component-base v0.26.15
k8s.io/cri-api => k8s.io/cri-api v0.26.15
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.26.15
k8s.io/klog/v2 => k8s.io/klog/v2 v2.80.1
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.26.15
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.26.15
k8s.io/kube-proxy => k8s.io/kube-proxy v0.26.15
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.26.15
k8s.io/kubelet => k8s.io/kubelet v0.26.15
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.26.15
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.26.15
)
replace github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 // Required by Helm

1151
go.sum

File diff suppressed because it is too large Load Diff

18
main.go
View File

@ -19,9 +19,12 @@ package main
import (
"flag"
"fmt"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
"os"
"runtime"
"strings"
"github.com/litmuschaos/chaos-operator/pkg/analytics"
"github.com/pkg/errors"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
@ -69,12 +72,19 @@ func main() {
printVersion()
namespace, err := k8sutil.GetWatchNamespace()
if err != nil {
setupLog.Error(err, "failed to get watch namespace")
namespace, found := os.LookupEnv("WATCH_NAMESPACE")
if !found {
setupLog.Error(errors.New("WATCH_NAMESPACE env is not set"), "failed to get watch namespace")
os.Exit(1)
}
// Trigger the Analytics if it's enabled
if isAnalytics := strings.ToUpper(os.Getenv("ANALYTICS")); isAnalytics != "FALSE" {
if err := analytics.TriggerAnalytics(); err != nil {
setupLog.Error(err, "failed to trigger analytics")
}
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,

View File

@ -36,9 +36,9 @@ type FakeChaosEngines struct {
ns string
}
var chaosenginesResource = schema.GroupVersionResource{Group: "litmuschaos", Version: "v1alpha1", Resource: "chaosengines"}
var chaosenginesResource = schema.GroupVersionResource{Group: "litmuschaos.io", Version: "v1alpha1", Resource: "chaosengines"}
var chaosenginesKind = schema.GroupVersionKind{Group: "litmuschaos", Version: "v1alpha1", Kind: "ChaosEngine"}
var chaosenginesKind = schema.GroupVersionKind{Group: "litmuschaos.io", Version: "v1alpha1", Kind: "ChaosEngine"}
// Get takes name of the chaosEngine, and returns the corresponding chaosEngine object, and an error if there is any.
func (c *FakeChaosEngines) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ChaosEngine, err error) {

View File

@ -36,9 +36,9 @@ type FakeChaosExperiments struct {
ns string
}
var chaosexperimentsResource = schema.GroupVersionResource{Group: "litmuschaos", Version: "v1alpha1", Resource: "chaosexperiments"}
var chaosexperimentsResource = schema.GroupVersionResource{Group: "litmuschaos.io", Version: "v1alpha1", Resource: "chaosexperiments"}
var chaosexperimentsKind = schema.GroupVersionKind{Group: "litmuschaos", Version: "v1alpha1", Kind: "ChaosExperiment"}
var chaosexperimentsKind = schema.GroupVersionKind{Group: "litmuschaos.io", Version: "v1alpha1", Kind: "ChaosExperiment"}
// Get takes name of the chaosExperiment, and returns the corresponding chaosExperiment object, and an error if there is any.
func (c *FakeChaosExperiments) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ChaosExperiment, err error) {

View File

@ -36,9 +36,9 @@ type FakeChaosResults struct {
ns string
}
var chaosresultsResource = schema.GroupVersionResource{Group: "litmuschaos", Version: "v1alpha1", Resource: "chaosresults"}
var chaosresultsResource = schema.GroupVersionResource{Group: "litmuschaos.io", Version: "v1alpha1", Resource: "chaosresults"}
var chaosresultsKind = schema.GroupVersionKind{Group: "litmuschaos", Version: "v1alpha1", Kind: "ChaosResult"}
var chaosresultsKind = schema.GroupVersionKind{Group: "litmuschaos.io", Version: "v1alpha1", Kind: "ChaosResult"}
// Get takes name of the chaosResult, and returns the corresponding chaosResult object, and an error if there is any.
func (c *FakeChaosResults) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ChaosResult, err error) {

View File

@ -6,7 +6,7 @@ import (
)
// CreateClientSet returns a Dynamic Kubernetes ClientSet
func CreateClientSet() (*dynamic.Interface, error) {
func CreateClientSet() (dynamic.Interface, error) {
restConfig, err := config.GetConfig()
if err != nil {
return nil, err
@ -15,5 +15,5 @@ func CreateClientSet() (*dynamic.Interface, error) {
if err != nil {
return nil, err
}
return &clientSet, nil
return clientSet, nil
}

View File

@ -1,71 +0,0 @@
package resource
import (
"context"
"errors"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
var (
gvrro = schema.GroupVersionResource{
Group: "argoproj.io",
Version: "v1alpha1",
Resource: "rollouts",
}
)
// CheckRolloutAnnotation will check the annotation of argo rollout
func CheckRolloutAnnotation(clientSet dynamic.Interface, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, error) {
rolloutList, err := getRolloutList(clientSet, engine)
if err != nil {
return engine, err
}
engine, chaosEnabledRollout, err := checkForChaosEnabledRollout(rolloutList, engine)
if err != nil {
return engine, err
}
if chaosEnabledRollout == 0 {
return engine, errors.New("no argo rollout chaos-candidate found")
}
return engine, nil
}
// getRolloutList returns a list of argo rollout resources that are found in the app namespace with specified label
func getRolloutList(clientSet dynamic.Interface, engine *chaosTypes.EngineInfo) (*unstructured.UnstructuredList, error) {
dynamicClient := clientSet.Resource(gvrro)
rolloutList, err := dynamicClient.Namespace(engine.AppInfo.Namespace).List(context.Background(), metav1.ListOptions{
LabelSelector: engine.Instance.Spec.Appinfo.Applabel})
if err != nil {
return nil, fmt.Errorf("error while listing argo rollouts with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
if len(rolloutList.Items) == 0 {
return nil, fmt.Errorf("no argo rollouts with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
return rolloutList, err
}
// checkForChaosEnabledRollout will check and count the total chaos enabled application
func checkForChaosEnabledRollout(rolloutList *unstructured.UnstructuredList, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, int, error) {
chaosEnabledRollout := 0
for _, rollout := range rolloutList.Items {
annotationValue := rollout.GetAnnotations()[ChaosAnnotationKey]
if IsChaosEnabled(annotationValue) {
chaosTypes.Log.Info("chaos candidate of", "kind:", engine.AppInfo.Kind, "appName: ", rollout.GetName(), "appUUID: ", rollout.GetUID())
chaosEnabledRollout++
}
}
return engine, chaosEnabledRollout, nil
}

View File

@ -1,72 +0,0 @@
/*
Copyright 2019 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 resource
import (
"context"
"errors"
"fmt"
appsV1 "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
// CheckDaemonSetAnnotation will check the annotation of DaemonSet
func CheckDaemonSetAnnotation(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, error) {
targetAppList, err := getDaemonSetLists(clientset, engine)
if err != nil {
return engine, err
}
engine, chaosEnabledDaemonSet, err := checkForChaosEnabledDaemonSet(targetAppList, engine)
if err != nil {
return engine, err
}
if chaosEnabledDaemonSet == 0 {
return engine, errors.New("no chaos-candidate found")
}
return engine, nil
}
// getDaemonSetLists will list the daemonSets which having the chaos label
func getDaemonSetLists(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*appsV1.DaemonSetList, error) {
targetAppList, err := clientset.AppsV1().DaemonSets(engine.AppInfo.Namespace).List(context.Background(), metaV1.ListOptions{
LabelSelector: engine.Instance.Spec.Appinfo.Applabel,
FieldSelector: ""})
if err != nil {
return nil, fmt.Errorf("error while listing daemonSets with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
if len(targetAppList.Items) == 0 {
return nil, fmt.Errorf("no daemonSets apps with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
return targetAppList, err
}
// checkForChaosEnabledDaemonSet will check and count the total chaos enabled application
func checkForChaosEnabledDaemonSet(targetAppList *appsV1.DaemonSetList, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, int, error) {
chaosEnabledDaemonSet := 0
for _, daemonSet := range targetAppList.Items {
annotationValue := daemonSet.ObjectMeta.GetAnnotations()[ChaosAnnotationKey]
if IsChaosEnabled(annotationValue) {
chaosTypes.Log.Info("chaos candidate of", "kind:", engine.AppInfo.Kind, "appName: ", daemonSet.ObjectMeta.Name, "appUUID: ", daemonSet.ObjectMeta.UID)
chaosEnabledDaemonSet++
}
}
return engine, chaosEnabledDaemonSet, nil
}

View File

@ -1,72 +0,0 @@
/*
Copyright 2019 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 resource
import (
"context"
"errors"
"fmt"
v1 "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
// CheckDeploymentAnnotation will check the annotation of deployment
func CheckDeploymentAnnotation(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, error) {
targetAppList, err := getDeploymentLists(clientset, engine)
if err != nil {
return engine, err
}
engine, chaosEnabledDeployment, err := checkForChaosEnabledDeployment(targetAppList, engine)
if err != nil {
return engine, err
}
if chaosEnabledDeployment == 0 {
return engine, errors.New("no chaos-candidate found")
}
return engine, nil
}
// getDeploymentLists will list the deployments which having the chaos label
func getDeploymentLists(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*v1.DeploymentList, error) {
targetAppList, err := clientset.AppsV1().Deployments(engine.AppInfo.Namespace).List(context.Background(), metaV1.ListOptions{
LabelSelector: engine.Instance.Spec.Appinfo.Applabel,
FieldSelector: ""})
if err != nil {
return nil, fmt.Errorf("error while listing deployments with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
if len(targetAppList.Items) == 0 {
return nil, fmt.Errorf("no deployments apps with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
return targetAppList, err
}
// checkForChaosEnabledDeployment will check and count the total chaos enabled application
func checkForChaosEnabledDeployment(targetAppList *v1.DeploymentList, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, int, error) {
chaosEnabledDeployment := 0
for _, deployment := range targetAppList.Items {
annotationValue := deployment.ObjectMeta.GetAnnotations()[ChaosAnnotationKey]
if IsChaosEnabled(annotationValue) {
chaosTypes.Log.Info("chaos candidate of", "kind:", engine.AppInfo.Kind, "appName: ", deployment.ObjectMeta.Name, "appUUID: ", deployment.ObjectMeta.UID)
chaosEnabledDeployment++
}
}
return engine, chaosEnabledDeployment, nil
}

View File

@ -1,75 +0,0 @@
package resource
import (
"context"
"errors"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
var (
gvrdc = schema.GroupVersionResource{
Group: "apps.openshift.io",
Version: "v1",
Resource: "deploymentconfigs",
}
)
// CheckDeploymentConfigAnnotation will check the annotation of deployment
func CheckDeploymentConfigAnnotation(clientSet dynamic.Interface, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, error) {
deploymentConfigList, err := getDeploymentConfigList(clientSet, engine)
if err != nil {
return engine, err
}
engine, chaosEnabledDeploymentConfig, err := checkForChaosEnabledDeploymentConfig(deploymentConfigList, engine)
if err != nil {
return engine, err
}
if chaosEnabledDeploymentConfig == 0 {
return engine, errors.New("no deploymentconfigs chaos-candidate found")
}
return engine, nil
}
func getDeploymentConfigList(clientSet dynamic.Interface, engine *chaosTypes.EngineInfo) (*unstructured.UnstructuredList, error) {
dynamicClient := clientSet.Resource(gvrdc)
deploymentConfigList, err := dynamicClient.Namespace(engine.AppInfo.Namespace).List(context.Background(), metav1.ListOptions{
LabelSelector: engine.Instance.Spec.Appinfo.Applabel})
if err != nil {
return nil, fmt.Errorf("error while listing deploymentconfigs with matching labels: %s", engine.Instance.Spec.Appinfo.Applabel)
}
if len(deploymentConfigList.Items) == 0 {
return nil, fmt.Errorf("no deploymentconfigs with matching labels: %s", engine.Instance.Spec.Appinfo.Applabel)
}
return deploymentConfigList, err
}
// checkForChaosEnabledDeploymentConfig will check and count the total chaos enabled application
func checkForChaosEnabledDeploymentConfig(deploymentConfigList *unstructured.UnstructuredList, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, int, error) {
chaosEnabledDeploymentConfig := 0
if deploymentConfigList == nil {
return engine, chaosEnabledDeploymentConfig, fmt.Errorf("deploymentconfigs is nil")
}
for _, deploymentConfig := range deploymentConfigList.Items {
annotationValue := deploymentConfig.GetAnnotations()[ChaosAnnotationKey]
if IsChaosEnabled(annotationValue) {
chaosTypes.Log.Info("chaos candidate of", "kind:", engine.AppInfo.Kind, "appName: ", deploymentConfig.GetName(), "appUUID: ", deploymentConfig.GetUID())
chaosEnabledDeploymentConfig++
}
}
return engine, chaosEnabledDeploymentConfig, nil
}

View File

@ -1,89 +0,0 @@
/*
Copyright 2019 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 resource
import (
"fmt"
"os"
"strings"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
)
// Annotations on app to enable chaos on it
const (
ChaosAnnotationValue = "true"
DefaultChaosAnnotationKey = "litmuschaos.io/chaos"
)
var (
// ChaosAnnotationKey is global variable used as the Key for annotation check.
ChaosAnnotationKey = GetAnnotationKey()
)
// GetAnnotationKey returns the annotation to be used while validating applications.
func GetAnnotationKey() string {
annotationKey := os.Getenv("CUSTOM_ANNOTATION")
if len(annotationKey) != 0 {
return annotationKey
}
return DefaultChaosAnnotationKey
}
// CheckChaosAnnotation will check for the annotation of required resources
func CheckChaosAnnotation(engine *chaosTypes.EngineInfo, clientset kubernetes.Interface, dynamicClientSet dynamic.Interface) (*chaosTypes.EngineInfo, error) {
switch strings.ToLower(engine.AppInfo.Kind) {
case "deployment", "deployments":
engine, err := CheckDeploymentAnnotation(clientset, engine)
if err != nil {
return engine, fmt.Errorf("resource type 'deployment', err: %+v", err)
}
case "statefulset", "statefulsets":
engine, err := CheckStatefulSetAnnotation(clientset, engine)
if err != nil {
return engine, fmt.Errorf("resource type 'statefulset', err: %+v", err)
}
case "daemonset", "daemonsets":
engine, err := CheckDaemonSetAnnotation(clientset, engine)
if err != nil {
return engine, fmt.Errorf("resource type 'daemonset', err: %+v", err)
}
case "deploymentconfig", "deploymentconfigs":
engine, err := CheckDeploymentConfigAnnotation(dynamicClientSet, engine)
if err != nil {
return engine, fmt.Errorf("resource type 'deploymentconfig', err: %+v", err)
}
case "rollout", "rollouts":
engine, err := CheckRolloutAnnotation(dynamicClientSet, engine)
if err != nil {
return engine, fmt.Errorf("resource type 'rollout', err: %+v", err)
}
default:
return engine, fmt.Errorf("resource type '%s' not supported for induce chaos", engine.AppInfo.Kind)
}
return engine, nil
}
// IsChaosEnabled check for the given annotation value
func IsChaosEnabled(annotationValue string) bool {
return annotationValue == ChaosAnnotationValue
}

File diff suppressed because it is too large Load Diff

View File

@ -1,72 +0,0 @@
/*
Copyright 2019 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 resource
import (
"context"
"errors"
"fmt"
appsV1 "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
chaosTypes "github.com/litmuschaos/chaos-operator/pkg/types"
)
// CheckStatefulSetAnnotation will check the annotation of StatefulSet
func CheckStatefulSetAnnotation(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, error) {
targetAppList, err := getStatefulSetLists(clientset, engine)
if err != nil {
return engine, err
}
engine, chaosEnabledStatefulSet, err := checkForChaosEnabledStatefulSet(targetAppList, engine)
if err != nil {
return engine, err
}
if chaosEnabledStatefulSet == 0 {
return engine, errors.New("no chaos-candidate found")
}
return engine, nil
}
// getStatefulSetLists will list the statefulset which having the chaos label
func getStatefulSetLists(clientset kubernetes.Interface, engine *chaosTypes.EngineInfo) (*appsV1.StatefulSetList, error) {
targetAppList, err := clientset.AppsV1().StatefulSets(engine.AppInfo.Namespace).List(context.Background(), metaV1.ListOptions{
LabelSelector: engine.Instance.Spec.Appinfo.Applabel,
FieldSelector: ""})
if err != nil {
return nil, fmt.Errorf("error while listing statefulsets with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
if len(targetAppList.Items) == 0 {
return nil, fmt.Errorf("no statefulset apps with matching labels %s", engine.Instance.Spec.Appinfo.Applabel)
}
return targetAppList, err
}
// checkForChaosEnabledStatefulSet will check and count the total chaos enabled application
func checkForChaosEnabledStatefulSet(targetAppList *appsV1.StatefulSetList, engine *chaosTypes.EngineInfo) (*chaosTypes.EngineInfo, int, error) {
chaosEnabledStatefulSet := 0
for _, statefulSet := range targetAppList.Items {
annotationValue := statefulSet.ObjectMeta.GetAnnotations()[ChaosAnnotationKey]
if IsChaosEnabled(annotationValue) {
chaosTypes.Log.Info("chaos candidate of", "kind:", engine.AppInfo.Kind, "appName: ", statefulSet.ObjectMeta.Name, "appUUID: ", statefulSet.ObjectMeta.UID)
chaosEnabledStatefulSet++
}
}
return engine, chaosEnabledStatefulSet, nil
}

View File

@ -38,14 +38,6 @@ import (
)
var (
// AppLabelKey contains the application label key
AppLabelKey string
// DefaultAnnotationCheck contains the default value (true) of the annotationCheck
DefaultAnnotationCheck = "false"
// AppLabelValue contains the application label value
AppLabelValue string
// Log with default name ie: controller_chaosengine
Log = log.Log.WithName("controller_chaosengine")
@ -57,21 +49,12 @@ var (
ResultCRDName = "chaosresults.litmuschaos.io"
)
// ApplicationInfo contains the chaos details for target application
type ApplicationInfo struct {
Namespace string
Label string
ExperimentList []litmuschaosv1alpha1.ExperimentList
ServiceAccountName string
Kind string
}
// EngineInfo Related information
type EngineInfo struct {
Instance *litmuschaosv1alpha1.ChaosEngine
AppInfo *ApplicationInfo
ConfigMaps []litmuschaosv1alpha1.ConfigMap
Secrets []litmuschaosv1alpha1.Secret
AppInfo litmuschaosv1alpha1.ApplicationParams
Selectors *litmuschaosv1alpha1.Selector
Targets string
VolumeOpts utils.VolumeOpts
AppExperiments []string
}

View File

@ -22,6 +22,9 @@ import (
// RemoveString removes a particular string from a slice of strings
func RemoveString(slice []string, s string) (result []string) {
if len(slice) == 0 {
return
}
for _, item := range slice {
if item == s {
continue
@ -33,7 +36,7 @@ func RemoveString(slice []string, s string) (result []string) {
// SetEnv sets the env inside envDetails struct
func (envDetails *ENVDetails) SetEnv(key, value string) *ENVDetails {
if value != "" {
if key != "" && value != "" {
envDetails.ENV = append(envDetails.ENV, corev1.EnvVar{
Name: key,
Value: value,

View File

@ -0,0 +1,117 @@
/*
Copyright 2024 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 utils
import (
"math/rand"
"testing"
"unicode"
fuzzheaders "github.com/AdaLogics/go-fuzz-headers"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
)
func FuzzSetEnv(f *testing.F) {
kv := map[string]string{
"KEY1": "VALUE1",
"KEY2": "VALUE2",
}
for k, v := range kv {
f.Add(k, v)
}
f.Fuzz(func(t *testing.T, key, value string) {
ed := ENVDetails{
ENV: make([]v1.EnvVar, 0),
}
edUpdated := ed.SetEnv(key, value)
if edUpdated == nil {
t.Error("nil object not expected")
}
if key == "" && edUpdated != nil {
assert.Equal(t, 0, len(edUpdated.ENV))
}
if value == "" && edUpdated != nil {
assert.Equal(t, 0, len(edUpdated.ENV))
}
if key != "" && value != "" && edUpdated != nil {
assert.Equal(t, 1, len(edUpdated.ENV))
}
if key != "" && value != "" && edUpdated != nil && len(edUpdated.ENV) == 1 {
env := edUpdated.ENV[0]
assert.Equal(t, key, env.Name)
assert.Equal(t, value, env.Value)
}
})
}
func FuzzRemoveString(f *testing.F) {
f.Fuzz(func(t *testing.T, extra string, data []byte) {
consumer := fuzzheaders.NewConsumer(data)
testInput := &struct {
Data map[string]int
}{}
err := consumer.GenerateStruct(testInput)
if err != nil {
return
}
max := len(testInput.Data) - 1
if max < 0 {
max = 0
}
randomNumber := func(min, max int) int {
if max == 0 {
return 0
}
return rand.Intn(max-min) + min
}(0, max)
index := 0
full := make([]string, 0)
exclude := ""
result := make([]string, 0)
for k := range testInput.Data {
if k == "" {
continue
}
if !func() bool {
for _, r := range k {
if !unicode.IsLetter(r) {
return false
}
}
return true
}() {
continue
}
full = append(full, k)
if index == randomNumber {
exclude = k
}
if index != randomNumber {
result = append(result, k)
}
}
if exclude != "" {
return
}
got := RemoveString(full, exclude)
if got == nil {
got = make([]string, 0)
}
assert.Equal(t, result, got)
})
}

View File

@ -6,11 +6,6 @@ import (
corev1 "k8s.io/api/core/v1"
)
var (
// hostpathTypeFile represents the hostpath type
hostpathTypeFile = corev1.HostPathFile
)
// CreateVolumeBuilders build Volume needed in execution of experiments
func CreateVolumeBuilders(configMaps []v1alpha1.ConfigMap, secrets []v1alpha1.Secret) []*volume.Builder {
volumeBuilderList := []*volume.Builder{}

View File

@ -437,25 +437,6 @@ var _ = Describe("BDD on chaos-operator", func() {
})
Expect(err).To(BeNil())
})
It("Should change EngineStatus ", func() {
err := retry.
Times(uint(180 / 2)).
Wait(time.Duration(2) * time.Second).
Try(func(attempt uint) error {
//Fetching engineStatus
engine, err := clientSet.ChaosEngines("litmus").Get(context.Background(), "engine-nginx-1", metav1.GetOptions{})
if err != nil {
return err
}
if engine.Status.EngineStatus != v1alpha1.EngineStatusCompleted {
return fmt.Errorf("engine is not in completed state")
}
return nil
})
Expect(err).To(BeNil())
})
})
Context("Validate via Chaos-Operator Logs", func() {