Skip to content

Commit dceffa9

Browse files
taken merge from main
2 parents 3a5ccd8 + 85fdd65 commit dceffa9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+6838
-74
lines changed

DockerfileEA

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.20 AS build-env
1+
FROM golang:1.21 AS build-env
22

33
RUN echo $GOPATH
44
RUN apt update

README.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,8 @@ Devtron is built on some of the most trusted and loved technologies:
187187

188188
## :muscle: Trusted By
189189

190-
Devtron is trusted by Enterprises and Communities all across the globe:
191-
<br>
190+
Devtron is trusted by communities all across the globe. The list of organizations using Devtron can be found [here](./USERS.MD).
192191

193-
- [Delhivery:](https://www.delhivery.com/) Delhivery is an Indian delivery and e-commerce logistics company, that provides end-to-end Supply Chain solutions through cutting-edge technology
194-
- [BharatPe:](https://bharatpe.com/) Bharatpe is an Indian fintech company that offers a range of products including interoperable QR codes for UPI payments, POS machines for card acceptance, and small business financing
195-
- [Livspace:](https://www.livspace.com/in) Livspace is a home interior and renovation company, that provides interior design and renovation services in Singapore and India
196-
- [Moglix:](https://www.moglix.com/) Moglix is an industrial B2B marketplace and an e-commerce platform for industrial tools and equipment, used largely by businesses in India
197-
- [Xoxoday:](https://www.xoxoday.com/) Xoxoday provides technology infrastructure to enable businesses to automate rewards, incentives & payouts for employees, customers & channel partners
198192

199193
## :question: FAQs & Troubleshooting
200194

USERS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Who is using Devtron?
2+
3+
As the community grows, we want to keep track of the users and organizations using Devtron. If you're using Devtron, please raise a PR to add your organization name and a link to your webpage.
4+
5+
Currently, Devtron is being used by the following organizations:
6+
7+
1. [73strings](https://www.73strings.com/)
8+
2. [Ather Energy](https://www.atherenergy.com/)
9+
3. [BharatPe](https://bharatpe.com/)
10+
4. [Birdeye](https://birdeye.com/)
11+
5. [Bluecopa](https://www.bluecopa.com/)
12+
6. [Chitale Bandhu](https://www.chitalebandhu.in/)
13+
7. [Centricity](https://centricity.co.in/)
14+
8. [Cyble](https://cyble.com/)
15+
9. [Delhivery](https://www.delhivery.com/)
16+
10. [KHEL Group](https://thekhelgroup.com/)
17+
11. [Lemnisk](https://www.lemnisk.co/)
18+
12. [OTPLess](https://otpless.com/)
19+
14. [Spinny](https://www.spinny.com/)
20+
15. [Tata 1Mg](https://www.1mg.com/)
21+
16. [TravClan](https://www.travclan.com/)
22+
17. [Unity Small Finance Bank](https://theunitybank.com/)
23+
18. [Xoxoday](https://www.xoxoday.com/)
24+
25+
26+

api/appStore/InstalledAppRestHandler.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
util3 "github.com/devtron-labs/devtron/pkg/appStore/util"
2929
"github.com/devtron-labs/devtron/pkg/bean"
3030
"net/http"
31+
"reflect"
3132
"strconv"
3233
"strings"
3334
"time"
@@ -363,6 +364,8 @@ func (handler *InstalledAppRestHandlerImpl) DeployBulk(w http.ResponseWriter, r
363364
common.WriteJsonResp(w, fmt.Errorf("unauthorized user"), nil, http.StatusForbidden)
364365
return
365366
}
367+
charts, authRes := handler.checkForHelmDeployAuth(request, token)
368+
request.ChartGroupInstallChartRequest = charts
366369
//RBAC block ends here
367370

368371
visited := make(map[string]bool)
@@ -391,10 +394,116 @@ func (handler *InstalledAppRestHandlerImpl) DeployBulk(w http.ResponseWriter, r
391394
handler.Logger.Errorw("service err, DeployBulk", "err", err, "payload", request)
392395
common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
393396
return
397+
} else {
398+
res = authRes
394399
}
395400
common.WriteJsonResp(w, err, res, http.StatusOK)
396401
}
397402

403+
func (handler *InstalledAppRestHandlerImpl) checkForHelmDeployAuth(request chartGroup.ChartGroupInstallRequest, token string) ([]*chartGroup.ChartGroupInstallChartRequest, *chartGroup.ChartGroupInstallAppRes) {
404+
//the value of this map is array of integer because the GetHelmObjectByProjectIdAndEnvId method may return "//" for error cases
405+
//so different environments may contain same object, to handle that we are using (map[string] []int)
406+
rbacObjectToEnvIdMap1 := make(map[string][]int)
407+
rbacObjectToEnvIdMap2 := make(map[string][]int)
408+
409+
rbacObjectArray1 := make([]string, 0)
410+
rbacObjectArray2 := make([]string, 0)
411+
412+
envIdToChartGroupInstallChartRequest := make(map[int][]*chartGroup.ChartGroupInstallChartRequest)
413+
414+
for _, chartGroupInstall := range request.ChartGroupInstallChartRequest {
415+
envIdToChartGroupInstallChartRequest[chartGroupInstall.EnvironmentId] = append(envIdToChartGroupInstallChartRequest[chartGroupInstall.EnvironmentId], chartGroupInstall)
416+
rbacObject1, rbacObject2 := handler.enforcerUtil.GetHelmObjectByProjectIdAndEnvId(request.ProjectId, chartGroupInstall.EnvironmentId)
417+
_, ok := rbacObjectToEnvIdMap1[rbacObject1]
418+
if !ok {
419+
rbacObjectToEnvIdMap1[rbacObject1] = make([]int, 0)
420+
}
421+
rbacObjectToEnvIdMap1[rbacObject1] = append(rbacObjectToEnvIdMap1[rbacObject1], chartGroupInstall.EnvironmentId)
422+
rbacObjectArray1 = append(rbacObjectArray1, rbacObject1)
423+
_, ok = rbacObjectToEnvIdMap2[rbacObject2]
424+
if !ok {
425+
rbacObjectToEnvIdMap2[rbacObject2] = make([]int, 0)
426+
}
427+
rbacObjectToEnvIdMap2[rbacObject2] = append(rbacObjectToEnvIdMap2[rbacObject2], chartGroupInstall.EnvironmentId)
428+
rbacObjectArray2 = append(rbacObjectArray2, rbacObject2)
429+
}
430+
resultObjectMap1 := handler.enforcer.EnforceInBatch(token, casbin.ResourceHelmApp, casbin.ActionCreate, rbacObjectArray1)
431+
resultObjectMap2 := handler.enforcer.EnforceInBatch(token, casbin.ResourceHelmApp, casbin.ActionCreate, rbacObjectArray2)
432+
433+
authorizedEnvIdSet := make(map[int]bool)
434+
435+
//O(n) time loop , at max we will only iterate through all the envs
436+
for obj, ok := range resultObjectMap1 {
437+
if ok {
438+
envIds := rbacObjectToEnvIdMap1[obj]
439+
for _, envId := range envIds {
440+
authorizedEnvIdSet[envId] = true
441+
}
442+
}
443+
}
444+
for obj, ok := range resultObjectMap2 {
445+
if ok {
446+
envIds := rbacObjectToEnvIdMap2[obj]
447+
for _, envId := range envIds {
448+
authorizedEnvIdSet[envId] = true
449+
}
450+
}
451+
}
452+
authorizedChartGroupInstallRequests := make([]*chartGroup.ChartGroupInstallChartRequest, 0)
453+
for envId, _ := range authorizedEnvIdSet {
454+
authorizedChartGroupInstall := envIdToChartGroupInstallChartRequest[envId]
455+
for _, authChartGroup := range authorizedChartGroupInstall {
456+
authorizedChartGroupInstallRequests = append(authorizedChartGroupInstallRequests, authChartGroup)
457+
}
458+
}
459+
unauthorizedChartGroupInstallRequests := make([]*chartGroup.ChartGroupInstallChartRequest, 0)
460+
461+
for _, req := range request.ChartGroupInstallChartRequest {
462+
isAuthorized := false
463+
for _, authReq := range authorizedChartGroupInstallRequests {
464+
if reflect.DeepEqual(req, authReq) {
465+
isAuthorized = true
466+
break
467+
}
468+
}
469+
if !isAuthorized {
470+
unauthorizedChartGroupInstallRequests = append(unauthorizedChartGroupInstallRequests, req)
471+
}
472+
}
473+
474+
// Create slices for ChartGroupInstallMetadata
475+
authorizedMetadata := make([]chartGroup.ChartGroupInstallMetadata, 0)
476+
unauthorizedMetadata := make([]chartGroup.ChartGroupInstallMetadata, 0)
477+
478+
for _, req := range authorizedChartGroupInstallRequests {
479+
metadata := handler.getChartGroupInstallMetadata(req, string(chartGroup.StatusSuccess), string(chartGroup.ReasonTriggered))
480+
authorizedMetadata = append(authorizedMetadata, metadata)
481+
}
482+
483+
for _, req := range unauthorizedChartGroupInstallRequests {
484+
metadata := handler.getChartGroupInstallMetadata(req, string(chartGroup.StatusFailed), string(chartGroup.ReasonNotAuthorize))
485+
unauthorizedMetadata = append(unauthorizedMetadata, metadata)
486+
}
487+
unauthorizeCount := len(unauthorizedChartGroupInstallRequests)
488+
totalCount := len(request.ChartGroupInstallChartRequest)
489+
// Combine all metadata into a single ChartGroupInstallAppRes
490+
chartGroupInstallAppRes := &chartGroup.ChartGroupInstallAppRes{
491+
ChartGroupInstallMetadata: append(authorizedMetadata, unauthorizedMetadata...),
492+
Summary: fmt.Sprintf(chartGroup.FAILED_TO_TRIGGER, unauthorizeCount, totalCount),
493+
}
494+
return authorizedChartGroupInstallRequests, chartGroupInstallAppRes
495+
}
496+
497+
func (handler *InstalledAppRestHandlerImpl) getChartGroupInstallMetadata(req *chartGroup.ChartGroupInstallChartRequest, triggerStatus string, reason string) chartGroup.ChartGroupInstallMetadata {
498+
metadata := chartGroup.ChartGroupInstallMetadata{
499+
AppName: req.AppName,
500+
EnvironmentId: req.EnvironmentId,
501+
TriggerStatus: triggerStatus,
502+
Reason: reason,
503+
}
504+
return metadata
505+
}
506+
398507
func (handler *InstalledAppRestHandlerImpl) CheckAppExists(w http.ResponseWriter, r *http.Request) {
399508
userId, err := handler.userAuthService.GetLoggedInUser(r)
400509
if userId == 0 || err != nil {

assets/bitbucket-logo-plugin.jpeg

7.19 KB
Loading

pkg/appStore/chartGroup/bean.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ type ChartGroupInstallChartRequest struct {
3333
ReferenceValueKind string `json:"referenceValueKind, omitempty" validate:"oneof=DEFAULT TEMPLATE DEPLOYED"`
3434
ChartGroupEntryId int `json:"chartGroupEntryId"` //optional
3535
}
36-
36+
type ChartGroupInstallMetadata struct {
37+
AppName string `json:"appName"`
38+
EnvironmentId int `json:"environmentId"`
39+
TriggerStatus string `json:"triggerStatus"`
40+
Reason string `json:"reason"`
41+
}
3742
type ChartGroupInstallAppRes struct {
43+
ChartGroupInstallMetadata []ChartGroupInstallMetadata `json:"chartGroupInstallMetadata"`
44+
Summary string `json:"summary"`
3845
}
46+
type TriggerStatus string
47+
type Reason string
48+
49+
const FAILED_TO_TRIGGER = "%d/%d failed to trigger"
50+
const (
51+
StatusFailed TriggerStatus = "failed"
52+
StatusSuccess TriggerStatus = "success"
53+
ReasonNotAuthorize Reason = "not authorized"
54+
ReasonTriggered Reason = "triggered"
55+
)

pkg/auth/user/UserCommonService.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -572,13 +572,16 @@ func (impl UserCommonServiceImpl) CheckRbacForClusterEntity(cluster, namespace,
572572
resourceObj = "*"
573573
}
574574

575-
rbacResource := fmt.Sprintf("%s/%s/%s", strings.ToLower(cluster), strings.ToLower(namespaceObj), casbin.ResourceUser)
576575
resourcesArray := strings.Split(resourceObj, ",")
577-
for _, resourceVal := range resourcesArray {
578-
rbacObject := fmt.Sprintf("%s/%s/%s", groupObj, kindObj, resourceVal)
579-
allowed := managerAuth(rbacResource, token, rbacObject)
580-
if !allowed {
581-
return false
576+
namespacesArray := strings.Split(namespaceObj, ",")
577+
for _, namespaceInArray := range namespacesArray {
578+
rbacResource := fmt.Sprintf("%s/%s/%s", strings.ToLower(cluster), strings.ToLower(namespaceInArray), casbin.ResourceUser)
579+
for _, resourceVal := range resourcesArray {
580+
rbacObject := fmt.Sprintf("%s/%s/%s", groupObj, kindObj, resourceVal)
581+
allowed := managerAuth(rbacResource, token, rbacObject)
582+
if !allowed {
583+
return false
584+
}
582585
}
583586
}
584587
return true
@@ -690,8 +693,8 @@ func (impl UserCommonServiceImpl) GetUniqueKeyForAllEntity(role repository.RoleM
690693
key = fmt.Sprintf("%s_%s_%s_%s", role.Team, role.Action, role.AccessType, role.Entity)
691694
} else if len(role.Entity) > 0 {
692695
if role.Entity == bean.CLUSTER_ENTITIY {
693-
key = fmt.Sprintf("%s_%s_%s_%s_%s_%s", role.Entity, role.Action, role.Cluster,
694-
role.Namespace, role.Group, role.Kind)
696+
key = fmt.Sprintf("%s_%s_%s_%s_%s", role.Entity, role.Action, role.Cluster,
697+
role.Group, role.Kind)
695698
} else {
696699
key = fmt.Sprintf("%s_%s", role.Entity, role.Action)
697700
}

pkg/deployment/manifest/deploymentTemplate/chartRef/ChartRefService.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package chartRef
1818

1919
import (
2020
"bytes"
21+
"context"
2122
"encoding/json"
2223
"errors"
2324
"fmt"
@@ -29,8 +30,10 @@ import (
2930
util2 "github.com/devtron-labs/devtron/util"
3031
dirCopy "github.com/otiai10/copy"
3132
"go.uber.org/zap"
33+
"golang.org/x/exp/maps"
3234
"io/ioutil"
3335
"k8s.io/helm/pkg/chartutil"
36+
chart2 "k8s.io/helm/pkg/proto/hapi/chart"
3437
"os"
3538
"path"
3639
"path/filepath"
@@ -70,19 +73,21 @@ type ChartRefFileOpService interface {
7073
GetRefChart(chartRefId int) (string, string, string, string, error)
7174
ExtractChartIfMissing(chartData []byte, refChartDir string, location string) (*bean.ChartDataInfo, error)
7275
GetChartInBytes(chartRefId int, deleteChart bool) ([]byte, error)
73-
GetChartBytesInBulk(chartRefIds []int, deleteChart bool) (map[int][]byte, error)
76+
GetChartBytesForApps(ctx context.Context, appIdToAppName map[int]string) (map[int][]byte, error)
7477
}
7578

7679
type ChartRefServiceImpl struct {
7780
logger *zap.SugaredLogger
7881
chartRefRepository chartRepoRepository.ChartRefRepository
7982
chartTemplateService util.ChartTemplateService
8083
mergeUtil util.MergeUtil
84+
chartRepository chartRepoRepository.ChartRepository
8185
}
8286

8387
func NewChartRefServiceImpl(logger *zap.SugaredLogger,
8488
chartRefRepository chartRepoRepository.ChartRefRepository,
8589
chartTemplateService util.ChartTemplateService,
90+
chartRepository chartRepoRepository.ChartRepository,
8691
mergeUtil util.MergeUtil) *ChartRefServiceImpl {
8792
// cache devtron reference charts list
8893
devtronChartList, _ := chartRefRepository.FetchAllNonUserUploadedChartInfo()
@@ -92,6 +97,7 @@ func NewChartRefServiceImpl(logger *zap.SugaredLogger,
9297
chartRefRepository: chartRefRepository,
9398
chartTemplateService: chartTemplateService,
9499
mergeUtil: mergeUtil,
100+
chartRepository: chartRepository,
95101
}
96102
}
97103

@@ -309,23 +315,83 @@ func (impl *ChartRefServiceImpl) extractChartInBytes(chartRef *chartRepoReposito
309315
}
310316
return manifestByteArr, nil
311317
}
312-
func (impl *ChartRefServiceImpl) GetChartBytesInBulk(chartRefIds []int, performCleanup bool) (map[int][]byte, error) {
318+
319+
func (impl *ChartRefServiceImpl) getChartPath(chartRef *chartRepoRepository.ChartRef) (string, error) {
320+
refChartPath := filepath.Join(bean.RefChartDirPath, chartRef.Location)
321+
// For user uploaded charts ChartData will be retrieved from DB
322+
if chartRef.ChartData != nil {
323+
chartInfo, err := impl.ExtractChartIfMissing(chartRef.ChartData, bean.RefChartDirPath, chartRef.Location)
324+
if chartInfo != nil && chartInfo.TemporaryFolder != "" {
325+
err1 := os.RemoveAll(chartInfo.TemporaryFolder)
326+
if err1 != nil {
327+
impl.logger.Errorw("error in deleting temp dir ", "err", err)
328+
}
329+
}
330+
} else {
331+
// For Devtron reference charts the chart will be load from the directory location
332+
}
333+
return refChartPath, nil
334+
}
335+
336+
func (impl *ChartRefServiceImpl) GetChartBytesForApps(ctx context.Context, appIdToAppName map[int]string) (map[int][]byte, error) {
337+
338+
appIds := maps.Keys(appIdToAppName)
339+
charts, err := impl.chartRepository.FindLatestChartByAppIds(appIds)
340+
if err != nil {
341+
impl.logger.Errorw("error in fetching chart", "err", err, "appIds", appIds)
342+
return nil, err
343+
}
344+
345+
chartRefIdTOAppIds := make(map[int][]int)
346+
var chartRefIds []int
347+
chartRefToChartVersion := make(map[int]string)
348+
349+
for _, chart := range charts {
350+
chartRefIds = append(chartRefIds, chart.ChartRefId)
351+
chartRefToChartVersion[chart.ChartRefId] = chart.ChartVersion
352+
refAppIds, ok := chartRefIdTOAppIds[chart.ChartRefId]
353+
if !ok {
354+
refAppIds = make([]int, 0)
355+
}
356+
refAppIds = append(refAppIds, chart.AppId)
357+
chartRefIdTOAppIds[chart.ChartRefId] = refAppIds
358+
}
359+
313360
chartRefs, err := impl.chartRefRepository.FindByIds(chartRefIds)
314361
if err != nil {
315362
impl.logger.Errorw("error getting chart data", "chartRefIds", chartRefIds, "err", err)
316363
return nil, err
317364
}
318-
chartRefIdToBytes := make(map[int][]byte)
365+
366+
appIdToBytes := make(map[int][]byte)
367+
368+
// this loops run with O(len(apps)) T.C
319369
for _, chartRef := range chartRefs {
320-
chartInBytes, err := impl.extractChartInBytes(chartRef, performCleanup)
370+
refChartPath, err := impl.getChartPath(chartRef)
321371
if err != nil {
322372
impl.logger.Errorw("error in converting chart to bytes", "chartRefId", chartRef.Id, "err", err)
323373
return nil, err
324374
}
325-
chartRefIdToBytes[chartRef.Id] = chartInBytes
375+
376+
refAppIds := chartRefIdTOAppIds[chartRef.Id]
377+
for _, appId := range refAppIds {
378+
chartMetaData := &chart2.Metadata{
379+
Name: appIdToAppName[appId],
380+
Version: chartRefToChartVersion[chartRef.Id],
381+
}
382+
tempReferenceTemplateDir, err := impl.chartTemplateService.BuildChart(ctx, chartMetaData, refChartPath)
383+
if err != nil {
384+
impl.logger.Errorw("error in building chart", "chartMetaData", chartMetaData, "refChartPath", refChartPath)
385+
return nil, err
386+
}
387+
chartInBytes, err := impl.chartTemplateService.LoadChartInBytes(tempReferenceTemplateDir, true)
388+
appIdToBytes[appId] = chartInBytes
389+
}
390+
326391
}
327-
return chartRefIdToBytes, nil
392+
return appIdToBytes, nil
328393
}
394+
329395
func (impl *ChartRefServiceImpl) FetchCustomChartsInfo() ([]*bean.ChartDto, error) {
330396
resultsMetadata, err := impl.chartRefRepository.GetAllChartMetadata()
331397
if err != nil {

0 commit comments

Comments
 (0)