Skip to content

Commit 70b32fa

Browse files
committed
add azurelogs module.
1 parent e67f5a3 commit 70b32fa

File tree

15 files changed

+1481
-364
lines changed

15 files changed

+1481
-364
lines changed

modules/azurelogs/config.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package azurelogs
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"os"
7+
8+
"gopkg.in/yaml.v3"
9+
)
10+
11+
// QueryFile represents the structure of a query configuration file
12+
type QueryFile struct {
13+
Title string `yaml:"title"` // Display title for the query
14+
SubscriptionID string `yaml:"azure_subscription_id"` // Azure subscription ID
15+
WorkspaceID string `yaml:"azure_workspace_id"` // Log Analytics workspace ID
16+
Columns []string `yaml:"columns"` // Expected column names
17+
Query string `yaml:"query"` // KQL query string
18+
}
19+
20+
// readQueryFile reads and parses a query configuration file
21+
func readQueryFile(sess *Session, queryPath string) error {
22+
file, err := os.OpenFile(queryPath, os.O_RDONLY, 0o600)
23+
if err != nil {
24+
return err
25+
}
26+
27+
filename := file.Name()
28+
if len(filename) > 5 && filename[len(filename)-5:] == ".yaml" {
29+
var configFile QueryFile
30+
configFile, err = readQueryFileContent(queryPath)
31+
if err != nil {
32+
return err
33+
}
34+
35+
sess.QueryFile = configFile
36+
} else {
37+
return fmt.Errorf("invalid query file format: %s, expected .yaml", filename)
38+
}
39+
40+
return nil
41+
}
42+
43+
// readQueryFileContent reads a single config file and returns a QueryFile struct
44+
func readQueryFileContent(filePath string) (QueryFile, error) {
45+
var configFile QueryFile
46+
data, err := os.ReadFile(filePath)
47+
if err != nil {
48+
return configFile, fmt.Errorf("failed to read query config file %s: %w", filePath, err)
49+
}
50+
51+
err = yaml.Unmarshal(data, &configFile)
52+
if err != nil {
53+
return configFile, fmt.Errorf("failed to parse YAML in config file %s: %w", filePath, err)
54+
}
55+
56+
return configFile, nil
57+
}

