Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CRUD operations #6315

Open
wants to merge 20 commits into
base: link-external-argocd
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type DevtronAppDeploymentRestHandler interface {
GetCdPipelinesByEnvironmentMin(w http.ResponseWriter, r *http.Request)

ChangeChartRef(w http.ResponseWriter, r *http.Request)
ValidateArgoCDAppLinkRequest(w http.ResponseWriter, r *http.Request)
}

type DevtronAppDeploymentConfigRestHandler interface {
Expand Down Expand Up @@ -2545,3 +2546,49 @@ func (handler *PipelineConfigRestHandlerImpl) getCdPipelinesForCdPatchRbac(deplo
}
return handler.pipelineRepository.FindByIdsIn(cdPipelineIds)
}

func (handler *PipelineConfigRestHandlerImpl) ValidateArgoCDAppLinkRequest(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("token")

decoder := json.NewDecoder(r.Body)
userId, err := handler.userAuthService.GetLoggedInUser(r)
if userId == 0 || err != nil {
common.WriteJsonResp(w, err, "Unauthorized User", http.StatusUnauthorized)
return
}

var request pipelineBean.ArgoCDAppLinkValidationRequest
err = decoder.Decode(&request)
if err != nil {
handler.Logger.Errorw("request err, request", "err", err, "payload", request)
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

app, err := handler.pipelineBuilder.GetApp(request.AppId)
if err != nil {
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}
resourceName := handler.enforcerUtil.GetAppRBACName(app.AppName)
if ok := handler.enforcer.Enforce(token, casbin.ResourceApplications, casbin.ActionGet, resourceName); !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}

env, err := handler.EnvironmentRepository.FindOneByNamespaceAndClusterId(request.Namespace, request.ClusterId)
if err != nil {
common.WriteJsonResp(w, fmt.Errorf("error in getting environment for given clusterId and namespace"), nil, http.StatusBadRequest)
return
}

envObject := handler.enforcerUtil.GetEnvRBACNameByAppId(app.Id, env.Id)
if ok := handler.enforcer.Enforce(token, casbin.ResourceEnvironment, casbin.ActionUpdate, envObject); !ok {
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), "Unauthorized User", http.StatusForbidden)
return
}

response := handler.pipelineBuilder.ValidateLinkExternalArgoCDRequest(request)

common.WriteJsonResp(w, err, response, http.StatusOK)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
gitProviderRead "github.com/devtron-labs/devtron/pkg/build/git/gitProvider/read"
bean3 "github.com/devtron-labs/devtron/pkg/build/pipeline/bean"
"github.com/devtron-labs/devtron/pkg/chart/gitOpsConfig"
repository2 "github.com/devtron-labs/devtron/pkg/cluster/environment/repository"
"github.com/devtron-labs/devtron/pkg/deployment/manifest/deployedAppMetrics"
"github.com/devtron-labs/devtron/pkg/deployment/manifest/deploymentTemplate"
"github.com/devtron-labs/devtron/pkg/deployment/manifest/deploymentTemplate/chartRef"
Expand Down Expand Up @@ -134,6 +135,7 @@ type PipelineConfigRestHandlerImpl struct {
chartRefService chartRef.ChartRefService
ciCdPipelineOrchestrator pipeline.CiCdPipelineOrchestrator
teamReadService read3.TeamReadService
EnvironmentRepository repository2.EnvironmentRepository
}

