diff --git a/telemetry/aiwrapper_test.go b/telemetry/aiwrapper_test.go new file mode 100644 index 0000000000..de9ac6bafb --- /dev/null +++ b/telemetry/aiwrapper_test.go @@ -0,0 +1,48 @@ +package telemetry + +import ( + "testing" + + "github.com/Azure/azure-container-networking/aitelemetry" + "github.com/stretchr/testify/require" +) + +func TestCreateAITelemetryHandle(t *testing.T) { + tests := []struct { + name string + aiConfig aitelemetry.AIConfig + disableAll bool + disableMetric bool + disableTrace bool + wantErr bool + }{ + { + name: "disable telemetry", + aiConfig: aitelemetry.AIConfig{}, + disableAll: false, + disableMetric: true, + disableTrace: true, + wantErr: true, + }, + { + name: "empty aiconfig", + aiConfig: aitelemetry.AIConfig{}, + disableAll: true, + disableMetric: true, + disableTrace: true, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + err := CreateAITelemetryHandle(tt.aiConfig, tt.disableAll, tt.disableMetric, tt.disableTrace) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go index 17087db407..f2659dd05e 100644 --- a/telemetry/telemetry.go +++ b/telemetry/telemetry.go @@ -5,11 +5,11 @@ package telemetry import ( "encoding/json" - "fmt" "github.com/Azure/azure-container-networking/aitelemetry" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/platform" + "github.com/pkg/errors" ) const ( @@ -122,30 +122,22 @@ func (reportMgr *ReportManager) SendReport(tb *TelemetryBuffer) error { // ReportToBytes - returns the report bytes func (reportMgr *ReportManager) ReportToBytes() ([]byte, error) { - var err error - var report []byte switch reportMgr.Report.(type) { case *CNIReport: case *AIMetric: default: - err = fmt.Errorf("[Telemetry] Invalid report type") - } - - if err != nil { - return []byte{}, err + return []byte{}, errors.Errorf("Invalid report type: %T", reportMgr.Report) } - report, err = json.Marshal(reportMgr.Report) + report, err := json.Marshal(reportMgr.Report) return report, err } // This function for sending CNI metrics to telemetry service func SendCNIMetric(cniMetric *AIMetric, tb *TelemetryBuffer) error { - var ( - err error - report []byte - ) + var err error + var report []byte if tb != nil && tb.Connected { reportMgr := &ReportManager{Report: cniMetric} diff --git a/telemetry/telemetry_test.go b/telemetry/telemetry_test.go index 05550e9e95..04f789831b 100644 --- a/telemetry/telemetry_test.go +++ b/telemetry/telemetry_test.go @@ -4,268 +4,150 @@ package telemetry import ( - "encoding/json" - "fmt" - "log" - "net/http" - "net/url" "os" - "runtime" "testing" - "time" - "github.com/Azure/azure-container-networking/common" - "github.com/Azure/azure-container-networking/platform" + "github.com/Azure/azure-container-networking/aitelemetry" + "github.com/stretchr/testify/require" ) -const ( - telemetryConfig = "azure-vnet-telemetry.config" -) - -var ( - reportManager *ReportManager - tb *TelemetryBuffer - ipamQueryUrl = "localhost:3501" - hostAgentUrl = "localhost:3500" -) - -var ipamQueryResponse = "" + - "" + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - "" +var telemetryTests = []struct { + name string + Data interface{} + wantErr bool +}{ + { + name: "aimetric type", + Data: &AIMetric{ + Metric: aitelemetry.Metric{ + Name: "test", + Value: float64(1.0), + CustomDimensions: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "cnireport type", + Data: &CNIReport{ + Name: "test-cnireport", + Version: "11", + OperationDuration: 10, + Context: "test-context", + SubContext: "test-subcontext", + }, + wantErr: false, + }, + { + name: "nil type", + Data: nil, + wantErr: true, + }, + { + name: "unexpected type", + Data: 1, + wantErr: true, + }, +} func TestMain(m *testing.M) { - u, _ := url.Parse("tcp://" + ipamQueryUrl) - ipamAgent, err := common.NewListener(u) - if err != nil { - fmt.Printf("Failed to create agent, err:%v.\n", err) - return - } - - ipamAgent.AddHandler("/", handleIpamQuery) - - err = ipamAgent.Start(make(chan error, 1)) - if err != nil { - fmt.Printf("Failed to start agent, err:%v.\n", err) - return - } - - 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("/", handlePayload) - - err = hostAgent.Start(make(chan error, 1)) - if err != nil { - fmt.Printf("Failed to start agent, err:%v.\n", err) - return - } - - if runtime.GOOS == "linux" { - platform.ExecuteCommand("cp metadata_test.json /tmp/azuremetadata.json") - } else { - platform.ExecuteCommand("copy metadata_test.json azuremetadata.json") - } - - reportManager = &ReportManager{} - reportManager.HostNetAgentURL = "http://" + hostAgentUrl - reportManager.ContentType = "application/json" - reportManager.Report = &CNIReport{} - - tb = NewTelemetryBuffer() - err = tb.StartServer() - if err == nil { - go tb.PushData() - } - - if err := tb.Connect(); err != nil { - fmt.Printf("connection to telemetry server failed %v", err) - } - + tb := NewTelemetryBuffer() + _ = tb.Cleanup(FdName) exitCode := m.Run() - - if runtime.GOOS == "linux" { - platform.ExecuteCommand("rm /tmp/azuremetadata.json") - } else { - platform.ExecuteCommand("del azuremetadata.json") - } - - tb.Cleanup(FdName) - ipamAgent.Stop() os.Exit(exitCode) } -// Handles queries from IPAM source. -func handleIpamQuery(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(ipamQueryResponse)) -} - -func handlePayload(rw http.ResponseWriter, req *http.Request) { - decoder := json.NewDecoder(req.Body) - var t Buffer - err := decoder.Decode(&t) - if err != nil { - panic(err) - } - defer req.Body.Close() - log.Println(t) - - fmt.Printf("Buffer: %+v", t) -} - -func TestGetOSDetails(t *testing.T) { - reportManager.Report.(*CNIReport).GetOSDetails() - if reportManager.Report.(*CNIReport).ErrorMessage != "" { - t.Errorf("GetOSDetails failed due to %v", reportManager.Report.(*CNIReport).ErrorMessage) - } -} - -func TestGetSystemDetails(t *testing.T) { - reportManager.Report.(*CNIReport).GetSystemDetails() - if reportManager.Report.(*CNIReport).ErrorMessage != "" { - t.Errorf("GetSystemDetails failed due to %v", reportManager.Report.(*CNIReport).ErrorMessage) - } -} - -func TestGetInterfaceDetails(t *testing.T) { - reportManager.Report.(*CNIReport).GetSystemDetails() - if reportManager.Report.(*CNIReport).ErrorMessage != "" { - t.Errorf("GetInterfaceDetails failed due to %v", reportManager.Report.(*CNIReport).ErrorMessage) - } -} - -func TestSendTelemetry(t *testing.T) { - err := reportManager.SendReport(tb) - if err != nil { - t.Errorf("SendTelemetry failed due to %v", err) - } - - i := 3 - rpMgr := &ReportManager{} - rpMgr.Report = &i - err = rpMgr.SendReport(tb) - if err == nil { - t.Errorf("SendTelemetry not failed for incorrect report type") - } -} - -func TestCloseTelemetryConnection(t *testing.T) { - tb.Cancel() - time.Sleep(300 * time.Millisecond) - - tb.mutex.Lock() - if len(tb.connections) != 0 { - t.Errorf("server didn't close connection") - } - tb.mutex.Unlock() -} - -func TestServerCloseTelemetryConnection(t *testing.T) { - // create server telemetrybuffer and start server - tb = NewTelemetryBuffer() - err := tb.StartServer() - if err == nil { - go tb.PushData() - } - - // create client telemetrybuffer and connect to server - tb1 := NewTelemetryBuffer() - if err := tb1.Connect(); err != nil { - t.Errorf("connection to telemetry server failed %v", err) - } - - // Exit server thread and close server connection - tb.Cancel() - time.Sleep(300 * time.Millisecond) - - b := []byte("tamil") - if _, err := tb1.Write(b); err == nil { - t.Errorf("Client couldn't recognise server close") - } - - tb.mutex.Lock() - if len(tb.connections) != 0 { - t.Errorf("All connections not closed as expected") +func TestReportToBytes(t *testing.T) { + reportManager := &ReportManager{} + for _, tt := range telemetryTests { + tt := tt + reportManager.Report = tt.Data + t.Run(tt.name, func(t *testing.T) { + _, err := reportManager.ReportToBytes() + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) } - tb.mutex.Unlock() - - // Close client connection - tb1.Close() } -func TestClientCloseTelemetryConnection(t *testing.T) { - // create server telemetrybuffer and start server - tb = NewTelemetryBuffer() - err := tb.StartServer() - if err == nil { - go tb.PushData() - } - - if !SockExists() { - t.Errorf("telemetry sock doesn't exist") - } - - // create client telemetrybuffer and connect to server - tb1 := NewTelemetryBuffer() - if err := tb1.Connect(); err != nil { - t.Errorf("connection to telemetry server failed %v", err) +func TestSendReport(t *testing.T) { + tb, closeTBServer := createTBServer(t) + defer closeTBServer() + + err := tb.Connect() + require.NoError(t, err) + + reportManager := &ReportManager{} + for _, tt := range telemetryTests { + tt := tt + reportManager.Report = tt.Data + t.Run(tt.name, func(t *testing.T) { + err := reportManager.SendReport(tb) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) } - - // Close client connection - tb1.Close() - time.Sleep(300 * time.Millisecond) - - tb.mutex.Lock() - if len(tb.connections) != 0 { - t.Errorf("All connections not closed as expected") - } - tb.mutex.Unlock() - - // Exit server thread and close server connection - tb.Cancel() } -func TestReadConfigFile(t *testing.T) { - config, err := ReadConfigFile(telemetryConfig) - if err != nil { - t.Errorf("Read telemetry config failed with error %v", err) - } - - if config.ReportToHostIntervalInSeconds != 30 { - t.Errorf("ReportToHostIntervalInSeconds not expected value. Got %d", config.ReportToHostIntervalInSeconds) - } - - config, err = ReadConfigFile("a.config") - if err == nil { - t.Errorf("[Telemetry] Didn't throw not found error: %v", err) - } - - config, err = ReadConfigFile("telemetry.go") - if err == nil { - t.Errorf("[Telemetry] Didn't report invalid telemetry config: %v", err) +func TestSendCNIMetric(t *testing.T) { + tb, closeTBServer := createTBServer(t) + defer closeTBServer() + + err := tb.Connect() + require.NoError(t, err) + + tests := []struct { + name string + metric *AIMetric + wantErr bool + }{ + { + name: "aimetric", + metric: &AIMetric{ + Metric: aitelemetry.Metric{ + Name: "test-metric", + Value: float64(1.0), + CustomDimensions: make(map[string]string), + }, + }, + wantErr: false, + }, + { + name: "nil", + metric: nil, + wantErr: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + err := SendCNIMetric(tt.metric, tb) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) } } -func TestStartTelemetryService(t *testing.T) { - err := StartTelemetryService("", nil) - if err == nil { - t.Errorf("StartTelemetryService didnt return error for incorrect service name %v", err) - } +func TestGetOSDetails(t *testing.T) { + expectedErrMsg := "" + cniReport := &CNIReport{} + cniReport.GetSystemDetails() + require.Equal(t, expectedErrMsg, cniReport.ErrorMessage) } -func TestWaitForTelemetrySocket(t *testing.T) { - WaitForTelemetrySocket(1, 10) +func TestGetSystemDetails(t *testing.T) { + expectedErrMsg := "" + cniReport := &CNIReport{} + cniReport.GetSystemDetails() + require.Equal(t, expectedErrMsg, cniReport.ErrorMessage) } diff --git a/telemetry/telemetrybuffer_test.go b/telemetry/telemetrybuffer_test.go new file mode 100644 index 0000000000..f96750ab76 --- /dev/null +++ b/telemetry/telemetrybuffer_test.go @@ -0,0 +1,156 @@ +package telemetry + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +const telemetryConfig = "azure-vnet-telemetry.config" + +func createTBServer(t *testing.T) (*TelemetryBuffer, func()) { + tbServer := NewTelemetryBuffer() + err := tbServer.StartServer() + require.NoError(t, err) + + return tbServer, func() { + tbServer.Close() + err := tbServer.Cleanup(FdName) + require.Error(t, err) + } +} + +func TestStartServer(t *testing.T) { + _, closeTBServer := createTBServer(t) + defer closeTBServer() + + secondTBServer := NewTelemetryBuffer() + err := secondTBServer.StartServer() + require.Error(t, err) +} + +func TestConnect(t *testing.T) { + _, closeTBServer := createTBServer(t) + defer closeTBServer() + + tbClient := NewTelemetryBuffer() + err := tbClient.Connect() + require.NoError(t, err) + + tbClient.Close() +} + +func TestServerConnClose(t *testing.T) { + tbServer, closeTBServer := createTBServer(t) + defer closeTBServer() + + tbClient := NewTelemetryBuffer() + err := tbClient.Connect() + require.NoError(t, err) + defer tbClient.Close() + + tbServer.Close() + + b := []byte("testdata") + _, err = tbClient.Write(b) + require.Error(t, err) +} + +func TestClientConnClose(t *testing.T) { + _, closeTBServer := createTBServer(t) + defer closeTBServer() + + tbClient := NewTelemetryBuffer() + err := tbClient.Connect() + require.NoError(t, err) + tbClient.Close() +} + +func TestWrite(t *testing.T) { + _, closeTBServer := createTBServer(t) + defer closeTBServer() + + tbClient := NewTelemetryBuffer() + err := tbClient.Connect() + require.NoError(t, err) + defer tbClient.Close() + + tests := []struct { + name string + data []byte + want int + wantErr bool + }{ + { + name: "write", + data: []byte("testdata"), + want: len("testdata") + 1, // +1 due to Delimiter('\n) + wantErr: false, + }, + { + name: "write zero data", + data: []byte(""), + want: 1, // +1 due to Delimiter('\n) + wantErr: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got, err := tbClient.Write(tt.data) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestReadConfigFile(t *testing.T) { + tests := []struct { + name string + fileName string + want TelemetryConfig + wantErr bool + }{ + { + name: "read existing file", + fileName: telemetryConfig, + want: TelemetryConfig{ + ReportToHostIntervalInSeconds: time.Duration(30), + RefreshTimeoutInSecs: 15, + BatchIntervalInSecs: 15, + BatchSizeInBytes: 16384, + }, + wantErr: false, + }, + { + name: "read non-existing file", + fileName: "non-existing-file", + want: TelemetryConfig{}, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + got, err := ReadConfigFile(tt.fileName) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestStartTelemetryService(t *testing.T) { + err := StartTelemetryService("", nil) + require.Error(t, err) +}