Repository: devtron-labs/devtron
Pull request #3616 (2023-07-10 20:02:39)

enhancement: enforcing deployment type in environment
<!--
Type of change: Title of the PR should clearly mention which type of PR is this, you can select any of the below mentioned types:

- docs - The PR contains Documentation ONLY changes. 
- feat - The PR contains new feature/enhancements.
- fix - The PR contains a bug fix.
- chore - Development changes related to the build system (involving scripts, configurations or tools) and package dependencies.
- test - Development changes related to tests.
- perf - Changes related to performance improvements.

Example Title: 
docs: Webhook CI documentation changes
-->

# Description
<!--
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
-->

Fixes # 3615


## How Has This Been Tested?
1) If gitOps or Helm Option is enabled from UI :- 
  a) only allowed env can be selected
2) If option is disabled :-
 a) If gitOps is not allowed, app is deployed through helm
 b) If gitOps is allowed and configured, app is deployed using gitOps
 c) If gitOps is allowed and not configured, app is deployed using helm
3) In app clone, new app is created with allowed deployment app type for that environment and not of actual deployment app type

## Checklist:

* [x] The title of the PR states what changed and the related issues number (used for the release note).
* [ ] Does this PR requires documentation updates?
* [ ] I've updated documentation as required by this PR.
* [x] I have performed a self-review of my own code.
* [ ] I have commented my code, particularly in hard-to-understand areas.
* [ ] I have tested it for all user roles.
* [ ] I have added all the required unit/api test cases.

## Does this PR introduce a user-facing change?
<!--
If NO, leave the release-note block blank.
If YES, a release note is required:
Enter your extended release note in the block below. If the PR requires additional manual action from users switching to the new version, include the string "action-required".

-->
```release-note

```




Patch:
diff --git a/api/restHandler/AttributesRestHandlder.go b/api/restHandler/AttributesRestHandlder.go
index d54476822..951105b97 100644
--- a/api/restHandler/AttributesRestHandlder.go
+++ b/api/restHandler/AttributesRestHandlder.go
@@ -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 {
@@ -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)
+}
diff --git a/api/router/AttributesRouter.go b/api/router/AttributesRouter.go
index 5a303a007..31c678b70 100644
--- a/api/router/AttributesRouter.go
+++ b/api/router/AttributesRouter.go
@@ -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")
 }
