Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
32 changes: 32 additions & 0 deletions api/restHandler/AttributesRestHandlder.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type AttributesRestHandler interface {
GetAttributesById(w http.ResponseWriter, r *http.Request)
GetAttributesActiveList(w http.ResponseWriter, r *http.Request)
GetAttributesByKey(w http.ResponseWriter, r *http.Request)
AddDeploymentEnforcementConfig(w http.ResponseWriter, r *http.Request)
}

type AttributesRestHandlerImpl struct {
Expand Down Expand Up @@ -191,3 +192,34 @@ func (handler AttributesRestHandlerImpl) GetAttributesByKey(w http.ResponseWrite
}
common.WriteJsonResp(w, nil, res, http.StatusOK)
}

func (handler AttributesRestHandlerImpl) AddDeploymentEnforcementConfig(w http.ResponseWriter, r *http.Request) {
userId, err := handler.userService.GetLoggedInUser(r)
if userId == 0 || err != nil {
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}
decoder := json.NewDecoder(r.Body)
var dto attributes.AttributesDto
err = decoder.Decode(&dto)
if err != nil {
handler.logger.Errorw("request err, AddAttributes", "err", err, "payload", dto)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

token := r.Header.Get("token")
if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*"); !ok {
common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
return
}

handler.logger.Infow("request payload, AddAttributes", "payload", dto)
resp, err := handler.attributesService.AddDeploymentEnforcementConfig(&dto)
if err != nil {
handler.logger.Errorw("service err, AddAttributes", "err", err, "payload", dto)
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
return
}
common.WriteJsonResp(w, nil, resp, http.StatusOK)
}
2 changes: 2 additions & 0 deletions api/router/AttributesRouter.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ func (router AttributesRouterImpl) InitAttributesRouter(attributesRouter *mux.Ro
HandlerFunc(router.attributesRestHandler.GetAttributesById).Methods("GET")
attributesRouter.Path("/active/list").
HandlerFunc(router.attributesRestHandler.GetAttributesActiveList).Methods("GET")
attributesRouter.Path("/create/deploymentConfig").
HandlerFunc(router.attributesRestHandler.AddDeploymentEnforcementConfig).Methods("POST")
}
27 changes: 26 additions & 1 deletion pkg/appClone/AppCloneService.go
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,31 @@ func (impl *AppCloneServiceImpl) CreateCdPipeline(req *cloneCdPipelineRequest, c
if strings.HasPrefix(pipelineName, req.refAppName) {
pipelineName = strings.Replace(pipelineName, req.refAppName+"-", "", 1)
}
// by default all deployment types are allowed
AllowedDeploymentAppTypes := map[string]bool{
util.PIPELINE_DEPLOYMENT_TYPE_ACD: true,
util.PIPELINE_DEPLOYMENT_TYPE_HELM: true,
}
DeploymentAppConfigForEnvironment, err := impl.pipelineBuilder.GetDeploymentConfigMap(refCdPipeline.EnvironmentId)
if err != nil {
impl.logger.Errorw("error in fetching deployment config for environment", "err", err)
}
for deploymentType, allowed := range DeploymentAppConfigForEnvironment {
AllowedDeploymentAppTypes[deploymentType] = allowed
}
isGitopsConfigured, err := impl.pipelineBuilder.IsGitopsConfigured()
if err != nil {
impl.logger.Errorw("error in checking if gitOps configured", "err", err)
return nil, err
}
var deploymentAppType string
if AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_ACD] && AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_HELM] {
deploymentAppType = refCdPipeline.DeploymentAppType
} else if AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_ACD] && isGitopsConfigured {
deploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_ACD
} else if AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_HELM] {
deploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_HELM
}
cdPipeline := &bean.CDPipelineConfigObject{
Id: 0,
EnvironmentId: refCdPipeline.EnvironmentId,
Expand All @@ -859,7 +884,7 @@ func (impl *AppCloneServiceImpl) CreateCdPipeline(req *cloneCdPipelineRequest, c
PostStageConfigMapSecretNames: refCdPipeline.PostStageConfigMapSecretNames,
RunPostStageInEnv: refCdPipeline.RunPostStageInEnv,
RunPreStageInEnv: refCdPipeline.RunPreStageInEnv,
DeploymentAppType: refCdPipeline.DeploymentAppType,
DeploymentAppType: deploymentAppType,
}
cdPipelineReq := &bean.CdPipelines{
Pipelines: []*bean.CDPipelineConfigObject{cdPipeline},
Expand Down
81 changes: 79 additions & 2 deletions pkg/attributes/AttributesService.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
package attributes

import (
"encoding/json"
"errors"
"fmt"
"github.com/devtron-labs/devtron/internal/sql/repository"
"github.com/go-pg/pg"
"go.uber.org/zap"
Expand All @@ -32,11 +35,13 @@ type AttributesService interface {
GetActiveList() ([]*AttributesDto, error)
GetByKey(key string) (*AttributesDto, error)
UpdateKeyValueByOne(key string) error
AddDeploymentEnforcementConfig(request *AttributesDto) (*AttributesDto, error)
}

const (
HostUrlKey string = "url"
API_SECRET_KEY string = "apiTokenSecret"
HostUrlKey string = "url"
API_SECRET_KEY string = "apiTokenSecret"
DEPLOYMENT_APP_TYPE_ALLOWED_CONFIG string = "deploymentAppTypeAllowedConfig"
Copy link
Contributor

Choose a reason for hiding this comment

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

replace to enforceDeploymentTypeConfig

)

type AttributesDto struct {
Expand Down Expand Up @@ -233,3 +238,75 @@ func (impl AttributesServiceImpl) UpdateKeyValueByOne(key string) error {
return err

}

func (impl AttributesServiceImpl) AddDeploymentEnforcementConfig(request *AttributesDto) (*AttributesDto, error) {
model, err := impl.attributesRepository.FindByKey(DEPLOYMENT_APP_TYPE_ALLOWED_CONFIG)
if err != nil && err != pg.ErrNoRows {
impl.logger.Errorw("error in fetching deploymentEnforcementConfig from db", "error", err, "key", request.Key)
return request, err
}
dbConnection := impl.attributesRepository.GetConnection()
tx, terr := dbConnection.Begin()
if terr != nil {
return request, terr
}
newConfig := make(map[string]map[string]bool)
err = json.Unmarshal([]byte(request.Value), &newConfig)
if err != nil {
return request, err
}
for environmentId, envConfig := range newConfig {
AllowedDeploymentAppTypes := 0
for _, allowed := range envConfig {
if allowed {
AllowedDeploymentAppTypes++
}
}
if AllowedDeploymentAppTypes == 0 && len(envConfig) > 0 {
return request, errors.New(fmt.Sprintf("Received invalid config for environment with id %s, "+
"at least one deployment app type should be allowed", environmentId))
}
}
// Rollback tx on error.
defer tx.Rollback()
if err == pg.ErrNoRows {
model := &repository.Attributes{
Key: DEPLOYMENT_APP_TYPE_ALLOWED_CONFIG,
Value: request.Value,
}
model.Active = true
model.UpdatedOn = time.Now()
model.UpdatedBy = request.UserId
_, err = impl.attributesRepository.Save(model, tx)
if err != nil {
return request, err
}
} else {

oldConfig := make(map[string]map[string]bool)
oldConfigString := model.Value
//initialConfigString = `{ "1": {"argo_cd": true}}`
err = json.Unmarshal([]byte(oldConfigString), &oldConfig)
if err != nil {
return request, err
}
mergedConfig := oldConfig
for k, v := range newConfig {
mergedConfig[k] = v
}
value, err := json.Marshal(mergedConfig)
if err != nil {
return request, err
}
model.Value = string(value)
model.UpdatedOn = time.Now()
model.UpdatedBy = request.UserId
model.Active = true
err = impl.attributesRepository.Update(model, tx)
if err != nil {
return request, err
}
}
tx.Commit()
return request, nil
}
9 changes: 6 additions & 3 deletions pkg/cluster/EnvironmentService.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
repository2 "github.com/devtron-labs/devtron/internal/sql/repository"
"github.com/devtron-labs/devtron/pkg/attributes"
"github.com/devtron-labs/devtron/pkg/user/bean"
"strconv"
"strings"
Expand Down Expand Up @@ -388,19 +389,21 @@ func (impl EnvironmentServiceImpl) GetEnvironmentListForAutocomplete(isDeploymen
if isDeploymentTypeParam {
for _, model := range models {
var (
deploymentConfig map[string]bool
allowedDeploymentConfigString []string
)
deploymentConfigValues, _ := impl.attributesRepository.FindByKey(fmt.Sprintf("%d", model.Id))
deploymentConfig := make(map[string]map[string]bool)
deploymentConfigEnvLevel := make(map[string]bool)
deploymentConfigValues, _ := impl.attributesRepository.FindByKey(attributes.DEPLOYMENT_APP_TYPE_ALLOWED_CONFIG)
//if empty config received(doesn't exist in table) which can't be parsed
if deploymentConfigValues.Value != "" {
if err = json.Unmarshal([]byte(deploymentConfigValues.Value), &deploymentConfig); err != nil {
return nil, err
}
deploymentConfigEnvLevel, _ = deploymentConfig[fmt.Sprintf("%d", model.Id)]
}

// if real config along with absurd values exist in table {"argo_cd": true, "helm": false, "absurd": false}",
if ok, filteredDeploymentConfig := impl.IsReceivedDeploymentTypeValid(deploymentConfig); ok {
if ok, filteredDeploymentConfig := impl.IsReceivedDeploymentTypeValid(deploymentConfigEnvLevel); ok {
allowedDeploymentConfigString = filteredDeploymentConfig
} else {
allowedDeploymentConfigString = permittedDeploymentConfigString
Expand Down
71 changes: 67 additions & 4 deletions pkg/pipeline/GitopsOrHelmOption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,70 @@ func TestGitopsOrHelmOption(t *testing.T) {
UserId: 0,
}
isGitOpsConfigured := true
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured)
deploymentConfig := make(map[string]bool)
deploymentConfig["argo_cd"] = true
Copy link
Contributor

Choose a reason for hiding this comment

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

use constants

deploymentConfig["helm"] = false
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)

for _, pipeline := range pipelineCreateRequest.Pipelines {
assert.Equal(t, pipeline.DeploymentAppType, "argo_cd")
}

})
t.Run("DeploymentAppSetterFunctionIfGitOpsConfiguredButNotAllowedExternalUse", func(t *testing.T) {

sugaredLogger, err := util.NewSugardLogger()
assert.Nil(t, err)

pipelineBuilderService := NewPipelineBuilderImpl(sugaredLogger, nil, nil, nil, nil,
nil, nil, nil,
nil, nil, nil, nil, nil,
nil, nil, nil, nil, util.MergeUtil{Logger: sugaredLogger}, nil,
nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil,
nil, nil, nil,
nil, nil, nil, nil,
nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, &DeploymentServiceTypeConfig{IsInternalUse: false}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)

pipelineCreateRequest := &bean.CdPipelines{
Pipelines: []*bean.CDPipelineConfigObject{
{
Id: 0,
EnvironmentId: 1,
EnvironmentName: "",
CiPipelineId: 1,
TriggerType: "AUTOMATIC",
Name: "cd-1-vo8q",
Strategies: nil,
Namespace: "devtron-demo",
AppWorkflowId: 1,
DeploymentTemplate: "",
PreStage: bean.CdStage{},
PostStage: bean.CdStage{},
PreStageConfigMapSecretNames: bean.PreStageConfigMapSecretNames{},
PostStageConfigMapSecretNames: bean.PostStageConfigMapSecretNames{},
RunPreStageInEnv: false,
RunPostStageInEnv: false,
CdArgoSetup: false,
ParentPipelineId: 1,
ParentPipelineType: "CI_PIPELINE",
DeploymentAppType: "",
},
},
AppId: 1,
UserId: 0,
}
isGitOpsConfigured := true
deploymentConfig := make(map[string]bool)
deploymentConfig["helm"] = true
deploymentConfig["argo_cd"] = false
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)

for _, pipeline := range pipelineCreateRequest.Pipelines {
assert.Equal(t, pipeline.DeploymentAppType, "helm")
}

})

t.Run("DeploymentAppSetterFunctionIfGitOpsNotConfiguredExternalUse", func(t *testing.T) {
Expand Down Expand Up @@ -108,7 +166,9 @@ func TestGitopsOrHelmOption(t *testing.T) {
UserId: 0,
}
isGitOpsConfigured := false
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured)
deploymentConfig := make(map[string]bool)
deploymentConfig["helm"] = true
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)

for _, pipeline := range pipelineCreateRequest.Pipelines {
assert.Equal(t, pipeline.DeploymentAppType, "helm")
Expand Down Expand Up @@ -161,7 +221,9 @@ func TestGitopsOrHelmOption(t *testing.T) {
UserId: 0,
}
isGitOpsConfigured := true
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestHelm, isGitOpsConfigured)
deploymentConfig := make(map[string]bool)
deploymentConfig["helm"] = true
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestHelm, isGitOpsConfigured, deploymentConfig)

for _, pipeline := range pipelineCreateRequestHelm.Pipelines {
assert.Equal(t, pipeline.DeploymentAppType, "helm")
Expand Down Expand Up @@ -195,7 +257,8 @@ func TestGitopsOrHelmOption(t *testing.T) {
AppId: 1,
UserId: 0,
}
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestGitOps, isGitOpsConfigured)
deploymentConfig["argo_cd"] = true
pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestGitOps, isGitOpsConfigured, deploymentConfig)

for _, pipeline := range pipelineCreateRequestGitOps.Pipelines {
assert.Equal(t, pipeline.DeploymentAppType, "argo_cd")
Expand Down
Loading