modules/azurelogs/config_test.go

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
package azurelogs
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestQueryFile_Structure(t *testing.T) {
12+
// Test QueryFile structure and YAML tags
13+
qf := QueryFile{
14+
Title: "Test Azure Query",
15+
SubscriptionID: "subscription-123",
16+
WorkspaceID: "workspace-456",
17+
Columns: []string{"TimeGenerated", "Level", "Message"},
18+
Query: "AzureActivity | where Level == 'Error' | limit 100",
19+
}
20+
21+
assert.Equal(t, "Test Azure Query", qf.Title)
22+
assert.Equal(t, "subscription-123", qf.SubscriptionID)
23+
assert.Equal(t, "workspace-456", qf.WorkspaceID)
24+
assert.Len(t, qf.Columns, 3)
25+
assert.Equal(t, "TimeGenerated", qf.Columns[0])
26+
assert.Equal(t, "Level", qf.Columns[1])
27+
assert.Equal(t, "Message", qf.Columns[2])
28+
assert.Contains(t, qf.Query, "AzureActivity")
29+
}
30+
31+
func TestReadQueryFileContent_ValidYAML(t *testing.T) {
32+
// Create a temporary YAML file for testing
33+
yamlContent := `title: "Test Query"
34+
azure_subscription_id: "test-sub-123"
35+
azure_workspace_id: "test-workspace-456"
36+
columns:
37+
- "TimeGenerated"
38+
- "Level"
39+
- "Message"
40+
query: "AzureActivity | limit 10"`
41+
42+
tmpFile, err := os.CreateTemp("", "test-query-*.yaml")
43+
require.NoError(t, err)
44+
defer os.Remove(tmpFile.Name())
45+
46+
_, err = tmpFile.WriteString(yamlContent)
47+
require.NoError(t, err)
48+
require.NoError(t, tmpFile.Close())
49+
50+
// Test reading the file
51+
queryFile, err := readQueryFileContent(tmpFile.Name())
52+
53+
assert.NoError(t, err)
54+
assert.Equal(t, "Test Query", queryFile.Title)
55+
assert.Equal(t, "test-sub-123", queryFile.SubscriptionID)
56+
assert.Equal(t, "test-workspace-456", queryFile.WorkspaceID)
57+
assert.Len(t, queryFile.Columns, 3)
58+
assert.Equal(t, "TimeGenerated", queryFile.Columns[0])
59+
assert.Equal(t, "Level", queryFile.Columns[1])
60+
assert.Equal(t, "Message", queryFile.Columns[2])
61+
assert.Equal(t, "AzureActivity | limit 10", queryFile.Query)
62+
}
63+
64+
func TestReadQueryFileContent_InvalidYAML(t *testing.T) {
65+
// Create a temporary file with invalid YAML
66+
invalidYamlContent := `title: "Test Query"
67+
azure_subscription_id: "test-sub-123"
68+
invalid_yaml: [unclosed bracket`
69+
70+
tmpFile, err := os.CreateTemp("", "test-invalid-*.yaml")
71+
require.NoError(t, err)
72+
defer os.Remove(tmpFile.Name())
73+
74+
_, err = tmpFile.WriteString(invalidYamlContent)
75+
require.NoError(t, err)
76+
require.NoError(t, tmpFile.Close())
77+
78+
// Test reading the invalid file
79+
_, err = readQueryFileContent(tmpFile.Name())
80+
81+
assert.Error(t, err)
82+
assert.Contains(t, err.Error(), "failed to parse YAML")
83+
}
84+
85+
func TestReadQueryFileContent_NonexistentFile(t *testing.T) {
86+
// Test reading a file that doesn't exist
87+
_, err := readQueryFileContent("/nonexistent/file.yaml")
88+
89+
assert.Error(t, err)
90+
assert.Contains(t, err.Error(), "failed to read query config file")
91+
}
92+
93+
func TestReadQueryFile_ValidYAMLFile(t *testing.T) {
94+
// Create a temporary YAML file for testing
95+
yamlContent := `title: "Integration Test Query"
96+
azure_subscription_id: "integration-sub-123"
97+
azure_workspace_id: "integration-workspace-456"
98+
columns:
99+
- "Computer"
100+
- "TimeGenerated"
101+
- "SourceSystem"
102+
query: "Heartbeat | limit 5"`
103+
104+
tmpFile, err := os.CreateTemp("", "test-integration-*.yaml")
105+
require.NoError(t, err)
106+
defer os.Remove(tmpFile.Name())
107+
108+
_, err = tmpFile.WriteString(yamlContent)
109+
require.NoError(t, err)
110+
require.NoError(t, tmpFile.Close())
111+
112+
// Test reading into session
113+
sess := &Session{}
114+
err = readQueryFile(sess, tmpFile.Name())
115+
116+
assert.NoError(t, err)
117+
assert.Equal(t, "Integration Test Query", sess.QueryFile.Title)
118+
assert.Equal(t, "integration-sub-123", sess.QueryFile.SubscriptionID)
119+
assert.Equal(t, "integration-workspace-456", sess.QueryFile.WorkspaceID)
120+
assert.Len(t, sess.QueryFile.Columns, 3)
121+
assert.Equal(t, "Computer", sess.QueryFile.Columns[0])
122+
assert.Equal(t, "Heartbeat | limit 5", sess.QueryFile.Query)
123+
}
124+
125+
func TestReadQueryFile_NonYAMLFile(t *testing.T) {
126+
// Create a temporary file with non-YAML extension
127+
tmpFile, err := os.CreateTemp("", "test-non-yaml-*.txt")
128+
require.NoError(t, err)
129+
defer os.Remove(tmpFile.Name())
130+
131+
_, err = tmpFile.WriteString("some content")
132+
require.NoError(t, err)
133+
require.NoError(t, tmpFile.Close())
134+
135+
// Test reading non-YAML file
136+
sess := &Session{}
137+
err = readQueryFile(sess, tmpFile.Name())
138+
139+
assert.Error(t, err)
140+
assert.Contains(t, err.Error(), "invalid query file format")
141+
assert.Contains(t, err.Error(), "expected .yaml")
142+
}
143+
144+
func TestReadQueryFile_EmptyYAMLFile(t *testing.T) {
145+
// Create an empty YAML file
146+
tmpFile, err := os.CreateTemp("", "test-empty-*.yaml")
147+
require.NoError(t, err)
148+
defer os.Remove(tmpFile.Name())
149+
150+
require.NoError(t, tmpFile.Close())
151+
152+
// Test reading empty file
153+
sess := &Session{}
154+
err = readQueryFile(sess, tmpFile.Name())
155+
156+
// Should succeed but with empty values
157+
assert.NoError(t, err)
158+
assert.Equal(t, "", sess.QueryFile.Title)
159+
assert.Equal(t, "", sess.QueryFile.SubscriptionID)
160+
assert.Equal(t, "", sess.QueryFile.WorkspaceID)
161+
assert.Empty(t, sess.QueryFile.Columns)
162+
assert.Equal(t, "", sess.QueryFile.Query)
163+
}
164+
165+
func TestReadQueryFile_PartialYAMLFile(t *testing.T) {
166+
// Create a YAML file with only some fields
167+
yamlContent := `title: "Partial Query"
168+
azure_subscription_id: "partial-sub-123"
169+
# Missing workspace_id, columns, and query`
170+
171+
tmpFile, err := os.CreateTemp("", "test-partial-*.yaml")
172+
require.NoError(t, err)
173+
defer os.Remove(tmpFile.Name())
174+
175+
_, err = tmpFile.WriteString(yamlContent)
176+
require.NoError(t, err)
177+
require.NoError(t, tmpFile.Close())
178+
179+
// Test reading partial file
180+
sess := &Session{}
181+
err = readQueryFile(sess, tmpFile.Name())
182+
183+
assert.NoError(t, err)
184+
assert.Equal(t, "Partial Query", sess.QueryFile.Title)
185+
assert.Equal(t, "partial-sub-123", sess.QueryFile.SubscriptionID)
186+
assert.Equal(t, "", sess.QueryFile.WorkspaceID) // Should be empty
187+
assert.Empty(t, sess.QueryFile.Columns) // Should be empty
188+
assert.Equal(t, "", sess.QueryFile.Query) // Should be empty
189+
}
190+
191+
func TestQueryFile_YAMLTags(t *testing.T) {
192+
// This is a structural test to ensure YAML tags are properly defined
193+
// We test this by creating a QueryFile and checking field mapping
194+
yamlContent := `title: "YAML Tag Test"
195+
azure_subscription_id: "yaml-sub-123"
196+
azure_workspace_id: "yaml-workspace-456"
197+
columns:
198+
- "TestColumn1"
199+
- "TestColumn2"
200+
query: "TestQuery | limit 1"`
201+
202+
tmpFile, err := os.CreateTemp("", "test-yaml-tags-*.yaml")
203+
require.NoError(t, err)
204+
defer os.Remove(tmpFile.Name())
205+
206+
_, err = tmpFile.WriteString(yamlContent)
207+
require.NoError(t, err)
208+
require.NoError(t, tmpFile.Close())
209+
210+
queryFile, err := readQueryFileContent(tmpFile.Name())
211+
212+
assert.NoError(t, err)
213+
214+
// Verify that YAML tags correctly map to struct fields
215+
assert.Equal(t, "YAML Tag Test", queryFile.Title) // yaml:"title"
216+
assert.Equal(t, "yaml-sub-123", queryFile.SubscriptionID) // yaml:"azure_subscription_id"
217+
assert.Equal(t, "yaml-workspace-456", queryFile.WorkspaceID) // yaml:"azure_workspace_id"
218+
assert.Equal(t, []string{"TestColumn1", "TestColumn2"}, queryFile.Columns) // yaml:"columns"
219+
assert.Equal(t, "TestQuery | limit 1", queryFile.Query) // yaml:"query"
220+
}

modules/azurelogs/keyboard.go

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)