Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.vscode
.env
/cmd/external-app/devtron-ea
devtron
5 changes: 3 additions & 2 deletions api/appStore/InstalledAppRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/devtron-labs/devtron/pkg/cluster"
"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/rbac"
"github.com/devtron-labs/devtron/util/response"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -360,11 +361,11 @@ func (handler *InstalledAppRestHandlerImpl) FetchAppDetailsForInstalledApp(w htt
InternalMessage: "app detail fetched, failed to get resource tree from acd",
UserMessage: "app detail fetched, failed to get resource tree from acd",
}
appDetail.ResourceTree = &application.ResourceTreeResponse{}
appDetail.ResourceTree = map[string]interface{}{}
common.WriteJsonResp(w, nil, appDetail, http.StatusOK)
return
}
appDetail.ResourceTree = resp
appDetail.ResourceTree = util2.InterfaceToMapAdapter(resp)
handler.Logger.Debugf("application %s in environment %s had status %+v\n", installedAppId, envId, resp)
} else {
handler.Logger.Infow("appName and envName not found - avoiding resource tree call", "app", appDetail.AppName, "env", appDetail.EnvironmentName)
Expand Down
10 changes: 5 additions & 5 deletions api/bean/AppView.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package bean
import (
"encoding/json"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/devtron-labs/devtron/client/argocdServer/application"
)

type AppContainer struct {
Expand Down Expand Up @@ -107,14 +106,15 @@ type DeploymentDetailContainer struct {
K8sVersion string `json:"k8sVersion"`
CiArtifactId int `json:"ciArtifactId"`
ClusterId int `json:"clusterId"`
DeploymentAppType string `json:"deploymentAppType"`
}

type AppDetailContainer struct {
DeploymentDetailContainer `json:",inline"`
InstanceDetail []InstanceDetail `json:"instanceDetail"` //pod list with cpu, memory usage percent
Environments []Environment `json:"otherEnvironment,omitempty"`
LinkOuts []LinkOuts `json:"linkOuts,omitempty"`
ResourceTree *application.ResourceTreeResponse `json:"resourceTree,omitempty"`
InstanceDetail []InstanceDetail `json:"instanceDetail"` //pod list with cpu, memory usage percent
Environments []Environment `json:"otherEnvironment,omitempty"`
LinkOuts []LinkOuts `json:"linkOuts,omitempty"`
ResourceTree map[string]interface{} `json:"resourceTree,omitempty"`
}

type Environment struct {
Expand Down
13 changes: 13 additions & 0 deletions api/helm-app/applicationClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type HelmAppClient interface {
IsReleaseInstalled(ctx context.Context, in *ReleaseIdentifier) (*BooleanResponse, error)
RollbackRelease(ctx context.Context, in *RollbackReleaseRequest) (*BooleanResponse, error)
TemplateChart(ctx context.Context, in *InstallReleaseRequest) (*TemplateChartResponse, error)
InstallReleaseWithCustomChart(ctx context.Context, in *HelmInstallCustomRequest) (*HelmInstallCustomResponse, error)
}

type HelmAppClientImpl struct {
Expand Down Expand Up @@ -261,3 +262,15 @@ func (impl *HelmAppClientImpl) TemplateChart(ctx context.Context, in *InstallRel
}
return response, nil
}

func (impl *HelmAppClientImpl) InstallReleaseWithCustomChart(ctx context.Context, in *HelmInstallCustomRequest) (*HelmInstallCustomResponse, error) {
applicationClient, err := impl.getApplicationClient()
if err != nil {
return nil, err
}
response, err := applicationClient.InstallReleaseWithCustomChart(ctx, in)
if err != nil {
return nil, err
}
return response, nil
}
514 changes: 368 additions & 146 deletions api/helm-app/applist.pb.go

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions api/helm-app/applist.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ service ApplicationService {
rpc IsReleaseInstalled(ReleaseIdentifier) returns (BooleanResponse){}
rpc RollbackRelease(RollbackReleaseRequest) returns (BooleanResponse){}
rpc TemplateChart(InstallReleaseRequest) returns (TemplateChartResponse){}
rpc InstallReleaseWithCustomChart(HelmInstallCustomRequest) returns (HelmInstallCustomResponse) {}
}

message DeployedAppList {
Expand Down Expand Up @@ -249,4 +250,18 @@ message RollbackReleaseRequest {

message TemplateChartResponse {
string generatedManifest = 1;
}

message HelmInstallCustomRequest {
string valuesYaml = 1;
ChartContent chartContent = 2;
ReleaseIdentifier releaseIdentifier = 3;
}

message HelmInstallCustomResponse {
bool success = 1;
}

message ChartContent {
bytes Content = 1;
}
36 changes: 36 additions & 0 deletions api/helm-app/applist_grpc.pb.go

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

150 changes: 94 additions & 56 deletions api/restHandler/AppListingRestHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import (
"context"
"encoding/json"
"fmt"
client "github.com/devtron-labs/devtron/api/helm-app"
"github.com/devtron-labs/devtron/api/restHandler/common"
"github.com/devtron-labs/devtron/pkg/cluster"
"github.com/devtron-labs/devtron/pkg/user/casbin"
util2 "github.com/devtron-labs/devtron/util"
"net/http"
"strconv"
"strings"
Expand Down Expand Up @@ -65,6 +68,9 @@ type AppListingRestHandlerImpl struct {
enforcerUtil rbac.EnforcerUtil
deploymentGroupService deploymentGroup.DeploymentGroupService
userService user.UserService
helmAppClient client.HelmAppClient
clusterService cluster.ClusterService
helmAppService client.HelmAppService
}

type AppStatus struct {
Expand All @@ -81,7 +87,8 @@ func NewAppListingRestHandlerImpl(application application.ServiceClient,
enforcer casbin.Enforcer,
pipeline pipeline.PipelineBuilder,
logger *zap.SugaredLogger, enforcerUtil rbac.EnforcerUtil,
deploymentGroupService deploymentGroup.DeploymentGroupService, userService user.UserService) *AppListingRestHandlerImpl {
deploymentGroupService deploymentGroup.DeploymentGroupService, userService user.UserService,
helmAppClient client.HelmAppClient, clusterService cluster.ClusterService, helmAppService client.HelmAppService) *AppListingRestHandlerImpl {
appListingHandler := &AppListingRestHandlerImpl{
application: application,
appListingService: appListingService,
Expand All @@ -92,6 +99,9 @@ func NewAppListingRestHandlerImpl(application application.ServiceClient,
enforcerUtil: enforcerUtil,
deploymentGroupService: deploymentGroupService,
userService: userService,
helmAppClient: helmAppClient,
clusterService: clusterService,
helmAppService: helmAppService,
}
return appListingHandler
}
Expand Down Expand Up @@ -254,6 +264,7 @@ func (handler AppListingRestHandlerImpl) FetchAppDetails(w http.ResponseWriter,
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
return
}

envId, err := strconv.Atoi(vars["env-id"])
if err != nil {
common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
Expand All @@ -271,61 +282,7 @@ func (handler AppListingRestHandlerImpl) FetchAppDetails(w http.ResponseWriter,
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden)
return
}

if len(appDetail.AppName) > 0 && len(appDetail.EnvironmentName) > 0 {
//RBAC enforcer Ends
acdAppName := appDetail.AppName + "-" + appDetail.EnvironmentName
query := &application2.ResourcesQuery{
ApplicationName: &acdAppName,
}
ctx, cancel := context.WithCancel(r.Context())
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
ctx = context.WithValue(ctx, "token", token)
defer cancel()
start := time.Now()
resp, err := handler.application.ResourceTree(ctx, query)
elapsed := time.Since(start)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails, resource tree", "err", err, "app", appId, "env", envId)
err = &util.ApiError{
Code: constants.AppDetailResourceTreeNotFound,
InternalMessage: "app detail fetched, failed to get resource tree from acd",
UserMessage: "Error fetching detail, if you have recently created this deployment pipeline please try after sometime.",
}
common.WriteJsonResp(w, err, "", http.StatusInternalServerError)
return
}
if resp.Status == v1alpha1.HealthStatusHealthy {
status, err := handler.appListingService.ISLastReleaseStopType(appId, envId)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails", "err", err, "app", appId, "env", envId)
} else if status {
resp.Status = application.HIBERNATING
}
}
handler.logger.Debugf("FetchAppDetails, time elapsed %s in fetching application %s for environment %s", elapsed, appId, envId)

if resp.Status == v1alpha1.HealthStatusDegraded {
count, err := handler.appListingService.GetReleaseCount(appId, envId)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails, release count", "err", err, "app", appId, "env", envId)
} else if count == 0 {
resp.Status = app.NotDeployed
}
}
appDetail.ResourceTree = resp
handler.logger.Debugf("application %s in environment %s had status %+v\n", appId, envId, resp)
} else {
handler.logger.Warnw("appName and envName not found - avoiding resource tree call", "app", appDetail.AppName, "env", appDetail.EnvironmentName)
}
appDetail = handler.fetchResourceTree(w, r, token, appId, envId, appDetail)
common.WriteJsonResp(w, err, appDetail, http.StatusOK)
}

Expand Down Expand Up @@ -569,3 +526,84 @@ func (handler AppListingRestHandlerImpl) RedirectToLinkouts(w http.ResponseWrite
}
http.Redirect(w, r, link, http.StatusOK)
}

func (handler AppListingRestHandlerImpl) fetchResourceTree(w http.ResponseWriter, r *http.Request, token string, appId int, envId int, appDetail bean.AppDetailContainer) bean.AppDetailContainer {
if len(appDetail.AppName) > 0 && len(appDetail.EnvironmentName) > 0 && appDetail.DeploymentAppType == util.PIPELINE_DEPLOYMENT_TYPE_ACD {
//RBAC enforcer Ends
acdAppName := appDetail.AppName + "-" + appDetail.EnvironmentName
query := &application2.ResourcesQuery{
ApplicationName: &acdAppName,
}
ctx, cancel := context.WithCancel(r.Context())
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
ctx = context.WithValue(ctx, "token", token)
defer cancel()
start := time.Now()
resp, err := handler.application.ResourceTree(ctx, query)
elapsed := time.Since(start)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails, resource tree", "err", err, "app", appId, "env", envId)
err = &util.ApiError{
Code: constants.AppDetailResourceTreeNotFound,
InternalMessage: "app detail fetched, failed to get resource tree from acd",
UserMessage: "Error fetching detail, if you have recently created this deployment pipeline please try after sometime.",
}
common.WriteJsonResp(w, err, "", http.StatusInternalServerError)
return appDetail
}
if resp.Status == v1alpha1.HealthStatusHealthy {
status, err := handler.appListingService.ISLastReleaseStopType(appId, envId)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails", "err", err, "app", appId, "env", envId)
} else if status {
resp.Status = application.HIBERNATING
}
}
handler.logger.Debugf("FetchAppDetails, time elapsed %s in fetching application %s for environment %s", elapsed, appId, envId)

if resp.Status == v1alpha1.HealthStatusDegraded {
count, err := handler.appListingService.GetReleaseCount(appId, envId)
if err != nil {
handler.logger.Errorw("service err, FetchAppDetails, release count", "err", err, "app", appId, "env", envId)
} else if count == 0 {
resp.Status = app.NotDeployed
}
}
appDetail.ResourceTree = util2.InterfaceToMapAdapter(resp)
handler.logger.Debugf("application %s in environment %s had status %+v\n", appId, envId, resp)
} else if len(appDetail.AppName) > 0 && len(appDetail.EnvironmentName) > 0 && appDetail.DeploymentAppType == util.PIPELINE_DEPLOYMENT_TYPE_HELM {
config, err := handler.helmAppService.GetClusterConf(appDetail.ClusterId)
if err != nil {
handler.logger.Errorw("error in fetching cluster detail", "err", err)
}
req := &client.AppDetailRequest{
ClusterConfig: config,
Namespace: appDetail.Namespace,
ReleaseName: fmt.Sprintf("%s-%s", appDetail.AppName, appDetail.EnvironmentName),
}
detail, err := handler.helmAppClient.GetAppDetail(context.Background(), req)
if err != nil {
handler.logger.Errorw("error in fetching app detail", "err", err)
}
if detail != nil {
resourceTree := util2.InterfaceToMapAdapter(detail.ResourceTreeResponse)
resourceTree["status"] = detail.ReleaseStatus.Status
appDetail.ResourceTree = resourceTree
handler.logger.Warnw("appName and envName not found - avoiding resource tree call", "app", appDetail.AppName, "env", appDetail.EnvironmentName)
} else {
appDetail.ResourceTree = map[string]interface{}{}
}
} else {
appDetail.ResourceTree = map[string]interface{}{}
handler.logger.Warnw("appName and envName not found - avoiding resource tree call", "app", appDetail.AppName, "env", appDetail.EnvironmentName)
}
return appDetail
}
4 changes: 2 additions & 2 deletions api/restHandler/common/apiError.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ func WriteJsonResp(w http.ResponseWriter, err error, respBody interface{}, statu
errorsResp = append(errorsResp, errorResp)
}
response.Errors = errorsResp
} else if errStatus, ok := err.(*errors2.StatusError);ok{
} else if errStatus, ok := err.(*errors2.StatusError); ok {
apiErr := &util.ApiError{}
apiErr.Code = strconv.Itoa(int(errStatus.ErrStatus.Code))
apiErr.InternalMessage = errStatus.Error()
apiErr.UserMessage = errStatus.Error()
response.Errors = []*util.ApiError{apiErr}
} else{
} else {
apiErr := &util.ApiError{}
apiErr.Code = "000" // 000=unknown
apiErr.InternalMessage = errors.Details(err)
Expand Down
Loading