Skip to content

Commit

Permalink
feat: [TKC-1745] workflow telemetry main (#5217)
Browse files Browse the repository at this point in the history
* feat: implement basic workflow telemetry (#5209)

* feat: add more sophisticated telemetry for workflows (#5211)
  • Loading branch information
vLia authored Mar 20, 2024
1 parent b40b449 commit 7213889
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 22 deletions.
1 change: 1 addition & 0 deletions cmd/api-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ func main() {
testWorkflowResultsRepository,
testWorkflowOutputRepository,
"http://"+cfg.APIServerFullname+":"+cfg.APIServerPort,
configMapConfig,
)
apiPro.AppendRoutes()

Expand Down
4 changes: 4 additions & 0 deletions pkg/tcl/apitcl/v1/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/kubeshop/testkube/internal/config"
"github.com/kubeshop/testkube/pkg/api/v1/testkube"
"github.com/kubeshop/testkube/pkg/imageinspector"
configRepo "github.com/kubeshop/testkube/pkg/repository/config"
"github.com/kubeshop/testkube/pkg/tcl/repositorytcl/testworkflow"
"github.com/kubeshop/testkube/pkg/tcl/testworkflowstcl/testworkflowexecutor"
)
Expand All @@ -36,6 +37,7 @@ type apiTCL struct {
TestWorkflowTemplatesClient testworkflowsv1.TestWorkflowTemplatesInterface
TestWorkflowExecutor testworkflowexecutor.TestWorkflowExecutor
ApiUrl string
configMap configRepo.Repository
}

type ApiTCL interface {
Expand All @@ -51,6 +53,7 @@ func NewApiTCL(
testWorkflowResults testworkflow.Repository,
testWorkflowOutput testworkflow.OutputRepository,
apiUrl string,
configMap configRepo.Repository,
) ApiTCL {
executor := testworkflowexecutor.New(testkubeAPI.Events, testkubeAPI.Clientset, testWorkflowResults, testWorkflowOutput, testkubeAPI.Namespace)
go executor.Recover(context.Background())
Expand All @@ -64,6 +67,7 @@ func NewApiTCL(
TestWorkflowTemplatesClient: testworkflowsv1.NewTestWorkflowTemplatesClient(kubeClient, testkubeAPI.Namespace),
TestWorkflowExecutor: executor,
ApiUrl: apiUrl,
configMap: configMap,
}
}

Expand Down
226 changes: 226 additions & 0 deletions pkg/tcl/apitcl/v1/testworkflowmetrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package v1

import (
"context"
"os"
"strings"

testworkflowsv1 "github.com/kubeshop/testkube-operator/api/testworkflows/v1"
"github.com/kubeshop/testkube/pkg/log"
"github.com/kubeshop/testkube/pkg/telemetry"
"github.com/kubeshop/testkube/pkg/version"
)

func (s *apiTCL) sendCreateWorkflowTelemetry(ctx context.Context, workflow *testworkflowsv1.TestWorkflow) {
if workflow == nil {
log.DefaultLogger.Debug("empty workflow passed to telemetry event")
return
}
telemetryEnabled, err := s.configMap.GetTelemetryEnabled(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting telemetry enabled error", "error", err)
}
if !telemetryEnabled {
return
}

clusterID, err := s.configMap.GetUniqueClusterId(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting cluster id error", "error", err)
}

host, err := os.Hostname()
if err != nil {
log.DefaultLogger.Debugf("getting hostname error", "hostname", host, "error", err)
}

var dataSource string
if len(workflow.Spec.Content.Files) != 0 {
dataSource = "files"
} else if workflow.Spec.Content.Git != nil {
dataSource = "git"
}

hasArtifacts := false
for _, step := range workflow.Spec.Steps {
if step.Artifacts != nil {
hasArtifacts = true
break
}
}

image := ""
if workflow.Spec.Container != nil {
image = workflow.Spec.Container.Image
}

isKubeshopGitURI := false
if workflow.Spec.Content != nil && workflow.Spec.Content.Git != nil {
if strings.Contains(workflow.Spec.Content.Git.Uri, "kubeshop") {
isKubeshopGitURI = true
}
}

out, err := telemetry.SendCreateWorkflowEvent("testkube_api_create_test_workflow", telemetry.CreateWorkflowParams{
CreateParams: telemetry.CreateParams{
AppVersion: version.Version,
DataSource: dataSource,
Host: host,
ClusterID: clusterID,
},
WorkflowParams: telemetry.WorkflowParams{
TestWorkflowSteps: int32(len(workflow.Spec.Steps)),
TestWorkflowTemplateUsed: len(workflow.Spec.Use) != 0,
TestWorkflowImage: image,
TestWorkflowArtifactUsed: hasArtifacts,
TestWorkflowKubeshopGitURI: isKubeshopGitURI,
},
})
if err != nil {
log.DefaultLogger.Debugf("sending create test workflow telemetry event error", "error", err)
} else {
log.DefaultLogger.Debugf("sending create test workflow telemetry event", "output", out)
}
}

func (s *apiTCL) sendCreateWorkflowTemplateTelemetry(ctx context.Context, template *testworkflowsv1.TestWorkflowTemplate) {
if template == nil {
log.DefaultLogger.Debug("empty template passed to telemetry event")
return
}
telemetryEnabled, err := s.configMap.GetTelemetryEnabled(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting telemetry enabled error", "error", err)
}
if !telemetryEnabled {
return
}

clusterID, err := s.configMap.GetUniqueClusterId(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting cluster id error", "error", err)
}

host, err := os.Hostname()
if err != nil {
log.DefaultLogger.Debugf("getting hostname error", "hostname", host, "error", err)
}

var dataSource string
if template.Spec.Content != nil && len(template.Spec.Content.Files) != 0 {
dataSource = "files"
} else if template.Spec.Content.Git != nil {
dataSource = "git"
}

hasArtifacts := false
for _, step := range template.Spec.Steps {
if step.Artifacts != nil {
hasArtifacts = true
break
}
}

image := ""
if template.Spec.Container != nil {
image = template.Spec.Container.Image
}

isKubeshopGitURI := false
if template.Spec.Content != nil && template.Spec.Content.Git != nil {
if strings.Contains(template.Spec.Content.Git.Uri, "kubeshop") {
isKubeshopGitURI = true
}
}

out, err := telemetry.SendCreateWorkflowEvent("testkube_api_create_test_workflow_template", telemetry.CreateWorkflowParams{
CreateParams: telemetry.CreateParams{
AppVersion: version.Version,
DataSource: dataSource,
Host: host,
ClusterID: clusterID,
},
WorkflowParams: telemetry.WorkflowParams{
TestWorkflowSteps: int32(len(template.Spec.Steps)),
TestWorkflowImage: image,
TestWorkflowArtifactUsed: hasArtifacts,
TestWorkflowKubeshopGitURI: isKubeshopGitURI,
},
})
if err != nil {
log.DefaultLogger.Debugf("sending create test workflow template telemetry event error", "error", err)
} else {
log.DefaultLogger.Debugf("sending create test workflow template telemetry event", "output", out)
}
}

func (s *apiTCL) sendRunWorkflowTelemetry(ctx context.Context, workflow *testworkflowsv1.TestWorkflow) {
if workflow == nil {
log.DefaultLogger.Debug("empty workflow passed to telemetry event")
return
}
telemetryEnabled, err := s.configMap.GetTelemetryEnabled(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting telemetry enabled error", "error", err)
}
if !telemetryEnabled {
return
}

clusterID, err := s.configMap.GetUniqueClusterId(ctx)
if err != nil {
log.DefaultLogger.Debugf("getting cluster id error", "error", err)
}

host, err := os.Hostname()
if err != nil {
log.DefaultLogger.Debugf("getting hostname error", "hostname", host, "error", err)
}

var dataSource string
if len(workflow.Spec.Content.Files) != 0 {
dataSource = "files"
} else if workflow.Spec.Content.Git != nil {
dataSource = "git"
}

hasArtifacts := false
for _, step := range workflow.Spec.Steps {
if step.Artifacts != nil {
hasArtifacts = true
break
}
}

image := ""
if workflow.Spec.Container != nil {
image = workflow.Spec.Container.Image
}
isKubeshopGitURI := false
if workflow.Spec.Content != nil && workflow.Spec.Content.Git != nil {
if strings.Contains(workflow.Spec.Content.Git.Uri, "kubeshop") {
isKubeshopGitURI = true
}
}

out, err := telemetry.SendRunWorkflowEvent("testkube_api_run_test_workflow", telemetry.RunWorkflowParams{
RunParams: telemetry.RunParams{
AppVersion: version.Version,
DataSource: dataSource,
Host: host,
ClusterID: clusterID,
},
WorkflowParams: telemetry.WorkflowParams{
TestWorkflowSteps: int32(len(workflow.Spec.Steps)),
TestWorkflowImage: image,
TestWorkflowArtifactUsed: hasArtifacts,
TestWorkflowKubeshopGitURI: isKubeshopGitURI,
},
})

if err != nil {
log.DefaultLogger.Debugf("sending run test workflow telemetry event error", "error", err)
} else {
log.DefaultLogger.Debugf("sending run test workflow telemetry event", "output", out)
}
}
2 changes: 2 additions & 0 deletions pkg/tcl/apitcl/v1/testworkflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func (s *apiTCL) CreateTestWorkflowHandler() fiber.Handler {
if err != nil {
return s.BadRequest(c, errPrefix, "client error", err)
}
s.sendCreateWorkflowTelemetry(c.Context(), obj)

err = SendResource(c, "TestWorkflow", testworkflowsv1.GroupVersion, testworkflowmappers.MapKubeToAPI, obj)
if err != nil {
Expand Down Expand Up @@ -396,6 +397,7 @@ func (s *apiTCL) ExecuteTestWorkflowHandler() fiber.Handler {

// Schedule the execution
s.TestWorkflowExecutor.Schedule(bundle, execution)
s.sendRunWorkflowTelemetry(c.Context(), workflow)

return c.JSON(execution)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/tcl/apitcl/v1/testworkflowtemplates.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func (s *apiTCL) CreateTestWorkflowTemplateHandler() fiber.Handler {
if err != nil {
return s.BadRequest(c, errPrefix, "client error", err)
}
s.sendCreateWorkflowTemplateTelemetry(c.Context(), obj)

err = SendResource(c, "TestWorkflowTemplate", testworkflowsv1.GroupVersion, mappers2.MapTemplateKubeToAPI, obj)
if err != nil {
Expand Down
Loading

0 comments on commit 7213889

Please sign in to comment.