@@ -3,11 +3,9 @@ package scalers
3
3
import (
4
4
"context"
5
5
"database/sql"
6
- "errors"
7
6
"fmt"
8
7
"net"
9
8
"net/url"
10
- "strconv"
11
9
12
10
// mssql driver required for this scaler
13
11
_ "github.com/denisenkom/go-mssqldb"
@@ -18,59 +16,35 @@ import (
18
16
"github.com/kedacore/keda/v2/pkg/scalers/scalersconfig"
19
17
)
20
18
21
- var (
22
- // ErrMsSQLNoQuery is returned when "query" is missing from the config.
23
- ErrMsSQLNoQuery = errors .New ("no query given" )
24
-
25
- // ErrMsSQLNoTargetValue is returned when "targetValue" is missing from the config.
26
- ErrMsSQLNoTargetValue = errors .New ("no targetValue given" )
27
- )
28
-
29
- // mssqlScaler exposes a data pointer to mssqlMetadata and sql.DB connection
30
19
type mssqlScaler struct {
31
20
metricType v2.MetricTargetType
32
- metadata * mssqlMetadata
21
+ metadata mssqlMetadata
33
22
connection * sql.DB
34
23
logger logr.Logger
35
24
}
36
25
37
- // mssqlMetadata defines metadata used by KEDA to query a Microsoft SQL database
38
26
type mssqlMetadata struct {
39
- // The connection string used to connect to the MSSQL database.
40
- // Both URL syntax (sqlserver://host?database=dbName) and OLEDB syntax is supported.
41
- // +optional
42
- connectionString string
43
- // The username credential for connecting to the MSSQL instance, if not specified in the connection string.
44
- // +optional
45
- username string
46
- // The password credential for connecting to the MSSQL instance, if not specified in the connection string.
47
- // +optional
48
- password string
49
- // The hostname of the MSSQL instance endpoint, if not specified in the connection string.
50
- // +optional
51
- host string
52
- // The port number of the MSSQL instance endpoint, if not specified in the connection string.
53
- // +optional
54
- port int
55
- // The name of the database to query, if not specified in the connection string.
56
- // +optional
57
- database string
58
- // The T-SQL query to run against the target database - e.g. SELECT COUNT(*) FROM table.
59
- // +required
60
- query string
61
- // The threshold that is used as targetAverageValue in the Horizontal Pod Autoscaler.
62
- // +required
63
- targetValue float64
64
- // The threshold that is used in activation phase
65
- // +optional
66
- activationTargetValue float64
67
- // The index of the scaler inside the ScaledObject
68
- // +internal
69
- triggerIndex int
27
+ ConnectionString string `keda:"name=connectionString, order=authParams;resolvedEnv;triggerMetadata, optional"`
28
+ Username string `keda:"name=username, order=authParams;triggerMetadata, optional"`
29
+ Password string `keda:"name=password, order=authParams;resolvedEnv;triggerMetadata, optional"`
30
+ Host string `keda:"name=host, order=authParams;triggerMetadata, optional"`
31
+ Port int `keda:"name=port, order=authParams;triggerMetadata, optional"`
32
+ Database string `keda:"name=database, order=authParams;triggerMetadata, optional"`
33
+ Query string `keda:"name=query, order=triggerMetadata"`
34
+ TargetValue float64 `keda:"name=targetValue, order=triggerMetadata"`
35
+ ActivationTargetValue float64 `keda:"name=activationTargetValue, order=triggerMetadata, default=0"`
36
+
37
+ TriggerIndex int
70
38
}
71
39
72
- // NewMSSQLScaler creates a new mssql scaler
73
- func NewMSSQLScaler (config * scalersconfig.ScalerConfig ) (Scaler , error ) {
40
+ func (m * mssqlMetadata ) Validate () error {
41
+ if m .ConnectionString == "" && m .Host == "" {
42
+ return fmt .Errorf ("must provide either connectionstring or host" )
43
+ }
44
+ return nil
45
+ }
46
+
47
+ func NewMSSQLScaler (ctx context.Context , config * scalersconfig.ScalerConfig ) (Scaler , error ) {
74
48
metricType , err := GetMetricTargetType (config )
75
49
if err != nil {
76
50
return nil , fmt .Errorf ("error getting scaler metric type: %w" , err )
@@ -83,155 +57,87 @@ func NewMSSQLScaler(config *scalersconfig.ScalerConfig) (Scaler, error) {
83
57
return nil , fmt .Errorf ("error parsing mssql metadata: %w" , err )
84
58
}
85
59
86
- conn , err := newMSSQLConnection (meta , logger )
87
- if err != nil {
88
- return nil , fmt .Errorf ("error establishing mssql connection: %w" , err )
89
- }
90
-
91
- return & mssqlScaler {
60
+ scaler := & mssqlScaler {
92
61
metricType : metricType ,
93
62
metadata : meta ,
94
- connection : conn ,
95
63
logger : logger ,
96
- }, nil
97
- }
98
-
99
- // parseMSSQLMetadata takes a ScalerConfig and returns a mssqlMetadata or an error if the config is invalid
100
- func parseMSSQLMetadata (config * scalersconfig.ScalerConfig ) (* mssqlMetadata , error ) {
101
- meta := mssqlMetadata {}
102
-
103
- // Query
104
- if val , ok := config .TriggerMetadata ["query" ]; ok {
105
- meta .query = val
106
- } else {
107
- return nil , ErrMsSQLNoQuery
108
64
}
109
65
110
- // Target query value
111
- if val , ok := config .TriggerMetadata ["targetValue" ]; ok {
112
- targetValue , err := strconv .ParseFloat (val , 64 )
113
- if err != nil {
114
- return nil , fmt .Errorf ("targetValue parsing error %w" , err )
115
- }
116
- meta .targetValue = targetValue
117
- } else {
118
- if config .AsMetricSource {
119
- meta .targetValue = 0
120
- } else {
121
- return nil , ErrMsSQLNoTargetValue
122
- }
66
+ conn , err := newMSSQLConnection (scaler )
67
+ if err != nil {
68
+ return nil , fmt .Errorf ("error establishing mssql connection: %w" , err )
123
69
}
124
70
125
- // Activation target value
126
- meta .activationTargetValue = 0
127
- if val , ok := config .TriggerMetadata ["activationTargetValue" ]; ok {
128
- activationTargetValue , err := strconv .ParseFloat (val , 64 )
129
- if err != nil {
130
- return nil , fmt .Errorf ("activationTargetValue parsing error %w" , err )
131
- }
132
- meta .activationTargetValue = activationTargetValue
133
- }
71
+ scaler .connection = conn
134
72
135
- // Connection string, which can either be provided explicitly or via the helper fields
136
- switch {
137
- case config .AuthParams ["connectionString" ] != "" :
138
- meta .connectionString = config .AuthParams ["connectionString" ]
139
- case config .TriggerMetadata ["connectionStringFromEnv" ] != "" :
140
- meta .connectionString = config .ResolvedEnv [config .TriggerMetadata ["connectionStringFromEnv" ]]
141
- default :
142
- meta .connectionString = ""
143
- var err error
144
-
145
- host , err := GetFromAuthOrMeta (config , "host" )
146
- if err != nil {
147
- return nil , err
148
- }
149
- meta .host = host
150
-
151
- var paramPort string
152
- paramPort , _ = GetFromAuthOrMeta (config , "port" )
153
- if paramPort != "" {
154
- port , err := strconv .Atoi (paramPort )
155
- if err != nil {
156
- return nil , fmt .Errorf ("port parsing error %w" , err )
157
- }
158
- meta .port = port
159
- }
73
+ return scaler , nil
74
+ }
160
75
161
- meta .username , _ = GetFromAuthOrMeta (config , "username" )
76
+ func parseMSSQLMetadata (config * scalersconfig.ScalerConfig ) (mssqlMetadata , error ) {
77
+ meta := mssqlMetadata {}
78
+ err := config .TypedConfig (& meta )
79
+ if err != nil {
80
+ return meta , err
81
+ }
162
82
163
- // database is optional in SQL s
164
- meta .database , _ = GetFromAuthOrMeta (config , "database" )
83
+ meta .TriggerIndex = config .TriggerIndex
165
84
166
- if config .AuthParams ["password" ] != "" {
167
- meta .password = config .AuthParams ["password" ]
168
- } else if config .TriggerMetadata ["passwordFromEnv" ] != "" {
169
- meta .password = config .ResolvedEnv [config .TriggerMetadata ["passwordFromEnv" ]]
170
- }
171
- }
172
- meta .triggerIndex = config .TriggerIndex
173
- return & meta , nil
85
+ return meta , nil
174
86
}
175
87
176
- // newMSSQLConnection returns a new, opened SQL connection for the provided mssqlMetadata
177
- func newMSSQLConnection (meta * mssqlMetadata , logger logr.Logger ) (* sql.DB , error ) {
178
- connStr := getMSSQLConnectionString (meta )
88
+ func newMSSQLConnection (s * mssqlScaler ) (* sql.DB , error ) {
89
+ connStr := getMSSQLConnectionString (s )
179
90
180
91
db , err := sql .Open ("sqlserver" , connStr )
181
92
if err != nil {
182
- logger .Error (err , fmt . Sprintf ( "Found error opening mssql: %s" , err ) )
93
+ s . logger .Error (err , "Found error opening mssql" )
183
94
return nil , err
184
95
}
185
96
186
97
err = db .Ping ()
187
98
if err != nil {
188
- logger .Error (err , fmt . Sprintf ( "Found error pinging mssql: %s" , err ) )
99
+ s . logger .Error (err , "Found error pinging mssql" )
189
100
return nil , err
190
101
}
191
102
192
103
return db , nil
193
104
}
194
105
195
- // getMSSQLConnectionString returns a connection string from a mssqlMetadata
196
- func getMSSQLConnectionString (meta * mssqlMetadata ) string {
197
- var connStr string
198
-
199
- if meta .connectionString != "" {
200
- connStr = meta .connectionString
201
- } else {
202
- query := url.Values {}
203
- if meta .database != "" {
204
- query .Add ("database" , meta .database )
205
- }
106
+ func getMSSQLConnectionString (s * mssqlScaler ) string {
107
+ meta := s .metadata
108
+ if meta .ConnectionString != "" {
109
+ return meta .ConnectionString
110
+ }
206
111
207
- connectionURL := & url.URL {Scheme : "sqlserver" , RawQuery : query .Encode ()}
208
- if meta .username != "" {
209
- if meta .password != "" {
210
- connectionURL .User = url .UserPassword (meta .username , meta .password )
211
- } else {
212
- connectionURL .User = url .User (meta .username )
213
- }
214
- }
112
+ query := url.Values {}
113
+ if meta .Database != "" {
114
+ query .Add ("database" , meta .Database )
115
+ }
215
116
216
- if meta .port > 0 {
217
- connectionURL .Host = net .JoinHostPort (meta .host , fmt .Sprintf ("%d" , meta .port ))
117
+ connectionURL := & url.URL {Scheme : "sqlserver" , RawQuery : query .Encode ()}
118
+ if meta .Username != "" {
119
+ if meta .Password != "" {
120
+ connectionURL .User = url .UserPassword (meta .Username , meta .Password )
218
121
} else {
219
- connectionURL .Host = meta .host
122
+ connectionURL .User = url . User ( meta .Username )
220
123
}
124
+ }
221
125
222
- connStr = connectionURL .String ()
126
+ if meta .Port > 0 {
127
+ connectionURL .Host = net .JoinHostPort (meta .Host , fmt .Sprintf ("%d" , meta .Port ))
128
+ } else {
129
+ connectionURL .Host = meta .Host
223
130
}
224
131
225
- return connStr
132
+ return connectionURL . String ()
226
133
}
227
134
228
- // GetMetricSpecForScaling returns the MetricSpec for the Horizontal Pod Autoscaler
229
135
func (s * mssqlScaler ) GetMetricSpecForScaling (context.Context ) []v2.MetricSpec {
230
136
externalMetric := & v2.ExternalMetricSource {
231
137
Metric : v2.MetricIdentifier {
232
- Name : GenerateMetricNameWithIndex (s .metadata .triggerIndex , "mssql" ),
138
+ Name : GenerateMetricNameWithIndex (s .metadata .TriggerIndex , "mssql" ),
233
139
},
234
- Target : GetMetricTargetMili (s .metricType , s .metadata .targetValue ),
140
+ Target : GetMetricTargetMili (s .metricType , s .metadata .TargetValue ),
235
141
}
236
142
237
143
metricSpec := v2.MetricSpec {
@@ -241,7 +147,6 @@ func (s *mssqlScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec {
241
147
return []v2.MetricSpec {metricSpec }
242
148
}
243
149
244
- // GetMetricsAndActivity returns a value for a supported metric or an error if there is a problem getting the metric
245
150
func (s * mssqlScaler ) GetMetricsAndActivity (ctx context.Context , metricName string ) ([]external_metrics.ExternalMetricValue , bool , error ) {
246
151
num , err := s .getQueryResult (ctx )
247
152
if err != nil {
@@ -250,13 +155,13 @@ func (s *mssqlScaler) GetMetricsAndActivity(ctx context.Context, metricName stri
250
155
251
156
metric := GenerateMetricInMili (metricName , num )
252
157
253
- return []external_metrics.ExternalMetricValue {metric }, num > s .metadata .activationTargetValue , nil
158
+ return []external_metrics.ExternalMetricValue {metric }, num > s .metadata .ActivationTargetValue , nil
254
159
}
255
160
256
- // getQueryResult returns the result of the scaler query
257
161
func (s * mssqlScaler ) getQueryResult (ctx context.Context ) (float64 , error ) {
258
162
var value float64
259
- err := s .connection .QueryRowContext (ctx , s .metadata .query ).Scan (& value )
163
+
164
+ err := s .connection .QueryRowContext (ctx , s .metadata .Query ).Scan (& value )
260
165
switch {
261
166
case err == sql .ErrNoRows :
262
167
value = 0
@@ -268,7 +173,6 @@ func (s *mssqlScaler) getQueryResult(ctx context.Context) (float64, error) {
268
173
return value , nil
269
174
}
270
175
271
- // Close closes the mssql database connections
272
176
func (s * mssqlScaler ) Close (context.Context ) error {
273
177
err := s .connection .Close ()
274
178
if err != nil {
0 commit comments