Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ CNI_MULTITENANCY_BUILD_DIR = $(BUILD_DIR)/cni-multitenancy
CNS_BUILD_DIR = $(BUILD_DIR)/cns
NPM_BUILD_DIR = $(BUILD_DIR)/npm
NPM_TELEMETRY_DIR = $(NPM_BUILD_DIR)/telemetry
CNI_AI_ID = 5515a1eb-b2bc-406a-98eb-ba462e6f0411
ACN_PACKAGE_PATH = github.com/Azure/azure-container-networking

# Containerized build parameters.
BUILD_CONTAINER_IMAGE = acn-build
Expand Down Expand Up @@ -169,7 +171,7 @@ $(CNI_BUILD_DIR)/azure-vnet-ipam$(EXE_EXT): $(CNIFILES)

# Build the Azure CNI telemetry plugin.
$(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT): $(CNIFILES)
go build -v -o $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) -ldflags "-X main.version=$(VERSION) -s -w" $(CNI_TELEMETRY_DIR)/*.go
go build -v -o $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) -ldflags "-X main.version=$(VERSION) -X $(ACN_PACKAGE_PATH)/telemetry.aiMetadata=$(CNI_AI_ID) -s -w" $(CNI_TELEMETRY_DIR)/*.go

# Build the Azure CNS Service.
$(CNS_BUILD_DIR)/azure-cns$(EXE_EXT): $(CNSFILES)
Expand Down
2 changes: 2 additions & 0 deletions aitelemetry/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type AIConfig struct {
BatchInterval int
DisableMetadataRefreshThread bool
RefreshTimeout int
GetEnvRetryCount int
GetEnvRetryWaitTimeInSecs int
DebugMode bool
}

Expand Down
73 changes: 56 additions & 17 deletions aitelemetry/telemetrywrapper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package aitelemetry

import (
"fmt"
"runtime"
"time"

Expand All @@ -11,30 +12,32 @@ import (
)

const (
resourceGroupStr = "ResourceGroup"
vmSizeStr = "VMSize"
osVersionStr = "OSVersion"
locationStr = "Region"
appNameStr = "AppName"
subscriptionIDStr = "SubscriptionID"
vmNameStr = "VMName"
defaultTimeout = 10
resourceGroupStr = "ResourceGroup"
vmSizeStr = "VMSize"
osVersionStr = "OSVersion"
locationStr = "Region"
appNameStr = "AppName"
subscriptionIDStr = "SubscriptionID"
vmNameStr = "VMName"
versionStr = "AppVersion"
azurePublicCloudStr = "AzurePublicCloud"
defaultTimeout = 10
)

var debugMode bool

func messageListener() appinsights.DiagnosticsMessageListener {
if debugMode {
return appinsights.NewDiagnosticsMessageListener(func(msg string) error {
debuglog("[AppInsights] [%s] %s\n", time.Now().Format(time.UnixDate), msg)
debugLog("[AppInsights] [%s] %s\n", time.Now().Format(time.UnixDate), msg)
return nil
})
}

return nil
}

func debuglog(format string, args ...interface{}) {
func debugLog(format string, args ...interface{}) {
if debugMode {
log.Printf(format, args...)
}
Expand All @@ -55,12 +58,12 @@ func getMetadata(th *telemetryHandle) {
break
}

debuglog("[AppInsights] Error getting metadata %v. Sleep for %d", err, th.refreshTimeout)
debugLog("[AppInsights] Error getting metadata %v. Sleep for %d", err, th.refreshTimeout)
time.Sleep(time.Duration(th.refreshTimeout) * time.Second)
}

if err != nil {
debuglog("[AppInsights] Error getting metadata %v", err)
debugLog("[AppInsights] Error getting metadata %v", err)
return
}

Expand All @@ -72,28 +75,63 @@ func getMetadata(th *telemetryHandle) {
// Save metadata retrieved from wireserver to a file
kvs, err := store.NewJsonFileStore(metadataFile)
if err != nil {
debuglog("[AppInsights] Error initializing kvs store: %v", err)
debugLog("[AppInsights] Error initializing kvs store: %v", err)
return
}

kvs.Lock(true)
err = common.SaveHostMetadata(th.metadata, metadataFile)
kvs.Unlock(true)
if err != nil {
debuglog("[AppInsights] saving host metadata failed with :%v", err)
debugLog("[AppInsights] saving host metadata failed with :%v", err)
}
}

func isPublicEnvironment(url string, retryCount, waitTimeInSecs int) (bool, error) {
var (
cloudName string
err error
)

for i := 0; i < retryCount; i++ {
cloudName, err = common.GetAzureCloud(url)
if cloudName == azurePublicCloudStr {
debugLog("[AppInsights] CloudName: %s\n", cloudName)
return true, nil
} else if err == nil {
debugLog("[AppInsights] This is not azure public cloud:%s", cloudName)
return false, fmt.Errorf("Not an azure public cloud: %s", cloudName)
}

debugLog("GetAzureCloud returned err :%v", err)
time.Sleep(time.Duration(waitTimeInSecs) * time.Second)
}

return false, err
}

// NewAITelemetry creates telemetry handle with user specified appinsights id.
func NewAITelemetry(
azEnvUrl string,
id string,
aiConfig AIConfig,
) TelemetryHandle {
) (TelemetryHandle, error) {
debugMode = aiConfig.DebugMode

if id == "" {
debugLog("Empty AI key")
return nil, fmt.Errorf("AI key is empty")
}

// check if azure instance is in public cloud
isPublic, err := isPublicEnvironment(azEnvUrl, aiConfig.GetEnvRetryCount, aiConfig.GetEnvRetryWaitTimeInSecs)
if !isPublic {
return nil, err
}

telemetryConfig := appinsights.NewTelemetryConfiguration(id)
telemetryConfig.MaxBatchSize = aiConfig.BatchSize
telemetryConfig.MaxBatchInterval = time.Duration(aiConfig.BatchInterval) * time.Second
debugMode = aiConfig.DebugMode

th := &telemetryHandle{
client: appinsights.NewTelemetryClientFromConfig(telemetryConfig),
Expand All @@ -110,7 +148,7 @@ func NewAITelemetry(
go getMetadata(th)
}

return th
return th, nil
}

// TrackLog function sends report (trace) to appinsights resource. It overrides few of the existing columns with app information
Expand Down Expand Up @@ -167,6 +205,7 @@ func (th *telemetryHandle) TrackMetric(metric Metric) {
aimetric.Properties[locationStr] = th.metadata.Location
aimetric.Properties[subscriptionIDStr] = th.metadata.SubscriptionID
aimetric.Properties[vmNameStr] = th.metadata.VMName
aimetric.Properties[versionStr] = th.appVersion
}

// copy custom dimensions
Expand Down
62 changes: 53 additions & 9 deletions aitelemetry/telemetrywrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,33 @@ package aitelemetry

import (
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/platform"
)

var th TelemetryHandle
var (
th TelemetryHandle
hostAgentUrl = "localhost:3501"
getCloudResponse = "AzurePublicCloud"
httpURL = "http://" + hostAgentUrl
)

func TestMain(m *testing.M) {
log.SetLogDirectory("/var/log/")
log.SetName("testaitelemetry")
log.SetLevel(log.LevelInfo)
err := log.SetTarget(log.TargetLogfile)
if err == nil {
fmt.Printf("TestST LogDir configuration succeeded\n")
}

if runtime.GOOS == "linux" {
platform.ExecuteCommand("cp metadata_test.json /tmp/azuremetadata.json")
Expand All @@ -22,6 +38,20 @@ func TestMain(m *testing.M) {
platform.ExecuteCommand(cmd)
}

hostu, _ := url.Parse("tcp://" + hostAgentUrl)
hostAgent, err := common.NewListener(hostu)
if err != nil {
fmt.Printf("Failed to create agent, err:%v.\n", err)
return
}

hostAgent.AddHandler("/", handleGetCloud)
err = hostAgent.Start(make(chan error, 1))
if err != nil {
fmt.Printf("Failed to start agent, err:%v.\n", err)
return
}

exitCode := m.Run()

if runtime.GOOS == "linux" {
Expand All @@ -32,10 +62,17 @@ func TestMain(m *testing.M) {
platform.ExecuteCommand(cmd)
}

log.Close()
os.Exit(exitCode)
}

func handleGetCloud(w http.ResponseWriter, req *http.Request) {
w.Write([]byte(getCloudResponse))
}

func TestEmptyAIKey(t *testing.T) {
var err error

aiConfig := AIConfig{
AppName: "testapp",
AppVersion: "v1.0.26",
Expand All @@ -45,26 +82,29 @@ func TestEmptyAIKey(t *testing.T) {
DebugMode: true,
DisableMetadataRefreshThread: true,
}
th := NewAITelemetry("", aiConfig)
if th == nil {
t.Errorf("Error intializing AI telemetry")
_, err = NewAITelemetry(httpURL, "", aiConfig)
if err == nil {
t.Errorf("Error intializing AI telemetry:%v", err)
}
th.Close(10)
}

func TestNewAITelemetry(t *testing.T) {
var err error

aiConfig := AIConfig{
AppName: "testapp",
AppVersion: "v1.0.26",
BatchSize: 4096,
BatchInterval: 2,
RefreshTimeout: 10,
GetEnvRetryCount: 1,
GetEnvRetryWaitTimeInSecs: 2,
DebugMode: true,
DisableMetadataRefreshThread: true,
}
th = NewAITelemetry("00ca2a73-c8d6-4929-a0c2-cf84545ec225", aiConfig)
th, err = NewAITelemetry(httpURL, "00ca2a73-c8d6-4929-a0c2-cf84545ec225", aiConfig)
if th == nil {
t.Errorf("Error intializing AI telemetry")
t.Errorf("Error intializing AI telemetry: %v", err)
}
}

Expand Down Expand Up @@ -95,18 +135,22 @@ func TestClose(t *testing.T) {
}

func TestClosewithoutSend(t *testing.T) {
var err error

aiConfig := AIConfig{
AppName: "testapp",
AppVersion: "v1.0.26",
BatchSize: 4096,
BatchInterval: 2,
DisableMetadataRefreshThread: true,
RefreshTimeout: 10,
GetEnvRetryCount: 1,
GetEnvRetryWaitTimeInSecs: 2,
}

thtest := NewAITelemetry("00ca2a73-c8d6-4929-a0c2-cf84545ec225", aiConfig)
thtest, err := NewAITelemetry(httpURL, "00ca2a73-c8d6-4929-a0c2-cf84545ec225", aiConfig)
if thtest == nil {
t.Errorf("Error intializing AI telemetry")
t.Errorf("Error intializing AI telemetry:%v", err)
}

thtest.Close(10)
Expand Down
Loading