Skip to content

Commit cd464f2

Browse files
kartik-579vikramdevtronSNe789kripanshdevtron
authored
Oss sync (#12)
* feat: cluster bearer token hide from dashboard (#2894) * cluster token config removed mandatory * api spec added for cluster update and create, and cluster list api changes for token * fix check config for cluster token * docs: mount pvc (#2941) * added pvc * added pvc * added pvc * added pvc * added pvc * feat: Resource browser child ref (#2913) * child rbac handling init commit * resource manifest validate handling added * handle rbac case * wire gen fix * gvk passed and ap resource handling * code cleaning * dead code cleaning * removed unused func * fix: k8s log stream cpu issue (#2929) * updated buffer size * refactored code for getting logs from k8s * updated Dockerfile * updated Dockerfile * downgraded go version * removed redundant log --------- Co-authored-by: Vikram <[email protected]> Co-authored-by: SNe789 <[email protected]> Co-authored-by: kripanshdevtron <[email protected]>
1 parent 0564d56 commit cd464f2

File tree

10 files changed

+342
-83
lines changed

10 files changed

+342
-83
lines changed

Dockerfile

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

33
RUN echo $GOPATH
44
RUN apt update

DockerfileEA

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

33
RUN echo $GOPATH
44
RUN apt update

api/connector/Connector.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"net/http"
3434
"regexp"
3535
"strconv"
36+
"strings"
3637
"sync"
3738
"time"
3839
)
@@ -104,28 +105,39 @@ func (impl PumpImpl) StartK8sStreamWithHeartBeat(w http.ResponseWriter, isReconn
104105
done <- true
105106
}()
106107

107-
// heartbeat end
108-
for {
109-
sc := bufio.NewScanner(stream)
110-
for sc.Scan() {
111-
log := sc.Text()
112-
a := regexp.MustCompile(" ")
113-
splitLog := a.Split(log, 2)
114-
timeParsed, err := time.Parse(time.RFC3339, splitLog[0])
115-
if err != nil {
116-
impl.logger.Errorw("error in writing data over sse", "err", err)
117-
return
118-
}
119-
mux.Lock()
120-
err = impl.sendEvent([]byte(strconv.FormatInt(timeParsed.UnixNano(), 10)), nil, []byte(splitLog[1]), w)
121-
mux.Unlock()
122-
if err != nil {
123-
impl.logger.Errorw("error in writing data over sse", "err", err)
108+
bufReader := bufio.NewReader(stream)
109+
eof := false
110+
for !eof {
111+
log, err := bufReader.ReadString('\n')
112+
if err == io.EOF {
113+
eof = true
114+
// stop if we reached end of stream and the next line is empty
115+
if log == "" {
124116
return
125117
}
126-
f.Flush()
118+
} else if err != nil && err != io.EOF {
119+
impl.logger.Errorw("error in reading buffer string, StartK8sStreamWithHeartBeat", "err", err)
120+
return
121+
}
122+
log = strings.TrimSpace(log) // Remove trailing line ending
123+
a := regexp.MustCompile(" ")
124+
splitLog := a.Split(log, 2)
125+
parsedTime, err := time.Parse(time.RFC3339, splitLog[0])
126+
if err != nil {
127+
impl.logger.Errorw("error in writing data over sse", "err", err)
128+
return
127129
}
130+
eventId := strconv.FormatInt(parsedTime.UnixNano(), 10)
131+
mux.Lock()
132+
err = impl.sendEvent([]byte(eventId), nil, []byte(splitLog[1]), w)
133+
mux.Unlock()
134+
if err != nil {
135+
impl.logger.Errorw("error in writing data over sse", "err", err)
136+
return
137+
}
138+
f.Flush()
128139
}
140+
// heartbeat end
129141
}
130142

131143
func (impl PumpImpl) StartStreamWithHeartBeat(w http.ResponseWriter, isReconnect bool, recv func() (*application.LogEntry, error), err error) {
@@ -223,11 +235,9 @@ func (impl *PumpImpl) sendEvent(eventId []byte, eventName []byte, payload []byte
223235
res = append(res, payload...)
224236
}
225237
res = append(res, '\n', '\n')
226-
if i, err := w.Write(res); err != nil {
238+
if _, err := w.Write(res); err != nil {
227239
impl.logger.Errorf("Failed to send response chunk: %v", err)
228240
return err
229-
} else {
230-
impl.logger.Debugw("msg written", "count", i)
231241
}
232242

233243
return nil

cmd/external-app/wire_gen.go

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/user-guide/creating-application/overview.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,63 @@ Click **Save**. The application will be moved to the selected project.
4747
* To remove the tags from propagation, click the symbol <img src="https://devtron-public-asset.s3.us-east-2.amazonaws.com/images/creating-application/propagate-dark.jpg" height="10"> again.
4848
* Click `Save`.
4949

50-
The changes in the tags will be reflected in the `Tags` on the `Overview` section.
50+
The changes in the tags will be reflected in the `Tags` on the `Overview` section.
51+
52+
53+
## Configure PersistentVolumeClaim (PVC) for Build Time Optimization
54+
55+
A PersistentVolumeClaim (PVC) volume is a request for storage, which is used to mount a PersistentVolume (PV) into a Pod. In order to optimize build time, you can configure PVC in your application.
56+
57+
If you want to optimize build time for the multiple target platforms (e.g., arm64, amd64), mounting a PVC will provide volume directly to a pod which helps in shorter build time by storing build cache. Mounting a PVC into a pod will provide storage for build cache which will not impact the normal build where the image is built on the basis of architecture and operating system of the K8s node on which CI is running.
58+
59+
### Create PVC file
60+
61+
* The following configuration file describes persistent volume claim e.g.,`cache-pvc.yaml`, where you have to define the metadata `name` and `storageClassname`.
62+
63+
```bash
64+
apiVersion: v1
65+
kind: PersistentVolumeClaim
66+
metadata:
67+
name: cache-pvc # here comes the name of PVC
68+
spec:
69+
accessModes:
70+
- ReadWriteOnce
71+
storageClassName: # here comes storage class name
72+
resources:
73+
requests:
74+
storage: 30Gi
75+
```
76+
77+
* Create the PersistentVolumeClaim by running the following command:
78+
79+
```bash
80+
kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml -n {namespace}
81+
```
82+
83+
For more detail, refer [Kubernetes PVC](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim).
84+
85+
86+
## Configure PVC
87+
88+
In order to configure PVC:
89+
* Go to the `Overview` section of your application.
90+
* On the right-corner, click `Edit Tags`.
91+
92+
![](https://devtron-public-asset.s3.us-east-2.amazonaws.com/images/creating-application/overview/pvc-edit-tags.jpg)
93+
94+
![](https://devtron-public-asset.s3.us-east-2.amazonaws.com/images/creating-application/overview/manage-tags-pvc.jpg)
95+
96+
* For app level PVC mounting, enter the following:<ul><li>key:`devtron.ai/ci-pvc-all`</li><li>value: metadata name (e.g., `cache-pvc)` which you define on the [PVC template](#create-pvc-file).</li></ul>`Note`: This PVC mounting will impact all the build pipilines of the application.
97+
* For pipeline level, enter the following:<ul><li>key:`devtron.ai/ci-pvc-{pipelinename}`</li><li>value: metadata name which you define on the [PVC template](#create-pvc-file).</li></ul>`Note`: This PVC mounting will impact only the particular build pipeline.
98+
99+
To know the `pipelinename` detail, go to the `App Configutation`, click `Workflow Editor` the pipeline name will be on the `Build` pipeline as shown below.
100+
101+
![](https://devtron-public-asset.s3.us-east-2.amazonaws.com/images/creating-application/overview/pipeline-name-pvc.jpg)
102+
103+
* Click `Save`.
104+
105+
106+
107+
108+
109+

internal/util/K8sUtil.go

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
error2 "errors"
2424
"flag"
2525
"fmt"
26+
"github.com/argoproj/gitops-engine/pkg/utils/kube"
2627
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
2728
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29+
"k8s.io/apimachinery/pkg/runtime/schema"
2830
"net/http"
2931
"os/user"
3032
"path/filepath"
@@ -546,11 +548,12 @@ func (impl K8sUtil) GetPodByName(namespace string, name string, client *v12.Core
546548
}
547549
}
548550

549-
func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.UnstructuredList, namespaced bool, kind string, validateResourceAccess func(namespace, resourceName string) bool) (*ClusterResourceListMap, error) {
551+
func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.UnstructuredList, namespaced bool, gvk schema.GroupVersionKind, validateResourceAccess func(namespace string, group string, kind string, resourceName string) bool) (*ClusterResourceListMap, error) {
550552
clusterResourceListMap := &ClusterResourceListMap{}
551553
// build headers
552554
var headers []string
553555
columnIndexes := make(map[int]string)
556+
kind := gvk.Kind
554557
if kind == "Event" {
555558
headers, columnIndexes = impl.getEventKindHeader()
556559
} else {
@@ -587,13 +590,11 @@ func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.Unstructu
587590
// build rows
588591
rowsMapping := make([]map[string]interface{}, 0)
589592
rowsDataUncast := manifest.Object[K8sClusterResourceRowsKey]
590-
var resourceName string
591593
var namespace string
592594
var allowed bool
593595
if rowsDataUncast != nil {
594596
rows := rowsDataUncast.([]interface{})
595597
for _, row := range rows {
596-
resourceName = ""
597598
namespace = ""
598599
allowed = true
599600
rowIndex := make(map[string]interface{})
@@ -614,11 +615,10 @@ func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.Unstructu
614615
rowIndex[columnName] = cell
615616
}
616617

617-
// set namespace
618-
619618
cellObjUncast := rowMap[K8sClusterResourceObjectKey]
619+
var cellObj map[string]interface{}
620620
if cellObjUncast != nil {
621-
cellObj := cellObjUncast.(map[string]interface{})
621+
cellObj = cellObjUncast.(map[string]interface{})
622622
if cellObj != nil && cellObj[K8sClusterResourceMetadataKey] != nil {
623623
metadata := cellObj[K8sClusterResourceMetadataKey].(map[string]interface{})
624624
if metadata[K8sClusterResourceNamespaceKey] != nil {
@@ -627,14 +627,9 @@ func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.Unstructu
627627
rowIndex[K8sClusterResourceNamespaceKey] = namespace
628628
}
629629
}
630-
if metadata[K8sClusterResourceMetadataNameKey] != nil {
631-
resourceName = metadata[K8sClusterResourceMetadataNameKey].(string)
632-
}
633630
}
634631
}
635-
if resourceName != "" {
636-
allowed = validateResourceAccess(namespace, resourceName)
637-
}
632+
allowed = impl.ValidateResource(cellObj, gvk, validateResourceAccess)
638633
if allowed {
639634
rowsMapping = append(rowsMapping, rowIndex)
640635
}
@@ -647,6 +642,89 @@ func (impl K8sUtil) BuildK8sObjectListTableData(manifest *unstructured.Unstructu
647642
return clusterResourceListMap, nil
648643
}
649644

645+
func (impl K8sUtil) ValidateResource(resourceObj map[string]interface{}, gvk schema.GroupVersionKind, validateCallback func(namespace string, group string, kind string, resourceName string) bool) bool {
646+
resKind := gvk.Kind
647+
groupName := gvk.Group
648+
metadata := resourceObj[K8sClusterResourceMetadataKey]
649+
if metadata == nil {
650+
return false
651+
}
652+
metadataMap := metadata.(map[string]interface{})
653+
var namespace, resourceName string
654+
var ownerReferences []interface{}
655+
if metadataMap[K8sClusterResourceNamespaceKey] != nil {
656+
namespace = metadataMap[K8sClusterResourceNamespaceKey].(string)
657+
}
658+
if metadataMap[K8sClusterResourceMetadataNameKey] != nil {
659+
resourceName = metadataMap[K8sClusterResourceMetadataNameKey].(string)
660+
}
661+
if metadataMap[K8sClusterResourceOwnerReferenceKey] != nil {
662+
ownerReferences = metadataMap[K8sClusterResourceOwnerReferenceKey].([]interface{})
663+
}
664+
if len(ownerReferences) > 0 {
665+
for _, ownerRef := range ownerReferences {
666+
allowed := impl.validateForResource(namespace, ownerRef, validateCallback)
667+
if allowed {
668+
return allowed
669+
}
670+
}
671+
}
672+
// check current RBAC in case not matched with above one
673+
return validateCallback(namespace, groupName, resKind, resourceName)
674+
}
675+
676+
func (impl K8sUtil) validateForResource(namespace string, resourceRef interface{}, validateCallback func(namespace string, group string, kind string, resourceName string) bool) bool {
677+
resourceReference := resourceRef.(map[string]interface{})
678+
resKind := resourceReference[K8sClusterResourceKindKey].(string)
679+
apiVersion := resourceReference[K8sClusterResourceApiVersionKey].(string)
680+
groupName := ""
681+
if strings.Contains(apiVersion, "/") {
682+
groupName = apiVersion[:strings.LastIndex(apiVersion, "/")] // extracting group from this apiVersion
683+
}
684+
resName := ""
685+
if resourceReference["name"] != "" {
686+
resName = resourceReference["name"].(string)
687+
switch resKind {
688+
case kube.ReplicaSetKind:
689+
// check deployment first, then RO and then RS
690+
if strings.Contains(resName, "-") {
691+
deploymentName := resName[:strings.LastIndex(resName, "-")]
692+
allowed := validateCallback(namespace, groupName, kube.DeploymentKind, deploymentName)
693+
if allowed {
694+
return true
695+
}
696+
allowed = validateCallback(namespace, K8sClusterResourceRolloutGroup, K8sClusterResourceRolloutKind, deploymentName)
697+
if allowed {
698+
return true
699+
}
700+
}
701+
allowed := validateCallback(namespace, groupName, resKind, resName)
702+
if allowed {
703+
return true
704+
}
705+
case kube.JobKind:
706+
// check CronJob first, then Job
707+
if strings.Contains(resName, "-") {
708+
cronJobName := resName[:strings.LastIndex(resName, "-")]
709+
allowed := validateCallback(namespace, groupName, K8sClusterResourceCronJobKind, cronJobName)
710+
if allowed {
711+
return true
712+
}
713+
}
714+
allowed := validateCallback(namespace, groupName, resKind, resName)
715+
if allowed {
716+
return true
717+
}
718+
case kube.DeploymentKind, K8sClusterResourceCronJobKind, kube.StatefulSetKind, kube.DaemonSetKind, K8sClusterResourceRolloutKind, K8sClusterResourceReplicationControllerKind:
719+
allowed := validateCallback(namespace, groupName, resKind, resName)
720+
if allowed {
721+
return true
722+
}
723+
}
724+
}
725+
return false
726+
}
727+
650728
func (impl K8sUtil) getEventKindHeader() ([]string, map[int]string) {
651729
headers := []string{"type", "message", "namespace", "involved object", "source", "count", "age", "last seen"}
652730
columnIndexes := make(map[int]string)

0 commit comments

Comments
 (0)