Merge remote-tracking branch 'upstream/master' into frontend-dockerimage-change
This commit is contained in:
commit
35da8d7dde
|
@ -95,8 +95,6 @@ spec:
|
|||
value: "cluster"
|
||||
- name: SUBSCRIBER_IMAGE
|
||||
value: #{subscriber}
|
||||
- name: DEPLOYER_IMAGE
|
||||
value: "litmuschaos/litmusportal-self-deployer:ci"
|
||||
- name: ARGO_SERVER_IMAGE
|
||||
value: "argoproj/argocli:v2.9.3"
|
||||
- name: ARGO_WORKFLOW_CONTROLLER_IMAGE
|
||||
|
|
|
@ -42,7 +42,6 @@ jobs:
|
|||
echo 'export AUTHENTICATION_SERVER_IMAGE="litmusportal-auth-server"' >> workspace/env-vars
|
||||
echo 'export FRONTEND_IMAGE="litmusportal-frontend"' >> workspace/env-vars
|
||||
echo 'export SUBSCRIBER_IMAGE="litmusportal-subscriber"' >> workspace/env-vars
|
||||
echo 'export SELF_DEPLOYER_IMAGE="litmusportal-self-deployer"' >> workspace/env-vars
|
||||
echo 'export IMGTAG="ci"' >> workspace/env-vars
|
||||
cat workspace/env-vars >> $BASH_ENV
|
||||
source $BASH_ENV
|
||||
|
@ -82,19 +81,11 @@ jobs:
|
|||
- run:
|
||||
name: Save subscriber docker image
|
||||
command: docker save -o /tmp/workspace/${SUBSCRIBER_IMAGE}.tar ${REPONAME}/${SUBSCRIBER_IMAGE}:${IMGTAG}
|
||||
- run:
|
||||
name: Build self-deployer docker image
|
||||
command: docker build . -f build/Dockerfile -t ${REPONAME}/${SELF_DEPLOYER_IMAGE}:${IMGTAG}
|
||||
working_directory: ~/project/litmus-portal/tools/self-deployer
|
||||
- run:
|
||||
name: Save self-deployer docker image
|
||||
command: docker save -o /tmp/workspace/${SELF_DEPLOYER_IMAGE}.tar ${REPONAME}/${SELF_DEPLOYER_IMAGE}:${IMGTAG}
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- litmusportal-server.tar
|
||||
- litmusportal-subscriber.tar
|
||||
- litmusportal-self-deployer.tar
|
||||
- litmusportal-auth-server.tar
|
||||
docker-build-frontend:
|
||||
machine:
|
||||
|
@ -134,7 +125,6 @@ jobs:
|
|||
command: |
|
||||
docker load -i /tmp/workspace/${GRAPHQL_SERVER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${SUBSCRIBER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${SELF_DEPLOYER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${AUTHENTICATION_SERVER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${FRONTEND_IMAGE}.tar
|
||||
- run:
|
||||
|
@ -149,10 +139,6 @@ jobs:
|
|||
- run:
|
||||
name: Pushing subscriber server
|
||||
command: bash ./hack/push --TYPE=ci --REPONAME=${REPONAME} --IMGNAME=${SUBSCRIBER_IMAGE} --IMGTAG=${IMGTAG}
|
||||
- run:
|
||||
name: Pushing self deployer
|
||||
command: bash ./hack/push --TYPE=ci --REPONAME=${REPONAME} --IMGNAME=${SELF_DEPLOYER_IMAGE} --IMGTAG=${IMGTAG}
|
||||
|
||||
release:
|
||||
machine:
|
||||
image: circleci/classic:201808-01
|
||||
|
@ -169,7 +155,6 @@ jobs:
|
|||
command: |
|
||||
docker load -i /tmp/workspace/${GRAPHQL_SERVER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${SUBSCRIBER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${SELF_DEPLOYER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${AUTHENTICATION_SERVER_IMAGE}.tar
|
||||
docker load -i /tmp/workspace/${FRONTEND_IMAGE}.tar
|
||||
- run:
|
||||
|
@ -187,9 +172,6 @@ jobs:
|
|||
- run:
|
||||
name: Pushing subscriber server
|
||||
command: bash ./hack/push --TYPE=release --REPONAME=${REPONAME} --IMGNAME=${SUBSCRIBER_IMAGE} --IMGTAG=${IMGTAG}
|
||||
- run:
|
||||
name: Pushing self deployer
|
||||
command: bash ./hack/push --TYPE=release --REPONAME=${REPONAME} --IMGNAME=${SELF_DEPLOYER_IMAGE} --IMGTAG=${IMGTAG}
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
|
36
ADOPTERS.md
36
ADOPTERS.md
|
@ -1,21 +1,23 @@
|
|||
This is the list of organizations and users that publicly shared details of how they are using LitmusChaos for running chaos experiments.
|
||||
Please send PRs to add or remove organizations/users.
|
||||
|
||||
| Organization | Applications/Workloads | Success Story |
|
||||
| :--- | :--- | :---|
|
||||
|[Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Zebrium K8s Demo](https://github.com/zebrium/zebrium-kubernetes-demo)|[our story](https://github.com/litmuschaos/litmus/blob/master/adopters/zebrium.md)|
|
||||
|[MayaData](https://mayadata.io)|[Director Online](https://director.mayadata.io/)|[our story](https://github.com/litmuschaos/litmus/tree/master/adopters/MayaData_DirectorOnline.md)|
|
||||
|[OpenEBS](https://openebs.io/)|[openebs-ci](https://openebs.ci/)|[our story](https://github.com/litmuschaos/litmus/tree/master/adopters/openebs.md)|
|
||||
|[Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[our story](https://github.com/litmuschaos/litmus/tree/master/adopters/AppAnywhere.md)|
|
||||
|[Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Argo-Litmus Demo](https://youtu.be/Uwqop-s99LA?t=720)|[our story](https://github.com/litmuschaos/litmus/tree/master/adopters/intuit.md)|
|
||||
|[Okteto](https://okteto.com)|[Okteto-Litmus Demo](https://okteto.com/blog/chaos-engineering-with-litmus/)| [our story](adopters/okteto.md)|
|
||||
|[WeScale](https://www.wescale.fr)|[Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/)|[our story](https://github.com/litmuschaos/litmus/blob/master/adopters/wescale.md)|
|
||||
|[NetApp](https://www.netapp.com)|[Chaos Engineering](https://www.netapp.com/us/index.aspx)|[our story](https://github.com/litmuschaos/litmus/blob/master/adopters/netapp.md)|
|
||||
| Organization | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
|[Zebrium](https://www.zebrium.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Zebrium K8s Chaos Project](https://github.com/zebrium/zebrium-kubernetes-demo)|[Our Story](adopters/organizations/zebrium.md)|
|
||||
|[MayaData](https://mayadata.io)|[Director Online](https://director.mayadata.io/)|[Our Story](adopters/organizations/mayadata.md)|
|
||||
|[OpenEBS](https://openebs.io/)|[Openebs-CI](https://openebs.ci/)|[Our Story](adopters/organizations/openebs.md)|
|
||||
|[Wipro](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Wipro AppAnywhere](https://www.wipro.com/en-IN/infrastructure/wipros-appanywhere/?utm_source=github&utm_campaign=litmuschaos_repo)|[Our Story](adopters/organizations/wipro.md)|
|
||||
|[Intuit](https://www.intuit.com?utm_source=github&utm_campaign=litmuschaos_repo)|[Argo Based Chaos Workflows](https://youtu.be/Uwqop-s99LA?t=720)|[Our Story](adopters/organizations/intuit.md)|
|
||||
|[Okteto](https://okteto.com)|[Okteto-Litmus Integration](https://okteto.com/blog/chaos-engineering-with-litmus/)| [Our Story](adopters/organizations/okteto.md)|
|
||||
|[WeScale](https://www.wescale.fr)|[Chaos Engineering](https://blog.wescale.fr/2020/03/19/le-guide-de-chaos-engineering-partie-2/)|[Our Story](adopters/organizations/wescale.md)|
|
||||
|[NetApp](https://www.netapp.com)|[Chaos Engineering](https://www.netapp.com/us/index.aspx)|[Our Story](adopters/organizations/netapp.md)|
|
||||
|[Keptn](https://keptn.sh)|[Chaos Engineering integration in CD](https://www.youtube.com/watch?v=aa5SzQmv4EQ)|To Be Added|
|
||||
|
||||
| User | Applications/Workloads | Success Story |
|
||||
| :--- | :--- | :--- |
|
||||
| [Laura Henning](https://github.com/LaumiH) | reasearch on how to do chaos engineering in minikube demo clusters like [these](https://github.com/LaumiH/k8sstuff) | [my story](https://github.com/litmuschaos/litmus/tree/master/adopters/Laura_Henning_Research_Project.md) |
|
||||
| [Johnny Jacob](https://github.com/johnnyjacob) | Testing deployment designs for resiliency | Coming Soon! |
|
||||
| [Jayesh Kumar Tank](https://github.com/k8s-dev) | Create Cloud Native Validation Suite on [Demo Application](https://github.com/k8s-dev/microservices-demo)| [my story](https://github.com/litmuschaos/litmus/tree/master/adopters/Jayesh_Kumar_CloudNative_Validation.md)|
|
||||
| [Bhaumik Shah](https://github.com/Bhaumik1802) | Use LitmusChaos for Kafka Resiliency on Dev/Staging| [my story](https://github.com/litmuschaos/litmus/tree/master/adopters/Bhaumik_Shah_Kafka_Chaos.md)|
|
||||
| [Jayadeep KM](https://github.com/kmjayadeep) | Ensure reliability of microservices| [my story](https://github.com/litmuschaos/litmus/tree/master/adopters/jayadeep_microservices.md)|
|
||||
| User | Usecase | Details |
|
||||
| :--- | :--- | :--- |
|
||||
| [Laura Henning](https://github.com/LaumiH)|Reasearch on how to do chaos engineering in minikube clusters like [these](https://github.com/LaumiH/k8sstuff)|[My Story](adopters/users/Laura_Henning.md)
|
||||
| [Johnny Jacob](https://github.com/johnnyjacob)|Testing deployment designs for resiliency|Coming Soon!|
|
||||
| [Jayesh Kumar Tank](https://github.com/k8s-dev)|Create Cloud Native Validation Suite on [Microservices Application](https://github.com/k8s-dev/microservices-demo)|[My Story](adopters/users/Jayesh_Kumar_Tank.md)|
|
||||
| [Bhaumik Shah](https://github.com/Bhaumik1802)|Use LitmusChaos for Kafka Resiliency on Dev/Staging|[My Story](adopters/users/Bhaumik_Shah.md)|
|
||||
| [Jayadeep KM](https://github.com/kmjayadeep)|Ensure reliability of microservices|[My Story](adopters/users/Jayadeep_KM.md)|
|
||||
| [Shantanu Deshpande](https://github.com/ishantanu)|Chaos Engineering Practice as SRE|[My Story](adopters/users/Shantanu_Deshpande.md)|
|
||||
|
|
|
@ -41,6 +41,7 @@ This document captures only the high level roadmap items. For the detailed backl
|
|||
|
||||
### Backlog
|
||||
|
||||
- Add pre-defined chaos workflows for the [podtato-head](https://github.com/cncf/podtato-head) model app from CNCF Ap-Delivery SIG
|
||||
- Pre-defined chaos workflows to inject chaos during application benchmark runs
|
||||
- Support for cloudevents compliant chaos events
|
||||
- Increased chaos metrics via prometheus chaos exporter
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
I was looking for a cloud-native way of introducing chaos and after going through the details
|
||||
and other options, Litmus was the best fit. Usage of Litmus is still in preliminary stages.
|
||||
A limited set of chaos experiments are being used for testing resiliency. This will change in the
|
||||
future, with more experiments planned to be adopted.
|
|
@ -70,15 +70,6 @@ backend-services-checks:
|
|||
&& exit 1; \
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check litmus-portal self-deployer [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd tools/self-deployer && go mod tidy); \
|
||||
if [ -n "$${tidyRes}" ]; then \
|
||||
echo "go mod tidy checking failed!" && echo "$${tidyRes}" \
|
||||
&& echo "Please ensure you are using $$($(GO) version) for formatting code." \
|
||||
&& exit 1; \
|
||||
fi
|
||||
@echo "------------------"
|
||||
@echo "--> Check litmus-portal workflow-agent [go mod tidy]"
|
||||
@echo "------------------"
|
||||
@tidyRes=$$(cd cluster-agents/workflow-agent && go mod tidy); \
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
"github.com/globalsign/mgo/bson"
|
||||
log "github.com/golang/glog"
|
||||
|
||||
"github.com/litmuschaos/litmus/litmus-portal/authentication/pkg/models"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/authentication/pkg/types"
|
||||
|
@ -31,6 +33,20 @@ func NewUserStore(cfg *Config, ucfgs ...*UserConfig) (*UserStore, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if types.DBUser != "" && types.DBPassword != "" {
|
||||
cred := mgo.Credential{
|
||||
Username: types.DBUser,
|
||||
Password: types.DBPassword,
|
||||
}
|
||||
err = session.Login(&cred)
|
||||
if err != nil {
|
||||
log.Errorln("Database connection failed", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("Some DB configs are not present")
|
||||
}
|
||||
|
||||
return NewUserStoreWithSession(session, cfg.DB, ucfgs...)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ var (
|
|||
DefaultUserName string = os.Getenv("ADMIN_USERNAME")
|
||||
DefaultUserPassword string = os.Getenv("ADMIN_PASSWORD")
|
||||
DefaultDBServerURL string = os.Getenv("DB_SERVER")
|
||||
DBUser string = os.Getenv("DB_USER")
|
||||
DBPassword string = os.Getenv("DB_PASSWORD")
|
||||
DefaultAuthDB string = "auth"
|
||||
DefaultLocalAuthCollection string = "usercredentials"
|
||||
PasswordEncryptionCost int = 15
|
||||
|
|
|
@ -98,6 +98,19 @@ func applyRequest(requestType string, obj *unstructured.Unstructured) (*unstruct
|
|||
log.Println("Resource successfully created")
|
||||
return response, nil
|
||||
} else if requestType == "update" {
|
||||
getObj, err := dr.Get(obj.GetName(), metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
// This doesnt ever happen even if it is already deleted or not found
|
||||
log.Printf("%v not found", obj.GetName())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj.SetResourceVersion(getObj.GetResourceVersion())
|
||||
|
||||
response, err := dr.Update(obj, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -14,6 +14,8 @@ data:
|
|||
AgentNamespace: litmus
|
||||
DataBaseServer: "mongodb://mongo-service:27017"
|
||||
JWTSecret: "litmus-portal@123"
|
||||
DB_USER: "admin"
|
||||
DB_PASSWORD: "1234"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
@ -61,38 +63,6 @@ spec:
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: self-deployer-admin-account
|
||||
namespace: litmus
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: deployer-admin
|
||||
namespace: litmus
|
||||
labels:
|
||||
name: deployer-admin
|
||||
rules:
|
||||
- apiGroups: ["*"]
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: deployer-admin-rb
|
||||
namespace: litmus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: self-deployer-admin-account
|
||||
namespace: litmus
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: deployer-admin
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: litmus-server-account
|
||||
namespace: litmus
|
||||
|
@ -106,24 +76,11 @@ metadata:
|
|||
name: litmus-server
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
- "*"
|
||||
resources:
|
||||
- pods
|
||||
- pods/exec
|
||||
- services
|
||||
- nodes
|
||||
- "*"
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
- get
|
||||
- list
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- create
|
||||
- "*"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
|
@ -186,12 +143,20 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: DB_USER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_USER
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_PASSWORD
|
||||
- name: PORTAL_SCOPE
|
||||
value: "cluster"
|
||||
- name: SUBSCRIBER_IMAGE
|
||||
value: "litmuschaos/litmusportal-subscriber:ci"
|
||||
- name: DEPLOYER_IMAGE
|
||||
value: "litmuschaos/litmusportal-self-deployer:ci"
|
||||
- name: ARGO_SERVER_IMAGE
|
||||
value: "argoproj/argocli:v2.9.3"
|
||||
- name: ARGO_WORKFLOW_CONTROLLER_IMAGE
|
||||
|
@ -218,6 +183,16 @@ spec:
|
|||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: JWTSecret
|
||||
- name: DB_USER
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_USER
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_PASSWORD
|
||||
- name: ADMIN_USERNAME
|
||||
value: "admin"
|
||||
- name: ADMIN_PASSWORD
|
||||
|
@ -270,6 +245,17 @@ spec:
|
|||
volumeMounts:
|
||||
- name: mongo-persistent-storage
|
||||
mountPath: /data/db
|
||||
env:
|
||||
- name: MONGO_INITDB_ROOT_USERNAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_USER
|
||||
- name: MONGO_INITDB_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: litmus-portal-admin-config
|
||||
key: DB_PASSWORD
|
||||
volumes:
|
||||
- name: mongo-persistent-storage
|
||||
persistentVolumeClaim:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="65" height="82" viewBox="0 0 65 82" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M65 0H0V75.85C0 79.2467 2.72826 82 6.09375 82H58.9062C62.2717 82 65 79.2467 65 75.85V0Z" fill="#858CDD"/>
|
||||
<path d="M10 10H20V33H10V10Z" fill="#5B44BA"/>
|
||||
<path d="M10 31H20V72H10V31Z" fill="white"/>
|
||||
<path d="M25 62H55V72H25V62Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 395 B |
|
@ -0,0 +1,11 @@
|
|||
<svg width="46" height="47" viewBox="0 0 46 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="0.605469" width="45.7" height="45.7" rx="22.85" fill="#858CDD"/>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M19.7294 33.4202C19.475 33.6761 19.1278 33.8189 18.7671 33.8189C18.4065 33.8189 18.0593 33.6761 17.8048 33.4202L10.1197 25.7337C9.3221 24.9362 9.3221 23.6429 10.1197 22.8468L11.082 21.8843C11.8798 21.0867 13.1715 21.0867 13.9691 21.8843L18.7671 26.6826L31.7321 13.7173C32.53 12.9198 33.823 12.9198 34.6193 13.7173L35.5816 14.6799C36.3792 15.4774 36.3792 16.7704 35.5816 17.5668L19.7294 33.4202Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="26.6583" height="26.6583" fill="white" transform="translate(9.52148 10.1289)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 772 B |
|
@ -0,0 +1,5 @@
|
|||
<svg width="78" height="64" viewBox="0 0 78 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1138 14.3014C21.0269 7.11955 28.3237 0.326172 39.2092 0.326172C50.9208 0.326172 60.9024 8.29471 63.0908 21.1899C66.181 21.6524 69.4164 22.8359 72.0804 24.8148C75.3916 27.2745 77.8695 31.0223 77.8695 36.0266C77.8695 40.8787 75.8267 44.7389 72.5009 47.3264C69.2407 49.8627 64.9018 51.0679 60.3516 51.0679H48.8743C47.5398 51.0679 46.458 49.9861 46.458 48.6516C46.458 47.3172 47.5398 46.2353 48.8743 46.2353H60.3516C64.1073 46.2353 67.3192 45.2349 69.5334 43.5122C71.682 41.8407 73.037 39.3883 73.037 36.0266C73.037 32.8171 71.513 30.4132 69.1988 28.6942C66.825 26.9309 63.6788 25.9557 60.8361 25.8149C59.6459 25.7559 58.6763 24.838 58.5525 23.6527C57.3351 12.0049 48.9635 5.15872 39.2092 5.15872C29.9984 5.15872 24.1094 11.235 22.1799 17.2529C21.8848 18.1733 21.0697 18.8292 20.1075 18.9206C11.7811 19.7116 5.38137 24.8377 5.38137 32.5834C5.38137 40.3703 12.11 46.2353 21.0871 46.2353H29.5441C30.8786 46.2353 31.9604 47.3172 31.9604 48.6516C31.9604 49.9861 30.8786 51.0679 29.5441 51.0679H21.0871C10.1301 51.0679 0.548828 43.6797 0.548828 32.5834C0.548828 22.0304 8.90384 15.7256 18.1138 14.3014Z" fill="#858CDD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.4998 20.364C38.4434 19.4203 39.9733 19.4203 40.9169 20.364L50.582 30.029C51.5256 30.9727 51.5256 32.5026 50.582 33.4462C49.6384 34.3898 48.1085 34.3898 47.1648 33.4462L39.2083 25.4896L31.2518 33.4462C30.3082 34.3898 28.7783 34.3898 27.8347 33.4462C26.891 32.5026 26.891 30.9727 27.8347 30.029L37.4998 20.364Z" fill="#858CDD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M39.2092 22.0723C40.5437 22.0723 41.6255 23.1541 41.6255 24.4885V60.796C41.6255 62.1305 40.5437 63.2123 39.2092 63.2123C37.8748 63.2123 36.793 62.1305 36.793 60.796V24.4885C36.793 23.1541 37.8748 22.0723 39.2092 22.0723Z" fill="#858CDD"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -30,6 +30,7 @@ welcomeModal:
|
|||
|
||||
sidebar:
|
||||
title: Litmus
|
||||
version: 'Version: 1.10'
|
||||
|
||||
header:
|
||||
notificationDropdown:
|
||||
|
@ -435,6 +436,7 @@ createWorkflow:
|
|||
schedule: Schedule
|
||||
schedulingNow: Scheduling now
|
||||
adjustedWeights: Adjusted Weights
|
||||
clustername: Cluster Name
|
||||
workflowCluster:
|
||||
activeCluster: Choose an active cluster
|
||||
none: None
|
||||
|
@ -459,6 +461,15 @@ targets:
|
|||
head4: 4.Click on the button
|
||||
head5: Connect the target
|
||||
conformation: Kuberenetes Agent is confirming, Please wait...
|
||||
clusterDetails: Cluster Details
|
||||
modalDelete:
|
||||
head1: Are you sure to
|
||||
head2: remove the current Agent?
|
||||
head3: It will be removed forever
|
||||
delete: Delete
|
||||
yes: Yes
|
||||
no: No
|
||||
|
||||
|
||||
connectTargets:
|
||||
title: A new Target,
|
||||
|
@ -574,13 +585,21 @@ myhub:
|
|||
customWorkflow:
|
||||
createWorkflow:
|
||||
create: Creating a new chaos workflow
|
||||
createDesc: Add new experiments from your ChaosHubs defined at MyHubs section
|
||||
createDesc: Add new experiments from your ChaosHubs defined at MyHub section
|
||||
workflowInfo: Workflow information
|
||||
workflowName: Workflow Name
|
||||
workflowDesc: Description
|
||||
firstChaos: First Chaos Experiment
|
||||
chooseNamespace: Choose Namespace
|
||||
firstChaos: Choose the hub
|
||||
selectHub: Select the hub
|
||||
public: Public Hub
|
||||
configure: Construct your workflow
|
||||
construct: Using experiments from MyHub
|
||||
upload: Upload your
|
||||
yaml: YAML
|
||||
uploadFile: Upload File
|
||||
uploadSuccess: Successfully uploaded
|
||||
drag: Drag & Drop here
|
||||
chooseExp: Choose the experiment
|
||||
loadingExp: Loading Experiments, please wait...
|
||||
selectExp: Select the experiment
|
||||
|
@ -604,6 +623,8 @@ customWorkflow:
|
|||
expName: Experiment name
|
||||
description: Description
|
||||
sequence: Sequence
|
||||
sequenceFirstExp: This is your first experiment
|
||||
sequenceNotFirstExp: This Experiment will execute after
|
||||
appInfo: Specify the appinfo for the experiment
|
||||
envText: We have created a chaos engine specification for this experiment. You can tune the following variables for the chaos engine.
|
||||
customEnvText: To change the variables in chaos experiment specific to this workflow, You can tune it here.
|
||||
|
|
|
@ -56,6 +56,12 @@ const Header: React.FC = () => {
|
|||
const memberList: Member[] = project.members;
|
||||
|
||||
memberList.forEach((member) => {
|
||||
if (member.role === 'Owner') {
|
||||
user.updateUserDetails({
|
||||
selectedProjectOwner: member.user_name,
|
||||
});
|
||||
}
|
||||
|
||||
if (member.user_name === data?.getUser.username) {
|
||||
user.updateUserDetails({
|
||||
selectedProjectID,
|
||||
|
|
|
@ -10,11 +10,13 @@ import useStyles from './styles';
|
|||
interface PredifinedWorkflowsProps {
|
||||
workflows: preDefinedWorkflowData[];
|
||||
callbackOnSelectWorkflow: (index: number) => void;
|
||||
isCustomWorkflowVisible: boolean;
|
||||
}
|
||||
|
||||
const PredifinedWorkflows: React.FC<PredifinedWorkflowsProps> = ({
|
||||
workflows,
|
||||
callbackOnSelectWorkflow,
|
||||
isCustomWorkflowVisible,
|
||||
}) => {
|
||||
const workflowAction = useActions(WorkflowActions);
|
||||
const classes = useStyles();
|
||||
|
@ -38,19 +40,22 @@ const PredifinedWorkflows: React.FC<PredifinedWorkflowsProps> = ({
|
|||
/>
|
||||
</div>
|
||||
))}
|
||||
<CustomWorkflowCard
|
||||
handleClick={() => {
|
||||
workflowAction.setWorkflowDetails({
|
||||
name: `custom-chaos-workflow-${Math.round(
|
||||
new Date().getTime() / 1000
|
||||
)}`,
|
||||
description: 'Custom Chaos Workflow',
|
||||
isCustomWorkflow: true,
|
||||
customWorkflows: [],
|
||||
});
|
||||
history.push('/create-workflow/custom');
|
||||
}}
|
||||
/>
|
||||
{isCustomWorkflowVisible ? (
|
||||
<CustomWorkflowCard
|
||||
handleClick={() => {
|
||||
workflowAction.setWorkflowDetails({
|
||||
name: `custom-chaos-workflow-${Math.round(
|
||||
new Date().getTime() / 1000
|
||||
)}`,
|
||||
description: 'Custom Chaos Workflow',
|
||||
isCustomWorkflow: true,
|
||||
namespace: 'litmus',
|
||||
customWorkflows: [],
|
||||
});
|
||||
history.push('/create-workflow/custom');
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -141,6 +141,16 @@ const SideBar: React.FC = () => {
|
|||
</CustomisedListItem>
|
||||
)}
|
||||
</List>
|
||||
<div className={classes.versionDiv}>
|
||||
<img
|
||||
src="/icons/litmusPurple.svg"
|
||||
alt="litmus logo"
|
||||
className={classes.versionlogo}
|
||||
/>
|
||||
<Typography className={classes.versionText}>
|
||||
{t('sidebar.version')}
|
||||
</Typography>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -67,6 +67,22 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
marginTop: theme.spacing(8),
|
||||
height: '16.68rem',
|
||||
},
|
||||
versionlogo: {
|
||||
width: '1.25rem',
|
||||
height: '2.185rem',
|
||||
},
|
||||
versionText: {
|
||||
margin: 'auto',
|
||||
marginLeft: theme.spacing(1.25),
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
versionDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
marginTop: 'auto',
|
||||
marginBottom: theme.spacing(2),
|
||||
marginLeft: theme.spacing(4),
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
import { Typography } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { history } from '../../../redux/configureStore';
|
||||
import ButtonOutline from '../../Button/ButtonOutline';
|
||||
// import BrowseWorkflow from '../TargetHome/BrowseWorkflow';
|
||||
import useStyles from './styles';
|
||||
import Scaffold from '../../../containers/layouts/Scaffold';
|
||||
import TargetCopy from '../TargetCopy';
|
||||
import { Cluster } from '../../../models/graphql/clusterData';
|
||||
import { Cluster, DeleteCluster } from '../../../models/graphql/clusterData';
|
||||
import { LocationState } from '../../../models/routerModel';
|
||||
import { DELETE_CLUSTER } from '../../../graphql';
|
||||
import Unimodal from '../../../containers/layouts/Unimodal';
|
||||
import ButtonFilled from '../../Button/ButtonFilled';
|
||||
import BackButton from '../../Button/BackButton';
|
||||
import { RootState } from '../../../redux/reducers';
|
||||
|
||||
interface ClusterProps {
|
||||
data: Cluster;
|
||||
|
@ -21,19 +28,25 @@ const ClusterInfo: React.FC<ClusterVarsProps> = ({ location }) => {
|
|||
const { data } = location.state;
|
||||
const classes = useStyles();
|
||||
const link: string = data.token;
|
||||
const handleClick = () => {
|
||||
|
||||
const [deleteCluster] = useMutation<DeleteCluster>(DELETE_CLUSTER);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const handleDelete = () => {
|
||||
deleteCluster({ variables: { cluster_id: data.cluster_id } });
|
||||
history.push('/targets');
|
||||
};
|
||||
|
||||
const userRole = useSelector((state: RootState) => state.userData.userRole);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Scaffold>
|
||||
<section className="Header section">
|
||||
<div className={classes.backBotton}>
|
||||
<ButtonOutline isDisabled={false} handleClick={handleClick}>
|
||||
<BackButton isDisabled={false}>
|
||||
<div>{t('workflowCluster.header.formControl.back')}</div>
|
||||
</ButtonOutline>
|
||||
</BackButton>
|
||||
<div className={classes.header}>
|
||||
<Typography variant="h4">
|
||||
{t('workflowCluster.header.formControl.clusterInfo')}
|
||||
|
@ -46,32 +59,47 @@ const ClusterInfo: React.FC<ClusterVarsProps> = ({ location }) => {
|
|||
<div className={classes.detailsDiv}>
|
||||
{/* name */}
|
||||
<div className={classes.firstCol}>
|
||||
<div className={classes.status}>
|
||||
<div className={classes.checkCluster}>
|
||||
<Typography variant="h6">
|
||||
<strong>Cluster Details</strong>
|
||||
</Typography>
|
||||
<div className={classes.linkBox}>
|
||||
<div className={classes.status}>
|
||||
<div>
|
||||
<Typography variant="h6">
|
||||
<strong>{t('targets.newTarget.clusterDetails')}</strong>
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
{data.is_active ? (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.active}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu1')}
|
||||
</Typography>
|
||||
) : data.is_cluster_confirmed === false ? (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.pending}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu6')}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.notactive}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu2')}
|
||||
</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{data.is_active ? (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.active}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu1')}
|
||||
</Typography>
|
||||
) : data.is_cluster_confirmed === false ? (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.pending}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu6')}
|
||||
</Typography>
|
||||
) : (
|
||||
<Typography
|
||||
className={`${classes.check} ${classes.notactive}`}
|
||||
>
|
||||
{t('workflowCluster.header.formControl.menu2')}
|
||||
</Typography>
|
||||
)}
|
||||
<div className={classes.buttonBox}>
|
||||
<ButtonOutline
|
||||
isDisabled={userRole === 'Viewer'}
|
||||
handleClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<div className={classes.status}>
|
||||
<img src="/icons/bin-red.svg" alt="Delete" />
|
||||
<div> {t('targets.modalDelete.delete')} </div>
|
||||
</div>
|
||||
</ButtonOutline>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -97,18 +125,58 @@ const ClusterInfo: React.FC<ClusterVarsProps> = ({ location }) => {
|
|||
<Typography>{t('targets.newTarget.head1')}</Typography>
|
||||
<Typography>{t('targets.newTarget.head2')}</Typography>
|
||||
<Typography>{t('targets.newTarget.head3')}</Typography>
|
||||
{/*
|
||||
<Typography>
|
||||
{t('targets.newTarget.head4')}{' '}
|
||||
<strong>{t('targets.newTarget.head5')}</strong>
|
||||
</Typography>
|
||||
*/}
|
||||
</div>
|
||||
<div className={classes.rightMargin}>
|
||||
{link && <TargetCopy yamlLink={link} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{open ? (
|
||||
<div>
|
||||
<Unimodal
|
||||
open={open}
|
||||
handleClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
hasCloseBtn
|
||||
>
|
||||
<div className={classes.body}>
|
||||
<img src="/icons/bin-red-delete.svg" alt="Delete" />
|
||||
<div className={classes.text}>
|
||||
<Typography className={classes.typo} align="center">
|
||||
{t('targets.modalDelete.head1')} <br />
|
||||
<strong> {t('targets.modalDelete.head2')}</strong>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.textSecond}>
|
||||
<Typography className={classes.typoSub} align="center">
|
||||
{t('targets.modalDelete.head3')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.buttonGroup}>
|
||||
<ButtonOutline
|
||||
isDisabled={false}
|
||||
handleClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<> {t('targets.modalDelete.no')}</>
|
||||
</ButtonOutline>
|
||||
|
||||
<ButtonFilled
|
||||
isDisabled={false}
|
||||
isPrimary
|
||||
handleClick={handleDelete}
|
||||
>
|
||||
<>{t('targets.modalDelete.yes')}</>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</Unimodal>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Scaffold>
|
||||
|
|
|
@ -36,9 +36,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
paddingTop: theme.spacing(5),
|
||||
marginLeft: theme.spacing(2),
|
||||
},
|
||||
checkCluster: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
|
||||
version: {
|
||||
marginTop: theme.spacing(2),
|
||||
},
|
||||
|
@ -48,9 +46,20 @@ const useStyles = makeStyles((theme) => ({
|
|||
marginLeft: theme.spacing(5),
|
||||
marginTop: theme.spacing(4),
|
||||
},
|
||||
linkBox: {
|
||||
backgroundColor: theme.palette.common.white,
|
||||
paddingRight: theme.spacing(9),
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
width: '100%',
|
||||
wordWrap: 'break-word',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
status: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
gap: '1rem',
|
||||
},
|
||||
expDiv: {
|
||||
display: 'flex',
|
||||
|
@ -68,6 +77,10 @@ const useStyles = makeStyles((theme) => ({
|
|||
rightMargin: {
|
||||
marginRight: theme.spacing(8),
|
||||
},
|
||||
buttonBox: {
|
||||
display: 'flex',
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
connectdevice: {
|
||||
fontSize: '1rem',
|
||||
lineHeight: '175%',
|
||||
|
@ -102,6 +115,39 @@ const useStyles = makeStyles((theme) => ({
|
|||
background: theme.palette.customColors.menuOption.pending,
|
||||
color: theme.palette.warning.main,
|
||||
},
|
||||
body: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: theme.spacing(7.5),
|
||||
},
|
||||
// styles for text
|
||||
text: {
|
||||
width: '31.93rem',
|
||||
height: '5.875rem',
|
||||
marginTop: theme.spacing(3.75),
|
||||
marginBottom: theme.spacing(3.75),
|
||||
},
|
||||
typo: {
|
||||
fontSize: '2rem',
|
||||
},
|
||||
textSecond: {
|
||||
width: '29.06rem',
|
||||
height: '1.6875rem',
|
||||
marginTop: theme.spacing(1.875),
|
||||
marginBottom: theme.spacing(3.75),
|
||||
},
|
||||
typoSub: {
|
||||
fontSize: '1rem',
|
||||
},
|
||||
// for yes or no buttons
|
||||
buttonGroup: {
|
||||
display: 'flex',
|
||||
gap: '1rem',
|
||||
marginTop: theme.spacing(2.5),
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
}));
|
||||
|
||||
export default useStyles;
|
||||
|
|
|
@ -18,6 +18,7 @@ import { RootState } from '../../../redux/reducers';
|
|||
import Loader from '../../Loader';
|
||||
import ButtonFilled from '../../Button/ButtonFilled';
|
||||
import Unimodal from '../../../containers/layouts/Unimodal';
|
||||
import BackButton from '../../Button/BackButton';
|
||||
|
||||
const ConnectTarget = () => {
|
||||
const classes = useStyles();
|
||||
|
@ -26,10 +27,6 @@ const ConnectTarget = () => {
|
|||
const [id, setID] = React.useState('');
|
||||
const [modal, setModal] = React.useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
history.push('/targets');
|
||||
};
|
||||
|
||||
const selectedProjectID = useSelector(
|
||||
(state: RootState) => state.userData.selectedProjectID
|
||||
);
|
||||
|
@ -71,6 +68,10 @@ const ConnectTarget = () => {
|
|||
project_id: selectedProjectID,
|
||||
cluster_type: 'external',
|
||||
agent_scope: 'cluster',
|
||||
agent_namespace: 'litmus',
|
||||
serviceaccount: '',
|
||||
agent_sa_exists: false,
|
||||
agent_ns_exists: false,
|
||||
};
|
||||
createClusterReg({
|
||||
variables: { ClusterInput: createClusterInput },
|
||||
|
@ -87,13 +88,9 @@ const ConnectTarget = () => {
|
|||
<Scaffold>
|
||||
<section className="Header section">
|
||||
<div className={classes.backBotton}>
|
||||
<ButtonOutline
|
||||
isDisabled={false}
|
||||
handleClick={handleClick}
|
||||
data-cy="backSelect"
|
||||
>
|
||||
<BackButton isDisabled={false}>
|
||||
<Typography>Back</Typography>
|
||||
</ButtonOutline>
|
||||
</BackButton>
|
||||
<div className={classes.header}>
|
||||
<Typography variant="h4">
|
||||
{t('targets.connectHome.connectText')}
|
||||
|
@ -140,7 +137,9 @@ const ConnectTarget = () => {
|
|||
<div>
|
||||
<Unimodal
|
||||
open={modal}
|
||||
handleClose={handleClick}
|
||||
handleClose={() => {
|
||||
history.push('/targets');
|
||||
}}
|
||||
aria-labelledby="simple-modal-title"
|
||||
aria-describedby="simple-modal-description"
|
||||
hasCloseBtn
|
||||
|
|
|
@ -23,7 +23,12 @@ const Welcomemodal: React.FC<WelcomemodalProps> = ({ handleIsOpen }) => {
|
|||
);
|
||||
|
||||
return (
|
||||
<Unimodal open handleClose={handleClose} hasCloseBtn={false}>
|
||||
<Unimodal
|
||||
open
|
||||
handleClose={handleClose}
|
||||
hasCloseBtn={false}
|
||||
disableBackdropClick
|
||||
>
|
||||
{body}
|
||||
</Unimodal>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,6 @@ import React from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import YAML from 'yaml';
|
||||
import workflowsList from '../PredifinedWorkflows/data';
|
||||
import Unimodal from '../../containers/layouts/Unimodal';
|
||||
import { CREATE_WORKFLOW } from '../../graphql';
|
||||
import {
|
||||
|
@ -35,6 +34,7 @@ import ButtonOutline from '../Button/ButtonOutline';
|
|||
import QontoConnector from './quontoConnector';
|
||||
import useStyles from './styles';
|
||||
import useQontoStepIconStyles from './useQontoStepIconStyles';
|
||||
import { cronWorkflow, workflowOnce } from '../../utils/workflowTemplate';
|
||||
|
||||
function getSteps(): string[] {
|
||||
return [
|
||||
|
@ -122,7 +122,6 @@ const CustomStepper = () => {
|
|||
(state: RootState) => state.workflowData
|
||||
);
|
||||
const {
|
||||
id,
|
||||
yaml,
|
||||
weights,
|
||||
description,
|
||||
|
@ -148,68 +147,50 @@ const CustomStepper = () => {
|
|||
const workflow = useActions(WorkflowActions);
|
||||
const [invalidYaml, setinValidYaml] = React.useState(false);
|
||||
const steps = getSteps();
|
||||
const scheduleOnce = workflowOnce;
|
||||
const scheduleMore = cronWorkflow;
|
||||
|
||||
function EditYaml() {
|
||||
const oldParsedYaml = YAML.parse(yaml);
|
||||
let NewLink: string = ' ';
|
||||
let NewYaml: string = ' ';
|
||||
const NewLink: string = ' ';
|
||||
if (
|
||||
oldParsedYaml.kind === 'Workflow' &&
|
||||
scheduleType.scheduleOnce !== 'now'
|
||||
) {
|
||||
NewLink = workflowsList[parseInt(id, 10)].chaosWkfCRDLink_Recur as string;
|
||||
fetch(NewLink)
|
||||
.then((data) => {
|
||||
data.text().then((yamlText) => {
|
||||
const oldParsedYaml = YAML.parse(yaml);
|
||||
const newParsedYaml = YAML.parse(yamlText);
|
||||
delete newParsedYaml.spec.workflowSpec;
|
||||
newParsedYaml.spec.schedule = cronSyntax;
|
||||
delete newParsedYaml.metadata.generateName;
|
||||
newParsedYaml.metadata.name = workflowData.name;
|
||||
newParsedYaml.spec.workflowSpec = oldParsedYaml.spec;
|
||||
const tz = {
|
||||
timezone:
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||
};
|
||||
Object.entries(tz).forEach(([key, value]) => {
|
||||
newParsedYaml.spec[key] = value;
|
||||
});
|
||||
NewYaml = YAML.stringify(newParsedYaml);
|
||||
workflow.setWorkflowDetails({
|
||||
link: NewLink,
|
||||
yaml: NewYaml,
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Unable to fetch the yaml text${err}`);
|
||||
});
|
||||
const oldParsedYaml = YAML.parse(yaml);
|
||||
const newParsedYaml = YAML.parse(scheduleMore);
|
||||
delete newParsedYaml.spec.workflowSpec;
|
||||
newParsedYaml.spec.schedule = cronSyntax;
|
||||
delete newParsedYaml.metadata.generateName;
|
||||
newParsedYaml.metadata.name = workflowData.name;
|
||||
newParsedYaml.spec.workflowSpec = oldParsedYaml.spec;
|
||||
const timeZone = {
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||
};
|
||||
Object.entries(timeZone).forEach(([key, value]) => {
|
||||
newParsedYaml.spec[key] = value;
|
||||
});
|
||||
const NewYaml = YAML.stringify(newParsedYaml);
|
||||
workflow.setWorkflowDetails({
|
||||
link: NewLink,
|
||||
yaml: NewYaml,
|
||||
});
|
||||
}
|
||||
if (
|
||||
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||
scheduleType.scheduleOnce === 'now'
|
||||
) {
|
||||
NewLink = workflowsList[parseInt(id, 10)].chaosWkfCRDLink as string;
|
||||
fetch(NewLink)
|
||||
.then((data) => {
|
||||
data.text().then((yamlText) => {
|
||||
const oldParsedYaml = YAML.parse(yaml);
|
||||
const newParsedYaml = YAML.parse(yamlText);
|
||||
delete newParsedYaml.spec;
|
||||
delete newParsedYaml.metadata.generateName;
|
||||
newParsedYaml.metadata.name = workflowData.name;
|
||||
newParsedYaml.spec = oldParsedYaml.spec.workflowSpec;
|
||||
NewYaml = YAML.stringify(newParsedYaml);
|
||||
workflow.setWorkflowDetails({
|
||||
link: NewLink,
|
||||
yaml: NewYaml,
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Unable to fetch the yaml text${err}`);
|
||||
});
|
||||
const oldParsedYaml = YAML.parse(yaml);
|
||||
const newParsedYaml = YAML.parse(scheduleOnce);
|
||||
delete newParsedYaml.spec;
|
||||
delete newParsedYaml.metadata.generateName;
|
||||
newParsedYaml.metadata.name = workflowData.name;
|
||||
newParsedYaml.spec = oldParsedYaml.spec.workflowSpec;
|
||||
const NewYaml = YAML.stringify(newParsedYaml);
|
||||
workflow.setWorkflowDetails({
|
||||
link: NewLink,
|
||||
yaml: NewYaml,
|
||||
});
|
||||
}
|
||||
if (
|
||||
oldParsedYaml.kind === 'CronWorkflow' &&
|
||||
|
@ -219,13 +200,13 @@ const CustomStepper = () => {
|
|||
newParsedYaml.spec.schedule = cronSyntax;
|
||||
delete newParsedYaml.metadata.generateName;
|
||||
newParsedYaml.metadata.name = workflowData.name;
|
||||
const tz = {
|
||||
const timeZone = {
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
|
||||
};
|
||||
Object.entries(tz).forEach(([key, value]) => {
|
||||
Object.entries(timeZone).forEach(([key, value]) => {
|
||||
newParsedYaml.spec[key] = value;
|
||||
});
|
||||
NewYaml = YAML.stringify(newParsedYaml);
|
||||
const NewYaml = YAML.stringify(newParsedYaml);
|
||||
workflow.setWorkflowDetails({
|
||||
link: NewLink,
|
||||
yaml: NewYaml,
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Redirect, Route, Router, Switch } from 'react-router-dom';
|
|||
import Loader from '../../components/Loader';
|
||||
import useActions from '../../redux/actions';
|
||||
import * as AnalyticsActions from '../../redux/actions/analytics';
|
||||
import * as MyHubActions from '../../redux/actions/myhub';
|
||||
import { history } from '../../redux/configureStore';
|
||||
import { RootState } from '../../redux/reducers';
|
||||
import withTheme from '../../theme';
|
||||
|
@ -131,16 +130,13 @@ const Routes: React.FC<RoutesProps> = ({ isOwner, isProjectAvailable }) => {
|
|||
function App() {
|
||||
const classes = useStyles();
|
||||
const analyticsAction = useActions(AnalyticsActions);
|
||||
const publicHubAction = useActions(MyHubActions);
|
||||
const userData = useSelector((state: RootState) => state.userData);
|
||||
const token = getToken();
|
||||
useEffect(() => {
|
||||
if (token !== '') {
|
||||
analyticsAction.loadCommunityAnalytics();
|
||||
publicHubAction.getAllPublicCharts();
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Router history={history}>
|
||||
|
|
|
@ -76,24 +76,30 @@ export const USER_CLUSTER_REG = gql`
|
|||
`;
|
||||
|
||||
export const ADD_MY_HUB = gql`
|
||||
mutation addMyHub($MyHubDetails: CreateMyHub!, $Username: String!) {
|
||||
addMyHub(myhubInput: $MyHubDetails, username: $Username) {
|
||||
username
|
||||
my_hub {
|
||||
HubName
|
||||
RepoURL
|
||||
RepoBranch
|
||||
}
|
||||
mutation addMyHub($MyHubDetails: CreateMyHub!, $projectID: String!) {
|
||||
addMyHub(myhubInput: $MyHubDetails, projectID: $projectID) {
|
||||
HubName
|
||||
RepoURL
|
||||
RepoBranch
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const SYNC_REPO = gql`
|
||||
mutation syncHub($data: ChartsInput!) {
|
||||
syncHub(syncHubInput: $data) {
|
||||
mutation syncHub($projectID: String!, $HubName: String!) {
|
||||
syncHub(projectID: $projectID, HubName: $HubName) {
|
||||
id
|
||||
RepoURL
|
||||
RepoBranch
|
||||
IsAvailable
|
||||
TotalExp
|
||||
HubName
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_CLUSTER = gql`
|
||||
mutation deleteCluster($cluster_id: String!) {
|
||||
deleteClusterReg(cluster_id: $cluster_id)
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -35,6 +35,7 @@ export const SCHEDULE_DETAILS = gql`
|
|||
cluster_id
|
||||
cluster_type
|
||||
cluster_name
|
||||
isRemoved
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -86,12 +87,6 @@ export const GET_USER = gql`
|
|||
name
|
||||
id
|
||||
}
|
||||
my_hub {
|
||||
id
|
||||
HubName
|
||||
RepoURL
|
||||
RepoBranch
|
||||
}
|
||||
company_name
|
||||
updated_at
|
||||
created_at
|
||||
|
@ -121,6 +116,11 @@ export const GET_CLUSTER = gql`
|
|||
no_of_schedules
|
||||
no_of_workflows
|
||||
token
|
||||
agent_namespace
|
||||
serviceaccount
|
||||
agent_scope
|
||||
agent_ns_exists
|
||||
agent_sa_exists
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -137,8 +137,8 @@ export const ALL_USERS = gql`
|
|||
`;
|
||||
|
||||
export const GET_CHARTS_DATA = gql`
|
||||
query getCharts($data: ChartsInput!) {
|
||||
getCharts(chartsInput: $data) {
|
||||
query getCharts($HubName: String!, $projectID: String!) {
|
||||
getCharts(HubName: $HubName, projectID: $projectID) {
|
||||
ApiVersion
|
||||
Kind
|
||||
Metadata {
|
||||
|
@ -242,7 +242,7 @@ export const GET_EXPERIMENT_DATA = gql`
|
|||
|
||||
export const GET_HUB_STATUS = gql`
|
||||
query getHubStatus($data: String!) {
|
||||
getHubStatus(username: $data) {
|
||||
getHubStatus(projectID: $data) {
|
||||
id
|
||||
HubName
|
||||
RepoBranch
|
||||
|
@ -252,3 +252,15 @@ export const GET_HUB_STATUS = gql`
|
|||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_ENGINE_YAML = gql`
|
||||
query getEngineData($experimentInput: ExperimentInput!) {
|
||||
getYAMLData(experimentInput: $experimentInput)
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_EXPERIMENT_YAML = gql`
|
||||
query getExperimentData($experimentInput: ExperimentInput!) {
|
||||
getYAMLData(experimentInput: $experimentInput)
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -2,7 +2,7 @@ export interface Cluster {
|
|||
cluster_id: string;
|
||||
project_id: string;
|
||||
cluster_name: string;
|
||||
description: String;
|
||||
description: string;
|
||||
platform_name: string;
|
||||
access_key: string;
|
||||
is_registered: boolean;
|
||||
|
@ -14,6 +14,11 @@ export interface Cluster {
|
|||
no_of_workflows: number;
|
||||
no_of_schedules: number;
|
||||
token: string;
|
||||
agent_namespace: string;
|
||||
serviceaccount: string;
|
||||
agent_scope: string;
|
||||
agent_ns_exists: boolean;
|
||||
agent_sa_exists: boolean;
|
||||
}
|
||||
|
||||
export interface Clusters {
|
||||
|
@ -27,7 +32,11 @@ export interface CreateClusterInput {
|
|||
platform_name: string;
|
||||
project_id: string;
|
||||
cluster_type: string;
|
||||
agent_namespace: string;
|
||||
serviceaccount: string;
|
||||
agent_scope: string;
|
||||
agent_ns_exists: boolean;
|
||||
agent_sa_exists: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -44,3 +53,7 @@ export interface CreateClusterInputResponse {
|
|||
export interface ClusterVars {
|
||||
project_id: string;
|
||||
}
|
||||
|
||||
export interface DeleteCluster {
|
||||
cluster_id: string;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ export interface ScheduleWorkflow {
|
|||
cluster_name: string;
|
||||
cluster_type: string;
|
||||
regularity?: string;
|
||||
isRemoved: boolean;
|
||||
}
|
||||
|
||||
export interface Schedules {
|
||||
|
|
|
@ -27,7 +27,6 @@ export interface UserDetails {
|
|||
is_email_verified: string;
|
||||
state: string;
|
||||
role: string;
|
||||
my_hub: MyHubDetail[];
|
||||
}
|
||||
|
||||
export interface MyHubDetail {
|
||||
|
|
|
@ -69,10 +69,6 @@ export interface Charts {
|
|||
getCharts: Chart[];
|
||||
}
|
||||
|
||||
export interface PublicHubData {
|
||||
charts: Chart[];
|
||||
}
|
||||
|
||||
export interface ExperimentDetail {
|
||||
getHubExperiment: Chart;
|
||||
}
|
||||
|
@ -92,7 +88,6 @@ export interface HubStatus {
|
|||
|
||||
export enum MyHubActions {
|
||||
SET_MYHUB = 'SET_MYHUBS',
|
||||
LOAD_PUBLIC_CHARTS = 'LOAD_PUBLIC_CHARTS',
|
||||
}
|
||||
|
||||
interface MyHubActionType<T, P> {
|
||||
|
@ -100,6 +95,7 @@ interface MyHubActionType<T, P> {
|
|||
payload: P;
|
||||
}
|
||||
|
||||
export type MyHubAction =
|
||||
| MyHubActionType<typeof MyHubActions.SET_MYHUB, HubDetails>
|
||||
| MyHubActionType<typeof MyHubActions.LOAD_PUBLIC_CHARTS, Chart[]>;
|
||||
export type MyHubAction = MyHubActionType<
|
||||
typeof MyHubActions.SET_MYHUB,
|
||||
HubDetails
|
||||
>;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export interface UpdateUser {
|
||||
selectedProjectID: string;
|
||||
selectedProjectName: string;
|
||||
selectedProjectOwner: string;
|
||||
userRole: string;
|
||||
loader: boolean;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface customWorkflow {
|
|||
hubName?: string;
|
||||
repoUrl?: string;
|
||||
repoBranch?: string;
|
||||
description: string;
|
||||
yamlLink?: string;
|
||||
yaml?: string;
|
||||
index?: number;
|
||||
|
@ -34,6 +35,8 @@ export interface WorkflowData {
|
|||
description: string;
|
||||
weights: experimentMap[];
|
||||
isCustomWorkflow: boolean;
|
||||
namespace: string;
|
||||
clustername: string;
|
||||
clusterid: string;
|
||||
cronSyntax: string;
|
||||
scheduleType: scheduleType;
|
||||
|
|
|
@ -12,34 +12,27 @@ function getStepContent(
|
|||
): React.ReactNode {
|
||||
switch (stepIndex) {
|
||||
case 0:
|
||||
return <CreateWorkflow gotoStep={(page: number) => gotoStep(page)} />;
|
||||
return <CreateWorkflow gotoStep={gotoStep} />;
|
||||
case 1:
|
||||
return <TuneCustomWorkflow gotoStep={(page: number) => gotoStep(page)} />;
|
||||
return <TuneCustomWorkflow gotoStep={gotoStep} />;
|
||||
case 2:
|
||||
return (
|
||||
<ScheduleCustomWorkflow gotoStep={(page: number) => gotoStep(page)} />
|
||||
);
|
||||
return <ScheduleCustomWorkflow gotoStep={gotoStep} />;
|
||||
case 3:
|
||||
return <ExperimentEditor gotoStep={(page: number) => gotoStep(page)} />;
|
||||
return <ExperimentEditor gotoStep={gotoStep} />;
|
||||
default:
|
||||
return <CreateWorkflow gotoStep={(page: number) => gotoStep(page)} />;
|
||||
return <CreateWorkflow gotoStep={gotoStep} />;
|
||||
}
|
||||
}
|
||||
|
||||
const CreateCustomWorkflow = () => {
|
||||
const classes = useStyles();
|
||||
const [activeStep, setActiveStep] = React.useState(0);
|
||||
function gotoStep({ page }: { page: number }) {
|
||||
const gotoStep = (page: number) => {
|
||||
setActiveStep(page);
|
||||
}
|
||||
|
||||
};
|
||||
return (
|
||||
<Scaffold>
|
||||
<div className={classes.root}>
|
||||
{getStepContent(activeStep, (page: number) => gotoStep({ page }))}
|
||||
</div>
|
||||
<div className={classes.root}>{getStepContent(activeStep, gotoStep)}</div>
|
||||
</Scaffold>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateCustomWorkflow;
|
||||
|
|
|
@ -115,6 +115,7 @@ const HomePage: React.FC = () => {
|
|||
selectedProjectID: isOwnerOfProject.id,
|
||||
userRole: 'Owner',
|
||||
selectedProjectName: isOwnerOfProject.name,
|
||||
selectedProjectOwner: userData.username,
|
||||
});
|
||||
user.updateUserDetails({ loader: false });
|
||||
// Flush data to persistor immediately
|
||||
|
|
|
@ -28,7 +28,7 @@ const MyHub = () => {
|
|||
|
||||
// Get MyHubs with Status
|
||||
const { data, loading, refetch } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.username },
|
||||
variables: { data: userData.selectedProjectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
|
@ -104,7 +104,11 @@ const MyHub = () => {
|
|||
{hub.IsAvailable ? 'Connected' : 'Error'}
|
||||
</Typography>
|
||||
<img
|
||||
src="/icons/my-hub-charts.svg"
|
||||
src={`/icons/${
|
||||
hub.HubName === 'Chaos Hub'
|
||||
? 'myhub-litmus.svg'
|
||||
: 'my-hub-charts.svg'
|
||||
}`}
|
||||
alt="add-hub"
|
||||
/>
|
||||
<Typography
|
||||
|
@ -144,12 +148,8 @@ const MyHub = () => {
|
|||
onClick={() => {
|
||||
syncRepo({
|
||||
variables: {
|
||||
data: {
|
||||
HubName: hub.HubName,
|
||||
UserName: userData.username,
|
||||
RepoURL: hub.RepoURL,
|
||||
RepoBranch: hub.RepoBranch,
|
||||
},
|
||||
HubName: hub.HubName,
|
||||
projectID: userData.selectedProjectID,
|
||||
},
|
||||
});
|
||||
setKey(hub.id);
|
||||
|
@ -165,22 +165,24 @@ const MyHub = () => {
|
|||
</Paper>
|
||||
);
|
||||
})}
|
||||
<Card
|
||||
elevation={3}
|
||||
className={classes.cardDiv}
|
||||
onClick={() => {
|
||||
history.push({ pathname: '/myhub/connect' });
|
||||
}}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<img src="/icons/add-hub.svg" alt="add-hub" />
|
||||
<Typography variant="h6" align="center">
|
||||
{t('myhub.mainPage.connectNewHub')}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
{userData.userRole !== 'Viewer' ? (
|
||||
<Card
|
||||
elevation={3}
|
||||
className={classes.cardDiv}
|
||||
onClick={() => {
|
||||
history.push({ pathname: '/myhub/connect' });
|
||||
}}
|
||||
>
|
||||
<CardActionArea>
|
||||
<CardContent className={classes.cardContent}>
|
||||
<img src="/icons/add-hub.svg" alt="add-hub" />
|
||||
<Typography variant="h6" align="center">
|
||||
{t('myhub.mainPage.connectNewHub')}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -11,21 +11,3 @@ export function setHubDetails(selectedHub: HubDetails): MyHubAction {
|
|||
payload: selectedHub,
|
||||
};
|
||||
}
|
||||
|
||||
export const getAllPublicCharts = () => (dispatch: Function) => {
|
||||
fetch('https://hub.litmuschaos.io/api/charts/master')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
dispatch({
|
||||
type: MyHubActions.LOAD_PUBLIC_CHARTS,
|
||||
payload: data,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Cant load data', error);
|
||||
dispatch({
|
||||
type: MyHubActions.LOAD_PUBLIC_CHARTS,
|
||||
payload: [],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -12,8 +12,7 @@ import * as templateReducer from './template';
|
|||
import * as userReducer from './user';
|
||||
import * as workflowReducer from './workflow';
|
||||
import * as hubDetails from './myhub';
|
||||
import * as publicHubDetails from './publicHub';
|
||||
import { HubDetails, PublicHubData } from '../../models/redux/myhub';
|
||||
import { HubDetails } from '../../models/redux/myhub';
|
||||
|
||||
export interface RootState {
|
||||
communityData: AnalyticsData;
|
||||
|
@ -23,7 +22,6 @@ export interface RootState {
|
|||
tabNumber: TabState;
|
||||
selectTemplate: TemplateData;
|
||||
hubDetails: HubDetails;
|
||||
publicHubDetails: PublicHubData;
|
||||
}
|
||||
|
||||
export default () =>
|
||||
|
@ -35,5 +33,4 @@ export default () =>
|
|||
...tabsReducer,
|
||||
...templateReducer,
|
||||
...hubDetails,
|
||||
...publicHubDetails,
|
||||
});
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import {
|
||||
Chart,
|
||||
MyHubAction,
|
||||
MyHubActions,
|
||||
PublicHubData,
|
||||
} from '../../models/redux/myhub';
|
||||
import createReducer from './createReducer';
|
||||
|
||||
const initialState: PublicHubData = {
|
||||
charts: [],
|
||||
};
|
||||
|
||||
export const publicHubDetails = createReducer<PublicHubData>(initialState, {
|
||||
[MyHubActions.LOAD_PUBLIC_CHARTS](state: PublicHubData, action: MyHubAction) {
|
||||
return {
|
||||
...state,
|
||||
charts: action.payload as Chart[],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default publicHubDetails;
|
|
@ -17,6 +17,7 @@ const initialState: UserData = {
|
|||
name: '',
|
||||
email: '',
|
||||
loader: false,
|
||||
selectedProjectOwner: '',
|
||||
};
|
||||
|
||||
export const userData = createReducer<UserData>(initialState, {
|
||||
|
|
|
@ -14,6 +14,7 @@ const initialState: WorkflowData = {
|
|||
description: '',
|
||||
weights: [],
|
||||
isCustomWorkflow: false,
|
||||
namespace: 'litmus',
|
||||
clusterid: '',
|
||||
cronSyntax: '',
|
||||
scheduleType: {
|
||||
|
@ -35,9 +36,11 @@ const initialState: WorkflowData = {
|
|||
yamlLink: '',
|
||||
yaml: '',
|
||||
index: -1,
|
||||
description: '',
|
||||
},
|
||||
customWorkflows: [],
|
||||
stepperActiveStep: 1,
|
||||
clustername: '',
|
||||
};
|
||||
|
||||
export const workflowData = createReducer<WorkflowData>(initialState, {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export const workflowOnce =
|
||||
'{\r\n "apiVersion": "argoproj.io/v1alpha1",\r\n "kind": "Workflow",\r\n "metadata": {\r\n "generateName": "argowf-chaos-node-cpu-hog-",\r\n "namespace": "litmus"\r\n },\r\n "spec": null\r\n}';
|
||||
export const cronWorkflow =
|
||||
'{\r\n "apiVersion": "argoproj.io/v1alpha1",\r\n "kind": "CronWorkflow",\r\n "metadata": {\r\n "name": "argo-chaos-pod-memory-cron-wf",\r\n "namespace": "litmus"\r\n },\r\n "spec": {\r\n "schedule": "0 * * * *",\r\n "concurrencyPolicy": "Forbid",\r\n "startingDeadlineSeconds": 0,\r\n "workflowSpec": null\r\n }\r\n}';
|
|
@ -3,27 +3,37 @@
|
|||
/* eslint-disable no-console */
|
||||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
IconButton,
|
||||
MuiThemeProvider,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
Paper,
|
||||
IconButton,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import useTheme from '@material-ui/core/styles/useTheme';
|
||||
import ExpandMoreTwoToneIcon from '@material-ui/icons/ExpandMoreTwoTone';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
import * as _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import useTheme from '@material-ui/core/styles/useTheme';
|
||||
import ExpandMoreTwoToneIcon from '@material-ui/icons/ExpandMoreTwoTone';
|
||||
import * as _ from 'lodash';
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import { WORKFLOW_LIST_DETAILS } from '../../../../graphql/quries';
|
||||
import {
|
||||
ExecutionData,
|
||||
WeightageMap,
|
||||
Workflow,
|
||||
WorkflowList,
|
||||
WorkflowListDataVars,
|
||||
} from '../../../../models/graphql/workflowListData';
|
||||
import { RootState } from '../../../../redux/reducers';
|
||||
import {
|
||||
customThemeAnalyticsTable,
|
||||
customThemeAnalyticsTableCompareMode,
|
||||
|
@ -34,21 +44,11 @@ import {
|
|||
sortNumAsc,
|
||||
sortNumDesc,
|
||||
} from '../../../../utils/sort';
|
||||
import { WORKFLOW_LIST_DETAILS } from '../../../../graphql/quries';
|
||||
import { RootState } from '../../../../redux/reducers';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import ResilienceScoreComparisonPlot from '../WorkflowComparisonPlot/index';
|
||||
import useStyles from './styles';
|
||||
import TableData from './TableData';
|
||||
import TableHeader from './TableHeader';
|
||||
import TableToolBar from './TableToolbar';
|
||||
import {
|
||||
WeightageMap,
|
||||
ExecutionData,
|
||||
Workflow,
|
||||
WorkflowList,
|
||||
WorkflowListDataVars,
|
||||
} from '../../../../models/graphql/workflowListData';
|
||||
|
||||
interface RangeType {
|
||||
startDate: string;
|
||||
|
@ -466,7 +466,7 @@ const WorkflowComparisonTable = () => {
|
|||
const doc = new jsPDF('p', 'mm', 'a4'); // A4 size page of PDF
|
||||
const position = -45;
|
||||
doc.setFontSize(10);
|
||||
doc.text('Litmus Portal Report Version: 1.9', 10, 10);
|
||||
doc.text('Litmus Portal Report Version: 1.10', 10, 10);
|
||||
doc.text('Time of Generation:', 10, 15);
|
||||
doc.text(new Date().toString(), 42, 15);
|
||||
doc.text(
|
||||
|
|
|
@ -2,16 +2,22 @@ import { TableCell, Typography, IconButton } from '@material-ui/core';
|
|||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import useStyles from './styles';
|
||||
import { Cluster } from '../../../models/graphql/clusterData';
|
||||
import { history } from '../../../redux/configureStore';
|
||||
import timeDifferenceForDate from '../../../utils/datesModifier';
|
||||
import Unimodal from '../../../containers/layouts/Unimodal';
|
||||
import ButtonFilled from '../../../components/Button/ButtonFilled';
|
||||
import ButtonOutline from '../../../components/Button/ButtonOutline';
|
||||
import { RootState } from '../../../redux/reducers';
|
||||
|
||||
interface TableDataProps {
|
||||
data: Cluster;
|
||||
deleteRow: (clid: string) => void;
|
||||
}
|
||||
|
||||
const TableData: React.FC<TableDataProps> = ({ data }) => {
|
||||
const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -22,6 +28,19 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
if (date) return resDate;
|
||||
return 'Date not available';
|
||||
};
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const handleClick = () => {
|
||||
setOpen(true);
|
||||
};
|
||||
const userRole = useSelector((state: RootState) => state.userData.userRole);
|
||||
|
||||
const handleClose = () => {
|
||||
deleteRow(data.cluster_id);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableCell className={classes.tableDataStatus}>
|
||||
|
@ -62,6 +81,64 @@ const TableData: React.FC<TableDataProps> = ({ data }) => {
|
|||
<Typography>{data.no_of_workflows}</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{timeDifferenceForDate(data.updated_at)}</TableCell>
|
||||
<TableCell>
|
||||
<div className={classes.deleteCluster}>
|
||||
<div>
|
||||
<IconButton onClick={handleClick}>
|
||||
<img alt="delete" src="./icons/bin-red.svg" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div>
|
||||
<Typography>{t('targets.modalDelete.delete')}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{open ? (
|
||||
<div>
|
||||
<Unimodal
|
||||
open={open}
|
||||
handleClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
hasCloseBtn
|
||||
>
|
||||
<div className={classes.body}>
|
||||
<img src="/icons/bin-red-delete.svg" alt="Delete" />
|
||||
<div className={classes.text}>
|
||||
<Typography className={classes.typo} align="center">
|
||||
{t('targets.modalDelete.head1')} <br />
|
||||
<strong> {t('targets.modalDelete.head2')}</strong>
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.textSecond}>
|
||||
<Typography className={classes.typoSub} align="center">
|
||||
{t('targets.modalDelete.head3')}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.buttonGroup}>
|
||||
<ButtonOutline
|
||||
isDisabled={false}
|
||||
handleClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<> {t('targets.modalDelete.no')}</>
|
||||
</ButtonOutline>
|
||||
|
||||
<ButtonFilled
|
||||
isDisabled={userRole === 'Viewer'}
|
||||
isPrimary
|
||||
handleClick={handleClose}
|
||||
>
|
||||
<>{t('targets.modalDelete.yes')}</>
|
||||
</ButtonFilled>
|
||||
</div>
|
||||
</div>
|
||||
</Unimodal>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</TableCell>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -13,11 +13,11 @@ import {
|
|||
import moment from 'moment';
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import HeaderSection from './HeaderSection';
|
||||
import useStyles from './styles';
|
||||
import { GET_CLUSTER } from '../../../graphql';
|
||||
import { GET_CLUSTER, DELETE_CLUSTER } from '../../../graphql';
|
||||
import Loader from '../../../components/Loader';
|
||||
import { RootState } from '../../../redux/reducers';
|
||||
import TableData from './TableData';
|
||||
|
@ -31,6 +31,7 @@ import {
|
|||
Cluster,
|
||||
ClusterVars,
|
||||
Clusters,
|
||||
DeleteCluster,
|
||||
} from '../../../models/graphql/clusterData';
|
||||
|
||||
interface FilterOptions {
|
||||
|
@ -79,6 +80,9 @@ const BrowseCluster = () => {
|
|||
}
|
||||
);
|
||||
|
||||
// Apollo mutation to delete the selected Target Cluster
|
||||
const [deleteCluster] = useMutation<DeleteCluster>(DELETE_CLUSTER);
|
||||
|
||||
const [dateRange, setDateRange] = React.useState<DateData>({
|
||||
dateValue: 'Select a period',
|
||||
fromDate: new Date(0).toString(),
|
||||
|
@ -205,6 +209,12 @@ const BrowseCluster = () => {
|
|||
};
|
||||
const { t } = useTranslation();
|
||||
|
||||
const deleteRow = (clid: string) => {
|
||||
deleteCluster({
|
||||
variables: { cluster_id: clid },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<section className="Heading section">
|
||||
|
@ -309,6 +319,15 @@ const BrowseCluster = () => {
|
|||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
||||
{/* Delete Cluster */}
|
||||
<TableCell className={classes.headData}>
|
||||
<div className={classes.tableCell}>
|
||||
<Typography>
|
||||
{t('workflowCluster.header.formControl.delete')}
|
||||
</Typography>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
|
@ -316,13 +335,13 @@ const BrowseCluster = () => {
|
|||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<TableCell colSpan={7}>
|
||||
<Loader />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error ? (
|
||||
<TableRow>
|
||||
<TableCell data-cy="browseClusterError" colSpan={6}>
|
||||
<TableCell data-cy="browseClusterError" colSpan={7}>
|
||||
<Typography align="center">
|
||||
{t('workflowCluster.header.formControl.fetchingError')}
|
||||
</Typography>
|
||||
|
@ -341,7 +360,7 @@ const BrowseCluster = () => {
|
|||
key={data.cluster_id}
|
||||
className={classes.dataRow}
|
||||
>
|
||||
<TableData data={data} />
|
||||
<TableData data={data} deleteRow={deleteRow} />
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
|
|
|
@ -213,10 +213,9 @@ const useStyles = makeStyles((theme) => ({
|
|||
// for yes or no buttons
|
||||
buttonGroup: {
|
||||
display: 'flex',
|
||||
width: '10.75rem',
|
||||
height: '2.75rem',
|
||||
marginTop: theme.spacing(2.5),
|
||||
justifyContent: 'space-between',
|
||||
gap: '1rem',
|
||||
},
|
||||
// delete user
|
||||
delDiv: {
|
||||
|
|
|
@ -13,6 +13,8 @@ import MoreVertIcon from '@material-ui/icons/MoreVert';
|
|||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import cronstrue from 'cronstrue';
|
||||
import YAML from 'yaml';
|
||||
import GetAppIcon from '@material-ui/icons/GetApp';
|
||||
import { ScheduleWorkflow } from '../../../models/graphql/scheduleData';
|
||||
import useStyles from './styles';
|
||||
import ExperimentPoints from './ExperimentPoints';
|
||||
|
@ -48,6 +50,21 @@ const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
|
|||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
// Function to download the manifest
|
||||
const downloadYAML = (manifest: string, name: string) => {
|
||||
const parsedYAML = YAML.parse(manifest);
|
||||
const doc = new YAML.Document();
|
||||
doc.contents = parsedYAML;
|
||||
const element = document.createElement('a');
|
||||
const file = new Blob([YAML.stringify(doc)], {
|
||||
type: 'text/yaml',
|
||||
});
|
||||
element.href = URL.createObjectURL(file);
|
||||
element.download = `${name}.yaml`;
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
};
|
||||
|
||||
// Function to convert UNIX time in format of DD MMM YYY
|
||||
const formatDate = (date: string) => {
|
||||
const updated = new Date(parseInt(date, 10) * 1000).toString();
|
||||
|
@ -150,6 +167,22 @@ const TableData: React.FC<TableDataProps> = ({ data, deleteRow }) => {
|
|||
open={open}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<MenuItem
|
||||
value="Download"
|
||||
onClick={() =>
|
||||
downloadYAML(data.workflow_manifest, data.workflow_name)
|
||||
}
|
||||
>
|
||||
<div className={classes.expDiv}>
|
||||
<GetAppIcon className={classes.downloadBtn} />
|
||||
<Typography
|
||||
data-cy="downloadManifest"
|
||||
className={classes.downloadText}
|
||||
>
|
||||
Download Manifest
|
||||
</Typography>
|
||||
</div>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
value="Analysis"
|
||||
onClick={() => deleteRow(data.workflow_id)}
|
||||
|
|
|
@ -152,7 +152,15 @@ const useStyles = makeStyles((theme) => ({
|
|||
btnText: {
|
||||
paddingLeft: theme.spacing(1.625),
|
||||
},
|
||||
|
||||
downloadText: {
|
||||
paddingLeft: theme.spacing(1.2),
|
||||
},
|
||||
downloadBtn: {
|
||||
marginTop: theme.spacing(0.375),
|
||||
marginLeft: theme.spacing(-0.375),
|
||||
width: '1.2rem',
|
||||
height: '1.2rem',
|
||||
},
|
||||
// Experiment Weights PopOver Property
|
||||
weightDiv: {
|
||||
width: '15.1875rem',
|
||||
|
|
|
@ -15,7 +15,7 @@ const Templates = () => {
|
|||
const testWeights: number[] = [];
|
||||
|
||||
// Setting initial selected template ID to 0
|
||||
template.selectTemplate({ selectedTemplateID: -1, isDisable: true });
|
||||
template.selectTemplate({ selectedTemplateID: 0, isDisable: true });
|
||||
|
||||
const selectWorkflow = (index: number) => {
|
||||
// Updating template ID to the selected one
|
||||
|
@ -67,6 +67,7 @@ const Templates = () => {
|
|||
selectWorkflow(index);
|
||||
}}
|
||||
workflows={workflowData}
|
||||
isCustomWorkflowVisible={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -157,6 +157,7 @@ const ChooseWorkflow: React.FC = () => {
|
|||
selectWorkflow(index);
|
||||
}}
|
||||
workflows={workflowsList}
|
||||
isCustomWorkflowVisible
|
||||
/>
|
||||
<div className={classes.paddedTop}>
|
||||
<ButtonFilled
|
||||
|
|
|
@ -5,10 +5,10 @@ import useStyles from './styles';
|
|||
|
||||
interface BackButtonProps {
|
||||
isDisabled: boolean;
|
||||
gotoStep: (page: number) => void;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const BackButton: React.FC<BackButtonProps> = ({ isDisabled, gotoStep }) => {
|
||||
const BackButton: React.FC<BackButtonProps> = ({ isDisabled, onClick }) => {
|
||||
const classes = useStyles();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
|
@ -16,7 +16,7 @@ const BackButton: React.FC<BackButtonProps> = ({ isDisabled, gotoStep }) => {
|
|||
size="medium"
|
||||
className={classes.btn}
|
||||
disabled={isDisabled}
|
||||
onClick={() => gotoStep(1)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img src="/icons/back.svg" alt="back" />
|
||||
<Typography className={classes.text}>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { useLazyQuery, useQuery } from '@apollo/client';
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
InputLabel,
|
||||
|
@ -8,29 +10,37 @@ import {
|
|||
MenuList,
|
||||
OutlinedInput,
|
||||
Paper,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Typography,
|
||||
ClickAwayListener,
|
||||
} from '@material-ui/core';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
||||
import BackButton from '../../../../components/Button/BackButton';
|
||||
import YAML from 'yaml';
|
||||
import ButtonFilled from '../../../../components/Button/ButtonFilled';
|
||||
import InputField from '../../../../components/InputField';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import { GET_CHARTS_DATA, GET_HUB_STATUS } from '../../../../graphql';
|
||||
import { MyHubDetail } from '../../../../models/graphql/user';
|
||||
import { Chart, Charts, HubStatus } from '../../../../models/redux/myhub';
|
||||
import { Charts, HubStatus } from '../../../../models/redux/myhub';
|
||||
import * as WorkflowActions from '../../../../redux/actions/workflow';
|
||||
import useActions from '../../../../redux/actions';
|
||||
import { RootState } from '../../../../redux/reducers';
|
||||
import useStyles, { CustomTextField, MenuProps } from './styles';
|
||||
import WorkflowDetails from '../../../../pages/WorkflowDetails';
|
||||
import { GET_EXPERIMENT_YAML } from '../../../../graphql/quries';
|
||||
import BackButton from '../BackButton';
|
||||
import * as TemplateSelectionActions from '../../../../redux/actions/template';
|
||||
import { history } from '../../../../redux/configureStore';
|
||||
|
||||
interface WorkflowDetails {
|
||||
workflow_name: string;
|
||||
workflow_desc: string;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
interface ChartName {
|
||||
|
@ -43,33 +53,61 @@ interface VerifyCommitProps {
|
|||
}
|
||||
|
||||
const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
||||
const userData = useSelector((state: RootState) => state.userData);
|
||||
const hubData = useSelector((state: RootState) => state.publicHubDetails);
|
||||
const workflowDetails = useSelector((state: RootState) => state.workflowData);
|
||||
const workflowAction = useActions(WorkflowActions);
|
||||
|
||||
const { selectedProjectID } = useSelector(
|
||||
(state: RootState) => state.userData
|
||||
);
|
||||
|
||||
const [workflowData, setWorkflowData] = useState<WorkflowDetails>({
|
||||
workflow_name: workflowDetails.name,
|
||||
workflow_desc: workflowDetails.description,
|
||||
namespace: workflowDetails.namespace,
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
const classes = useStyles();
|
||||
const [allExperiment, setAllExperiment] = useState<ChartName[]>([]);
|
||||
const [selectedHub, setSelectedHub] = useState('Public Hub');
|
||||
|
||||
const [allExperiments, setAllExperiments] = useState<ChartName[]>([]);
|
||||
const [selectedHub, setSelectedHub] = useState('');
|
||||
const [selectedExp, setSelectedExp] = useState(
|
||||
t('customWorkflow.createWorkflow.selectAnExp') as string
|
||||
);
|
||||
const allExp: ChartName[] = [];
|
||||
|
||||
const [availableHubs, setAvailableHubs] = useState<MyHubDetail[]>([]);
|
||||
const template = useActions(TemplateSelectionActions);
|
||||
const [constructYAML, setConstructYAML] = useState('construct');
|
||||
const [selectedHubDetails, setSelectedHubDetails] = useState<MyHubDetail>();
|
||||
// Get all MyHubs with status
|
||||
const { data } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.username },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
|
||||
const [getExperimentYaml] = useLazyQuery(GET_EXPERIMENT_YAML, {
|
||||
variables: {
|
||||
experimentInput: {
|
||||
ProjectID: selectedProjectID,
|
||||
HubName: selectedHub,
|
||||
ChartName: selectedExp.split('/')[0],
|
||||
ExperimentName: selectedExp.split('/')[1],
|
||||
FileType: 'experiment',
|
||||
},
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
const parsedYaml = YAML.parse(data.getYAMLData);
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
description: parsedYaml.description.message,
|
||||
},
|
||||
});
|
||||
gotoStep(1);
|
||||
},
|
||||
});
|
||||
|
||||
// Graphql query to get charts
|
||||
const [getCharts, { loading: chartsLoading }] = useLazyQuery<Charts>(
|
||||
GET_CHARTS_DATA,
|
||||
{
|
||||
onCompleted: (data) => {
|
||||
const allExp: ChartName[] = [];
|
||||
data.getCharts.forEach((data) => {
|
||||
return data.Spec.Experiments?.forEach((experiment) => {
|
||||
allExp.push({
|
||||
|
@ -78,11 +116,29 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
});
|
||||
});
|
||||
});
|
||||
setAllExperiment(allExp);
|
||||
setAllExperiments([...allExp]);
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
}
|
||||
);
|
||||
|
||||
// Get all MyHubs with status
|
||||
const { data } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: selectedProjectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
onCompleted: (data) => {
|
||||
setSelectedHub(data.getHubStatus[0].HubName);
|
||||
setAvailableHubs([...data.getHubStatus]);
|
||||
getCharts({
|
||||
variables: {
|
||||
projectID: selectedProjectID,
|
||||
HubName: data.getHubStatus[0].HubName,
|
||||
},
|
||||
});
|
||||
setSelectedHubDetails(data.getHubStatus[0]);
|
||||
},
|
||||
});
|
||||
|
||||
// Function to get charts of a particular hub
|
||||
const findChart = (hubname: string) => {
|
||||
const myHubData = data?.getHubStatus.filter((myHub) => {
|
||||
|
@ -90,68 +146,77 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
})[0];
|
||||
getCharts({
|
||||
variables: {
|
||||
data: {
|
||||
UserName: userData.username,
|
||||
RepoURL: myHubData?.RepoURL,
|
||||
RepoBranch: myHubData?.RepoBranch,
|
||||
HubName: hubname,
|
||||
},
|
||||
projectID: selectedProjectID,
|
||||
HubName: hubname,
|
||||
},
|
||||
});
|
||||
setSelectedHubDetails(myHubData);
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
hubName: hubname,
|
||||
repoUrl: myHubData?.RepoURL,
|
||||
repoBranch: myHubData?.RepoBranch,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedHub === 'Public Hub') {
|
||||
setSelectedHub('Public Hub');
|
||||
const ChartsData = hubData.charts;
|
||||
ChartsData.forEach((data: Chart) => {
|
||||
if (data.Spec.Experiments) {
|
||||
data.Spec.Experiments.forEach((experiment) => {
|
||||
allExp.push({
|
||||
ChaosName: data.Metadata.Name,
|
||||
ExperimentName: experiment,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
setAllExperiment([...allExp]);
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
hubName: 'Public Hub',
|
||||
repoUrl: 'https://github.com/litmuschaos/chaos-charts',
|
||||
repoBranch: 'master',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setAllExperiment([]);
|
||||
}
|
||||
}, [selectedHub]);
|
||||
const availableHubs: MyHubDetail[] = data ? data.getHubStatus : [];
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const filteredExperiment = allExperiment.filter((exp) => {
|
||||
const filteredExperiment = allExperiments.filter((exp) => {
|
||||
const name = `${exp.ChaosName}/${exp.ExperimentName}`;
|
||||
if (selectedExp === 'Select an experiment') {
|
||||
return true;
|
||||
}
|
||||
return name.includes(selectedExp);
|
||||
});
|
||||
const [uploadedYAML, setUploadedYAML] = useState('');
|
||||
const [fileName, setFileName] = useState<string | null>('');
|
||||
|
||||
const handleDrag = (e: React.DragEvent<HTMLDivElement>) => {
|
||||
Array.from(e.dataTransfer.files)
|
||||
.filter(
|
||||
(file) =>
|
||||
file.name.split('.')[1] === 'yaml' ||
|
||||
file.name.split('.')[1] === 'yml'
|
||||
)
|
||||
.forEach(async (file) => {
|
||||
const readFile = await file.text();
|
||||
setUploadedYAML(readFile);
|
||||
setFileName(file.name);
|
||||
const parsedYaml = YAML.parse(readFile);
|
||||
workflowAction.setWorkflowDetails({
|
||||
...workflowDetails,
|
||||
yaml: YAML.stringify(parsedYaml),
|
||||
});
|
||||
});
|
||||
};
|
||||
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const readFile = e.target.files && e.target.files[0];
|
||||
setFileName(readFile && readFile.name);
|
||||
const extension = readFile?.name.substring(
|
||||
readFile.name.lastIndexOf('.') + 1
|
||||
);
|
||||
if ((extension === 'yaml' || extension === 'yml') && readFile) {
|
||||
readFile.text().then((response) => {
|
||||
setUploadedYAML(response);
|
||||
const parsedYaml = YAML.parse(response);
|
||||
workflowAction.setWorkflowDetails({
|
||||
...workflowDetails,
|
||||
yaml: YAML.stringify(parsedYaml),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
workflowAction.setWorkflowDetails({
|
||||
...workflowDetails,
|
||||
yaml: '',
|
||||
});
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.headerDiv}>
|
||||
<BackButton isDisabled={false} />
|
||||
<BackButton
|
||||
isDisabled={false}
|
||||
onClick={() => {
|
||||
workflowAction.setWorkflowDetails({
|
||||
isCustomWorkflow: false,
|
||||
});
|
||||
window.history.back();
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h3" className={classes.headerText} gutterBottom>
|
||||
{t('customWorkflow.createWorkflow.create')}
|
||||
</Typography>
|
||||
|
@ -173,12 +238,13 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
styles={{
|
||||
width: '100%',
|
||||
}}
|
||||
data-cy="inputWorkflow"
|
||||
data-cy="inputWorkflowName"
|
||||
validationError={false}
|
||||
handleChange={(e) => {
|
||||
setWorkflowData({
|
||||
workflow_name: e.target.value,
|
||||
workflow_desc: workflowData.workflow_desc,
|
||||
namespace: workflowData.namespace,
|
||||
});
|
||||
}}
|
||||
value={workflowData.workflow_name}
|
||||
|
@ -190,7 +256,7 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
</Typography>
|
||||
<CustomTextField
|
||||
label="Description"
|
||||
data-cy="inputWorkflow"
|
||||
data-cy="inputWorkflowDesc"
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
classes: {
|
||||
|
@ -201,6 +267,7 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
setWorkflowData({
|
||||
workflow_name: workflowData.workflow_name,
|
||||
workflow_desc: e.target.value,
|
||||
namespace: workflowData.namespace,
|
||||
});
|
||||
}}
|
||||
value={workflowData.workflow_desc}
|
||||
|
@ -209,152 +276,285 @@ const CreateWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className={classes.inputDiv}>
|
||||
<Typography variant="h6" className={classes.titleText}>
|
||||
{t('customWorkflow.createWorkflow.firstChaos')}
|
||||
</Typography>
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
color="secondary"
|
||||
focused
|
||||
<Typography variant="h5" className={classes.configureYAML}>
|
||||
<strong>{t('customWorkflow.createWorkflow.configure')}</strong>
|
||||
</Typography>
|
||||
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup
|
||||
aria-label="gender"
|
||||
name="gender1"
|
||||
value={constructYAML}
|
||||
onChange={(event) => {
|
||||
setConstructYAML(event.target.value);
|
||||
if (event.target.value === 'construct') {
|
||||
setUploadedYAML('');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('customWorkflow.createWorkflow.selectHub')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={selectedHub}
|
||||
onChange={(e) => {
|
||||
setSelectedHub(e.target.value as string);
|
||||
if (e.target.value !== 'Public Hub') {
|
||||
findChart(e.target.value as string);
|
||||
}
|
||||
}}
|
||||
label="Cluster Status"
|
||||
MenuProps={MenuProps}
|
||||
className={classes.selectText}
|
||||
>
|
||||
<MenuItem value="Public Hub">
|
||||
{t('customWorkflow.createWorkflow.public')}
|
||||
</MenuItem>
|
||||
{availableHubs.map((hubs) => (
|
||||
<MenuItem key={hubs.HubName} value={hubs.HubName}>
|
||||
{hubs.HubName}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.inputDiv}>
|
||||
<Typography variant="h6" className={classes.titleText}>
|
||||
{t('customWorkflow.createWorkflow.chooseExp')}
|
||||
</Typography>
|
||||
{chartsLoading ? (
|
||||
<div className={classes.chooseExpDiv}>
|
||||
<Loader />
|
||||
<Typography variant="body2">
|
||||
{t('customWorkflow.createWorkflow.loadingExp')}
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
focused
|
||||
component="button"
|
||||
className={classes.formControlExp}
|
||||
>
|
||||
<InputLabel className={classes.selectText1}>
|
||||
{t('customWorkflow.createWorkflow.selectExp')}
|
||||
</InputLabel>
|
||||
<OutlinedInput
|
||||
value={selectedExp}
|
||||
onChange={(e) => {
|
||||
setSelectedExp(e.target.value);
|
||||
setOpen(true);
|
||||
}}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={() => {
|
||||
setOpen(!open);
|
||||
<FormControlLabel
|
||||
value="construct"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
{t('customWorkflow.createWorkflow.construct')}
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{constructYAML === 'construct' ? (
|
||||
<>
|
||||
<div className={classes.inputDiv}>
|
||||
<Typography variant="h6" className={classes.titleText}>
|
||||
{t('customWorkflow.createWorkflow.firstChaos')}
|
||||
</Typography>
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
color="secondary"
|
||||
focused
|
||||
>
|
||||
<InputLabel className={classes.selectText}>
|
||||
{t('customWorkflow.createWorkflow.selectHub')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
value={selectedHub}
|
||||
onChange={(e) => {
|
||||
setSelectedHub(e.target.value as string);
|
||||
findChart(e.target.value as string);
|
||||
}}
|
||||
edge="end"
|
||||
label="Cluster Status"
|
||||
MenuProps={MenuProps}
|
||||
className={classes.selectText}
|
||||
>
|
||||
<ArrowDropDownIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
className={classes.inputExpDiv}
|
||||
labelWidth={150}
|
||||
/>
|
||||
{open ? (
|
||||
<Paper elevation={3}>
|
||||
<MenuList className={classes.expMenu}>
|
||||
{filteredExperiment.length > 0 ? (
|
||||
filteredExperiment.map((exp) => (
|
||||
<MenuItem
|
||||
key={`${exp.ChaosName}/${exp.ExperimentName}`}
|
||||
value={`${exp.ChaosName}/${exp.ExperimentName}`}
|
||||
onClick={() => {
|
||||
setSelectedExp(
|
||||
`${exp.ChaosName}/${exp.ExperimentName}`
|
||||
);
|
||||
setOpen(false);
|
||||
if (selectedHub === 'Public Hub') {
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
experiment_name: `${exp.ChaosName}/${exp.ExperimentName}`,
|
||||
yamlLink: `${workflowDetails.customWorkflow.repoUrl}/raw/${workflowDetails.customWorkflow.repoBranch}/charts/${exp.ChaosName}/${exp.ExperimentName}/engine.yaml`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
experiment_name: `${exp.ChaosName}/${exp.ExperimentName}`,
|
||||
yamlLink: `${selectedHubDetails?.RepoURL}/raw/${selectedHubDetails?.RepoBranch}/charts/${exp.ChaosName}/${exp.ExperimentName}/engine.yaml`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{exp.ExperimentName}
|
||||
{availableHubs.map((hubs) => (
|
||||
<MenuItem key={hubs.HubName} value={hubs.HubName}>
|
||||
{hubs.HubName}
|
||||
</MenuItem>
|
||||
))
|
||||
) : (
|
||||
<MenuItem value="Select an experiment">
|
||||
{t('customWorkflow.createWorkflow.noExp')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Paper>
|
||||
) : null}
|
||||
</FormControl>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
<div className={classes.inputDiv}>
|
||||
<Typography variant="h6" className={classes.titleText}>
|
||||
{t('customWorkflow.createWorkflow.chooseExp')}
|
||||
</Typography>
|
||||
{chartsLoading ? (
|
||||
<div className={classes.chooseExpDiv}>
|
||||
<Loader />
|
||||
<Typography variant="body2">
|
||||
{t('customWorkflow.createWorkflow.loadingExp')}
|
||||
</Typography>
|
||||
</div>
|
||||
) : (
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
focused
|
||||
component="button"
|
||||
className={classes.formControlExp}
|
||||
>
|
||||
<InputLabel className={classes.selectText1}>
|
||||
{t('customWorkflow.createWorkflow.selectExp')}
|
||||
</InputLabel>
|
||||
<OutlinedInput
|
||||
value={selectedExp}
|
||||
onChange={(e) => {
|
||||
setSelectedExp(e.target.value);
|
||||
setOpen(true);
|
||||
}}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setOpen(!open);
|
||||
}}
|
||||
edge="end"
|
||||
>
|
||||
<ArrowDropDownIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
className={classes.inputExpDiv}
|
||||
labelWidth={150}
|
||||
/>
|
||||
{open ? (
|
||||
<ClickAwayListener onClickAway={() => setOpen(!open)}>
|
||||
<Paper elevation={3}>
|
||||
<MenuList className={classes.expMenu}>
|
||||
{filteredExperiment.length > 0 ? (
|
||||
filteredExperiment.map((exp) => (
|
||||
<MenuItem
|
||||
key={`${exp.ChaosName}/${exp.ExperimentName}`}
|
||||
value={`${exp.ChaosName}/${exp.ExperimentName}`}
|
||||
onClick={() => {
|
||||
setSelectedExp(
|
||||
`${exp.ChaosName}/${exp.ExperimentName}`
|
||||
);
|
||||
setOpen(false);
|
||||
if (selectedHub === 'Public Hub') {
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
experiment_name: `${exp.ChaosName}/${exp.ExperimentName}`,
|
||||
yamlLink: `${workflowDetails.customWorkflow.repoUrl}/raw/${workflowDetails.customWorkflow.repoBranch}/charts/${exp.ChaosName}/${exp.ExperimentName}/engine.yaml`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
experiment_name: `${exp.ChaosName}/${exp.ExperimentName}`,
|
||||
yamlLink: `${selectedHubDetails?.RepoURL}/raw/${selectedHubDetails?.RepoBranch}/charts/${exp.ChaosName}/${exp.ExperimentName}/engine.yaml`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{exp.ExperimentName}
|
||||
</MenuItem>
|
||||
))
|
||||
) : (
|
||||
<MenuItem value="Select an experiment">
|
||||
{t('customWorkflow.createWorkflow.noExp')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Paper>
|
||||
</ClickAwayListener>
|
||||
) : null}
|
||||
</FormControl>
|
||||
)}
|
||||
</div>
|
||||
<div className={classes.inputDiv}>
|
||||
<Typography variant="h6" className={classes.titleText}>
|
||||
{t('customWorkflow.createWorkflow.chooseNamespace')}
|
||||
</Typography>
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
focused
|
||||
component="button"
|
||||
className={classes.formControlExp}
|
||||
>
|
||||
<InputLabel className={classes.selectText1}>
|
||||
{t('customWorkflow.createWorkflow.chooseNamespace')}
|
||||
</InputLabel>
|
||||
|
||||
<OutlinedInput
|
||||
value={workflowData.namespace}
|
||||
onChange={(e) => {
|
||||
setWorkflowData({
|
||||
workflow_name: workflowData.workflow_name,
|
||||
workflow_desc: workflowData.workflow_desc,
|
||||
namespace: e.target.value,
|
||||
});
|
||||
}}
|
||||
className={classes.inputExpDiv}
|
||||
labelWidth={130}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<FormControlLabel
|
||||
value="upload"
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Typography className={classes.radioText}>
|
||||
{t('customWorkflow.createWorkflow.upload')}{' '}
|
||||
<strong>{t('customWorkflow.createWorkflow.yaml')}</strong>
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
{constructYAML === 'upload' ? (
|
||||
<Paper
|
||||
elevation={3}
|
||||
component="div"
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
onDrop={(e) => {
|
||||
e.preventDefault();
|
||||
handleDrag(e);
|
||||
}}
|
||||
className={classes.uploadYAMLDiv}
|
||||
>
|
||||
{uploadedYAML === '' ? (
|
||||
<div className={classes.uploadYAMLText}>
|
||||
<img src="/icons/upload-yaml.svg" alt="upload yaml" />
|
||||
<Typography variant="h5">
|
||||
{t('customWorkflow.createWorkflow.drag')}
|
||||
</Typography>
|
||||
<Typography>or</Typography>
|
||||
<input
|
||||
accept=".yaml"
|
||||
style={{ display: 'none' }}
|
||||
id="contained-button-file"
|
||||
type="file"
|
||||
onChange={(e) => {
|
||||
handleFileUpload(e);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="contained-button-file">
|
||||
<Button
|
||||
variant="outlined"
|
||||
className={classes.uploadBtn}
|
||||
component="span"
|
||||
>
|
||||
{t('customWorkflow.createWorkflow.uploadFile')}
|
||||
</Button>
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<div className={classes.uploadSuccessDiv}>
|
||||
<img
|
||||
src="/icons/upload-success.svg"
|
||||
alt="checkmark"
|
||||
className={classes.uploadSuccessImg}
|
||||
/>
|
||||
<Typography className={classes.uploadSuccessText}>
|
||||
{t('customWorkflow.createWorkflow.uploadSuccess')}{' '}
|
||||
{fileName}
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
</Paper>
|
||||
) : null}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.nextButtonDiv}>
|
||||
<ButtonFilled
|
||||
handleClick={() => {
|
||||
if (constructYAML === 'upload' && uploadedYAML !== '') {
|
||||
history.push('/create-workflow');
|
||||
template.selectTemplate({ isDisable: false });
|
||||
}
|
||||
workflowAction.setWorkflowDetails({
|
||||
name: workflowData.workflow_name,
|
||||
description: workflowData.workflow_desc,
|
||||
namespace: workflowData.namespace,
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflow,
|
||||
hubName: selectedHub,
|
||||
repoUrl: selectedHubDetails?.RepoURL,
|
||||
repoBranch: selectedHubDetails?.RepoBranch,
|
||||
yaml: '',
|
||||
index: -1,
|
||||
},
|
||||
});
|
||||
gotoStep(1);
|
||||
getExperimentYaml();
|
||||
}}
|
||||
isPrimary
|
||||
isDisabled={
|
||||
selectedExp === 'Select an experiment' ||
|
||||
filteredExperiment.length !== 1
|
||||
constructYAML === 'construct' &&
|
||||
(selectedExp === 'Select an experiment' ||
|
||||
filteredExperiment.length !== 1 ||
|
||||
workflowData.namespace.trim() === '')
|
||||
? true
|
||||
: !!(constructYAML === 'upload' && uploadedYAML === '')
|
||||
}
|
||||
>
|
||||
<div>
|
||||
|
|
|
@ -65,15 +65,72 @@ const useStyles = makeStyles((theme) => ({
|
|||
height: '2.5rem',
|
||||
minWidth: '9rem',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
outline: 'none',
|
||||
},
|
||||
inputExpDiv: {
|
||||
height: '2.5rem',
|
||||
maxWidth: '15.625rem',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
},
|
||||
expMenu: {
|
||||
minWidth: '15.625rem',
|
||||
maxHeight: '9.375rem',
|
||||
overflow: 'auto',
|
||||
zIndex: 2,
|
||||
backgroundColor: theme.palette.common.white,
|
||||
},
|
||||
configureYAML: {
|
||||
marginTop: theme.spacing(2.5),
|
||||
marginBottom: theme.spacing(2.5),
|
||||
},
|
||||
radioText: {
|
||||
fontSize: '1rem',
|
||||
},
|
||||
uploadYAMLDiv: {
|
||||
width: '39.375rem',
|
||||
height: '7.5rem',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
border: `1px dashed ${theme.palette.common.black}`,
|
||||
margin: 'auto',
|
||||
marginTop: theme.spacing(5),
|
||||
borderRadius: theme.spacing(1.25),
|
||||
},
|
||||
uploadYAMLText: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
width: '31.25rem',
|
||||
margin: 'auto',
|
||||
paddingTop: theme.spacing(3.125),
|
||||
},
|
||||
uploadBtn: {
|
||||
textTransform: 'none',
|
||||
width: '8.5rem',
|
||||
fontSize: '0.9rem',
|
||||
height: '2.8125rem',
|
||||
border: `2px solid ${theme.palette.secondary.main}`,
|
||||
borderRadius: theme.spacing(0.5),
|
||||
},
|
||||
uploadSuccessDiv: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
maxWidth: '31.25rem',
|
||||
margin: '0 auto',
|
||||
paddingTop: theme.spacing(4.375),
|
||||
},
|
||||
uploadSuccessImg: {
|
||||
width: '3.125rem',
|
||||
height: '3.125rem',
|
||||
verticalAlign: 'middle',
|
||||
paddingBottom: theme.spacing(1),
|
||||
},
|
||||
uploadSuccessText: {
|
||||
display: 'inline-block',
|
||||
fontSize: '1rem',
|
||||
marginBottom: theme.spacing(1.25),
|
||||
marginLeft: theme.spacing(2.5),
|
||||
},
|
||||
}));
|
||||
export default useStyles;
|
||||
|
|
|
@ -3,7 +3,6 @@ import React, { useState } from 'react';
|
|||
import { useSelector } from 'react-redux';
|
||||
import YAML from 'yaml';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import BackButton from '../BackButton';
|
||||
import ButtonFilled from '../../../../components/Button/ButtonFilled';
|
||||
import ButtonOutline from '../../../../components/Button/ButtonOutline';
|
||||
import { customWorkflow } from '../../../../models/redux/workflow';
|
||||
|
@ -56,6 +55,7 @@ const ScheduleCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
// Edit experiment operation
|
||||
const editExperiment = (index: number) => {
|
||||
workflowAction.setWorkflowDetails({
|
||||
...workflowDetails,
|
||||
customWorkflow: {
|
||||
...workflowDetails.customWorkflows[index],
|
||||
index,
|
||||
|
@ -94,7 +94,7 @@ const ScheduleCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
parameters: [
|
||||
{
|
||||
name: 'adminModeNamespace',
|
||||
value: 'litmus',
|
||||
value: `${workflowDetails.namespace}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -217,7 +217,6 @@ const ScheduleCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.headerDiv}>
|
||||
<BackButton isDisabled={false} gotoStep={() => gotoStep(1)} />
|
||||
<Typography variant="h3" className={classes.headerText} gutterBottom>
|
||||
{t('customWorkflow.scheduleWorkflow.scheduleHeader')}
|
||||
</Typography>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import YAML from 'yaml';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLazyQuery } from '@apollo/client';
|
||||
import BackButton from '../BackButton';
|
||||
import ButtonFilled from '../../../../components/Button/ButtonFilled';
|
||||
import InputField from '../../../../components/InputField';
|
||||
|
@ -11,6 +12,7 @@ import { RootState } from '../../../../redux/reducers';
|
|||
import useActions from '../../../../redux/actions';
|
||||
import * as WorkflowActions from '../../../../redux/actions/workflow';
|
||||
import Loader from '../../../../components/Loader';
|
||||
import { GET_ENGINE_YAML } from '../../../../graphql/quries';
|
||||
|
||||
interface EnvValues {
|
||||
name: string;
|
||||
|
@ -32,17 +34,37 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
const [overrideEnvs, setOverrideEnvs] = useState<EnvValues[]>([
|
||||
{ name: '', value: '' },
|
||||
]);
|
||||
const { customWorkflow, customWorkflows } = useSelector(
|
||||
(state: RootState) => state.workflowData
|
||||
);
|
||||
const [appInfo, setAppInfo] = useState<AppInfo>({
|
||||
appns: 'kube-system',
|
||||
applabel: 'k8s-app=kube-proxy',
|
||||
appkind: 'daemonset',
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const workflowDetails = useSelector((state: RootState) => state.workflowData);
|
||||
const workflowAction = useActions(WorkflowActions);
|
||||
const [expDesc, setExpDesc] = useState('');
|
||||
const [loadingEnv, setLoadingEnv] = useState(true);
|
||||
|
||||
const [env, setEnv] = useState<EnvValues[]>([]);
|
||||
const [yaml, setYaml] = useState<string>('');
|
||||
const [loadingEnv, setLoadingEnv] = useState(true);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const userData = useSelector((state: RootState) => state.userData);
|
||||
const [getEngineYaml] = useLazyQuery(GET_ENGINE_YAML, {
|
||||
onCompleted: (data) => {
|
||||
const parsedYaml = YAML.parse(data.getYAMLData);
|
||||
setEnv([...parsedYaml.spec.experiments[0].spec.components.env]);
|
||||
setAppInfo({
|
||||
appns: parsedYaml.spec.appinfo.appns,
|
||||
applabel: parsedYaml.spec.appinfo.applabel,
|
||||
appkind: parsedYaml.spec.appinfo.appkind,
|
||||
});
|
||||
setYaml(YAML.stringify(parsedYaml));
|
||||
setLoadingEnv(false);
|
||||
},
|
||||
});
|
||||
|
||||
const workflowAction = useActions(WorkflowActions);
|
||||
|
||||
const changeKey = (
|
||||
index: number,
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
|
@ -60,7 +82,6 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
const AddEnvPair = () => {
|
||||
setOverrideEnvs([...overrideEnvs, { name: '', value: '' }]);
|
||||
};
|
||||
const [env, setEnv] = useState<EnvValues[]>([]);
|
||||
|
||||
const changeOriginalEnvValue = (
|
||||
index: number,
|
||||
|
@ -69,87 +90,54 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
env[index].value = event.target.value;
|
||||
setEnv([...env]);
|
||||
};
|
||||
// Function to fetch workflow description
|
||||
const getWorkflowDesc = () => {
|
||||
fetch(
|
||||
`${workflowDetails.customWorkflow.repoUrl}/raw/${workflowDetails.customWorkflow.repoBranch}/charts/${workflowDetails.customWorkflow.experiment_name}/experiment.yaml`
|
||||
)
|
||||
.then((data) => {
|
||||
data.text().then((yamlText) => {
|
||||
const parsedYaml = YAML.parse(yamlText);
|
||||
setExpDesc(parsedYaml.description.message);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error', error);
|
||||
});
|
||||
if (expDesc) {
|
||||
return expDesc;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// UseEffect to fetch the env variables
|
||||
useEffect(() => {
|
||||
if (workflowDetails.customWorkflow.yaml === '') {
|
||||
fetch(workflowDetails.customWorkflow.yamlLink as string)
|
||||
.then((data) => {
|
||||
data.text().then((yamlText) => {
|
||||
const parsedYaml = YAML.parse(yamlText);
|
||||
parsedYaml.metadata.name =
|
||||
workflowDetails.customWorkflow.experiment_name;
|
||||
setEnv([...parsedYaml.spec.experiments[0].spec.components.env]);
|
||||
setAppInfo({
|
||||
appns: parsedYaml.spec.appinfo.appns,
|
||||
applabel: parsedYaml.spec.appinfo.applabel,
|
||||
appkind: parsedYaml.spec.appinfo.appkind,
|
||||
});
|
||||
setYaml(YAML.stringify(parsedYaml));
|
||||
setLoadingEnv(false);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error', error);
|
||||
});
|
||||
if (customWorkflow.yaml === '') {
|
||||
getEngineYaml({
|
||||
variables: {
|
||||
experimentInput: {
|
||||
ProjectID: userData.selectedProjectID,
|
||||
HubName: customWorkflow.hubName,
|
||||
ChartName: customWorkflow.experiment_name.split('/')[0],
|
||||
ExperimentName: customWorkflow.experiment_name.split('/')[1],
|
||||
FileType: 'engine',
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const parsedYaml = YAML.parse(
|
||||
workflowDetails.customWorkflow.yaml as string
|
||||
);
|
||||
const parsedYaml = YAML.parse(customWorkflow.yaml as string);
|
||||
setAppInfo({
|
||||
appns: parsedYaml.spec.appinfo.appns,
|
||||
applabel: parsedYaml.spec.appinfo.applabel,
|
||||
appkind: parsedYaml.spec.appinfo.appkind,
|
||||
});
|
||||
setEnv([...parsedYaml.spec.experiments[0].spec.components.env]);
|
||||
setYaml(workflowDetails.customWorkflow.yaml as string);
|
||||
setYaml(customWorkflow.yaml as string);
|
||||
setLoadingEnv(false);
|
||||
}
|
||||
}, []);
|
||||
// Function to generate sequence of experiemnt
|
||||
const experimentSequence = () => {
|
||||
const elemPos = workflowDetails.customWorkflows
|
||||
const elemPos = customWorkflows
|
||||
.map((exp) => {
|
||||
return exp.experiment_name;
|
||||
})
|
||||
.indexOf(workflowDetails.customWorkflow.experiment_name);
|
||||
.indexOf(customWorkflow.experiment_name);
|
||||
if (
|
||||
(workflowDetails.customWorkflow.index === -1 &&
|
||||
workflowDetails.customWorkflows.length === 0) ||
|
||||
(customWorkflow.index === -1 && customWorkflows.length === 0) ||
|
||||
elemPos === 0
|
||||
) {
|
||||
return 'This is your first experiment';
|
||||
return t('customWorkflow.tuneExperiment.sequenceFirstExp');
|
||||
}
|
||||
if (workflowDetails.customWorkflow.index === -1) {
|
||||
return `This experiment will execute after
|
||||
${
|
||||
workflowDetails.customWorkflows[
|
||||
workflowDetails.customWorkflows.length - 1
|
||||
].experiment_name
|
||||
}`;
|
||||
if (customWorkflow.index === -1) {
|
||||
return `${t('customWorkflow.tuneExperiment.sequenceNotFirstExp')} ${
|
||||
customWorkflows[customWorkflows.length - 1].experiment_name
|
||||
}`;
|
||||
}
|
||||
return `This experiment will execute after
|
||||
${
|
||||
workflowDetails.customWorkflows[elemPos - 1].experiment_name
|
||||
}`;
|
||||
return `${t('customWorkflow.tuneExperiment.sequenceNotFirstExp')} ${
|
||||
customWorkflows[elemPos - 1].experiment_name
|
||||
}`;
|
||||
};
|
||||
// Function to handle the change in env variables
|
||||
const handleEnvModification = () => {
|
||||
|
@ -176,33 +164,30 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
parsedYaml.spec.appinfo.appns = appInfo.appns;
|
||||
parsedYaml.spec.appinfo.applabel = appInfo.applabel;
|
||||
parsedYaml.spec.appinfo.appkind = appInfo.appkind;
|
||||
parsedYaml.metadata.name = workflowDetails.customWorkflow.experiment_name?.split(
|
||||
'/'
|
||||
)[1];
|
||||
parsedYaml.metadata.name = customWorkflow.experiment_name?.split('/')[1];
|
||||
parsedYaml.metadata.namespace =
|
||||
'{{workflow.parameters.adminModeNamespace}}';
|
||||
parsedYaml.spec.chaosServiceAccount = 'litmus-admin';
|
||||
const YamlString = YAML.stringify(parsedYaml);
|
||||
const experimentArray = workflowDetails.customWorkflows;
|
||||
const experimentArray = customWorkflows;
|
||||
|
||||
if (workflowDetails.customWorkflow.index === -1) {
|
||||
if (customWorkflow.index === -1) {
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflows: [
|
||||
...workflowDetails.customWorkflows,
|
||||
...customWorkflows,
|
||||
{
|
||||
experiment_name: workflowDetails.customWorkflow.experiment_name,
|
||||
hubName: workflowDetails.customWorkflow.hubName,
|
||||
repoUrl: workflowDetails.customWorkflow.repoUrl,
|
||||
repoBranch: workflowDetails.customWorkflow.repoBranch,
|
||||
yamlLink: workflowDetails.customWorkflow.yamlLink,
|
||||
experiment_name: customWorkflow.experiment_name,
|
||||
hubName: customWorkflow.hubName,
|
||||
repoUrl: customWorkflow.repoUrl,
|
||||
repoBranch: customWorkflow.repoBranch,
|
||||
yamlLink: customWorkflow.yamlLink,
|
||||
yaml: YamlString,
|
||||
description: customWorkflow.description,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
experimentArray[
|
||||
workflowDetails.customWorkflow.index as number
|
||||
].yaml = YamlString;
|
||||
experimentArray[customWorkflow.index as number].yaml = YamlString;
|
||||
workflowAction.setWorkflowDetails({
|
||||
customWorkflows: [...experimentArray],
|
||||
});
|
||||
|
@ -212,7 +197,9 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.headerDiv}>
|
||||
<BackButton isDisabled={false} gotoStep={() => gotoStep(0)} />
|
||||
{customWorkflow.index === -1 ? (
|
||||
<BackButton isDisabled={false} onClick={() => gotoStep(0)} />
|
||||
) : null}
|
||||
<Typography variant="h3" className={classes.headerText} gutterBottom>
|
||||
{t('customWorkflow.tuneExperiment.headerText')}
|
||||
</Typography>
|
||||
|
@ -230,7 +217,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
{t('customWorkflow.tuneExperiment.expName')}:
|
||||
</Typography>
|
||||
<Typography className={classes.mainDetail}>
|
||||
{workflowDetails.customWorkflow.experiment_name}
|
||||
{customWorkflow.experiment_name}
|
||||
</Typography>
|
||||
</div>
|
||||
<div className={classes.inputDiv}>
|
||||
|
@ -238,7 +225,7 @@ const TuneCustomWorkflow: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
{t('customWorkflow.tuneExperiment.description')}:
|
||||
</Typography>
|
||||
<Typography className={classes.mainDetail}>
|
||||
{getWorkflowDesc()}
|
||||
{customWorkflow.description}
|
||||
</Typography>
|
||||
</div>
|
||||
<hr className={classes.horizontalLineHeader} />
|
||||
|
|
|
@ -24,7 +24,7 @@ const ExperimentEditor: React.FC<ExperimentEditorProps> = ({ gotoStep }) => {
|
|||
return (
|
||||
<div className={classes.root}>
|
||||
<div className={classes.tuneDiv}>
|
||||
<BackButton isDisabled={false} gotoStep={() => gotoStep(2)} />
|
||||
<BackButton isDisabled={false} onClick={() => gotoStep(2)} />
|
||||
<Typography className={classes.headerText} variant="h4">
|
||||
{customWorkflow.experiment_name}
|
||||
</Typography>
|
||||
|
|
|
@ -43,6 +43,7 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
description,
|
||||
weights,
|
||||
cronSyntax,
|
||||
clustername,
|
||||
} = workflowData;
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
@ -92,7 +93,7 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
annotations: editorValidations.annotations,
|
||||
};
|
||||
if (stateObject.annotations.length > 0) {
|
||||
setYamlStatus('Error in CRD Yaml.');
|
||||
setYamlStatus('Error in CRHeloD Yaml.');
|
||||
} else {
|
||||
setYamlStatus('Your code is fine. You can move on !');
|
||||
}
|
||||
|
@ -141,6 +142,18 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classes.summaryDiv}>
|
||||
<div className={classes.innerSumDiv}>
|
||||
<Typography className={classes.col1}>
|
||||
{t('createWorkflow.verifyCommit.summary.clustername')}:
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography className={classes.clusterName}>
|
||||
{clustername}
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<div className={classes.summaryDiv}>
|
||||
<div className={classes.innerSumDiv}>
|
||||
<Typography className={classes.col1}>
|
||||
|
@ -254,22 +267,6 @@ const VerifyCommit: React.FC<VerifyCommitProps> = ({ gotoStep }) => {
|
|||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
{/*
|
||||
<div>
|
||||
<Typography className={classes.config}>
|
||||
The configuration details of this workflow will be committed to:{' '}
|
||||
<span>
|
||||
<Link
|
||||
href="https://github.com/abcorn-org/reputeops/sandbox-repute.yaml"
|
||||
onClick={preventDefault}
|
||||
className={classes.link}
|
||||
>
|
||||
https://github.com/abcorn-org/reputeops/sandbox-repute.yaml
|
||||
</Link>
|
||||
</span>
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
|
||||
<Unimodal
|
||||
|
|
|
@ -72,6 +72,11 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
clusterName: {
|
||||
fontSize: '1rem',
|
||||
marginLeft: theme.spacing(5),
|
||||
paddingTop: theme.spacing(0.5),
|
||||
},
|
||||
editButton1: {
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
|
|
|
@ -46,20 +46,25 @@ const WorkflowCluster: React.FC<WorkflowClusterProps> = ({ gotoStep }) => {
|
|||
const selectedProjectID = useSelector(
|
||||
(state: RootState) => state.userData.selectedProjectID
|
||||
);
|
||||
|
||||
const [clusterData, setclusterData] = useState<Cluster[]>([]);
|
||||
const [name, setName] = React.useState('');
|
||||
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
|
||||
const str: string = event.target.value as string;
|
||||
let clusterName;
|
||||
clusterData.forEach((cluster) => {
|
||||
if (str === cluster.cluster_id) {
|
||||
clusterName = cluster.cluster_name;
|
||||
}
|
||||
});
|
||||
workflow.setWorkflowDetails({
|
||||
clusterid: str,
|
||||
project_id: selectedProjectID,
|
||||
clustername: clusterName,
|
||||
});
|
||||
setName(str);
|
||||
setTarget(false);
|
||||
};
|
||||
|
||||
const [clusterData, setclusterData] = useState<Cluster[]>([]);
|
||||
|
||||
const [getCluster] = useLazyQuery(GET_CLUSTER, {
|
||||
onCompleted: (data) => {
|
||||
const clusters: Cluster[] = [];
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { Avatar } from '@material-ui/core';
|
||||
import CheckCircleSharpIcon from '@material-ui/icons/CheckCircleSharp';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import CancelSharpIcon from '@material-ui/icons/CancelSharp';
|
||||
import CheckCircleSharpIcon from '@material-ui/icons/CheckCircleSharp';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useStyles from './styles';
|
||||
|
||||
|
@ -48,7 +48,7 @@ const PassedVsFailed: React.FC<PassedVsFailedProps> = ({ passed, failed }) => {
|
|||
<Box width={`${passedValue}%`} className={classes.passedBox}>
|
||||
{/* Render an empty div if props is not
|
||||
passed */}
|
||||
{passedValue === 0 ? (
|
||||
{passedValue < 11 ? (
|
||||
<div />
|
||||
) : (
|
||||
<Avatar className={classes.passedIcon}>
|
||||
|
@ -59,7 +59,7 @@ const PassedVsFailed: React.FC<PassedVsFailedProps> = ({ passed, failed }) => {
|
|||
<Box width={`${failedValue}%`} className={classes.failedBox}>
|
||||
{/* Render an empty div if props is not
|
||||
passed */}
|
||||
{failedValue === 0 ? (
|
||||
{failedValue < 11 ? (
|
||||
<div />
|
||||
) : (
|
||||
<Avatar className={classes.failedIcon}>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
/* eslint-disable max-len */
|
||||
import React, { useEffect } from 'react';
|
||||
import Plotly from 'plotly.js';
|
||||
import createPlotlyComponent from 'react-plotly.js/factory';
|
||||
import {
|
||||
FormControl,
|
||||
IconButton,
|
||||
|
@ -10,14 +7,17 @@ import {
|
|||
Select,
|
||||
Tooltip,
|
||||
} from '@material-ui/core';
|
||||
import AssessmentOutlinedIcon from '@material-ui/icons/AssessmentOutlined';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import useStyles from './style';
|
||||
import Score from './Score';
|
||||
import { history } from '../../../redux/configureStore';
|
||||
import AssessmentOutlinedIcon from '@material-ui/icons/AssessmentOutlined';
|
||||
import Plotly from 'plotly.js';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import createPlotlyComponent from 'react-plotly.js/factory';
|
||||
import useActions from '../../../redux/actions';
|
||||
import * as TabActions from '../../../redux/actions/tabs';
|
||||
import { history } from '../../../redux/configureStore';
|
||||
import Score from './Score';
|
||||
import useStyles from './style';
|
||||
|
||||
const Plot = createPlotlyComponent(Plotly);
|
||||
|
||||
|
@ -80,10 +80,10 @@ const ResilienceScoreComparisonPlot: React.FC<ResilienceScoreComparisonPlotProps
|
|||
dataY = yData.Monthly;
|
||||
}
|
||||
const colors = [
|
||||
palette.error.dark,
|
||||
palette.primary.dark,
|
||||
palette.warning.main,
|
||||
palette.secondary.main,
|
||||
palette.warning.main,
|
||||
palette.primary.dark,
|
||||
palette.error.dark,
|
||||
];
|
||||
const lineSize = [3, 3, 3, 3];
|
||||
const data = [];
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
/* eslint-disable no-loop-func */
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable no-console */
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import moment from 'moment';
|
||||
import * as _ from 'lodash';
|
||||
import { Paper, Typography } from '@material-ui/core';
|
||||
import * as _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useStyles from './style';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Loader from '../../../components/Loader';
|
||||
import QuickActionCard from '../../../components/QuickActionCard';
|
||||
import { WORKFLOW_LIST_DETAILS } from '../../../graphql';
|
||||
import { ExecutionData } from '../../../models/graphql/workflowData';
|
||||
import { RootState } from '../../../redux/reducers';
|
||||
import PassedVsFailed from '../PassedVsFailed';
|
||||
import TotalWorkflows from '../TotalWorkflows';
|
||||
import AverageResilienceScore from '../AverageResilienceScore';
|
||||
import QuickActionCard from '../../../components/QuickActionCard';
|
||||
import { Message } from '../../../models/header';
|
||||
import ResilienceScoreComparisonPlot from '../ResilienceScoreComparisonPlot';
|
||||
import RecentActivity from '../RecentActivity';
|
||||
import Loader from '../../../components/Loader';
|
||||
import {
|
||||
WeightageMap,
|
||||
WorkflowList,
|
||||
WorkflowListDataVars,
|
||||
} from '../../../models/graphql/workflowListData';
|
||||
import { Message } from '../../../models/header';
|
||||
import { RootState } from '../../../redux/reducers';
|
||||
import AverageResilienceScore from '../AverageResilienceScore';
|
||||
import PassedVsFailed from '../PassedVsFailed';
|
||||
import RecentActivity from '../RecentActivity';
|
||||
import ResilienceScoreComparisonPlot from '../ResilienceScoreComparisonPlot';
|
||||
import TotalWorkflows from '../TotalWorkflows';
|
||||
import useStyles from './style';
|
||||
|
||||
interface DataPresentCallBackType {
|
||||
(dataPresent: boolean): void;
|
||||
|
@ -93,7 +93,6 @@ const ReturningHome: React.FC<ReturningHomeProps> = ({
|
|||
WORKFLOW_LIST_DETAILS,
|
||||
{
|
||||
variables: { projectID: userData.selectedProjectID, workflowIDs: [] },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ const MyHub = () => {
|
|||
const userData = useSelector((state: RootState) => state.userData);
|
||||
|
||||
// Get all MyHubs with status
|
||||
const { data: userDetails } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.username },
|
||||
const { data: hubDetails } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.selectedProjectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
|
@ -43,7 +43,7 @@ const MyHub = () => {
|
|||
const paramData: URLParams = useParams();
|
||||
|
||||
// Filter the selected MyHub
|
||||
const UserHub = userDetails?.getHubStatus.filter((myHub) => {
|
||||
const UserHub = hubDetails?.getHubStatus.filter((myHub) => {
|
||||
return paramData.hubname === myHub.HubName;
|
||||
})[0];
|
||||
|
||||
|
@ -55,12 +55,8 @@ const MyHub = () => {
|
|||
// Query to get charts of selected MyHub
|
||||
const { data, loading } = useQuery<Charts>(GET_CHARTS_DATA, {
|
||||
variables: {
|
||||
data: {
|
||||
UserName: userData.username,
|
||||
RepoURL: UserHub?.RepoURL,
|
||||
RepoBranch: UserHub?.RepoBranch,
|
||||
HubName: paramData.hubname,
|
||||
},
|
||||
HubName: paramData.hubname,
|
||||
projectID: userData.selectedProjectID,
|
||||
},
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
|
|
@ -60,7 +60,7 @@ const MyHub = () => {
|
|||
RepoURL: gitHub.GitURL,
|
||||
RepoBranch: gitHub.GitBranch,
|
||||
},
|
||||
Username: userData.username,
|
||||
projectID: userData.selectedProjectID,
|
||||
},
|
||||
});
|
||||
setCloningRepo(true);
|
||||
|
|
|
@ -28,8 +28,8 @@ const MyHub = () => {
|
|||
const userData = useSelector((state: RootState) => state.userData);
|
||||
|
||||
// Get all MyHubs with status
|
||||
const { data: userDetails } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.username },
|
||||
const { data: hubDetails } = useQuery<HubStatus>(GET_HUB_STATUS, {
|
||||
variables: { data: userData.selectedProjectID },
|
||||
fetchPolicy: 'cache-and-network',
|
||||
});
|
||||
|
||||
|
@ -37,7 +37,7 @@ const MyHub = () => {
|
|||
const paramData: URLParams = useParams();
|
||||
|
||||
// Filter the selected MyHub
|
||||
const UserHub = userDetails?.getHubStatus.filter((myHub) => {
|
||||
const UserHub = hubDetails?.getHubStatus.filter((myHub) => {
|
||||
return paramData.hubname === myHub.HubName;
|
||||
})[0];
|
||||
|
||||
|
@ -46,9 +46,7 @@ const MyHub = () => {
|
|||
variables: {
|
||||
data: {
|
||||
HubName: paramData.hubname,
|
||||
UserName: userData.username,
|
||||
RepoURL: UserHub?.RepoURL,
|
||||
RepoBranch: UserHub?.RepoBranch,
|
||||
ProjectID: userData.selectedProjectID,
|
||||
ChartName: paramData.chart,
|
||||
ExperimentName: paramData.experiment,
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/cors v1.6.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/tidwall/gjson v1.6.0
|
||||
github.com/tidwall/sjson v1.1.1
|
||||
github.com/vektah/gqlparser/v2 v2.0.1
|
||||
go.mongodb.org/mongo-driver v1.3.5
|
||||
|
@ -25,7 +26,6 @@ require (
|
|||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.18.6
|
||||
k8s.io/apimachinery v0.18.6
|
||||
k8s.io/client-go v0.18.6
|
||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 // indirect
|
||||
|
|
|
@ -50,6 +50,7 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr
|
|||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680 h1:ZktWZesgun21uEDrwW7iEV1zPCGQldM2atlJZ3TdvVM=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
|
@ -162,8 +163,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/litmuschaos/litmus v0.0.0-20201030044325-64ebcdc8ffcb h1:3DGMJMWqy8Yeyb7jkfTFE7MHMJ+2hDj9ov8vO8GtLjg=
|
||||
github.com/litmuschaos/litmus v0.0.0-20201103140214-d61f522c8b8f h1:G7nRXRpmMmkkhIvEysz0i4UTZZwflRdoyWlJaGTmEuQ=
|
||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||
|
@ -189,8 +188,10 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
|||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
|
@ -286,7 +287,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -316,7 +316,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -357,11 +356,11 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
|
|||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
|
@ -371,6 +370,7 @@ gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOA
|
|||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
|
@ -394,6 +394,7 @@ k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
|||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
|
||||
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
|
||||
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE=
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,9 +9,9 @@ import (
|
|||
)
|
||||
|
||||
type ActionPayload struct {
|
||||
RequestType *string `json:"request_type"`
|
||||
K8sManifest *string `json:"k8s_manifest"`
|
||||
Namespace *string `json:"namespace"`
|
||||
RequestType string `json:"request_type"`
|
||||
K8sManifest string `json:"k8s_manifest"`
|
||||
Namespace string `json:"namespace"`
|
||||
ExternalData *string `json:"external_data"`
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ type Annotation struct {
|
|||
}
|
||||
|
||||
type ChaosWorkFlowInput struct {
|
||||
WorkflowID *string `json:"workflow_id"`
|
||||
WorkflowManifest string `json:"workflow_manifest"`
|
||||
CronSyntax string `json:"cronSyntax"`
|
||||
WorkflowName string `json:"workflow_name"`
|
||||
|
@ -56,9 +57,9 @@ type Charts struct {
|
|||
Charts []*Chart `json:"Charts"`
|
||||
}
|
||||
|
||||
type ChartsInput struct {
|
||||
type CloningInput struct {
|
||||
HubName string `json:"HubName"`
|
||||
UserName string `json:"UserName"`
|
||||
ProjectID string `json:"ProjectID"`
|
||||
RepoBranch string `json:"RepoBranch"`
|
||||
RepoURL string `json:"RepoURL"`
|
||||
}
|
||||
|
@ -150,12 +151,11 @@ type CreateUserInput struct {
|
|||
}
|
||||
|
||||
type ExperimentInput struct {
|
||||
UserName string `json:"UserName"`
|
||||
RepoURL string `json:"RepoURL"`
|
||||
RepoBranch string `json:"RepoBranch"`
|
||||
ChartName string `json:"ChartName"`
|
||||
ExperimentName string `json:"ExperimentName"`
|
||||
HubName string `json:"HubName"`
|
||||
ProjectID string `json:"ProjectID"`
|
||||
ChartName string `json:"ChartName"`
|
||||
ExperimentName string `json:"ExperimentName"`
|
||||
HubName string `json:"HubName"`
|
||||
FileType *string `json:"FileType"`
|
||||
}
|
||||
|
||||
type Experiments struct {
|
||||
|
@ -197,11 +197,13 @@ type Metadata struct {
|
|||
}
|
||||
|
||||
type MyHub struct {
|
||||
ID string `json:"id"`
|
||||
RepoURL string `json:"RepoURL"`
|
||||
RepoBranch string `json:"RepoBranch"`
|
||||
IsConfirmed bool `json:"IsConfirmed"`
|
||||
HubName string `json:"HubName"`
|
||||
ID string `json:"id"`
|
||||
RepoURL string `json:"RepoURL"`
|
||||
RepoBranch string `json:"RepoBranch"`
|
||||
ProjectID string `json:"ProjectID"`
|
||||
HubName string `json:"HubName"`
|
||||
CreatedAt string `json:"CreatedAt"`
|
||||
UpdatedAt string `json:"UpdatedAt"`
|
||||
}
|
||||
|
||||
type MyHubStatus struct {
|
||||
|
@ -273,6 +275,7 @@ type ScheduledWorkflows struct {
|
|||
ProjectID string `json:"project_id"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
ClusterType string `json:"cluster_type"`
|
||||
IsRemoved bool `json:"isRemoved"`
|
||||
}
|
||||
|
||||
type Spec struct {
|
||||
|
@ -302,7 +305,6 @@ type User struct {
|
|||
Username string `json:"username"`
|
||||
Email *string `json:"email"`
|
||||
IsEmailVerified *bool `json:"is_email_verified"`
|
||||
MyHub []*MyHub `json:"my_hub"`
|
||||
CompanyName *string `json:"company_name"`
|
||||
Name *string `json:"name"`
|
||||
Projects []*Project `json:"projects"`
|
||||
|
@ -332,6 +334,7 @@ type Workflow struct {
|
|||
ProjectID string `json:"project_id"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
ClusterType string `json:"cluster_type"`
|
||||
IsRemoved bool `json:"isRemoved"`
|
||||
WorkflowRuns []*WorkflowRuns `json:"workflow_runs"`
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@ type MyHub {
|
|||
id: ID!
|
||||
RepoURL: String!
|
||||
RepoBranch: String!
|
||||
IsConfirmed: Boolean!
|
||||
ProjectID: String!
|
||||
HubName: String!
|
||||
CreatedAt: String!
|
||||
UpdatedAt: String!
|
||||
}
|
||||
|
||||
type Charts {
|
||||
|
@ -90,17 +92,16 @@ input CreateMyHub {
|
|||
}
|
||||
|
||||
input ExperimentInput {
|
||||
UserName: String!
|
||||
RepoURL: String!
|
||||
RepoBranch: String!
|
||||
ProjectID: String!
|
||||
ChartName: String!
|
||||
ExperimentName: String!
|
||||
HubName: String!
|
||||
FileType: String
|
||||
}
|
||||
|
||||
input ChartsInput {
|
||||
input CloningInput {
|
||||
HubName: String!
|
||||
UserName: String!
|
||||
ProjectID: String!
|
||||
RepoBranch: String!
|
||||
RepoURL: String!
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ type ClusterEvent {
|
|||
}
|
||||
|
||||
type ActionPayload {
|
||||
request_type: String
|
||||
k8s_manifest: String
|
||||
namespace: String
|
||||
request_type: String!
|
||||
k8s_manifest: String!
|
||||
namespace: String!
|
||||
external_data: String
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,7 @@ type weightages {
|
|||
}
|
||||
|
||||
input ChaosWorkFlowInput {
|
||||
workflow_id: String
|
||||
workflow_manifest: String!
|
||||
cronSyntax: String!
|
||||
workflow_name: String!
|
||||
|
@ -174,6 +175,7 @@ type ScheduledWorkflows {
|
|||
project_id: ID!
|
||||
cluster_id: ID!
|
||||
cluster_type: String!
|
||||
isRemoved: Boolean!
|
||||
}
|
||||
|
||||
type Workflow {
|
||||
|
@ -190,6 +192,7 @@ type Workflow {
|
|||
project_id: ID!
|
||||
cluster_id: ID!
|
||||
cluster_type: String!
|
||||
isRemoved: Boolean!
|
||||
workflow_runs: [WorkflowRuns]
|
||||
}
|
||||
|
||||
|
@ -222,11 +225,13 @@ type Query {
|
|||
|
||||
ListWorkflow(project_id: String!, workflow_ids: [ID]): [Workflow]! @authorized
|
||||
|
||||
getCharts(chartsInput: ChartsInput!): [Chart!]! @authorized
|
||||
getCharts(HubName: String!, projectID: String!): [Chart!]! @authorized
|
||||
|
||||
getHubExperiment(experimentInput: ExperimentInput!): Chart! @authorized
|
||||
|
||||
getHubStatus(username: String!): [MyHubStatus]! @authorized
|
||||
getHubStatus(projectID: String!): [MyHubStatus]! @authorized
|
||||
|
||||
getYAMLData(experimentInput: ExperimentInput!): String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
@ -261,9 +266,14 @@ type Mutation {
|
|||
|
||||
podLog(log: PodLog!): String!
|
||||
|
||||
addMyHub(myhubInput: CreateMyHub!, username: String!): User! @authorized
|
||||
addMyHub(myhubInput: CreateMyHub!, projectID: String!): MyHub! @authorized
|
||||
|
||||
syncHub(syncHubInput: ChartsInput!): [MyHubStatus!]! @authorized
|
||||
syncHub(projectID: String!, HubName: String!): [MyHubStatus!]! @authorized
|
||||
|
||||
updateChaosWorkflow(input: ChaosWorkFlowInput): ChaosWorkFlowResponse!
|
||||
@authorized
|
||||
|
||||
deleteClusterReg(cluster_id: String!): String! @authorized
|
||||
}
|
||||
|
||||
type Subscription {
|
||||
|
|
|
@ -42,7 +42,7 @@ func (r *mutationResolver) UpdateUser(ctx context.Context, user model.UpdateUser
|
|||
}
|
||||
|
||||
func (r *mutationResolver) DeleteChaosWorkflow(ctx context.Context, workflowid string) (bool, error) {
|
||||
return database.DeleteChaosWorkflow(workflowid)
|
||||
return mutations.DeleteWorkflow(workflowid, *store)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) SendInvitation(ctx context.Context, member model.MemberInput) (*model.Member, error) {
|
||||
|
@ -77,12 +77,20 @@ func (r *mutationResolver) PodLog(ctx context.Context, log model.PodLog) (string
|
|||
return mutations.LogsHandler(log, *store)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) AddMyHub(ctx context.Context, myhubInput model.CreateMyHub, username string) (*model.User, error) {
|
||||
return myhub.AddMyHub(ctx, myhubInput, username)
|
||||
func (r *mutationResolver) AddMyHub(ctx context.Context, myhubInput model.CreateMyHub, projectID string) (*model.MyHub, error) {
|
||||
return myhub.AddMyHub(ctx, myhubInput, projectID)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) SyncHub(ctx context.Context, syncHubInput model.ChartsInput) ([]*model.MyHubStatus, error) {
|
||||
return myhub.SyncHub(ctx, syncHubInput)
|
||||
func (r *mutationResolver) SyncHub(ctx context.Context, projectID string, hubName string) ([]*model.MyHubStatus, error) {
|
||||
return myhub.SyncHub(ctx, projectID, hubName)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateChaosWorkflow(ctx context.Context, input *model.ChaosWorkFlowInput) (*model.ChaosWorkFlowResponse, error) {
|
||||
return mutations.UpdateWorkflow(input, *store)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteClusterReg(ctx context.Context, clusterID string) (string, error) {
|
||||
return mutations.DeleteCluster(clusterID, *store)
|
||||
}
|
||||
|
||||
func (r *queryResolver) GetWorkFlowRuns(ctx context.Context, projectID string) ([]*model.WorkflowRun, error) {
|
||||
|
@ -117,16 +125,20 @@ func (r *queryResolver) ListWorkflow(ctx context.Context, projectID string, work
|
|||
}
|
||||
}
|
||||
|
||||
func (r *queryResolver) GetCharts(ctx context.Context, chartsInput model.ChartsInput) ([]*model.Chart, error) {
|
||||
return myhub.GetCharts(ctx, chartsInput)
|
||||
func (r *queryResolver) GetCharts(ctx context.Context, hubName string, projectID string) ([]*model.Chart, error) {
|
||||
return myhub.GetCharts(ctx, hubName, projectID)
|
||||
}
|
||||
|
||||
func (r *queryResolver) GetHubExperiment(ctx context.Context, experimentInput model.ExperimentInput) (*model.Chart, error) {
|
||||
return myhub.GetExperiment(ctx, experimentInput)
|
||||
}
|
||||
|
||||
func (r *queryResolver) GetHubStatus(ctx context.Context, username string) ([]*model.MyHubStatus, error) {
|
||||
return myhub.HubStatus(ctx, username)
|
||||
func (r *queryResolver) GetHubStatus(ctx context.Context, projectID string) ([]*model.MyHubStatus, error) {
|
||||
return myhub.HubStatus(ctx, projectID)
|
||||
}
|
||||
|
||||
func (r *queryResolver) GetYAMLData(ctx context.Context, experimentInput model.ExperimentInput) (string, error) {
|
||||
return myhub.GetYAMLData(ctx, experimentInput)
|
||||
}
|
||||
|
||||
func (r *subscriptionResolver) ClusterEventListener(ctx context.Context, projectID string) (<-chan *model.ClusterEvent, error) {
|
||||
|
|
|
@ -3,7 +3,6 @@ type User {
|
|||
username: String!
|
||||
email: String
|
||||
is_email_verified: Boolean
|
||||
my_hub: [MyHub!]!
|
||||
company_name: String
|
||||
name: String
|
||||
projects: [Project!]!
|
||||
|
|
Binary file not shown.
|
@ -1033,7 +1033,6 @@ metadata:
|
|||
name: argo-chaos
|
||||
namespace: #{AGENT-NAMESPACE}
|
||||
---
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
|
@ -1074,6 +1073,7 @@ rules:
|
|||
verbs:
|
||||
- get
|
||||
- create
|
||||
- delete
|
||||
- apiGroups:
|
||||
- litmuschaos.io
|
||||
resources:
|
||||
|
@ -1097,6 +1097,8 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- delete
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
@ -1105,6 +1107,12 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
|
|
|
@ -401,6 +401,7 @@ rules:
|
|||
verbs:
|
||||
- get
|
||||
- create
|
||||
- delete
|
||||
- apiGroups:
|
||||
- litmuschaos.io
|
||||
resources:
|
||||
|
@ -422,6 +423,8 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- delete
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
@ -430,6 +433,12 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- deployments
|
||||
verbs:
|
||||
- delete
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
|
|
|
@ -47,6 +47,7 @@ type Cluster struct {
|
|||
CreatedAt string `bson:"created_at"`
|
||||
ClusterType string `bson:"cluster_type"`
|
||||
Token string `bson:"token"`
|
||||
IsRemoved bool `bson:"is_removed"`
|
||||
}
|
||||
|
||||
type ChaosWorkFlowInput struct {
|
||||
|
@ -62,6 +63,7 @@ type ChaosWorkFlowInput struct {
|
|||
ProjectID string `bson:"project_id"`
|
||||
ClusterID string `bson:"cluster_id"`
|
||||
WorkflowRuns []*WorkflowRun `bson:"workflow_runs"`
|
||||
IsRemoved bool `bson:"isRemoved"`
|
||||
}
|
||||
|
||||
type WorkflowRun struct {
|
||||
|
@ -79,11 +81,22 @@ type WeightagesInput struct {
|
|||
//init initializes database connection
|
||||
func init() {
|
||||
|
||||
dbServer := os.Getenv("DB_SERVER")
|
||||
if dbServer == "" {
|
||||
log.Fatal("Environment Variable DB_SERVER is not present")
|
||||
var (
|
||||
dbServer = os.Getenv("DB_SERVER")
|
||||
username = os.Getenv("DB_USER")
|
||||
pwd = os.Getenv("DB_PASSWORD")
|
||||
)
|
||||
|
||||
if dbServer == "" || username == "" || pwd == "" {
|
||||
log.Fatal("DB configuration failed")
|
||||
}
|
||||
clientOptions := options.Client().ApplyURI(dbServer)
|
||||
|
||||
credential := options.Credential{
|
||||
Username: username,
|
||||
Password: pwd,
|
||||
}
|
||||
|
||||
clientOptions := options.Client().ApplyURI(dbServer).SetAuth(credential)
|
||||
client, err := mongo.Connect(backgroundContext, clientOptions)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
@ -132,9 +131,9 @@ func GetClusterWithProjectID(project_id string, cluster_type *string) ([]*Cluste
|
|||
|
||||
var query bson.M
|
||||
if cluster_type == nil {
|
||||
query = bson.M{"project_id": project_id}
|
||||
query = bson.M{"project_id": project_id, "is_removed": false}
|
||||
} else {
|
||||
query = bson.M{"project_id": project_id, "cluster_type": cluster_type}
|
||||
query = bson.M{"project_id": project_id, "cluster_type": cluster_type, "is_removed": false}
|
||||
}
|
||||
|
||||
fmt.Print(query)
|
||||
|
@ -164,16 +163,13 @@ func InsertChaosWorkflow(chaosWorkflow ChaosWorkFlowInput) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteChaosWorkflow(workflowid string) (bool, error) {
|
||||
func UpdateChaosWorkflow(query bson.D, update bson.D) error {
|
||||
ctx, _ := context.WithTimeout(backgroundContext, 10*time.Second)
|
||||
res, err := workflowCollection.DeleteOne(ctx, bson.M{"workflow_id": workflowid})
|
||||
|
||||
_, err := workflowCollection.UpdateOne(ctx, query, update)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if res.DeletedCount == 0 {
|
||||
return false, nil
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Successfully delete %v", workflowid)
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package operations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb"
|
||||
dbSchema "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb/schema"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
var myhubCollection *mongo.Collection
|
||||
|
||||
func init() {
|
||||
myhubCollection = mongodb.Database.Collection("myhub")
|
||||
}
|
||||
|
||||
//CreateMyHub ...
|
||||
func CreateMyHub(ctx context.Context, myhub *dbSchema.MyHub) error {
|
||||
_, err := myhubCollection.InsertOne(ctx, myhub)
|
||||
if err != nil {
|
||||
log.Print("Error creating MyHub: ", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//GetMyHubByProjectID ...
|
||||
func GetMyHubByProjectID(ctx context.Context, projectID string) ([]dbSchema.MyHub, error) {
|
||||
query := bson.M{"project_id": projectID}
|
||||
cursor, err := myhubCollection.Find(ctx, query)
|
||||
if err != nil {
|
||||
log.Print("ERROR GETTING USERS : ", err)
|
||||
return []dbSchema.MyHub{}, err
|
||||
}
|
||||
var myhubs []dbSchema.MyHub
|
||||
err = cursor.All(ctx, &myhubs)
|
||||
if err != nil {
|
||||
log.Print("Error deserializing myhubs in myhub object : ", err)
|
||||
return []dbSchema.MyHub{}, err
|
||||
}
|
||||
return myhubs, nil
|
||||
}
|
||||
|
||||
//GetHubs ...
|
||||
func GetHubs(ctx context.Context) ([]dbSchema.MyHub, error) {
|
||||
// ctx, _ := context.WithTimeout(backgroundContext, 10*time.Second)
|
||||
query := bson.D{{}}
|
||||
cursor, err := myhubCollection.Find(ctx, query)
|
||||
if err != nil {
|
||||
log.Print("ERROR GETTING MYHUBS : ", err)
|
||||
return []dbSchema.MyHub{}, err
|
||||
}
|
||||
var MyHubs []dbSchema.MyHub
|
||||
err = cursor.All(ctx, &MyHubs)
|
||||
if err != nil {
|
||||
log.Print("Error deserializing myhubs in the myhub object : ", err)
|
||||
return []dbSchema.MyHub{}, err
|
||||
}
|
||||
return MyHubs, nil
|
||||
}
|
|
@ -89,16 +89,3 @@ func UpdateUser(ctx context.Context, user *dbSchema.User) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddNewMyHub ...
|
||||
func AddNewMyHub(ctx context.Context, username string, myHub *dbSchema.MyHub) error {
|
||||
query := bson.M{"username": username}
|
||||
change := bson.M{"$push": bson.M{"myhub": myHub}}
|
||||
|
||||
_, err := userCollection.UpdateOne(ctx, query, change)
|
||||
if err != nil {
|
||||
log.Print("Error adding hub")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package schema
|
||||
|
||||
import "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
|
||||
|
||||
//MyHub ...
|
||||
type MyHub struct {
|
||||
ID string `bson:"myhub_id"`
|
||||
ProjectID string `bson:"project_id"`
|
||||
RepoURL string `bson:"repo_url"`
|
||||
RepoBranch string `bson:"repo_branch"`
|
||||
HubName string `bson:"hub_name"`
|
||||
CreatedAt string `bson:"created_at"`
|
||||
UpdatedAt string `bson:"updated_at"`
|
||||
}
|
||||
|
||||
//GetOutputMyHub ...
|
||||
func (myhub *MyHub) GetOutputMyHub() *model.MyHub {
|
||||
|
||||
return &model.MyHub{
|
||||
ID: myhub.ID,
|
||||
ProjectID: myhub.ProjectID,
|
||||
RepoURL: myhub.RepoURL,
|
||||
RepoBranch: myhub.RepoBranch,
|
||||
HubName: myhub.HubName,
|
||||
CreatedAt: myhub.CreatedAt,
|
||||
UpdatedAt: myhub.UpdatedAt,
|
||||
}
|
||||
}
|
|
@ -4,18 +4,17 @@ import "github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
|
|||
|
||||
//User ...
|
||||
type User struct {
|
||||
ID string `bson:"_id"`
|
||||
Username string `bson:"username"`
|
||||
Email *string `bson:"email"`
|
||||
IsEmailVerified *bool `bson:"is_email_verified"`
|
||||
MyHub []*MyHub `bson:"myhub"`
|
||||
CompanyName *string `bson:"company_name"`
|
||||
Name *string `bson:"name"`
|
||||
Role *string `bson:"role"`
|
||||
State *string `bson:"state"`
|
||||
CreatedAt string `bson:"created_at"`
|
||||
UpdatedAt string `bson:"updated_at"`
|
||||
RemovedAt string `bson:"removed_at"`
|
||||
ID string `bson:"_id"`
|
||||
Username string `bson:"username"`
|
||||
Email *string `bson:"email"`
|
||||
IsEmailVerified *bool `bson:"is_email_verified"`
|
||||
CompanyName *string `bson:"company_name"`
|
||||
Name *string `bson:"name"`
|
||||
Role *string `bson:"role"`
|
||||
State *string `bson:"state"`
|
||||
CreatedAt string `bson:"created_at"`
|
||||
UpdatedAt string `bson:"updated_at"`
|
||||
RemovedAt string `bson:"removed_at"`
|
||||
}
|
||||
|
||||
//GetOutputUser ...
|
||||
|
@ -26,7 +25,6 @@ func (user User) GetOutputUser() *model.User {
|
|||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
IsEmailVerified: user.IsEmailVerified,
|
||||
MyHub: user.GetOuputMyHubs(),
|
||||
CompanyName: user.CompanyName,
|
||||
Name: user.Name,
|
||||
Role: user.Role,
|
||||
|
@ -37,36 +35,3 @@ func (user User) GetOutputUser() *model.User {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
//MyHub ...
|
||||
type MyHub struct {
|
||||
ID string `bson:"myhub_id"`
|
||||
RepoURL string `bson:"repo_url"`
|
||||
RepoBranch string `bson:"repo_branch"`
|
||||
IsConfirmed bool `bson:"is_confirmed"`
|
||||
HubName string `bson:"hub_name"`
|
||||
}
|
||||
|
||||
//GetOuputMyHubs ...
|
||||
func (user *User) GetOuputMyHubs() []*model.MyHub {
|
||||
|
||||
outputMyHub := []*model.MyHub{}
|
||||
|
||||
for _, myhub := range user.MyHub {
|
||||
outputMyHub = append(outputMyHub, myhub.GetOutputMyHub())
|
||||
}
|
||||
|
||||
return outputMyHub
|
||||
}
|
||||
|
||||
//GetOutputMyHub ...
|
||||
func (myhub *MyHub) GetOutputMyHub() *model.MyHub {
|
||||
|
||||
return &model.MyHub{
|
||||
ID: myhub.ID,
|
||||
RepoURL: myhub.RepoURL,
|
||||
RepoBranch: myhub.RepoBranch,
|
||||
IsConfirmed: myhub.IsConfirmed,
|
||||
HubName: myhub.HubName,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
package file_handlers
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/k8s"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/cluster"
|
||||
database "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/k8s"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/types"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/utils"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var subscriberConfiguration = &types.SubscriberConfigurationVars{
|
||||
|
@ -28,36 +26,45 @@ var subscriberConfiguration = &types.SubscriberConfigurationVars{
|
|||
//FileHandler dynamically generates the manifest file and sends it as a response
|
||||
func FileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
vars = mux.Vars(r)
|
||||
token = vars["key"]
|
||||
vars = mux.Vars(r)
|
||||
token = vars["key"]
|
||||
)
|
||||
|
||||
response, statusCode, err := GetManifest(token)
|
||||
if err != nil {
|
||||
log.Print("error", err)
|
||||
utils.WriteHeaders(&w, statusCode)
|
||||
}
|
||||
|
||||
utils.WriteHeaders(&w, statusCode)
|
||||
w.Write(response)
|
||||
}
|
||||
|
||||
func GetManifest(token string) ([]byte, int, error) {
|
||||
var (
|
||||
portalEndpoint string
|
||||
)
|
||||
|
||||
id, err := cluster.ClusterValidateJWT(token)
|
||||
if err != nil {
|
||||
log.Print("ERROR", err)
|
||||
utils.WriteHeaders(&w, 404)
|
||||
return
|
||||
return nil, 404, err
|
||||
}
|
||||
|
||||
reqCluster, err := database.GetCluster(id)
|
||||
if err != nil {
|
||||
log.Print("ERROR", err)
|
||||
utils.WriteHeaders(&w, 500)
|
||||
return
|
||||
return nil, 500, err
|
||||
}
|
||||
|
||||
if os.Getenv("PORTAL_SCOPE") == "cluster" {
|
||||
portalEndpoint, err = k8s.GetPortalEndpoint()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return nil, 500, err
|
||||
}
|
||||
} else if os.Getenv("PORTAL_SCOPE") == "namespace" {
|
||||
portalEndpoint = os.Getenv("PORTAL_ENDPOINT")
|
||||
}
|
||||
|
||||
subscriberConfiguration.GQLServerURI = portalEndpoint + "/query"
|
||||
|
||||
if !reqCluster.IsRegistered {
|
||||
var respData []byte
|
||||
|
||||
|
@ -68,16 +75,13 @@ func FileHandler(w http.ResponseWriter, r *http.Request) {
|
|||
} else {
|
||||
log.Print("ERROR- AGENT SCOPE NOT SELECTED!")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Print("ERROR", err)
|
||||
utils.WriteHeaders(&w, 500)
|
||||
return
|
||||
return nil, 500, err
|
||||
}
|
||||
utils.WriteHeaders(&w, 200)
|
||||
w.Write(respData)
|
||||
return
|
||||
|
||||
return respData, 200, nil
|
||||
} else {
|
||||
return []byte("Cluster is already registered"), 409, nil
|
||||
}
|
||||
|
||||
utils.WriteHeaders(&w, 404)
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ package mutations
|
|||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/graphql"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
@ -17,6 +20,7 @@ import (
|
|||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/graphql/subscriptions"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
@ -46,6 +50,7 @@ func ClusterRegister(input model.ClusterInput) (*model.ClusterRegResponse, error
|
|||
CreatedAt: strconv.FormatInt(time.Now().Unix(), 10),
|
||||
UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
|
||||
Token: token,
|
||||
IsRemoved: false,
|
||||
}
|
||||
|
||||
err = database.InsertCluster(newCluster)
|
||||
|
@ -191,6 +196,8 @@ func CreateChaosWorkflow(input *model.ChaosWorkFlowInput, r store.StateData) (*m
|
|||
newWorkflowManifest, _ = sjson.Set(input.WorkflowManifest, "spec.workflowMetadata.labels.workflow_id", workflow_id)
|
||||
}
|
||||
|
||||
isRemoved := false
|
||||
|
||||
newChaosWorkflow := database.ChaosWorkFlowInput{
|
||||
WorkflowID: workflow_id,
|
||||
WorkflowManifest: newWorkflowManifest,
|
||||
|
@ -204,6 +211,7 @@ func CreateChaosWorkflow(input *model.ChaosWorkFlowInput, r store.StateData) (*m
|
|||
CreatedAt: strconv.FormatInt(time.Now().Unix(), 10),
|
||||
UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
|
||||
WorkflowRuns: []*database.WorkflowRun{},
|
||||
IsRemoved: isRemoved,
|
||||
}
|
||||
|
||||
err = database.InsertChaosWorkflow(newChaosWorkflow)
|
||||
|
@ -211,7 +219,19 @@ func CreateChaosWorkflow(input *model.ChaosWorkFlowInput, r store.StateData) (*m
|
|||
return nil, err
|
||||
}
|
||||
|
||||
subscriptions.SendWorkflowRequest(&newChaosWorkflow, r)
|
||||
workflowNamespace := gjson.Get(newWorkflowManifest, "metadata.namespace").String()
|
||||
|
||||
if workflowNamespace == "" {
|
||||
workflowNamespace = os.Getenv("AGENT_NAMESPACE")
|
||||
}
|
||||
|
||||
subscriptions.SendRequestToSubscriber(graphql.SubscriberRequests{
|
||||
K8sManifest: newWorkflowManifest,
|
||||
RequestType: "create",
|
||||
ProjectID: input.ProjectID,
|
||||
ClusterID: input.ClusterID,
|
||||
Namespace: workflowNamespace,
|
||||
}, r)
|
||||
|
||||
return &model.ChaosWorkFlowResponse{
|
||||
WorkflowID: workflow_id,
|
||||
|
@ -221,3 +241,135 @@ func CreateChaosWorkflow(input *model.ChaosWorkFlowInput, r store.StateData) (*m
|
|||
IsCustomWorkflow: input.IsCustomWorkflow,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeleteCluster(cluster_id string, r store.StateData) (string, error) {
|
||||
time := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
|
||||
query := bson.D{{"cluster_id", cluster_id}}
|
||||
update := bson.D{{"$set", bson.D{{"is_removed", true}, {"updated_at", time}}}}
|
||||
|
||||
err := database.UpdateCluster(query, update)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cluster, err := database.GetCluster(cluster_id)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
requests := []string{
|
||||
`{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "subscriber",
|
||||
"namespace": ` + *cluster.AgentNamespace + `
|
||||
}
|
||||
}`,
|
||||
`{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": {
|
||||
"name": "litmus-portal-config",
|
||||
"namespace": ` + *cluster.AgentNamespace + `
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
for _, request := range requests {
|
||||
subscriptions.SendRequestToSubscriber(graphql.SubscriberRequests{
|
||||
K8sManifest: request,
|
||||
RequestType: "delete",
|
||||
ProjectID: cluster.ProjectID,
|
||||
ClusterID: cluster_id,
|
||||
Namespace: *cluster.AgentNamespace,
|
||||
}, r)
|
||||
}
|
||||
|
||||
return "Successfully deleted cluster", nil
|
||||
}
|
||||
|
||||
func UpdateWorkflow(workflow *model.ChaosWorkFlowInput, r store.StateData) (*model.ChaosWorkFlowResponse, error) {
|
||||
|
||||
var newWeightages []*database.WeightagesInput
|
||||
copier.Copy(&newWeightages, &workflow.Weightages)
|
||||
|
||||
var workflowManifest map[string]interface{}
|
||||
err := json.Unmarshal([]byte(workflow.WorkflowManifest), &workflowManifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newWorkflowManifest, err := sjson.Set(workflow.WorkflowManifest, "metadata.labels.workflow_id", workflow.WorkflowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.ToLower(workflowManifest["kind"].(string)) == "cronworkflow" {
|
||||
newWorkflowManifest, _ = sjson.Set(workflow.WorkflowManifest, "spec.workflowMetadata.labels.workflow_id", workflow.WorkflowID)
|
||||
}
|
||||
|
||||
query := bson.D{{"workflow_id", workflow.WorkflowID}}
|
||||
update := bson.D{{"$set", bson.D{{"workflow_manifest", newWorkflowManifest}, {"cronSyntax", workflow.CronSyntax}, {"Workflow_name", workflow.WorkflowName}, {"Workflow_description", workflow.WorkflowDescription}, {"isCustomWorkflow", workflow.IsCustomWorkflow}, {"Weightages", newWeightages}, {"updated_at", strconv.FormatInt(time.Now().Unix(), 10)}}}}
|
||||
|
||||
err = database.UpdateChaosWorkflow(query, update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workflowNamespace := gjson.Get(workflow.WorkflowManifest, "metadata.namespace").String()
|
||||
|
||||
if workflowNamespace == "" {
|
||||
workflowNamespace = os.Getenv("AGENT_NAMESPACE")
|
||||
}
|
||||
|
||||
subscriptions.SendRequestToSubscriber(graphql.SubscriberRequests{
|
||||
K8sManifest: newWorkflowManifest,
|
||||
RequestType: "update",
|
||||
ProjectID: workflow.ProjectID,
|
||||
ClusterID: workflow.ClusterID,
|
||||
Namespace: workflowNamespace,
|
||||
}, r)
|
||||
|
||||
return &model.ChaosWorkFlowResponse{
|
||||
WorkflowID: *workflow.WorkflowID,
|
||||
CronSyntax: workflow.CronSyntax,
|
||||
WorkflowName: workflow.WorkflowName,
|
||||
WorkflowDescription: workflow.WorkflowDescription,
|
||||
IsCustomWorkflow: workflow.IsCustomWorkflow,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DeleteWorkflow(workflow_id string, r store.StateData) (bool, error) {
|
||||
|
||||
workflows, err := database.GetWorkflowsByIDs([]*string{&workflow_id})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
query := bson.D{{"workflow_id", workflow_id}}
|
||||
update := bson.D{{"$set", bson.D{{"isRemoved", true}}}}
|
||||
|
||||
err = database.UpdateChaosWorkflow(query, update)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, workflow := range workflows {
|
||||
workflowNamespace := gjson.Get(workflow.WorkflowManifest, "metadata.namespace").String()
|
||||
|
||||
if workflowNamespace == "" {
|
||||
workflowNamespace = os.Getenv("AGENT_NAMESPACE")
|
||||
}
|
||||
|
||||
subscriptions.SendRequestToSubscriber(graphql.SubscriberRequests{
|
||||
K8sManifest: workflow.WorkflowManifest,
|
||||
RequestType: "delete",
|
||||
ProjectID: workflow.ProjectID,
|
||||
ClusterID: workflow.ClusterID,
|
||||
Namespace: workflowNamespace,
|
||||
}, r)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -55,25 +55,28 @@ func QueryWorkflows(project_id string) ([]*model.ScheduledWorkflows, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var Weightages []*model.Weightages
|
||||
copier.Copy(&Weightages, &workflow.Weightages)
|
||||
if workflow.IsRemoved == false {
|
||||
var Weightages []*model.Weightages
|
||||
copier.Copy(&Weightages, &workflow.Weightages)
|
||||
|
||||
newChaosWorkflows := model.ScheduledWorkflows{
|
||||
WorkflowID: workflow.WorkflowID,
|
||||
WorkflowManifest: workflow.WorkflowManifest,
|
||||
WorkflowName: workflow.WorkflowName,
|
||||
CronSyntax: workflow.CronSyntax,
|
||||
WorkflowDescription: workflow.WorkflowDescription,
|
||||
Weightages: Weightages,
|
||||
IsCustomWorkflow: workflow.IsCustomWorkflow,
|
||||
UpdatedAt: workflow.UpdatedAt,
|
||||
CreatedAt: workflow.CreatedAt,
|
||||
ProjectID: workflow.ProjectID,
|
||||
ClusterName: cluster.ClusterName,
|
||||
ClusterID: cluster.ClusterID,
|
||||
ClusterType: cluster.ClusterType,
|
||||
newChaosWorkflows := model.ScheduledWorkflows{
|
||||
WorkflowID: workflow.WorkflowID,
|
||||
WorkflowManifest: workflow.WorkflowManifest,
|
||||
WorkflowName: workflow.WorkflowName,
|
||||
CronSyntax: workflow.CronSyntax,
|
||||
WorkflowDescription: workflow.WorkflowDescription,
|
||||
Weightages: Weightages,
|
||||
IsCustomWorkflow: workflow.IsCustomWorkflow,
|
||||
UpdatedAt: workflow.UpdatedAt,
|
||||
CreatedAt: workflow.CreatedAt,
|
||||
ProjectID: workflow.ProjectID,
|
||||
IsRemoved: workflow.IsRemoved,
|
||||
ClusterName: cluster.ClusterName,
|
||||
ClusterID: cluster.ClusterID,
|
||||
ClusterType: cluster.ClusterType,
|
||||
}
|
||||
result = append(result, &newChaosWorkflows)
|
||||
}
|
||||
result = append(result, &newChaosWorkflows)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -110,6 +113,7 @@ func QueryListWorkflow(project_id string) ([]*model.Workflow, error) {
|
|||
UpdatedAt: workflow.UpdatedAt,
|
||||
CreatedAt: workflow.CreatedAt,
|
||||
ProjectID: workflow.ProjectID,
|
||||
IsRemoved: workflow.IsRemoved,
|
||||
ClusterName: cluster.ClusterName,
|
||||
ClusterID: cluster.ClusterID,
|
||||
ClusterType: cluster.ClusterType,
|
||||
|
@ -174,7 +178,7 @@ func GetLogs(reqID string, pod model.PodLogRequest, r store.StateData) {
|
|||
payload := model.ClusterAction{
|
||||
ProjectID: reqID,
|
||||
Action: &model.ActionPayload{
|
||||
RequestType: &reqType,
|
||||
RequestType: reqType,
|
||||
ExternalData: &externalData,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package subscriptions
|
||||
|
||||
import (
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/graphql"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/litmuschaos/litmus/litmus-portal/graphql-server/graph/model"
|
||||
store "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/data-store"
|
||||
database "github.com/litmuschaos/litmus/litmus-portal/graphql-server/pkg/database/mongodb"
|
||||
)
|
||||
|
||||
//SendClusterEvent sends events from the clusters to the appropriate users listening for the events
|
||||
|
@ -38,9 +38,7 @@ func SendWorkflowEvent(wfRun model.WorkflowRun, r store.StateData) {
|
|||
r.Mutex.Unlock()
|
||||
}
|
||||
|
||||
func SendWorkflowRequest(wfRequest *database.ChaosWorkFlowInput, r store.StateData) {
|
||||
|
||||
namespace := os.Getenv("AGENT_NAMESPACE")
|
||||
func SendRequestToSubscriber(subscriberRequest graphql.SubscriberRequests, r store.StateData) {
|
||||
if os.Getenv("AGENT_SCOPE") == "cluster" {
|
||||
/*
|
||||
namespace = Obtain from WorkflowManifest or
|
||||
|
@ -48,19 +46,18 @@ func SendWorkflowRequest(wfRequest *database.ChaosWorkFlowInput, r store.StateDa
|
|||
for CreateChaosWorkflow mutation to be passed to this function.
|
||||
*/
|
||||
}
|
||||
requesttype := "create"
|
||||
newAction := &model.ClusterAction{
|
||||
ProjectID: wfRequest.ProjectID,
|
||||
ProjectID: subscriberRequest.ProjectID,
|
||||
Action: &model.ActionPayload{
|
||||
K8sManifest: &wfRequest.WorkflowManifest,
|
||||
Namespace: &namespace,
|
||||
RequestType: &requesttype,
|
||||
K8sManifest: subscriberRequest.K8sManifest,
|
||||
Namespace: subscriberRequest.Namespace,
|
||||
RequestType: subscriberRequest.RequestType,
|
||||
},
|
||||
}
|
||||
|
||||
r.Mutex.Lock()
|
||||
|
||||
if observer, ok := r.ConnectedCluster[wfRequest.ClusterID]; ok {
|
||||
if observer, ok := r.ConnectedCluster[subscriberRequest.ClusterID]; ok {
|
||||
observer <- newAction
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package graphql
|
||||
|
||||
type SubscriberRequests struct {
|
||||
RequestType string `json:"request_type"`
|
||||
K8sManifest string `json:"k8s_manifest"`
|
||||
ExternalData *string `json:"external_data"`
|
||||
ProjectID string `json:"project_id"`
|
||||
ClusterID string `json:"cluster_id"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
@ -25,3 +27,26 @@ func GetGenericK8sClient() (*kubernetes.Clientset, error) {
|
|||
|
||||
return kubernetes.NewForConfig(config)
|
||||
}
|
||||
|
||||
//This function returns dynamic client and discovery client
|
||||
func GetDynamicAndDiscoveryClient() (discovery.DiscoveryInterface, dynamic.Interface, error) {
|
||||
// returns a config object which uses the service account kubernetes gives to pods
|
||||
config, err := GetKubeConfig()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// NewDiscoveryClientForConfig creates a new DiscoveryClient for the given config
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// NewForConfig creates a new dynamic client or returns an error.
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return discoveryClient, dynamicClient, nil
|
||||
}
|
||||
|
|
|
@ -2,94 +2,74 @@ package k8s
|
|||
|
||||
import (
|
||||
"context"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/restmapper"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
memory "k8s.io/client-go/discovery/cached"
|
||||
)
|
||||
|
||||
func CreateDeployment(namespace, token string) (*appsv1.Deployment, error) {
|
||||
deployerImage := os.Getenv("DEPLOYER_IMAGE")
|
||||
subscriberSC := os.Getenv("AGENT_SCOPE")
|
||||
selfDeployerSvcAccount := "self-deployer-namespace-account"
|
||||
if subscriberSC == "cluster" {
|
||||
selfDeployerSvcAccount = "self-deployer-admin-account"
|
||||
}
|
||||
cfg, err := GetKubeConfig()
|
||||
clientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||
var (
|
||||
decUnstructured = yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
|
||||
dr dynamic.ResourceInterface
|
||||
AgentNamespace = os.Getenv("AGENT_NAMESPACE")
|
||||
)
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "self-deployer",
|
||||
Labels: map[string]string{"component": "self-deployer"},
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: func(i int32) *int32 { return &i }(1),
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"component": "self-deployer",
|
||||
},
|
||||
},
|
||||
Template: apiv1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"component": "self-deployer",
|
||||
},
|
||||
},
|
||||
Spec: apiv1.PodSpec{
|
||||
Containers: []apiv1.Container{
|
||||
{
|
||||
Name: "deployer",
|
||||
Image: deployerImage,
|
||||
Resources: apiv1.ResourceRequirements{
|
||||
Limits: apiv1.ResourceList{
|
||||
"cpu": resource.MustParse("100m"),
|
||||
"memory": resource.MustParse("128Mi"),
|
||||
},
|
||||
},
|
||||
ImagePullPolicy: "Always",
|
||||
Env: []apiv1.EnvVar{
|
||||
{
|
||||
Name: "SERVER",
|
||||
Value: "http://litmusportal-server-service:9002",
|
||||
},
|
||||
{
|
||||
Name: "TOKEN",
|
||||
Value: token,
|
||||
},
|
||||
{
|
||||
Name: "NAMESPACE",
|
||||
ValueFrom: &apiv1.EnvVarSource{
|
||||
FieldRef: &apiv1.ObjectFieldSelector{
|
||||
FieldPath: "metadata.namespace",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceAccountName: selfDeployerSvcAccount,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create Deployment
|
||||
log.Println("Creating deployment...")
|
||||
result, err := deploymentsClient.Create(context.TODO(), deployment, metav1.CreateOptions{})
|
||||
// This function handles cluster operations
|
||||
func ClusterResource(manifest string, namespace string) (*unstructured.Unstructured, error) {
|
||||
// Getting dynamic and discovery client
|
||||
discoveryClient, dynamicClient, err := GetDynamicAndDiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
|
||||
// Create a mapper using dynamic client
|
||||
mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient))
|
||||
|
||||
// Decode YAML manifest into unstructured.Unstructured
|
||||
obj := &unstructured.Unstructured{}
|
||||
_, gvk, err := decUnstructured.Decode([]byte(manifest), nil, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find GVR
|
||||
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Obtain REST interface for the GVR
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
||||
// namespaced resources should specify the namespace
|
||||
dr = dynamicClient.Resource(mapping.Resource).Namespace(namespace)
|
||||
} else {
|
||||
// for cluster-wide resources
|
||||
dr = dynamicClient.Resource(mapping.Resource)
|
||||
}
|
||||
|
||||
response, err := dr.Create(context.TODO(), obj, metav1.CreateOptions{})
|
||||
if errors.IsAlreadyExists(err) {
|
||||
// This doesnt ever happen even if it does already exist
|
||||
log.Print("Already exists")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Println("Resource successfully created")
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func GetPortalEndpoint() (string, error) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue