Skip to content

Commit 96c482a

Browse files
authored
fix: complete mssql analyser implementation (#1290)
* fix: complete mssql analyser implementation * Add mssql analyser tests and mssql collector logs * Close mssql db after collecting data
1 parent 5439316 commit 96c482a

File tree

5 files changed

+167
-8
lines changed

5 files changed

+167
-8
lines changed

pkg/analyze/analyzer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ func getAnalyzer(analyzer *troubleshootv1beta2.Analyze) Analyzer {
218218
return &AnalyzePostgres{analyzer: analyzer.Postgres}
219219
case analyzer.Mysql != nil:
220220
return &AnalyzeMysql{analyzer: analyzer.Mysql}
221+
case analyzer.Mssql != nil:
222+
return &AnalyzeMssql{analyzer: analyzer.Mssql}
221223
case analyzer.Redis != nil:
222224
return &AnalyzeRedis{analyzer: analyzer.Redis}
223225
case analyzer.CephStatus != nil:

pkg/analyze/mssql.go

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,39 @@ import (
1313
"github.com/replicatedhq/troubleshoot/pkg/collect"
1414
)
1515

16+
type AnalyzeMssql struct {
17+
analyzer *troubleshootv1beta2.DatabaseAnalyze
18+
}
19+
20+
func (a *AnalyzeMssql) Title() string {
21+
title := a.analyzer.CheckName
22+
if title == "" {
23+
title = a.collectorName()
24+
}
25+
26+
return title
27+
}
28+
29+
func (a *AnalyzeMssql) IsExcluded() (bool, error) {
30+
return isExcluded(a.analyzer.Exclude)
31+
}
32+
33+
func (a *AnalyzeMssql) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
34+
result, err := analyzeMssql(a.analyzer, getFile)
35+
if err != nil {
36+
return nil, err
37+
}
38+
result.Strict = a.analyzer.Strict.BoolOrDefaultFalse()
39+
return []*AnalyzeResult{result}, nil
40+
}
41+
42+
func (a *AnalyzeMssql) collectorName() string {
43+
if a.analyzer.CollectorName != "" {
44+
return a.analyzer.CollectorName
45+
}
46+
return "mysql"
47+
}
48+
1649
func compareMssqlConditionalToActual(conditional string, result *collect.DatabaseConnection) (bool, error) {
1750
parts := strings.Split(strings.TrimSpace(conditional), " ")
1851

@@ -90,9 +123,7 @@ func analyzeMssql(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFil
90123
}
91124

92125
result := &AnalyzeResult{
93-
Title: title,
94-
IconKey: "kubernetes_sqlserver_analyze",
95-
IconURI: "https://troubleshoot.sh/images/analyzer-icons/sqlserver-analyze.svg",
126+
Title: title,
96127
}
97128

98129
for _, outcome := range analyzer.Outcomes {

pkg/analyze/mssql_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package analyzer
22

33
import (
4+
"encoding/json"
5+
"reflect"
46
"testing"
57

8+
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
69
"github.com/replicatedhq/troubleshoot/pkg/collect"
710
"github.com/stretchr/testify/assert"
811
"github.com/stretchr/testify/require"
@@ -115,3 +118,109 @@ func Test_compareMssqlConditionalToActual(t *testing.T) {
115118
})
116119
}
117120
}
121+
122+
func TestAnalyzeMssql_Analyze(t *testing.T) {
123+
tests := []struct {
124+
name string
125+
analyzer *troubleshootv1beta2.DatabaseAnalyze
126+
want []*AnalyzeResult
127+
data map[string]any
128+
wantErr bool
129+
}{
130+
{
131+
name: "mssql analyze with passing condition",
132+
data: map[string]any{
133+
"isConnected": true,
134+
"version": "15.0.2000.1565",
135+
},
136+
analyzer: &troubleshootv1beta2.DatabaseAnalyze{
137+
AnalyzeMeta: troubleshootv1beta2.AnalyzeMeta{
138+
CheckName: "Must be SQLServer 15.x or later",
139+
},
140+
Outcomes: []*troubleshootv1beta2.Outcome{
141+
{
142+
Pass: &troubleshootv1beta2.SingleOutcome{
143+
Message: "The SQLServer connection checks out",
144+
},
145+
},
146+
{
147+
Fail: &troubleshootv1beta2.SingleOutcome{
148+
When: "connected == false",
149+
Message: "Cannot connect to SQLServer",
150+
},
151+
},
152+
},
153+
},
154+
want: []*AnalyzeResult{
155+
{
156+
Title: "Must be SQLServer 15.x or later",
157+
Message: "The SQLServer connection checks out",
158+
IsPass: true,
159+
},
160+
},
161+
},
162+
{
163+
name: "mssql analyze with failing condition",
164+
data: map[string]any{
165+
"isConnected": true,
166+
"version": "14.0.2000.1565",
167+
},
168+
analyzer: &troubleshootv1beta2.DatabaseAnalyze{
169+
Outcomes: []*troubleshootv1beta2.Outcome{
170+
{
171+
Fail: &troubleshootv1beta2.SingleOutcome{
172+
When: "version < 15.x",
173+
Message: "The SQLServer must be at least version 15",
174+
},
175+
},
176+
{
177+
Pass: &troubleshootv1beta2.SingleOutcome{
178+
Message: "The SQLServer connection checks out",
179+
},
180+
},
181+
},
182+
},
183+
want: []*AnalyzeResult{
184+
{
185+
Title: "mssql",
186+
Message: "The SQLServer must be at least version 15",
187+
IsFail: true,
188+
},
189+
},
190+
},
191+
}
192+
193+
for _, tt := range tests {
194+
t.Run(tt.name, func(t *testing.T) {
195+
a := &AnalyzeMssql{
196+
analyzer: tt.analyzer,
197+
}
198+
199+
getFile := func(_ string) ([]byte, error) {
200+
return json.Marshal(tt.data)
201+
}
202+
203+
got, err := a.Analyze(getFile, nil)
204+
assert.Equalf(t, tt.wantErr, err != nil, "got error = %v, wantErr %v", err, tt.wantErr)
205+
206+
got2 := fromPointerSlice(got)
207+
want2 := fromPointerSlice(tt.want)
208+
if !reflect.DeepEqual(got2, want2) {
209+
t.Errorf("got = %v, want %v", toJSON(got2), toJSON(want2))
210+
}
211+
})
212+
}
213+
}
214+
215+
func fromPointerSlice(in []*AnalyzeResult) []AnalyzeResult {
216+
out := make([]AnalyzeResult, len(in))
217+
for i := range in {
218+
out[i] = *in[i]
219+
}
220+
return out
221+
}
222+
223+
func toJSON(in any) string {
224+
out, _ := json.MarshalIndent(in, "", " ")
225+
return string(out)
226+
}

pkg/analyze/mysql.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ func (a *AnalyzeMysql) collectorName() string {
4040
if a.analyzer.CollectorName != "" {
4141
return a.analyzer.CollectorName
4242
}
43-
return "mysql"
43+
return "mssql"
4444
}
4545

4646
func (a *AnalyzeMysql) analyzeMysql(analyzer *troubleshootv1beta2.DatabaseAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) {
47-
fullPath := path.Join("mysql", fmt.Sprintf("%s.json", a.collectorName()))
47+
fullPath := path.Join("mssql", fmt.Sprintf("%s.json", a.collectorName()))
4848

4949
collected, err := getCollectedFileContents(fullPath)
5050
if err != nil {
@@ -58,7 +58,7 @@ func (a *AnalyzeMysql) analyzeMysql(analyzer *troubleshootv1beta2.DatabaseAnalyz
5858

5959
result := &AnalyzeResult{
6060
Title: a.Title(),
61-
IconKey: "kubernetes_mysql_analyze",
61+
IconKey: "kubernetes_mssql_analyze",
6262
IconURI: "https://troubleshoot.sh/images/analyzer-icons/mysql-analyze.svg",
6363
}
6464

pkg/collect/mssql.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import (
66
"database/sql"
77
"encoding/json"
88
"fmt"
9+
"net/url"
910
"regexp"
1011

1112
_ "github.com/microsoft/go-mssqldb"
1213
"github.com/pkg/errors"
1314
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
1415
"k8s.io/client-go/kubernetes"
1516
"k8s.io/client-go/rest"
17+
"k8s.io/klog/v2"
1618
)
1719

1820
type CollectMssql struct {
@@ -36,14 +38,26 @@ func (c *CollectMssql) IsExcluded() (bool, error) {
3638
func (c *CollectMssql) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
3739
databaseConnection := DatabaseConnection{}
3840

41+
connUrl, err := url.Parse(c.Collector.URI)
42+
// Parsing the uri should not lead to the collector failing
43+
// sql.Open will fail if the uri is invalid.
44+
if err == nil {
45+
klog.V(2).Infof("Connect to %q MSSQL Database", connUrl.Host)
46+
} else {
47+
klog.V(2).Info("Connect to MSSQL Database")
48+
}
49+
3950
db, err := sql.Open("mssql", c.Collector.URI)
4051
if err != nil {
52+
klog.V(2).Infof("Failed to connect to %q MSSQL Database: %w", connUrl.Host, err)
4153
databaseConnection.Error = err.Error()
4254
} else {
55+
defer db.Close()
4356
query := `select @@VERSION as version`
4457
row := db.QueryRow(query)
4558
version := ""
4659
if err := row.Scan(&version); err != nil {
60+
klog.V(2).Infof("Failed to query version string from database: %s", err)
4761
databaseConnection.Error = err.Error()
4862
} else {
4963
databaseConnection.IsConnected = true
@@ -55,6 +69,7 @@ func (c *CollectMssql) Collect(progressChan chan<- interface{}) (CollectorResult
5569
} else {
5670
databaseConnection.Version = mssqlVersion
5771
}
72+
klog.V(2).Infof("Successfully queried version string from database: %s", version)
5873
}
5974
}
6075

@@ -75,12 +90,14 @@ func (c *CollectMssql) Collect(progressChan chan<- interface{}) (CollectorResult
7590
}
7691

7792
func parseMsSqlVersion(mssqlVersion string) (string, error) {
78-
re := regexp.MustCompile(".*SQL.*-\\s+([0-9.]+)")
93+
re, err := regexp.Compile(`.*SQL.*-\s+([0-9.]+)`)
94+
if err != nil {
95+
return "", errors.Wrap(err, "failed to compile regex")
96+
}
7997
matches := re.FindStringSubmatch(mssqlVersion)
8098
if len(matches) < 2 {
8199
return "", errors.Errorf("mssql version did not match regex: %q", mssqlVersion)
82100
}
83101

84102
return matches[1], nil
85-
86103
}

0 commit comments

Comments
 (0)