Skip to content

Commit 28bd893

Browse files
authored
misc: Api changes for env enhancement (#6769)
* clusterID added * error handling in namespace when cluster is not reachable * wire * clusterID as param * review comment
1 parent b177abc commit 28bd893

File tree

9 files changed

+180
-24
lines changed

9 files changed

+180
-24
lines changed

api/cluster/ClusterRestHandler.go

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import (
2020
"context"
2121
"encoding/json"
2222
"errors"
23-
bean2 "github.com/devtron-labs/devtron/pkg/cluster/bean"
24-
"github.com/devtron-labs/devtron/pkg/cluster/environment"
25-
"github.com/devtron-labs/devtron/pkg/cluster/rbac"
2623
"net/http"
2724
"strconv"
2825
"strings"
2926
"time"
3027

28+
bean2 "github.com/devtron-labs/devtron/pkg/cluster/bean"
29+
"github.com/devtron-labs/devtron/pkg/cluster/environment"
30+
"github.com/devtron-labs/devtron/pkg/cluster/rbac"
31+
3132
"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
3233
"github.com/devtron-labs/devtron/pkg/auth/user"
3334
"github.com/devtron-labs/devtron/pkg/genericNotes"
@@ -60,6 +61,7 @@ type ClusterRestHandler interface {
6061
GetClusterNamespaces(w http.ResponseWriter, r *http.Request)
6162
GetAllClusterNamespaces(w http.ResponseWriter, r *http.Request)
6263
FindAllForClusterPermission(w http.ResponseWriter, r *http.Request)
64+
FindByIds(w http.ResponseWriter, r *http.Request)
6365
}
6466

6567
type ClusterRestHandlerImpl struct {
@@ -296,6 +298,59 @@ func (impl ClusterRestHandlerImpl) FindAll(w http.ResponseWriter, r *http.Reques
296298
common.WriteJsonResp(w, err, result, http.StatusOK)
297299
}
298300

301+
func (impl ClusterRestHandlerImpl) FindByIds(w http.ResponseWriter, r *http.Request) {
302+
token := r.Header.Get("token")
303+
304+
// Parse clusterId query parameter
305+
clusterIdsStr := r.URL.Query().Get("clusterId")
306+
if clusterIdsStr == "" {
307+
// If no clusterId parameter, return all clusters (same as FindAll)
308+
impl.FindAll(w, r)
309+
return
310+
}
311+
312+
// Parse comma-separated cluster IDs
313+
var clusterIds []int
314+
clusterIdStrs := strings.Split(clusterIdsStr, ",")
315+
for _, idStr := range clusterIdStrs {
316+
idStr = strings.TrimSpace(idStr)
317+
if idStr == "" {
318+
continue
319+
}
320+
id, err := strconv.Atoi(idStr)
321+
if err != nil {
322+
impl.logger.Errorw("request err, FindByIds", "error", err, "clusterId", idStr)
323+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
324+
return
325+
}
326+
clusterIds = append(clusterIds, id)
327+
}
328+
329+
if len(clusterIds) == 0 {
330+
// If no valid cluster IDs, return empty result
331+
common.WriteJsonResp(w, nil, []*bean2.ClusterBean{}, http.StatusOK)
332+
return
333+
}
334+
335+
clusterList, err := impl.clusterService.FindByIdsWithoutConfig(clusterIds)
336+
if err != nil {
337+
impl.logger.Errorw("service err, FindByIds", "err", err)
338+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
339+
return
340+
}
341+
342+
// RBAC enforcer applying
343+
var result []*bean2.ClusterBean
344+
for _, item := range clusterList {
345+
if ok := impl.enforcer.Enforce(token, casbin.ResourceCluster, casbin.ActionGet, item.ClusterName); ok {
346+
result = append(result, item)
347+
}
348+
}
349+
//RBAC enforcer Ends
350+
351+
common.WriteJsonResp(w, nil, result, http.StatusOK)
352+
}
353+
299354
func (impl ClusterRestHandlerImpl) FindById(w http.ResponseWriter, r *http.Request) {
300355
vars := mux.Vars(r)
301356
id := vars["id"]
@@ -671,7 +726,14 @@ func (impl ClusterRestHandlerImpl) GetClusterNamespaces(w http.ResponseWriter, r
671726

672727
allClusterNamespaces, err := impl.clusterService.FindAllNamespacesByUserIdAndClusterId(userId, clusterId, isActionUserSuperAdmin)
673728
if err != nil {
674-
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
729+
// Check if it's a cluster connectivity error and return appropriate status code
730+
if err.Error() == cluster.ErrClusterNotReachable {
731+
impl.logger.Errorw("cluster connectivity error in GetClusterNamespaces", "error", err, "clusterId", clusterId)
732+
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
733+
} else {
734+
impl.logger.Errorw("error in GetClusterNamespaces", "error", err, "clusterId", clusterId)
735+
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
736+
}
675737
return
676738
}
677739
common.WriteJsonResp(w, nil, allClusterNamespaces, http.StatusOK)

api/cluster/ClusterRouter.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ func (impl ClusterRouterImpl) InitClusterRouter(clusterRouter *mux.Router) {
5757
Queries("id", "{id}").
5858
HandlerFunc(impl.clusterRestHandler.FindNoteByClusterId)
5959

60+
clusterRouter.Path("").
61+
Methods("GET").
62+
Queries("clusterId", "{clusterId}").
63+
HandlerFunc(impl.clusterRestHandler.FindByIds)
64+
6065
clusterRouter.Path("").
6166
Methods("GET").
6267
HandlerFunc(impl.clusterRestHandler.FindAll)

pkg/cluster/ClusterService.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import (
2020
"context"
2121
"encoding/json"
2222
"fmt"
23+
"log"
24+
"net/url"
25+
"sync"
26+
"time"
27+
2328
"github.com/devtron-labs/common-lib/async"
2429
informerBean "github.com/devtron-labs/common-lib/informer"
2530
"github.com/devtron-labs/common-lib/utils/k8s/commonBean"
@@ -32,10 +37,6 @@ import (
3237
"github.com/devtron-labs/devtron/pkg/cluster/read"
3338
cronUtil "github.com/devtron-labs/devtron/util/cron"
3439
"github.com/robfig/cron/v3"
35-
"log"
36-
"net/url"
37-
"sync"
38-
"time"
3940

4041
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
4142
"github.com/devtron-labs/common-lib/utils/k8s"
@@ -75,6 +76,7 @@ type ClusterService interface {
7576
FindById(id int) (*bean.ClusterBean, error)
7677
FindByIdWithoutConfig(id int) (*bean.ClusterBean, error)
7778
FindByIds(id []int) ([]bean.ClusterBean, error)
79+
FindByIdsWithoutConfig(ids []int) ([]*bean.ClusterBean, error)
7880
Update(ctx context.Context, bean *bean.ClusterBean, userId int32) (*bean.ClusterBean, error)
7981
Delete(bean *bean.ClusterBean, userId int32) error
8082

@@ -355,6 +357,21 @@ func (impl *ClusterServiceImpl) FindByIds(ids []int) ([]bean.ClusterBean, error)
355357
return beans, nil
356358
}
357359

360+
func (impl *ClusterServiceImpl) FindByIdsWithoutConfig(ids []int) ([]*bean.ClusterBean, error) {
361+
models, err := impl.clusterRepository.FindByIds(ids)
362+
if err != nil {
363+
return nil, err
364+
}
365+
var beans []*bean.ClusterBean
366+
for _, model := range models {
367+
bean := adapter.GetClusterBean(model)
368+
//empty bearer token as it will be hidden for user
369+
bean.Config = map[string]string{commonBean.BearerToken: ""}
370+
beans = append(beans, &bean)
371+
}
372+
return beans, nil
373+
}
374+
358375
func (impl *ClusterServiceImpl) Update(ctx context.Context, bean *bean.ClusterBean, userId int32) (*bean.ClusterBean, error) {
359376
model, err := impl.clusterRepository.FindById(bean.Id)
360377
if err != nil {
@@ -640,13 +657,25 @@ func (impl *ClusterServiceImpl) GetAllClusterNamespaces() map[string][]string {
640657
return result
641658
}
642659

660+
const (
661+
// Cluster connectivity error constants
662+
ErrClusterNotReachable = "cluster is not reachable"
663+
)
664+
643665
func (impl *ClusterServiceImpl) FindAllNamespacesByUserIdAndClusterId(userId int32, clusterId int, isActionUserSuperAdmin bool) ([]string, error) {
644666
result := make([]string, 0)
645667
clusterBean, err := impl.clusterReadService.FindById(clusterId)
646668
if err != nil {
647669
impl.logger.Errorw("failed to find cluster for id", "error", err, "clusterId", clusterId)
648670
return nil, err
649671
}
672+
673+
// Check if cluster has connection errors
674+
if len(clusterBean.ErrorInConnecting) > 0 {
675+
impl.logger.Errorw("cluster is not reachable", "clusterId", clusterId, "clusterName", clusterBean.ClusterName, "error", clusterBean.ErrorInConnecting)
676+
return nil, fmt.Errorf("%s: %s", ErrClusterNotReachable, clusterBean.ErrorInConnecting)
677+
}
678+
650679
namespaceListGroupByCLuster := impl.K8sInformerFactory.GetLatestNamespaceListGroupByCLuster()
651680
namespaces := namespaceListGroupByCLuster[clusterBean.ClusterName]
652681
if len(namespaces) == 0 {

pkg/cluster/ClusterServiceExtended.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ package cluster
1919
import (
2020
"context"
2121
"fmt"
22+
"net/http"
23+
"strings"
24+
"time"
25+
2226
"github.com/devtron-labs/common-lib/utils/k8s/commonBean"
2327
"github.com/devtron-labs/devtron/client/argocdServer"
2428
"github.com/devtron-labs/devtron/pkg/cluster/bean"
2529
"github.com/devtron-labs/devtron/pkg/cluster/environment/repository"
2630
"github.com/devtron-labs/devtron/pkg/deployment/gitOps/config"
27-
"net/http"
28-
"strings"
29-
"time"
3031

3132
cluster3 "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
3233
"github.com/devtron-labs/devtron/client/grafana"
@@ -75,6 +76,14 @@ func (impl *ClusterServiceImplExtended) FindAllWithoutConfig() ([]*bean.ClusterB
7576
return beans, nil
7677
}
7778

79+
func (impl *ClusterServiceImplExtended) FindByIdsWithoutConfig(ids []int) ([]*bean.ClusterBean, error) {
80+
beans, err := impl.ClusterServiceImpl.FindByIdsWithoutConfig(ids)
81+
if err != nil {
82+
return nil, err
83+
}
84+
return impl.GetClusterFullModeDTO(beans)
85+
}
86+
7887
func (impl *ClusterServiceImplExtended) GetClusterFullModeDTO(beans []*bean.ClusterBean) ([]*bean.ClusterBean, error) {
7988
//devtron full mode logic
8089
var clusterIds []int

pkg/pipeline/workflowStatus/WorkflowStageStatusService.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ package workflowStatus
22

33
import (
44
"encoding/json"
5+
"slices"
6+
"strconv"
7+
"strings"
8+
"time"
9+
510
"github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1"
611
"github.com/devtron-labs/devtron/api/bean"
712
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
813
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig/bean/workflow/cdWorkflow"
914
bean3 "github.com/devtron-labs/devtron/pkg/bean"
15+
envRepository "github.com/devtron-labs/devtron/pkg/cluster/environment/repository"
1016
"github.com/devtron-labs/devtron/pkg/pipeline/constants"
1117
"github.com/devtron-labs/devtron/pkg/pipeline/types"
1218
"github.com/devtron-labs/devtron/pkg/pipeline/workflowStatus/adapter"
@@ -16,9 +22,6 @@ import (
1622
"github.com/devtron-labs/devtron/pkg/sql"
1723
"github.com/go-pg/pg"
1824
"go.uber.org/zap"
19-
"slices"
20-
"strings"
21-
"time"
2225
)
2326

2427
type WorkFlowStageStatusService interface {
@@ -35,6 +38,7 @@ type WorkFlowStageStatusServiceImpl struct {
3538
workflowStatusRepository repository.WorkflowStageRepository
3639
ciWorkflowRepository pipelineConfig.CiWorkflowRepository
3740
cdWorkflowRepository pipelineConfig.CdWorkflowRepository
41+
envRepository envRepository.EnvironmentRepository
3842
transactionManager sql.TransactionWrapper
3943
config *types.CiConfig
4044
}
@@ -43,13 +47,15 @@ func NewWorkflowStageFlowStatusServiceImpl(logger *zap.SugaredLogger,
4347
workflowStatusRepository repository.WorkflowStageRepository,
4448
ciWorkflowRepository pipelineConfig.CiWorkflowRepository,
4549
cdWorkflowRepository pipelineConfig.CdWorkflowRepository,
50+
envRepository envRepository.EnvironmentRepository,
4651
transactionManager sql.TransactionWrapper,
4752
) *WorkFlowStageStatusServiceImpl {
4853
wfStageServiceImpl := &WorkFlowStageStatusServiceImpl{
4954
logger: logger,
5055
workflowStatusRepository: workflowStatusRepository,
5156
ciWorkflowRepository: ciWorkflowRepository,
5257
cdWorkflowRepository: cdWorkflowRepository,
58+
envRepository: envRepository,
5359
transactionManager: transactionManager,
5460
}
5561
ciConfig, err := types.GetCiConfig()
@@ -109,9 +115,32 @@ func (impl *WorkFlowStageStatusServiceImpl) updatePodStages(currentWorkflowStage
109115
//update pod stage status by using convertPodStatusToDevtronStatus
110116
for _, stage := range currentWorkflowStages {
111117
if stage.StatusFor == bean2.WORKFLOW_STAGE_STATUS_TYPE_POD {
112-
// add pod name in stage metadata if not empty
118+
// add pod name and clusterId in stage metadata if not empty
113119
if len(podName) > 0 {
114-
marshalledMetadata, _ := json.Marshal(map[string]string{"podName": podName})
120+
metadata := map[string]string{"podName": podName}
121+
122+
// Try to get clusterId from the workflow
123+
if stage.WorkflowType == bean.CI_WORKFLOW_TYPE.String() {
124+
// For CI workflows, get clusterId from environment
125+
ciWorkflow, err := impl.ciWorkflowRepository.FindById(stage.WorkflowId)
126+
if err == nil && ciWorkflow.EnvironmentId != 0 {
127+
env, err := impl.envRepository.FindById(ciWorkflow.EnvironmentId)
128+
if err == nil && env != nil && env.Cluster != nil {
129+
metadata["clusterId"] = strconv.Itoa(env.Cluster.Id)
130+
}
131+
}
132+
} else if stage.WorkflowType == bean.CD_WORKFLOW_TYPE_PRE.String() || stage.WorkflowType == bean.CD_WORKFLOW_TYPE_POST.String() || stage.WorkflowType == bean.CD_WORKFLOW_TYPE_DEPLOY.String() {
133+
// For CD workflows, get clusterId from environment
134+
cdWorkflowRunner, err := impl.cdWorkflowRepository.FindWorkflowRunnerById(stage.WorkflowId)
135+
if err == nil && cdWorkflowRunner != nil && cdWorkflowRunner.CdWorkflow != nil && cdWorkflowRunner.CdWorkflow.Pipeline != nil {
136+
env, err := impl.envRepository.FindById(cdWorkflowRunner.CdWorkflow.Pipeline.EnvironmentId)
137+
if err == nil && env != nil && env.Cluster != nil {
138+
metadata["clusterId"] = strconv.Itoa(env.Cluster.Id)
139+
}
140+
}
141+
}
142+
143+
marshalledMetadata, _ := json.Marshal(metadata)
115144
stage.Metadata = string(marshalledMetadata)
116145
}
117146
switch podStatus {

specs/cluster_api_spec.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,24 @@ paths:
4949
required: true
5050
schema:
5151
type: integer
52+
- name: clusterId
53+
in: query
54+
description: comma-separated list of cluster IDs to filter clusters. If not provided, returns all clusters.
55+
required: false
56+
schema:
57+
type: string
58+
example: "1,2,3"
5259
responses:
5360
'200':
54-
description: Successfully get cluster
61+
description: Successfully get cluster(s)
5562
content:
5663
application/json:
5764
schema:
58-
$ref: '#/components/schemas/ClusterBean'
65+
oneOf:
66+
- $ref: '#/components/schemas/ClusterBean'
67+
- type: array
68+
items:
69+
$ref: '#/components/schemas/ClusterBean'
5970
'400':
6071
description: Bad Request. Input Validation(decode) error/wrong request body.
6172
content:

specs/swagger/openapi.yaml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,13 +1832,24 @@ paths:
18321832
required: true
18331833
schema:
18341834
type: integer
1835+
- name: clusterId
1836+
in: query
1837+
description: comma-separated list of cluster IDs to filter clusters. If not provided, returns all clusters.
1838+
required: false
1839+
schema:
1840+
type: string
1841+
example: "1,2,3"
18351842
responses:
18361843
'200':
1837-
description: Successfully get cluster
1844+
description: Successfully get cluster(s)
18381845
content:
18391846
application/json:
18401847
schema:
1841-
$ref: '#/components/schemas/ClusterBean'
1848+
oneOf:
1849+
- $ref: '#/components/schemas/ClusterBean'
1850+
- type: array
1851+
items:
1852+
$ref: '#/components/schemas/ClusterBean'
18421853
'400':
18431854
description: Bad Request. Input Validation(decode) error/wrong request body.
18441855
content:

specs/workflow/workflow-stage-status.internal.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ paths:
1313
application/json:
1414
schema:
1515
$ref: '#/components/schemas/GetWorkflowStatusResponse'
16-
example: '{"status":"In progress","startTime":"1","endTime":"","message":"e-message","podStatus":"Running","podName":"pod-name","workflowExecutionStages":{"workflow":[{"stageName":"Preparation","status":"SUCCESS","startTime":"1","endTime":"2","message":"p-message","metadata":{}},{"stageName":"Execution","status":"STARTED","startTime":"2","endTime":"","message":"e-message","metadata":{}}],"pod":[{"stageName":"Execution","status":"STARTED","startTime":"2","endTime":"","message":"e-message","metadata":{"ClusterID":"?? (possible?)","podName":"pod-name"}}]}}'
16+
example: '{"status":"In progress","startTime":"1","endTime":"","message":"e-message","podStatus":"Running","podName":"pod-name","workflowExecutionStages":{"workflow":[{"stageName":"Preparation","status":"SUCCESS","startTime":"1","endTime":"2","message":"p-message","metadata":{}},{"stageName":"Execution","status":"STARTED","startTime":"2","endTime":"","message":"e-message","metadata":{}}],"pod":[{"stageName":"Execution","status":"STARTED","startTime":"2","endTime":"","message":"e-message","metadata":{"clusterId":"123","podName":"pod-name"}}]}}'
1717

1818
components:
1919
schemas:
@@ -89,7 +89,7 @@ components:
8989
metadata:
9090
type: object
9191
properties:
92-
ClusterID:
92+
clusterId:
9393
type: string
9494
description: Cluster ID
9595
podName:

0 commit comments

Comments
 (0)