Skip to content

Commit

Permalink
Fix ga settings (#466)
Browse files Browse the repository at this point in the history
  • Loading branch information
schoren authored May 10, 2022
1 parent 9e0cf27 commit b0af415
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 145 deletions.
44 changes: 4 additions & 40 deletions docs/run-locally.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,46 +61,11 @@ Now Tracetest is available at [http://localhost:8080]
## **Run a Development Build**

Now that Tracetest is running, we can expose the dependencies in our cluster to the host machine so they are accessible to the development build.

### Expose jaeger-query

Any method for exposing a service will work. Here is an example using `LoadBalancer`
Tracetests needs postgres to store the tests, results, etc, and access to the trace backend (jaeger, tempo, etc) to fetch traces.
We can use kubectl's port forwarding capabilites for this

```
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
name: jaeger-query-exposed
spec:
type: LoadBalancer
ports:
- port: 16685
targetPort: 16685
selector:
app: jaeger
EOF
```

### Expose postgres

Any method for exposing a service will work. Here is an example using `LoadBalancer`

```
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
name: psql
spec:
type: LoadBalancer
ports:
- port: 5432
targetPort: 5432
selector:
app.kubernetes.io/instance: tracetest
app.kubernetes.io/name: postgresql
EOF
(trap "kill 0" SIGINT; kubectl port-forward svc/tracetest-postgresql 5432:5432 & kubectl port-forward svc/jaeger-query 16685:16685)
```

### Start Development Server
Expand All @@ -110,8 +75,7 @@ When running the development version, the frontend and backend are built and run
To start the backend server:

```
cd server
make run-server # builds the server and starts it
make server-run
```

To start the frontend server:
Expand Down
213 changes: 120 additions & 93 deletions server/analytics/analytics.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,139 +8,132 @@ import (
"net/http"
"os"
"runtime"
"strconv"

"github.com/denisbrodbeck/machineid"
"github.com/kubeshop/tracetest/config"
)

var (
appName = "tracetest"
analyticsEnabled = false
gaURL = "https://www.google-analytics.com/mp/collect?measurement_id=%s&api_secret=%s"
gaValidationURL = "https://www.google-analytics.com/debug/mp/collect?measurement_id=%s&api_secret=%s"
gaMeasurementId = ""
gaSecretKey = ""
const (
gaURL = "https://www.google-analytics.com/mp/collect?measurement_id=%s&api_secret=%s"
gaValidationURL = "https://www.google-analytics.com/debug/mp/collect?measurement_id=%s&api_secret=%s"
)

func init() {
gaMeasurementId = os.Getenv("GOOGLE_ANALYTICS_MEASUREMENT_ID")
gaSecretKey = os.Getenv("GOOGLE_ANALYTICS_SECRET_KEY")
analyticsIsEnabled, err := strconv.ParseBool(os.Getenv("ANALYTICS_ENABLED"))
var defaultClient ga

func Init(cfg config.GoogleAnalytics, appName, appVersion string) error {
// ga not enabled, use dumb settings
if !cfg.Enabled {
defaultClient = ga{enabled: false}
return nil
}

// setup an actual client
hostname, err := os.Hostname()
if err != nil {
analyticsEnabled = false
} else {
analyticsEnabled = analyticsIsEnabled
return fmt.Errorf("could not get hostname: %w", err)
}
}

type Params struct {
EventCount int64 `json:"event_count,omitempty"`
EventCategory string `json:"event_category,omitempty"`
AppVersion string `json:"app_version,omitempty"`
AppName string `json:"app_name,omitempty"`
CustomDimensions string `json:"custom_dimensions,omitempty"`
DataSource string `json:"data_source,omitempty"`
Host string `json:"host,omitempty"`
MachineID string `json:"machine_id,omitempty"`
OperatingSystem string `json:"operating_system,omitempty"`
Architecture string `json:"architecture,omitempty"`
}
machineID, err := machineid.ProtectedID(appName)
if err != nil {
return fmt.Errorf("could not get machineID: %w", err)
}

type Event struct {
Name string `json:"name"`
Params Params `json:"params,omitempty"`
defaultClient = ga{
enabled: cfg.Enabled,
measurementID: cfg.MeasurementID,
secretKey: cfg.SecretKey,
appVersion: appVersion,
appName: appName,
hostname: hostname,
machineID: machineID,
}

return nil
}

type Payload struct {
UserID string `json:"user_id,omitempty"`
ClientID string `json:"client_id,omitempty"`
Events []Event `json:"events,omitempty"`
func CreateAndSendEvent(name, category string) error {
if defaultClient.ready() {
return fmt.Errorf("uninitalized client. Call analytics.Init")
}
return defaultClient.CreateAndSendEvent(name, category)
}

type validationResponse struct {
ValidationMessages []validationMessage `json:"validationMessages"`
type ga struct {
enabled bool
appVersion string
appName string
measurementID string
secretKey string
hostname string
machineID string
}

type validationMessage struct {
FieldPath string `json:"fieldPath"`
Description string `json:"description"`
ValidationCode string `json:"validationCode"`
func (ga ga) ready() bool {
return !ga.enabled || (ga.appVersion != "" &&
ga.appName != "" &&
ga.measurementID != "" &&
ga.secretKey != "" &&
ga.hostname != "" &&
ga.machineID != "")

}

func NewEvent(name string, category string) (Event, error) {
appVersion := os.Getenv("VERSION")
host, err := os.Hostname()
if err != nil {
return Event{}, fmt.Errorf("could not get hostname: %w", err)
func (ga ga) CreateAndSendEvent(name, category string) error {
if !ga.enabled {
return nil
}

machineID, err := machineid.ProtectedID(appName)
event, err := ga.newEvent(name, category)
if err != nil {
return Event{}, fmt.Errorf("could not get machineID: %w", err)
return fmt.Errorf("could not create event: %w", err)
}

return Event{
return ga.sendEvent(event)
}

func (ga ga) newEvent(name, category string) (event, error) {
return event{
Name: name,
Params: Params{
Params: params{
EventCount: 1,
AppName: "tracetest",
EventCategory: category,
Host: host,
MachineID: machineID,
AppVersion: appVersion,
AppName: ga.appName,
Host: ga.hostname,
MachineID: ga.machineID,
AppVersion: ga.appVersion,
Architecture: runtime.GOARCH,
OperatingSystem: runtime.GOOS,
},
}, nil
}

// SendEvent sends an event to Google Analytics.
func SendEvent(event Event) error {
if !analyticsEnabled {
return nil
}

return sendEvent(event)
}

// CreateAndSendEvent is a syntax-sugar to create and send the event in a single command
func CreateAndSendEvent(name string, category string) error {
event, err := NewEvent(name, category)
if err != nil {
return fmt.Errorf("could not create event: %w", err)
}

return SendEvent(event)
}

func sendEvent(event Event) error {
machineID, err := machineid.ProtectedID(appName)
if err != nil {
return fmt.Errorf("could not get machine id: %w", err)
}
payload := Payload{
UserID: machineID,
ClientID: machineID,
Events: []Event{
event,
func (ga ga) sendEvent(e event) error {
payload := payload{
UserID: ga.machineID,
ClientID: ga.machineID,
Events: []event{
e,
},
}

err = sendValidationRequest(payload)
fmt.Printf("ga %+v\n", payload)
err := ga.sendValidationRequest(payload)
if err != nil {
fmt.Println("err validation", err)
return err
}

err = sendDataToGA(payload)
err = ga.sendDataToGA(payload)
if err != nil {
fmt.Println("err sendData", err)
return fmt.Errorf("could not send request to google analytics: %w", err)
}

fmt.Println("success data")
return nil
}

func sendValidationRequest(payload Payload) error {
response, body, err := sendPayloadToURL(payload, gaValidationURL)
func (ga ga) sendValidationRequest(p payload) error {
response, body, err := ga.sendPayloadToURL(p, gaValidationURL)

if err != nil {
return err
Expand All @@ -164,8 +157,8 @@ func sendValidationRequest(payload Payload) error {
return nil
}

func sendDataToGA(payload Payload) error {
response, _, err := sendPayloadToURL(payload, gaURL)
func (ga ga) sendDataToGA(p payload) error {
response, _, err := ga.sendPayloadToURL(p, gaURL)
if err != nil {
return fmt.Errorf("could not send event to google analytics: %w", err)
}
Expand All @@ -177,13 +170,13 @@ func sendDataToGA(payload Payload) error {
return nil
}

func sendPayloadToURL(payload Payload, url string) (*http.Response, []byte, error) {
jsonData, err := json.Marshal(payload)
func (ga ga) sendPayloadToURL(p payload, url string) (*http.Response, []byte, error) {
jsonData, err := json.Marshal(p)
if err != nil {
return nil, []byte{}, fmt.Errorf("could not marshal json payload: %w", err)
}

request, err := http.NewRequest("POST", fmt.Sprintf(url, gaMeasurementId, gaSecretKey), bytes.NewBuffer(jsonData))
request, err := http.NewRequest("POST", fmt.Sprintf(url, ga.measurementID, ga.secretKey), bytes.NewBuffer(jsonData))
if err != nil {
return nil, []byte{}, fmt.Errorf("could not create request: %w", err)
}
Expand All @@ -204,3 +197,37 @@ func sendPayloadToURL(payload Payload, url string) (*http.Response, []byte, erro

return resp, body, err
}

type params struct {
EventCount int64 `json:"event_count,omitempty"`
EventCategory string `json:"event_category,omitempty"`
AppVersion string `json:"app_version,omitempty"`
AppName string `json:"app_name,omitempty"`
CustomDimensions string `json:"custom_dimensions,omitempty"`
DataSource string `json:"data_source,omitempty"`
Host string `json:"host,omitempty"`
MachineID string `json:"machine_id,omitempty"`
OperatingSystem string `json:"operating_system,omitempty"`
Architecture string `json:"architecture,omitempty"`
}

type event struct {
Name string `json:"name"`
Params params `json:"params,omitempty"`
}

type payload struct {
UserID string `json:"user_id,omitempty"`
ClientID string `json:"client_id,omitempty"`
Events []event `json:"events,omitempty"`
}

type validationResponse struct {
ValidationMessages []validationMessage `json:"validationMessages"`
}

type validationMessage struct {
FieldPath string `json:"fieldPath"`
Description string `json:"description"`
ValidationCode string `json:"validationCode"`
}
8 changes: 7 additions & 1 deletion server/config.yaml.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
maxWaitTimeForTrace: 30s
postgresConnString: "host=postgres user=postgres password=postgres port=5432 sslmode=disable"
jaegerConnectionConfig:
endpoint: jaeger-query:16685
tls:
insecure: true

maxWaitTimeForTrace: 30s

googleAnalytics:
enabled: false
measurementId: ""
secretKey: ""
21 changes: 15 additions & 6 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@ import (
"gopkg.in/yaml.v2"
)

type Config struct {
PostgresConnString string `mapstructure:"postgresConnString"`
JaegerConnectionConfig *configgrpc.GRPCClientSettings `mapstructure:"jaegerConnectionConfig"`
TempoConnectionConfig *configgrpc.GRPCClientSettings `mapstructure:"tempoConnectionConfig"`
MaxWaitTimeForTrace string `mapstructure:"maxWaitTimeForTrace"`
}
type (
Config struct {
PostgresConnString string `mapstructure:"postgresConnString"`
JaegerConnectionConfig *configgrpc.GRPCClientSettings `mapstructure:"jaegerConnectionConfig"`
TempoConnectionConfig *configgrpc.GRPCClientSettings `mapstructure:"tempoConnectionConfig"`
MaxWaitTimeForTrace string `mapstructure:"maxWaitTimeForTrace"`
GA GoogleAnalytics `mapstructure:"googleAnalytics"`
}

GoogleAnalytics struct {
MeasurementID string `mapstructure:"measurementId"`
SecretKey string `mapstructure:"secretKey"`
Enabled bool `mapstructure:"enabled"`
}
)

func (c Config) MaxWaitTimeForTraceDuration() time.Duration {
maxWaitTimeForTrace, err := time.ParseDuration(c.MaxWaitTimeForTrace)
Expand Down
2 changes: 1 addition & 1 deletion server/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestFromFileError(t *testing.T) {
cl := c
t.Parallel()

_, err := config.FromFile("./testdata/config.yaml")
_, err := config.FromFile(cl.file)

assert.True(t, strings.HasPrefix(err.Error(), cl.expected))

Expand Down
Loading

0 comments on commit b0af415

Please sign in to comment.