func NewPipelineRestHandlerImpl(pipelineBuilder pipeline.PipelineBuilder, Logger *zap.SugaredLogger,
Expand Down Expand Up @@ -165,7 +167,8 @@ func NewPipelineRestHandlerImpl(pipelineBuilder pipeline.PipelineBuilder, Logger
chartRefService chartRef.ChartRefService,
ciCdPipelineOrchestrator pipeline.CiCdPipelineOrchestrator,
gitProviderReadService gitProviderRead.GitProviderReadService,
teamReadService read3.TeamReadService) *PipelineConfigRestHandlerImpl {
teamReadService read3.TeamReadService,
EnvironmentRepository repository2.EnvironmentRepository) *PipelineConfigRestHandlerImpl {
envConfig := &PipelineRestHandlerEnvConfig{}
err := env.Parse(envConfig)
if err != nil {
Expand Down Expand Up @@ -205,6 +208,7 @@ func NewPipelineRestHandlerImpl(pipelineBuilder pipeline.PipelineBuilder, Logger
ciCdPipelineOrchestrator: ciCdPipelineOrchestrator,
gitProviderReadService: gitProviderReadService,
teamReadService: teamReadService,
EnvironmentRepository: EnvironmentRepository,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (router PipelineConfigRouterImpl) InitPipelineConfigRouter(configRouter *mu
configRouter.Path("/cd-pipeline/patch/deployment/trigger").HandlerFunc(router.restHandler.HandleTriggerDeploymentAfterTypeChange).Methods("POST")
configRouter.Path("/cd-pipeline/{appId}").HandlerFunc(router.restHandler.GetCdPipelines).Methods("GET")
configRouter.Path("/cd-pipeline/{appId}/env/{envId}").HandlerFunc(router.restHandler.GetCdPipelinesForAppAndEnv).Methods("GET")
configRouter.Path("/cd-pipeline/validate-link-request").HandlerFunc(router.restHandler.ValidateArgoCDAppLinkRequest).Methods("POST")
//save environment specific override
configRouter.Path("/env/{appId}/{environmentId}").HandlerFunc(router.restHandler.EnvConfigOverrideCreate).Methods("POST")
configRouter.Path("/env/patch").HandlerFunc(router.restHandler.ChangeChartRef).Methods("PATCH")
Expand Down
35 changes: 35 additions & 0 deletions client/argocdServer/ArgoClientWrapperService.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ type ApplicationClientWrapper interface {
// GetArgoAppByName fetches an argoCd app by its name
GetArgoAppByName(ctx context.Context, appName string) (*v1alpha1.Application, error)

GetArgoAppByNameWithK8sClient(ctx context.Context, clusterId int, namespace, appName string) (map[string]interface{}, error)

DeleteArgoAppWithK8sClient(ctx context.Context, clusterId int, namespace, appName string, cascadeDelete bool) error

// SyncArgoCDApplicationIfNeededAndRefresh - if ARGO_AUTO_SYNC_ENABLED=true, app will be refreshed to initiate refresh at argoCD side or else it will be synced and refreshed
SyncArgoCDApplicationIfNeededAndRefresh(context context.Context, argoAppName string) error

Expand Down Expand Up @@ -150,6 +154,7 @@ type ArgoClientWrapperServiceImpl struct {
gitOperationService git.GitOperationService
asyncRunnable *async.Runnable
acdConfigGetter config2.ArgoCDConfigGetter
argoK8sClient ArgoK8sClient
*ArgoClientWrapperServiceEAImpl
}

Expand All @@ -164,6 +169,7 @@ func NewArgoClientWrapperServiceImpl(
gitOperationService git.GitOperationService, asyncRunnable *async.Runnable,
acdConfigGetter config2.ArgoCDConfigGetter,
ArgoClientWrapperServiceEAImpl *ArgoClientWrapperServiceEAImpl,
argoK8sClient ArgoK8sClient,
) *ArgoClientWrapperServiceImpl {
return &ArgoClientWrapperServiceImpl{
acdApplicationClient: acdClient,
Expand All @@ -178,6 +184,7 @@ func NewArgoClientWrapperServiceImpl(
asyncRunnable: asyncRunnable,
acdConfigGetter: acdConfigGetter,
ArgoClientWrapperServiceEAImpl: ArgoClientWrapperServiceEAImpl,
argoK8sClient: argoK8sClient,
}
}

Expand Down Expand Up @@ -401,6 +408,34 @@ func (impl *ArgoClientWrapperServiceImpl) GetArgoAppByName(ctx context.Context,
return argoApplication, nil
}

func (impl *ArgoClientWrapperServiceImpl) GetArgoAppByNameWithK8sClient(ctx context.Context, clusterId int, namespace, appName string) (map[string]interface{}, error) {
k8sConfig, err := impl.acdConfigGetter.GetK8sConfigWithClusterIdAndNamespace(clusterId, namespace)
if err != nil {
impl.logger.Errorw("error in getting k8s config", "err", err)
return nil, err
}
argoApplication, err := impl.argoK8sClient.GetArgoApplication(k8sConfig, appName)
if err != nil {
impl.logger.Errorw("err in getting argo app by name", "app", appName)
return nil, err
}
return argoApplication, nil
}

func (impl *ArgoClientWrapperServiceImpl) DeleteArgoAppWithK8sClient(ctx context.Context, clusterId int, namespace, appName string, cascadeDelete bool) error {
k8sConfig, err := impl.acdConfigGetter.GetK8sConfigWithClusterIdAndNamespace(clusterId, namespace)
if err != nil {
impl.logger.Errorw("error in getting k8s config", "err", err)
return err
}
err = impl.argoK8sClient.DeleteArgoApplication(ctx, k8sConfig, appName, cascadeDelete)
if err != nil {
impl.logger.Errorw("err in getting argo app by name", "app", appName)
return err
}
return nil
}

func (impl *ArgoClientWrapperServiceImpl) IsArgoAppPatchRequired(argoAppSpec *v1alpha1.ApplicationSource, currentGitRepoUrl, currentChartPath string) bool {
return (len(currentGitRepoUrl) != 0 && argoAppSpec.RepoURL != currentGitRepoUrl) ||
argoAppSpec.Path != currentChartPath ||
Expand Down
9 changes: 9 additions & 0 deletions client/argocdServer/ArgoClientWrapperServiceEA.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ func (impl *ArgoClientWrapperServiceEAImpl) DeleteArgoApp(ctx context.Context, a
return nil, nil
}

func (impl *ArgoClientWrapperServiceEAImpl) DeleteArgoAppWithK8sClient(ctx context.Context, clusterId int, namespace, appName string, cascadeDelete bool) error {
return nil
}

func (impl *ArgoClientWrapperServiceEAImpl) SyncArgoCDApplicationIfNeededAndRefresh(ctx context.Context, argoAppName string) error {
impl.logger.Info("not implemented")
return nil
Expand Down Expand Up @@ -123,6 +127,11 @@ func (impl *ArgoClientWrapperServiceEAImpl) GetArgoAppByName(ctx context.Context
return nil, nil
}

func (impl *ArgoClientWrapperServiceEAImpl) GetArgoAppByNameWithK8sClient(ctx context.Context, clusterId int, namespace, appName string) (map[string]interface{}, error) {
impl.logger.Info("not implemented for EA mode")
return nil, nil
}

func (impl *ArgoClientWrapperServiceEAImpl) IsArgoAppPatchRequired(argoAppSpec *v1alpha1.ApplicationSource, currentGitRepoUrl, currentChartPath string) bool {
impl.logger.Info("not implemented for EA mode")
return false
Expand Down
20 changes: 20 additions & 0 deletions client/argocdServer/config/Config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
type ArgoCDConfigGetter interface {
GetGRPCConfig() (*bean.ArgoGRPCConfig, error)
GetK8sConfig() (*bean.ArgoK8sConfig, error)
GetK8sConfigWithClusterIdAndNamespace(clusterId int, namespace string) (*bean.ArgoK8sConfig, error)
}

type ArgoCDConfigGetterImpl struct {
Expand Down Expand Up @@ -88,3 +89,22 @@ func (impl *ArgoCDConfigGetterImpl) GetK8sConfig() (*bean.ArgoK8sConfig, error)
}
return k8sConfig, nil
}

func (impl *ArgoCDConfigGetterImpl) GetK8sConfigWithClusterIdAndNamespace(clusterId int, namespace string) (*bean.ArgoK8sConfig, error) {
clusterBean, err := impl.clusterReadService.FindById(clusterId)
if err != nil {
impl.logger.Errorw("error in fetching cluster bean from db", "err", err)
return nil, err
}
cfg := clusterBean.GetClusterConfig()
restConfig, err := impl.K8sService.GetRestConfigByCluster(cfg)
if err != nil {
impl.logger.Errorw("error in getting k8s config", "err", err)
return nil, err
}
k8sConfig := &bean.ArgoK8sConfig{
RestConfig: restConfig,
AcdNamespace: namespace,
}
return k8sConfig, nil
}
14 changes: 14 additions & 0 deletions client/argocdServer/helper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package argocdServer

import (
json2 "encoding/json"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -36,3 +37,16 @@ func isArgoAppSyncModeMigrationNeeded(argoApplication *v1alpha1.Application, acd
}
return false
}

func GetAppObject(appMapObj map[string]interface{}) (*v1alpha1.Application, error) {
appJson, err := json2.Marshal(appMapObj)
if err != nil {
return nil, err
}
var app v1alpha1.Application
err = json2.Unmarshal(appJson, &app)
if err != nil {
return nil, err
}
return &app, err
}
46 changes: 37 additions & 9 deletions client/argocdServer/k8sClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/devtron-labs/common-lib/utils/k8s"
"github.com/devtron-labs/devtron/client/argocdServer/bean"
"github.com/devtron-labs/devtron/internal/util"
"github.com/devtron-labs/devtron/pkg/cluster/repository"
"go.uber.org/zap"
"io/ioutil"
k8sError "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"path/filepath"
"text/template"
Expand All @@ -56,7 +58,8 @@ const (

type ArgoK8sClient interface {
CreateAcdApp(ctx context.Context, appRequest *AppTemplate, applicationTemplatePath string) (string, error)
GetArgoApplication(namespace string, appName string, cluster *repository.Cluster) (map[string]interface{}, error)
GetArgoApplication(k8sConfig *bean.ArgoK8sConfig, appName string) (map[string]interface{}, error)
DeleteArgoApplication(ctx context.Context, k8sConfig *bean.ArgoK8sConfig, appName string, cascadeDelete bool) error
}
type ArgoK8sClientImpl struct {
logger *zap.SugaredLogger
Expand Down Expand Up @@ -171,13 +174,9 @@ func (impl ArgoK8sClientImpl) handleArgoAppCreationError(res []byte, err error)
return apiError
}

func (impl ArgoK8sClientImpl) GetArgoApplication(namespace string, appName string, cluster *repository.Cluster) (map[string]interface{}, error) {
func (impl ArgoK8sClientImpl) GetArgoApplication(k8sConfig *bean.ArgoK8sConfig, appName string) (map[string]interface{}, error) {

config, err := rest.InClusterConfig()
if err != nil {
impl.logger.Errorw("error in cluster config", "err", err)
return nil, err
}
config := k8sConfig.RestConfig
config.GroupVersion = &schema.GroupVersion{Group: "argoproj.io", Version: "v1alpha1"}
config.NegotiatedSerializer = serializer.NewCodecFactory(runtime.NewScheme())
config.APIPath = "/apis"
Expand All @@ -191,7 +190,7 @@ func (impl ArgoK8sClientImpl) GetArgoApplication(namespace string, appName strin
//opts := metav1.GetOptions{}
res, err := client.
Get().
Namespace(namespace).
Namespace(k8sConfig.AcdNamespace).
Resource("applications").
Name(appName).
//VersionedParams(&opts, metav1.ParameterCodec).
Expand All @@ -207,3 +206,32 @@ func (impl ArgoK8sClientImpl) GetArgoApplication(namespace string, appName strin
impl.logger.Infow("get argo cd application", "res", response, "err", err)
return response, err
}

func (impl ArgoK8sClientImpl) DeleteArgoApplication(ctx context.Context, k8sConfig *bean.ArgoK8sConfig, appName string, cascadeDelete bool) error {

patchType := types.JSONPatchType
patchJSON := ""

//TODO: ayush test cascade delete
if cascadeDelete {
patchJSON = `{"metadata": {"finalizers": ["resources-finalizer.argocd.argoproj.io"]}}`
} else {
patchJSON = `{"metadata": {"finalizers": null}}`
}

applicationGVK := v1alpha1.ApplicationSchemaGroupVersionKind

_, err := impl.k8sUtil.PatchResourceRequest(ctx, k8sConfig.RestConfig, patchType, patchJSON, appName, k8sConfig.AcdNamespace, applicationGVK)
if err != nil {
impl.logger.Errorw("error in patching argo application", "err", err)
return err
}

_, err = impl.k8sUtil.DeleteResource(ctx, k8sConfig.RestConfig, applicationGVK, k8sConfig.AcdNamespace, appName, true)
if err != nil {
impl.logger.Errorw("error in patching argo application", "acdAppName", appName, "err", err)
return err
}

return nil
}
3 changes: 2 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.

2 changes: 1 addition & 1 deletion internal/sql/repository/deploymentConfig/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type DeploymentConfig struct {
RepoUrl string `sql:"repo_url"`
RepoName string `sql:"repo_name"`
ReleaseMode string `sql:"release_mode"`
ReleaseConfig []byte `sql:"release_config"`
ReleaseConfig string `sql:"release_config"`
Active bool `sql:"active,notnull"`
sql.AuditLog
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ type PipelineRepository interface {
FindIdsByAppIdsAndEnvironmentIds(appIds, environmentIds []int) (ids []int, err error)
FindIdsByProjectIdsAndEnvironmentIds(projectIds, environmentIds []int) ([]int, error)

GetArgoPipelineByArgoAppName(argoAppName string) (Pipeline, error)
GetArgoPipelineByArgoAppName(argoAppName string) ([]Pipeline, error)
FindActiveByAppIds(appIds []int) (pipelines []*Pipeline, err error)
FindAppAndEnvironmentAndProjectByPipelineIds(pipelineIds []int) (pipelines []*Pipeline, err error)
FilterDeploymentDeleteRequestedPipelineIds(cdPipelineIds []int) (map[int]bool, error)
Expand Down Expand Up @@ -698,8 +698,8 @@ func (impl PipelineRepositoryImpl) FindIdsByProjectIdsAndEnvironmentIds(projectI
return pipelineIds, err
}

func (impl PipelineRepositoryImpl) GetArgoPipelineByArgoAppName(argoAppName string) (Pipeline, error) {
var pipeline Pipeline
func (impl PipelineRepositoryImpl) GetArgoPipelineByArgoAppName(argoAppName string) ([]Pipeline, error) {
var pipeline []Pipeline
err := impl.dbConnection.Model(&pipeline).
Join("LEFT JOIN deployment_config dc on dc.app_id = pipeline.app_id and dc.environment_id=pipeline.environment_id and dc.active=true").
Column("pipeline.*", "Environment").
Expand Down
1 change: 1 addition & 0 deletions internal/util/ChartTemplateService.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (

const (
PIPELINE_RELEASE_MODE_CREATE = "create"
PIPELINE_RELEASE_MODE_LINK = "link"
)

type ChartCreateRequest struct {
Expand Down
Loading