diff --git a/pkg/appClone/AppCloneService.go b/pkg/appClone/AppCloneService.go
index 2c2bdae75..bf3cab66a 100644
--- a/pkg/appClone/AppCloneService.go
+++ b/pkg/appClone/AppCloneService.go
@@ -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,
@@ -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},
diff --git a/pkg/attributes/AttributesService.go b/pkg/attributes/AttributesService.go
index 286243927..ed3dae7e9 100644
--- a/pkg/attributes/AttributesService.go
+++ b/pkg/attributes/AttributesService.go
@@ -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"
@@ -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"
+	ENFORCE_DEPLOYMENT_TYPE_CONFIG string = "enforceDeploymentTypeConfig"
 )
 
 type AttributesDto struct {
@@ -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(ENFORCE_DEPLOYMENT_TYPE_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:   ENFORCE_DEPLOYMENT_TYPE_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
+}
diff --git a/pkg/cluster/EnvironmentService.go b/pkg/cluster/EnvironmentService.go
index 0bbba767f..8ea0634b6 100644
--- a/pkg/cluster/EnvironmentService.go
+++ b/pkg/cluster/EnvironmentService.go
@@ -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"
@@ -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.ENFORCE_DEPLOYMENT_TYPE_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
diff --git a/pkg/pipeline/PipelineBuilder.go b/pkg/pipeline/PipelineBuilder.go
index 06392cda7..6beceede1 100644
--- a/pkg/pipeline/PipelineBuilder.go
+++ b/pkg/pipeline/PipelineBuilder.go
@@ -153,7 +153,7 @@ type PipelineBuilder interface {
 	PerformBulkActionOnCdPipelines(dto *bean.CdBulkActionRequestDto, impactedPipelines []*pipelineConfig.Pipeline, ctx context.Context, dryRun bool, userId int32) ([]*bean.CdBulkActionResponseDto, error)
 	DeleteCiPipeline(request *bean.CiPatchRequest) (*bean.CiPipeline, error)
 	IsGitOpsRequiredForCD(pipelineCreateRequest *bean.CdPipelines) bool
-	SetPipelineDeploymentAppType(pipelineCreateRequest *bean.CdPipelines, isGitOpsConfigured bool)
+	SetPipelineDeploymentAppType(pipelineCreateRequest *bean.CdPipelines, isGitOpsConfigured bool, deploymentTypeValidationConfig map[string]bool)
 	MarkGitOpsDevtronAppsDeletedWhereArgoAppIsDeleted(appId int, envId int, acdToken string, pipeline *pipelineConfig.Pipeline) (bool, error)
 	GetCiPipelineByEnvironment(request appGroup2.AppGroupingRequest) ([]*bean.CiConfigRequest, error)
 	GetCiPipelineByEnvironmentMin(request appGroup2.AppGroupingRequest) ([]*bean.CiPipelineMinResponse, error)
@@ -162,6 +162,8 @@ type PipelineBuilder interface {
 	GetExternalCiByEnvironment(request appGroup2.AppGroupingRequest) (ciConfig []*bean.ExternalCiConfig, err error)
 	GetEnvironmentListForAutocompleteFilter(envName string, clusterIds []int, offset int, size int, emailId string, checkAuthBatch func(emailId string, appObject []string, envObject []string) (map[string]bool, map[string]bool), ctx context.Context) (*cluster.AppGroupingResponse, error)
 	GetAppListForEnvironment(request appGroup2.AppGroupingRequest) ([]*AppBean, error)
+	GetDeploymentConfigMap(environmentId int) (map[string]bool, error)
+	IsGitopsConfigured() (bool, error)
 }
 type PipelineBuilderImpl struct {
 	logger                        *zap.SugaredLogger
@@ -1748,68 +1750,52 @@ func (impl PipelineBuilderImpl) IsGitOpsRequiredForCD(pipelineCreateRequest *bea
 	return haveAtLeastOneGitOps
 }
 
-func (impl PipelineBuilderImpl) SetPipelineDeploymentAppType(pipelineCreateRequest *bean.CdPipelines, isGitOpsConfigured bool) {
-	//isInternalUse := impl.deploymentConfig.IsInternalUse
-	//var globalDeploymentAppType string
-	//if !isInternalUse {
-	//	if isGitOpsConfigured {
-	//		globalDeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_ACD
-	//	} else {
-	//		globalDeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_HELM
-	//	}
-	//} else {
-	//	// if gitops or helm is option available, and deployment app type is not present in pipeline request/
-	//	for _, pipeline := range pipelineCreateRequest.Pipelines {
-	//		if pipeline.DeploymentAppType == "" {
-	//			if isGitOpsConfigured {
-	//				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_ACD
-	//			} else {
-	//				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_HELM
-	//			}
-	//		}
-	//	}
-	//}
-	//for _, pipeline := range pipelineCreateRequest.Pipelines {
-	//	if !isInternalUse {
-	//		pipeline.DeploymentAppType = globalDeploymentAppType
-	//	}
-	//}
-
+func (impl PipelineBuilderImpl) SetPipelineDeploymentAppType(pipelineCreateRequest *bean.CdPipelines, isGitOpsConfigured bool, deploymentTypeValidationConfig map[string]bool) {
 	for _, pipeline := range pipelineCreateRequest.Pipelines {
+		// by default both deployment app type are allowed
+		AllowedDeploymentAppTypes := map[string]bool{
+			util.PIPELINE_DEPLOYMENT_TYPE_ACD:  true,
+			util.PIPELINE_DEPLOYMENT_TYPE_HELM: true,
+		}
+		for k, v := range deploymentTypeValidationConfig {
+			// rewriting allowed deployment types based on config provided by user
+			AllowedDeploymentAppTypes[k] = v
+		}
 		if !impl.deploymentConfig.IsInternalUse {
-			if isGitOpsConfigured {
+			if isGitOpsConfigured && AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_ACD] {
 				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_ACD
-			} else {
+			} else if AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_HELM] {
 				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_HELM
 			}
 		}
 		if pipeline.DeploymentAppType == "" {
-			if isGitOpsConfigured {
+			if isGitOpsConfigured && AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_ACD] {
 				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_ACD
-			} else {
+			} else if AllowedDeploymentAppTypes[util.PIPELINE_DEPLOYMENT_TYPE_HELM] {
 				pipeline.DeploymentAppType = util.PIPELINE_DEPLOYMENT_TYPE_HELM
 			}
 		}
 	}
-
 }
 
 func (impl PipelineBuilderImpl) CreateCdPipelines(pipelineCreateRequest *bean.CdPipelines, ctx context.Context) (*bean.CdPipelines, error) {
 
 	//Validation for checking deployment App type
+	isGitOpsConfigured, err := impl.IsGitopsConfigured()
+
 	for _, pipeline := range pipelineCreateRequest.Pipelines {
 		// if no deployment app type sent from user then we'll not validate
-		if pipeline.DeploymentAppType != "" {
-			if err := impl.validateDeploymentAppType(pipeline); err != nil {
-				impl.logger.Errorw("validation error in creating pipeline", "name", pipeline.Name, "err", err)
-				return nil, err
-			}
+		deploymentConfig, err := impl.GetDeploymentConfigMap(pipeline.EnvironmentId)
+		if err != nil {
+			return nil, err
+		}
+		impl.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)
+		if err := impl.validateDeploymentAppType(pipeline, deploymentConfig); err != nil {
+			impl.logger.Errorw("validation error in creating pipeline", "name", pipeline.Name, "err", err)
+			return nil, err
 		}
-		continue
 	}
 
-	isGitOpsConfigured, err := impl.IsGitopsConfigured()
-	impl.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured)
 	isGitOpsRequiredForCD := impl.IsGitOpsRequiredForCD(pipelineCreateRequest)
 	app, err := impl.appRepo.FindById(pipelineCreateRequest.AppId)
 	if err != nil {
@@ -1856,20 +1842,7 @@ func (impl PipelineBuilderImpl) CreateCdPipelines(pipelineCreateRequest *bean.Cd
 	return pipelineCreateRequest, nil
 }
 
-func (impl PipelineBuilderImpl) validateDeploymentAppType(pipeline *bean.CDPipelineConfigObject) error {
-	var deploymentConfig map[string]bool
-	deploymentConfigValues, _ := impl.attributesRepository.FindByKey(fmt.Sprintf("%d", pipeline.EnvironmentId))
-	//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 {
-			rerr := &util.ApiError{
-				HttpStatusCode:  http.StatusInternalServerError,
-				InternalMessage: err.Error(),
-				UserMessage:     "Failed to fetch deployment config values from the attributes table",
-			}
-			return rerr
-		}
-	}
+func (impl PipelineBuilderImpl) validateDeploymentAppType(pipeline *bean.CDPipelineConfigObject, deploymentConfig map[string]bool) error {
 
 	// Config value doesn't exist in attribute table
 	if deploymentConfig == nil {
@@ -1910,6 +1883,28 @@ func validDeploymentConfigReceived(deploymentConfig map[string]bool, deploymentT
 	return false
 }
 
+func (impl PipelineBuilderImpl) GetDeploymentConfigMap(environmentId int) (map[string]bool, error) {
+	var deploymentConfig map[string]map[string]bool
+	var deploymentConfigEnv map[string]bool
+	deploymentConfigValues, err := impl.attributesRepository.FindByKey(attributes.ENFORCE_DEPLOYMENT_TYPE_CONFIG)
+	if err == pg.ErrNoRows {
+		return deploymentConfigEnv, nil
+	}
+	//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 {
+			rerr := &util.ApiError{
+				HttpStatusCode:  http.StatusInternalServerError,
+				InternalMessage: err.Error(),
+				UserMessage:     "Failed to fetch deployment config values from the attributes table",
+			}
+			return deploymentConfigEnv, rerr
+		}
+		deploymentConfigEnv, _ = deploymentConfig[fmt.Sprintf("%d", environmentId)]
+	}
+	return deploymentConfigEnv, nil
+}
+
 func (impl PipelineBuilderImpl) PatchCdPipelines(cdPipelines *bean.CDPatchRequest, ctx context.Context) (*bean.CdPipelines, error) {
 	pipelineRequest := &bean.CdPipelines{
 		UserId:    cdPipelines.UserId,


Test patch:
diff --git a/pkg/pipeline/GitopsOrHelmOption_test.go b/pkg/pipeline/GitopsOrHelmOption_test.go
index f3833871f..1b3f34738 100644
--- a/pkg/pipeline/GitopsOrHelmOption_test.go
+++ b/pkg/pipeline/GitopsOrHelmOption_test.go
@@ -55,12 +55,70 @@ func TestGitopsOrHelmOption(t *testing.T) {
 			UserId: 0,
 		}
 		isGitOpsConfigured := true
-		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig[bean.ArgoCd] = true
+		deploymentConfig[bean.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[bean.Helm] = true
+		deploymentConfig[bean.ArgoCd] = false
+		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)
+
+		for _, pipeline := range pipelineCreateRequest.Pipelines {
+			assert.Equal(t, pipeline.DeploymentAppType, "helm")
+		}
+
 	})
 
 	t.Run("DeploymentAppSetterFunctionIfGitOpsNotConfiguredExternalUse", func(t *testing.T) {
@@ -108,7 +166,9 @@ func TestGitopsOrHelmOption(t *testing.T) {
 			UserId: 0,
 		}
 		isGitOpsConfigured := false
-		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig[bean.Helm] = true
+		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequest, isGitOpsConfigured, deploymentConfig)
 
 		for _, pipeline := range pipelineCreateRequest.Pipelines {
 			assert.Equal(t, pipeline.DeploymentAppType, "helm")
@@ -161,7 +221,9 @@ func TestGitopsOrHelmOption(t *testing.T) {
 			UserId: 0,
 		}
 		isGitOpsConfigured := true
-		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestHelm, isGitOpsConfigured)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig[bean.Helm] = true
+		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestHelm, isGitOpsConfigured, deploymentConfig)
 
 		for _, pipeline := range pipelineCreateRequestHelm.Pipelines {
 			assert.Equal(t, pipeline.DeploymentAppType, "helm")
@@ -189,13 +251,14 @@ func TestGitopsOrHelmOption(t *testing.T) {
 					CdArgoSetup:                   false,
 					ParentPipelineId:              1,
 					ParentPipelineType:            "CI_PIPELINE",
-					DeploymentAppType:             "argo_cd",
+					DeploymentAppType:             bean.ArgoCd,
 				},
 			},
 			AppId:  1,
 			UserId: 0,
 		}
-		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestGitOps, isGitOpsConfigured)
+		deploymentConfig[bean.ArgoCd] = true
+		pipelineBuilderService.SetPipelineDeploymentAppType(pipelineCreateRequestGitOps, isGitOpsConfigured, deploymentConfig)
 
 		for _, pipeline := range pipelineCreateRequestGitOps.Pipelines {
 			assert.Equal(t, pipeline.DeploymentAppType, "argo_cd")
@@ -302,7 +365,7 @@ func TestGitopsOrHelmOption(t *testing.T) {
 					CdArgoSetup:                   false,
 					ParentPipelineId:              1,
 					ParentPipelineType:            "CI_PIPELINE",
-					DeploymentAppType:             "argo_cd",
+					DeploymentAppType:             bean.ArgoCd,
 				},
 			},
 			AppId:  1,
diff --git a/pkg/pipeline/PipelineBuilder_test.go b/pkg/pipeline/PipelineBuilder_test.go
index 34f8dc32d..b35f2c2ce 100644
--- a/pkg/pipeline/PipelineBuilder_test.go
+++ b/pkg/pipeline/PipelineBuilder_test.go
@@ -25,17 +25,19 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 			DeploymentAppType: "SomeAppType",
 		}
 
-		mockDeploymentConfigConfig := &repository.Attributes{
-			Id:       1,
-			Key:      "2",
-			Value:    "{\"argo_cd\": true, \"helm\": true}",
-			Active:   false,
-			AuditLog: sql.AuditLog{},
-		}
-		mockError := error(nil)
-		attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
-
-		err := impl.validateDeploymentAppType(pipeline)
+		//mockDeploymentConfigConfig := &repository.Attributes{
+		//	Id:       1,
+		//	Key:      "2",
+		//	Value:    "{\"argo_cd\": true, \"helm\": true}",
+		//	Active:   false,
+		//	AuditLog: sql.AuditLog{},
+		//}
+		//mockError := error(nil)
+		//attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig["argo_cd"] = true
+		deploymentConfig["helm"] = true
+		err := impl.validateDeploymentAppType(pipeline, deploymentConfig)
 		assert.Nil(t, err)
 	})
 
@@ -45,11 +47,6 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 		impl := PipelineBuilderImpl{
 			attributesRepository: attributesRepoMock, // Provide a mock implementation of attributesRepository
 		}
-		pipeline := &bean.CDPipelineConfigObject{
-			EnvironmentId:     123,
-			DeploymentAppType: "SomeAppType",
-		}
-
 		mockDeploymentConfigConfig := &repository.Attributes{
 			Id:       1,
 			Key:      "2",
@@ -59,8 +56,10 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 		}
 		mockError := error(nil)
 		attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
-
-		err := impl.validateDeploymentAppType(pipeline)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig["argo_cd"] = true
+		deploymentConfig["helm"] = true
+		_, err := impl.GetDeploymentConfigMap(1)
 		apiErr, _ := err.(*util.ApiError)
 		assert.Equal(t, http.StatusInternalServerError, apiErr.HttpStatusCode)
 	})
@@ -76,17 +75,20 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 			DeploymentAppType: "SomeAppType",
 		}
 
-		mockDeploymentConfig := &repository.Attributes{
-			Id:       1,
-			Key:      "123",
-			Value:    "{\"argo_cd\": true, \"helm\": true}",
-			Active:   false,
-			AuditLog: sql.AuditLog{},
-		}
-		mockError := error(nil)
-		attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfig, mockError)
-
-		err := impl.validateDeploymentAppType(pipeline)
+		//mockDeploymentConfig := &repository.Attributes{
+		//	Id:       1,
+		//	Key:      "123",
+		//	Value:    "{\"argo_cd\": true, \"helm\": true}",
+		//	Active:   false,
+		//	AuditLog: sql.AuditLog{},
+		//}
+		//mockError := error(nil)
+		//attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfig, mockError)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig["argo_cd"] = true
+		deploymentConfig["helm"] = true
+
+		err := impl.validateDeploymentAppType(pipeline, deploymentConfig)
 
 		assert.Nil(t, err)
 	})
