From b467d0fa36f9720038bf5714d7ce1409a375d2a8 Mon Sep 17 00:00:00 2001
From: Brian Ginsburg <7957636+bgins@users.noreply.github.com>
Date: Wed, 23 Oct 2024 12:01:18 -0700
Subject: [PATCH] feat: Add job creator tracer (#411)

* chore: Add telemetry options

* feat: Add onchain job-creator tracer

* feat: Add run job-creator tracer

* chore: Add integration test noop tracer

* chore: Remove comment

We can read the yacspin docs: https://github.com/theckman/yacspin

* chore: Disable telemetry in CI

* fix: Process onchain telemetry options
---
 .github/workflows/test.yml           |  3 ++-
 cmd/lilypad/jobcreator.go            | 18 ++++++++++++------
 cmd/lilypad/run.go                   | 27 ++++++++++++---------------
 pkg/jobcreator/controller.go         |  4 ++++
 pkg/jobcreator/jobcreator.go         |  5 ++++-
 pkg/jobcreator/onchain_jobcreator.go |  4 +++-
 pkg/jobcreator/run.go                |  8 ++++----
 pkg/options/job-creator.go           | 22 ++++++++++++++++++++++
 test/integration_test.go             |  3 ++-
 9 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 9515526f..cf0d8446 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -21,7 +21,7 @@ jobs:
 
       - name: Run unit tests
         run: ./stack unit-tests
-        
+
   run-integration-tests:
     runs-on: ubuntu-latest
     steps:
@@ -50,6 +50,7 @@ jobs:
       - name: Run tests
         env:
           LOG_LEVEL: debug
+          DISABLE_TELEMETRY: true
         run: ./stack integration-tests
 
       - name: Display resource provider logs
diff --git a/cmd/lilypad/jobcreator.go b/cmd/lilypad/jobcreator.go
index 1affab81..ee5a92a0 100644
--- a/cmd/lilypad/jobcreator.go
+++ b/cmd/lilypad/jobcreator.go
@@ -5,8 +5,8 @@ import (
 	optionsfactory "github.com/lilypad-tech/lilypad/pkg/options"
 	"github.com/lilypad-tech/lilypad/pkg/system"
 	"github.com/lilypad-tech/lilypad/pkg/web3"
+	"github.com/rs/zerolog/log"
 	"github.com/spf13/cobra"
-	"go.opentelemetry.io/otel/trace/noop"
 )
 
 func newJobCreatorCmd() *cobra.Command {
@@ -24,7 +24,7 @@ func newJobCreatorCmd() *cobra.Command {
 			if err != nil {
 				return err
 			}
-			return runJobCreator(cmd, options)
+			return runJobCreator(cmd, options, network)
 		},
 	}
 
@@ -33,18 +33,24 @@ func newJobCreatorCmd() *cobra.Command {
 	return solverCmd
 }
 
-func runJobCreator(cmd *cobra.Command, options jobcreator.JobCreatorOptions) error {
+func runJobCreator(cmd *cobra.Command, options jobcreator.JobCreatorOptions, network string) error {
 	commandCtx := system.NewCommandContext(cmd)
 	defer commandCtx.Cleanup()
 
-	noopTracer := noop.NewTracerProvider().Tracer(system.GetOTelServiceName(system.JobCreatorService))
-	web3SDK, err := web3.NewContractSDK(commandCtx.Ctx, options.Web3, noopTracer)
+	telemetry, err := configureTelemetry(commandCtx.Ctx, system.JobCreatorService, network, options.Telemetry, options.Web3)
+	if err != nil {
+		log.Warn().Msgf("failed to setup opentelemetry: %s", err)
+	}
+	commandCtx.Cm.RegisterCallbackWithContext(telemetry.Shutdown)
+	tracer := telemetry.TracerProvider.Tracer(system.GetOTelServiceName(system.JobCreatorService))
+
+	web3SDK, err := web3.NewContractSDK(commandCtx.Ctx, options.Web3, tracer)
 	if err != nil {
 		return err
 	}
 
 	// create the job creator and start it's control loop
-	jobCreatorService, err := jobcreator.NewOnChainJobCreator(options, web3SDK)
+	jobCreatorService, err := jobcreator.NewOnChainJobCreator(options, web3SDK, tracer)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/lilypad/run.go b/cmd/lilypad/run.go
index 9e9e62a4..2cc3cd2b 100644
--- a/cmd/lilypad/run.go
+++ b/cmd/lilypad/run.go
@@ -14,6 +14,7 @@ import (
 	optionsfactory "github.com/lilypad-tech/lilypad/pkg/options"
 	"github.com/lilypad-tech/lilypad/pkg/solver"
 	"github.com/lilypad-tech/lilypad/pkg/system"
+	"github.com/rs/zerolog/log"
 	"github.com/spf13/cobra"
 
 	"github.com/theckman/yacspin"
@@ -33,7 +34,7 @@ func newRunCmd() *cobra.Command {
 			if err != nil {
 				return err
 			}
-			return runJob(cmd, options)
+			return runJob(cmd, options, network)
 		},
 	}
 
@@ -42,7 +43,7 @@ func newRunCmd() *cobra.Command {
 	return runCmd
 }
 
-func runJob(cmd *cobra.Command, options jobcreator.JobCreatorOptions) error {
+func runJob(cmd *cobra.Command, options jobcreator.JobCreatorOptions, network string) error {
 	c := color.New(color.FgCyan).Add(color.Bold)
 	header := `
 ⠀⠀⠀⠀⠀⠀⣀⣤⣤⢠⣤⣀⠀⠀⠀⠀⠀
@@ -72,18 +73,6 @@ func runJob(cmd *cobra.Command, options jobcreator.JobCreatorOptions) error {
 		return fmt.Errorf("failed to start spinner: %w", err)
 	}
 
-	// update message
-	// spinner.Message("uploading files")
-
-	// let spinner render some more
-	// time.Sleep(1 * time.Second)
-
-	// if you wanted to print a failure message...
-	//
-	// if err := spinner.StopFail(); err != nil {
-	// 	return fmt.Errorf("failed to stop spinner: %w", err)
-	// }
-
 	if err := spinner.Stop(); err != nil {
 		return fmt.Errorf("failed to stop spinner: %w", err)
 	}
@@ -95,7 +84,15 @@ func runJob(cmd *cobra.Command, options jobcreator.JobCreatorOptions) error {
 
 	commandCtx := system.NewCommandContext(cmd)
 	defer commandCtx.Cleanup()
-	result, err := jobcreator.RunJob(commandCtx, options, func(evOffer data.JobOfferContainer) {
+
+	telemetry, err := configureTelemetry(commandCtx.Ctx, system.JobCreatorService, network, options.Telemetry, options.Web3)
+	if err != nil {
+		log.Warn().Msgf("failed to setup opentelemetry: %s", err)
+	}
+	commandCtx.Cm.RegisterCallbackWithContext(telemetry.Shutdown)
+	tracer := telemetry.TracerProvider.Tracer(system.GetOTelServiceName(system.JobCreatorService))
+
+	result, err := jobcreator.RunJob(commandCtx, options, tracer, func(evOffer data.JobOfferContainer) {
 		spinner.Stop()
 		st := data.GetAgreementStateString(evOffer.State)
 		var desc string
diff --git a/pkg/jobcreator/controller.go b/pkg/jobcreator/controller.go
index c711524a..8cb3e846 100644
--- a/pkg/jobcreator/controller.go
+++ b/pkg/jobcreator/controller.go
@@ -13,6 +13,7 @@ import (
 	"github.com/lilypad-tech/lilypad/pkg/system"
 	"github.com/lilypad-tech/lilypad/pkg/web3"
 	"github.com/lilypad-tech/lilypad/pkg/web3/bindings/storage"
+	"go.opentelemetry.io/otel/trace"
 )
 
 type JobOfferSubscriber func(offer data.JobOfferContainer)
@@ -25,6 +26,7 @@ type JobCreatorController struct {
 	loop                  *system.ControlLoop
 	log                   *system.ServiceLogger
 	jobOfferSubscriptions []JobOfferSubscriber
+	tracer                trace.Tracer
 }
 
 // the background "even if we have not heard of an event" loop
@@ -36,6 +38,7 @@ const CONTROL_LOOP_INTERVAL = 10 * time.Second
 func NewJobCreatorController(
 	options JobCreatorOptions,
 	web3SDK *web3.Web3SDK,
+	tracer trace.Tracer,
 ) (*JobCreatorController, error) {
 	// we know the address of the solver but what is it's url?
 	solverUrl, err := web3SDK.GetSolverUrl(options.Offer.Services.Solver)
@@ -63,6 +66,7 @@ func NewJobCreatorController(
 		web3Events:            web3.NewEventChannels(),
 		log:                   system.NewServiceLogger(system.JobCreatorService),
 		jobOfferSubscriptions: []JobOfferSubscriber{},
+		tracer:                tracer,
 	}
 	return controller, nil
 }
diff --git a/pkg/jobcreator/jobcreator.go b/pkg/jobcreator/jobcreator.go
index 984af3f8..94354880 100644
--- a/pkg/jobcreator/jobcreator.go
+++ b/pkg/jobcreator/jobcreator.go
@@ -6,6 +6,7 @@ import (
 	"github.com/lilypad-tech/lilypad/pkg/data"
 	"github.com/lilypad-tech/lilypad/pkg/system"
 	"github.com/lilypad-tech/lilypad/pkg/web3"
+	"go.opentelemetry.io/otel/trace"
 )
 
 type JobCreatorMediationOptions struct {
@@ -39,6 +40,7 @@ type JobCreatorOptions struct {
 	Mediation JobCreatorMediationOptions
 	Offer     JobCreatorOfferOptions
 	Web3      web3.Web3Options
+	Telemetry system.TelemetryOptions
 }
 
 type JobCreator struct {
@@ -50,8 +52,9 @@ type JobCreator struct {
 func NewJobCreator(
 	options JobCreatorOptions,
 	web3SDK *web3.Web3SDK,
+	tracer trace.Tracer,
 ) (*JobCreator, error) {
-	controller, err := NewJobCreatorController(options, web3SDK)
+	controller, err := NewJobCreatorController(options, web3SDK, tracer)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/jobcreator/onchain_jobcreator.go b/pkg/jobcreator/onchain_jobcreator.go
index fd930bae..6b3c49c5 100644
--- a/pkg/jobcreator/onchain_jobcreator.go
+++ b/pkg/jobcreator/onchain_jobcreator.go
@@ -11,6 +11,7 @@ import (
 	"github.com/lilypad-tech/lilypad/pkg/system"
 	"github.com/lilypad-tech/lilypad/pkg/web3"
 	jobcreatorweb3 "github.com/lilypad-tech/lilypad/pkg/web3/bindings/jobcreator"
+	"go.opentelemetry.io/otel/trace"
 )
 
 const JOB_PRICE = 2
@@ -26,8 +27,9 @@ type OnChainJobCreator struct {
 func NewOnChainJobCreator(
 	options JobCreatorOptions,
 	web3SDK *web3.Web3SDK,
+	tracer trace.Tracer,
 ) (*OnChainJobCreator, error) {
-	controller, err := NewJobCreatorController(options, web3SDK)
+	controller, err := NewJobCreatorController(options, web3SDK, tracer)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/jobcreator/run.go b/pkg/jobcreator/run.go
index 3e2920fe..db762d57 100644
--- a/pkg/jobcreator/run.go
+++ b/pkg/jobcreator/run.go
@@ -7,7 +7,7 @@ import (
 	"github.com/lilypad-tech/lilypad/pkg/data"
 	"github.com/lilypad-tech/lilypad/pkg/system"
 	"github.com/lilypad-tech/lilypad/pkg/web3"
-	"go.opentelemetry.io/otel/trace/noop"
+	"go.opentelemetry.io/otel/trace"
 )
 
 type RunJobResults struct {
@@ -18,16 +18,16 @@ type RunJobResults struct {
 func RunJob(
 	ctx *system.CommandContext,
 	options JobCreatorOptions,
+	tracer trace.Tracer,
 	eventSub JobOfferSubscriber,
 ) (*RunJobResults, error) {
-	noopTracer := noop.NewTracerProvider().Tracer(system.GetOTelServiceName(system.JobCreatorService))
-	web3SDK, err := web3.NewContractSDK(ctx.Ctx, options.Web3, noopTracer)
+	web3SDK, err := web3.NewContractSDK(ctx.Ctx, options.Web3, tracer)
 	if err != nil {
 		return nil, err
 	}
 
 	// create the job creator and start it's control loop
-	jobCreatorService, err := NewJobCreator(options, web3SDK)
+	jobCreatorService, err := NewJobCreator(options, web3SDK, tracer)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/options/job-creator.go b/pkg/options/job-creator.go
index 78382bcf..86c5bc65 100644
--- a/pkg/options/job-creator.go
+++ b/pkg/options/job-creator.go
@@ -14,6 +14,7 @@ func NewJobCreatorOptions() jobcreator.JobCreatorOptions {
 		Offer:     GetDefaultJobCreatorOfferOptions(),
 		Web3:      GetDefaultWeb3Options(),
 		Mediation: GetDefaultJobCreatorMediationOptions(),
+		Telemetry: GetDefaultTelemetryOptions(),
 	}
 	options.Web3.Service = system.JobCreatorService
 	return options
@@ -62,6 +63,7 @@ func AddJobCreatorCliFlags(cmd *cobra.Command, options *jobcreator.JobCreatorOpt
 	AddJobCreatorMediationCliFlags(cmd, &options.Mediation)
 	AddWeb3CliFlags(cmd, &options.Web3)
 	AddJobCreatorOfferCliFlags(cmd, &options.Offer)
+	AddTelemetryCliFlags(cmd, &options.Telemetry)
 }
 
 func CheckJobCreatorOptions(options jobcreator.JobCreatorOptions) error {
@@ -77,6 +79,10 @@ func CheckJobCreatorOptions(options jobcreator.JobCreatorOptions) error {
 	if err != nil {
 		return err
 	}
+	err = CheckTelemetryOptions(options.Telemetry)
+	if err != nil {
+		return err
+	}
 
 	if options.Mediation.CheckResultsPercentage < 0 || options.Mediation.CheckResultsPercentage > 100 {
 		return fmt.Errorf("mediation-chance must be between 0 and 100")
@@ -119,6 +125,12 @@ func ProcessJobCreatorOptions(options jobcreator.JobCreatorOptions, args []strin
 	}
 	options.Offer.Target = newTargetOptions
 
+	newTelemetryOptions, err := ProcessTelemetryOptions(options.Telemetry, network)
+	if err != nil {
+		return options, err
+	}
+	options.Telemetry = newTelemetryOptions
+
 	return options, CheckJobCreatorOptions(options)
 }
 
@@ -135,6 +147,12 @@ func ProcessOnChainJobCreatorOptions(options jobcreator.JobCreatorOptions, args
 	}
 	options.Offer.Services = newServicesOptions
 
+	newTelemetryOptions, err := ProcessTelemetryOptions(options.Telemetry, network)
+	if err != nil {
+		return options, err
+	}
+	options.Telemetry = newTelemetryOptions
+
 	err = CheckWeb3Options(options.Web3)
 	if err != nil {
 		return options, err
@@ -143,6 +161,10 @@ func ProcessOnChainJobCreatorOptions(options jobcreator.JobCreatorOptions, args
 	if err != nil {
 		return options, err
 	}
+	err = CheckTelemetryOptions(options.Telemetry)
+	if err != nil {
+		return options, err
+	}
 
 	options.Mediation.CheckResultsPercentage = 0
 
diff --git a/test/integration_test.go b/test/integration_test.go
index 5aa79f3f..286ea6f9 100644
--- a/test/integration_test.go
+++ b/test/integration_test.go
@@ -95,7 +95,8 @@ func testStackWithOptions(
 		return nil, err
 	}
 
-	result, err := jobcreator.RunJob(commandCtx, jobCreatorOptions, func(evOffer data.JobOfferContainer) {
+	noopTracer := traceNoop.NewTracerProvider().Tracer(system.GetOTelServiceName(system.DefaultService))
+	result, err := jobcreator.RunJob(commandCtx, jobCreatorOptions, noopTracer, func(evOffer data.JobOfferContainer) {
 
 	})
 	if err != nil {