Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4d2fb76
feat:Cluster description note added
Ash-exp Mar 22, 2023
234e1f7
Merge branch 'main' into feature-cluster-description
Ash-exp Mar 22, 2023
716fb9b
fixed bugs and modifed
Ash-exp Mar 22, 2023
e6e1a0c
updated rback and removed unnecessary context
Ash-exp Mar 22, 2023
8765936
updated validations
Ash-exp Mar 22, 2023
299b4aa
added Integration test cases
Ash-exp Mar 23, 2023
1b9516b
incorporated the feedbacks
Ash-exp Mar 23, 2023
800f24c
Merge branch 'main' into feature-cluster-description
Ash-exp Mar 23, 2023
64e12cb
updated the migration script numbers
Ash-exp Mar 23, 2023
5ce5601
updated param name for id
Ash-exp Mar 23, 2023
9dda6e9
migration scripts updated
Ash-exp Apr 24, 2023
7c56b3b
Merge branch 'main' into feature-cluster-description
Ash-exp Apr 24, 2023
3122cbc
error handler update and rbac added
Ash-exp Apr 24, 2023
d911fd0
updated and sanity
Ash-exp Apr 25, 2023
2bb0d72
fixed bug while saving
Ash-exp Apr 25, 2023
5753ede
updated the integration test case
Ash-exp Apr 25, 2023
1e4bc05
Merge branch 'main' into feature-cluster-description
Ash-exp Apr 25, 2023
4065da9
updated and refactored
Ash-exp Apr 30, 2023
f04e982
updated intigration test
Ash-exp Apr 30, 2023
948ec9b
Merge branch 'main' into feature-cluster-description
Ash-exp Apr 30, 2023
3de0ed7
updated wire_gen
Ash-exp May 1, 2023
cc183f3
updated integration test case
Ash-exp May 1, 2023
2da511a
updated Rbac and audit save bug
Ash-exp May 1, 2023
c0c25ef
Merge branch 'main' into feature-cluster-description
Ash-exp May 2, 2023
2cf4dde
updated migration count
Ash-exp May 2, 2023
db5f280
Merge branch 'main' into feature-cluster-description
Ash-exp May 4, 2023
8ee8fc7
Merge branch 'main' into feature-cluster-description
Ash-exp May 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 175 additions & 20 deletions api/cluster/ClusterRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ import (
"time"

"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/pkg/cluster"
delete2 "github.com/devtron-labs/devtron/pkg/delete"
"github.com/devtron-labs/devtron/pkg/user"
"github.com/devtron-labs/devtron/pkg/user/casbin"
util2 "github.com/devtron-labs/devtron/util"
"github.com/devtron-labs/devtron/util/argo"

"github.com/devtron-labs/devtron/pkg/cluster"
"github.com/devtron-labs/devtron/pkg/user"
"github.com/go-pg/pg"
"github.com/gorilla/mux"
"go.uber.org/zap"
"gopkg.in/go-playground/validator.v9"
Expand All @@ -44,10 +44,10 @@ const CLUSTER_DELETE_SUCCESS_RESP = "Cluster deleted successfully."
type ClusterRestHandler interface {
Save(w http.ResponseWriter, r *http.Request)
FindAll(w http.ResponseWriter, r *http.Request)

FindById(w http.ResponseWriter, r *http.Request)
FindNoteByClusterId(w http.ResponseWriter, r *http.Request)
Update(w http.ResponseWriter, r *http.Request)

UpdateClusterNote(w http.ResponseWriter, r *http.Request)
FindAllForAutoComplete(w http.ResponseWriter, r *http.Request)
DeleteCluster(w http.ResponseWriter, r *http.Request)
GetClusterNamespaces(w http.ResponseWriter, r *http.Request)
Expand All @@ -56,30 +56,39 @@ type ClusterRestHandler interface {
}

type ClusterRestHandlerImpl struct {
clusterService cluster.ClusterService
logger *zap.SugaredLogger
userService user.UserService
validator *validator.Validate
enforcer casbin.Enforcer
deleteService delete2.DeleteService
argoUserService argo.ArgoUserService
clusterService cluster.ClusterService
clusterNoteService cluster.ClusterNoteService
clusterDescriptionService cluster.ClusterDescriptionService
logger *zap.SugaredLogger
userService user.UserService
validator *validator.Validate
enforcer casbin.Enforcer
deleteService delete2.DeleteService
argoUserService argo.ArgoUserService
environmentService cluster.EnvironmentService
}

func NewClusterRestHandlerImpl(clusterService cluster.ClusterService,
clusterNoteService cluster.ClusterNoteService,
clusterDescriptionService cluster.ClusterDescriptionService,
logger *zap.SugaredLogger,
userService user.UserService,
validator *validator.Validate,
enforcer casbin.Enforcer,
deleteService delete2.DeleteService,
argoUserService argo.ArgoUserService) *ClusterRestHandlerImpl {
argoUserService argo.ArgoUserService,
environmentService cluster.EnvironmentService) *ClusterRestHandlerImpl {
return &ClusterRestHandlerImpl{
clusterService: clusterService,
logger: logger,
userService: userService,
validator: validator,
enforcer: enforcer,
deleteService: deleteService,
argoUserService: argoUserService,
clusterService: clusterService,
clusterNoteService: clusterNoteService,
clusterDescriptionService: clusterDescriptionService,
logger: logger,
userService: userService,
validator: validator,
enforcer: enforcer,
deleteService: deleteService,
argoUserService: argoUserService,
environmentService: environmentService,
}
}

Expand Down Expand Up @@ -200,6 +209,42 @@ func (impl ClusterRestHandlerImpl) FindById(w http.ResponseWriter, r *http.Reque
common.WriteJsonResp(w, err, bean, http.StatusOK)
}

func (impl ClusterRestHandlerImpl) FindNoteByClusterId(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
i, err := strconv.Atoi(id)
if err != nil {
impl.logger.Errorw("request err, FindById", "error", err, "clusterId", id)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}
bean, err := impl.clusterDescriptionService.FindByClusterIdWithClusterDetails(i)
if err != nil {
if err == pg.ErrNoRows {
impl.logger.Errorw("cluster not found, FindById", "err", err, "clusterId", id)
common.WriteJsonResp(w, errors.New("invalid cluster id"), nil, http.StatusNotFound)
return
}
impl.logger.Errorw("service err, FindById", "err", err, "clusterId", id)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
// RBAC enforcer applying
token := r.Header.Get("token")
authenticated, err := impl.CheckRbacForClusterDetails(bean.ClusterId, token)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use join instead of two queries

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented 👍

impl.logger.Errorw("error in checking rbac for cluster", "err", err, "clusterId", bean.ClusterId)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
if !authenticated {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
//RBAC enforcer Ends
common.WriteJsonResp(w, err, bean, http.StatusOK)
}

func (impl ClusterRestHandlerImpl) Update(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("token")
decoder := json.NewDecoder(r.Body)
Expand Down Expand Up @@ -261,6 +306,76 @@ func (impl ClusterRestHandlerImpl) Update(w http.ResponseWriter, r *http.Request
common.WriteJsonResp(w, err, bean, http.StatusOK)
}

func (impl ClusterRestHandlerImpl) UpdateClusterNote(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("token")
decoder := json.NewDecoder(r.Body)
userId, err := impl.userService.GetLoggedInUser(r)
if userId == 0 || err != nil {
impl.logger.Errorw("service err, Update", "error", err, "userId", userId)
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}
var bean cluster.ClusterNoteBean
err = decoder.Decode(&bean)
if err != nil {
impl.logger.Errorw("request err, Update", "error", err, "payload", bean)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}
impl.logger.Infow("request payload, Update", "payload", bean)
err = impl.validator.Struct(bean)
if err != nil {
impl.logger.Errorw("validate err, Update", "error", err, "payload", bean)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}
clusterDescription, err := impl.clusterDescriptionService.FindByClusterIdWithClusterDetails(bean.ClusterId)
if err != nil {
impl.logger.Errorw("service err, FindById", "err", err, "clusterId", bean.ClusterId)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
// RBAC enforcer applying
if ok := impl.enforcer.Enforce(token, casbin.ResourceCluster, casbin.ActionUpdate, strings.ToLower(clusterDescription.ClusterName)); !ok {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}
// RBAC enforcer ends

_, err = impl.clusterNoteService.Update(&bean, userId)
if err == pg.ErrNoRows {
_, err = impl.clusterNoteService.Save(&bean, userId)
if err != nil {
impl.logger.Errorw("service err, Save", "error", err, "payload", bean)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
}
if err != nil {
impl.logger.Errorw("service err, Update", "error", err, "payload", bean)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
userInfo, err := impl.userService.GetById(bean.UpdatedBy)
if err != nil {
impl.logger.Errorw("user service err, FindById", "err", err, "userId", bean.UpdatedBy)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
clusterNoteResponseBean := &cluster.ClusterNoteResponseBean{
Id: bean.Id,
Description: bean.Description,
UpdatedOn: bean.UpdatedOn,
UpdatedBy: userInfo.EmailId,
}
if err != nil {
impl.logger.Errorw("cluster note service err, Update", "error", err, "payload", bean)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
common.WriteJsonResp(w, err, clusterNoteResponseBean, http.StatusOK)
}

func (impl ClusterRestHandlerImpl) FindAllForAutoComplete(w http.ResponseWriter, r *http.Request) {
start := time.Now()
clusterList, err := impl.clusterService.FindAllForAutoComplete()
Expand Down Expand Up @@ -422,3 +537,43 @@ func (impl ClusterRestHandlerImpl) FindAllForClusterPermission(w http.ResponseWr
}
common.WriteJsonResp(w, err, clusterList, http.StatusOK)
}

func (impl *ClusterRestHandlerImpl) CheckRbacForClusterDetails(clusterId int, token string) (authenticated bool, err error) {
//getting all environments for this cluster
envs, err := impl.environmentService.GetByClusterId(clusterId)
if err != nil {
impl.logger.Errorw("error in getting environments by clusterId", "err", err, "clusterId", clusterId)
return false, err
}
if len(envs) == 0 {
if ok := impl.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionGet, "*"); !ok {
return false, nil
}
return true, nil
}
emailId, err := impl.userService.GetEmailFromToken(token)
if err != nil {
impl.logger.Errorw("error in getting emailId from token", "err", err)
return false, err
}

var envIdentifierList []string
envIdentifierMap := make(map[string]bool)
for _, env := range envs {
envIdentifier := strings.ToLower(env.EnvironmentIdentifier)
envIdentifierList = append(envIdentifierList, envIdentifier)
envIdentifierMap[envIdentifier] = true
}
if len(envIdentifierList) == 0 {
return false, errors.New("environment identifier list for rbac batch enforcing contains zero environments")
}
// RBAC enforcer applying
rbacResultMap := impl.enforcer.EnforceByEmailInBatch(emailId, casbin.ResourceGlobalEnvironment, casbin.ActionGet, envIdentifierList)
for envIdentifier, _ := range envIdentifierMap {
if rbacResultMap[envIdentifier] {
//if user has view permission to even one environment of this cluster, authorise the request
return true, nil
}
}
return false, nil
}
9 changes: 9 additions & 0 deletions api/cluster/ClusterRouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func (impl ClusterRouterImpl) InitClusterRouter(clusterRouter *mux.Router) {
Queries("id", "{id}").
HandlerFunc(impl.clusterRestHandler.FindById)

clusterRouter.Path("/description").
Methods("GET").
Queries("id", "{id}").
HandlerFunc(impl.clusterRestHandler.FindNoteByClusterId)

clusterRouter.Path("").
Methods("GET").
HandlerFunc(impl.clusterRestHandler.FindAll)
Expand All @@ -53,6 +58,10 @@ func (impl ClusterRouterImpl) InitClusterRouter(clusterRouter *mux.Router) {
Methods("PUT").
HandlerFunc(impl.clusterRestHandler.Update)

clusterRouter.Path("/description/note").
Methods("PUT").
HandlerFunc(impl.clusterRestHandler.UpdateClusterNote)

clusterRouter.Path("/autocomplete").
Methods("GET").
HandlerFunc(impl.clusterRestHandler.FindAllForAutoComplete)
Expand Down
30 changes: 29 additions & 1 deletion api/cluster/wire_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ var ClusterWireSet = wire.NewSet(
wire.Bind(new(repository.ClusterRepository), new(*repository.ClusterRepositoryImpl)),
cluster.NewClusterServiceImplExtended,
wire.Bind(new(cluster.ClusterService), new(*cluster.ClusterServiceImplExtended)),

repository.NewClusterDescriptionRepositoryImpl,
wire.Bind(new(repository.ClusterDescriptionRepository), new(*repository.ClusterDescriptionRepositoryImpl)),
repository.NewClusterNoteHistoryRepositoryImpl,
wire.Bind(new(repository.ClusterNoteHistoryRepository), new(*repository.ClusterNoteHistoryRepositoryImpl)),
repository.NewClusterNoteRepositoryImpl,
wire.Bind(new(repository.ClusterNoteRepository), new(*repository.ClusterNoteRepositoryImpl)),
cluster.NewClusterNoteHistoryServiceImpl,
wire.Bind(new(cluster.ClusterNoteHistoryService), new(*cluster.ClusterNoteHistoryServiceImpl)),
cluster.NewClusterNoteServiceImpl,
wire.Bind(new(cluster.ClusterNoteService), new(*cluster.ClusterNoteServiceImpl)),
cluster.NewClusterDescriptionServiceImpl,
wire.Bind(new(cluster.ClusterDescriptionService), new(*cluster.ClusterDescriptionServiceImpl)),

NewClusterRestHandlerImpl,
wire.Bind(new(ClusterRestHandler), new(*ClusterRestHandlerImpl)),
NewClusterRouterImpl,
Expand All @@ -28,12 +42,26 @@ var ClusterWireSet = wire.NewSet(
wire.Bind(new(EnvironmentRouter), new(*EnvironmentRouterImpl)),
)

//minimal wire to be used with EA
// minimal wire to be used with EA
var ClusterWireSetEa = wire.NewSet(
repository.NewClusterRepositoryImpl,
wire.Bind(new(repository.ClusterRepository), new(*repository.ClusterRepositoryImpl)),
cluster.NewClusterServiceImpl,
wire.Bind(new(cluster.ClusterService), new(*cluster.ClusterServiceImpl)),

repository.NewClusterDescriptionRepositoryImpl,
wire.Bind(new(repository.ClusterDescriptionRepository), new(*repository.ClusterDescriptionRepositoryImpl)),
repository.NewClusterNoteHistoryRepositoryImpl,
wire.Bind(new(repository.ClusterNoteHistoryRepository), new(*repository.ClusterNoteHistoryRepositoryImpl)),
repository.NewClusterNoteRepositoryImpl,
wire.Bind(new(repository.ClusterNoteRepository), new(*repository.ClusterNoteRepositoryImpl)),
cluster.NewClusterNoteHistoryServiceImpl,
wire.Bind(new(cluster.ClusterNoteHistoryService), new(*cluster.ClusterNoteHistoryServiceImpl)),
cluster.NewClusterNoteServiceImpl,
wire.Bind(new(cluster.ClusterNoteService), new(*cluster.ClusterNoteServiceImpl)),
cluster.NewClusterDescriptionServiceImpl,
wire.Bind(new(cluster.ClusterDescriptionService), new(*cluster.ClusterDescriptionServiceImpl)),

NewClusterRestHandlerImpl,
wire.Bind(new(ClusterRestHandler), new(*ClusterRestHandlerImpl)),
NewClusterRouterImpl,
Expand Down
8 changes: 7 additions & 1 deletion cmd/external-app/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading