Skip to content

Commit 20be7c8

Browse files
FEATURE: Unique client id for telemetry events (#529)
* telemetry api added for dashboard to get ucid * code refactored * code refacored for client id api for posthog * review changes on posthog client id api for dashboard * cache implemented for configmap value for cient id
1 parent a0022aa commit 20be7c8

File tree

8 files changed

+185
-54
lines changed

8 files changed

+185
-54
lines changed

Wire.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,11 @@ func InitializeApp() (*App, error) {
652652
wire.Bind(new(router.SsoLoginRouter), new(*router.SsoLoginRouterImpl)),
653653
restHandler.NewSsoLoginRestHandlerImpl,
654654
wire.Bind(new(restHandler.SsoLoginRestHandler), new(*restHandler.SsoLoginRestHandlerImpl)),
655+
656+
router.NewTelemetryRouterImpl,
657+
wire.Bind(new(router.TelemetryRouter), new(*router.TelemetryRouterImpl)),
658+
restHandler.NewTelemetryRestHandlerImpl,
659+
wire.Bind(new(restHandler.TelemetryRestHandler), new(*restHandler.TelemetryRestHandlerImpl)),
655660
telemetry.NewPosthogClient,
656661

657662
telemetry.NewTelemetryEventClientImpl,
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2020 Devtron Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package restHandler
19+
20+
import (
21+
"github.com/devtron-labs/devtron/client/telemetry"
22+
"go.uber.org/zap"
23+
"net/http"
24+
)
25+
26+
type TelemetryRestHandler interface {
27+
GetClientPlatformIdAndTelemetryUrl(w http.ResponseWriter, r *http.Request)
28+
}
29+
30+
type TelemetryRestHandlerImpl struct {
31+
logger *zap.SugaredLogger
32+
telemetryEventClient telemetry.TelemetryEventClient
33+
}
34+
35+
func NewTelemetryRestHandlerImpl(logger *zap.SugaredLogger,
36+
telemetryEventClient telemetry.TelemetryEventClient) *TelemetryRestHandlerImpl {
37+
handler := &TelemetryRestHandlerImpl{logger: logger, telemetryEventClient: telemetryEventClient}
38+
return handler
39+
}
40+
41+
func (handler TelemetryRestHandlerImpl) GetClientPlatformIdAndTelemetryUrl(w http.ResponseWriter, r *http.Request) {
42+
res, err := handler.telemetryEventClient.GetClientPlatformIdAndTelemetryUrl()
43+
if err != nil {
44+
handler.logger.Errorw("service err, GetClientPlatformIdAndTelemetryUrl", "err", err)
45+
writeJsonResp(w, err, nil, http.StatusInternalServerError)
46+
return
47+
}
48+
writeJsonResp(w, nil, res, http.StatusOK)
49+
}

api/router/TelemetryRouter.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2020 Devtron Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package router
19+
20+
import (
21+
"github.com/devtron-labs/devtron/api/restHandler"
22+
"github.com/gorilla/mux"
23+
"go.uber.org/zap"
24+
)
25+
26+
type TelemetryRouter interface {
27+
initTelemetryRouter(router *mux.Router)
28+
}
29+
30+
type TelemetryRouterImpl struct {
31+
logger *zap.SugaredLogger
32+
handler restHandler.TelemetryRestHandler
33+
}
34+
35+
func NewTelemetryRouterImpl(logger *zap.SugaredLogger, handler restHandler.TelemetryRestHandler) *TelemetryRouterImpl {
36+
router := &TelemetryRouterImpl{
37+
handler: handler,
38+
}
39+
return router
40+
}
41+
42+
func (router TelemetryRouterImpl) initTelemetryRouter(telemetryRouter *mux.Router) {
43+
telemetryRouter.Path("/ucid").
44+
HandlerFunc(router.handler.GetClientPlatformIdAndTelemetryUrl).Methods("GET")
45+
}

api/router/router.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ type MuxRouter struct {
7373
commonRouter CommonRouter
7474
grafanaRouter GrafanaRouter
7575
ssoLoginRouter SsoLoginRouter
76+
telemetryRouter TelemetryRouter
7677
telemetryWatcher telemetry.TelemetryEventClient
7778
}
7879

@@ -92,7 +93,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
9293
ReleaseMetricsRouter ReleaseMetricsRouter, deploymentGroupRouter DeploymentGroupRouter, batchOperationRouter BatchOperationRouter,
9394
chartGroupRouter ChartGroupRouter, testSuitRouter TestSuitRouter, imageScanRouter ImageScanRouter,
9495
policyRouter PolicyRouter, gitOpsConfigRouter GitOpsConfigRouter, dashboardRouter DashboardRouter, attributesRouter AttributesRouter,
95-
commonRouter CommonRouter, grafanaRouter GrafanaRouter, ssoLoginRouter SsoLoginRouter, telemetryWatcher telemetry.TelemetryEventClient) *MuxRouter {
96+
commonRouter CommonRouter, grafanaRouter GrafanaRouter, ssoLoginRouter SsoLoginRouter, telemetryRouter TelemetryRouter, telemetryWatcher telemetry.TelemetryEventClient) *MuxRouter {
9697
r := &MuxRouter{
9798
Router: mux.NewRouter(),
9899
HelmRouter: HelmRouter,
@@ -136,6 +137,7 @@ func NewMuxRouter(logger *zap.SugaredLogger, HelmRouter HelmRouter, PipelineConf
136137
commonRouter: commonRouter,
137138
grafanaRouter: grafanaRouter,
138139
ssoLoginRouter: ssoLoginRouter,
140+
telemetryRouter: telemetryRouter,
139141
telemetryWatcher: telemetryWatcher,
140142
}
141143
return r
@@ -259,4 +261,7 @@ func (r MuxRouter) Init() {
259261

260262
ssoLoginRouter := r.Router.PathPrefix("/orchestrator/sso").Subrouter()
261263
r.ssoLoginRouter.initSsoLoginRouter(ssoLoginRouter)
264+
265+
telemetryRouter := r.Router.PathPrefix("/orchestrator/telemetry").Subrouter()
266+
r.telemetryRouter.initTelemetryRouter(telemetryRouter)
262267
}

client/telemetry/PosthogClient.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,23 @@ package telemetry
1919

2020
import (
2121
"github.com/caarlos0/env"
22+
"github.com/patrickmn/go-cache"
2223
"github.com/posthog/posthog-go"
2324
"go.uber.org/zap"
25+
"time"
2426
)
2527

2628
type PosthogClient struct {
2729
Client posthog.Client
30+
cache *cache.Cache
2831
}
2932

3033
type PosthogConfig struct {
3134
ApiKey string `env:"API_KEY" envDefault:""`
3235
PosthogEndpoint string `env:"POSTHOG_ENDPOINT" envDefault:"https://app.posthog.com"`
3336
SummaryInterval int `env:"SUMMARY_INTERVAL" envDefault:"24"`
3437
HeartbeatInterval int `env:"HEARTBEAT_INTERVAL" envDefault:"3"`
38+
CacheExpiry int `env:"CACHE_EXPIRY" envDefault:"120"`
3539
}
3640

3741
func GetPosthogConfig() (*PosthogConfig, error) {
@@ -52,8 +56,11 @@ func NewPosthogClient(logger *zap.SugaredLogger) (*PosthogClient, error) {
5256
}
5357
client, _ := posthog.NewWithConfig(cfg.ApiKey, posthog.Config{Endpoint: cfg.PosthogEndpoint})
5458
//defer client.Close()
59+
d := time.Duration(cfg.CacheExpiry)
60+
c := cache.New(d*time.Minute, 240*time.Minute)
5561
pgClient := &PosthogClient{
5662
Client: client,
63+
cache: c,
5764
}
5865
return pgClient, nil
5966
}

client/telemetry/TelemetryEventClient.go

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package telemetry
33
import (
44
"encoding/json"
55
"fmt"
6-
client "github.com/devtron-labs/devtron/client/events"
76
"github.com/devtron-labs/devtron/internal/sql/repository"
87
"github.com/devtron-labs/devtron/internal/sql/repository/pipelineConfig"
98
util2 "github.com/devtron-labs/devtron/internal/util"
109
"github.com/devtron-labs/devtron/pkg/cluster"
1110
"github.com/devtron-labs/devtron/pkg/user"
1211
"github.com/devtron-labs/devtron/util"
1312
"github.com/go-pg/pg"
13+
"github.com/patrickmn/go-cache"
1414
"github.com/posthog/posthog-go"
1515
"go.uber.org/zap"
1616
"google.golang.org/grpc/codes"
@@ -29,7 +29,6 @@ type TelemetryEventClientImpl struct {
2929
clusterService cluster.ClusterService
3030
K8sUtil *util2.K8sUtil
3131
aCDAuthConfig *user.ACDAuthConfig
32-
config *client.EventClientConfig
3332
environmentService cluster.EnvironmentService
3433
userService user.UserService
3534
appListingRepository repository.AppListingRepository
@@ -40,10 +39,11 @@ type TelemetryEventClientImpl struct {
4039
}
4140

4241
type TelemetryEventClient interface {
42+
GetClientPlatformIdAndTelemetryUrl() (*PosthogData, error)
4343
}
4444

4545
func NewTelemetryEventClientImpl(logger *zap.SugaredLogger, client *http.Client, clusterService cluster.ClusterService,
46-
K8sUtil *util2.K8sUtil, aCDAuthConfig *user.ACDAuthConfig, config *client.EventClientConfig,
46+
K8sUtil *util2.K8sUtil, aCDAuthConfig *user.ACDAuthConfig,
4747
environmentService cluster.EnvironmentService, userService user.UserService,
4848
appListingRepository repository.AppListingRepository, PosthogClient *PosthogClient,
4949
ciPipelineRepository pipelineConfig.CiPipelineRepository, pipelineRepository pipelineConfig.PipelineRepository,
@@ -55,12 +55,13 @@ func NewTelemetryEventClientImpl(logger *zap.SugaredLogger, client *http.Client,
5555
cron: cron,
5656
logger: logger,
5757
client: client, clusterService: clusterService,
58-
K8sUtil: K8sUtil, aCDAuthConfig: aCDAuthConfig, config: config,
58+
K8sUtil: K8sUtil, aCDAuthConfig: aCDAuthConfig,
5959
environmentService: environmentService, userService: userService,
6060
appListingRepository: appListingRepository, PosthogClient: PosthogClient,
6161
ciPipelineRepository: ciPipelineRepository, pipelineRepository: pipelineRepository,
6262
posthogConfig: posthogConfig,
6363
}
64+
6465
watcher.HeartbeatEventForTelemetry()
6566
_, err := cron.AddFunc(fmt.Sprintf("@every %dm", watcher.posthogConfig.SummaryInterval), watcher.SummaryEventForTelemetry)
6667
if err != nil {
@@ -122,30 +123,11 @@ func (d TelemetryEventType) String() string {
122123
}
123124

124125
func (impl *TelemetryEventClientImpl) SummaryEventForTelemetry() {
125-
client, err := impl.K8sUtil.GetClientForInCluster()
126+
ucid, err := impl.getUCID()
126127
if err != nil {
127128
impl.logger.Errorw("exception caught inside telemetry summary event", "err", err)
128129
return
129130
}
130-
cm, err := impl.K8sUtil.GetConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, DevtronUniqueClientIdConfigMap, client)
131-
if errStatus, ok := status.FromError(err); !ok || errStatus.Code() == codes.NotFound || errStatus.Code() == codes.Unknown {
132-
// if not found, create new cm
133-
cm = &v1.ConfigMap{ObjectMeta: v12.ObjectMeta{Name: DevtronUniqueClientIdConfigMap}}
134-
data := map[string]string{}
135-
data[DevtronUniqueClientIdConfigMapKey] = util.Generate(16) // generate unique random number
136-
cm.Data = data
137-
_, err = impl.K8sUtil.CreateConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, cm, client)
138-
if err != nil {
139-
return
140-
}
141-
}
142-
if cm == nil {
143-
impl.logger.Errorw("cm found nil inside telemetry summary event", "cm", cm)
144-
return
145-
}
146-
dataMap := cm.Data
147-
ucid := dataMap[DevtronUniqueClientIdConfigMapKey]
148-
149131
discoveryClient, err := impl.K8sUtil.GetK8sDiscoveryClientInCluster()
150132
if err != nil {
151133
impl.logger.Errorw("exception caught inside telemetry summary event", "err", err)
@@ -229,31 +211,11 @@ func (impl *TelemetryEventClientImpl) SummaryEventForTelemetry() {
229211
}
230212

231213
func (impl *TelemetryEventClientImpl) HeartbeatEventForTelemetry() {
232-
client, err := impl.K8sUtil.GetClientForInCluster()
214+
ucid, err := impl.getUCID()
233215
if err != nil {
234216
impl.logger.Errorw("exception caught inside telemetry heartbeat event", "err", err)
235217
return
236218
}
237-
cm, err := impl.K8sUtil.GetConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, DevtronUniqueClientIdConfigMap, client)
238-
if errStatus, ok := status.FromError(err); !ok || errStatus.Code() == codes.NotFound || errStatus.Code() == codes.Unknown {
239-
// if not found, create new cm
240-
cm = &v1.ConfigMap{ObjectMeta: v12.ObjectMeta{Name: DevtronUniqueClientIdConfigMap}}
241-
data := map[string]string{}
242-
data[DevtronUniqueClientIdConfigMapKey] = util.Generate(16) // generate unique random number
243-
cm.Data = data
244-
_, err = impl.K8sUtil.CreateConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, cm, client)
245-
if err != nil {
246-
impl.logger.Errorw("exception caught inside telemetry heartbeat event", "err", err)
247-
return
248-
}
249-
}
250-
if cm == nil {
251-
impl.logger.Errorw("configmap found nil for telemetry heartbeat event", "cm", cm)
252-
return
253-
}
254-
dataMap := cm.Data
255-
ucid := dataMap[DevtronUniqueClientIdConfigMapKey]
256-
257219
discoveryClient, err := impl.K8sUtil.GetK8sDiscoveryClientInCluster()
258220
if err != nil {
259221
impl.logger.Errorw("exception caught inside telemetry heartbeat event", "err", err)
@@ -283,3 +245,56 @@ func (impl *TelemetryEventClientImpl) HeartbeatEventForTelemetry() {
283245
Properties: prop,
284246
})
285247
}
248+
249+
func (impl *TelemetryEventClientImpl) GetClientPlatformIdAndTelemetryUrl() (*PosthogData, error) {
250+
ucid, err := impl.getUCID()
251+
if err != nil {
252+
impl.logger.Errorw("exception while getting unique client id", "error", err)
253+
return nil, err
254+
}
255+
data := &PosthogData{
256+
Url: impl.posthogConfig.PosthogEndpoint,
257+
UCID: ucid,
258+
}
259+
return data, err
260+
}
261+
262+
type PosthogData struct {
263+
Url string `json:"url,omitempty"`
264+
UCID string `json:"ucid,omitempty"`
265+
}
266+
267+
func (impl *TelemetryEventClientImpl) getUCID() (string, error) {
268+
ucid, found := impl.PosthogClient.cache.Get(DevtronUniqueClientIdConfigMapKey)
269+
if found {
270+
return ucid.(string), nil
271+
} else {
272+
client, err := impl.K8sUtil.GetClientForInCluster()
273+
if err != nil {
274+
impl.logger.Errorw("exception while getting unique client id", "error", err)
275+
return "", err
276+
}
277+
278+
cm, err := impl.K8sUtil.GetConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, DevtronUniqueClientIdConfigMap, client)
279+
if errStatus, ok := status.FromError(err); !ok || errStatus.Code() == codes.NotFound || errStatus.Code() == codes.Unknown {
280+
// if not found, create new cm
281+
cm = &v1.ConfigMap{ObjectMeta: v12.ObjectMeta{Name: DevtronUniqueClientIdConfigMap}}
282+
data := map[string]string{}
283+
data[DevtronUniqueClientIdConfigMapKey] = util.Generate(16) // generate unique random number
284+
cm.Data = data
285+
_, err = impl.K8sUtil.CreateConfigMap(impl.aCDAuthConfig.ACDConfigMapNamespace, cm, client)
286+
if err != nil {
287+
impl.logger.Errorw("exception while getting unique client id", "error", err)
288+
return "", err
289+
}
290+
}
291+
dataMap := cm.Data
292+
ucid = dataMap[DevtronUniqueClientIdConfigMapKey]
293+
impl.PosthogClient.cache.Set(DevtronUniqueClientIdConfigMapKey, ucid, cache.DefaultExpiration)
294+
if cm == nil {
295+
impl.logger.Errorw("configmap not found while getting unique client id", "cm", cm)
296+
return ucid.(string), err
297+
}
298+
}
299+
return ucid.(string), nil
300+
}

pkg/sso/SSOLoginService.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/argoproj/argo-cd/util/session"
2424
"github.com/devtron-labs/devtron/api/bean"
2525
session2 "github.com/devtron-labs/devtron/client/argocdServer/session"
26+
"github.com/devtron-labs/devtron/client/telemetry"
2627
"github.com/devtron-labs/devtron/internal/sql/repository"
2728
"github.com/devtron-labs/devtron/internal/util"
2829
"github.com/devtron-labs/devtron/pkg/cluster"
@@ -53,13 +54,14 @@ type SSOLoginServiceImpl struct {
5354
clusterService cluster.ClusterService
5455
envService cluster.EnvironmentService
5556
aCDAuthConfig *user.ACDAuthConfig
57+
posthogConfig *telemetry.PosthogConfig
5658
}
5759

5860
func NewSSOLoginServiceImpl(userAuthRepository repository.UserAuthRepository, sessionManager *session.SessionManager,
5961
client session2.ServiceClient, logger *zap.SugaredLogger, userRepository repository.UserRepository,
6062
userGroupRepository repository.RoleGroupRepository, ssoLoginRepository repository.SSOLoginRepository,
6163
K8sUtil *util.K8sUtil, clusterService cluster.ClusterService, envService cluster.EnvironmentService,
62-
aCDAuthConfig *user.ACDAuthConfig) *SSOLoginServiceImpl {
64+
aCDAuthConfig *user.ACDAuthConfig, posthogConfig *telemetry.PosthogConfig) *SSOLoginServiceImpl {
6365
serviceImpl := &SSOLoginServiceImpl{
6466
userAuthRepository: userAuthRepository,
6567
sessionManager: sessionManager,
@@ -72,6 +74,7 @@ func NewSSOLoginServiceImpl(userAuthRepository repository.UserAuthRepository, se
7274
clusterService: clusterService,
7375
envService: envService,
7476
aCDAuthConfig: aCDAuthConfig,
77+
posthogConfig: posthogConfig,
7578
}
7679
return serviceImpl
7780
}

0 commit comments

Comments
 (0)