diff --git a/api/bean/AppView.go b/api/bean/AppView.go index ba54f8494c..91a928c5ea 100644 --- a/api/bean/AppView.go +++ b/api/bean/AppView.go @@ -138,6 +138,7 @@ type AppEnvironmentContainer struct { TeamName string `json:"teamName"` Description string `json:"description" validate:"max=40"` TotalCount int `json:"-"` + Commits []string `json:"commits"` } type DeploymentDetailContainer struct { @@ -197,22 +198,25 @@ type Notes struct { } type Environment struct { - AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only - EnvironmentId int `json:"environmentId"` - EnvironmentName string `json:"environmentName"` - AppMetrics *bool `json:"appMetrics"` - InfraMetrics *bool `json:"infraMetrics"` - Prod bool `json:"prod"` - ChartRefId int `json:"chartRefId"` - LastDeployed string `json:"lastDeployed"` - LastDeployedBy string `json:"lastDeployedBy"` - LastDeployedImage string `json:"lastDeployedImage"` - DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` - Description string `json:"description" validate:"max=40"` - IsVirtualEnvironment bool `json:"isVirtualEnvironment"` - ClusterId int `json:"clusterId"` - PipelineId int `json:"pipelineId"` - LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"` + AppStatus string `json:"appStatus"` //this is not the status of environment , this make sense with a specific app only + EnvironmentId int `json:"environmentId"` + EnvironmentName string `json:"environmentName"` + AppMetrics *bool `json:"appMetrics"` + InfraMetrics *bool `json:"infraMetrics"` + Prod bool `json:"prod"` + ChartRefId int `json:"chartRefId"` + LastDeployed string `json:"lastDeployed"` + LastDeployedBy string `json:"lastDeployedBy"` + LastDeployedImage string `json:"lastDeployedImage"` + DeploymentAppDeleteRequest bool `json:"deploymentAppDeleteRequest"` + Description string `json:"description" validate:"max=40"` + IsVirtualEnvironment bool `json:"isVirtualEnvironment"` + ClusterId int `json:"clusterId"` + PipelineId int `json:"pipelineId"` + LatestCdWorkflowRunnerId int `json:"latestCdWorkflowRunnerId,omitempty"` + CiArtifactId int `json:"ciArtifactId"` + ParentCiArtifactId int `json:"-"` + Commits []string `json:"commits"` } type InstanceDetail struct { diff --git a/api/restHandler/app/appList/AppListingRestHandler.go b/api/restHandler/app/appList/AppListingRestHandler.go index 82a10e10c6..b711653194 100644 --- a/api/restHandler/app/appList/AppListingRestHandler.go +++ b/api/restHandler/app/appList/AppListingRestHandler.go @@ -448,7 +448,7 @@ func (handler AppListingRestHandlerImpl) FetchOverviewAppsByEnvironment(w http.R common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } - resp, err := handler.appListingService.FetchOverviewAppsByEnvironment(envId, limit, offset) + resp, err := handler.appListingService.FetchOverviewAppsByEnvironment(envId, limit, offset, r.Context()) if err != nil { handler.logger.Errorw("error in getting apps for app-group overview", "envid", envId, "limit", limit, "offset", offset) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) diff --git a/internal/sql/repository/AppListingRepository.go b/internal/sql/repository/AppListingRepository.go index 4de732409e..6e0783c4bd 100644 --- a/internal/sql/repository/AppListingRepository.go +++ b/internal/sql/repository/AppListingRepository.go @@ -54,7 +54,7 @@ type AppListingRepository interface { DeploymentDetailByArtifactId(ciArtifactId int, envId int) (bean.DeploymentDetailContainer, error) FindAppCount(isProd bool) (int, error) FetchAppsByEnvironmentV2(appListingFilter helper.AppListingFilter) ([]*bean.AppEnvironmentContainer, int, error) - FetchOverviewAppsByEnvironment(envId, limit, offset int) ([]*bean.AppEnvironmentContainer, error) + FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) ([]*bean.AppEnvironmentContainer, error) FetchLastDeployedImage(appId, envId int) (*LastDeployed, error) } @@ -76,6 +76,8 @@ type AppNameTypeIdContainerDBResponse struct { } type LastDeployed struct { + ParentCiArtifact int `sql:"parent_ci_artifact_id"` + CiArtifactId int `sql:"ci_artifact_id"` LastDeployedBy string `sql:"last_deployed_by"` LastDeployedImage string `sql:"last_deployed_image"` } @@ -133,7 +135,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewCiPipelines(jobId int) ([]*bea return jobContainers, nil } -func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int) ([]*bean.AppEnvironmentContainer, error) { +func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) ([]*bean.AppEnvironmentContainer, error) { query := " SELECT a.id as app_id,a.app_name,aps.status as app_status, ld.last_deployed_time " + " FROM app a " + " INNER JOIN pipeline p ON p.app_id = a.id and p.deleted = false and p.environment_id = ? " + @@ -157,7 +159,7 @@ func (impl AppListingRepositoryImpl) FetchOverviewAppsByEnvironment(envId, limit func (impl AppListingRepositoryImpl) FetchLastDeployedImage(appId, envId int) (*LastDeployed, error) { var lastDeployed []*LastDeployed // we are adding a case in the query to concatenate the string "(inactive)" to the users' email id when user is inactive - query := `select ca.image as last_deployed_image, + query := `select ca.id as ci_artifact_id, ca.parent_ci_artifact as parent_ci_artifact_id, ca.image as last_deployed_image, case when u.active = false then u.email_id || ' (inactive)' else u.email_id @@ -605,7 +607,7 @@ func (impl AppListingRepositoryImpl) FetchOtherEnvironment(appId int) ([]*bean.E //TODO: remove infra metrics from query as it is not being used from here query := `select pcwr.pipeline_id, pcwr.last_deployed, pcwr.latest_cd_workflow_runner_id, pcwr.environment_id, pcwr.deployment_app_delete_request, e.cluster_id, e.environment_name, e.default as prod, e.description, ca.image as last_deployed_image, - u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status + u.email_id as last_deployed_by, elam.app_metrics, elam.infra_metrics, ap.status as app_status, ca.id as ci_artifact_id, ca.parent_ci_artifact as parent_ci_artifact_id from (select * from (select p.id as pipeline_id, p.app_id, cwr.started_on as last_deployed, cwr.triggered_by, cwr.id as latest_cd_workflow_runner_id, cw.ci_artifact_id, p.environment_id, p.deployment_app_delete_request, diff --git a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go index 338032227a..3ef5d3c096 100644 --- a/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go +++ b/internal/sql/repository/pipelineConfig/CiWorkflowRepository.go @@ -18,9 +18,11 @@ package pipelineConfig import ( + "context" "fmt" "github.com/devtron-labs/devtron/internal/sql/repository/helper" "github.com/go-pg/pg" + "go.opentelemetry.io/otel" "go.uber.org/zap" "time" ) @@ -48,6 +50,7 @@ type CiWorkflowRepository interface { ExistsByStatus(status string) (bool, error) FindBuildTypeAndStatusDataOfLast1Day() []*BuildTypeCount FIndCiWorkflowStatusesByAppId(appId int) ([]*CiWorkflowStatus, error) + FindGitTriggersByArtifactIds(ciArtifactIds []int, ctx context.Context) (ciWorkflows []ArtifactAndGitCommitMapping, err error) } type CiWorkflowRepositoryImpl struct { @@ -120,6 +123,12 @@ type WorkflowWithArtifact struct { ImagePathReservationIds []int `json:"image_path_reservation_ids" pg:",array"` } +type ArtifactAndGitCommitMapping struct { + //Id int `sql:"id,pk"` + GitTriggers map[int]GitCommit `sql:"git_triggers"` + ArtifactId int `sql:"artifact_id"` +} + type GitCommit struct { Commit string //git hash Author string @@ -287,6 +296,25 @@ func (impl *CiWorkflowRepositoryImpl) FindLastTriggeredWorkflowByArtifactId(ciAr return workflow, err } +func (impl *CiWorkflowRepositoryImpl) FindGitTriggersByArtifactIds(ciArtifactIds []int, ctx context.Context) (ciWorkflows []ArtifactAndGitCommitMapping, err error) { + _, span := otel.Tracer("envOverrideRepository").Start(ctx, "fetchCiArtifactAndGitTriggersMapping") + defer span.End() + if ciArtifactIds == nil { + // If ciArtifactIds is nil or empty, return an empty slice and nil error + return []ArtifactAndGitCommitMapping{}, err + } + var workflows []ArtifactAndGitCommitMapping + err = impl.dbConnection.Model(). + Table("ci_workflow"). + Column("ci_workflow.git_triggers"). + ColumnExpr("cia.id as artifact_id"). + Join("INNER JOIN ci_artifact cia on cia.ci_workflow_id = ci_workflow.id"). + Where("cia.id in (?) ", pg.In(ciArtifactIds)). + Select(&workflows) + + return workflows, nil +} + func (impl *CiWorkflowRepositoryImpl) FindAllLastTriggeredWorkflowByArtifactId(ciArtifactIds []int) (ciWorkflows []*CiWorkflow, err error) { err = impl.dbConnection.Model(&ciWorkflows). Column("ci_workflow.git_triggers", "ci_workflow.ci_pipeline_id", "CiPipeline", "cia.id"). diff --git a/pkg/app/AppListingService.go b/pkg/app/AppListingService.go index f4b09f2e22..ada7ae3abb 100644 --- a/pkg/app/AppListingService.go +++ b/pkg/app/AppListingService.go @@ -71,7 +71,7 @@ type AppListingService interface { GetReleaseCount(appId, envId int) (int, error) FetchAppsByEnvironmentV2(fetchAppListingRequest FetchAppListingRequest, w http.ResponseWriter, r *http.Request, token string) ([]*bean.AppEnvironmentContainer, int, error) - FetchOverviewAppsByEnvironment(envId, limit, offset int) (*OverviewAppsByEnvironmentBean, error) + FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) (*OverviewAppsByEnvironmentBean, error) } const ( @@ -99,6 +99,10 @@ type AppNameTypeIdContainer struct { Type string `json:"type"` AppId int `json:"appId"` } +type CiArtifactWithParentArtifact struct { + ParentCiArtifact int + CiArtifactId int +} func (req FetchAppListingRequest) GetNamespaceClusterMapping() (namespaceClusterPair []*repository2.ClusterNamespacePair, clusterIds []int, err error) { for _, ns := range req.Namespaces { @@ -143,6 +147,7 @@ type AppListingServiceImpl struct { dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService userRepository userrepository.UserRepository deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService + ciWorkflowRepository pipelineConfig.CiWorkflowRepository } func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository repository.AppListingRepository, @@ -153,7 +158,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re argoUserService argo.ArgoUserService, envOverrideRepository chartConfig.EnvConfigOverrideRepository, chartRepository chartRepoRepository.ChartRepository, ciPipelineRepository pipelineConfig.CiPipelineRepository, dockerRegistryIpsConfigService dockerRegistry.DockerRegistryIpsConfigService, userRepository userrepository.UserRepository, - deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService) *AppListingServiceImpl { + deployedAppMetricsService deployedAppMetrics.DeployedAppMetricsService, ciWorkflowRepository pipelineConfig.CiWorkflowRepository) *AppListingServiceImpl { serviceImpl := &AppListingServiceImpl{ Logger: Logger, appListingRepository: appListingRepository, @@ -172,6 +177,7 @@ func NewAppListingServiceImpl(Logger *zap.SugaredLogger, appListingRepository re dockerRegistryIpsConfigService: dockerRegistryIpsConfigService, userRepository: userRepository, deployedAppMetricsService: deployedAppMetricsService, + ciWorkflowRepository: ciWorkflowRepository, } return serviceImpl } @@ -198,7 +204,7 @@ const ( NonProduction = "Non-Production" ) -func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int) (*OverviewAppsByEnvironmentBean, error) { +func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, offset int, ctx context.Context) (*OverviewAppsByEnvironmentBean, error) { resp := &OverviewAppsByEnvironmentBean{} env, err := impl.environmentRepository.FindById(envId) if err != nil { @@ -229,20 +235,47 @@ func (impl AppListingServiceImpl) FetchOverviewAppsByEnvironment(envId, limit, o resp.CreatedBy = fmt.Sprintf("%s (inactive)", createdBy.EmailId) } } - envContainers, err := impl.appListingRepository.FetchOverviewAppsByEnvironment(envId, limit, offset) + envContainers, err := impl.appListingRepository.FetchOverviewAppsByEnvironment(envId, limit, offset, ctx) if err != nil { impl.Logger.Errorw("failed to fetch environment containers", "err", err, "envId", envId) return resp, err } + + artifactDetails := make([]*CiArtifactWithParentArtifact, 0) for _, envContainer := range envContainers { lastDeployed, err := impl.appListingRepository.FetchLastDeployedImage(envContainer.AppId, envId) if err != nil { impl.Logger.Errorw("failed to fetch last deployed image", "err", err, "appId", envContainer.AppId, "envId", envId) return resp, err } + if lastDeployed != nil { envContainer.LastDeployedImage = lastDeployed.LastDeployedImage envContainer.LastDeployedBy = lastDeployed.LastDeployedBy + artifactDetails = append(artifactDetails, &CiArtifactWithParentArtifact{ + ParentCiArtifact: lastDeployed.ParentCiArtifact, CiArtifactId: lastDeployed.CiArtifactId, + }) + envContainer.CiArtifactId = lastDeployed.CiArtifactId + } + } + + artifactWithGitCommit, err := impl.fetchCiArtifactAndGitTriggersMapping(artifactDetails, ctx) + if err != nil { + impl.Logger.Errorw("failed to fetch Artifacts to git Triggers ", "err", err, "envId", envId) + return resp, err + } + for _, envContainer := range envContainers { + if envContainer.CiArtifactId > 0 { + if commits, ok := artifactWithGitCommit[envContainer.CiArtifactId]; ok && commits != nil { + envContainer.Commits = commits + } else { + envContainer.Commits = []string{} + + } + + } else { + envContainer.Commits = []string{} + } } resp.Apps = envContainers @@ -769,6 +802,85 @@ func (impl AppListingServiceImpl) FetchAppStageStatus(appId int, appType int) ([ appStageStatuses, err := impl.appListingRepository.FetchAppStageStatus(appId, appType) return appStageStatuses, err } +func (impl AppListingServiceImpl) fetchCiArtifactAndGitTriggersMapping(artifacts []*CiArtifactWithParentArtifact, ctx context.Context) (CiArtifactAndGitCommitsMap map[int][]string, err error) { + newCtx, span := otel.Tracer("envOverrideRepository").Start(ctx, "fetchCiArtifactAndGitTriggersMapping") + defer span.End() + ciArtifactIds, artifactChildParentMap, err := mapCiArtifactsWithChild(artifacts) + if err != nil { + // Log the error along with the artifact IDs that caused it. + impl.Logger.Errorw("error in getting the set of ciArtifactIds", "ArtifactIds", ciArtifactIds, "err", err) + return nil, err // Return nil map and the encountered error. + } + + // Retrieve workflows associated with the artifact IDs, handling any potential error. + artifactsWithGitTriggers, err := impl.ciWorkflowRepository.FindGitTriggersByArtifactIds(ciArtifactIds, newCtx) + if err != nil { + // Log the error along with the artifact IDs that caused it. + impl.Logger.Errorw("error retrieving GitTriggers of the CiWorkflows", "ciArtifactIds", ciArtifactIds, "err", err) + return nil, err // Return nil map and the encountered error. + } + + gitCommitsWithChildArtifactMap, err := mapArtifactToGitCommits(artifactsWithGitTriggers, artifactChildParentMap) + if err != nil { + // Log the error along with the artifact child IDs . + impl.Logger.Errorw("error retrieving GitCommits of these ids", "CiArtifactIds", ciArtifactIds, "err", err) + return nil, err + } + + return gitCommitsWithChildArtifactMap, nil +} + +func mapCiArtifactsWithChild(artifacts []*CiArtifactWithParentArtifact) (ciArtifactIds []int, artifactChildParentMap map[int]int, err error) { + artifactChildParentMap = make(map[int]int) + + uniqueArtifactsMap := make(map[int]struct{}) + for _, artifact := range artifacts { + if artifact.ParentCiArtifact > 0 { + artifactChildParentMap[artifact.CiArtifactId] = artifact.ParentCiArtifact + } else { + artifactChildParentMap[artifact.CiArtifactId] = artifact.CiArtifactId + } + + if _, ok := uniqueArtifactsMap[artifactChildParentMap[artifact.CiArtifactId]]; !ok { + ciArtifactIds = append(ciArtifactIds, artifactChildParentMap[artifact.CiArtifactId]) + uniqueArtifactsMap[artifactChildParentMap[artifact.CiArtifactId]] = struct{}{} + } + } + return ciArtifactIds, artifactChildParentMap, err + +} + +// mapArtifactToGitCommits +func mapArtifactToGitCommits(artifactsWithGitTriggers []pipelineConfig.ArtifactAndGitCommitMapping, artifactChildParentMap map[int]int) (ciArtifactAndGitCommitsMap map[int][]string, err error) { + + gitTriggers := make(map[int][]string) + + for _, artifactWithGitTriggers := range artifactsWithGitTriggers { + if artifactWithGitTriggers.ArtifactId == 0 { + continue + } + + // Check if gitTriggers contains the key artifactWithGitTriggers.ArtifactId + if _, ok := gitTriggers[artifactWithGitTriggers.ArtifactId]; !ok { + for _, gitCommit := range artifactWithGitTriggers.GitTriggers { + gitTriggers[artifactWithGitTriggers.ArtifactId] = append(gitTriggers[artifactWithGitTriggers.ArtifactId], gitCommit.Commit) + } + } + } + + ciArtifactAndGitCommitsMap = make(map[int][]string) + + for child, parent := range artifactChildParentMap { + gitCommits, exists := gitTriggers[parent] + if !exists { + ciArtifactAndGitCommitsMap[child] = []string{} + } else { + ciArtifactAndGitCommitsMap[child] = append(ciArtifactAndGitCommitsMap[child], gitCommits...) + } + } + return ciArtifactAndGitCommitsMap, err + +} func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, appId int) ([]*bean.Environment, error) { newCtx, span := otel.Tracer("appListingRepository").Start(ctx, "FetchOtherEnvironment") @@ -793,6 +905,23 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app impl.Logger.Errorw("error in fetching latest chart", "err", err) return envs, err } + ciArtifactsWithParent := make([]*CiArtifactWithParentArtifact, 0) + for _, env := range envs { + + if env.CiArtifactId > 0 { + ciArtifactsWithParent = append(ciArtifactsWithParent, &CiArtifactWithParentArtifact{ + ParentCiArtifact: env.ParentCiArtifactId, CiArtifactId: env.CiArtifactId, + }) + } + + } + + gitCommitsWithArtifacts, err := impl.fetchCiArtifactAndGitTriggersMapping(ciArtifactsWithParent, ctx) + if err != nil { + impl.Logger.Errorw("Error in fetching the git commits of the ciArtifacts", "err", err, "ciArtifactsWithParent", ciArtifactsWithParent) + return envs, err + } + for _, env := range envs { newCtx, span = otel.Tracer("envOverrideRepository").Start(newCtx, "FindLatestChartForAppByAppIdAndEnvId") envOverride, err := impl.envOverrideRepository.FindLatestChartForAppByAppIdAndEnvId(appId, env.EnvironmentId) @@ -806,6 +935,14 @@ func (impl AppListingServiceImpl) FetchOtherEnvironment(ctx context.Context, app } else { env.ChartRefId = chart.ChartRefId } + + if gitCommits, exists := gitCommitsWithArtifacts[env.CiArtifactId]; exists { + env.Commits = gitCommits + } else { + gitCommits = make([]string, 0) + env.Commits = gitCommits + } + if env.AppMetrics == nil { env.AppMetrics = &appLevelAppMetrics } diff --git a/pkg/plugin/GlobalPluginService.go b/pkg/plugin/GlobalPluginService.go index 3698c8d793..16e9186a30 100644 --- a/pkg/plugin/GlobalPluginService.go +++ b/pkg/plugin/GlobalPluginService.go @@ -726,7 +726,6 @@ func (impl *GlobalPluginServiceImpl) updatePlugin(pluginUpdateReq *PluginMetadat if len(pluginUpdateReq.Type) == 0 { return nil, errors.New("invalid plugin type, should be of the type PRESET or SHARED") } - dbConnection := impl.globalPluginRepository.GetConnection() tx, err := dbConnection.Begin() if err != nil { @@ -814,6 +813,7 @@ func (impl *GlobalPluginServiceImpl) updatePlugin(pluginUpdateReq *PluginMetadat return nil, err } } + if len(pluginStepsToUpdate) > 0 { err = impl.updateDeepPluginStepData(pluginStepsToUpdate, pluginStepVariables, pluginStepConditions, pluginSteps, userId, tx) if err != nil { @@ -1344,7 +1344,6 @@ func filterPluginStepData(existingPluginStepsInDb []*repository.PluginStep, plug } else { return nil, nil, pluginStepUpdateReq } - return newPluginStepsToCreate, pluginStepsToRemove, pluginStepsToUpdate } diff --git a/specs/global-plugin-system.yaml b/specs/global-plugin-system.yaml new file mode 100644 index 0000000000..a169176ad9 --- /dev/null +++ b/specs/global-plugin-system.yaml @@ -0,0 +1,615 @@ +openapi: "3.0.3" +info: + title: "Plugin System Integration - CI Stages" + description: | + This API facilitates the management of plugins used in pre/post CI or CD steps, + enhancing the customization and automation capabilities of CI/CD pipelines. + version: "1.0.0" + +paths: + /orchestrator/plugin/global/detail/{pluginId}: + get: + description: > + Retrieve detailed information about a specific plugin by its ID. Before proceeding to Patch Plugin, ensure to retrieve the plugin details using this endpoint as the same object will be used in the patch action of the global plugin. + parameters: + - name: pluginId + in: path + required: true + schema: + type: integer + description: Unique identifier of the plugin + + responses: + '200': + description: successfully return the Detailed information about the plugin + content: + application/json: + schema: + properties: + code: + type: integer + description: status code + status: + type: string + description: status + result: + $ref: '#/components/schemas/PluginMetaDataDto' + '400': + description: Bad request, Input Validation error/wrong request body. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized, not found or invalid API token provided + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + '403': + description: Forbidden, user is not allowed to access this plugin information + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: Not Found, the plugin with the specified ID does not exist + + '500': + description: Internal server error, could not retrieve plugin details + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /orchestrator/plugin/global/detail/all: + get: + description: > + Get detailed information of available plugins. + operationId: GetAllDetailedPluginInfo + responses: + '200': + description: A list of plugins with detailed information + content: + application/json: + schema: + properties: + code: + type: integer + description: status code + status: + type: string + description: status + result: + type: array + items: + $ref: '#/components/schemas/PluginMetaDataDto' + '401': + description: Unauthorized user, Invalid or missing API token + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Forbidden, user is not authorized to access this resource + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error, could not fetch the plugin details + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + + + /orchestrator/plugin/global/: + post: + summary: "Manipulating the Global Plugin" + description: | + Allows for the management (creation, update, or deletion) of global plugins through a single endpoint. This endpoint is versatile and supports various actions based on the provided `action` field in the request payload. Before performing any action, ensure to retrieve detailed information about the plugin by its ID using the endpoint `/orchestrator/plugin/global/detail/{pluginId}`. The same or modified object retrieved can be used in the request payload for this endpoint. + operationId: "PatchPlugin" + requestBody: + description: "A JSON Object containing the PluginMetaData fields, including the specific action to be performed." + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PluginMetaDataDto' + responses: + '200': + description: "Successfully processed the global plugin operation. The response includes the updated plugin data." + content: + application/json: + schema: + $ref: '#/components/schemas/PluginMetaDataDto' + '400': + description: "Bad Request due to input validation errors or incorrect request body format." + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: "Unauthorized access attempt. This may occur if the user does not have the necessary permissions for the operation." + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: "Internal Server Error indicating an unexpected condition that prevented the request from being fulfilled." + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + /orchestrator/plugin/global/list/global-variable: + get: + description: Get list of all global variables. + operationId: GetAllGlobalVariables + parameters: + - name: appId + in: query + required: true + schema: + type: string + description: The ID of the application for which global variables are retrieved. + + responses: + '200': + description: Successfully returns all global variables. + content: + application/json: + schema: + type: object + properties: + code: + type: integer + description: Status code. + status: + type: string + description: Status. + result: + type: array + items: + $ref: '#/components/schemas/GlobalVariable' + '400': + description: Bad Request. Input validation error or wrong request body. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: Unauthorized User. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal Server Error. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + + + + +components: + schemas: + GlobalVariable: + type: object + properties: + name: + type: string + description: The name of the global variable. + value: + type: string + description: Optional.The value of the global variable. + format: + type: string + description: The format of the value. + description: + type: string + description: A description of the global variable and its purpose. + stageType: + type: string + description: The type of stage this global variable is associated with. + required: + - name + - format + - description + - type + + + PluginMetaDataDto: + type: object + properties: + id: + type: integer + description: Unique identifier for the plugin. + name: + type: string + description: Name of the plugin. + description: + type: string + description: Detailed description of what the plugin does. + type: + type: string + enum: [SHARED, PRESET] + description: > + Type of the plugin indicating whether it's a SHARED plugin accessible by all users or a PRESET plugin provided by the system. + icon: + type: string + description: URL or a base64 encoded string representing an icon for the plugin + example: https://raw.githubusercontent.com/devtron-labs/devtron/main/assets/k6-plugin-icon.png + tags: + type: array + items: + type: string + description: A list of tags associated with the plugin for categorization and search. + action: + type: integer + description: | + This field represents the action to be performed with this plugin metadata. + - Use `0` to create a new plugin. + - Use `1` to update an existing plugin. + - Use `2` to delete a plugin. + + If you're opting to update an existing plugin (action `1`), + please take note: + - Ensure that the request body parameters are accurate before updating. + - Your request body (payload) will discard all previous updates and be treated as final. + + pluginStage: + type: string + description: Optional. Specifies the stage of the CI/CD pipeline or both (CI/CD) where this plugin can be used. + pluginSteps: + type: array + items: + $ref: '#/components/schemas/PluginStepsDto' + description: Optional, A list of steps defined for the plugin. Each step contains specific instructions or actions the plugin will perform. + required: + - name + - description + - type + - icon + - tags + - action + + PluginStepsDto: + type: object + properties: + id: + type: integer + description: Unique identifier of the plugin step. + name: + type: string + description: Name of the plugin step. + description: + type: string + description: Detailed description of what the plugin step does. + index: + type: integer + description: The order index of the plugin step within the plugin. + stepType: + type: string + enum: [INLINE, REF_PLUGIN] + description: Type of the plugin step, indicating whether it's an INLINE step defined within the plugin or a REF_PLUGIN step referencing another plugin. + refPluginId: + type: integer + description: Unique identifier of the plugin used as reference by this step. + outputDirectoryPath: + type: array + items: + type: string + description: Paths to directories where the output of the plugin step should be stored. + dependentOnStep: + type: string + description: Identifier of the step, this step depends on to run. It can be used to establish execution order. + pluginStepVariable: + type: array + items: + $ref: '#/components/schemas/PluginVariableDto' + description: Optional. A list of variables associated with this plugin step. + pluginPipelineScript: + allOf: + - $ref: '#/components/schemas/PluginPipelineScript' + - description: Script associated with this plugin step to be executed as part of the pipeline. Optional. + required: + - name + - description + - index + - stepType + - refPluginId + - outputDirectoryPath + - dependentOnStep + + PluginVariableDto: + type: object + properties: + id: + type: integer + description: The unique identifier of the plugin variable. + name: + type: string + description: The name of the plugin variable. + format: + type: string + description: The format of the variable value. + enum: + - STRING + - NUMBER + - BOOL + - DATE + example: + - STRING + description: + type: string + description: A description of the plugin variable. + isExposed: + type: boolean + description: Indicates whether the variable is exposed. + allowEmptyValue: + type: boolean + description: Indicates whether an empty value is allowed for the variable. + defaultValue: + type: string + description: The default value of the variable. + value: + type: string + description: The value of the variable. + variableType: + type: string + description: | + The type of the variable. This specifies whether the variable is required by the plugin (Marked as INPUT type) or whether that variable is produced by the plugin (Marked as OUTPUT type). + enum: + - OUTPUT + - INPUT + example: + - INPUT + valueType: + type: string + description: | + The value type of the variable. Specifies whether the plugin uses a new value provided by the user (marked as NEW), retrieves the value from the previous step (marked as FROM_PREVIOUS_STEP), or fetches a global value (marked as GLOBAL). + This indicates whether the plugin utilizes a new user-provided value, a value from a previous step, or a global value. + enum: + - NEW + - FROM_PREVIOUS_STEP + - GLOBAL + example: + - NEW + previousStepIndex: + type: integer + description: The index of the previous step. + variableStepIndex: + type: integer + description: The index of the variable step. + variableStepIndexInPlugin: + type: integer + description: The index of the variable step in the plugin. + referenceVariableName: + type: string + description: The name of the reference variable. + pluginStepCondition: + type: array + items: + allOf: + - $ref: '#/components/schemas/PluginStepCondition' + - description: The conditions associated with the plugin variable. + required: + - name + - format + - description + - isExposed + - allowEmptyValue + - defaultValue + - variableType + - variableStepIndex + - variableStepIndexInPlugin + + + PluginStepCondition: + type: object + properties: + id: + type: integer + description: The unique identifier of the plugin step condition. + pluginStepId: + type: integer + description: The identifier of the plugin step associated with this condition. + conditionVariableId: + type: integer + description: The identifier of the variable on which the condition is written. + conditionType: + type: string + description: > + The type of condition. + Possible values are: + - SKIP: Skips the plugin step. + - TRIGGER: Triggers the plugin step. + - SUCCESS: Executes the plugin step on success. + - FAIL: Executes the plugin step on failure. + enum: + - SKIP + - TRIGGER + - SUCCESS + - FAIL + example: SKIP + conditionalOperator: + type: string + description: The operator used in the condition. + conditionalValue: + type: string + description: The value associated with the condition. + deleted : + type: boolean + description: Specifies whether the condition is deleted. + required: + - pluginStepId + - conditionVariableId + - conditionType + - conditionalOperator + - conditionalValue + - deleted + + PluginPipelineScript: + type: object + properties: + id: + type: integer + description: The unique identifier of the plugin pipeline script. + script: + type: string + description: The script associated with the plugin pipeline. + storeScriptAt: + type: string + description: The location where the script is stored. + type: + type: string + description: > + Specifies the type of script. + Possible values are: + - SHELL: Shell script. + - DOCKERFILE: Dockerfile script. + - CONTAINER_IMAGE: Container image script. + enum: + - SHELL + - DOCKERFILE + - CONTAINER_IMAGE + example: + - SHELL + dockerfileExists: + type: boolean + description: Indicates whether a Dockerfile exists for the script. + mountPath: + type: string + description: The path where the script is mounted. + mountCodeToContainer: + type: boolean + description: Indicates whether code is mounted to the container. + mountCodeToContainerPath: + type: string + description: The path where code is mounted to the container. + mountDirectoryFromHost: + type: boolean + description: Indicates whether a directory is mounted from the host. + containerImagePath: + type: string + description: The path to the container image. + imagePullSecretType: + type: string + description: > + Specifies the type of image pull secret. + Possible values are: + - CONTAINER_REGISTRY: Container registry image pull secret. + - SECRET_PATH: Secret path image pull secret. + enum: + - CONTAINER_REGISTRY + - SECRET_PATH + example: + - CONTAINER_REGISTRY + + imagePullSecret: + type: string + description: The image pull secret. + deleted: + type: boolean + description: Indicates whether the plugin pipeline script is deleted. + pathArgPortMapping: + type: array + items: + $ref: '#/components/schemas/ScriptPathArgPortMapping' + description: The path argument port mappings associated with the plugin pipeline script. + required: + - script + - storeScriptAt + - type + - dockerfileExists + - mountPath + - mountCodeToContainer + - mountCodeToContainerPath + - mountDirectoryFromHost + - containerImagePath + - imagePullSecretType + - imagePullSecret + - deleted + - pathArgPortMapping + + + ScriptPathArgPortMapping: + type: object + properties: + id: + type: integer + description: The unique identifier of the script path argument port mapping. + typeOfMapping: + type: string + description: > + Specifies the type of mapping. + Possible values are: + - FILE_PATH + - DOCKER_ARG + - PORT + enum: + - FILE_PATH + - DOCKER_ARG + - PORT + example: + - PORT + filePathOnDisk: + type: string + description: The file path on the local disk. + filePathOnContainer: + type: string + description: The file path on the container. + command: + type: string + description: The command associated with the mapping. + args: + type: array + items: + type: string + description: The arguments associated with the command. + portOnLocal: + type: integer + description: The port on the local machine. + portOnContainer: + type: integer + description: The port on the container. + scriptId: + type: integer + description: The identifier of the script associated with the mapping. + required: + - typeOfMapping + - filePathOnDisk + - filePathOnContainer + - command + - args + - portOnLocal + - portOnContainer + - scriptId + + + Error: + title: Error + type: object + description: "A general error schema returned when status is not 200 OK" + properties: + code: + type: string + description: "a code for this particular error" + message: + type: string + description: "a message with further detail" + + + + + + + + diff --git a/wire_gen.go b/wire_gen.go index 1d1e39f4ed..c870dfb9df 100644 --- a/wire_gen.go +++ b/wire_gen.go @@ -582,7 +582,7 @@ func InitializeApp() (*App, error) { appWorkflowServiceImpl := appWorkflow2.NewAppWorkflowServiceImpl(sugaredLogger, appWorkflowRepositoryImpl, ciCdPipelineOrchestratorImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, enforcerUtilImpl, resourceGroupServiceImpl, appRepositoryImpl, userAuthServiceImpl, chartServiceImpl) appListingViewBuilderImpl := app2.NewAppListingViewBuilderImpl(sugaredLogger) linkoutsRepositoryImpl := repository2.NewLinkoutsRepositoryImpl(sugaredLogger, db) - appListingServiceImpl := app2.NewAppListingServiceImpl(sugaredLogger, appListingRepositoryImpl, applicationServiceClientImpl, appRepositoryImpl, appListingViewBuilderImpl, pipelineRepositoryImpl, linkoutsRepositoryImpl, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, environmentRepositoryImpl, argoUserServiceImpl, envConfigOverrideRepositoryImpl, chartRepositoryImpl, ciPipelineRepositoryImpl, dockerRegistryIpsConfigServiceImpl, userRepositoryImpl, deployedAppMetricsServiceImpl) + appListingServiceImpl := app2.NewAppListingServiceImpl(sugaredLogger, appListingRepositoryImpl, applicationServiceClientImpl, appRepositoryImpl, appListingViewBuilderImpl, pipelineRepositoryImpl, linkoutsRepositoryImpl, cdWorkflowRepositoryImpl, pipelineOverrideRepositoryImpl, environmentRepositoryImpl, argoUserServiceImpl, envConfigOverrideRepositoryImpl, chartRepositoryImpl, ciPipelineRepositoryImpl, dockerRegistryIpsConfigServiceImpl, userRepositoryImpl, deployedAppMetricsServiceImpl, ciWorkflowRepositoryImpl) appCloneServiceImpl := appClone.NewAppCloneServiceImpl(sugaredLogger, pipelineBuilderImpl, chartServiceImpl, configMapServiceImpl, appWorkflowServiceImpl, appListingServiceImpl, propertiesConfigServiceImpl, pipelineStageServiceImpl, ciTemplateServiceImpl, appRepositoryImpl, ciPipelineRepositoryImpl, pipelineRepositoryImpl, ciPipelineConfigServiceImpl, gitOpsConfigReadServiceImpl) deploymentTemplateRepositoryImpl := repository2.NewDeploymentTemplateRepositoryImpl(db, sugaredLogger) generateManifestDeploymentTemplateServiceImpl := generateManifest.NewDeploymentTemplateServiceImpl(sugaredLogger, chartServiceImpl, appListingServiceImpl, appListingRepositoryImpl, deploymentTemplateRepositoryImpl, helmAppServiceImpl, chartRepositoryImpl, chartTemplateServiceImpl, helmAppClientImpl, k8sServiceImpl, propertiesConfigServiceImpl, deploymentTemplateHistoryServiceImpl, environmentRepositoryImpl, appRepositoryImpl, scopedVariableManagerImpl, chartRefServiceImpl)