@@ -102,17 +104,20 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 			DeploymentAppType: "helm",
 		}
 
-		mockDeploymentConfigConfig := &repository.Attributes{
-			Id:       1,
-			Key:      "123",
-			Value:    "{\"argo_cd\": false, \"helm\": true}",
-			Active:   false,
-			AuditLog: sql.AuditLog{},
-		}
-		mockError := error(nil)
-		attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
-
-		err := impl.validateDeploymentAppType(pipeline)
+		//mockDeploymentConfigConfig := &repository.Attributes{
+		//	Id:       1,
+		//	Key:      "123",
+		//	Value:    "{\"argo_cd\": false, \"helm\": true}",
+		//	Active:   false,
+		//	AuditLog: sql.AuditLog{},
+		//}
+		//mockError := error(nil)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig["argo_cd"] = false
+		deploymentConfig["helm"] = true
+		//attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
+
+		err := impl.validateDeploymentAppType(pipeline, deploymentConfig)
 
 		assert.Nil(t, err)
 	})
@@ -128,18 +133,20 @@ func TestPipelineBuilderImpl_validateDeploymentAppType(t *testing.T) {
 			DeploymentAppType: "SomeOtherAppType",
 		}
 
-		mockDeploymentConfigConfig := &repository.Attributes{
-			Id:       1,
-			Key:      "123",
-			Value:    "{\"argo_cd\": false, \"helm\": true}",
-			Active:   false,
-			AuditLog: sql.AuditLog{},
-		}
-
-		mockError := error(nil)
-		attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
-
-		err := impl.validateDeploymentAppType(pipeline)
+		//mockDeploymentConfigConfig := &repository.Attributes{
+		//	Id:       1,
+		//	Key:      "123",
+		//	Value:    "{\"argo_cd\": false, \"helm\": true}",
+		//	Active:   false,
+		//	AuditLog: sql.AuditLog{},
+		//}
+
+		//mockError := error(nil)
+		//attributesRepoMock.On("FindByKey", mock.Anything).Return(mockDeploymentConfigConfig, mockError)
+		deploymentConfig := make(map[string]bool)
+		deploymentConfig["argo_cd"] = false
+		deploymentConfig["helm"] = true
+		err := impl.validateDeploymentAppType(pipeline, deploymentConfig)
 		apiErr, _ := err.(*util.ApiError)
 		assert.Equal(t, http.StatusBadRequest, apiErr.HttpStatusCode)
 	})