diff --git a/CHANGELOG/release-notes-v1.8.0.md b/CHANGELOG/release-notes-v1.8.0.md new file mode 100644 index 0000000000..4aa73edc81 --- /dev/null +++ b/CHANGELOG/release-notes-v1.8.0.md @@ -0,0 +1,57 @@ +## v1.8.0 + +## Enhancements +- feat: added the plugin for aws ecr retagging (#6695) +- feat: Argo version update v2.13.3(#6817) +- feat: ArgoCD v2.13.3 support for private chart providers (#6766) +- feat: flux cd deployment (#6660) +- feat: add app name in labels list api (#6688) +- feat: Audit ci trigger, precd and post cd trigger so that retrigger can happen from last failed config snapshot (#6659) +- feat: optimize ci cd workflow (#6744) +- feat: automate API specs workflow and documentation (#6786) + +## Bugs +- fix: argo sync (#6718) +- fix: cluster delete (#6706) +- fix: Notifier v1 removed (#6705) +- fix: app clone panic (#6696) +- fix: Spdy migration to websocket (#6682) +- fix: Fix scanning optimisation (#6683) +- fix: panic in logs api (#6684) +- fix: Empty migration seq (#6673) +- fix: Removing default value of idleReplicaCount (#6721) +- fix: send HideApiToken env var in api token create and update response (#6727) +- fix: removed timeout notification (#6760) +- fix: unify unit formatting and conversion for CPU and memory in cluster resources (#6768) +- fix: cron job suspend (#6771) +- fix: panic app group (#6785) +- fix: api responses enhancements (#6797) +- fix: post apis minor validations (#6811) +- fix: dex scopes override default required scopes (#6816) +- fix: error handler correction (#6824) + +## Others +- chore: added sql file of 4.21 (#6716) +- misc: added support for service extraSpec (#6702) +- chore: when output dir path is /devtroncd in any pipeline stage step then the ci runner is stuck in recursive self-copy situation (#6686) +- misc: Removing default value of idleReplicaCount (#6730) +- chore: chart service crud and configProperties (#6642) +- misc: Added Github Actions for linting and building api specs (#6720) +- misc: open api spec addition (#6750) +- misc: Updated the build-docs.yaml (#6752) +- misc: fixing swagger openapi.yaml (#6751) +- misc: adding codeowner for swagger api spec (#6754) +- misc: swagger api spec fixes (#6755) +- misc: Update openapi spec (#6758) +- misc: updated the title of openapi specs (#6759) +- misc: Timeout main notification (#6761) +- misc: Api changes for env enhancement (#6769) +- misc: changes in description of api spec (#6767) +- chore: Update `authenticator` and `common-lib` dependencies to latest versions. (#6779) +- chore: added sql files for sync (#6780) +- chore: migration number chnage (#6783) +- chore: add resource recommendation APIs and update openapi.yaml (#6784) +- misc: added support for cronjob annotations and probes (#6787) +- misc: Validation on payload and Error handling, API Specs revised (#6790) +- misc: sql query param refacter (#6810) + diff --git a/api/appStore/chartGroup/ChartGroupRestHandler.go b/api/appStore/chartGroup/ChartGroupRestHandler.go index d8c35a16e6..1567f68570 100644 --- a/api/appStore/chartGroup/ChartGroupRestHandler.go +++ b/api/appStore/chartGroup/ChartGroupRestHandler.go @@ -191,7 +191,7 @@ func (impl *ChartGroupRestHandlerImpl) GetChartGroupWithChartMetaData(w http.Res } // Use enhanced parameter parsing with context - chartGroupId, err := common.ExtractIntPathParamWithContext(w, r, "chartGroupId", "chart group") + chartGroupId, err := common.ExtractIntPathParamWithContext(w, r, "chartGroupId") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -223,7 +223,7 @@ func (impl *ChartGroupRestHandlerImpl) GetChartGroupInstallationDetail(w http.Re } // Use enhanced parameter parsing with context - chartGroupId, err := common.ExtractIntPathParamWithContext(w, r, "chartGroupId", "chart group") + chartGroupId, err := common.ExtractIntPathParamWithContext(w, r, "chartGroupId") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/auth/user/UserRestHandler.go b/api/auth/user/UserRestHandler.go index 9fac7e4c98..de8093a1da 100644 --- a/api/auth/user/UserRestHandler.go +++ b/api/auth/user/UserRestHandler.go @@ -210,7 +210,7 @@ func (handler UserRestHandlerImpl) GetById(w http.ResponseWriter, r *http.Reques } // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "user") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -334,7 +334,7 @@ func (handler UserRestHandlerImpl) DeleteUser(w http.ResponseWriter, r *http.Req } // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "user") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -427,7 +427,7 @@ func (handler UserRestHandlerImpl) BulkDeleteUsers(w http.ResponseWriter, r *htt func (handler UserRestHandlerImpl) FetchRoleGroupById(w http.ResponseWriter, r *http.Request) { // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "role group") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/chartRepo/ChartRepositoryRestHandler.go b/api/chartRepo/ChartRepositoryRestHandler.go index ccd417827d..d8699a7fd6 100644 --- a/api/chartRepo/ChartRepositoryRestHandler.go +++ b/api/chartRepo/ChartRepositoryRestHandler.go @@ -85,7 +85,7 @@ func (handler *ChartRepositoryRestHandlerImpl) GetChartRepoById(w http.ResponseW } // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "chart repository") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/cluster/ClusterRestHandler.go b/api/cluster/ClusterRestHandler.go index d3d92e4db9..f092a6de84 100644 --- a/api/cluster/ClusterRestHandler.go +++ b/api/cluster/ClusterRestHandler.go @@ -356,7 +356,7 @@ func (impl ClusterRestHandlerImpl) FindByIds(w http.ResponseWriter, r *http.Requ func (impl ClusterRestHandlerImpl) FindById(w http.ResponseWriter, r *http.Request) { // Use enhanced parameter parsing with context - clusterId, err := common.ExtractIntPathParamWithContext(w, r, "id", "cluster") + clusterId, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -734,7 +734,7 @@ func (impl ClusterRestHandlerImpl) GetClusterNamespaces(w http.ResponseWriter, r isActionUserSuperAdmin = true } // extract cluster and handle response on error - clusterId, err := common.ExtractIntPathParamWithContext(w, r, "clusterId", "cluster") + clusterId, err := common.ExtractIntPathParamWithContext(w, r, "clusterId") if err != nil { impl.logger.Error("error in parsing clusterId", "clusterId", clusterId, "err", err) return diff --git a/api/cluster/EnvironmentRestHandler.go b/api/cluster/EnvironmentRestHandler.go index c7c81e9c0a..49094ad4c0 100644 --- a/api/cluster/EnvironmentRestHandler.go +++ b/api/cluster/EnvironmentRestHandler.go @@ -314,7 +314,7 @@ func (impl EnvironmentRestHandlerImpl) Update(w http.ResponseWriter, r *http.Req func (impl EnvironmentRestHandlerImpl) FindById(w http.ResponseWriter, r *http.Request) { // Use enhanced parameter parsing with context - envId, err := common.ExtractIntPathParamWithContext(w, r, "id", "environment") + envId, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/k8s/application/k8sApplicationRestHandler.go b/api/k8s/application/k8sApplicationRestHandler.go index 830895dd38..517bef5991 100644 --- a/api/k8s/application/k8sApplicationRestHandler.go +++ b/api/k8s/application/k8sApplicationRestHandler.go @@ -1002,14 +1002,19 @@ func (handler *K8sApplicationRestHandlerImpl) CreateEphemeralContainer(w http.Re common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } - if err = handler.validator.Struct(request); err != nil || (request.BasicData == nil && request.AdvancedData == nil) { - if err != nil { - err = errors.New("invalid request payload") - } - handler.logger.Errorw("invalid request payload", "err", err, "payload", request) + if request.BasicData == nil && request.AdvancedData == nil { + err = errors.New("invalid request payload, neither basic data nor advanced data provided") + handler.logger.Errorw("invalid request payload missing basic data and invalid data", "err", err, "payload", request) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } + err = handler.validator.Struct(request) + if err != nil { + handler.logger.Errorw("invalid request payload", "err", err, "payload", request) + //common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + common.HandleValidationErrors(w, r, err) + return + } // check for super admin restricted := handler.restrictTerminalAccessForNonSuperUsers(w, token) if restricted { @@ -1079,6 +1084,13 @@ func (handler *K8sApplicationRestHandlerImpl) DeleteEphemeralContainer(w http.Re _, err = handler.k8sApplicationService.TerminatePodEphemeralContainer(request) if err != nil { handler.logger.Errorw("error occurred in terminating ephemeral container", "err", err, "requestPayload", request) + if err.Error() == bean4.EXTERNAL_EPHIMERAL_CONTAINER_ERR { + common.WriteJsonResp(w, err, nil, http.StatusForbidden) + return + } else if err.Error() == bean4.EPHEMERAL_CONTAINER_NOT_FOUND_ERR { + common.WriteJsonResp(w, err, nil, http.StatusNotFound) + return + } common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } diff --git a/api/middleware/ErrorHandlingMiddleware.go b/api/middleware/ErrorHandlingMiddleware.go index a1309c5fb6..0d9508b459 100644 --- a/api/middleware/ErrorHandlingMiddleware.go +++ b/api/middleware/ErrorHandlingMiddleware.go @@ -19,12 +19,11 @@ package middleware import ( "context" "fmt" - "github.com/devtron-labs/devtron/internal/util" - "github.com/gorilla/mux" - "go.uber.org/zap" "net/http" - "strconv" "time" + + "github.com/gorilla/mux" + "go.uber.org/zap" ) // ErrorHandlingMiddleware provides enhanced error handling and logging for REST handlers @@ -156,47 +155,3 @@ func GetRequestContext(r *http.Request) *RequestContext { } return nil } - -// LogError logs an error with request context for better debugging -func (m *ErrorHandlingMiddleware) LogError(r *http.Request, err error, operation string) { - reqCtx := GetRequestContext(r) - if reqCtx != nil { - m.logger.Errorw("Request error", - "requestId", reqCtx.RequestID, - "operation", operation, - "error", err, - "method", reqCtx.Method, - "path", reqCtx.Path, - "resourceType", reqCtx.ResourceType, - "resourceId", reqCtx.ResourceID, - ) - } else { - m.logger.Errorw("Request error", - "operation", operation, - "error", err, - "method", r.Method, - "path", r.URL.Path, - ) - } -} - -// ValidateIntPathParam validates and extracts an integer path parameter with enhanced error handling -func ValidateIntPathParam(r *http.Request, paramName string) (int, *util.ApiError) { - vars := mux.Vars(r) - paramValue := vars[paramName] - - if paramValue == "" { - return 0, util.NewMissingRequiredFieldError(paramName) - } - - id, err := strconv.Atoi(paramValue) - if err != nil { - return 0, util.NewInvalidPathParameterError(paramName, paramValue) - } - - if id <= 0 { - return 0, util.NewValidationErrorForField(paramName, "must be a positive integer") - } - - return id, nil -} diff --git a/api/restHandler/CoreAppRestHandler.go b/api/restHandler/CoreAppRestHandler.go index 39e1934f00..2f0e6286c5 100644 --- a/api/restHandler/CoreAppRestHandler.go +++ b/api/restHandler/CoreAppRestHandler.go @@ -155,11 +155,9 @@ func (handler CoreAppRestHandlerImpl) GetAppAllDetail(w http.ResponseWriter, r * return } - vars := mux.Vars(r) - appId, err := strconv.Atoi(vars["appId"]) + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { - handler.logger.Errorw("request err, GetAppAllDetail", "err", err, "appId", vars["appId"]) - common.WriteInvalidAppIdError(w, vars["appId"]) + common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } diff --git a/api/restHandler/NotificationRestHandler.go b/api/restHandler/NotificationRestHandler.go index 621d615d15..582a8fab1f 100644 --- a/api/restHandler/NotificationRestHandler.go +++ b/api/restHandler/NotificationRestHandler.go @@ -220,13 +220,13 @@ func (impl NotificationRestHandlerImpl) DeleteNotificationSettings(w http.Respon func (impl NotificationRestHandlerImpl) GetAllNotificationSettings(w http.ResponseWriter, r *http.Request) { // Use enhanced parameter parsing with context - size, err := common.ExtractIntPathParamWithContext(w, r, "size", "pagination") + size, err := common.ExtractIntPathParamWithContext(w, r, "size") if err != nil { // Error already written by ExtractIntPathParamWithContext return } - offset, err := common.ExtractIntPathParamWithContext(w, r, "offset", "pagination") + offset, err := common.ExtractIntPathParamWithContext(w, r, "offset") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -522,7 +522,7 @@ func (impl NotificationRestHandlerImpl) FindSESConfig(w http.ResponseWriter, r * } // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "SES config") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -551,7 +551,7 @@ func (impl NotificationRestHandlerImpl) FindSlackConfig(w http.ResponseWriter, r } // Use enhanced parameter parsing with context - id, err := common.ExtractIntPathParamWithContext(w, r, "id", "Slack config") + id, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/restHandler/UserAttributesRestHandler.go b/api/restHandler/UserAttributesRestHandler.go index e88dee6e5a..ea2764de31 100644 --- a/api/restHandler/UserAttributesRestHandler.go +++ b/api/restHandler/UserAttributesRestHandler.go @@ -18,15 +18,14 @@ package restHandler import ( "encoding/json" - "errors" - "github.com/devtron-labs/devtron/pkg/attributes/bean" "net/http" + "github.com/devtron-labs/devtron/pkg/attributes/bean" + "github.com/devtron-labs/devtron/api/restHandler/common" "github.com/devtron-labs/devtron/pkg/attributes" "github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin" "github.com/devtron-labs/devtron/pkg/auth/user" - "github.com/gorilla/mux" "go.uber.org/zap" ) @@ -56,18 +55,32 @@ func NewUserAttributesRestHandlerImpl(logger *zap.SugaredLogger, enforcer casbin } func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseWriter, r *http.Request) { - dto, success := handler.validateUserAttributesRequest(w, r, "PatchUserAttributes") + dto, success := handler.validateUserAttributesRequest(w, r, "AddUserAttributes") if !success { return } - handler.logger.Infow("request payload, AddUserAttributes", "payload", dto) + handler.logger.Infow("Adding user attributes", + "operation", "add_user_attributes", + "userId", dto.UserId, + "key", dto.Key) + resp, err := handler.userAttributesService.AddUserAttributes(dto) if err != nil { - handler.logger.Errorw("service err, AddUserAttributes", "err", err, "payload", dto) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("Failed to add user attributes", + "operation", "add_user_attributes", + "userId", dto.UserId, + "key", dto.Key, + "err", err) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("user attributes creation"). + WithResource("user attribute", dto.Key) + errBuilder.HandleError(err) return } + common.WriteJsonResp(w, nil, resp, http.StatusOK) } @@ -78,18 +91,32 @@ func (handler *UserAttributesRestHandlerImpl) AddUserAttributes(w http.ResponseW // @Success 200 {object} attributes.UserAttributesDto // @Router /orchestrator/attributes/user/update [POST] func (handler *UserAttributesRestHandlerImpl) UpdateUserAttributes(w http.ResponseWriter, r *http.Request) { - dto, success := handler.validateUserAttributesRequest(w, r, "PatchUserAttributes") + dto, success := handler.validateUserAttributesRequest(w, r, "UpdateUserAttributes") if !success { return } - handler.logger.Infow("request payload, UpdateUserAttributes", "payload", dto) + handler.logger.Infow("Updating user attributes", + "operation", "update_user_attributes", + "userId", dto.UserId, + "key", dto.Key) + resp, err := handler.userAttributesService.UpdateUserAttributes(dto) if err != nil { - handler.logger.Errorw("service err, UpdateUserAttributes", "err", err, "payload", dto) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("Failed to update user attributes", + "operation", "update_user_attributes", + "userId", dto.UserId, + "key", dto.Key, + "err", err) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("user attributes update"). + WithResource("user attribute", dto.Key) + errBuilder.HandleError(err) return } + common.WriteJsonResp(w, nil, resp, http.StatusOK) } @@ -99,38 +126,68 @@ func (handler *UserAttributesRestHandlerImpl) PatchUserAttributes(w http.Respons return } - handler.logger.Infow("request payload, PatchUserAttributes", "payload", dto) + handler.logger.Infow("Patching user attributes", + "operation", "patch_user_attributes", + "userId", dto.UserId, + "key", dto.Key) + resp, err := handler.userAttributesService.PatchUserAttributes(dto) if err != nil { - handler.logger.Errorw("service err, PatchUserAttributes", "err", err, "payload", dto) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("Failed to patch user attributes", + "operation", "patch_user_attributes", + "userId", dto.UserId, + "key", dto.Key, + "err", err) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("user attributes patch"). + WithResource("user attribute", dto.Key) + errBuilder.HandleError(err) return } + common.WriteJsonResp(w, nil, resp, http.StatusOK) } func (handler *UserAttributesRestHandlerImpl) validateUserAttributesRequest(w http.ResponseWriter, r *http.Request, operation string) (*bean.UserAttributesDto, bool) { + // 1. Authentication check using enhanced error handling userId, err := handler.userService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return nil, false } + // 2. Request body parsing with enhanced error handling decoder := json.NewDecoder(r.Body) var dto bean.UserAttributesDto err = decoder.Decode(&dto) if err != nil { - handler.logger.Errorw("request err, "+operation, "err", err, "payload", dto) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + handler.logger.Errorw("Request parsing error", + "operation", operation, + "err", err, + "userId", userId) + + // Use enhanced error response builder for request parsing errors + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation(operation). + WithResource("user attributes", "") + errBuilder.HandleError(err) return nil, false } dto.UserId = userId + // 3. Get user email with enhanced error handling emailId, err := handler.userService.GetActiveEmailById(userId) if err != nil { - handler.logger.Errorw("request err, "+operation, "err", err, "payload", dto) - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) + handler.logger.Errorw("Failed to get user email", + "operation", operation, + "userId", userId, + "err", err) + + // Use enhanced error response for forbidden access + common.WriteForbiddenError(w, "access user attributes", "user") return nil, false } dto.EmailId = emailId @@ -145,36 +202,58 @@ func (handler *UserAttributesRestHandlerImpl) validateUserAttributesRequest(w ht // @Success 200 {object} attributes.UserAttributesDto // @Router /orchestrator/attributes/user/get [GET] func (handler *UserAttributesRestHandlerImpl) GetUserAttribute(w http.ResponseWriter, r *http.Request) { + // 1. Authentication check using enhanced error handling userId, err := handler.userService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } - vars := mux.Vars(r) - key := vars["key"] - if key == "" { - handler.logger.Errorw("request err, GetUserAttribute", "err", err, "key", key) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + // 2. Enhanced parameter extraction with automatic validation + key, err := common.ExtractStringPathParamWithContext(w, r, "key") + if err != nil { + // Error already written by ExtractStringPathParamWithContext return } - dto := bean.UserAttributesDto{} - + // 3. Get user email with enhanced error handling emailId, err := handler.userService.GetActiveEmailById(userId) if err != nil { - handler.logger.Errorw("request err, UpdateUserAttributes", "err", err, "payload", dto) - common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) + handler.logger.Errorw("Failed to get user email", + "operation", "get_user_attribute", + "userId", userId, + "key", key, + "err", err) + + // Use enhanced error response for forbidden access + common.WriteForbiddenError(w, "access user attributes", "user") return } - dto.EmailId = emailId - dto.Key = key + // 4. Prepare DTO + dto := bean.UserAttributesDto{ + UserId: userId, + EmailId: emailId, + Key: key, + } + + // 5. Service call with enhanced error handling res, err := handler.userAttributesService.GetUserAttribute(&dto) if err != nil { - handler.logger.Errorw("service err, GetAttributesById", "err", err) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("Failed to get user attribute", + "operation", "get_user_attribute", + "userId", userId, + "key", key, + "err", err) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("user attribute retrieval"). + WithResource("user attribute", key) + errBuilder.HandleError(err) return } + + // 6. Success response common.WriteJsonResp(w, nil, res, http.StatusOK) } diff --git a/api/restHandler/app/appInfo/AppInfoRestHandler.go b/api/restHandler/app/appInfo/AppInfoRestHandler.go index b389ae364a..645ae2ca07 100644 --- a/api/restHandler/app/appInfo/AppInfoRestHandler.go +++ b/api/restHandler/app/appInfo/AppInfoRestHandler.go @@ -126,7 +126,7 @@ func (handler AppInfoRestHandlerImpl) GetAppMetaInfo(w http.ResponseWriter, r *h return } // Use enhanced parameter parsing with context - appId, err := common.ExtractIntPathParamWithContext(w, r, "appId", "application") + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/restHandler/app/pipeline/AutoCompleteRestHandler.go b/api/restHandler/app/pipeline/AutoCompleteRestHandler.go index 85b3d83327..998e528a6e 100644 --- a/api/restHandler/app/pipeline/AutoCompleteRestHandler.go +++ b/api/restHandler/app/pipeline/AutoCompleteRestHandler.go @@ -136,7 +136,7 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) GetAppListForAutocomplete(w return } } else { - teamIdInt, err = common.ExtractIntPathParamWithContext(w, r, "teamId", teamId+" team") + teamIdInt, err = common.ExtractIntPathParamWithContext(w, r, "teamId") if err != nil { // Error already written by ExtractIntPathParamWithContext return @@ -246,7 +246,7 @@ func (handler DevtronAppAutoCompleteRestHandlerImpl) GitListAutocomplete(w http. func (handler DevtronAppAutoCompleteRestHandlerImpl) RegistriesListAutocomplete(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("token") // Use enhanced parameter parsing with context - appId, err := common.ExtractIntPathParamWithContext(w, r, "appId", "application") + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go b/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go index a1b634d5ab..4d2f0414cb 100644 --- a/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go +++ b/api/restHandler/app/pipeline/configure/BuildPipelineRestHandler.go @@ -1211,7 +1211,7 @@ func (handler *PipelineConfigRestHandlerImpl) GetCIPipelineById(w http.ResponseW token := r.Header.Get("token") // vars := mux.Vars(r) //appId, err := strconv.Atoi(vars["appId"]) - appId, err := common.ExtractIntPathParamWithContext(w, r, "appId", "app") + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { // response already written by ExtractIntPathParamWithContext //common.WriteJsonResp(w, err, nil, http.StatusBadRequest) @@ -1219,7 +1219,7 @@ func (handler *PipelineConfigRestHandlerImpl) GetCIPipelineById(w http.ResponseW return } //pipelineId, err := strconv.Atoi(vars["pipelineId"]) - pipelineId, err := common.ExtractIntPathParamWithContext(w, r, "pipelineId", "pipeline") + pipelineId, err := common.ExtractIntPathParamWithContext(w, r, "pipelineId") if err != nil { // response already written by ExtractIntPathParamWithContext // common.WriteJsonResp(w, err, nil, http.StatusBadRequest) @@ -1719,7 +1719,7 @@ func (handler *PipelineConfigRestHandlerImpl) FetchWorkflowDetails(w http.Respon } // vars := mux.Vars(r) // appId, err = strconv.Atoi(vars["appId"]) - appId, err := common.ExtractIntPathParamWithContext(w, r, "appId", "app") + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { // response already written by ExtractIntPathParamWithContext // common.WriteJsonResp(w, err, nil, http.StatusBadRequest) @@ -1727,7 +1727,7 @@ func (handler *PipelineConfigRestHandlerImpl) FetchWorkflowDetails(w http.Respon return } // pipelineId, err := strconv.Atoi(vars["pipelineId"]) - pipelineId, err := common.ExtractIntPathParamWithContext(w, r, "pipelineId", "pipeline") + pipelineId, err := common.ExtractIntPathParamWithContext(w, r, "pipelineId") if err != nil { // response already written by ExtractIntPathParamWithContext // common.WriteJsonResp(w, err, nil, http.StatusBadRequest) @@ -1735,7 +1735,7 @@ func (handler *PipelineConfigRestHandlerImpl) FetchWorkflowDetails(w http.Respon return } // buildId, err := strconv.Atoi(vars["workflowId"]) - buildId, err := common.ExtractIntPathParamWithContext(w, r, "workflowId", "workflow") + buildId, err := common.ExtractIntPathParamWithContext(w, r, "workflowId") if err != nil || buildId == 0 { // response already written by ExtractIntPathParamWithContext // common.WriteJsonResp(w, err, nil, http.StatusBadRequest) diff --git a/api/restHandler/app/pipeline/trigger/PipelineTriggerRestHandler.go b/api/restHandler/app/pipeline/trigger/PipelineTriggerRestHandler.go index 6adbe072ec..78bde8e2ee 100644 --- a/api/restHandler/app/pipeline/trigger/PipelineTriggerRestHandler.go +++ b/api/restHandler/app/pipeline/trigger/PipelineTriggerRestHandler.go @@ -18,7 +18,8 @@ package trigger import ( "encoding/json" - "fmt" + "net/http" + util2 "github.com/devtron-labs/devtron/internal/util" bean5 "github.com/devtron-labs/devtron/pkg/auth/user/bean" "github.com/devtron-labs/devtron/pkg/deployment/deployedApp" @@ -27,14 +28,11 @@ import ( bean3 "github.com/devtron-labs/devtron/pkg/deployment/trigger/devtronApps/bean" "github.com/devtron-labs/devtron/pkg/eventProcessor/out" bean4 "github.com/devtron-labs/devtron/pkg/eventProcessor/out/bean" - "net/http" - "strconv" "github.com/devtron-labs/devtron/pkg/app" "github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin" "github.com/devtron-labs/devtron/pkg/auth/user" "github.com/devtron-labs/devtron/util" - "github.com/gorilla/mux" "go.opentelemetry.io/otel" "github.com/devtron-labs/devtron/api/bean" @@ -99,43 +97,54 @@ func (handler PipelineTriggerRestHandlerImpl) validateCdTriggerRBAC(token string // RBAC block starts from here object := handler.enforcerUtil.GetAppRBACNameByAppId(appId) if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { - return util2.NewApiError(http.StatusForbidden, common.UnAuthorisedUser, common.UnAuthorisedUser) + return util2.NewApiError(http.StatusForbidden, + "Access denied for application trigger operation", + "forbidden").WithCode("11011") } object = handler.enforcerUtil.GetAppRBACByAppIdAndPipelineId(appId, cdPipelineId) if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object); !ok { - return util2.NewApiError(http.StatusForbidden, common.UnAuthorisedUser, common.UnAuthorisedUser) + return util2.NewApiError(http.StatusForbidden, + "Access denied for environment trigger operation", + "forbidden").WithCode("11011") } // RBAC block ends here return nil } func (handler PipelineTriggerRestHandlerImpl) OverrideConfig(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } + + // 2. Request body parsing + decoder := json.NewDecoder(r.Body) var overrideRequest bean.ValuesOverrideRequest err = decoder.Decode(&overrideRequest) if err != nil { - handler.logger.Errorw("request err, OverrideConfig", "err", err, "payload", overrideRequest) + handler.logger.Errorw("request parsing error", "err", err) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } overrideRequest.UserId = userId - handler.logger.Infow("request for OverrideConfig", "payload", overrideRequest) + + // 3. Struct validation err = handler.validator.Struct(overrideRequest) if err != nil { - handler.logger.Errorw("request err, OverrideConfig", "err", err, "payload", overrideRequest) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + handler.logger.Errorw("validation error", "err", err, "payload", overrideRequest) + common.HandleValidationErrors(w, r, err) return } + + // 4. RBAC validation token := r.Header.Get("token") if rbacErr := handler.validateCdTriggerRBAC(token, overrideRequest.AppId, overrideRequest.PipelineId); rbacErr != nil { - common.WriteJsonResp(w, rbacErr, nil, http.StatusForbidden) + common.WriteJsonResp(w, rbacErr, nil, rbacErr.(*util2.ApiError).HttpStatusCode) return } + // 5. Service call with enhanced error handling ctx := r.Context() _, span := otel.Tracer("orchestrator").Start(ctx, "workflowDagExecutor.ManualCdTrigger") triggerContext := bean3.TriggerContext{ @@ -151,47 +160,60 @@ func (handler PipelineTriggerRestHandlerImpl) OverrideConfig(w http.ResponseWrit mergeResp, helmPackageName, _, err := handler.cdHandlerService.ManualCdTrigger(triggerContext, &overrideRequest, userMetadata) span.End() if err != nil { - handler.logger.Errorw("request err, OverrideConfig", "err", err, "payload", overrideRequest) - common.WriteJsonResp(w, err, err.Error(), http.StatusInternalServerError) + handler.logger.Errorw("service error", "err", err, "payload", overrideRequest) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("CD pipeline trigger"). + WithResourceFromId("pipeline", overrideRequest.PipelineId) + errBuilder.HandleError(err) return } + + // 6. Success response res := map[string]interface{}{"releaseId": mergeResp, "helmPackageName": helmPackageName} - common.WriteJsonResp(w, err, res, http.StatusOK) + common.WriteJsonResp(w, nil, res, http.StatusOK) } func (handler PipelineTriggerRestHandlerImpl) RotatePods(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } + + // 2. Request body parsing + decoder := json.NewDecoder(r.Body) var podRotateRequest bean2.PodRotateRequest err = decoder.Decode(&podRotateRequest) if err != nil { - handler.logger.Errorw("request err, RotatePods", "err", err, "payload", podRotateRequest) + handler.logger.Errorw("request parsing error", "err", err) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } podRotateRequest.UserId = userId - handler.logger.Infow("request payload, RotatePods", "err", err, "payload", podRotateRequest) + + // 3. Struct validation err = handler.validator.Struct(podRotateRequest) if err != nil { - handler.logger.Errorw("validation err, RotatePods", "err", err, "payload", podRotateRequest) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + handler.logger.Errorw("validation error", "err", err, "payload", podRotateRequest) + common.HandleValidationErrors(w, r, err) return } + // 4. RBAC validation token := r.Header.Get("token") object := handler.enforcerUtil.GetAppRBACNameByAppId(podRotateRequest.AppId) if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "pod rotation", "application") return } object = handler.enforcerUtil.GetEnvRBACNameByAppId(podRotateRequest.AppId, podRotateRequest.EnvironmentId) if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "pod rotation", "environment") return } + // 5. Service call with enhanced error handling isSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*") userEmail := util.GetEmailFromContext(r.Context()) userMetadata := &bean5.UserMetadata{ @@ -201,48 +223,59 @@ func (handler PipelineTriggerRestHandlerImpl) RotatePods(w http.ResponseWriter, } rotatePodResponse, err := handler.deployedAppService.RotatePods(r.Context(), &podRotateRequest, userMetadata) if err != nil { - handler.logger.Errorw("service err, RotatePods", "err", err, "payload", podRotateRequest) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("service error", "err", err, "payload", podRotateRequest) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("pod rotation"). + WithResourceFromId("application", podRotateRequest.AppId) + errBuilder.HandleError(err) return } + + // 6. Success response common.WriteJsonResp(w, nil, rotatePodResponse, http.StatusOK) } func (handler PipelineTriggerRestHandlerImpl) StartStopApp(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } + + // 2. Request body parsing + decoder := json.NewDecoder(r.Body) var overrideRequest bean2.StopAppRequest err = decoder.Decode(&overrideRequest) if err != nil { - handler.logger.Errorw("request err, StartStopApp", "err", err, "payload", overrideRequest) + handler.logger.Errorw("request parsing error", "err", err) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } overrideRequest.UserId = userId - handler.logger.Infow("request payload, StartStopApp", "err", err, "payload", overrideRequest) + + // 3. Struct validation err = handler.validator.Struct(overrideRequest) if err != nil { - handler.logger.Errorw("validation err, StartStopApp", "err", err, "payload", overrideRequest) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + handler.logger.Errorw("validation error", "err", err, "payload", overrideRequest) + common.HandleValidationErrors(w, r, err) return } + // 4. RBAC validation token := r.Header.Get("token") - //rbac block starts from here object := handler.enforcerUtil.GetAppRBACNameByAppId(overrideRequest.AppId) if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "start/stop", "application") return } object = handler.enforcerUtil.GetEnvRBACNameByAppId(overrideRequest.AppId, overrideRequest.EnvironmentId) if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "start/stop", "environment") return } - //rback block ends here + // 5. Service call with enhanced error handling ctx := r.Context() isSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*") userEmail := util.GetEmailFromContext(ctx) @@ -253,58 +286,72 @@ func (handler PipelineTriggerRestHandlerImpl) StartStopApp(w http.ResponseWriter } mergeResp, err := handler.deployedAppService.StopStartApp(ctx, &overrideRequest, userMetadata) if err != nil { - handler.logger.Errorw("service err, StartStopApp", "err", err, "payload", overrideRequest) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("service error", "err", err, "payload", overrideRequest) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("application start/stop"). + WithResourceFromId("application", overrideRequest.AppId) + errBuilder.HandleError(err) return } + + // 6. Success response res := map[string]interface{}{"releaseId": mergeResp} - common.WriteJsonResp(w, err, res, http.StatusOK) + common.WriteJsonResp(w, nil, res, http.StatusOK) } func (handler PipelineTriggerRestHandlerImpl) StartStopDeploymentGroup(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) - + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } + + // 2. Request body parsing + decoder := json.NewDecoder(r.Body) var stopDeploymentGroupRequest bean4.StopDeploymentGroupRequest err = decoder.Decode(&stopDeploymentGroupRequest) if err != nil { - handler.logger.Errorw("request err, StartStopDeploymentGroup", "err", err, "payload", stopDeploymentGroupRequest) + handler.logger.Errorw("request parsing error", "err", err) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } stopDeploymentGroupRequest.UserId = userId + + // 3. Struct validation err = handler.validator.Struct(stopDeploymentGroupRequest) if err != nil { - handler.logger.Errorw("validation err, StartStopDeploymentGroup", "err", err, "payload", stopDeploymentGroupRequest) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + handler.logger.Errorw("validation error", "err", err, "payload", stopDeploymentGroupRequest) + // Enhanced validation error handling + common.HandleValidationErrors(w, r, err) return } - handler.logger.Infow("request payload, StartStopDeploymentGroup", "err", err, "payload", stopDeploymentGroupRequest) - //rbac block starts from here + // 4. Deployment group retrieval and RBAC validation dg, err := handler.deploymentGroupService.GetDeploymentGroupById(stopDeploymentGroupRequest.DeploymentGroupId) if err != nil { - handler.logger.Errorw("request err, StartStopDeploymentGroup", "err", err, "payload", stopDeploymentGroupRequest) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("deployment group retrieval error", "err", err, "deploymentGroupId", stopDeploymentGroupRequest.DeploymentGroupId) + + // Use enhanced error response with resource context + common.WriteJsonRespWithResourceContextFromId(w, err, nil, 0, "deployment group", stopDeploymentGroupRequest.DeploymentGroupId) return } + token := r.Header.Get("token") // RBAC enforcer applying object := handler.enforcerUtil.GetTeamRBACByCiPipelineId(dg.CiPipelineId) if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "deployment group start/stop", "application") return } object = handler.enforcerUtil.GetEnvRBACNameByCiPipelineIdAndEnvId(dg.CiPipelineId, dg.EnvironmentId) if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionTrigger, object); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "deployment group start/stop", "environment") return } - //rback block ends here + // 5. Service call with enhanced error handling isSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*") userEmail := util.GetEmailFromContext(r.Context()) userMetadata := &bean5.UserMetadata{ @@ -314,72 +361,93 @@ func (handler PipelineTriggerRestHandlerImpl) StartStopDeploymentGroup(w http.Re } res, err := handler.workflowEventPublishService.TriggerBulkHibernateAsync(stopDeploymentGroupRequest, userMetadata) if err != nil { - handler.logger.Errorw("service err, StartStopDeploymentGroup", "err", err, "payload", stopDeploymentGroupRequest) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("service error", "err", err, "payload", stopDeploymentGroupRequest) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("deployment group start/stop"). + WithResourceFromId("deployment group", stopDeploymentGroupRequest.DeploymentGroupId) + errBuilder.HandleError(err) return } - common.WriteJsonResp(w, err, res, http.StatusOK) + + // 6. Success response + common.WriteJsonResp(w, nil, res, http.StatusOK) } func (handler PipelineTriggerRestHandlerImpl) ReleaseStatusUpdate(w http.ResponseWriter, r *http.Request) { - decoder := json.NewDecoder(r.Body) + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } + + // 2. Request body parsing + decoder := json.NewDecoder(r.Body) var releaseStatusUpdateRequest bean.ReleaseStatusUpdateRequest err = decoder.Decode(&releaseStatusUpdateRequest) if err != nil { - handler.logger.Errorw("request err, ReleaseStatusUpdate", "err", err, "payload", releaseStatusUpdateRequest) + handler.logger.Errorw("request parsing error", "err", err) common.WriteJsonResp(w, err, nil, http.StatusBadRequest) return } - handler.logger.Infow("request payload, ReleaseStatusUpdate, override request ----", "err", err, "payload", releaseStatusUpdateRequest) - res, err := handler.appService.UpdateReleaseStatus(&releaseStatusUpdateRequest) + + // 3. Struct validation (if validator is available for this struct) + err = handler.validator.Struct(releaseStatusUpdateRequest) if err != nil { - handler.logger.Errorw("service err, ReleaseStatusUpdate", "err", err, "payload", releaseStatusUpdateRequest) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("validation error", "err", err, "payload", releaseStatusUpdateRequest) + // Enhanced validation error handling + common.HandleValidationErrors(w, r, err) return } - m := map[string]bool{ - "status": res} - resJson, err := json.Marshal(m) + + // 4. Service call with enhanced error handling + res, err := handler.appService.UpdateReleaseStatus(&releaseStatusUpdateRequest) if err != nil { - handler.logger.Errorw("marshal err, ReleaseStatusUpdate", "err", err, "payload", m) + handler.logger.Errorw("service error", "err", err, "payload", releaseStatusUpdateRequest) + + // Use enhanced error response builder + errBuilder := common.NewErrorResponseBuilder(w, r). + WithOperation("release status update") + errBuilder.HandleError(err) + return } - common.WriteJsonResp(w, err, resJson, http.StatusOK) + + // 5. Success response + response := map[string]bool{"status": res} + common.WriteJsonResp(w, nil, response, http.StatusOK) } func (handler PipelineTriggerRestHandlerImpl) GetAllLatestDeploymentConfiguration(w http.ResponseWriter, r *http.Request) { + // 1. Authentication check userId, err := handler.userAuthService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } - handler.logger.Infow("request payload, GetAllLatestDeploymentConfiguration") - vars := mux.Vars(r) - appId, err := strconv.Atoi(vars["appId"]) + // 2. Enhanced path parameter extraction + appId, err := common.ExtractIntPathParamWithContext(w, r, "appId") if err != nil { - handler.logger.Errorw("request err, GetAllDeployedConfigurationHistoryForSpecificWfrIdForPipeline", "err", err, "appId", appId) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + // Error already written by ExtractIntPathParamWithContext return } - pipelineId, err := strconv.Atoi(vars["pipelineId"]) + + pipelineId, err := common.ExtractIntPathParamWithContext(w, r, "pipelineId") if err != nil { - handler.logger.Errorw("request err, GetAllDeployedConfigurationHistoryForSpecificWfrIdForPipeline", "err", err, "pipelineId", pipelineId) - common.WriteJsonResp(w, err, nil, http.StatusBadRequest) + // Error already written by ExtractIntPathParamWithContext return } - //RBAC START + // 3. RBAC validation token := r.Header.Get("token") resourceName := handler.enforcerUtil.GetAppRBACNameByAppId(appId) if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionGet, resourceName); !ok { - common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden) + common.WriteForbiddenError(w, "view", "application deployment configuration") return } - //RBAC END + + // 4. Service call with enhanced error handling isSuperAdmin := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*") ctx := r.Context() ctx = util.SetSuperAdminInContext(ctx, isSuperAdmin) @@ -387,9 +455,13 @@ func (handler PipelineTriggerRestHandlerImpl) GetAllLatestDeploymentConfiguratio userHasAdminAccess := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionUpdate, resourceName) allDeploymentconfig, err := handler.deploymentConfigService.GetLatestDeploymentConfigurationByPipelineId(ctx, pipelineId, userHasAdminAccess) if err != nil { - handler.logger.Errorw("error in getting latest deployment config, GetAllDeployedConfigurationHistoryForSpecificWfrIdForPipeline", "err", err, "pipelineId", pipelineId) - common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) + handler.logger.Errorw("service error", "err", err, "pipelineId", pipelineId) + + // Use enhanced error response with resource context + common.WriteJsonRespWithResourceContextFromId(w, err, nil, 0, "pipeline", pipelineId) return } + + // 5. Success response common.WriteJsonResp(w, nil, allDeploymentconfig, http.StatusOK) } diff --git a/api/restHandler/common/ApiResponseWriter.go b/api/restHandler/common/ApiResponseWriter.go index d8d2c05dcb..e56b77d183 100644 --- a/api/restHandler/common/ApiResponseWriter.go +++ b/api/restHandler/common/ApiResponseWriter.go @@ -18,8 +18,9 @@ package common import ( "encoding/json" - "github.com/devtron-labs/devtron/internal/util" "net/http" + + "github.com/devtron-labs/devtron/internal/util" ) type ApiResponse struct { diff --git a/api/restHandler/common/ParamParserUtils.go b/api/restHandler/common/ParamParserUtils.go index 40bf593d18..8f31977d10 100644 --- a/api/restHandler/common/ParamParserUtils.go +++ b/api/restHandler/common/ParamParserUtils.go @@ -17,34 +17,47 @@ package common import ( - "github.com/devtron-labs/devtron/internal/util" - "github.com/gorilla/mux" "net/http" "strconv" "strings" + + "github.com/devtron-labs/devtron/internal/util" + "github.com/gorilla/mux" ) const TokenHeaderKey = "token" -func ExtractIntPathParam(w http.ResponseWriter, r *http.Request, paramName string) (int, error) { +// ExtractIntPathParamWithContext provides enhanced error messages with resource context +func ExtractIntPathParamWithContext(w http.ResponseWriter, r *http.Request, paramName string) (int, error) { vars := mux.Vars(r) paramValue := vars[paramName] - paramIntValue, err := convertToInt(w, paramValue) + paramIntValue, err := convertToIntWithContext(w, paramValue, paramName) if err != nil { return 0, err } return paramIntValue, nil } -// ExtractIntPathParamWithContext provides enhanced error messages with resource context -func ExtractIntPathParamWithContext(w http.ResponseWriter, r *http.Request, paramName string, resourceType string) (int, error) { +// ExtractStringPathParamWithContext provides enhanced error messages for string path parameters +func ExtractStringPathParamWithContext(w http.ResponseWriter, r *http.Request, paramName string) (string, error) { vars := mux.Vars(r) paramValue := vars[paramName] - paramIntValue, err := convertToIntWithContext(w, paramValue, paramName, resourceType) - if err != nil { - return 0, err + + if paramValue == "" { + apiErr := util.NewMissingRequiredFieldError(paramName) + WriteJsonResp(w, apiErr, nil, apiErr.HttpStatusCode) + return "", apiErr } - return paramIntValue, nil + + // Trim whitespace and validate + paramValue = strings.TrimSpace(paramValue) + if paramValue == "" { + apiErr := util.NewValidationErrorForField(paramName, "cannot be empty or contain only whitespace") + WriteJsonResp(w, apiErr, nil, apiErr.HttpStatusCode) + return "", apiErr + } + + return paramValue, nil } func convertToInt(w http.ResponseWriter, paramValue string) (int, error) { @@ -57,7 +70,7 @@ func convertToInt(w http.ResponseWriter, paramValue string) (int, error) { } // convertToIntWithContext provides better error messages for parameter conversion -func convertToIntWithContext(w http.ResponseWriter, paramValue, paramName, resourceType string) (int, error) { +func convertToIntWithContext(w http.ResponseWriter, paramValue, paramName string) (int, error) { if paramValue == "" { apiErr := util.NewMissingRequiredFieldError(paramName) WriteJsonResp(w, apiErr, nil, apiErr.HttpStatusCode) @@ -129,33 +142,3 @@ func ExtractBoolQueryParam(r *http.Request, paramName string) (bool, error) { return boolValue, nil } - -// ExtractIntArrayFromQueryParam returns list of all ids in []int extracted from query param -// use this method over ExtractIntArrayQueryParam if there is list of query params -func ExtractIntArrayFromQueryParam(r *http.Request, paramName string) ([]int, error) { - queryParams := r.URL.Query() - paramValue := queryParams[paramName] - paramIntValues := make([]int, 0) - var err error - if paramValue != nil && len(paramValue) > 0 { - if strings.Contains(paramValue[0], ",") { - paramIntValues, err = convertToIntArray(paramValue[0]) - } else { - paramIntValues, err = convertStringArrayToIntArray(paramValue) - } - } - - return paramIntValues, err -} - -func convertStringArrayToIntArray(strArr []string) ([]int, error) { - var paramValues []int - for _, item := range strArr { - paramIntValue, err := strconv.Atoi(item) - if err != nil { - return paramValues, err - } - paramValues = append(paramValues, paramIntValue) - } - return paramValues, nil -} diff --git a/api/restHandler/common/ResourceContextExtractor.go b/api/restHandler/common/ResourceContextExtractor.go index 623e8bc02a..ea19008310 100644 --- a/api/restHandler/common/ResourceContextExtractor.go +++ b/api/restHandler/common/ResourceContextExtractor.go @@ -18,10 +18,11 @@ package common import ( "fmt" - "github.com/devtron-labs/devtron/internal/util" "net/http" "strconv" "strings" + + "github.com/devtron-labs/devtron/internal/util" ) // extractResourceContext tries to extract resource type and ID from response body @@ -107,8 +108,3 @@ func WriteUnauthorizedError(w http.ResponseWriter) { WithCode("11010") WriteJsonResp(w, apiErr, nil, apiErr.HttpStatusCode) } - -func WriteInvalidAppIdError(w http.ResponseWriter, appId string) { - apiErr := util.NewInvalidPathParameterError("appId", appId) - WriteJsonResp(w, apiErr, nil, apiErr.HttpStatusCode) -} diff --git a/api/team/TeamRestHandler.go b/api/team/TeamRestHandler.go index c99e967392..f9ba218c95 100644 --- a/api/team/TeamRestHandler.go +++ b/api/team/TeamRestHandler.go @@ -144,7 +144,7 @@ func (impl TeamRestHandlerImpl) FetchAll(w http.ResponseWriter, r *http.Request) func (impl TeamRestHandlerImpl) FetchOne(w http.ResponseWriter, r *http.Request) { // Use enhanced parameter parsing with context - teamId, err := common.ExtractIntPathParamWithContext(w, r, "id", "team") + teamId, err := common.ExtractIntPathParamWithContext(w, r, "id") if err != nil { // Error already written by ExtractIntPathParamWithContext return diff --git a/charts/devtron/Chart.yaml b/charts/devtron/Chart.yaml index adc92a13d4..7061008f7e 100644 --- a/charts/devtron/Chart.yaml +++ b/charts/devtron/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: devtron-operator -appVersion: 1.7.0 +appVersion: 1.8.0 description: Chart to configure and install Devtron. Devtron is a Kubernetes Orchestration system. keywords: - Devtron @@ -11,12 +11,12 @@ keywords: - argocd - Hyperion engine: gotpl -version: 0.22.95 +version: 0.22.96 sources: - https://github.com/devtron-labs/charts dependencies: - name: argo-cd - version: "5.9.1" + version: "7.7.15" repository: https://argoproj.github.io/argo-helm condition: argo-cd.enabled - name: security diff --git a/charts/devtron/devtron-bom.yaml b/charts/devtron/devtron-bom.yaml index 1f1f025a7d..ac0fb7575e 100644 --- a/charts/devtron/devtron-bom.yaml +++ b/charts/devtron/devtron-bom.yaml @@ -15,7 +15,7 @@ global: PG_DATABASE: orchestrator extraManifests: [] installer: - release: "v1.7.0" + release: "v1.8.0" registry: "" image: "inception" tag: "473deaa4-185-21582" @@ -39,14 +39,15 @@ components: FEATURE_USER_DEFINED_GITOPS_REPO_ENABLE: "true" ENABLE_RESOURCE_SCAN: "true" FEATURE_CODE_MIRROR_ENABLE: "true" + FEATURE_GROUPED_APP_LIST_FILTERS_ENABLE: "true" registry: "" - image: "dashboard:a85f2624-690-33873" + image: "dashboard:5196e935-690-36024" imagePullPolicy: IfNotPresent healthPort: 8080 devtron: registry: "" - image: "hyperion:c8e75fb3-280-33879" - cicdImage: "devtron:c8e75fb3-434-33854" + image: "hyperion:1ae65fbb-280-36074" + cicdImage: "devtron:1ae65fbb-434-36069" imagePullPolicy: IfNotPresent customOverrides: {} podSecurityContext: @@ -60,7 +61,7 @@ components: healthPort: 8080 ciRunner: registry: "" - image: "ci-runner:a4fc9044-138-33875" + image: "ci-runner:880420ac-138-36030" argocdDexServer: registry: "" image: "dex:v2.30.2" @@ -69,7 +70,7 @@ components: authenticator: "authenticator:e414faff-393-13273" kubelink: registry: "" - image: "kubelink:a4fc9044-564-33855" + image: "kubelink:880420ac-564-36036" imagePullPolicy: IfNotPresent configs: ENABLE_HELM_RELEASE_CACHE: "true" @@ -92,7 +93,7 @@ components: healthPort: 50052 kubewatch: registry: "" - image: "kubewatch:a4fc9044-419-33852" + image: "kubewatch:880420ac-419-36026" imagePullPolicy: IfNotPresent healthPort: 8080 configs: @@ -116,7 +117,7 @@ components: image: postgres_exporter:v0.10.1 gitsensor: registry: "" - image: "git-sensor:a4fc9044-200-33872" + image: "git-sensor:880420ac-200-36032" imagePullPolicy: IfNotPresent serviceMonitor: enabled: false @@ -134,7 +135,7 @@ components: # Values for lens lens: registry: "" - image: "lens:a4fc9044-333-33874" + image: "lens:880420ac-333-36029" imagePullPolicy: IfNotPresent configs: GIT_SENSOR_PROTOCOL: GRPC @@ -169,7 +170,7 @@ components: entMigratorImage: "devtron-utils:geni-v1.1.4" chartSync: registry: "" - image: chart-sync:a4fc9044-836-33878 + image: chart-sync:880420ac-836-36037 schedule: "0 19 * * *" podSecurityContext: fsGroup: 1001 @@ -186,9 +187,18 @@ argo-cd: # -- If defined, a repository applied to all Argo CD deployments repository: quay.io/argoproj/argocd # -- Overrides the global Argo CD image tag whose default is the chart appVersion - tag: "v2.5.2" + tag: "v2.13.3" # -- If defined, a imagePullPolicy applied to all Argo CD deployments imagePullPolicy: IfNotPresent + configs: + cm: + create: false + # argocd-rbac-cm + rbac: + create: true + policy.default: role:admin + applicationSet: + replicas: 0 # Change below values for workflow controller workflowController: registry: "quay.io/argoproj" @@ -198,7 +208,7 @@ workflowController: IMDSv1ExecutorImage: "argoexec:v3.0.7" security: imageScanner: - image: "image-scanner:a4fc9044-141-33877" + image: "image-scanner:f21e02cb-141-34534" healthPort: 8080 configs: TRIVY_DB_REPOSITORY: mirror.gcr.io/aquasec/trivy-db @@ -209,7 +219,7 @@ security: tag: 4.3.6 # Values for notifier integration notifier: - image: "notifier:19d654ff-372-33876" + image: "notifier:00f17215-372-36041" healthPort: 3000 minio: image: "minio:RELEASE.2021-02-14T04-01-33Z" diff --git a/charts/devtron/templates/argocd-secret.yaml b/charts/devtron/templates/argocd-secret.yaml index 1d55e545bc..ee12df5593 100644 --- a/charts/devtron/templates/argocd-secret.yaml +++ b/charts/devtron/templates/argocd-secret.yaml @@ -64,173 +64,205 @@ data: health.lua: | hs = {} if obj.status ~= nil then - if obj.status.status ~= nil then - hs.status = "Degraded" - hs.message = obj.status.status - else + if obj.status.status ~= nil then + hs.status = "Degraded" + hs.message = obj.status.status + else hs.status = "Healthy" - end + end else - hs.status = "Healthy" + hs.status = "Healthy" end return hs argoproj.io/Rollout: health.lua: | + function getNumberValueOrDefault(field) + if field ~= nil then + return field + end + return 0 + end + function checkPaused(obj) + hs = {} + local paused = false + if obj.status.verifyingPreview ~= nil then + paused = obj.status.verifyingPreview + elseif obj.spec.paused ~= nil then + paused = obj.spec.paused + end + + if paused then + hs.status = "Suspended" + hs.message = "Rollout is paused" + return hs + end + return nil + end function checkReplicasStatus(obj) - hs = {} - replicasCount = getNumberValueOrDefault(obj.spec.replicas) - replicasStatus = getNumberValueOrDefault(obj.status.replicas) - updatedReplicas = getNumberValueOrDefault(obj.status.updatedReplicas) - availableReplicas = getNumberValueOrDefault(obj.status.availableReplicas) + hs = {} + replicasCount = getNumberValueOrDefault(obj.spec.replicas) + replicasStatus = getNumberValueOrDefault(obj.status.replicas) + updatedReplicas = getNumberValueOrDefault(obj.status.updatedReplicas) + availableReplicas = getNumberValueOrDefault(obj.status.availableReplicas) - if updatedReplicas < replicasCount then + if updatedReplicas < replicasCount then hs.status = "Progressing" hs.message = "Waiting for roll out to finish: More replicas need to be updated" return hs - end - -- Since the scale down delay can be very high, BlueGreen does not wait for all the old replicas to scale - -- down before marking itself healthy. As a result, only evaluate this condition if the strategy is canary. - if obj.spec.strategy.canary ~= nil and replicasStatus > updatedReplicas then + end + -- Since the scale down delay can be very high, BlueGreen does not wait for all the old replicas to scale + -- down before marking itself healthy. As a result, only evaluate this condition if the strategy is canary. + if obj.spec.strategy.canary ~= nil and replicasStatus > updatedReplicas then hs.status = "Progressing" hs.message = "Waiting for roll out to finish: old replicas are pending termination" return hs - end - if availableReplicas < updatedReplicas then + end + if availableReplicas < updatedReplicas then hs.status = "Progressing" hs.message = "Waiting for roll out to finish: updated replicas are still becoming available" return hs - end - return nil - end - - function getNumberValueOrDefault(field) - if field ~= nil then - return field - end - return 0 - end - - function checkPaused(obj) - hs = {} - local paused = false - if obj.status.verifyingPreview ~= nil then - paused = obj.status.verifyingPreview - elseif obj.spec.paused ~= nil then - paused = obj.spec.paused - end - - if paused then - hs.status = "Suspended" - hs.message = "Rollout is paused" - return hs - end - return nil - end + end + return nil + end - hs = {} - if obj.status ~= nil then - if obj.status.conditions ~= nil then + function statusfromcondition(obj) + local hs={} for _, condition in ipairs(obj.status.conditions) do - if condition.type == "InvalidSpec" then - hs.status = "Degraded" - hs.message = condition.message + if condition.type == "InvalidSpec" then + hs.status = "Degraded" + hs.message = condition.message return hs - end - if condition.type == "Progressing" and condition.reason == "RolloutAborted" then - hs.status = "Degraded" - hs.message = condition.message + end + if condition.type == "Progressing" and condition.reason == "RolloutAborted" then + hs.status = "Degraded" + hs.message = condition.message return hs - end - if condition.type == "Progressing" and condition.reason == "ProgressDeadlineExceeded" then - hs.status = "Degraded" - hs.message = condition.message + end + if condition.type == "Progressing" and condition.reason == "ProgressDeadlineExceeded" then + hs.status = "Degraded" + hs.message = condition.message return hs - end + end end - end - if obj.status.currentPodHash ~= nil then + return nil + end + + function statusfrompodhash(obj) + local hs={} if obj.spec.strategy.blueGreen ~= nil then - isPaused = checkPaused(obj) - if isPaused ~= nil then - return isPaused - end - replicasHS = checkReplicasStatus(obj) - if replicasHS ~= nil then - return replicasHS - end - if obj.status.blueGreen ~= nil and obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then - hs.status = "Healthy" - hs.message = "The active Service is serving traffic to the current pod spec" + isPaused = checkPaused(obj) + if isPaused ~= nil then + return isPaused + end + replicasHS = checkReplicasStatus(obj) + if replicasHS ~= nil then + return replicasHS + end + if obj.status.blueGreen ~= nil and obj.status.blueGreen.activeSelector ~= nil and obj.status.blueGreen.activeSelector == obj.status.currentPodHash then + hs.status = "Healthy" + hs.message = "The active Service is serving traffic to the current pod spec" + return hs + end + hs.status = "Progressing" + hs.message = "The current pod spec is not receiving traffic from the active service" return hs - end - hs.status = "Progressing" - hs.message = "The current pod spec is not receiving traffic from the active service" - return hs end if obj.spec.strategy.recreate ~= nil then - isPaused = checkPaused(obj) - if isPaused ~= nil then - return isPaused - end - replicasHS = checkReplicasStatus(obj) - if replicasHS ~= nil then - return replicasHS - end - if obj.status.recreate ~= nil and obj.status.recreate.currentRS ~= nil and obj.status.recreate.currentRS == obj.status.currentPodHash then - hs.status = "Healthy" - hs.message = "Rollout is successful" + isPaused = checkPaused(obj) + if isPaused ~= nil then + return isPaused + end + replicasHS = checkReplicasStatus(obj) + if replicasHS ~= nil then + return replicasHS + end + if obj.status.recreate ~= nil and obj.status.recreate.currentRS ~= nil and obj.status.recreate.currentRS == obj.status.currentPodHash then + hs.status = "Healthy" + hs.message = "Rollout is successful" + return hs + end + hs.status = "Progressing" + hs.message = "Rollout is in progress" return hs - end - hs.status = "Progressing" - hs.message = "Rollout is in progress" - return hs end if obj.spec.strategy.canary ~= nil then - currentRSIsStable = obj.status.canary.stableRS == obj.status.currentPodHash - if obj.spec.strategy.canary.steps ~= nil and table.getn(obj.spec.strategy.canary.steps) > 0 then + if obj.status.stableRS ~= nil then + currentRSIsStable = obj.status.stableRS == obj.status.currentPodHash + end + if obj.status.canary.stableRS ~= nil then + currentRSIsStable = obj.status.canary.stableRS == obj.status.currentPodHash + end + if obj.spec.strategy.canary.steps ~= nil and table.getn(obj.spec.strategy.canary.steps) > 0 then stepCount = table.getn(obj.spec.strategy.canary.steps) if obj.status.currentStepIndex ~= nil then - currentStepIndex = obj.status.currentStepIndex - isPaused = checkPaused(obj) - if isPaused ~= nil then + currentStepIndex = obj.status.currentStepIndex + isPaused = checkPaused(obj) + if isPaused ~= nil then return isPaused - end - - if paused then + end + + if paused then hs.status = "Suspended" hs.message = "Rollout is paused" return hs - end - if currentRSIsStable and stepCount == currentStepIndex then + end + if currentRSIsStable and stepCount == currentStepIndex then replicasHS = checkReplicasStatus(obj) if replicasHS ~= nil then - return replicasHS + return replicasHS end hs.status = "Healthy" hs.message = "The rollout has completed all steps" return hs - end + end end hs.status = "Progressing" hs.message = "Waiting for rollout to finish steps" return hs - end + end - -- The detecting the health of the Canary deployment when there are no steps - replicasHS = checkReplicasStatus(obj) - if replicasHS ~= nil then + -- The detecting the health of the Canary deployment when there are no steps + replicasHS = checkReplicasStatus(obj) + if replicasHS ~= nil then return replicasHS - end - if currentRSIsStable then + end + if currentRSIsStable then hs.status = "Healthy" hs.message = "The rollout has completed canary deployment" return hs - end - hs.status = "Progressing" - hs.message = "Waiting for rollout to finish canary deployment" + end + hs.status = "Progressing" + hs.message = "Waiting for rollout to finish canary deployment" end - end - end - hs.status = "Progressing" - hs.message = "Waiting for rollout to finish: status has not been reconciled." + + + return hs + end + + -- Main Code + hs = {} + if obj.status.phase ~= nil then + if obj.status.phase == "Paused" then + hs.status = "Progressing" + hs.message = "Rollout is paused" + elseif obj.status.phase == "Progressing" then + hs=statusfromcondition(obj) or hs + hs=statusfrompodhash(obj) or hs + elseif obj.status.phase == "Healthy" then + hs=statusfromcondition(obj) or hs + hs=statusfrompodhash(obj) or hs + else + hs.status = obj.status.phase + hs.message = obj.status.message + end + else + if obj.status ~= nil then + if obj.status.conditions ~= nil then + hs=statusfromcondition(obj) + end + if obj.status.currentPodHash ~= nil then + hs=statusfrompodhash(obj) + end + end + end return hs \ No newline at end of file diff --git a/charts/devtron/templates/devtron.yaml b/charts/devtron/templates/devtron.yaml index a27d44529b..14ab283a11 100644 --- a/charts/devtron/templates/devtron.yaml +++ b/charts/devtron/templates/devtron.yaml @@ -13,6 +13,7 @@ data: DEVTRON_HELM_RELEASE_NAME: {{ $.Release.Name }} DEVTRON_HELM_RELEASE_NAMESPACE: {{ $.Release.Namespace }} FEATURE_MIGRATE_ARGOCD_APPLICATION_ENABLE: "true" + GRPC_ENFORCE_ALPN_ENABLED: "false" {{ toYaml $.Values.global.dbConfig | indent 2 }} HELM_CLIENT_URL: kubelink-service-headless:50051 DASHBOARD_PORT: "80" diff --git a/charts/devtron/templates/migrator.yaml b/charts/devtron/templates/migrator.yaml index b08dd32d1a..c95d8b7212 100644 --- a/charts/devtron/templates/migrator.yaml +++ b/charts/devtron/templates/migrator.yaml @@ -443,4 +443,67 @@ spec: activeDeadlineSeconds: 1500 {{- end }} {{- end }} +{{- end }} +{{- if and $.Values.global.externalPostgres $.Values.global.externalPostgres.enabled }} +--- +{{- if $.Capabilities.APIVersions.Has "batch/v1/Job" }} +apiVersion: batch/v1 +{{- else }} +apiVersion: batch/v1beta1 +{{- end }} +kind: Job +metadata: + namespace: devtroncd + name: postgresql-create-databases-{{ randAlphaNum 5 | lower }} + annotations: + "helm.sh/hook": pre-install +spec: + activeDeadlineSeconds: 1500 + ttlSecondsAfterFinished: 1000 + backoffLimit: 20 + completions: 1 + parallelism: 1 + template: + metadata: + labels: + app: database-creator + spec: + {{- include "common.schedulerConfig" (dict "nodeSelector" $.Values.components.migrator.nodeSelector "tolerations" $.Values.components.migrator.tolerations "imagePullSecrets" $.Values.components.migrator.imagePullSecrets "global" $.Values.global) | indent 6 }} + serviceAccountName: devtron-default-sa + containers: + - command: + - /bin/sh + - -c + - | + # Create databases + export PGPASSWORD="${DB_PASSWORD}" + + echo "Creating database: orchestrator" + psql -h ${PG_ADDR} -p ${PG_PORT} -U ${PG_USER} -d postgres -c "CREATE DATABASE orchestrator;" || echo "Database orchestrator already exists or failed to create" + + echo "Creating database: git_sensor" + psql -h ${PG_ADDR} -p ${PG_PORT} -U ${PG_USER} -d postgres -c "CREATE DATABASE git_sensor;" || echo "Database git_sensor already exists or failed to create" + + echo "Creating database: lens" + psql -h ${PG_ADDR} -p ${PG_PORT} -U ${PG_USER} -d postgres -c "CREATE DATABASE lens;" || echo "Database lens already exists or failed to create" + + echo "Creating database: casbin" + psql -h ${PG_ADDR} -p ${PG_PORT} -U ${PG_USER} -d postgres -c "CREATE DATABASE casbin;" || echo "Database casbin already exists or failed to create" + + echo "Creating database: clairv4" + psql -h ${PG_ADDR} -p ${PG_PORT} -U ${PG_USER} -d postgres -c "CREATE DATABASE clairv4;" || echo "Database clairv4 already exists or failed to create" + + echo "All databases created successfully" + envFrom: + - secretRef: + name: postgresql-migrator + - configMapRef: + name: devtron-cm + - configMapRef: + name: devtron-custom-cm + - configMapRef: + name: devtron-common-cm + image: {{ include "common.image" (dict "component" $.Values.components.postgres "global" $.Values.global "extraImage" $.Values.components.postgres.image ) }} + name: postgresql-database-creator + restartPolicy: OnFailure {{- end }} \ No newline at end of file diff --git a/charts/devtron/templates/networkpolicies.yaml b/charts/devtron/templates/networkpolicies.yaml index 1a262d8c9c..0ba5939494 100644 --- a/charts/devtron/templates/networkpolicies.yaml +++ b/charts/devtron/templates/networkpolicies.yaml @@ -28,7 +28,7 @@ kind: NetworkPolicy metadata: labels: app: postgresql - name: netpol-devtron-postgress + name: netpol-devtron-postgres namespace: devtroncd spec: policyTypes: @@ -42,8 +42,8 @@ spec: - port: 5432 podSelector: matchLabels: - app: postgresql - release: devtron + app.kubernetes.io/name: postgres + app.kubernetes.io/instance: devtron --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy @@ -231,5 +231,19 @@ spec: matchLabels: app.kubernetes.io/name: nats app.kubernetes.io/instance: devtron-nats +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-all-ingress-in-devtron + namespace: devtroncd +spec: + podSelector: + matchLabels: + app: devtron + policyTypes: + - Ingress + ingress: + - {} {{- end }} {{- end }} diff --git a/charts/devtron/values.yaml b/charts/devtron/values.yaml index 4a0055aa41..22c4527774 100644 --- a/charts/devtron/values.yaml +++ b/charts/devtron/values.yaml @@ -42,7 +42,7 @@ nfs: extraManifests: [] installer: repo: "devtron-labs/devtron" - release: "v1.7.0" + release: "v1.8.0" registry: "" image: inception tag: 473deaa4-185-21582 @@ -95,14 +95,15 @@ components: FEATURE_USER_DEFINED_GITOPS_REPO_ENABLE: "true" ENABLE_RESOURCE_SCAN: "true" FEATURE_CODE_MIRROR_ENABLE: "true" + FEATURE_GROUPED_APP_LIST_FILTERS_ENABLE: "true" registry: "" - image: "dashboard:a85f2624-690-33873" + image: "dashboard:5196e935-690-36024" imagePullPolicy: IfNotPresent healthPort: 8080 devtron: registry: "" - image: "hyperion:c8e75fb3-280-33879" - cicdImage: "devtron:c8e75fb3-434-33854" + image: "hyperion:1ae65fbb-280-36074" + cicdImage: "devtron:1ae65fbb-434-36069" imagePullPolicy: IfNotPresent customOverrides: {} healthPort: 8080 @@ -139,7 +140,7 @@ components: # - devtron.example.com ciRunner: registry: "" - image: "ci-runner:a4fc9044-138-33875" + image: "ci-runner:880420ac-138-36030" # Add annotations for ci-runner & cd-runner serviceAccount. annotations: {} argocdDexServer: @@ -150,7 +151,7 @@ components: authenticator: "authenticator:e414faff-393-13273" kubelink: registry: "" - image: "kubelink:a4fc9044-564-33855" + image: "kubelink:880420ac-564-36036" imagePullPolicy: IfNotPresent healthPort: 50052 podSecurityContext: @@ -173,7 +174,7 @@ components: keyName: postgresql-password kubewatch: registry: "" - image: "kubewatch:a4fc9044-419-33852" + image: "kubewatch:880420ac-419-36026" imagePullPolicy: IfNotPresent healthPort: 8080 configs: @@ -199,7 +200,7 @@ components: volumeSize: "20Gi" gitsensor: registry: "" - image: "git-sensor:a4fc9044-200-33872" + image: "git-sensor:880420ac-200-36032" imagePullPolicy: IfNotPresent serviceMonitor: enabled: false @@ -217,7 +218,7 @@ components: # Values for lens lens: registry: "" - image: "lens:a4fc9044-333-33874" + image: "lens:880420ac-333-36029" imagePullPolicy: IfNotPresent secrets: {} resources: {} @@ -254,7 +255,7 @@ components: entMigratorImage: "devtron-utils:geni-v1.1.4" chartSync: registry: "" - image: chart-sync:a4fc9044-836-33878 + image: chart-sync:880420ac-836-36037 schedule: "0 19 * * *" extraConfigs: {} podSecurityContext: @@ -274,12 +275,18 @@ argo-cd: # -- If defined, a repository applied to all Argo CD deployments repository: quay.io/argoproj/argocd # -- Overrides the global Argo CD image tag whose default is the chart appVersion - tag: "v2.5.2" + tag: "v2.13.3" # -- If defined, a imagePullPolicy applied to all Argo CD deployments imagePullPolicy: IfNotPresent configs: secret: createSecret: false + cm: + create: false + # argocd-rbac-cm + rbac: + create: true + policy.default: role:admin # argocd-application-controller controller: args: @@ -342,7 +349,6 @@ argo-cd: tag: 7.0.5-alpine # argocd-server server: - configEnabled: false affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: @@ -365,9 +371,6 @@ argo-cd: - all readOnlyRootFilesystem: true runAsNonRoot: true - # argocd-rbac-cm - rbacConfig: - policy.default: role:admin # argocd-repo-server repoServer: affinity: @@ -401,14 +404,14 @@ argo-cd: readOnlyRootFilesystem: true runAsNonRoot: true applicationSet: - enabled: false + replicas: 0 notifications: enabled: false # Values for security integration security: enabled: false imageScanner: - image: "image-scanner:a4fc9044-141-33877" + image: "image-scanner:f21e02cb-141-34534" healthPort: 8080 configs: TRIVY_DB_REPOSITORY: mirror.gcr.io/aquasec/trivy-db @@ -427,7 +430,7 @@ security: notifier: enabled: false imagePullPolicy: IfNotPresent - image: "notifier:19d654ff-372-33876" + image: "notifier:00f17215-372-36041" configs: CD_ENVIRONMENT: PROD secrets: {} diff --git a/devtron-images.txt.source b/devtron-images.txt.source index d7b4499a3e..a7d6eefdd4 100644 --- a/devtron-images.txt.source +++ b/devtron-images.txt.source @@ -1,5 +1,5 @@ public.ecr.aws/docker/library/redis:7.0.5-alpine -quay.io/argoproj/argocd:v2.5.2 +quay.io/argoproj/argocd:v2.13.3 quay.io/argoproj/workflow-controller:v3.4.3 quay.io/devtron/alpine-k8s-utils:latest quay.io/devtron/alpine-netshoot:latest @@ -7,26 +7,26 @@ quay.io/devtron/authenticator:e414faff-393-13273 quay.io/devtron/bats:v1.4.1 quay.io/devtron/busybox:1.31.1 quay.io/devtron/centos-k8s-utils:latest -quay.io/devtron/chart-sync:a4fc9044-836-33878 -quay.io/devtron/ci-runner:a4fc9044-138-33875 +quay.io/devtron/chart-sync:880420ac-836-36037 +quay.io/devtron/ci-runner:880420ac-138-36030 quay.io/devtron/clair:4.3.6 quay.io/devtron/curl:7.73.0 -quay.io/devtron/dashboard:a85f2624-690-33873 +quay.io/devtron/dashboard:5196e935-690-36024 quay.io/devtron/devtron-utils:dup-chart-repo-v1.1.0 -quay.io/devtron/devtron:c8e75fb3-434-33854 +quay.io/devtron/devtron:1ae65fbb-434-36069 quay.io/devtron/dex:v2.30.2 -quay.io/devtron/git-sensor:a4fc9044-200-33872 +quay.io/devtron/git-sensor:880420ac-200-36032 quay.io/devtron/grafana:7.3.1 -quay.io/devtron/hyperion:c8e75fb3-280-33879 -quay.io/devtron/image-scanner:a4fc9044-141-33877 +quay.io/devtron/hyperion:1ae65fbb-280-36074 +quay.io/devtron/image-scanner:f21e02cb-141-34534 quay.io/devtron/inception:473deaa4-185-21582 quay.io/devtron/k8s-sidecar:1.1.0 quay.io/devtron/k8s-utils:tutum-curl quay.io/devtron/k9s-k8s-utils:latest quay.io/devtron/kubectl:latest -quay.io/devtron/kubelink:a4fc9044-564-33855 -quay.io/devtron/kubewatch:a4fc9044-419-33852 -quay.io/devtron/lens:a4fc9044-333-33874 +quay.io/devtron/kubelink:880420ac-564-36036 +quay.io/devtron/kubewatch:880420ac-419-36026 +quay.io/devtron/lens:880420ac-333-36029 quay.io/devtron/migrator:v4.16.2 quay.io/devtron/minideb:latest quay.io/devtron/minio-mc:RELEASE.2021-02-14T04-28-06Z @@ -34,7 +34,7 @@ quay.io/devtron/minio:RELEASE.2021-02-14T04-01-33Z quay.io/devtron/nats-box quay.io/devtron/nats-server-config-reloader:0.6.2 quay.io/devtron/nats:2.9.3-alpine -quay.io/devtron/notifier:19d654ff-372-33876 +quay.io/devtron/notifier:00f17215-372-36041 quay.io/devtron/postgres:14.9 quay.io/devtron/postgres_exporter:v0.10.1 quay.io/devtron/postgres_exporter:v0.4.7 diff --git a/manifests/install/devtron-installer.yaml b/manifests/install/devtron-installer.yaml index 96c19c3857..588ccb20de 100644 --- a/manifests/install/devtron-installer.yaml +++ b/manifests/install/devtron-installer.yaml @@ -4,4 +4,4 @@ metadata: name: installer-devtron namespace: devtroncd spec: - url: https://raw.githubusercontent.com/devtron-labs/devtron/v1.7.0/manifests/installation-script + url: https://raw.githubusercontent.com/devtron-labs/devtron/v1.8.0/manifests/installation-script diff --git a/manifests/installation-script b/manifests/installation-script index 547c3c0adf..b9571e0df9 100644 --- a/manifests/installation-script +++ b/manifests/installation-script @@ -1,4 +1,4 @@ -LTAG="v1.7.0"; +LTAG="v1.8.0"; REPO_RAW_URL="https://raw.githubusercontent.com/devtron-labs/devtron/"; shebang = `#!/bin/bash `; diff --git a/pkg/bean/app.go b/pkg/bean/app.go index d198f849c2..82b6c1e0fc 100644 --- a/pkg/bean/app.go +++ b/pkg/bean/app.go @@ -53,8 +53,8 @@ type SourceTypeConfig struct { type CreateAppDTO struct { Id int `json:"id,omitempty" validate:"number"` - AppName string `json:"appName" validate:"name-component,max=100"` - Description string `json:"description"` + AppName string `json:"appName" validate:"name-component,max=30"` + Description string `json:"description" validate:"max=350"` UserId int32 `json:"-"` //not exposed to UI Material []*GitMaterial `json:"material" validate:"dive,min=1"` TeamId int `json:"teamId,omitempty" validate:"number,required"` diff --git a/pkg/build/trigger/HandlerService.go b/pkg/build/trigger/HandlerService.go index 0e82e62cd2..4b6a0b37ad 100644 --- a/pkg/build/trigger/HandlerService.go +++ b/pkg/build/trigger/HandlerService.go @@ -5,6 +5,16 @@ import ( "context" "errors" "fmt" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "slices" + "strconv" + "strings" + "time" + "github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1" "github.com/caarlos0/env" "github.com/devtron-labs/common-lib/async" @@ -60,16 +70,7 @@ import ( "github.com/devtron-labs/devtron/util/sliceUtil" "github.com/go-pg/pg" "go.uber.org/zap" - "io/ioutil" "k8s.io/client-go/rest" - "net/http" - "os" - "path" - "path/filepath" - "slices" - "strconv" - "strings" - "time" ) type HandlerService interface { diff --git a/pkg/cluster/environment/bean/ephemeralContainerBean.go b/pkg/cluster/environment/bean/ephemeralContainerBean.go index 5db8b7a25b..5e72667b26 100644 --- a/pkg/cluster/environment/bean/ephemeralContainerBean.go +++ b/pkg/cluster/environment/bean/ephemeralContainerBean.go @@ -6,7 +6,7 @@ import ( ) type EphemeralContainerRequest struct { - BasicData *EphemeralContainerBasicData `json:"basicData"` + BasicData *EphemeralContainerBasicData `json:"basicData" validate:"required"` AdvancedData *EphemeralContainerAdvancedData `json:"advancedData"` Namespace string `json:"namespace" validate:"required"` ClusterId int `json:"clusterId" validate:"gt=0"` @@ -18,13 +18,13 @@ type EphemeralContainerRequest struct { } type EphemeralContainerAdvancedData struct { - Manifest string `json:"manifest"` + Manifest string `json:"manifest" validate:"required"` } type EphemeralContainerBasicData struct { - ContainerName string `json:"containerName"` - TargetContainerName string `json:"targetContainerName"` - Image string `json:"image"` + ContainerName string `json:"containerName" validate:"required"` + TargetContainerName string `json:"targetContainerName" validate:"required"` + Image string `json:"image" validate:"required"` } func (request EphemeralContainerRequest) GetContainerBean() repository.EphemeralContainerBean { @@ -38,3 +38,6 @@ func (request EphemeralContainerRequest) GetContainerBean() repository.Ephemeral IsExternallyCreated: false, } } + +const EXTERNAL_EPHIMERAL_CONTAINER_ERR string = "externally created ephemeral containers cannot be removed" +const EPHEMERAL_CONTAINER_NOT_FOUND_ERR string = "ephemeral container not found" diff --git a/pkg/k8s/application/k8sApplicationService.go b/pkg/k8s/application/k8sApplicationService.go index 028113b930..f8188f07f9 100644 --- a/pkg/k8s/application/k8sApplicationService.go +++ b/pkg/k8s/application/k8sApplicationService.go @@ -1038,7 +1038,7 @@ func (impl *K8sApplicationServiceImpl) TerminatePodEphemeralContainer(req bean5. return false, err } if container == nil { - return false, errors.New("externally created ephemeral containers cannot be removed") + return false, errors.New(bean5.EXTERNAL_EPHIMERAL_CONTAINER_ERR) } // Check if pod exists and is running before attempting to execute command var v1Client *v1.CoreV1Client @@ -1102,7 +1102,7 @@ func (impl *K8sApplicationServiceImpl) TerminatePodEphemeralContainer(req bean5. impl.logger.Errorw("error in saving ephemeral container data", "err", err) return true, err } - return true, nil + return true, errors.New(bean5.EPHEMERAL_CONTAINER_NOT_FOUND_ERR) } containerKillCommand := fmt.Sprintf("kill -16 $(pgrep -f '%s' -o)", fmt.Sprintf(k8sObjectUtils.EphemeralContainerStartingShellScriptFileName, terminalReq.ContainerName)) cmds := []string{"sh", "-c", containerKillCommand} diff --git a/pkg/k8s/bean/bean.go b/pkg/k8s/bean/bean.go index 9ad53a11f6..d6d08723e5 100644 --- a/pkg/k8s/bean/bean.go +++ b/pkg/k8s/bean/bean.go @@ -33,7 +33,7 @@ type ResourceRequestBean struct { DevtronAppIdentifier *bean.DevtronAppIdentifier `json:"-"` // For Devtron App Resources ClusterId int `json:"clusterId"` // clusterId is used when request is for direct cluster (not for helm release) ExternalArgoApplicationName string `json:"externalArgoApplicationName,omitempty"` - ExternalFluxAppIdentifier *bean2.FluxAppIdentifier `json: "-"` + ExternalFluxAppIdentifier *bean2.FluxAppIdentifier `json:"-"` ExternalArgoAppIdentifier *bean3.ArgoAppIdentifier `json:"-"` } diff --git a/releasenotes.md b/releasenotes.md index db70d037ab..4aa73edc81 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,33 +1,57 @@ -## v1.7.0 +## v1.8.0 ## Enhancements -- feat: Added Cronjob chart 1-6-0 (#6650) -- feat: wf logs (#6606) -- feat: Enable selection of all CI pipelines at once when the Environment filter is applied in Notifications (#6526) +- feat: added the plugin for aws ecr retagging (#6695) +- feat: Argo version update v2.13.3(#6817) +- feat: ArgoCD v2.13.3 support for private chart providers (#6766) +- feat: flux cd deployment (#6660) +- feat: add app name in labels list api (#6688) +- feat: Audit ci trigger, precd and post cd trigger so that retrigger can happen from last failed config snapshot (#6659) +- feat: optimize ci cd workflow (#6744) +- feat: automate API specs workflow and documentation (#6786) + ## Bugs -- fix: app workflow cd pipleine check (#6658) -- fix: panic fixes on concurrent delete request (#6657) -- fix: panic fix on concurrent deletion request (#6644) -- fix: duplicate entries in deployment history without override (#6637) -- fix: overriden pipeline ids filtering in case of material deletion (#6636) -- fix: prevent deletion of git material used in overridden CI templates (#6633) -- fix: ea mode fixes (#6624) -- fix: stack Manager issues (#6619) -- fix: Change ci to webhook fix (#6626) -- fix: oci chart deployment values.yaml and requirement.yaml not compatible (#6620) -- fix: panic fix installedApp type timeline update (#6614) -- fix: workflow getting incorrectly deleted in case of webhook and unreachable cluster's cd pipeline (#6602) -- fix: add safety checks to prevent index-out-of-range panics in CdHandler (#6597) -- fix: reverted telemetry connection error (#6587) -- fix: anomalies in deployment status timeline (#6569) -- fix: scoped var complex type resolution not working in patch type overrides (#6572) +- fix: argo sync (#6718) +- fix: cluster delete (#6706) +- fix: Notifier v1 removed (#6705) +- fix: app clone panic (#6696) +- fix: Spdy migration to websocket (#6682) +- fix: Fix scanning optimisation (#6683) +- fix: panic in logs api (#6684) +- fix: Empty migration seq (#6673) +- fix: Removing default value of idleReplicaCount (#6721) +- fix: send HideApiToken env var in api token create and update response (#6727) +- fix: removed timeout notification (#6760) +- fix: unify unit formatting and conversion for CPU and memory in cluster resources (#6768) +- fix: cron job suspend (#6771) +- fix: panic app group (#6785) +- fix: api responses enhancements (#6797) +- fix: post apis minor validations (#6811) +- fix: dex scopes override default required scopes (#6816) +- fix: error handler correction (#6824) + ## Others -- chore: when a cluster event occurs, create config map instead of secret (#6607) -- chore: Gpu workload chart (#6608) -- misc: update sample dockerfiles use non-root user (UID 2002) and base images (#6512) -- misc: wire in EA (#6616) -- chore: removed multi-arch section from readme (#6613) -- chore: git sensor grpc lb policy change (#6610) -- misc: go routines wrapped into panic safe function (#6589) -- chore: http transport service refactoring (#6592) -- misc: GetConfigDBObj in tx (#6584) +- chore: added sql file of 4.21 (#6716) +- misc: added support for service extraSpec (#6702) +- chore: when output dir path is /devtroncd in any pipeline stage step then the ci runner is stuck in recursive self-copy situation (#6686) +- misc: Removing default value of idleReplicaCount (#6730) +- chore: chart service crud and configProperties (#6642) +- misc: Added Github Actions for linting and building api specs (#6720) +- misc: open api spec addition (#6750) +- misc: Updated the build-docs.yaml (#6752) +- misc: fixing swagger openapi.yaml (#6751) +- misc: adding codeowner for swagger api spec (#6754) +- misc: swagger api spec fixes (#6755) +- misc: Update openapi spec (#6758) +- misc: updated the title of openapi specs (#6759) +- misc: Timeout main notification (#6761) +- misc: Api changes for env enhancement (#6769) +- misc: changes in description of api spec (#6767) +- chore: Update `authenticator` and `common-lib` dependencies to latest versions. (#6779) +- chore: added sql files for sync (#6780) +- chore: migration number chnage (#6783) +- chore: add resource recommendation APIs and update openapi.yaml (#6784) +- misc: added support for cronjob annotations and probes (#6787) +- misc: Validation on payload and Error handling, API Specs revised (#6790) +- misc: sql query param refacter (#6810) + diff --git a/scripts/sql/34504201_poll_artifact_data.down.sql b/scripts/sql/34504201_poll_artifact_data.down.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/scripts/sql/34504201_poll_artifact_data.up.sql b/scripts/sql/34504201_poll_artifact_data.up.sql new file mode 100644 index 0000000000..e69de29bb2 diff --git a/specs/application/application-management.yaml b/specs/application/application-management.yaml new file mode 100644 index 0000000000..b0d32f96c2 --- /dev/null +++ b/specs/application/application-management.yaml @@ -0,0 +1,415 @@ +openapi: "3.0.3" +info: + title: Devtron Application Management API + description: API specifications for application hibernation, deployment status, and lifecycle management + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + HibernateRequest: + type: object + properties: + appId: + type: string + example: "123" + resources: + type: array + items: + $ref: '#/components/schemas/HibernateTargetObject' + + HibernateTargetObject: + type: object + properties: + group: + type: string + example: "apps" + kind: + type: string + example: "Deployment" + version: + type: string + example: "v1" + name: + type: string + example: "my-app" + namespace: + type: string + example: "default" + + HibernateStatus: + type: object + properties: + success: + type: boolean + example: true + errorMessage: + type: string + example: "" + targetObject: + $ref: '#/components/schemas/HibernateTargetObject' + + PipelineTriggerRequest: + type: object + properties: + appId: + type: integer + example: 123 + environmentId: + type: integer + example: 456 + pipelineId: + type: integer + example: 789 + +tags: + - name: Application Hibernation + description: Application hibernation and unhibernation operations + - name: Pipeline Management + description: CD pipeline trigger and management operations + - name: Deployment Status + description: Deployment status and timeline operations + +paths: + /application/hibernate: + post: + tags: + - Application Hibernation + summary: Hibernate application + description: Hibernates the specified application resources + operationId: hibernateApplication + parameters: + - name: appType + in: query + required: true + schema: + type: integer + enum: [1, 2, 3] + description: "App type identifier: 1=Helm, 2=Argo, 3=Flux" + example: 2 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/HibernateRequest' + example: + appId: "123" + resources: + - group: "apps" + kind: "Deployment" + version: "v1" + name: "my-app" + namespace: "default" + responses: + '200': + description: Application hibernated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/HibernateStatus' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /application/unhibernate: + post: + tags: + - Application Hibernation + summary: Unhibernate application + description: Unhibernates the specified application resources + operationId: unhibernateApplication + parameters: + - name: appType + in: query + required: true + schema: + type: integer + enum: [1, 2, 3] + description: "App type identifier: 1=Helm, 2=Argo, 3=Flux" + example: 2 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/HibernateRequest' + example: + appId: "123" + resources: + - group: "apps" + kind: "Deployment" + version: "v1" + name: "my-app" + namespace: "default" + responses: + '200': + description: Application unhibernated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/HibernateStatus' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/cd-pipeline/trigger: + post: + tags: + - Pipeline Management + summary: Trigger CD pipeline + description: Triggers a CD pipeline deployment for the specified application and environment + operationId: triggerCdPipeline + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PipelineTriggerRequest' + example: + appId: 123 + environmentId: 456 + pipelineId: 789 + responses: + '200': + description: CD pipeline triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/deployment-status/timeline/{appId}/{envId}: + get: + tags: + - Deployment Status + summary: Get deployment status timeline + description: Retrieves the deployment status timeline for the specified application and environment + operationId: getDeploymentStatusTimeline + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + - name: envId + in: path + required: true + schema: + type: integer + example: 456 + responses: + '200': + description: Deployment status timeline retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/deployment-status/manual-sync/{appId}/{envId}: + get: + tags: + - Deployment Status + summary: Manual sync deployment status + description: Manually synchronizes the deployment status for the specified application and environment + operationId: manualSyncDeploymentStatus + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + - name: envId + in: path + required: true + schema: + type: integer + example: 456 + responses: + '200': + description: Manual sync completed successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/application/material-management.yaml b/specs/application/material-management.yaml new file mode 100644 index 0000000000..bb19243f81 --- /dev/null +++ b/specs/application/material-management.yaml @@ -0,0 +1,324 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Orchestrator Application Material Management API + description: | + API specifications for Devtron orchestrator application material management endpoints. + This includes creating and updating git materials for applications, managing git repositories, + checkout paths, and filter patterns for CI/CD pipelines. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Status message + result: + type: object + description: Response result data + + ErrorResponse: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error status + errors: + type: array + items: + type: string + description: List of error messages + + GitMaterial: + type: object + required: + - url + - gitProviderId + - checkoutPath + properties: + id: + type: integer + description: Material ID (for updates) + example: 456 + name: + type: string + description: Material name (auto-generated if not provided) + example: "my-app-material" + url: + type: string + description: Git repository URL + example: "https://github.com/user/repo.git" + gitProviderId: + type: integer + description: Git provider ID + example: 1 + minimum: 1 + checkoutPath: + type: string + description: Path where code will be checked out + example: "./" + default: "./" + fetchSubmodules: + type: boolean + description: Whether to fetch git submodules + example: false + default: false + filterPattern: + type: array + items: + type: string + description: File patterns to include/exclude during build + example: ["*.yaml", "!test/*"] + + CreateMaterialRequest: + type: object + required: + - appId + - material + properties: + appId: + type: integer + description: Application ID + example: 123 + minimum: 1 + material: + type: array + items: + $ref: '#/components/schemas/GitMaterial' + description: Array of git materials to create + minItems: 1 + + UpdateMaterialRequest: + type: object + required: + - appId + - material + properties: + appId: + type: integer + description: Application ID + example: 123 + minimum: 1 + material: + $ref: '#/components/schemas/GitMaterial' + description: Git material to update + + MaterialResponse: + type: object + properties: + id: + type: integer + description: Material ID + name: + type: string + description: Material name + url: + type: string + description: Git repository URL + gitProviderId: + type: integer + description: Git provider ID + checkoutPath: + type: string + description: Checkout path + fetchSubmodules: + type: boolean + description: Whether submodules are fetched + filterPattern: + type: array + items: + type: string + description: Filter patterns + active: + type: boolean + description: Whether material is active + createdOn: + type: string + format: date-time + description: Creation timestamp + createdBy: + type: integer + description: ID of user who created the material + +security: + - bearerAuth: [] + +paths: + /app/material: + post: + tags: + - Application Material Management + summary: Create git materials for application + description: | + Creates one or more git materials for an application. Git materials define the source code repositories + that will be used for CI/CD pipelines. Each material specifies a git repository URL, checkout path, + and optional filter patterns. + operationId: createMaterial + requestBody: + description: Material creation request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateMaterialRequest' + examples: + single_material: + summary: Single material example + value: + appId: 123 + material: + - url: "https://github.com/user/repo.git" + checkoutPath: "./" + gitProviderId: 1 + fetchSubmodules: false + filterPattern: ["*.yaml", "!test/*"] + multiple_materials: + summary: Multiple materials example + value: + appId: 123 + material: + - url: "https://github.com/user/frontend.git" + checkoutPath: "./frontend" + gitProviderId: 1 + fetchSubmodules: false + filterPattern: ["src/**", "!src/test/**"] + - url: "https://github.com/user/backend.git" + checkoutPath: "./backend" + gitProviderId: 1 + fetchSubmodules: true + filterPattern: ["*.go", "!*_test.go"] + responses: + '200': + description: Materials created successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/MaterialResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + put: + tags: + - Application Material Management + summary: Update git material for application + description: | + Updates an existing git material for an application. This allows modifying the git repository URL, + checkout path, filter patterns, and other material properties. + operationId: updateMaterial + requestBody: + description: Material update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateMaterialRequest' + examples: + update_material: + summary: Update material example + value: + appId: 123 + material: + id: 456 + url: "https://github.com/user/updated-repo.git" + checkoutPath: "./src" + gitProviderId: 1 + fetchSubmodules: true + filterPattern: ["src/**", "!src/test/**"] + responses: + '200': + description: Material updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/MaterialResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Material not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/audit/deployment-history-api-spec.yaml b/specs/audit/deployment-history-api-spec.yaml index 2160b67951..1c2cb037f0 100644 --- a/specs/audit/deployment-history-api-spec.yaml +++ b/specs/audit/deployment-history-api-spec.yaml @@ -2,6 +2,12 @@ openapi: "3.0.3" info: version: 1.0.0 title: Helm App Deployment History + description: API for retrieving deployment history information for Helm applications + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + paths: /orchestrator/application/deployment-history: get: diff --git a/specs/audit/deployment-history.yaml b/specs/audit/deployment-history.yaml index 7e68a116d3..926ca9be2a 100644 --- a/specs/audit/deployment-history.yaml +++ b/specs/audit/deployment-history.yaml @@ -2,6 +2,12 @@ openapi: "3.0.0" info: version: 1.0.0 title: Deployment History API + description: API for retrieving deployment history and component details + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + paths: /orchestrator/app/history/deployed-component/detail/{appId}/{pipelineId}/{id}: get: diff --git a/specs/deployment/cd-pipeline-workflow.yaml b/specs/deployment/cd-pipeline-workflow.yaml new file mode 100644 index 0000000000..273d4966b4 --- /dev/null +++ b/specs/deployment/cd-pipeline-workflow.yaml @@ -0,0 +1,933 @@ +openapi: "3.0.3" +info: + version: 1.0.0 + title: CD Pipeline Workflow Management + description: Devtron API for CD pipeline workflow history, logs, status, and artifact management + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Labs + email: support@devtron.ai + url: https://devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: https://devtron-ent-2.devtron.info + description: Devtron Enterprise Server + +security: + - bearerAuth: [] + +paths: + /orchestrator/app/cd-pipeline/workflow/history/{appId}/{environmentId}/{pipelineId}: + get: + tags: + - CD Pipeline Workflow + summary: Get CD Pipeline Workflow History + description: | + Retrieves the deployment history for a specific CD pipeline in an environment. + Returns a list of workflow runs with their status, timestamps, and metadata. + + **Use Cases:** + - View deployment history for troubleshooting + - Track deployment frequency and success rates + - Audit deployment activities + + **Required Permissions:** + - Application view permission + - Environment view permission + operationId: getCdPipelineWorkflowHistory + parameters: + - name: appId + in: path + required: true + description: Unique identifier of the application + schema: + type: integer + minimum: 1 + example: 123 + - name: environmentId + in: path + required: true + description: Unique identifier of the environment + schema: + type: integer + minimum: 1 + example: 456 + - name: pipelineId + in: path + required: true + description: Unique identifier of the CD pipeline + schema: + type: integer + minimum: 1 + example: 789 + - name: offset + in: query + required: false + description: Number of records to skip for pagination + schema: + type: integer + minimum: 0 + default: 0 + example: 0 + - name: size + in: query + required: false + description: Number of records to return per page + schema: + type: integer + minimum: 1 + maximum: 100 + default: 20 + example: 20 + responses: + '200': + description: Workflow history retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowHistoryResponse' + examples: + successful_history: + summary: Successful deployment history + value: + code: 200 + status: "OK" + result: + workflows: + - workflowId: 12345 + workflowRunnerId: 67890 + status: "Succeeded" + startedOn: "2024-01-15T10:30:00Z" + finishedOn: "2024-01-15T10:35:00Z" + triggeredBy: "user@example.com" + artifactId: 98765 + imageTag: "v1.2.3" + message: "Deployment completed successfully" + - workflowId: 12344 + workflowRunnerId: 67889 + status: "Failed" + startedOn: "2024-01-15T09:30:00Z" + finishedOn: "2024-01-15T09:32:00Z" + triggeredBy: "user@example.com" + artifactId: 98764 + imageTag: "v1.2.2" + message: "Deployment failed: pod startup timeout" + totalCount: 25 + offset: 0 + size: 20 + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + invalid_app_id: + summary: Invalid application ID + value: + code: 400 + status: "Bad Request" + errors: + - code: "000" + internalMessage: "invalid appId: must be positive integer" + userMessage: "Invalid application ID provided" + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - Insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not found - Application, environment, or pipeline not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + pipeline_not_found: + summary: Pipeline not found + value: + code: 404 + status: "Not Found" + errors: + - code: "11006" + internalMessage: "cd pipeline not found" + userMessage: "CD pipeline not found for the specified application and environment" + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orchestrator/app/cd-pipeline/workflow/logs/{appId}/{environmentId}/{pipelineId}/{workflowId}: + get: + tags: + - CD Pipeline Workflow + summary: Get CD Pipeline Workflow Logs + description: | + Retrieves pre-deployment and post-deployment logs for a specific workflow run. + Supports both streaming and historical log retrieval. + + **Use Cases:** + - Debug deployment failures + - Monitor deployment progress + - Audit deployment activities + + **Required Permissions:** + - Application view permission + - Environment view permission + operationId: getCdPipelineWorkflowLogs + parameters: + - name: appId + in: path + required: true + description: Unique identifier of the application + schema: + type: integer + minimum: 1 + example: 123 + - name: environmentId + in: path + required: true + description: Unique identifier of the environment + schema: + type: integer + minimum: 1 + example: 456 + - name: pipelineId + in: path + required: true + description: Unique identifier of the CD pipeline + schema: + type: integer + minimum: 1 + example: 789 + - name: workflowId + in: path + required: true + description: Unique identifier of the workflow run + schema: + type: integer + minimum: 1 + example: 12345 + - name: logType + in: query + required: false + description: Type of logs to retrieve + schema: + type: string + enum: ["pre-deployment", "post-deployment", "all"] + default: "all" + example: "all" + - name: follow + in: query + required: false + description: Whether to stream logs in real-time + schema: + type: boolean + default: false + example: false + responses: + '200': + description: Workflow logs retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowLogsResponse' + text/plain: + schema: + type: string + description: Raw log content when follow=true + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - Insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not found - Workflow not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orchestrator/app/cd-pipeline/workflow/trigger-info/{appId}/{environmentId}/{pipelineId}/{workflowRunnerId}: + get: + tags: + - CD Pipeline Workflow + summary: Get CD Workflow Trigger Information + description: | + Retrieves detailed information about a specific workflow run including trigger details, + configuration, and runtime parameters. + + **Use Cases:** + - Get detailed workflow execution information + - Debug workflow configuration issues + - Audit workflow trigger parameters + + **Required Permissions:** + - Application view permission + - Environment view permission + operationId: getCdWorkflowTriggerInfo + parameters: + - name: appId + in: path + required: true + description: Unique identifier of the application + schema: + type: integer + minimum: 1 + example: 123 + - name: environmentId + in: path + required: true + description: Unique identifier of the environment + schema: + type: integer + minimum: 1 + example: 456 + - name: pipelineId + in: path + required: true + description: Unique identifier of the CD pipeline + schema: + type: integer + minimum: 1 + example: 789 + - name: workflowRunnerId + in: path + required: true + description: Unique identifier of the workflow runner + schema: + type: integer + minimum: 1 + example: 67890 + responses: + '200': + description: Workflow trigger information retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowTriggerInfoResponse' + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - Insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not found - Workflow runner not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orchestrator/app/cd-pipeline/workflow/download/{appId}/{environmentId}/{pipelineId}/{workflowRunnerId}: + get: + tags: + - CD Pipeline Workflow + summary: Download CD Workflow Artifacts + description: | + Downloads deployment artifacts (logs, manifests, etc.) for a specific workflow run. + Returns a compressed archive containing all workflow artifacts. + + **Use Cases:** + - Download deployment artifacts for offline analysis + - Archive deployment records for compliance + - Share deployment artifacts with team members + + **Required Permissions:** + - Application view permission + - Environment view permission + operationId: downloadCdWorkflowArtifacts + parameters: + - name: appId + in: path + required: true + description: Unique identifier of the application + schema: + type: integer + minimum: 1 + example: 123 + - name: environmentId + in: path + required: true + description: Unique identifier of the environment + schema: + type: integer + minimum: 1 + example: 456 + - name: pipelineId + in: path + required: true + description: Unique identifier of the CD pipeline + schema: + type: integer + minimum: 1 + example: 789 + - name: workflowRunnerId + in: path + required: true + description: Unique identifier of the workflow runner + schema: + type: integer + minimum: 1 + example: 67890 + responses: + '200': + description: Workflow artifacts downloaded successfully + content: + application/zip: + schema: + type: string + format: binary + description: Compressed archive containing workflow artifacts + application/octet-stream: + schema: + type: string + format: binary + description: Binary artifact file + headers: + Content-Disposition: + description: Attachment filename + schema: + type: string + example: "workflow-artifacts-67890.zip" + Content-Length: + description: Size of the download in bytes + schema: + type: integer + example: 1048576 + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - Insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not found - Workflow artifacts not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + examples: + artifacts_not_found: + summary: Artifacts not available + value: + code: 404 + status: "Not Found" + errors: + - code: "11006" + internalMessage: "workflow artifacts not found or expired" + userMessage: "Workflow artifacts are not available for download" + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orchestrator/app/cd-pipeline/workflow/status/{appId}/{environmentId}/{pipelineId}: + get: + tags: + - CD Pipeline Workflow + summary: Get CD Pipeline Deployment Stage Status + description: | + Retrieves the current status of all deployment stages for a CD pipeline. + Shows the progress and status of pre-deployment, deployment, and post-deployment stages. + + **Use Cases:** + - Monitor real-time deployment progress + - Check deployment stage status + - Debug deployment stage failures + + **Required Permissions:** + - Application view permission + - Environment view permission + operationId: getCdPipelineDeploymentStatus + parameters: + - name: appId + in: path + required: true + description: Unique identifier of the application + schema: + type: integer + minimum: 1 + example: 123 + - name: environmentId + in: path + required: true + description: Unique identifier of the environment + schema: + type: integer + minimum: 1 + example: 456 + - name: pipelineId + in: path + required: true + description: Unique identifier of the CD pipeline + schema: + type: integer + minimum: 1 + example: 789 + responses: + '200': + description: Deployment stage status retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/DeploymentStatusResponse' + examples: + deployment_in_progress: + summary: Deployment in progress + value: + code: 200 + status: "OK" + result: + pipelineId: 789 + currentStage: "DEPLOYMENT" + overallStatus: "Running" + stages: + - name: "PRE_DEPLOYMENT" + status: "Succeeded" + startTime: "2024-01-15T10:30:00Z" + endTime: "2024-01-15T10:32:00Z" + message: "Pre-deployment scripts completed successfully" + - name: "DEPLOYMENT" + status: "Running" + startTime: "2024-01-15T10:32:00Z" + endTime: null + message: "Deploying application pods..." + - name: "POST_DEPLOYMENT" + status: "Pending" + startTime: null + endTime: null + message: "Waiting for deployment to complete" + '400': + description: Bad request - Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - Insufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Not found - Pipeline not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + WorkflowHistoryResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + $ref: '#/components/schemas/WorkflowHistoryResult' + + WorkflowHistoryResult: + type: object + properties: + workflows: + type: array + description: List of workflow runs + items: + $ref: '#/components/schemas/WorkflowRun' + totalCount: + type: integer + description: Total number of workflow runs + example: 25 + offset: + type: integer + description: Current offset for pagination + example: 0 + size: + type: integer + description: Number of records returned + example: 20 + + WorkflowRun: + type: object + properties: + workflowId: + type: integer + description: Unique identifier of the workflow + example: 12345 + workflowRunnerId: + type: integer + description: Unique identifier of the workflow run + example: 67890 + status: + type: string + description: Current status of the workflow + enum: ["Starting", "Running", "Succeeded", "Failed", "Cancelled", "Aborted"] + example: "Succeeded" + startedOn: + type: string + format: date-time + description: Timestamp when the workflow started + example: "2024-01-15T10:30:00Z" + finishedOn: + type: string + format: date-time + description: Timestamp when the workflow finished + example: "2024-01-15T10:35:00Z" + triggeredBy: + type: string + description: User who triggered the workflow + example: "user@example.com" + artifactId: + type: integer + description: ID of the artifact being deployed + example: 98765 + imageTag: + type: string + description: Docker image tag being deployed + example: "v1.2.3" + message: + type: string + description: Status message or error details + example: "Deployment completed successfully" + + WorkflowLogsResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + $ref: '#/components/schemas/WorkflowLogsResult' + + WorkflowLogsResult: + type: object + properties: + preDeploymentLogs: + type: string + description: Pre-deployment script logs + example: "Starting pre-deployment tasks...\nRunning database migrations...\nMigrations completed successfully" + postDeploymentLogs: + type: string + description: Post-deployment script logs + example: "Starting post-deployment tasks...\nRunning health checks...\nAll health checks passed" + deploymentLogs: + type: string + description: Main deployment logs + example: "Deploying application...\nPods starting...\nDeployment successful" + + ErrorResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 400 + status: + type: string + description: Error status + example: "Bad Request" + errors: + type: array + description: List of error details + items: + $ref: '#/components/schemas/ErrorDetail' + + ErrorDetail: + type: object + properties: + code: + type: string + description: Error code + example: "000" + internalMessage: + type: string + description: Internal error message for debugging + example: "validation failed for field 'appId': required" + userMessage: + type: string + description: User-friendly error message + example: "Application ID is required" + + WorkflowTriggerInfoResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + $ref: '#/components/schemas/WorkflowTriggerInfo' + + WorkflowTriggerInfo: + type: object + properties: + workflowRunnerId: + type: integer + description: Unique identifier of the workflow runner + example: 67890 + triggeredBy: + type: string + description: User who triggered the workflow + example: "user@example.com" + triggeredAt: + type: string + format: date-time + description: Timestamp when the workflow was triggered + example: "2024-01-15T10:30:00Z" + triggerType: + type: string + description: Type of trigger + enum: ["MANUAL", "AUTOMATIC", "WEBHOOK"] + example: "MANUAL" + artifactInfo: + $ref: '#/components/schemas/ArtifactInfo' + deploymentConfig: + $ref: '#/components/schemas/DeploymentConfig' + runtimeParameters: + type: object + description: Runtime parameters used for deployment + additionalProperties: + type: string + example: + ENVIRONMENT: "production" + REPLICAS: "3" + CPU_LIMIT: "500m" + + ArtifactInfo: + type: object + properties: + artifactId: + type: integer + description: Unique identifier of the artifact + example: 98765 + imageTag: + type: string + description: Docker image tag + example: "v1.2.3" + imageDigest: + type: string + description: Docker image digest + example: "sha256:abcd1234..." + buildTime: + type: string + format: date-time + description: Timestamp when the artifact was built + example: "2024-01-15T10:00:00Z" + commitHash: + type: string + description: Git commit hash + example: "abc123def456" + commitMessage: + type: string + description: Git commit message + example: "Fix critical bug in payment processing" + + DeploymentConfig: + type: object + properties: + strategy: + type: string + description: Deployment strategy + enum: ["ROLLING", "BLUE_GREEN", "RECREATE", "CANARY"] + example: "ROLLING" + namespace: + type: string + description: Kubernetes namespace + example: "production" + replicas: + type: integer + description: Number of replicas + example: 3 + resources: + $ref: '#/components/schemas/ResourceRequirements' + environmentVariables: + type: object + description: Environment variables + additionalProperties: + type: string + example: + DATABASE_URL: "postgresql://..." + REDIS_URL: "redis://..." + + ResourceRequirements: + type: object + properties: + requests: + $ref: '#/components/schemas/ResourceSpec' + limits: + $ref: '#/components/schemas/ResourceSpec' + + ResourceSpec: + type: object + properties: + cpu: + type: string + description: CPU requirement + example: "500m" + memory: + type: string + description: Memory requirement + example: "512Mi" + + DeploymentStatusResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + $ref: '#/components/schemas/DeploymentStatus' + + DeploymentStatus: + type: object + properties: + pipelineId: + type: integer + description: Unique identifier of the CD pipeline + example: 789 + currentStage: + type: string + description: Currently executing stage + enum: ["PRE_DEPLOYMENT", "DEPLOYMENT", "POST_DEPLOYMENT", "COMPLETED"] + example: "DEPLOYMENT" + overallStatus: + type: string + description: Overall deployment status + enum: ["Pending", "Running", "Succeeded", "Failed", "Cancelled"] + example: "Running" + stages: + type: array + description: List of deployment stages + items: + $ref: '#/components/schemas/DeploymentStage' + + DeploymentStage: + type: object + properties: + name: + type: string + description: Stage name + enum: ["PRE_DEPLOYMENT", "DEPLOYMENT", "POST_DEPLOYMENT"] + example: "DEPLOYMENT" + status: + type: string + description: Stage status + enum: ["Pending", "Running", "Succeeded", "Failed", "Cancelled"] + example: "Running" + startTime: + type: string + format: date-time + description: Stage start time + example: "2024-01-15T10:32:00Z" + endTime: + type: string + format: date-time + description: Stage end time (null if still running) + example: "2024-01-15T10:35:00Z" + message: + type: string + description: Stage status message + example: "Deploying application pods..." diff --git a/specs/deployment/pipeline.yaml b/specs/deployment/pipeline.yaml index 7b6a8940bf..34134e35e8 100644 --- a/specs/deployment/pipeline.yaml +++ b/specs/deployment/pipeline.yaml @@ -56,29 +56,66 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' - /orchestrator/deployment/pipeline/trigger: + /orchestrator/app/cd-pipeline/trigger: post: - description: Trigger a deployment pipeline + description: Trigger a CD pipeline deployment with configuration override requestBody: - description: Pipeline trigger details + description: CD pipeline trigger details with deployment configuration required: true content: application/json: schema: - $ref: '#/components/schemas/PipelineTrigger' + $ref: '#/components/schemas/CdPipelineTrigger' + examples: + manual_trigger: + summary: Manual deployment trigger + value: + pipelineId: 789 + artifactId: 98765 + triggeredBy: 123 + deploymentType: "HELM" + deploymentStrategy: "ROLLING" + configOverrides: + replicas: 3 + environment: "production" + runtimeParameters: + CPU_LIMIT: "500m" + MEMORY_LIMIT: "512Mi" responses: '200': - description: Pipeline triggered successfully + description: CD pipeline triggered successfully content: application/json: schema: - $ref: '#/components/schemas/PipelineTriggerResponse' + $ref: '#/components/schemas/CdPipelineTriggerResponse' + examples: + successful_trigger: + summary: Successful trigger + value: + code: 200 + status: "OK" + result: + workflowId: 12345 + workflowRunnerId: 67890 + pipelineId: 789 + status: "Starting" + message: "CD pipeline triggered successfully" '400': - description: Bad Request + description: Bad Request - Invalid trigger parameters content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + invalid_pipeline: + summary: Invalid pipeline ID + value: + code: 400 + status: "Bad Request" + errors: + - code: "000" + internalMessage: "invalid pipelineId: pipeline not found" + userMessage: "The specified pipeline does not exist" '401': description: Unauthorized content: @@ -86,11 +123,21 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' '403': - description: Forbidden + description: Forbidden - Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + examples: + insufficient_permissions: + summary: Insufficient permissions + value: + code: 403 + status: "Forbidden" + errors: + - code: "000" + internalMessage: "unauthorized user" + userMessage: "Insufficient permissions to trigger this pipeline" '500': description: Internal Server Error content: @@ -140,29 +187,88 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' - /orchestrator/deployment/pipeline/history: - get: - description: Get deployment pipeline history - parameters: - - name: appId - in: query - required: true - schema: - type: integer - description: Application ID - - name: envId - in: query - required: true - schema: - type: integer - description: Environment ID + /orchestrator/app/update-release-status: + post: + description: Update release status for a deployment + requestBody: + description: Release status update details + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ReleaseStatusUpdate' + examples: + success_update: + summary: Mark release as successful + value: + workflowRunnerId: 67890 + status: "SUCCESS" + message: "Deployment completed successfully" + updatedBy: 123 + responses: + '200': + description: Release status updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ReleaseStatusResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /orchestrator/app/stop-start-app: + post: + description: Start or stop application pods + requestBody: + description: Application start/stop request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AppStartStopRequest' + examples: + stop_app: + summary: Stop application + value: + appId: 123 + environmentId: 456 + action: "STOP" + requestedBy: 789 + start_app: + summary: Start application + value: + appId: 123 + environmentId: 456 + action: "START" + requestedBy: 789 responses: '200': - description: Pipeline history retrieved successfully + description: Application start/stop operation initiated successfully content: application/json: schema: - $ref: '#/components/schemas/PipelineHistory' + $ref: '#/components/schemas/AppStartStopResponse' '400': description: Bad Request content: @@ -243,39 +349,223 @@ components: type: string description: Name of the pipeline - PipelineTrigger: + CdPipelineTrigger: type: object required: - pipelineId + - artifactId + - triggeredBy properties: pipelineId: type: integer - description: Pipeline ID + description: CD Pipeline ID + minimum: 1 + example: 789 artifactId: type: integer description: Artifact ID to deploy + minimum: 1 + example: 98765 triggeredBy: type: integer description: User ID who triggered the pipeline + minimum: 1 + example: 123 + deploymentType: + type: string + description: Type of deployment + enum: ["HELM", "ARGOCD", "MANIFEST"] + example: "HELM" + deploymentStrategy: + type: string + description: Deployment strategy + enum: ["ROLLING", "BLUE_GREEN", "RECREATE", "CANARY"] + example: "ROLLING" + configOverrides: + type: object + description: Configuration overrides for this deployment + additionalProperties: + oneOf: + - type: string + - type: number + - type: boolean + example: + replicas: 3 + environment: "production" + enableMetrics: true + runtimeParameters: + type: object + description: Runtime parameters for deployment + additionalProperties: + type: string + example: + CPU_LIMIT: "500m" + MEMORY_LIMIT: "512Mi" + LOG_LEVEL: "INFO" + preDeploymentScript: + type: string + description: Pre-deployment script to execute + example: "#!/bin/bash\necho 'Running pre-deployment checks...'" + postDeploymentScript: + type: string + description: Post-deployment script to execute + example: "#!/bin/bash\necho 'Running post-deployment validation...'" - PipelineTriggerResponse: + CdPipelineTriggerResponse: type: object properties: code: type: integer - description: Status code + description: HTTP status code + example: 200 status: type: string - description: Status message + description: Response status + example: "OK" result: type: object properties: + workflowId: + type: integer + description: Workflow ID + example: 12345 + workflowRunnerId: + type: integer + description: Workflow Runner ID + example: 67890 pipelineId: type: integer description: Pipeline ID - workflowId: + example: 789 + status: + type: string + description: Initial workflow status + enum: ["Starting", "Queued", "Failed"] + example: "Starting" + message: + type: string + description: Status message + example: "CD pipeline triggered successfully" + + ReleaseStatusUpdate: + type: object + required: + - workflowRunnerId + - status + - updatedBy + properties: + workflowRunnerId: + type: integer + description: Workflow Runner ID + minimum: 1 + example: 67890 + status: + type: string + description: Release status + enum: ["SUCCESS", "FAILED", "CANCELLED", "TIMEOUT"] + example: "SUCCESS" + message: + type: string + description: Status message or failure reason + example: "Deployment completed successfully" + updatedBy: + type: integer + description: User ID who updated the status + minimum: 1 + example: 123 + + ReleaseStatusResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + type: object + properties: + workflowRunnerId: type: integer - description: Workflow ID + description: Workflow Runner ID + example: 67890 + status: + type: string + description: Updated release status + example: "SUCCESS" + updatedAt: + type: string + format: date-time + description: Timestamp when status was updated + example: "2024-01-15T10:35:00Z" + + AppStartStopRequest: + type: object + required: + - appId + - environmentId + - action + - requestedBy + properties: + appId: + type: integer + description: Application ID + minimum: 1 + example: 123 + environmentId: + type: integer + description: Environment ID + minimum: 1 + example: 456 + action: + type: string + description: Action to perform + enum: ["START", "STOP"] + example: "STOP" + requestedBy: + type: integer + description: User ID who requested the action + minimum: 1 + example: 789 + + AppStartStopResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + example: 200 + status: + type: string + description: Response status + example: "OK" + result: + type: object + properties: + appId: + type: integer + description: Application ID + example: 123 + environmentId: + type: integer + description: Environment ID + example: 456 + action: + type: string + description: Action performed + example: "STOP" + status: + type: string + description: Operation status + enum: ["INITIATED", "IN_PROGRESS", "COMPLETED", "FAILED"] + example: "INITIATED" + message: + type: string + description: Operation message + example: "Application stop operation initiated successfully" PipelineRollback: type: object diff --git a/specs/ent-only/panels_api-spec.yaml b/specs/ent-only/panels_api-spec.yaml index a10cd6e255..b3f66d5076 100644 --- a/specs/ent-only/panels_api-spec.yaml +++ b/specs/ent-only/panels_api-spec.yaml @@ -116,6 +116,29 @@ paths: '204': description: Panel deleted successfully + /orchestrator/clusters/{cluster_id}/panel: + post: + summary: Create a new panel for a specific cluster (plural clusters path) + parameters: + - in: path + name: cluster_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Panel' + responses: + '201': + description: Panel created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/Panel' + components: schemas: Panel: diff --git a/specs/kubernetes/apis.yaml b/specs/kubernetes/apis.yaml index c2cd6a652e..642b097111 100644 --- a/specs/kubernetes/apis.yaml +++ b/specs/kubernetes/apis.yaml @@ -44,12 +44,24 @@ components: K8sRequestDto: type: object + required: + - resourceIdentifier properties: resourceIdentifier: $ref: '#/components/schemas/ResourceIdentifier' patch: type: string - description: patch data for update operations + description: | + JSON patch data for update operations. Contains the complete Kubernetes manifest + for the resource being updated/edited. + example: '{"apiVersion":"v1","kind":"Pod","metadata":{"name":"my-pod","namespace":"default"},"spec":{"containers":[{"name":"nginx","image":"nginx:1.25"}]}}' + forceDelete: + type: boolean + nullable: true + description: | + Force delete the resource. When true, the resource will be deleted immediately + without waiting for graceful termination. Only applicable for delete operations. + example: false ResourceIdentifier: type: object @@ -64,25 +76,28 @@ components: $ref: '#/components/schemas/GroupVersionKind' required: - name - - namespace - - groupVersionKind + - namespace + - groupVersionKind GroupVersionKind: type: object + required: + - Group + - Version + - Kind properties: - group: + Group: type: string - description: API group - version: + description: API group (capitalized field name as per Kubernetes API) + example: "" + Version: type: string - description: API version - kind: + description: API version (capitalized field name as per Kubernetes API) + example: "v1" + Kind: type: string - description: resource kind - required: - - group - - version - - kind + description: Resource kind (capitalized field name as per Kubernetes API) + example: "Pod" ManifestResponse: type: object @@ -373,14 +388,14 @@ paths: description: Internal server error put: summary: Update Kubernetes resource - description: Updates an existing Kubernetes resource manifest + description: Updates an existing Kubernetes resource manifest using patch data operationId: UpdateResource requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ResourceRequestObject' + $ref: '#/components/schemas/ResourceRequestBean' responses: "200": description: Updated resource manifest @@ -454,11 +469,11 @@ paths: description: Fetches events for Kubernetes resources operationId: ListEvents requestBody: - required: false + required: true content: application/json: schema: - $ref: '#/components/schemas/ResourceRequestObject' + $ref: '#/components/schemas/ResourceRequestBean' responses: "200": description: Resource events diff --git a/specs/kubernetes/ephemeral-containers.yaml b/specs/kubernetes/ephemeral-containers.yaml index c8b6a5a341..4556165ea6 100644 --- a/specs/kubernetes/ephemeral-containers.yaml +++ b/specs/kubernetes/ephemeral-containers.yaml @@ -106,6 +106,9 @@ components: schemas: EphemeralContainerRequest: type: object + description: | + Request to create an ephemeral container. Either basicData or advancedData is required. + If both are provided, advancedData takes priority over basicData. properties: basicData: $ref: "#/components/schemas/EphemeralContainerBasicData" @@ -121,9 +124,6 @@ components: podName: type: string description: Name of the pod - userId: - type: integer - description: User ID externalArgoApplicationName: type: string description: Name of the external Argo application (if applicable) @@ -131,6 +131,9 @@ components: - namespace - clusterId - podName + anyOf: + - required: ["basicData"] + - required: ["advancedData"] EphemeralContainerBasicData: type: object properties: @@ -149,10 +152,16 @@ components: - image EphemeralContainerAdvancedData: type: object + description: | + Advanced configuration for ephemeral container using Kubernetes EphemeralContainer specification. + For complete field reference, see: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#ephemeralcontainer-v1-core properties: manifest: type: string - description: Kubernetes manifest for the ephemeral container + description: | + Kubernetes manifest for the ephemeral container in JSON format. + Should conform to the EphemeralContainer v1 core specification. + Reference: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.34/#ephemeralcontainer-v1-core PodContainerList: type: object properties: diff --git a/specs/kubernetes/kubernetes-resource-management.yaml b/specs/kubernetes/kubernetes-resource-management.yaml new file mode 100644 index 0000000000..0ceb7573d2 --- /dev/null +++ b/specs/kubernetes/kubernetes-resource-management.yaml @@ -0,0 +1,387 @@ +openapi: "3.0.3" +info: + title: Devtron Pod and Resource Management API + description: API specifications for pod logs, resource rotation, and Kubernetes resource management + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + PodRotateRequest: + type: object + properties: + resources: + type: array + items: + $ref: '#/components/schemas/ResourceIdentifier' + + ResourceIdentifier: + type: object + properties: + Group: + type: string + example: "" + Version: + type: string + example: "v1" + Kind: + type: string + example: "Pod" + Name: + type: string + example: "my-pod" + Namespace: + type: string + example: "default" + + ResourceRotateRequest: + type: object + properties: + resourceId: + type: string + example: "res-123" + +tags: + - name: Pod Logs + description: Pod log retrieval operations + - name: Resource Management + description: Kubernetes resource management operations + - name: Pod Rotation + description: Pod rotation and restart operations + +paths: + /k8s/pods/logs/{podName}: + get: + tags: + - Pod Logs + summary: Get pod logs + description: Retrieves logs from the specified pod and container + operationId: getPodLogs + parameters: + - name: podName + in: path + required: true + schema: + type: string + example: "my-app-pod-123" + - name: containerName + in: query + required: true + schema: + type: string + example: "main-container" + - name: follow + in: query + required: false + schema: + type: boolean + default: false + example: false + responses: + '200': + description: Pod logs retrieved successfully + content: + text/plain: + schema: + type: string + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Pod not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /k8s/pods/logs/download/{podName}: + get: + tags: + - Pod Logs + summary: Download pod logs + description: Downloads logs from the specified pod and container as a file + operationId: downloadPodLogs + parameters: + - name: podName + in: path + required: true + schema: + type: string + example: "my-app-pod-123" + - name: containerName + in: query + required: true + schema: + type: string + example: "main-container" + responses: + '200': + description: Pod logs file download + content: + application/octet-stream: + schema: + type: string + format: binary + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Pod not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /k8s/resource/rotate: + post: + tags: + - Resource Management + summary: Rotate Kubernetes resource + description: Rotates the specified Kubernetes resource + operationId: rotateKubernetesResource + parameters: + - name: appId + in: query + required: true + schema: + type: integer + example: 123 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceRotateRequest' + example: + resourceId: "res-123" + responses: + '200': + description: Resource rotated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/rotate-pods: + post: + tags: + - Pod Rotation + summary: Rotate pods + description: Rotates the specified pods using resource identifiers + operationId: rotatePods + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PodRotateRequest' + example: + resources: + - Group: "" + Version: "v1" + Kind: "Pod" + Name: "my-pod" + Namespace: "default" + responses: + '200': + description: Pods rotated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /cluster/list: + get: + tags: + - Resource Management + summary: Get cluster list + description: Retrieves a list of all available clusters + operationId: getClusterList + responses: + '200': + description: List of clusters retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /cluster/namespaces: + get: + tags: + - Resource Management + summary: Get all cluster namespaces + description: Retrieves namespaces from all clusters + operationId: getAllClusterNamespaces + responses: + '200': + description: All cluster namespaces retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/miscellaneous/orchestrator-miscellaneous-apis.yaml b/specs/miscellaneous/orchestrator-miscellaneous-apis.yaml new file mode 100644 index 0000000000..a149276ec6 --- /dev/null +++ b/specs/miscellaneous/orchestrator-miscellaneous-apis.yaml @@ -0,0 +1,813 @@ +openapi: "3.0.3" +info: + title: Devtron Orchestrator Miscellaneous APIs + description: API specifications for miscellaneous Devtron orchestrator endpoints including security scans, application management, pod operations, and cluster management + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + ScanRequest: + type: object + properties: + appId: + type: integer + example: 123 + envId: + type: integer + example: 456 + scanType: + type: string + example: "VULNERABILITY" + + ExecutionDetailRequest: + type: object + properties: + executionId: + type: integer + example: 789 + + HibernateRequest: + type: object + properties: + appId: + type: integer + example: 123 + envId: + type: integer + example: 456 + + RotatePodsRequest: + type: object + properties: + appId: + type: integer + example: 123 + envId: + type: integer + example: 456 + podName: + type: string + example: "my-pod" + + TriggerRequest: + type: object + properties: + appId: + type: integer + example: 123 + environmentId: + type: integer + example: 456 + pipelineId: + type: integer + example: 789 + + ResourceRotateRequest: + type: object + properties: + resourceId: + type: string + example: "res-123" + + ResourceTreeRequest: + type: object + properties: + appId: + type: integer + example: 123 + envId: + type: integer + example: 456 + + EphemeralContainerRequest: + type: object + properties: + podName: + type: string + example: "my-pod" + container: + type: string + example: "debug-container" + +tags: + - name: Security Scans + description: Security scanning operations + - name: Pod & Resource Management + description: Pod logs, rotation, and resource operations + - name: Application Management + description: Application hibernation, deployment status, and lifecycle + - name: Cluster & Infrastructure + description: Cluster and namespace management + +paths: + /security/scan/list: + post: + tags: + - Security Scans + summary: Get scan execution list + description: Retrieves a list of security scan executions based on the provided criteria + operationId: scanExecutionList + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ScanRequest' + example: + appId: 123 + envId: 456 + scanType: "VULNERABILITY" + responses: + '200': + description: Scan execution list retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /security/scan/executionDetail: + get: + tags: + - Security Scans + summary: Get scan execution detail + description: Retrieves detailed information about a specific scan execution + operationId: fetchExecutionDetail + parameters: + - name: executionId + in: query + required: true + schema: + type: integer + example: 789 + responses: + '200': + description: Scan execution detail retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Scan execution not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /application/hibernate: + post: + tags: + - Application Management + summary: Hibernate application + description: Hibernates the specified application + operationId: hibernateApplication + parameters: + - name: appType + in: query + required: true + schema: + type: string + enum: ["argo", "helm", "flux"] + example: "argo" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/HibernateRequest' + example: + appId: 123 + envId: 456 + responses: + '200': + description: Application hibernated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/rotate-pods: + post: + tags: + - Pod & Resource Management + summary: Rotate pods + description: Rotates the specified pods + operationId: rotatePods + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/RotatePodsRequest' + example: + appId: 123 + envId: 456 + podName: "my-pod" + responses: + '200': + description: Pods rotated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/cd-pipeline/trigger: + post: + tags: + - Application Management + summary: Trigger CD pipeline + description: Triggers a CD pipeline deployment for the specified application and environment + operationId: triggerCdPipeline + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TriggerRequest' + example: + appId: 123 + environmentId: 456 + pipelineId: 789 + responses: + '200': + description: CD pipeline triggered successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /cluster/list: + get: + tags: + - Cluster & Infrastructure + summary: Get cluster list + description: Retrieves a list of all available clusters + operationId: getClusterList + responses: + '200': + description: List of clusters retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /cluster/namespaces: + get: + tags: + - Cluster & Infrastructure + summary: Get all cluster namespaces + description: Retrieves namespaces from all clusters + operationId: getAllClusterNamespaces + responses: + '200': + description: All cluster namespaces retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /k8s/pods/logs/{podName}: + get: + tags: + - Pod & Resource Management + summary: Get pod logs + description: Retrieves logs from the specified pod and container + operationId: getPodLogs + parameters: + - name: podName + in: path + required: true + schema: + type: string + example: "my-app-pod-123" + - name: containerName + in: query + required: true + schema: + type: string + example: "main-container" + - name: follow + in: query + required: false + schema: + type: boolean + default: false + example: false + responses: + '200': + description: Pod logs retrieved successfully + content: + text/plain: + schema: + type: string + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Pod not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /k8s/pods/logs/download/{podName}: + get: + tags: + - Pod & Resource Management + summary: Download pod logs + description: Downloads logs from the specified pod and container as a file + operationId: downloadPodLogs + parameters: + - name: podName + in: path + required: true + schema: + type: string + example: "my-app-pod-123" + - name: containerName + in: query + required: true + schema: + type: string + example: "main-container" + responses: + '200': + description: Pod logs file download + content: + application/octet-stream: + schema: + type: string + format: binary + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Pod not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /k8s/resource/rotate: + post: + tags: + - Pod & Resource Management + summary: Rotate Kubernetes resource + description: Rotates the specified Kubernetes resource + operationId: rotateKubernetesResource + parameters: + - name: appId + in: query + required: true + schema: + type: integer + example: 123 + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceRotateRequest' + example: + resourceId: "res-123" + responses: + '200': + description: Resource rotated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/deployment-status/timeline/{appId}/{envId}: + get: + tags: + - Application Management + summary: Get deployment status timeline + description: Retrieves the deployment status timeline for the specified application and environment + operationId: getDeploymentStatusTimeline + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + - name: envId + in: path + required: true + schema: + type: integer + example: 456 + responses: + '200': + description: Deployment status timeline retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /app/deployment-status/manual-sync/{appId}/{envId}: + get: + tags: + - Application Management + summary: Manual sync deployment status + description: Manually synchronizes the deployment status for the specified application and environment + operationId: manualSyncDeploymentStatus + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + - name: envId + in: path + required: true + schema: + type: integer + example: 456 + responses: + '200': + description: Manual sync completed successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /application/unhibernate: + post: + tags: + - Application Management + summary: Unhibernate application + description: Unhibernates the specified application + operationId: unhibernateApplication + parameters: + - name: appType + in: query + required: true + schema: + type: string + enum: ["argo", "helm", "flux"] + example: "argo" + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/HibernateRequest' + example: + appId: 123 + envId: 456 + responses: + '200': + description: Application unhibernated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/monitoring/monitoring.yaml b/specs/monitoring/monitoring.yaml new file mode 100644 index 0000000000..3918a3c0d4 --- /dev/null +++ b/specs/monitoring/monitoring.yaml @@ -0,0 +1,591 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Orchestrator Monitoring API + description: | + API specifications for Devtron orchestrator monitoring endpoints including + application metrics, logs, and health status monitoring. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Status message + result: + type: object + description: Response result data + + ErrorResponse: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error status + errors: + type: array + items: + type: string + description: List of error messages + + ApplicationMetrics: + type: object + properties: + appId: + type: integer + format: int64 + description: Application ID + appName: + type: string + description: Application name + environmentId: + type: integer + format: int64 + description: Environment ID + environmentName: + type: string + description: Environment name + cpu: + $ref: '#/components/schemas/ResourceMetric' + memory: + $ref: '#/components/schemas/ResourceMetric' + network: + $ref: '#/components/schemas/NetworkMetric' + storage: + $ref: '#/components/schemas/StorageMetric' + replicas: + $ref: '#/components/schemas/ReplicaMetric' + timestamp: + type: string + format: date-time + description: Metrics timestamp + + ResourceMetric: + type: object + properties: + usage: + type: number + format: float + description: Current usage + limit: + type: number + format: float + description: Resource limit + request: + type: number + format: float + description: Resource request + unit: + type: string + description: Unit of measurement + utilizationPercentage: + type: number + format: float + description: Utilization percentage + + NetworkMetric: + type: object + properties: + inbound: + type: number + format: float + description: Inbound network traffic + outbound: + type: number + format: float + description: Outbound network traffic + unit: + type: string + description: Unit of measurement (bytes/sec) + + StorageMetric: + type: object + properties: + used: + type: number + format: float + description: Used storage + available: + type: number + format: float + description: Available storage + total: + type: number + format: float + description: Total storage + unit: + type: string + description: Unit of measurement + + ReplicaMetric: + type: object + properties: + desired: + type: integer + description: Desired replica count + current: + type: integer + description: Current replica count + ready: + type: integer + description: Ready replica count + available: + type: integer + description: Available replica count + + ApplicationLogs: + type: object + properties: + appId: + type: integer + format: int64 + description: Application ID + environmentId: + type: integer + format: int64 + description: Environment ID + podName: + type: string + description: Pod name + containerName: + type: string + description: Container name + logs: + type: array + items: + $ref: '#/components/schemas/LogEntry' + description: Log entries + hasMore: + type: boolean + description: Whether more logs are available + nextToken: + type: string + description: Token for pagination + + LogEntry: + type: object + properties: + timestamp: + type: string + format: date-time + description: Log timestamp + level: + type: string + enum: [DEBUG, INFO, WARN, ERROR, FATAL] + description: Log level + message: + type: string + description: Log message + source: + type: string + description: Log source + metadata: + type: object + description: Additional log metadata + + HealthStatus: + type: object + properties: + status: + type: string + enum: [Healthy, Degraded, Failed, Unknown] + description: Overall health status + components: + type: array + items: + $ref: '#/components/schemas/ComponentHealth' + description: Component health statuses + timestamp: + type: string + format: date-time + description: Health check timestamp + uptime: + type: integer + format: int64 + description: System uptime in seconds + + ComponentHealth: + type: object + properties: + name: + type: string + description: Component name + status: + type: string + enum: [Healthy, Degraded, Failed, Unknown] + description: Component health status + message: + type: string + description: Health status message + lastChecked: + type: string + format: date-time + description: Last health check timestamp + details: + type: object + description: Additional health details + + MetricsRequest: + type: object + properties: + appId: + type: integer + format: int64 + description: Application ID + environmentId: + type: integer + format: int64 + description: Environment ID + startTime: + type: string + format: date-time + description: Start time for metrics query + endTime: + type: string + format: date-time + description: End time for metrics query + step: + type: string + description: Query step interval (e.g., "5m", "1h") + metricTypes: + type: array + items: + type: string + enum: [cpu, memory, network, storage, replicas] + description: Types of metrics to retrieve + + LogsRequest: + type: object + properties: + appId: + type: integer + format: int64 + description: Application ID + environmentId: + type: integer + format: int64 + description: Environment ID + podName: + type: string + description: Pod name (optional) + containerName: + type: string + description: Container name (optional) + startTime: + type: string + format: date-time + description: Start time for log query + endTime: + type: string + format: date-time + description: End time for log query + logLevel: + type: string + enum: [DEBUG, INFO, WARN, ERROR, FATAL] + description: Minimum log level to retrieve + searchQuery: + type: string + description: Search query for log filtering + limit: + type: integer + minimum: 1 + maximum: 1000 + description: Maximum number of log entries to return + nextToken: + type: string + description: Token for pagination + +tags: + - name: Application Metrics + description: Operations for retrieving application metrics + - name: Application Logs + description: Operations for retrieving application logs + - name: Health Status + description: Operations for checking system health status + +paths: + /metrics/application: + post: + tags: + - Application Metrics + summary: Get application metrics + description: Retrieves metrics for applications including CPU, memory, network, and storage usage + operationId: getApplicationMetrics + requestBody: + description: Metrics query request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MetricsRequest' + responses: + '200': + description: Application metrics retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/ApplicationMetrics' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /logs/application: + post: + tags: + - Application Logs + summary: Get application logs + description: Retrieves logs for applications with filtering and search capabilities + operationId: getApplicationLogs + requestBody: + description: Logs query request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/LogsRequest' + responses: + '200': + description: Application logs retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ApplicationLogs' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /health/status: + get: + tags: + - Health Status + summary: Get system health status + description: Retrieves overall system health status and component health details + operationId: getSystemHealthStatus + parameters: + - name: includeComponents + in: query + description: Whether to include detailed component health information + required: false + schema: + type: boolean + default: true + - name: componentFilter + in: query + description: Filter components by name pattern + required: false + schema: + type: string + responses: + '200': + description: System health status retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/HealthStatus' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /deployment-metrics: + get: + tags: + - Application Metrics + summary: Get deployment metrics + description: Retrieves deployment-specific metrics and statistics + operationId: getDeploymentMetrics + parameters: + - name: appId + in: query + description: Application ID + required: false + schema: + type: integer + format: int64 + - name: environmentId + in: query + description: Environment ID + required: false + schema: + type: integer + format: int64 + - name: timeRange + in: query + description: Time range for metrics (e.g., "1h", "24h", "7d") + required: false + schema: + type: string + default: "1h" + responses: + '200': + description: Deployment metrics retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: object + properties: + deploymentCount: + type: integer + description: Total number of deployments + successRate: + type: number + format: float + description: Deployment success rate percentage + averageDeploymentTime: + type: number + format: float + description: Average deployment time in minutes + failureRate: + type: number + format: float + description: Deployment failure rate percentage + recentDeployments: + type: array + items: + type: object + properties: + deploymentId: + type: integer + format: int64 + appName: + type: string + environmentName: + type: string + status: + type: string + enum: [Success, Failed, InProgress] + startTime: + type: string + format: date-time + endTime: + type: string + format: date-time + duration: + type: number + format: float + description: Deployment duration in minutes + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] diff --git a/specs/pipeline/webhook-management.yaml b/specs/pipeline/webhook-management.yaml new file mode 100644 index 0000000000..449c69b7c6 --- /dev/null +++ b/specs/pipeline/webhook-management.yaml @@ -0,0 +1,613 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Orchestrator Webhook Management API + description: | + API specifications for Devtron orchestrator webhook configuration management + including creating, listing, and deleting webhook configurations. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Status message + result: + type: object + description: Response result data + + ErrorResponse: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error status + errors: + type: array + items: + type: string + description: List of error messages + + WebhookConfig: + type: object + properties: + id: + type: integer + format: int64 + description: Webhook configuration ID + name: + type: string + description: Webhook configuration name + description: + type: string + description: Webhook description + webhookUrl: + type: string + format: uri + description: Webhook URL endpoint + httpMethod: + type: string + enum: [POST, PUT, PATCH] + description: HTTP method for webhook calls + headers: + type: array + items: + $ref: '#/components/schemas/WebhookHeader' + description: HTTP headers to include in webhook calls + payload: + type: object + description: Webhook payload template + eventTypes: + type: array + items: + type: string + enum: [DEPLOYMENT_SUCCESS, DEPLOYMENT_FAILURE, CI_SUCCESS, CI_FAILURE, APPLICATION_CREATED, APPLICATION_DELETED] + description: Event types that trigger this webhook + filters: + $ref: '#/components/schemas/WebhookFilters' + active: + type: boolean + description: Whether the webhook is active + retryConfig: + $ref: '#/components/schemas/RetryConfig' + createdOn: + type: string + format: date-time + description: Webhook creation timestamp + createdBy: + type: integer + description: ID of user who created the webhook + lastTriggered: + type: string + format: date-time + description: Last time webhook was triggered + + WebhookHeader: + type: object + required: + - key + - value + properties: + key: + type: string + description: Header key + value: + type: string + description: Header value + isSecret: + type: boolean + description: Whether the header value is secret + default: false + + WebhookFilters: + type: object + properties: + appIds: + type: array + items: + type: integer + format: int64 + description: Filter by application IDs + environmentIds: + type: array + items: + type: integer + format: int64 + description: Filter by environment IDs + teamIds: + type: array + items: + type: integer + format: int64 + description: Filter by team IDs + projectIds: + type: array + items: + type: integer + format: int64 + description: Filter by project IDs + + RetryConfig: + type: object + properties: + maxRetries: + type: integer + minimum: 0 + maximum: 10 + description: Maximum number of retry attempts + default: 3 + retryInterval: + type: integer + minimum: 1 + maximum: 3600 + description: Retry interval in seconds + default: 60 + backoffMultiplier: + type: number + format: float + minimum: 1.0 + maximum: 10.0 + description: Backoff multiplier for retry intervals + default: 2.0 + + CreateWebhookRequest: + type: object + required: + - name + - webhookUrl + - eventTypes + properties: + name: + type: string + description: Webhook configuration name + minLength: 1 + maxLength: 100 + description: + type: string + description: Webhook description + webhookUrl: + type: string + format: uri + description: Webhook URL endpoint + httpMethod: + type: string + enum: [POST, PUT, PATCH] + description: HTTP method for webhook calls + default: POST + headers: + type: array + items: + $ref: '#/components/schemas/WebhookHeader' + description: HTTP headers to include in webhook calls + payload: + type: object + description: Webhook payload template + eventTypes: + type: array + items: + type: string + enum: [DEPLOYMENT_SUCCESS, DEPLOYMENT_FAILURE, CI_SUCCESS, CI_FAILURE, APPLICATION_CREATED, APPLICATION_DELETED] + description: Event types that trigger this webhook + minItems: 1 + filters: + $ref: '#/components/schemas/WebhookFilters' + active: + type: boolean + description: Whether the webhook is active + default: true + retryConfig: + $ref: '#/components/schemas/RetryConfig' + + UpdateWebhookRequest: + type: object + required: + - id + - name + - webhookUrl + - eventTypes + properties: + id: + type: integer + format: int64 + description: Webhook configuration ID + name: + type: string + description: Webhook configuration name + minLength: 1 + maxLength: 100 + description: + type: string + description: Webhook description + webhookUrl: + type: string + format: uri + description: Webhook URL endpoint + httpMethod: + type: string + enum: [POST, PUT, PATCH] + description: HTTP method for webhook calls + headers: + type: array + items: + $ref: '#/components/schemas/WebhookHeader' + description: HTTP headers to include in webhook calls + payload: + type: object + description: Webhook payload template + eventTypes: + type: array + items: + type: string + enum: [DEPLOYMENT_SUCCESS, DEPLOYMENT_FAILURE, CI_SUCCESS, CI_FAILURE, APPLICATION_CREATED, APPLICATION_DELETED] + description: Event types that trigger this webhook + minItems: 1 + filters: + $ref: '#/components/schemas/WebhookFilters' + active: + type: boolean + description: Whether the webhook is active + retryConfig: + $ref: '#/components/schemas/RetryConfig' + + WebhookListResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Status message + result: + type: object + properties: + webhooks: + type: array + items: + $ref: '#/components/schemas/WebhookConfig' + description: List of webhook configurations + totalCount: + type: integer + description: Total number of webhooks + + WebhookExecutionLog: + type: object + properties: + id: + type: integer + format: int64 + description: Execution log ID + webhookId: + type: integer + format: int64 + description: Webhook configuration ID + eventType: + type: string + description: Event type that triggered the webhook + triggeredAt: + type: string + format: date-time + description: When the webhook was triggered + status: + type: string + enum: [SUCCESS, FAILED, RETRYING] + description: Execution status + httpStatusCode: + type: integer + description: HTTP response status code + responseBody: + type: string + description: Response body from webhook endpoint + errorMessage: + type: string + description: Error message if execution failed + retryCount: + type: integer + description: Number of retry attempts + executionTime: + type: number + format: float + description: Execution time in milliseconds + +tags: + - name: Webhook Configuration + description: Operations for managing webhook configurations + - name: Webhook Execution + description: Operations for webhook execution and monitoring + +paths: + /webhook/create: + post: + tags: + - Webhook Configuration + summary: Create webhook configuration + description: Creates a new webhook configuration for event notifications + operationId: createWebhookConfiguration + requestBody: + description: Webhook configuration creation request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWebhookRequest' + responses: + '200': + description: Webhook configuration created successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/WebhookConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Webhook configuration with the same name already exists + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /webhook/list: + get: + tags: + - Webhook Configuration + summary: List webhook configurations + description: Retrieves a list of webhook configurations with optional filtering + operationId: listWebhookConfigurations + parameters: + - name: active + in: query + description: Filter by active status + required: false + schema: + type: boolean + - name: eventType + in: query + description: Filter by event type + required: false + schema: + type: string + enum: [DEPLOYMENT_SUCCESS, DEPLOYMENT_FAILURE, CI_SUCCESS, CI_FAILURE, APPLICATION_CREATED, APPLICATION_DELETED] + - name: appId + in: query + description: Filter by application ID + required: false + schema: + type: integer + format: int64 + - name: teamId + in: query + description: Filter by team ID + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: Webhook configurations retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookListResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /webhook/{webhookId}: + get: + tags: + - Webhook Configuration + summary: Get webhook configuration + description: Retrieves a specific webhook configuration by ID + operationId: getWebhookConfiguration + parameters: + - name: webhookId + in: path + description: Webhook configuration ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + responses: + '200': + description: Webhook configuration retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/WebhookConfig' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Webhook configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + put: + tags: + - Webhook Configuration + summary: Update webhook configuration + description: Updates an existing webhook configuration + operationId: updateWebhookConfiguration + parameters: + - name: webhookId + in: path + description: Webhook configuration ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + requestBody: + description: Webhook configuration update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateWebhookRequest' + responses: + '200': + description: Webhook configuration updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/WebhookConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Webhook configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + delete: + tags: + - Webhook Configuration + summary: Delete webhook configuration + description: Deletes a webhook configuration + operationId: deleteWebhookConfiguration + parameters: + - name: webhookId + in: path + description: Webhook configuration ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + responses: + '200': + description: Webhook configuration deleted successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Webhook configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] diff --git a/specs/security/scan-result.yml b/specs/security/scan-result.yml new file mode 100644 index 0000000000..3fbe62e7d6 --- /dev/null +++ b/specs/security/scan-result.yml @@ -0,0 +1,295 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Scan Result Management API + description: | + API for managing vulnerability scan results and triggering rescans in Devtron. + Provides endpoints for retrieving scan results for applications, environments, + and installed apps, as well as triggering rescans on Software Bill of Materials. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + schemas: + Error: + description: Error object + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + description: Error code + message: + type: string + description: Error message + + ScanResultResponse: + description: Response containing scan results data + type: object + properties: + scanResults: + type: array + description: List of scan results + items: + $ref: '#/components/schemas/ScanResult' + metadata: + $ref: '#/components/schemas/ScanResultMetadata' + + ScanResult: + description: Individual scan result information + type: object + properties: + imageScanHistoryId: + type: integer + description: Image scan history ID + imageScanDeployInfoId: + type: integer + description: Image scan deploy info ID + appId: + type: integer + description: Application ID + envId: + type: integer + description: Environment ID + appName: + type: string + description: Application name + envName: + type: string + description: Environment name + image: + type: string + description: Scanned image name + vulnerabilities: + type: array + description: List of vulnerabilities found + items: + $ref: '#/components/schemas/Vulnerability' + severityCount: + $ref: '#/components/schemas/SeverityCount' + scanExecutionTime: + type: string + format: date-time + description: When the scan was executed + scanEnabled: + type: boolean + description: Whether scanning is enabled for this resource + scanned: + type: boolean + description: Whether the resource has been scanned + scanToolId: + type: integer + description: ID of the scan tool used + scanToolName: + type: string + description: Name of the scan tool + status: + type: string + description: Scan status + enum: + - RUNNING + - COMPLETED + - FAILED + - CANCELLED + + ScanResultMetadata: + description: Metadata about scan results + type: object + properties: + imageScanHistoryIds: + type: array + description: List of image scan history IDs + items: + type: integer + rescannedImageScanHistoryIds: + type: array + description: List of rescanned image scan history IDs + items: + type: integer + isImageScanEnabled: + type: boolean + description: Whether image scanning is enabled + materialInfo: + type: array + description: CI material information + items: + $ref: '#/components/schemas/MaterialInfo' + + MaterialInfo: + description: CI material information + type: object + properties: + id: + type: integer + description: Material ID + gitCommit: + type: string + description: Git commit hash + gitUrl: + type: string + description: Git repository URL + author: + type: string + description: Commit author + message: + type: string + description: Commit message + date: + type: string + format: date-time + description: Commit date + + SeverityCount: + description: Count of vulnerabilities by severity + type: object + properties: + critical: + type: integer + description: Number of critical vulnerabilities + high: + type: integer + description: Number of high severity vulnerabilities + medium: + type: integer + description: Number of medium severity vulnerabilities + low: + type: integer + description: Number of low severity vulnerabilities + unknown: + type: integer + description: Number of unknown severity vulnerabilities + + Vulnerability: + description: Individual vulnerability details + type: object + properties: + cveName: + type: string + description: CVE identifier + severity: + type: string + description: Vulnerability severity + enum: + - CRITICAL + - HIGH + - MEDIUM + - LOW + - UNKNOWN + package: + type: string + description: Affected package name + currentVersion: + type: string + description: Current version of the package + fixedVersion: + type: string + description: Version where the vulnerability is fixed + permission: + type: string + description: Permission level + target: + type: string + description: Target of the vulnerability + class: + type: string + description: Vulnerability class + type: + type: string + description: Vulnerability type + + RescanResponse: + description: Response for rescan operation + type: object + properties: + message: + type: string + description: Success message + rescannedCount: + type: integer + description: Number of items queued for rescan +paths: + /orchestrator/scan-result: + get: + tags: + - Scan Results + summary: Get scan results + description: Retrieve vulnerability scan results for applications, environments, or installed apps + operationId: getScanResults + security: + - bearerAuth: [] + parameters: + - name: appId + in: query + description: Application ID to get scan results for + required: false + schema: + type: integer + - name: envId + in: query + description: Environment ID to get scan results for + required: false + schema: + type: integer + - name: installedAppId + in: query + description: Installed application ID (for Helm apps) + required: false + schema: + type: integer + - name: installedAppVersionHistoryId + in: query + description: Installed app version history ID + required: false + schema: + type: integer + - name: artifactId + in: query + description: Artifact ID to get scan results for + required: false + schema: + type: integer + responses: + '200': + description: Scan results retrieved successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ScanResultResponse' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '401': + description: Unauthorized + '403': + description: Forbidden + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + + diff --git a/specs/security/security-dashboard-apis.yml b/specs/security/security-dashboard-apis.yml index a4552a714f..9517ddc487 100644 --- a/specs/security/security-dashboard-apis.yml +++ b/specs/security/security-dashboard-apis.yml @@ -1,15 +1,129 @@ -openapi: '3.0.2' +openapi: "3.0.3" info: - title: Security Scan API - version: '2.0' - description: API for managing security scans and vulnerability assessments + title: Devtron Security Scan API + description: API specifications for security scanning and vulnerability management in Devtron + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + servers: - - url: https://api.server.test/v1 + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + ImageScanRequest: + type: object + properties: + imageScanDeployInfoId: + type: integer + example: 123 + image: + type: string + example: "nginx:latest" + artifactId: + type: integer + example: 456 + appId: + type: integer + example: 789 + envId: + type: integer + example: 101 + size: + type: integer + example: 20 + offset: + type: integer + example: 0 + + ImageScanHistoryResponse: + type: object + properties: + imageScanDeployInfoId: + type: integer + image: + type: string + tag: + type: string + appId: + type: integer + envId: + type: integer + executedOn: + type: string + format: date-time + executionHistory: + type: array + items: + type: object + + ImageScanHistoryListingResponse: + type: object + properties: + imageScanHistoryResponse: + type: array + items: + $ref: '#/components/schemas/ImageScanHistoryResponse' + + VulnerabilityExposureRequest: + type: object + properties: + cveId: + type: string + example: "CVE-2025-1234" + appId: + type: integer + example: 123 + +tags: + - name: Security Scans + description: Security scanning operations + - name: Vulnerability Management + description: Vulnerability exposure and policy management + paths: - /orchestrator/security/scan/list: + /security/scan/list: post: - summary: Get list of scan executions - description: Fetch scan execution history with filtering options + tags: + - Security Scans + summary: Get scan execution list + description: Retrieves a list of security scan executions based on the provided criteria operationId: scanExecutionList requestBody: required: true @@ -17,456 +131,246 @@ paths: application/json: schema: $ref: '#/components/schemas/ImageScanRequest' + example: + imageScanDeployInfoId: 123 + image: "nginx:latest" + artifactId: 456 + appId: 789 + envId: 101 + size: 20 + offset: 0 responses: '200': - description: List of scan executions + description: Scan execution list retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/ImageScanHistoryListingResponse' + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ImageScanHistoryListingResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/security/scan/executionDetail: + /security/scan/executionDetail: get: - summary: Fetch image scan execution result - description: Get detailed scan results by multiple ways for different use cases. At least one parameter is required. + tags: + - Security Scans + summary: Get scan execution detail + description: Retrieves detailed information about a specific scan execution operationId: fetchExecutionDetail parameters: - name: imageScanDeployInfoId in: query - description: Image scan deploy info ID for fetching scan result required: false schema: type: integer + example: 123 + - name: image + in: query + required: false + schema: + type: string + example: "nginx:latest" - name: artifactId in: query - description: CI artifact ID to fetch scan result for image required: false schema: type: integer + example: 456 - name: appId in: query - description: Application ID for fetching scan result required: false schema: type: integer + example: 789 - name: envId in: query - description: Environment ID for fetching scan result required: false schema: type: integer - - name: image + example: 101 + - name: executionId in: query - description: Image name to fetch scan result for required: false schema: - type: string + type: integer + example: 999 responses: '200': - description: Scan execution details + description: Scan execution detail retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/ImageScanExecutionDetail' + $ref: '#/components/schemas/ApiResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Scan execution not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/security/scan/executionDetail/min: + /security/scan/executionDetail/min: get: - summary: Fetch minimal scan result by app and environment ID - description: Get minimal scan result information for a specific app and environment - operationId: fetchMinScanResult + tags: + - Security Scans + summary: Get minimal scan execution detail + description: Retrieves minimal scan result information by application and environment + operationId: fetchMinScanResultByAppIdAndEnvId parameters: - name: appId in: query - description: Application ID required: true schema: type: integer + example: 789 - name: envId in: query - description: Environment ID required: true schema: type: integer + example: 101 responses: '200': - description: Minimal scan execution details + description: Minimal scan result retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/ImageScanExecutionDetail' + $ref: '#/components/schemas/ApiResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Scan result not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/security/scan/cve/exposure: + /security/scan/cve/exposure: post: - summary: Get vulnerability exposure information - description: Fetch vulnerability exposure data across applications and environments + tags: + - Vulnerability Management + summary: Get CVE vulnerability exposure + description: Retrieves vulnerability exposure information for a specific CVE operationId: vulnerabilityExposure requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/VulnerabilityRequest' + $ref: '#/components/schemas/VulnerabilityExposureRequest' + example: + cveId: "CVE-2025-1234" + appId: 123 responses: '200': - description: Vulnerability exposure data + description: CVE exposure information retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/VulnerabilityExposureListingResponse' + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ImageScanHistoryListingResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: CVE not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' -components: - schemas: - Error: - description: Error object - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - description: Error code - message: - type: string - description: Error message - - ImageScanRequest: - description: Request object for image scan operations - type: object - properties: - scanExecutionId: - type: integer - description: Scan execution ID - imageScanDeployInfoId: - type: integer - description: Image scan deploy info ID - appId: - type: integer - description: Application ID - envId: - type: integer - description: Environment ID - objectId: - type: integer - description: Object ID - artifactId: - type: integer - description: Artifact ID - image: - type: string - description: Image name - offset: - type: integer - description: Pagination offset - size: - type: integer - description: Page size - - ImageScanHistoryListingResponse: - description: Response containing list of scan history - type: object - properties: - offset: - type: integer - description: Pagination offset - size: - type: integer - description: Page size - total: - type: integer - description: Total number of records - scanList: - type: array - items: - $ref: '#/components/schemas/ImageScanHistoryResponse' - - ImageScanHistoryResponse: - description: Individual scan history item - type: object - properties: - imageScanDeployInfoId: - type: integer - description: Image scan deploy info ID - appId: - type: integer - description: Application ID - envId: - type: integer - description: Environment ID - name: - type: string - description: Name of the scanned resource - type: - type: string - description: Type of the scanned resource - environment: - type: string - description: Environment name - lastChecked: - type: string - format: date-time - description: Last scan timestamp - image: - type: string - description: Image name - severityCount: - $ref: '#/components/schemas/SeverityCount' - - ImageScanExecutionDetail: - description: Detailed scan execution result - type: object - properties: - imageScanDeployInfoId: - type: integer - description: Image scan deploy info ID - appId: - type: integer - description: Application ID - envId: - type: integer - description: Environment ID - appName: - type: string - description: Application name - envName: - type: string - description: Environment name - pod: - type: string - description: Pod name - replicaSet: - type: string - description: ReplicaSet name - image: - type: string - description: Image name - vulnerabilities: - type: array - items: - $ref: '#/components/schemas/Vulnerability' - severityCount: - $ref: '#/components/schemas/SeverityCount' - executionTime: - type: string - format: date-time - description: Scan execution time - scanEnabled: - type: boolean - description: Whether scanning is enabled - scanned: - type: boolean - description: Whether the image has been scanned - objectType: - type: string - description: Type of the scanned object - scanToolId: - type: integer - description: ID of the scan tool used - scanToolName: - type: string - description: Name of the scan tool - scanToolUrl: - type: string - description: URL of the scan tool - status: - type: string - description: Scan execution status - enum: - - RUNNING - - COMPLETED - - FAILED - - CANCELLED - - SeverityCount: - description: Count of vulnerabilities by severity - type: object - properties: - critical: - type: integer - description: Number of critical vulnerabilities - high: - type: integer - description: Number of high severity vulnerabilities - medium: - type: integer - description: Number of medium severity vulnerabilities - low: - type: integer - description: Number of low severity vulnerabilities - unknown: - type: integer - description: Number of unknown severity vulnerabilities - - Vulnerability: - description: Individual vulnerability details - type: object - properties: - cveName: - type: string - description: CVE identifier - severity: - type: string - description: Vulnerability severity - enum: - - CRITICAL - - HIGH - - MEDIUM - - LOW - - UNKNOWN - package: - type: string - description: Affected package name - currentVersion: - type: string - description: Current version of the package - fixedVersion: - type: string - description: Version where the vulnerability is fixed - permission: - type: string - description: Permission level - target: - type: string - description: Target of the vulnerability - class: - type: string - description: Vulnerability class - type: - type: string - description: Vulnerability type - - VulnerabilityRequest: - description: Request for vulnerability exposure data - type: object - properties: - appName: - type: string - description: Application name filter - cveName: - type: string - description: CVE name filter - envIds: - type: array - items: - type: integer - description: Environment IDs to filter by - clusterIds: - type: array - items: - type: integer - description: Cluster IDs to filter by - offset: - type: integer - description: Pagination offset - size: - type: integer - description: Page size - - VulnerabilityExposureListingResponse: - description: Response containing vulnerability exposure data - type: object - properties: - offset: - type: integer - description: Pagination offset - size: - type: integer - description: Page size - total: - type: integer - description: Total number of records - list: - type: array - items: - $ref: '#/components/schemas/VulnerabilityExposure' - - VulnerabilityExposure: - description: Vulnerability exposure information - type: object - properties: - appName: - type: string - description: Application name - envName: - type: string - description: Environment name - appId: - type: integer - description: Application ID - envId: - type: integer - description: Environment ID - appType: - type: string - description: Application type - enum: - - DEVTRON_APP - - HELM_APP - - EXTERNAL_HELM_APP - blocked: - type: boolean - description: Whether the vulnerability is blocked \ No newline at end of file + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/security/security-policy.yml b/specs/security/security-policy.yml index 89290e8b19..8609d4f1ce 100644 --- a/specs/security/security-policy.yml +++ b/specs/security/security-policy.yml @@ -1,16 +1,253 @@ -openapi: '3.0.2' +openapi: 3.0.3 info: - title: Security Policy Management - version: '2.0' - description: API for managing security policies and vulnerability controls + version: 1.0.0 + title: Devtron Security Policy Management API + description: | + API for managing security policies and vulnerability controls in Devtron. + Provides endpoints for creating, updating, and managing security policies + at global, cluster, environment, and application levels. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + servers: - - url: https://api.server.test/v1 + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + schemas: + Error: + description: Error object + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + description: Error code + message: + type: string + description: Error message + + CreateVulnerabilityPolicyRequest: + description: Request object for creating vulnerability policy. For global policy don't set clusterId, envId and appId. For cluster set clusterId, for environment set envId, for app set appId and envId. Only one of severity or cveId should be set. + type: object + properties: + action: + $ref: '#/components/schemas/VulnerabilityAction' + appId: + type: integer + description: Application ID (required for app-level policies) + clusterId: + type: integer + description: Cluster ID (required for cluster-level policies) + cveId: + type: string + description: CVE ID for specific CVE policies + envId: + type: integer + description: Environment ID (required for environment and app-level policies) + severity: + type: string + description: Severity level for severity-based policies + enum: + - critical + - high + - medium + - low + + UpdatePolicyParams: + description: Parameters for updating a policy + type: object + required: + - id + - action + properties: + id: + type: integer + description: Policy ID to update + action: + type: string + description: New action for the policy + enum: + - block + - allow + + IdVulnerabilityPolicyResult: + description: Result containing policy ID + type: object + required: + - id + properties: + id: + type: integer + description: Policy ID + + ResourceLevel: + description: Resource Level can be one of Global, Cluster, Environment, Application + type: string + enum: + - Global + - Cluster + - Environment + - Application + + VulnerabilityAction: + description: Actions which can be taken on vulnerabilities + type: string + enum: + - block + - allow + + VulnerabilityPermission: + description: Whether vulnerability is allowed or blocked and is it inherited or is it overridden + type: object + required: + - action + properties: + action: + $ref: '#/components/schemas/VulnerabilityAction' + inherited: + type: boolean + description: Whether the policy is inherited from a higher level + isOverriden: + type: boolean + description: Whether the policy overrides a higher level policy + + SeverityPolicy: + description: Severity related policy information + type: object + required: + - severity + - policyOrigin + - policy + - id + properties: + id: + type: integer + description: Policy ID + severity: + type: string + description: Vulnerability severity level + enum: + - critical + - high + - medium + - low + policyOrigin: + type: string + description: Origin of the policy + policy: + $ref: '#/components/schemas/VulnerabilityPermission' + CvePolicy: + description: CVE related policy information + allOf: + - $ref: '#/components/schemas/SeverityPolicy' + - type: object + properties: + name: + description: In case of CVE policy this is same as CVE name else it is blank + type: string + + VulnerabilityPolicy: + description: Vulnerability policy for a specific scope (global, cluster, environment, or application) + type: object + required: + - severities + - cves + properties: + name: + type: string + description: Name of cluster, environment, or application/environment + envId: + type: integer + description: Environment ID in case of application-level policy + appId: + type: integer + description: Application ID (internal use) + clusterId: + type: integer + description: Cluster ID (internal use) + severities: + type: array + description: Severity-based policies + items: + $ref: '#/components/schemas/SeverityPolicy' + cves: + type: array + description: CVE-specific policies + items: + $ref: '#/components/schemas/CvePolicy' + + GetVulnerabilityPolicyResult: + description: Result containing vulnerability policies + type: object + required: + - level + - policies + properties: + level: + $ref: '#/components/schemas/ResourceLevel' + policies: + type: array + items: + $ref: '#/components/schemas/VulnerabilityPolicy' + + VerifyImageRequest: + description: Request for image verification against security policies + type: object + properties: + image: + type: string + description: Image name to verify + appId: + type: integer + description: Application ID + envId: + type: integer + description: Environment ID + clusterId: + type: integer + description: Cluster ID + + VerifyImageResponse: + description: Response containing image verification results + type: object + properties: + allowed: + type: boolean + description: Whether the image is allowed + blockedCves: + type: array + description: List of blocked CVEs found in the image + items: + type: string + message: + type: string + description: Verification message paths: /orchestrator/security/policy/save: post: + tags: + - Security Policy summary: Create a new security policy description: Create a new vulnerability policy for global, cluster, environment, or application level operationId: savePolicy + security: + - bearerAuth: [] requestBody: required: true content: @@ -43,9 +280,13 @@ paths: /orchestrator/security/policy/update: post: + tags: + - Security Policy summary: Update an existing security policy description: Update an existing vulnerability policy action operationId: updatePolicy + security: + - bearerAuth: [] requestBody: required: true content: @@ -78,9 +319,13 @@ paths: /orchestrator/security/policy/list: get: + tags: + - Security Policy summary: Get security policies description: Fetch current security policy for global, cluster, environment and application level operationId: getPolicy + security: + - bearerAuth: [] parameters: - name: level in: query @@ -120,9 +365,13 @@ paths: /orchestrator/security/policy/verify/webhook: post: + tags: + - Security Policy summary: Verify image against security policies description: Webhook endpoint to verify if an image passes security policy checks operationId: verifyImage + security: + - bearerAuth: [] requestBody: required: true content: @@ -152,216 +401,3 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' -components: - schemas: - Error: - description: Error object - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - description: Error code - message: - type: string - description: Error message - - CreateVulnerabilityPolicyRequest: - description: Request object for creating vulnerability policy. For global policy don't set clusterId, envId and appId. For cluster set clusterId, for environment set envId, for app set appId and envId. Only one of severity or cveId should be set. - type: object - properties: - action: - $ref: '#/components/schemas/VulnerabilityAction' - appId: - type: integer - description: Application ID (required for app-level policies) - clusterId: - type: integer - description: Cluster ID (required for cluster-level policies) - cveId: - type: string - description: CVE ID for specific CVE policies - envId: - type: integer - description: Environment ID (required for environment and app-level policies) - severity: - type: string - description: Severity level for severity-based policies - enum: - - critical - - high - - medium - - low - - UpdatePolicyParams: - description: Parameters for updating a policy - type: object - required: - - id - - action - properties: - id: - type: integer - description: Policy ID to update - action: - type: string - description: New action for the policy - enum: - - block - - allow - - IdVulnerabilityPolicyResult: - description: Result containing policy ID - type: object - required: - - id - properties: - id: - type: integer - description: Policy ID - - ResourceLevel: - description: Resource Level can be one of Global, Cluster, Environment, Application - type: string - enum: - - Global - - Cluster - - Environment - - Application - - VulnerabilityAction: - description: Actions which can be taken on vulnerabilities - type: string - enum: - - block - - allow - - VulnerabilityPermission: - description: Whether vulnerability is allowed or blocked and is it inherited or is it overridden - type: object - required: - - action - properties: - action: - $ref: '#/components/schemas/VulnerabilityAction' - inherited: - type: boolean - description: Whether the policy is inherited from a higher level - isOverriden: - type: boolean - description: Whether the policy overrides a higher level policy - - SeverityPolicy: - description: Severity related policy information - type: object - required: - - severity - - policyOrigin - - policy - - id - properties: - id: - type: integer - description: Policy ID - severity: - type: string - description: Vulnerability severity level - enum: - - critical - - high - - medium - - low - policyOrigin: - type: string - description: Origin of the policy - policy: - $ref: '#/components/schemas/VulnerabilityPermission' - CvePolicy: - description: CVE related policy information - allOf: - - $ref: '#/components/schemas/SeverityPolicy' - - type: object - properties: - name: - description: In case of CVE policy this is same as CVE name else it is blank - type: string - - VulnerabilityPolicy: - description: Vulnerability policy for a specific scope (global, cluster, environment, or application) - type: object - required: - - severities - - cves - properties: - name: - type: string - description: Name of cluster, environment, or application/environment - envId: - type: integer - description: Environment ID in case of application-level policy - appId: - type: integer - description: Application ID (internal use) - clusterId: - type: integer - description: Cluster ID (internal use) - severities: - type: array - description: Severity-based policies - items: - $ref: '#/components/schemas/SeverityPolicy' - cves: - type: array - description: CVE-specific policies - items: - $ref: '#/components/schemas/CvePolicy' - - GetVulnerabilityPolicyResult: - description: Result containing vulnerability policies - type: object - required: - - level - - policies - properties: - level: - $ref: '#/components/schemas/ResourceLevel' - policies: - type: array - items: - $ref: '#/components/schemas/VulnerabilityPolicy' - - VerifyImageRequest: - description: Request for image verification against security policies - type: object - properties: - image: - type: string - description: Image name to verify - appId: - type: integer - description: Application ID - envId: - type: integer - description: Environment ID - clusterId: - type: integer - description: Cluster ID - - VerifyImageResponse: - description: Response containing image verification results - type: object - properties: - allowed: - type: boolean - description: Whether the image is allowed - blockedCves: - type: array - description: List of blocked CVEs found in the image - items: - type: string - message: - type: string - description: Verification message diff --git a/specs/template/config-maps.yaml b/specs/template/config-maps.yaml index f8a03227b6..5e44e49c11 100644 --- a/specs/template/config-maps.yaml +++ b/specs/template/config-maps.yaml @@ -1,13 +1,136 @@ -openapi: "3.0.0" +openapi: "3.0.3" info: - title: Global ConfigMap and Secret Management - description: API for managing global ConfigMaps and Secrets - version: "1.0" + title: Devtron ConfigMap and Secret Management API + description: API specifications for ConfigMap and Secret management in Devtron orchestrator + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + ConfigDataRequest: + type: object + required: + - appId + - configData + properties: + id: + type: integer + example: 0 + appId: + type: integer + example: 123 + environmentId: + type: integer + example: 456 + configData: + type: array + items: + $ref: '#/components/schemas/ConfigData' + isDeletable: + type: boolean + default: true + + ConfigData: + type: object + required: + - name + - type + properties: + name: + type: string + example: "global-configmap" + type: + type: string + enum: ["CONFIGMAP", "SECRET"] + example: "CONFIGMAP" + external: + type: boolean + default: false + mountPath: + type: string + example: "/etc/config" + data: + type: object + additionalProperties: + type: string + example: + key1: "value1" + key2: "value2" + global: + type: boolean + default: true + subPath: + type: boolean + default: false + filePermission: + type: string + example: "0644" + + ConfigsList: + type: object + properties: + maps: + type: array + items: + $ref: '#/components/schemas/ConfigData' + +tags: + - name: Global ConfigMaps + description: Global ConfigMap management + - name: Global Secrets + description: Global Secret management + - name: Environment ConfigMaps + description: Environment-specific ConfigMap operations + - name: Environment Secrets + description: Environment-specific Secret operations paths: - /orchestrator/config/global/cm: + /config/global/cm: post: - description: Create or update a global ConfigMap + tags: + - Global ConfigMaps + summary: Create or update global ConfigMap + description: Creates a new global ConfigMap or updates an existing one operationId: CMGlobalAddUpdate requestBody: required: true @@ -15,83 +138,119 @@ paths: application/json: schema: $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + configData: + - name: "global-configmap" + type: "CONFIGMAP" + external: false + mountPath: "/etc/config" + data: + key1: "value1" + key2: "value2" + global: true + subPath: false + filePermission: "0644" + isDeletable: true responses: '200': - description: Successfully created/updated ConfigMap + description: ConfigMap created/updated successfully content: application/json: schema: - $ref: '#/components/schemas/ConfigDataRequest' + $ref: '#/components/schemas/ApiResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': - description: Unauthorized user + description: Unauthorized content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '403': - description: Forbidden, user is not authorized + description: Forbidden content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/config/environment/cm: + /config/global/cs: post: - description: Create or update an environment-specific ConfigMap - operationId: CMEnvironmentAddUpdate + tags: + - Global Secrets + summary: Create or update global Secret + description: Creates a new global Secret or updates an existing one + operationId: CSGlobalAddUpdate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + configData: + - name: "global-secret" + type: "SECRET" + external: false + mountPath: "/etc/secrets" + data: + username: "admin" + password: "s3cr3t" + global: true + subPath: false + filePermission: "0600" + isDeletable: true responses: '200': - description: Successfully created/updated ConfigMap + description: Secret created/updated successfully content: application/json: schema: - $ref: '#/components/schemas/ConfigDataRequest' + $ref: '#/components/schemas/ApiResponse' '400': - description: Bad request + description: Invalid request parameters content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': - description: Unauthorized user + description: Unauthorized content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '403': - description: Forbidden, user is not authorized + description: Forbidden content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/config/global/cm/{appId}: + /config/global/cm/{appId}: get: - description: Get all global ConfigMaps for an application + tags: + - Global ConfigMaps + summary: Get global ConfigMaps for application + description: Retrieves all global ConfigMaps for the specified application operationId: CMGlobalFetch parameters: - name: appId @@ -99,858 +258,103 @@ paths: required: true schema: type: integer + example: 123 responses: '200': - description: Successfully retrieved ConfigMaps - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cm/{appId}/{envId}: - get: - description: Get all environment-specific ConfigMaps for an application - operationId: CMEnvironmentFetch - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: envId - in: path - required: true - schema: - type: integer - responses: - '200': - description: Successfully retrieved ConfigMaps + description: Global ConfigMaps retrieved successfully content: application/json: schema: - $ref: '#/components/schemas/ConfigDataRequest' + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ConfigsList' '400': - description: Bad request + description: Invalid application ID content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': - description: Unauthorized user + description: Unauthorized content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cm/edit/{appId}/{id}: - get: - description: Get a global ConfigMap for editing - operationId: CMGlobalFetchForEdit - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully retrieved ConfigMap - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user + description: Forbidden content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application not found content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' - /orchestrator/config/environment/cm/edit/{appId}/{envId}/{id}: + /config/global/cs/{appId}: get: - description: Get an environment-specific ConfigMap for editing - operationId: CMEnvironmentFetchForEdit - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: envId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully retrieved ConfigMap - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cm/{appId}/{id}: - delete: - description: Delete a global ConfigMap - operationId: CMGlobalDelete - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully deleted ConfigMap - content: - application/json: - schema: - type: boolean - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cm/{appId}/{envId}/{id}: - delete: - description: Delete an environment-specific ConfigMap - operationId: CMEnvironmentDelete + tags: + - Global Secrets + summary: Get global Secrets for application + description: Retrieves all global Secrets for the specified application + operationId: CSGlobalFetch parameters: - name: appId in: path required: true schema: type: integer - - name: envId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string + example: 123 responses: '200': - description: Successfully deleted ConfigMap + description: Global Secrets retrieved successfully content: application/json: schema: - type: boolean + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ConfigsList' '400': - description: Bad request + description: Invalid application ID content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '401': - description: Unauthorized user + description: Unauthorized content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cs: - post: - description: Create or update a global Secret - operationId: CSGlobalAddUpdate - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - responses: - '200': - description: Successfully created/updated Secret - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user + description: Forbidden content: application/json: schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application not found content: application/json: schema: - $ref: '#/components/schemas/Error' + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cs: - post: - description: Create or update an environment-specific Secret - operationId: CSEnvironmentAddUpdate - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - responses: - '200': - description: Successfully created/updated Secret - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cs/{appId}: - get: - description: Get all global Secrets for an application - operationId: CSGlobalFetch - parameters: - - name: appId - in: path - required: true - schema: - type: integer - responses: - '200': - description: Successfully retrieved Secrets - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cs/{appId}/{envId}: - get: - description: Get all environment-specific Secrets for an application - operationId: CSEnvironmentFetch - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: envId - in: path - required: true - schema: - type: integer - responses: - '200': - description: Successfully retrieved Secrets - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cs/edit/{appId}/{id}: - get: - description: Get a global Secret for editing - operationId: CSGlobalFetchForEdit - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully retrieved Secret - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cs/edit/{appId}/{envId}/{id}: - get: - description: Get an environment-specific Secret for editing - operationId: CSEnvironmentFetchForEdit - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: envId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully retrieved Secret - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigDataRequest' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/global/cs/{appId}/{id}: - delete: - description: Delete a global Secret - operationId: CSGlobalDelete - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully deleted Secret - content: - application/json: - schema: - type: boolean - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/environment/cs/{appId}/{envId}/{id}: - delete: - description: Delete an environment-specific Secret - operationId: CSEnvironmentDelete - parameters: - - name: appId - in: path - required: true - schema: - type: integer - - name: envId - in: path - required: true - schema: - type: integer - - name: id - in: path - required: true - schema: - type: integer - - name: name - in: query - required: true - schema: - type: string - responses: - '200': - description: Successfully deleted Secret - content: - application/json: - schema: - type: boolean - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - - /orchestrator/config/bulk/patch: - post: - description: Bulk patch ConfigMaps and Secrets - operationId: ConfigSecretBulkPatch - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/BulkPatchRequest' - responses: - '200': - description: Successfully patched ConfigMaps and Secrets - content: - application/json: - schema: - type: boolean - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '401': - description: Unauthorized user - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '403': - description: Forbidden, user is not authorized - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - -components: - schemas: - ConfigDataRequest: - type: object - required: - - appId - - userId - - configData - properties: - id: - type: integer - description: ID of the ConfigMap/Secret - appId: - type: integer - description: ID of the application - userId: - type: integer - description: ID of the user making the request - environmentId: - type: integer - description: ID of the environment (for environment-specific ConfigMaps/Secrets) - configData: - type: array - items: - $ref: '#/components/schemas/ConfigData' - - ConfigData: - type: object - required: - - name - - type - properties: - id: - type: integer - description: ID of the ConfigMap/Secret - name: - type: string - description: Name of the ConfigMap/Secret - type: - type: string - enum: [CONFIGMAP, SECRET] - description: Type of the configuration (ConfigMap or Secret) - external: - type: boolean - description: Whether this is an external ConfigMap/Secret - data: - type: object - additionalProperties: - type: string - description: Key-value pairs for the ConfigMap/Secret - mountPath: - type: string - description: Path where the ConfigMap/Secret should be mounted - subPath: - type: string - description: Subpath within the mount path - filePermission: - type: string - description: File permissions for the mounted ConfigMap/Secret - externalSecretType: - type: string - description: Type of external secret (for Secrets only) - roleARN: - type: string - description: ARN of the role to use for external secrets (for Secrets only) - externalSecret: - type: array - items: - $ref: '#/components/schemas/ExternalSecret' - description: External secret configuration (for Secrets only) - - ExternalSecret: - type: object - required: - - name - - key - properties: - name: - type: string - description: Name of the external secret - key: - type: string - description: Key in the external secret store - property: - type: string - description: Property to extract from the external secret - isBinary: - type: boolean - description: Whether the secret value is binary - - BulkPatchRequest: - type: object - required: - - userId - - global - properties: - userId: - type: integer - description: ID of the user making the request - global: - type: boolean - description: Whether to patch global or environment-specific ConfigMaps/Secrets - appId: - type: integer - description: ID of the application - environmentId: - type: integer - description: ID of the environment (for environment-specific patches) - configData: - type: array - items: - $ref: '#/components/schemas/ConfigData' - - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - description: Error code - message: - type: string - description: Error message + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/template/configmap-secret-corrected.yaml b/specs/template/configmap-secret-corrected.yaml new file mode 100644 index 0000000000..7ff2ed2017 --- /dev/null +++ b/specs/template/configmap-secret-corrected.yaml @@ -0,0 +1,491 @@ +openapi: "3.0.3" +info: + title: Devtron ConfigMap and Secret Management API + description: API specifications for ConfigMap and Secret management in Devtron orchestrator + version: "1.0.0" + contact: + name: Devtron Support + email: support@devtron.ai + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +security: + - bearerAuth: [] + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + status: + type: string + result: + type: object + + ErrorResponse: + type: object + properties: + code: + type: integer + status: + type: string + errors: + type: array + items: + type: string + + ConfigDataRequest: + type: object + description: Configuration data request structure for ConfigMap and Secret operations + required: + - appId + - configData + properties: + id: + type: integer + example: 0 + appId: + type: integer + example: 123 + environmentId: + type: integer + example: 456 + configData: + type: array + items: + $ref: '#/components/schemas/ConfigData' + isDeletable: + type: boolean + default: true + + ConfigData: + type: object + description: Individual ConfigMap or Secret data structure + required: + - name + - type + properties: + name: + type: string + example: "global-configmap" + type: + type: string + enum: ["CONFIGMAP", "SECRET"] + example: "CONFIGMAP" + external: + type: boolean + default: false + mountPath: + type: string + description: Path where the ConfigMap/Secret should be mounted + example: "/etc/config" + data: + type: object + description: Key-value pairs for ConfigMap or Secret data + additionalProperties: + type: string + example: + key1: "value1" + key2: "value2" + global: + type: boolean + default: true + subPath: + type: boolean + default: false + filePermission: + type: string + description: File permissions for mounted files + example: "0644" + + ConfigsList: + type: object + properties: + maps: + type: array + items: + $ref: '#/components/schemas/ConfigData' + +tags: + - name: Global ConfigMaps + description: Operations for global ConfigMaps + - name: Global Secrets + description: Operations for global Secrets + - name: Environment ConfigMaps + description: Operations for environment-specific ConfigMaps + - name: Environment Secrets + description: Operations for environment-specific Secrets + +paths: + /config/global/cm: + post: + tags: + - Global ConfigMaps + summary: Create or update global ConfigMap + description: Creates a new global ConfigMap or updates an existing one + operationId: CMGlobalAddUpdate + requestBody: + description: ConfigMap configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + configData: + - name: "global-configmap" + type: "CONFIGMAP" + external: false + mountPath: "/etc/config" + data: + key1: "value1" + key2: "value2" + global: true + subPath: false + filePermission: "0644" + isDeletable: true + responses: + '200': + description: ConfigMap created/updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /config/global/cs: + post: + tags: + - Global Secrets + summary: Create or update global Secret + description: Creates a new global Secret or updates an existing one + operationId: CSGlobalAddUpdate + requestBody: + description: Secret configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + configData: + - name: "global-secret" + type: "SECRET" + external: false + mountPath: "/etc/secrets" + data: + username: "admin" + password: "s3cr3t" + global: true + subPath: false + filePermission: "0600" + isDeletable: true + responses: + '200': + description: Secret created/updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /config/global/cm/{appId}: + get: + tags: + - Global ConfigMaps + summary: Get global ConfigMaps for application + description: Retrieves all global ConfigMaps for the specified application + operationId: CMGlobalFetch + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + responses: + '200': + description: Global ConfigMaps retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ConfigsList' + '400': + description: Invalid application ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /config/global/cs/{appId}: + get: + tags: + - Global Secrets + summary: Get global Secrets for application + description: Retrieves all global Secrets for the specified application + operationId: CSGlobalFetch + parameters: + - name: appId + in: path + required: true + schema: + type: integer + example: 123 + responses: + '200': + description: Global Secrets retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/ConfigsList' + '400': + description: Invalid application ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /config/environment/cm: + post: + tags: + - Environment ConfigMaps + summary: Create or update environment-specific ConfigMap + description: Creates a new environment-specific ConfigMap or updates an existing one + operationId: CMEnvironmentAddUpdate + requestBody: + description: Environment ConfigMap configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + environmentId: 456 + configData: + - name: "env-configmap" + type: "CONFIGMAP" + external: false + mountPath: "/etc/env-config" + data: + envKey: "envValue" + environment: "production" + global: false + subPath: false + filePermission: "0644" + isDeletable: true + responses: + '200': + description: Environment ConfigMap created/updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /config/environment/cs: + post: + tags: + - Environment Secrets + summary: Create or update environment-specific Secret + description: Creates a new environment-specific Secret or updates an existing one + operationId: CSEnvironmentAddUpdate + requestBody: + description: Environment Secret configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigDataRequest' + example: + id: 0 + appId: 123 + environmentId: 456 + configData: + - name: "env-secret" + type: "SECRET" + external: false + mountPath: "/etc/env-secrets" + data: + dbPassword: "env-specific-password" + apiKey: "env-specific-key" + global: false + subPath: false + filePermission: "0600" + isDeletable: true + responses: + '200': + description: Environment Secret created/updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' diff --git a/specs/template/deployment-template-config.yaml b/specs/template/deployment-template-config.yaml new file mode 100644 index 0000000000..bd5138f026 --- /dev/null +++ b/specs/template/deployment-template-config.yaml @@ -0,0 +1,1615 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Orchestrator Deployment Template & Configuration API + description: | + API specifications for Devtron orchestrator deployment template creation and + environment/deployment configuration management endpoints. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Status message + result: + type: object + description: Response result data + + ErrorResponse: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error status + errors: + type: array + items: + type: string + description: List of error messages + + DeploymentTemplate: + type: object + properties: + id: + type: integer + format: int64 + description: Template ID + name: + type: string + description: Template name + description: + type: string + description: Template description + chartRefId: + type: integer + format: int64 + description: Chart reference ID + version: + type: string + description: Template version + isAppMetricsEnabled: + type: boolean + description: Whether application metrics are enabled + defaultAppOverride: + type: object + description: Default application override values + globalConfig: + type: object + description: Global configuration + pipelineStrategy: + type: object + description: Pipeline strategy configuration + createdOn: + type: string + format: date-time + description: Template creation timestamp + createdBy: + type: integer + description: ID of user who created the template + + + + EnvironmentConfig: + type: object + properties: + id: + type: integer + format: int64 + description: Environment configuration ID + environmentId: + type: integer + format: int64 + description: Environment ID + environmentName: + type: string + description: Environment name + namespace: + type: string + description: Kubernetes namespace + clusterId: + type: integer + format: int64 + description: Cluster ID + clusterName: + type: string + description: Cluster name + isProduction: + type: boolean + description: Whether this is a production environment + environmentType: + type: string + enum: [PRODUCTION, NON_PRODUCTION] + description: Environment type + description: + type: string + description: Environment description + active: + type: boolean + description: Whether the environment is active + + DeploymentConfig: + type: object + properties: + id: + type: integer + format: int64 + description: Deployment configuration ID + appId: + type: integer + format: int64 + description: Application ID + environmentId: + type: integer + format: int64 + description: Environment ID + templateVersion: + type: string + description: Template version + pipelineStrategy: + type: object + description: Pipeline strategy configuration + configMapData: + type: object + description: ConfigMap data + secretData: + type: object + description: Secret data + envOverrideValues: + type: object + description: Environment override values + mergedValues: + type: object + description: Merged configuration values + status: + type: string + enum: [DRAFT, PUBLISHED] + description: Configuration status + createdOn: + type: string + format: date-time + description: Configuration creation timestamp + createdBy: + type: integer + description: ID of user who created the configuration + + UpdateDeploymentConfigRequest: + type: object + required: + - appId + - environmentId + properties: + appId: + type: integer + format: int64 + description: Application ID + environmentId: + type: integer + format: int64 + description: Environment ID + templateVersion: + type: string + description: Template version + pipelineStrategy: + type: object + description: Pipeline strategy configuration + configMapData: + type: object + description: ConfigMap data + secretData: + type: object + description: Secret data + envOverrideValues: + type: object + description: Environment override values + + TemplateValidationResponse: + type: object + properties: + isValid: + type: boolean + description: Whether the template is valid + errors: + type: array + items: + type: string + description: Validation error messages + warnings: + type: array + items: + type: string + description: Validation warning messages + chartInfo: + type: object + description: Chart information + properties: + name: + type: string + description: Chart name + version: + type: string + description: Chart version + description: + type: string + description: Chart description + + StageConfig: + type: object + description: Pre/Post deployment stage configuration + properties: + triggerType: + type: string + enum: [AUTOMATIC, MANUAL] + description: Stage trigger type + example: "AUTOMATIC" + name: + type: string + description: Stage name + example: "pre-stage" + config: + type: string + description: Stage configuration as YAML string + example: "echo 'Pre-deployment stage'" + + ConfigMapSecretNames: + type: object + description: ConfigMap and Secret names for stage configuration + properties: + configMaps: + type: array + items: + type: string + description: List of ConfigMap names + example: ["configMap1", "configMap2"] + secrets: + type: array + items: + type: string + description: List of Secret names + example: ["secret1", "secret2"] + + AppTemplateRequest: + type: object + description: Request for creating/updating application deployment template + required: + - deploymentTemplate + properties: + appId: + type: integer + format: int64 + description: Application ID (required for updates) + example: 123 + deploymentTemplate: + type: string + description: Deployment template as YAML string + example: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app + preStage: + $ref: '#/components/schemas/StageConfig' + postStage: + $ref: '#/components/schemas/StageConfig' + preStageConfigMapSecretNames: + $ref: '#/components/schemas/ConfigMapSecretNames' + postStageConfigMapSecretNames: + $ref: '#/components/schemas/ConfigMapSecretNames' + runPreStageInEnv: + type: boolean + description: Whether to run pre-stage in environment + default: true + example: true + runPostStageInEnv: + type: boolean + description: Whether to run post-stage in environment + default: false + example: false + isClusterCdActive: + type: boolean + description: Whether cluster CD is active + default: true + example: true + + AppTemplateUpdateRequest: + allOf: + - $ref: '#/components/schemas/AppTemplateRequest' + - type: object + required: + - templateId + properties: + templateId: + type: integer + format: int64 + description: Template ID to update + example: 1 + + EnvironmentConfigRequest: + type: object + description: Environment configuration request for deployment template override + required: + - chartRefId + - isAppMetricsEnabled + - saveEligibleChanges + - resourceName + - environmentId + - IsOverride + - envOverrideValues + - mergeStrategy + properties: + chartRefId: + type: integer + description: Chart version reference ID + example: 789 + isAppMetricsEnabled: + type: boolean + description: Whether application metrics are enabled + example: true + saveEligibleChanges: + type: boolean + description: Whether to save eligible changes + example: true + readme: + type: string + description: Optional readme content + example: "Deployment configuration for production environment" + schema: + type: object + description: Optional schema definitions + additionalProperties: + type: string + example: + version: "v1.0.0" + type: "deployment" + resourceName: + type: string + description: Name of the resource + example: "my-app-deployment" + isExpressEdit: + type: boolean + description: Whether this is an express edit operation + example: false + environmentId: + type: integer + description: Environment ID + example: 456 + IsOverride: + type: boolean + description: Whether this is an override configuration + example: true + isDraftOverriden: + type: boolean + description: Whether draft is overridden + example: false + globalConfig: + type: object + description: Optional global configuration values + additionalProperties: + type: string + example: + region: "us-west-2" + cluster: "production" + envOverrideValues: + type: object + description: Environment-specific override values + additionalProperties: true + example: + replicaCount: 3 + image: + tag: "v1.2.3" + resources: + limits: + cpu: "500m" + memory: "512Mi" + mergeStrategy: + type: string + description: Override merge strategy type + enum: ["replace", "merge", "strategic-merge"] + example: "merge" + environmentConfig: + type: object + description: Optional environment configuration from DeploymentTemplateEditorDataStateType + additionalProperties: true + example: + namespace: "production" + active: true + + EnvironmentValuesRequest: + type: object + description: Environment values configuration request + required: + - appId + - envId + - chartId + properties: + appId: + type: integer + format: int64 + description: Application ID + example: 123 + envId: + type: integer + format: int64 + description: Environment ID + example: 456 + chartId: + type: integer + format: int64 + description: Chart ID + example: 789 + values: + type: object + description: Helm chart values + example: + replicaCount: 2 + image: "my-app:latest" + + CreateEnvironmentRequest: + type: object + description: Request to create environment configuration based on EnvironmentBean structure + required: + - environment_name + - cluster_id + - namespace + properties: + id: + type: integer + description: Environment ID (optional for creation) + example: 0 + environment_name: + type: string + description: Environment name + maxLength: 50 + example: "staging" + cluster_id: + type: integer + description: Cluster ID + example: 1 + clusterName: + type: string + description: Cluster name (read-only) + example: "production-cluster" + active: + type: boolean + description: Whether environment is active + default: true + example: true + default: + type: boolean + description: Whether this is the default environment + default: false + example: false + prometheus_endpoint: + type: string + description: Prometheus endpoint URL + example: "http://prometheus.monitoring.svc.cluster.local:9090" + namespace: + type: string + description: Kubernetes namespace + maxLength: 50 + example: "staging-ns" + isClusterCdActive: + type: boolean + description: Whether cluster CD is active + example: true + environmentIdentifier: + type: string + description: Environment identifier + example: "staging-env-001" + description: + type: string + description: Environment description + maxLength: 40 + example: "Staging environment for testing" + isVirtualEnvironment: + type: boolean + description: Whether this is a virtual environment + default: false + example: false + allowedDeploymentTypes: + type: array + description: Allowed deployment types for this environment + items: + type: string + enum: ["helm", "argo_cd", "flux_cd"] + example: ["argo_cd", "helm"] + +tags: + - name: Deployment Templates + description: | + Operations for managing deployment templates including: + - Application deployment template configuration + - Template creation and updates + - Pre/post deployment stage management + - ConfigMap and Secret integration + - name: Environment Configuration + description: | + Operations for managing environment configurations including: + - Environment-specific overrides + - Chart values configuration + - Environment creation and updates + - Namespace and cluster management + - name: Deployment Configuration + description: Operations for managing deployment configurations + +paths: + /app/template: + post: + tags: + - Deployment Templates + summary: Configure deployment template for application + description: | + Creates or configures a deployment template for an application including: + - Deployment template YAML configuration + - Pre-stage and post-stage configurations + - ConfigMap and Secret references for stages + - Environment execution settings + - Cluster CD activation settings + operationId: configureDeploymentTemplateForApp + requestBody: + description: Application template configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AppTemplateRequest' + examples: + basic_template: + summary: Basic deployment template + value: + deploymentTemplate: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app + spec: + replicas: 2 + selector: + matchLabels: + app: sample-app + template: + metadata: + labels: + app: sample-app + spec: + containers: + - name: app + image: nginx:latest + preStage: + triggerType: "AUTOMATIC" + name: "pre-stage" + config: "echo 'Pre-deployment validation'" + postStage: + triggerType: "MANUAL" + name: "post-stage" + config: "echo 'Post-deployment cleanup'" + preStageConfigMapSecretNames: + configMaps: ["pre-config"] + secrets: ["pre-secret"] + postStageConfigMapSecretNames: + configMaps: ["post-config"] + secrets: ["post-secret"] + runPreStageInEnv: true + runPostStageInEnv: false + isClusterCdActive: true + responses: + '200': + description: Template configured successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/DeploymentTemplate' + example: + code: 200 + status: "OK" + result: + id: 1 + name: "sample-app-template" + chartRefId: 1 + isAppMetricsEnabled: true + createdOn: "2024-01-15T10:30:00Z" + createdBy: 1 + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + code: 400 + status: "Bad Request" + errors: ["Invalid deployment template YAML format"] + '401': + description: Unauthorized - Invalid or missing authentication token + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - User lacks permission to configure templates + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/template/update: + post: + tags: + - Deployment Templates + summary: Update deployment template + description: | + Updates an existing deployment template configuration including: + - Modified deployment template YAML + - Updated pre/post stage configurations + - Changed ConfigMap and Secret references + - Modified environment execution settings + operationId: updateAppOverride + requestBody: + description: Template update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/AppTemplateUpdateRequest' + examples: + update_template: + summary: Update existing template + value: + templateId: 1 + deploymentTemplate: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app-updated + spec: + replicas: 3 + preStage: + triggerType: "MANUAL" + name: "updated-pre-stage" + config: "echo 'Updated pre-deployment'" + postStage: + triggerType: "AUTOMATIC" + name: "updated-post-stage" + config: "echo 'Updated post-deployment'" + runPreStageInEnv: false + runPostStageInEnv: true + isClusterCdActive: true + responses: + '200': + description: Template updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/DeploymentTemplate' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Template not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + + + /deployment/template/validate: + post: + tags: + - Deployment Templates + summary: Validate deployment template chart file + description: | + Validates a deployment template chart file upload (.tgz format) including: + - Chart structure validation + - Template syntax validation + - Chart metadata verification + - File format validation + + This endpoint is used for validating custom chart uploads before saving them. + operationId: validateDeploymentTemplate + requestBody: + description: Chart template file for validation + required: true + content: + multipart/form-data: + schema: + type: object + required: + - BinaryFile + properties: + BinaryFile: + type: string + format: binary + description: Zipped chart template file (.tgz format) + responses: + '200': + description: Template validation completed successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/TemplateValidationResponse' + example: + code: 200 + status: "OK" + result: + isValid: true + errors: [] + warnings: ["Chart version not specified"] + chartInfo: + name: "sample-chart" + version: "1.0.0" + description: "Sample deployment chart" + '400': + description: Invalid file format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + example: + code: 400 + status: "Bad Request" + errors: ["Unsupported format file is uploaded, please upload file with .tgz extension"] + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden - User lacks global create permission + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /deployment/template/upload: + put: + tags: + - Deployment Templates + summary: Upload deployment template chart + description: | + Uploads and saves a validated deployment template chart including: + - Chart file processing and storage + - Chart metadata extraction + - Chart reference creation + - Template registration in system + operationId: saveDeploymentTemplate + requestBody: + description: Chart upload request + required: true + content: + application/json: + schema: + type: object + required: + - chartName + - chartVersion + - fileId + - action + properties: + chartName: + type: string + description: Chart name + example: "sample-chart" + chartVersion: + type: string + description: Chart version + example: "1.0.0" + description: + type: string + description: Chart description + example: "Sample deployment chart" + fileId: + type: string + description: File ID from validation step + example: "file-123" + action: + type: string + description: Action to perform + example: "SAVE" + message: + type: string + description: Additional message + example: "Chart uploaded successfully" + responses: + '200': + description: Chart uploaded and saved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: string + example: "Processed successfully" + '400': + description: Invalid request format + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /deployment/template/download/{chartRefId}: + get: + tags: + - Deployment Templates + summary: Download deployment template chart + description: Downloads a deployment template chart file by chart reference ID + operationId: downloadDeploymentTemplate + parameters: + - name: chartRefId + in: path + description: Chart reference ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 123 + responses: + '200': + description: Chart file downloaded successfully + content: + application/octet-stream: + schema: + type: string + format: binary + description: Chart file in .tgz format + '400': + description: Invalid chart reference ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Chart not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /deployment/template/fetch: + get: + tags: + - Deployment Templates + summary: Get uploaded deployment template charts + description: | + Retrieves a list of all uploaded deployment template charts including: + - Chart metadata and information + - Upload status and details + - Chart reference information + - Available chart versions + operationId: getUploadedCharts + responses: + '200': + description: Uploaded charts retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + description: Chart reference ID + name: + type: string + description: Chart name + version: + type: string + description: Chart version + description: + type: string + description: Chart description + createdOn: + type: string + format: date-time + description: Upload timestamp + createdBy: + type: integer + description: User ID who uploaded + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /config/environment: + get: + tags: + - Environment Configuration + summary: Get environment configuration + description: Retrieves environment configuration details + operationId: getEnvironmentConfiguration + parameters: + - name: environmentId + in: query + description: Environment ID to filter by + required: false + schema: + type: integer + format: int64 + - name: clusterId + in: query + description: Cluster ID to filter by + required: false + schema: + type: integer + format: int64 + - name: appId + in: query + description: Application ID to filter by + required: false + schema: + type: integer + format: int64 + responses: + '200': + description: Environment configuration retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + type: array + items: + $ref: '#/components/schemas/EnvironmentConfig' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env/{appId}/{envId}: + get: + tags: + - Environment Configuration + summary: Get environment configuration override + description: | + Retrieves environment-specific configuration override for an application including: + - Environment deployment configuration + - ConfigMap and Secret overrides + - Deployment template overrides + - Environment-specific values + operationId: getEnvConfigOverride + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 456 + responses: + '200': + description: Environment configuration retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentConfig' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + post: + tags: + - Environment Configuration + summary: Create environment configuration override + description: | + Creates environment-specific configuration override for an application including: + - Environment deployment settings + - Namespace configuration + - Environment activation status + - Custom environment values + operationId: envConfigOverrideCreate + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 456 + requestBody: + description: Environment configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EnvironmentConfigRequest' + examples: + dev_environment: + summary: Development environment configuration + value: + appId: 123 + envId: 456 + config: + namespace: "dev" + active: true + responses: + '200': + description: Environment configuration created successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Environment configuration already exists + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env/{appId}/{envId}/{chartId}: + get: + tags: + - Environment Configuration + summary: Get environment configuration with chart details + description: | + Retrieves environment-specific configuration with chart details including: + - Chart-specific environment values + - Deployment template with chart reference + - Environment override values + - Chart version information + operationId: getEnvConfigOverrideWithChart + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 456 + - name: chartId + in: path + description: Chart ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 789 + responses: + '200': + description: Environment configuration with chart details retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + allOf: + - $ref: '#/components/schemas/EnvironmentConfig' + - type: object + properties: + chartId: + type: integer + format: int64 + description: Chart ID + chartVersion: + type: string + description: Chart version + values: + type: object + description: Chart values + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application, environment, or chart not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + put: + tags: + - Environment Configuration + summary: Update environment configuration with chart values + description: | + Updates environment-specific configuration with chart values including: + - Modified chart values + - Updated environment settings + - Chart version changes + - Environment override updates + operationId: updateEnvConfigOverrideWithChart + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 456 + - name: chartId + in: path + description: Chart ID + required: true + schema: + type: integer + format: int64 + minimum: 1 + example: 789 + requestBody: + description: Environment values configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EnvironmentValuesRequest' + examples: + production_values: + summary: Production environment values + value: + appId: 123 + envId: 456 + chartId: 789 + values: + replicaCount: 3 + image: "my-app:v1.2.0" + resources: + limits: + cpu: "500m" + memory: "512Mi" + requests: + cpu: "250m" + memory: "256Mi" + responses: + '200': + description: Environment configuration updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application, environment, or chart not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env: + put: + tags: + - Environment Configuration + summary: Update environment deployment template configuration + description: | + Updates environment-specific deployment template configuration including: + - Chart version reference updates + - Application metrics configuration + - Environment override values + - Merge strategy configuration + - Resource name and configuration updates + - Global and environment-specific configuration values + operationId: envConfigOverrideUpdate + requestBody: + description: Environment deployment template configuration update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EnvironmentConfigRequest' + examples: + update_deployment_config: + summary: Update environment deployment configuration + value: + chartRefId: 789 + isAppMetricsEnabled: true + saveEligibleChanges: true + readme: "Production deployment configuration" + schema: + version: "v1.2.3" + type: "deployment" + resourceName: "my-app-production" + isExpressEdit: false + environmentId: 456 + IsOverride: true + isDraftOverriden: false + globalConfig: + region: "us-west-2" + cluster: "production" + envOverrideValues: + replicaCount: 3 + image: + tag: "v1.2.3" + resources: + limits: + cpu: "1000m" + memory: "1Gi" + mergeStrategy: "merge" + environmentConfig: + namespace: "production" + active: true + responses: + '200': + description: Environment configuration updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Environment configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /config/update: + put: + tags: + - Deployment Configuration + summary: Update deployment configuration + description: Updates deployment configuration for an application in a specific environment + operationId: updateDeploymentConfiguration + requestBody: + description: Deployment configuration update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateDeploymentConfigRequest' + responses: + '200': + description: Deployment configuration updated successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/DeploymentConfig' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] diff --git a/specs/template/orchestrator-app-endpoints.yaml b/specs/template/orchestrator-app-endpoints.yaml new file mode 100644 index 0000000000..e3cc56de4a --- /dev/null +++ b/specs/template/orchestrator-app-endpoints.yaml @@ -0,0 +1,923 @@ +openapi: 3.0.3 +info: + version: 1.0.0 + title: Devtron Orchestrator App Template & Environment API + description: | + API specifications for Devtron orchestrator app template and environment configuration endpoints. + These endpoints are based on the actual codebase implementation and match the frontend payloads. + termsOfService: https://devtron.ai/terms/ + contact: + name: Devtron Support + email: support@devtron.ai + url: https://devtron.ai/support + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + +servers: + - url: /orchestrator + description: Devtron Orchestrator API Server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token for authentication + + schemas: + ApiResponse: + type: object + properties: + code: + type: integer + description: HTTP status code + status: + type: string + description: Response status + result: + type: object + description: Response data + + ErrorResponse: + type: object + properties: + code: + type: integer + description: Error code + status: + type: string + description: Error status + errors: + type: array + items: + type: string + description: List of error messages + + # Based on pkg/chart/bean/TemplateRequest + TemplateRequest: + type: object + description: Template request for app deployment configuration + required: + - appId + - valuesOverride + properties: + id: + type: integer + description: Template ID (0 for new, >0 for updates) + example: 0 + appId: + type: integer + description: Application ID + example: 123 + refChartTemplate: + type: string + description: Reference chart template + refChartTemplateVersion: + type: string + description: Reference chart template version + chartRepositoryId: + type: integer + description: Chart repository ID + valuesOverride: + type: object + description: Values override in JSON format (matches frontend payload) + example: + deploymentTemplate: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app + spec: + replicas: 2 + preStage: + triggerType: "AUTOMATIC" + name: "pre-stage" + config: "echo 'Pre-deployment validation'" + postStage: + triggerType: "MANUAL" + name: "post-stage" + config: "echo 'Post-deployment cleanup'" + preStageConfigMapSecretNames: + configMaps: ["configMap1"] + secrets: ["secret1"] + postStageConfigMapSecretNames: + configMaps: ["configMap2"] + secrets: ["secret2"] + runPreStageInEnv: true + runPostStageInEnv: false + isClusterCdActive: true + defaultAppOverride: + type: object + description: Default app override values + chartRefId: + type: integer + description: Chart reference ID + isAppMetricsEnabled: + type: boolean + description: Whether app metrics are enabled + default: false + isBasicViewLocked: + type: boolean + description: Whether basic view is locked + default: false + currentViewEditor: + type: string + description: Current view editor type + enum: ["UNDEFINED", "BASIC", "ADVANCED"] + default: "UNDEFINED" + + # Based on pkg/pipeline/bean/EnvironmentProperties + EnvironmentProperties: + type: object + description: Environment properties for configuration override + required: + - environmentId + - active + - manualReviewed + - status + properties: + id: + type: integer + description: Environment properties ID + envOverrideValues: + type: object + description: Environment override values in JSON format + example: + appId: 123 + envId: 456 + config: + namespace: "dev" + active: true + status: + type: integer + description: Chart status (0=new, 1=success, etc.) + example: 0 + manualReviewed: + type: boolean + description: Whether manually reviewed + example: true + active: + type: boolean + description: Whether environment is active + example: true + namespace: + type: string + description: Kubernetes namespace + example: "dev" + environmentId: + type: integer + description: Environment ID + example: 456 + environmentName: + type: string + description: Environment name + example: "development" + latest: + type: boolean + description: Whether this is the latest version + appMetrics: + type: boolean + description: Whether app metrics are enabled + chartRefId: + type: integer + description: Chart reference ID + isOverride: + type: boolean + description: Whether this is an override + isBasicViewLocked: + type: boolean + description: Whether basic view is locked + currentViewEditor: + type: string + description: Current view editor type + enum: ["UNDEFINED", "BASIC", "ADVANCED"] + description: + type: string + description: Environment description + maxLength: 40 + clusterId: + type: integer + description: Cluster ID + mergeStrategy: + type: string + description: Merge strategy + appId: + type: integer + description: Application ID + +tags: + - name: App Templates + description: | + Operations for managing application deployment templates including: + - Template creation and configuration + - Template updates and overrides + - Pre/post deployment stage management + - name: Environment Configuration + description: | + Operations for managing environment-specific configurations including: + - Environment overrides and values + - Chart-specific configurations + - Environment creation and updates + +paths: + /app/template: + post: + tags: + - App Templates + summary: Configure deployment template for application + description: | + Creates or configures a deployment template for an application. + Uses TemplateRequest structure from the codebase. + Handler: ConfigureDeploymentTemplateForApp + operationId: configureDeploymentTemplateForApp + requestBody: + description: Template configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateRequest' + examples: + basic_template: + summary: Basic deployment template + value: + appId: 123 + valuesOverride: + deploymentTemplate: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app + spec: + replicas: 2 + preStage: + triggerType: "AUTOMATIC" + name: "pre-stage" + config: "echo 'Pre-deployment validation'" + postStage: + triggerType: "MANUAL" + name: "post-stage" + config: "echo 'Post-deployment cleanup'" + preStageConfigMapSecretNames: + configMaps: ["configMap1"] + secrets: ["secret1"] + postStageConfigMapSecretNames: + configMaps: ["configMap2"] + secrets: ["secret2"] + runPreStageInEnv: true + runPostStageInEnv: false + isClusterCdActive: true + responses: + '200': + description: Template configured successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env/{appId}/{envId}: + get: + tags: + - Environment Configuration + summary: Get environment configuration override + description: | + Retrieves environment-specific configuration override for an application. + Handler: GetEnvConfigOverride + Path parameters: appId, environmentId, chartRefId (from URL path) + operationId: getEnvConfigOverride + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID (maps to environmentId in handler) + required: true + schema: + type: integer + minimum: 1 + example: 456 + - name: chartRefId + in: query + description: Chart reference ID (required for GET) + required: true + schema: + type: integer + minimum: 1 + example: 789 + responses: + '200': + description: Environment configuration retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentProperties' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application or environment not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + post: + tags: + - Environment Configuration + summary: Create environment configuration override + description: | + Creates environment-specific configuration override for an application. + Handler: EnvConfigOverrideCreate + Uses EnvironmentProperties structure from the codebase. + operationId: envConfigOverrideCreate + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID (maps to environmentId in handler) + required: true + schema: + type: integer + minimum: 1 + example: 456 + requestBody: + description: Environment configuration request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EnvironmentProperties' + examples: + dev_environment: + summary: Development environment configuration + value: + envOverrideValues: + appId: 123 + envId: 456 + config: + namespace: "dev" + active: true + status: 0 + manualReviewed: true + active: true + namespace: "dev" + chartRefId: 789 + responses: + '200': + description: Environment configuration created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Environment configuration already exists + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env/{appId}/{envId}/{chartId}: + get: + tags: + - Environment Configuration + summary: Get environment configuration with chart details + description: | + Retrieves environment-specific configuration with chart details. + This is the same as /app/env/{appId}/{envId} but with chartId in path. + Handler: GetEnvConfigOverride (same handler, different path structure) + operationId: getEnvConfigOverrideWithChart + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + minimum: 1 + example: 456 + - name: chartId + in: path + description: Chart ID (chartRefId) + required: true + schema: + type: integer + minimum: 1 + example: 789 + responses: + '200': + description: Environment configuration with chart details retrieved successfully + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/ApiResponse' + - type: object + properties: + result: + $ref: '#/components/schemas/EnvironmentProperties' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application, environment, or chart not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + put: + tags: + - Environment Configuration + summary: Update environment configuration with chart values + description: | + Updates environment-specific configuration with chart values. + Based on your frontend payload, this matches the structure. + Handler: EnvConfigOverrideUpdate (PUT method on /env endpoint) + operationId: updateEnvConfigOverrideWithChart + parameters: + - name: appId + in: path + description: Application ID + required: true + schema: + type: integer + minimum: 1 + example: 123 + - name: envId + in: path + description: Environment ID + required: true + schema: + type: integer + minimum: 1 + example: 456 + - name: chartId + in: path + description: Chart ID + required: true + schema: + type: integer + minimum: 1 + example: 789 + requestBody: + description: Environment values configuration request + required: true + content: + application/json: + schema: + type: object + required: + - appId + - envId + - chartId + - values + properties: + appId: + type: integer + description: Application ID + example: 123 + envId: + type: integer + description: Environment ID + example: 456 + chartId: + type: integer + description: Chart ID + example: 789 + values: + type: object + description: Chart values to update + example: + replicaCount: 2 + image: "my-app:latest" + examples: + production_values: + summary: Production environment values + value: + appId: 123 + envId: 456 + chartId: 789 + values: + replicaCount: 3 + image: "my-app:v1.2.0" + resources: + limits: + cpu: "500m" + memory: "512Mi" + requests: + cpu: "250m" + memory: "256Mi" + responses: + '200': + description: Environment configuration updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Application, environment, or chart not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/env: + post: + tags: + - Environment Configuration + summary: Create new environment configuration + description: | + Creates a new environment configuration for an application. + Based on your frontend payload structure. + Note: This endpoint may not exist in current codebase - verify the actual handler. + operationId: createEnvironmentConfig + requestBody: + description: Create environment request + required: true + content: + application/json: + schema: + type: object + required: + - appId + - envName + - namespace + - clusterId + - active + properties: + appId: + type: integer + description: Application ID + example: 123 + envName: + type: string + description: Environment name + example: "staging" + namespace: + type: string + description: Kubernetes namespace + example: "staging-ns" + clusterId: + type: integer + description: Cluster ID + example: 1 + active: + type: boolean + description: Whether environment is active + example: true + examples: + staging_environment: + summary: Staging environment creation + value: + appId: 123 + envName: "staging" + namespace: "staging-ns" + clusterId: 1 + active: true + responses: + '200': + description: Environment created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Environment already exists + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + put: + tags: + - Environment Configuration + summary: Update environment configuration + description: | + Updates existing environment configuration. + Handler: EnvConfigOverrideUpdate + Uses EnvironmentProperties structure from the codebase. + operationId: envConfigOverrideUpdate + requestBody: + description: Environment configuration update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/EnvironmentProperties' + examples: + update_environment: + summary: Update environment configuration + value: + id: 1 + envOverrideValues: + appId: 123 + envId: 456 + config: + namespace: "updated-dev" + active: false + status: 1 + manualReviewed: true + active: false + namespace: "updated-dev" + environmentId: 456 + chartRefId: 789 + responses: + '200': + description: Environment configuration updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Environment configuration not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] + + /app/template/update: + post: + tags: + - App Templates + summary: Update deployment template + description: | + Updates an existing deployment template configuration. + Uses TemplateRequest structure from the codebase. + Handler: UpdateAppOverride + operationId: updateAppOverride + requestBody: + description: Template update request + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/TemplateRequest' + examples: + update_template: + summary: Update existing template + value: + id: 1 + appId: 123 + valuesOverride: + templateId: 1 + deploymentTemplate: | + apiVersion: apps/v1 + kind: Deployment + metadata: + name: sample-app-updated + spec: + replicas: 3 + preStage: + triggerType: "MANUAL" + name: "pre-stage-updated" + config: "echo 'Updated pre-deployment validation'" + postStage: + triggerType: "AUTOMATIC" + name: "post-stage-updated" + config: "echo 'Updated post-deployment cleanup'" + preStageConfigMapSecretNames: + configMaps: ["updated-configMap1"] + secrets: ["updated-secret1"] + postStageConfigMapSecretNames: + configMaps: ["updated-configMap2"] + secrets: ["updated-secret2"] + runPreStageInEnv: false + runPostStageInEnv: true + isClusterCdActive: false + responses: + '200': + description: Template updated successfully + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + '400': + description: Invalid request format or validation error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - bearerAuth: [] diff --git a/tests/api-spec-validation/framework.go b/tests/api-spec-validation/framework.go index 65f0db0528..c5c222cfae 100644 --- a/tests/api-spec-validation/framework.go +++ b/tests/api-spec-validation/framework.go @@ -186,7 +186,7 @@ func (v *APISpecValidator) validateEndpoint(path, method string, operation *open } // Test the endpoint - if err := v.testEndpoint(&result, path, method, operation); err != nil { + if err := v.testEndpoint(&result, path, method, operation, spec); err != nil { result.Issues = append(result.Issues, ValidationIssue{ Type: "REQUEST_ERROR", Message: err.Error(), @@ -219,13 +219,30 @@ func (v *APISpecValidator) validateEndpoint(path, method string, operation *open } // testEndpoint makes an actual HTTP request to test the endpoint -func (v *APISpecValidator) testEndpoint(result *ValidationResult, path, method string, operation *openapi3.Operation) error { +func (v *APISpecValidator) testEndpoint(result *ValidationResult, path, method string, operation *openapi3.Operation, spec *openapi3.T) error { // Process path parameters and build the full URL processedPath, err := v.processPathParameters(path, operation) if err != nil { return fmt.Errorf("failed to process path parameters: %w", err) } - url := v.serverURL + processedPath + + // Build the full URL considering OpenAPI spec server URLs + baseURL := v.serverURL + if spec.Servers != nil && len(spec.Servers) > 0 { + // Use the first server URL from the spec and combine with base server URL + specServerURL := spec.Servers[0].URL + if specServerURL != "" { + // If spec server URL is relative (starts with /), append to base URL + if strings.HasPrefix(specServerURL, "/") { + baseURL = v.serverURL + specServerURL + } else { + // If spec server URL is absolute, use it as is (but this is rare for our case) + baseURL = specServerURL + } + } + } + + url := baseURL + processedPath // Create request with proper body var req *http.Request diff --git a/vendor/github.com/devtron-labs/authenticator/client/oidcClient.go b/vendor/github.com/devtron-labs/authenticator/client/oidcClient.go index 551dec305e..9c37b5026d 100644 --- a/vendor/github.com/devtron-labs/authenticator/client/oidcClient.go +++ b/vendor/github.com/devtron-labs/authenticator/client/oidcClient.go @@ -41,19 +41,42 @@ func GetSettings(conf *DexConfig) (*oidc.Settings, error) { if err != nil { return nil, err } + settings := &oidc.Settings{ URL: conf.Url, OIDCConfig: oidc.OIDCConfig{CLIClientID: conf.DexClientID, ClientSecret: conf.DexClientSecret, Issuer: proxyUrl, ServerSecret: conf.ServerSecret, - RequestedScopes: conf.DexScopes, + RequestedScopes: conf.GetDexScopes(), }, UserSessionDuration: time.Duration(conf.UserSessionDurationSeconds) * time.Second, AdminPasswordMtime: conf.AdminPasswordMtime, } return settings, nil } +func (conf *DexConfig) GetDexScopes() []string { + // passing empty array to get default scopes + defaultScopes := oidc.GetScopesOrDefault([]string{}) + additionalScopes := conf.DexScopes + + occurrenceMap := make(map[string]bool) + finalScopes := make([]string, 0, len(defaultScopes)+len(additionalScopes)) + + // first add all the default + for _, scope := range defaultScopes { + occurrenceMap[scope] = true + finalScopes = append(finalScopes, scope) + } + // append extra configs + for _, scope := range additionalScopes { + if _, exists := occurrenceMap[scope]; !exists { + occurrenceMap[scope] = true + finalScopes = append(finalScopes, scope) + } + } + return finalScopes +} func getOidcClient(dexServerAddress string, settings *oidc.Settings, userVerifier oidc.UserVerifier, RedirectUrlSanitiser oidc.RedirectUrlSanitiser) (*oidc.ClientApp, func(writer http.ResponseWriter, request *http.Request), error) { dexClient := &http.Client{ Transport: &http.Transport{