diff --git a/api/v1/checks.go b/api/v1/checks.go index 73eb43bea..aab4eb1a0 100644 --- a/api/v1/checks.go +++ b/api/v1/checks.go @@ -56,8 +56,9 @@ func (c Check) GetLabels() map[string]string { } type Oauth2Config struct { - Scopes []string `json:"scope,omitempty" yaml:"scope,omitempty"` - TokenURL string `json:"tokenURL,omitempty" yaml:"tokenURL,omitempty"` + Scopes []string `json:"scope,omitempty" yaml:"scope,omitempty"` + TokenURL string `json:"tokenURL,omitempty" yaml:"tokenURL,omitempty"` + Params map[string]string `json:"params,omitempty" yaml:"params,omitempty"` } type HTTPCheck struct { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index b598aa2dd..b3e2c2bd0 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -2398,6 +2398,13 @@ func (in *Oauth2Config) DeepCopyInto(out *Oauth2Config) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Oauth2Config. diff --git a/checks/folder.go b/checks/folder.go index b19759f1c..f1f0ae7af 100644 --- a/checks/folder.go +++ b/checks/folder.go @@ -89,10 +89,11 @@ func checkLocalFolder(ctx *context.Context, check v1.FolderCheck) pkg.Results { var results pkg.Results results = append(results, result) folders, err := getLocalFolderCheck(check.Path, check.Filter) + result.AddDetails(folders) + if err != nil { return results.ErrorMessage(err) } - result.AddDetails(folders) if test := folders.Test(check.FolderTest); test != "" { return results.Failf(test) @@ -100,23 +101,31 @@ func checkLocalFolder(ctx *context.Context, check v1.FolderCheck) pkg.Results { return results } -func getLocalFolderCheck(path string, filter v1.FolderFilter) (*FolderCheck, error) { +func getLocalFolderCheck(path string, filter v1.FolderFilter) (FolderCheck, error) { result := FolderCheck{} _filter, err := filter.New() if err != nil { - return nil, err + return result, err + } + if dir, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return result, nil + } + return result, err + } else if !dir.IsDir() { + return result, fmt.Errorf("%s is not a directory", path) } files, err := os.ReadDir(path) if err != nil { - return nil, err + return result, err } if len(files) == 0 { // directory is empty. returning duration of directory info, err := os.Stat(path) if err != nil { - return nil, err + return result, err } - return &FolderCheck{ + return FolderCheck{ Oldest: newFile(info), Newest: newFile(info), AvailableSize: SizeNotSupported, @@ -126,7 +135,7 @@ func getLocalFolderCheck(path string, filter v1.FolderFilter) (*FolderCheck, err for _, file := range files { info, err := file.Info() if err != nil { - return nil, err + return result, err } if file.IsDir() || !_filter.Filter(info) { continue @@ -134,7 +143,7 @@ func getLocalFolderCheck(path string, filter v1.FolderFilter) (*FolderCheck, err result.Append(info) } - return &result, err + return result, err } func getGenericFolderCheck(fs Filesystem, dir string, filter v1.FolderFilter) (*FolderCheck, error) { diff --git a/checks/folder_check.go b/checks/folder_check.go index 4b3834ec8..3090ac5c1 100644 --- a/checks/folder_check.go +++ b/checks/folder_check.go @@ -21,7 +21,7 @@ type FolderCheck struct { MaxSize *File `json:"largest,omitempty"` TotalSize int64 `json:"size,omitempty"` AvailableSize int64 `json:"availableSize,omitempty"` - Files []File `json:"files,omitempty"` + Files []File `json:"files"` } type File struct { diff --git a/checks/http.go b/checks/http.go index 9a79db811..14d28369d 100644 --- a/checks/http.go +++ b/checks/http.go @@ -86,6 +86,7 @@ func (c *HTTPChecker) generateHTTPRequest(ctx *context.Context, check v1.HTTPChe ClientSecret: connection.Password, TokenURL: check.Oauth2.TokenURL, Scopes: check.Oauth2.Scopes, + Params: check.Oauth2.Params, }) } diff --git a/checks/sql.go b/checks/sql.go index dfa4c648a..fbfbb8c11 100644 --- a/checks/sql.go +++ b/checks/sql.go @@ -19,7 +19,7 @@ type SQLChecker interface { } type SQLDetails struct { - Rows []map[string]interface{} `json:"rows,omitempty"` + Rows []map[string]interface{} `json:"rows"` Count int `json:"count,omitempty"` } @@ -28,23 +28,24 @@ type SQLDetails struct { // Connects to a db using the specified `driver` and `connectionstring` // Performs the test query given in `query`. // Gives the single row test query result as result. -func querySQL(driver string, connection string, query string) (*SQLDetails, error) { +func querySQL(driver string, connection string, query string) (SQLDetails, error) { + result := SQLDetails{} db, err := sql.Open(driver, connection) if err != nil { - return nil, fmt.Errorf("failed to connect to db: %w", err) + return result, fmt.Errorf("failed to connect to db: %w", err) } defer db.Close() rows, err := db.Query(query) - result := SQLDetails{} + if err != nil || rows.Err() != nil { - return nil, fmt.Errorf("failed to query db: %w", err) + return result, fmt.Errorf("failed to query db: %w", err) } defer rows.Close() columns, err := rows.Columns() if err != nil { - return nil, fmt.Errorf("failed to get columns: %w", err) + return result, fmt.Errorf("failed to get columns: %w", err) } for rows.Next() { @@ -54,7 +55,7 @@ func querySQL(driver string, connection string, query string) (*SQLDetails, erro rowValues[i] = &s } if err := rows.Scan(rowValues...); err != nil { - return nil, err + return result, err } var row = make(map[string]interface{}) @@ -71,7 +72,7 @@ func querySQL(driver string, connection string, query string) (*SQLDetails, erro } result.Count = len(result.Rows) - return &result, nil + return result, nil } // CheckSQL : Attempts to connect to a DB using the specified @@ -110,11 +111,12 @@ func CheckSQL(ctx *context.Context, checker SQLChecker) pkg.Results { // nolint: } details, err := querySQL(checker.GetDriver(), connection.URL, query) + result.AddDetails(details) + if err != nil { return results.ErrorMessage(err) } - result.AddDetails(details) if details.Count < check.Result { return results.Failf("Query returned %d rows, expected %d", details.Count, check.Result) } diff --git a/config/deploy/crd.yaml b/config/deploy/crd.yaml index 626843271..3ec8f2651 100644 --- a/config/deploy/crd.yaml +++ b/config/deploy/crd.yaml @@ -3292,6 +3292,10 @@ spec: oauth2: description: Oauth2 Configuration. The client ID & Client secret should go to username & password respectively. properties: + params: + additionalProperties: + type: string + type: object scope: items: type: string diff --git a/config/deploy/manifests.yaml b/config/deploy/manifests.yaml index a9ac1413f..5f0cfa299 100644 --- a/config/deploy/manifests.yaml +++ b/config/deploy/manifests.yaml @@ -3292,6 +3292,10 @@ spec: oauth2: description: Oauth2 Configuration. The client ID & Client secret should go to username & password respectively. properties: + params: + additionalProperties: + type: string + type: object scope: items: type: string diff --git a/config/schemas/canary.schema.json b/config/schemas/canary.schema.json index 88aaacbcf..deb37dc30 100644 --- a/config/schemas/canary.schema.json +++ b/config/schemas/canary.schema.json @@ -2416,6 +2416,14 @@ }, "tokenURL": { "type": "string" + }, + "params": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" } }, "additionalProperties": false, diff --git a/config/schemas/component.schema.json b/config/schemas/component.schema.json index d9a2b74f6..d44b9e67d 100644 --- a/config/schemas/component.schema.json +++ b/config/schemas/component.schema.json @@ -2678,6 +2678,14 @@ }, "tokenURL": { "type": "string" + }, + "params": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" } }, "additionalProperties": false, diff --git a/config/schemas/health_http.schema.json b/config/schemas/health_http.schema.json index 6d4ad7988..aa5eae170 100644 --- a/config/schemas/health_http.schema.json +++ b/config/schemas/health_http.schema.json @@ -226,6 +226,14 @@ }, "tokenURL": { "type": "string" + }, + "params": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" } }, "additionalProperties": false, diff --git a/config/schemas/topology.schema.json b/config/schemas/topology.schema.json index 142d68f32..e625243b0 100644 --- a/config/schemas/topology.schema.json +++ b/config/schemas/topology.schema.json @@ -2648,6 +2648,14 @@ }, "tokenURL": { "type": "string" + }, + "params": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" } }, "additionalProperties": false, diff --git a/fixtures/datasources/folder_pass.yaml b/fixtures/datasources/folder_pass.yaml index 5e7ef73b8..ef824518d 100644 --- a/fixtures/datasources/folder_pass.yaml +++ b/fixtures/datasources/folder_pass.yaml @@ -3,7 +3,7 @@ kind: Canary metadata: name: folder-pass spec: - interval: 10 + interval: 300 folder: - path: /etc/ name: Check for updated /etc files @@ -32,3 +32,15 @@ spec: - name: new_files value: results.?files.orValue([]).size() type: counter +--- +apiVersion: canaries.flanksource.com/v1 +kind: Canary +metadata: + name: folder-pass-empty +spec: + interval: 300 + folder: + - name: folder-nil-handling + path: /some/folder/that/does/not/exist + test: + expr: results.files.size() == 0