litmus/chaoscenter/graphql/server/pkg/chaoshub/service.go

982 lines
28 KiB
Go

package chaoshub
import (
"context"
"errors"
"fmt"
"os"
"strconv"
"time"
"github.com/google/uuid"
"github.com/jinzhu/copier"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/graph/model"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/authorization"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/chaoshub/handler"
chaosHubOps "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/chaoshub/ops"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb"
dbSchemaChaosHub "github.com/litmuschaos/litmus/chaoscenter/graphql/server/pkg/database/mongodb/chaos_hub"
"github.com/litmuschaos/litmus/chaoscenter/graphql/server/utils"
log "github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
const (
timeInterval = 6 * time.Hour
DefaultPath = "/tmp/"
DefaultHubID = "6f39cea9-6264-4951-83a8-29976b614289"
DefaultHubSyncTimeInterval = 6 * time.Hour
)
type Service interface {
AddChaosHub(ctx context.Context, chaosHub model.CreateChaosHubRequest, projectID string) (*model.ChaosHub, error)
AddRemoteChaosHub(ctx context.Context, chaosHub model.CreateRemoteChaosHub, projectID string) (*model.ChaosHub, error)
SaveChaosHub(ctx context.Context, chaosHub model.CreateChaosHubRequest, projectID string) (*model.ChaosHub, error)
SyncChaosHub(ctx context.Context, hubID string, projectID string) (string, error)
UpdateChaosHub(ctx context.Context, chaosHub model.UpdateChaosHubRequest, projectID string) (*model.ChaosHub, error)
DeleteChaosHub(ctx context.Context, hubID string, projectID string) (bool, error)
ListChaosFaults(ctx context.Context, hubID string, projectID string) ([]*model.Chart, error)
GetChaosFault(ctx context.Context, request model.ExperimentRequest, projectID string) (*model.FaultDetails, error)
GetChaosHub(ctx context.Context, chaosHubID string, projectID string) (*model.ChaosHubStatus, error)
ListChaosHubs(ctx context.Context, projectID string, request *model.ListChaosHubRequest) ([]*model.ChaosHubStatus, error)
ListPredefinedExperiments(ctx context.Context, hubID string, projectID string) ([]*model.PredefinedExperimentList, error)
GetPredefinedExperiment(ctx context.Context, hubID string, experiment []string, projectID string) ([]*model.PredefinedExperimentList, error)
IsChaosHubAvailable(ctx context.Context, name string, projectID string) (bool, error)
GetAllHubs(ctx context.Context) ([]*model.ChaosHub, error)
RecurringHubSync()
SyncDefaultChaosHubs()
GetChaosHubStats(ctx context.Context, projectID string) (*model.GetChaosHubStatsResponse, error)
}
type chaosHubService struct {
chaosHubOperator *dbSchemaChaosHub.Operator
}
// NewService returns a new instance of Service
func NewService(chaosHubOperator *dbSchemaChaosHub.Operator) Service {
return &chaosHubService{
chaosHubOperator: chaosHubOperator,
}
}
// AddChaosHub is used for Adding a new ChaosHub
func (c *chaosHubService) AddChaosHub(ctx context.Context, chaosHub model.CreateChaosHubRequest, projectID string) (*model.ChaosHub, error) {
if IsExist, err := c.IsChaosHubAvailable(ctx, chaosHub.Name, projectID); err != nil {
return nil, err
} else if IsExist == true {
return nil, errors.New("name already exists")
}
currentTime := time.Now()
cloneHub := NewCloningInputFrom(chaosHub)
description := ""
if chaosHub.Description != nil {
description = *chaosHub.Description
}
tkn := ctx.Value(authorization.AuthKey).(string)
username, err := authorization.GetUsername(tkn)
if err != nil {
log.Error("error getting username: ", err)
return nil, err
}
newHub := &dbSchemaChaosHub.ChaosHub{
ID: uuid.New().String(),
ProjectID: projectID,
RepoURL: chaosHub.RepoURL,
RepoBranch: chaosHub.RepoBranch,
ResourceDetails: mongodb.ResourceDetails{
Name: chaosHub.Name,
Description: description,
Tags: chaosHub.Tags,
},
IsPrivate: chaosHub.IsPrivate,
AuthType: string(chaosHub.AuthType),
HubType: string(model.HubTypeGit),
Token: chaosHub.Token,
UserName: chaosHub.UserName,
Password: chaosHub.Password,
SSHPrivateKey: chaosHub.SSHPrivateKey,
SSHPublicKey: chaosHub.SSHPublicKey,
Audit: mongodb.Audit{
CreatedAt: currentTime.UnixMilli(),
UpdatedAt: currentTime.UnixMilli(),
IsRemoved: false,
CreatedBy: mongodb.UserDetailResponse{
Username: username,
},
UpdatedBy: mongodb.UserDetailResponse{
Username: username,
},
},
LastSyncedAt: time.Now().UnixMilli(),
IsDefault: false,
}
// Adding the new hub into database with the given username.
if err := c.chaosHubOperator.CreateChaosHub(ctx, newHub); err != nil {
log.Error(err)
return nil, err
}
// Cloning the repository at a path from ChaosHub link structure.
if err := chaosHubOps.GitClone(cloneHub, projectID); err != nil {
log.Error(err)
}
return newHub.GetOutputChaosHub(), nil
}
func (c *chaosHubService) AddRemoteChaosHub(ctx context.Context, chaosHub model.CreateRemoteChaosHub, projectID string) (*model.ChaosHub, error) {
IsExist, err := c.IsChaosHubAvailable(ctx, chaosHub.Name, projectID)
if err != nil {
return nil, err
}
if IsExist == true {
return nil, errors.New("name already exists")
}
description := ""
if chaosHub.Description != nil {
description = *chaosHub.Description
}
currentTime := time.Now()
tkn := ctx.Value(authorization.AuthKey).(string)
username, err := authorization.GetUsername(tkn)
if err != nil {
log.Error("error getting userID: ", err)
return nil, err
}
newHub := &dbSchemaChaosHub.ChaosHub{
ID: uuid.New().String(),
ProjectID: projectID,
RepoURL: chaosHub.RepoURL,
RepoBranch: "",
ResourceDetails: mongodb.ResourceDetails{
Name: chaosHub.Name,
Description: description,
Tags: chaosHub.Tags,
},
IsPrivate: false,
HubType: string(model.HubTypeRemote),
AuthType: string(model.AuthTypeNone),
Audit: mongodb.Audit{
CreatedAt: currentTime.UnixMilli(),
UpdatedAt: currentTime.UnixMilli(),
IsRemoved: false,
CreatedBy: mongodb.UserDetailResponse{
Username: username,
},
UpdatedBy: mongodb.UserDetailResponse{
Username: username,
},
},
LastSyncedAt: time.Now().UnixMilli(),
IsDefault: false,
}
// Adding the new hub into database with the given name.
err = c.chaosHubOperator.CreateChaosHub(ctx, newHub)
if err != nil {
log.Error(err)
return nil, err
}
err = handler.DownloadRemoteHub(chaosHub, projectID)
if err != nil {
err = fmt.Errorf("Hub configurations saved successfully. Failed to connect the remote repo: " + err.Error())
log.Error(err)
return nil, err
}
return newHub.GetOutputChaosHub(), nil
}
// SaveChaosHub is used for Adding a new ChaosHub
func (c *chaosHubService) SaveChaosHub(ctx context.Context, chaosHub model.CreateChaosHubRequest, projectID string) (*model.ChaosHub, error) {
IsExist, err := c.IsChaosHubAvailable(ctx, chaosHub.Name, projectID)
if err != nil {
return nil, err
}
if IsExist == true {
return nil, errors.New("name already exists")
}
// Initialize a UID for new Hub.
uuid := uuid.New()
tkn := ctx.Value(authorization.AuthKey).(string)
username, err := authorization.GetUsername(tkn)
if err != nil {
log.Error("error getting userID: ", err)
return nil, err
}
description := ""
if chaosHub.Description != nil {
description = *chaosHub.Description
}
currentTime := time.Now()
newHub := &dbSchemaChaosHub.ChaosHub{
ID: uuid.String(),
ProjectID: projectID,
RepoURL: chaosHub.RepoURL,
RepoBranch: chaosHub.RepoBranch,
ResourceDetails: mongodb.ResourceDetails{
Name: chaosHub.Name,
Description: description,
Tags: chaosHub.Tags,
},
IsPrivate: chaosHub.IsPrivate,
AuthType: string(chaosHub.AuthType),
Token: chaosHub.Token,
UserName: chaosHub.UserName,
Password: chaosHub.Password,
SSHPrivateKey: chaosHub.SSHPrivateKey,
SSHPublicKey: chaosHub.SSHPublicKey,
Audit: mongodb.Audit{
CreatedAt: currentTime.UnixMilli(),
UpdatedAt: currentTime.UnixMilli(),
IsRemoved: false,
CreatedBy: mongodb.UserDetailResponse{
Username: username,
},
UpdatedBy: mongodb.UserDetailResponse{
Username: username,
},
},
LastSyncedAt: time.Now().UnixMilli(),
}
// Adding the new hub into database with the given username without cloning.
err = c.chaosHubOperator.CreateChaosHub(ctx, newHub)
if err != nil {
log.Error(err)
return nil, err
}
return newHub.GetOutputChaosHub(), nil
}
// SyncChaosHub is used for syncing the hub again if some not present or some error happens.
func (c *chaosHubService) SyncChaosHub(ctx context.Context, hubID string, projectID string) (string, error) {
chaosHub, err := c.chaosHubOperator.GetHubByID(ctx, hubID, projectID)
if err != nil {
return "", err
}
syncHubInput := model.CloningInput{
Name: chaosHub.Name,
RepoURL: chaosHub.RepoURL,
RepoBranch: chaosHub.RepoBranch,
IsPrivate: chaosHub.IsPrivate,
UserName: chaosHub.UserName,
Password: chaosHub.Password,
AuthType: model.AuthType(chaosHub.AuthType),
Token: chaosHub.Token,
SSHPrivateKey: chaosHub.SSHPrivateKey,
IsDefault: false,
}
time := time.Now().UnixMilli()
query := bson.D{{"hub_id", hubID}, {"is_removed", false}}
update := bson.D{{"$set", bson.D{{"last_synced_at", time}}}}
if chaosHub.HubType == string(model.HubTypeRemote) {
err = handler.SyncRemoteRepo(syncHubInput, projectID)
if err != nil {
return "", err
}
} else {
err = chaosHubOps.GitSyncHandlerForProjects(syncHubInput, projectID)
if err != nil {
return "", err
}
}
// Updating the last_synced_at time using hubID
err = c.chaosHubOperator.UpdateChaosHub(ctx, query, update)
if err != nil {
log.Error(err)
return "", err
}
return "Successfully synced ChaosHub", nil
}
func (c *chaosHubService) UpdateChaosHub(ctx context.Context, chaosHub model.UpdateChaosHubRequest, projectID string) (*model.ChaosHub, error) {
cloneHub := model.CloningInput{
RepoBranch: chaosHub.RepoBranch,
RepoURL: chaosHub.RepoURL,
Name: chaosHub.Name,
IsPrivate: chaosHub.IsPrivate,
UserName: chaosHub.UserName,
Password: chaosHub.Password,
AuthType: chaosHub.AuthType,
Token: chaosHub.Token,
SSHPrivateKey: chaosHub.SSHPrivateKey,
IsDefault: false,
}
fmt.Println(chaosHub.SSHPrivateKey)
prevChaosHub, err := c.chaosHubOperator.GetHubByID(ctx, chaosHub.ID, projectID)
if err != nil {
return nil, err
}
clonePath := DefaultPath + prevChaosHub.ProjectID + "/" + prevChaosHub.Name
if prevChaosHub.HubType == string(model.HubTypeRemote) {
if prevChaosHub.Name != chaosHub.Name || prevChaosHub.RepoURL != chaosHub.RepoURL {
remoteHub := model.CreateRemoteChaosHub{
Name: chaosHub.Name,
RepoURL: chaosHub.RepoURL,
}
err = os.RemoveAll(clonePath)
if err != nil {
return nil, err
}
err = handler.DownloadRemoteHub(remoteHub, projectID)
if err != nil {
return nil, err
}
}
} else {
// Syncing/Cloning the repository at a path from ChaosHub link structure.
if prevChaosHub.Name != chaosHub.Name || prevChaosHub.RepoURL != chaosHub.RepoURL || prevChaosHub.RepoBranch != chaosHub.RepoBranch || prevChaosHub.IsPrivate != chaosHub.IsPrivate || prevChaosHub.AuthType != chaosHub.AuthType.String() {
err = os.RemoveAll(clonePath)
if err != nil {
return nil, err
}
err = chaosHubOps.GitClone(cloneHub, projectID)
if err != nil {
return nil, err
}
} else {
err := chaosHubOps.GitSyncHandlerForProjects(cloneHub, projectID)
if err != nil {
return nil, err
}
}
}
time := time.Now().UnixMilli()
tkn := ctx.Value(authorization.AuthKey).(string)
username, err := authorization.GetUsername(tkn)
query := bson.D{{"hub_id", chaosHub.ID}, {"is_removed", false}}
update := bson.D{
{"$set", bson.D{
{"repo_url", chaosHub.RepoURL},
{"repo_branch", chaosHub.RepoBranch},
{"name", chaosHub.Name},
{"description", chaosHub.Description},
{"tags", chaosHub.Tags},
{"is_private", chaosHub.IsPrivate},
{"auth_type", chaosHub.AuthType},
{"token", chaosHub.Token},
{"username", chaosHub.UserName},
{"password", chaosHub.Password},
{"ssh_private_key", chaosHub.SSHPrivateKey},
{"ssh_public_key", chaosHub.SSHPublicKey},
{"updated_at", time},
{"updated_by", mongodb.UserDetailResponse{
Username: username,
}},
},
},
}
// Updating the new hub into database with the given username.
err = c.chaosHubOperator.UpdateChaosHub(ctx, query, update)
if err != nil {
log.Error(err)
return nil, err
}
var newChaosHub model.ChaosHub
copier.Copy(&newChaosHub, &chaosHub)
newChaosHub.UpdatedAt = strconv.FormatInt(time, 10)
return &newChaosHub, nil
}
func (c *chaosHubService) DeleteChaosHub(ctx context.Context, hubID string, projectID string) (bool, error) {
tkn := ctx.Value(authorization.AuthKey).(string)
username, err := authorization.GetUsername(tkn)
chaosHub, err := c.chaosHubOperator.GetHubByID(ctx, hubID, projectID)
if err != nil {
log.Error(err)
return false, err
}
query := bson.D{
{"hub_id", hubID},
{"project_id", projectID},
}
update := bson.D{
{"$set", bson.D{
{"is_removed", true},
{"updated_at", time.Now().UnixMilli()},
{"updated_by", mongodb.UserDetailResponse{
Username: username,
}},
},
},
}
err = c.chaosHubOperator.UpdateChaosHub(ctx, query, update)
if err != nil {
log.Error(err)
return false, err
}
clonePath := DefaultPath + projectID + "/" + chaosHub.Name
err = os.RemoveAll(clonePath)
if err != nil {
log.Error(err)
return false, err
}
return true, nil
}
// ListChaosFaults is responsible for getting the charts details
func (c *chaosHubService) ListChaosFaults(ctx context.Context, hubID string, projectID string) ([]*model.Chart, error) {
chartsInput := model.CloningInput{}
hub, err := c.getChaosHubDetails(ctx, hubID, projectID)
if err != nil {
return nil, err
}
chartsInput = model.CloningInput{
Name: hub.Name,
RepoURL: hub.RepoURL,
RepoBranch: hub.RepoBranch,
}
ChartsPath := handler.GetChartsPath(chartsInput, projectID, hub.IsDefault)
ChartsData, err := handler.GetChartsData(ChartsPath)
if err != nil {
return nil, err
}
return ChartsData, nil
}
// GetChaosFault is used for getting details of chartserviceversion.yaml.
func (c *chaosHubService) GetChaosFault(ctx context.Context, request model.ExperimentRequest, projectID string) (*model.FaultDetails, error) {
chaosHub, err := c.getChaosHubDetails(ctx, request.HubID, projectID)
if err != nil {
return nil, err
}
var basePath string
if chaosHub.IsDefault {
basePath = "/tmp/default/" + chaosHub.Name + "/faults/" + request.Category + "/" + request.ExperimentName
} else {
basePath = DefaultPath + projectID + "/" + chaosHub.Name + "/faults/" + request.Category + "/" + request.ExperimentName
}
//Get fault chartserviceversion.yaml data
csvPath := basePath + "/" + request.ExperimentName + ".chartserviceversion.yaml"
csvYaml, err := os.ReadFile(csvPath)
if err != nil {
csvYaml = []byte("")
}
//Get engine.yaml data
enginePath := basePath + "/" + "engine.yaml"
engineYaml, err := os.ReadFile(enginePath)
if err != nil {
engineYaml = []byte("")
}
//Get fault.yaml data
faultPath := basePath + "/" + "fault.yaml"
faultYaml, err := os.ReadFile(faultPath)
if err != nil {
faultYaml = []byte("")
}
return &model.FaultDetails{
CSV: string(csvYaml),
Engine: string(engineYaml),
Fault: string(faultYaml),
}, nil
}
// ListChaosHubs returns the array of hub details with their current status.
func (c *chaosHubService) ListChaosHubs(ctx context.Context, projectID string, request *model.ListChaosHubRequest) ([]*model.ChaosHubStatus, error) {
defaultHub := c.listDefaultHubs()
updatedDefaultHub := dbSchemaChaosHub.ChaosHub{
ID: defaultHub.ID,
ResourceDetails: mongodb.ResourceDetails{
Name: defaultHub.Name,
},
RepoURL: defaultHub.RepoURL,
RepoBranch: defaultHub.RepoBranch,
IsDefault: true,
}
var pipeline mongo.Pipeline
// Match with identifiers
matchIdentifierStage := bson.D{
{"$match", bson.D{
{"project_id", projectID},
{"is_removed", false},
}},
}
pipeline = append(pipeline, matchIdentifierStage)
if request != nil {
// Match the ChaosHub IDs from the input array
if request.ChaosHubIDs != nil && len(request.ChaosHubIDs) != 0 {
matchHubIdStage := bson.D{
{"$match", bson.D{
{"hub_id", bson.D{
{"$in", request.ChaosHubIDs},
}},
}},
}
pipeline = append(pipeline, matchHubIdStage)
}
// Filtering based on given parameters
if request.Filter != nil {
// Filtering based on chaos_infra name
if request.Filter.ChaosHubName != nil && *request.Filter.ChaosHubName != "" {
matchHubNameStage := bson.D{
{"$match", bson.D{
{"name", bson.M{
"$regex": request.Filter.ChaosHubName, "$options": "mix",
}},
}},
}
pipeline = append(pipeline, matchHubNameStage)
}
// Filtering based on description
if request.Filter.Description != nil && *request.Filter.Description != "" {
matchDescriptionStage := bson.D{
{"$match", bson.D{
{"description", bson.M{
"$regex": request.Filter.Description, "$options": "mix",
}},
}},
}
pipeline = append(pipeline, matchDescriptionStage)
}
// Filtering based on tags
if request.Filter.Tags != nil && len(request.Filter.Tags) > 0 {
matchHubTagsStage := bson.D{
{"$match", bson.D{
{"tags", bson.D{
{"$all", request.Filter.Tags},
}},
}},
}
pipeline = append(pipeline, matchHubTagsStage)
}
}
}
var allHubs []dbSchemaChaosHub.ChaosHub
hubCursor, err := c.chaosHubOperator.GetAggregateChaosHubs(ctx, pipeline)
if err != nil {
return nil, err
}
err = hubCursor.All(context.Background(), &allHubs)
if err != nil {
return nil, err
}
var (
hubDetails []*model.ChaosHubStatus
)
allHubs = append([]dbSchemaChaosHub.ChaosHub{updatedDefaultHub}, allHubs...)
for _, hub := range allHubs {
sum := 0
experimentCount := 0
isConfirmed := false
var chartPath string
if hub.IsDefault {
chartPath = DefaultPath + "default/" + hub.Name + "/faults/"
} else {
chartPath = DefaultPath + hub.ProjectID + "/" + hub.Name + "/faults/"
}
chartsData, err := handler.GetChartsData(chartPath)
if err != nil {
sum = 0
} else {
for _, chart := range chartsData {
sum = sum + len(chart.Spec.Faults)
}
}
wfs, err := c.ListPredefinedExperiments(ctx, hub.ID, projectID)
if err != nil {
experimentCount = 0
}
experimentCount = len(wfs)
isConfirmed, err = handler.ValidateLocalRepository(hub)
if err != nil {
isConfirmed = false
}
hubDesc := hub.Description
hubDetail := &model.ChaosHubStatus{
IsAvailable: isConfirmed,
ID: hub.ID,
RepoURL: hub.RepoURL,
IsDefault: hub.IsDefault,
IsPrivate: hub.IsPrivate,
Name: hub.Name,
Description: &hubDesc,
RepoBranch: hub.RepoBranch,
Tags: hub.Tags,
Token: hub.Token,
SSHPublicKey: hub.SSHPublicKey,
SSHPrivateKey: hub.SSHPrivateKey,
AuthType: model.AuthType(hub.AuthType),
LastSyncedAt: strconv.FormatInt(hub.LastSyncedAt, 10),
TotalFaults: strconv.Itoa(sum),
TotalExperiments: strconv.Itoa(experimentCount),
CreatedAt: strconv.Itoa(int(hub.CreatedAt)),
UpdatedAt: strconv.Itoa(int(hub.UpdatedAt)),
CreatedBy: &model.UserDetails{Username: hub.CreatedBy.Username},
UpdatedBy: &model.UserDetails{Username: hub.UpdatedBy.Username},
}
hubDetails = append(hubDetails, hubDetail)
}
return hubDetails, nil
}
// GetChaosHub returns details of a requested hub
func (c *chaosHubService) GetChaosHub(ctx context.Context, chaosHubID string, projectID string) (*model.ChaosHubStatus, error) {
hub, err := c.chaosHubOperator.GetHubByID(ctx, chaosHubID, projectID)
if err != nil {
return &model.ChaosHubStatus{}, errors.New("DB fetch stage error: " + err.Error())
}
sum := 0
experimentCount := 0
var chartPath string
if hub.IsDefault {
chartPath = DefaultPath + "default/" + hub.Name
} else {
chartPath = DefaultPath + hub.ProjectID + "/" + hub.Name
}
chartsData, err := handler.GetChartsData(chartPath)
if err != nil {
sum = 0
} else {
for _, chart := range chartsData {
sum = sum + len(chart.Spec.Faults)
}
}
wfs, err := c.ListPredefinedExperiments(ctx, hub.ID, projectID)
if err != nil {
experimentCount = 0
}
experimentCount = len(wfs)
isConfirmed, err := handler.ValidateLocalRepository(hub)
if err != nil {
isConfirmed = false
}
hubDesc := hub.Description
hubDetail := &model.ChaosHubStatus{
IsAvailable: isConfirmed,
ID: hub.ID,
RepoURL: hub.RepoURL,
Name: hub.Name,
Description: &hubDesc,
RepoBranch: hub.RepoBranch,
Tags: hub.Tags,
AuthType: model.AuthType(hub.AuthType),
LastSyncedAt: strconv.FormatInt(hub.LastSyncedAt, 10),
TotalFaults: strconv.Itoa(sum),
TotalExperiments: strconv.Itoa(experimentCount),
CreatedAt: strconv.Itoa(int(hub.CreatedAt)),
UpdatedAt: strconv.Itoa(int(hub.UpdatedAt)),
CreatedBy: &model.UserDetails{Username: hub.CreatedBy.Username},
UpdatedBy: &model.UserDetails{Username: hub.UpdatedBy.Username},
}
return hubDetail, nil
}
func (c *chaosHubService) ListPredefinedExperiments(ctx context.Context, hubID string, projectID string) ([]*model.PredefinedExperimentList, error) {
hub, err := c.getChaosHubDetails(ctx, hubID, projectID)
if err != nil {
return nil, err
}
var hubPath string
if hub.IsDefault {
hubPath = "/tmp/default/" + hub.Name + "/experiments/"
} else {
hubPath = DefaultPath + projectID + "/" + hub.Name + "/experiments/"
}
var predefinedWorkflows []*model.PredefinedExperimentList
files, err := os.ReadDir(hubPath)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name() != "icons" {
preDefinedWorkflow := c.getPredefinedExperimentDetails(hubPath, file.Name())
if preDefinedWorkflow.ExperimentCSV != "" {
predefinedWorkflows = append(predefinedWorkflows, preDefinedWorkflow)
}
}
}
return predefinedWorkflows, nil
}
func (c *chaosHubService) getChaosHubDetails(ctx context.Context, hubID string, projectID string) (model.ChaosHub, error) {
defaultHub := c.listDefaultHubs()
if defaultHub.ID == hubID {
return *defaultHub, nil
}
hub, err := c.chaosHubOperator.GetHubByID(ctx, hubID, projectID)
if err != nil {
return model.ChaosHub{}, errors.New("DB fetch stage error: " + err.Error())
}
return model.ChaosHub{
ID: hub.ID,
ProjectID: hub.ProjectID,
RepoURL: hub.RepoURL,
RepoBranch: hub.RepoBranch,
AuthType: model.AuthType(hub.AuthType),
Name: hub.Name,
CreatedAt: strconv.Itoa(int(hub.CreatedAt)),
UpdatedAt: strconv.Itoa(int(hub.UpdatedAt)),
LastSyncedAt: strconv.FormatInt(hub.LastSyncedAt, 10),
Tags: hub.Tags,
Description: &hub.Description,
//TODO util functions for this
CreatedBy: &model.UserDetails{Username: hub.CreatedBy.Username},
UpdatedBy: &model.UserDetails{Username: hub.UpdatedBy.Username},
}, nil
}
func (c *chaosHubService) GetPredefinedExperiment(ctx context.Context, hubID string, experiments []string, projectID string) ([]*model.PredefinedExperimentList, error) {
hub, err := c.getChaosHubDetails(ctx, hubID, projectID)
if err != nil {
return nil, err
}
var hubPath string
if hub.IsDefault {
hubPath = "/tmp/default/" + hub.Name + "/experiments/"
} else {
hubPath = DefaultPath + projectID + "/" + hub.Name + "/experiments/"
}
var predefinedWorkflows []*model.PredefinedExperimentList
for _, experiment := range experiments {
preDefinedWorkflow := c.getPredefinedExperimentDetails(hubPath, experiment)
if preDefinedWorkflow.ExperimentCSV != "" {
predefinedWorkflows = append(predefinedWorkflows, preDefinedWorkflow)
}
}
return predefinedWorkflows, nil
}
func (c *chaosHubService) getPredefinedExperimentDetails(experimentsPath string, experiment string) *model.PredefinedExperimentList {
var (
csvManifest = ""
workflowManifest = ""
path = experimentsPath + experiment + "/" + experiment + ".chartserviceversion.yaml"
isExist = true
preDefinedWorkflow = &model.PredefinedExperimentList{}
)
_, err := os.Stat(path)
if err != nil {
isExist = false
}
if isExist {
yamlData, err := os.ReadFile(experimentsPath + experiment + "/" + experiment + ".chartserviceversion.yaml")
if err != nil {
csvManifest = ""
}
csvManifest = string(yamlData)
yamlData, err = os.ReadFile(experimentsPath + experiment + "/" + "experiment.yaml")
if err != nil {
workflowManifest = ""
}
workflowManifest = string(yamlData)
preDefinedWorkflow = &model.PredefinedExperimentList{
ExperimentName: experiment,
ExperimentManifest: workflowManifest,
ExperimentCSV: csvManifest,
}
}
return preDefinedWorkflow
}
// IsChaosHubAvailable is used for checking if hub already exist or not
func (c *chaosHubService) IsChaosHubAvailable(ctx context.Context, name string, projectID string) (bool, error) {
chaosHubs, err := c.chaosHubOperator.GetChaosHubByProjectID(ctx, projectID)
if err != nil {
return true, err
}
for _, n := range chaosHubs {
if n.Name == name {
return true, nil
}
}
return false, nil
}
// GetAllHubs ...
func (c *chaosHubService) GetAllHubs(ctx context.Context) ([]*model.ChaosHub, error) {
chaosHubs, err := c.chaosHubOperator.GetHubs(ctx)
if err != nil {
return nil, err
}
var outputChaosHubs []*model.ChaosHub
for _, chaosHub := range chaosHubs {
outputChaosHubs = append(outputChaosHubs, chaosHub.GetOutputChaosHub())
}
return outputChaosHubs, nil
}
// RecurringHubSync is used for syncing
func (c *chaosHubService) RecurringHubSync() {
for {
// Started Syncing of hubs
chaosHubs, _ := c.GetAllHubs(nil)
for _, chaosHub := range chaosHubs {
if !chaosHub.IsRemoved {
chartsInput := model.CloningInput{
Name: chaosHub.Name,
RepoURL: chaosHub.RepoURL,
RepoBranch: chaosHub.RepoBranch,
IsPrivate: chaosHub.IsPrivate,
AuthType: chaosHub.AuthType,
Token: chaosHub.Token,
UserName: chaosHub.UserName,
Password: chaosHub.Password,
SSHPrivateKey: chaosHub.SSHPrivateKey,
IsDefault: false,
}
if chaosHub.HubType != model.HubTypeRemote {
err := chaosHubOps.GitSyncHandlerForProjects(chartsInput, chaosHub.ProjectID)
if err != nil {
log.Error(err)
}
} else {
err := handler.SyncRemoteRepo(chartsInput, chaosHub.ProjectID)
if err != nil {
log.Error(err)
}
}
}
}
// Syncing Completed
time.Sleep(timeInterval)
}
}
// GetChaosHubStats returns stats related to Chaos Hubs
func (c *chaosHubService) GetChaosHubStats(ctx context.Context, projectID string) (*model.GetChaosHubStatsResponse, error) {
var pipeline mongo.Pipeline
// Match with identifiers
matchIdentifierStage := bson.D{
{"$match", bson.D{
{"project_id", projectID},
{"is_removed", false},
}},
}
facetStage := bson.D{
{"$facet", bson.D{
{"total_chaos_hubs", bson.A{
matchIdentifierStage,
bson.D{{"$count", "count"}},
}},
}},
}
pipeline = append(pipeline, facetStage)
// Call aggregation on pipeline
hubCursor, err := c.chaosHubOperator.GetAggregateChaosHubs(ctx, pipeline)
if err != nil {
return nil, err
}
var res []dbSchemaChaosHub.AggregatedChaosHubStats
if err = hubCursor.All(ctx, &res); err != nil || len(res) == 0 || len(res[0].TotalChaosHubs) == 0 {
return &model.GetChaosHubStatsResponse{
TotalChaosHubs: 1,
}, err
}
return &model.GetChaosHubStatsResponse{
TotalChaosHubs: res[0].TotalChaosHubs[0].Count + 1,
}, nil
}
func (c *chaosHubService) listDefaultHubs() *model.ChaosHub {
defaultHubs := &model.ChaosHub{
ID: DefaultHubID,
Name: "Litmus ChaosHub",
RepoURL: utils.Config.DefaultHubGitURL,
RepoBranch: utils.Config.DefaultHubBranchName,
IsDefault: true,
}
return defaultHubs
}
func (c *chaosHubService) SyncDefaultChaosHubs() {
log.Infof("syncing default chaos hub directories")
for {
defaultHub := c.listDefaultHubs()
chartsInput := model.CloningInput{
Name: defaultHub.Name,
RepoURL: defaultHub.RepoURL,
RepoBranch: defaultHub.RepoBranch,
IsDefault: true,
}
err := chaosHubOps.GitSyncDefaultHub(chartsInput)
if err != nil {
log.WithFields(log.Fields{
"repoUrl": defaultHub.RepoURL,
"repoBranch": defaultHub.RepoBranch,
"hubName": defaultHub.Name,
}).WithError(err).Error("failed to sync default chaos hubs")
}
// Syncing Completed
time.Sleep(DefaultHubSyncTimeInterval)
}
}