diff --git a/api/context/functions.go b/api/context/functions.go index 37e410440..c7552777c 100644 --- a/api/context/functions.go +++ b/api/context/functions.go @@ -18,15 +18,13 @@ func (ctx *Context) GetContextualFunctions() map[string]any { if result, ok := ctx.cache["last_result"]; ok { return result } - var status map[string]any - if checkID == "" { - return status + return nil } if ctx.DB() == nil { logger.Errorf("[last_result] db connection not initialized") - return status + return nil } type CheckStatus struct { @@ -48,10 +46,10 @@ func (ctx *Context) GetContextualFunctions() map[string]any { Order("time DESC").Limit(1).Scan(&checkStatus).Error if err != nil { logger.Warnf("[last_result] failed => %s", err) - return status + return nil } - status = map[string]any{ + lastResult := map[string]any{ "status": checkStatus.Status, "invalid": checkStatus.Invalid, "createdAt": checkStatus.CreatedAt, @@ -64,15 +62,15 @@ func (ctx *Context) GetContextualFunctions() map[string]any { if checkStatus.Details != "" { var details = make(map[string]any) if err := json.Unmarshal([]byte(checkStatus.Details), &details); err == nil { - status["results"] = details + lastResult["results"] = details } else { if ctx.IsTrace() { ctx.Warnf("[last_result] Failed to unmarshal results: %s", err.Error()) } } } - ctx.cache["last_result"] = status - return status + ctx.cache["last_result"] = lastResult + return lastResult } } return funcs diff --git a/checks/alertmanager.go b/checks/alertmanager.go index a1c7aec13..1a6794631 100644 --- a/checks/alertmanager.go +++ b/checks/alertmanager.go @@ -36,12 +36,12 @@ func (c *AlertManagerChecker) Check(ctx *context.Context, extConfig external.Che connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } parsedURL, err := url.Parse(connection.URL) if err != nil { - return results.Failf("error parsing url: %v", err) + return results.Errorf("error parsing url: %v", err) } client := alertmanagerClient.NewHTTPClientWithConfig(nil, &alertmanagerClient.TransportConfig{ Host: parsedURL.Host, @@ -67,8 +67,7 @@ func (c *AlertManagerChecker) Check(ctx *context.Context, extConfig external.Che Filter: filters, }) if err != nil { - results.ErrorMessage(fmt.Errorf("Error fetching from alertmanager: %v", err)) - return results + return results.Errorf("error fetching from alertmanager: %v", err) } type Alerts struct { diff --git a/checks/aws_config.go b/checks/aws_config.go index f3aed003c..a75186bc8 100644 --- a/checks/aws_config.go +++ b/checks/aws_config.go @@ -40,13 +40,13 @@ func (c *AwsConfigChecker) Check(ctx *context.Context, extConfig external.Check) check.AWSConnection = &connection.AWSConnection{} } else { if err := check.AWSConnection.Populate(ctx); err != nil { - return results.Failf("failed to populate aws connection: %v", err) + return results.Errorf("failed to populate aws connection: %v", err) } } cfg, err := awsUtil.NewSession(ctx.Context, *check.AWSConnection) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } client := configservice.NewFromConfig(*cfg) @@ -56,7 +56,7 @@ func (c *AwsConfigChecker) Check(ctx *context.Context, extConfig external.Check) Expression: &check.Query, }) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } result.AddDetails(output.Results) } else { @@ -64,7 +64,7 @@ func (c *AwsConfigChecker) Check(ctx *context.Context, extConfig external.Check) Expression: &check.Query, }) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } result.AddDetails(output.Results) } diff --git a/checks/aws_config_rule.go b/checks/aws_config_rule.go index ee6489f35..6dfb60b7d 100644 --- a/checks/aws_config_rule.go +++ b/checks/aws_config_rule.go @@ -42,19 +42,15 @@ func (c *AwsConfigRuleChecker) Check(ctx *context.Context, extConfig external.Ch if check.AWSConnection == nil { check.AWSConnection = &connection.AWSConnection{} } else if err := check.AWSConnection.Populate(ctx); err != nil { - return results.Failf("failed to populate aws connection: %v", err) + return results.Errorf("failed to populate aws connection: %v", err) } cfg, err := awsUtil.NewSession(ctx.Context, *check.AWSConnection) if err != nil { - return results.Failf("failed to create a session: %v", err) + return results.Errorf("failed to create a session: %v", err) } client := configservice.NewFromConfig(*cfg) - if err != nil { - return results.Failf("failed to describe compliance rules: %v", err) - } - var complianceTypes = []types.ComplianceType{} for _, i := range check.ComplianceTypes { complianceTypes = append(complianceTypes, types.ComplianceType(i)) @@ -64,7 +60,7 @@ func (c *AwsConfigRuleChecker) Check(ctx *context.Context, extConfig external.Ch ConfigRuleNames: check.Rules, }) if err != nil { - return results.Failf("failed to describe compliance rules: %v", err) + return results.Errorf("failed to describe compliance rules: %v", err) } type ConfigRuleResource struct { diff --git a/checks/azure_devops.go b/checks/azure_devops.go index 2e96a0344..fe772c903 100644 --- a/checks/azure_devops.go +++ b/checks/azure_devops.go @@ -41,22 +41,22 @@ func (t *AzureDevopsChecker) check(ctx *context.Context, check v1.AzureDevopsChe if check.PersonalAccessToken.ValueStatic != "" { c = &models.Connection{Password: check.PersonalAccessToken.ValueStatic} } else if c, err = ctx.HydrateConnectionByURL(check.ConnectionName); err != nil { - return results.Failf("failed to hydrate connection: %v", err) + return results.Errorf("failed to hydrate connection: %v", err) } else if c != nil { if c, err = c.Merge(ctx, check); err != nil { - return results.Failf("failed to merge connection: %v", err) + return results.Errorf("failed to merge connection: %v", err) } } connection := azuredevops.NewPatConnection(fmt.Sprintf("https://dev.azure.com/%s", check.Organization), c.Password) coreClient, err := core.NewClient(ctx, connection) if err != nil { - return results.ErrorMessage(fmt.Errorf("failed to create core client: %w", err)) + return results.Errorf("failed to create core client: %v", err) } project, err := coreClient.GetProject(ctx, core.GetProjectArgs{ProjectId: &check.Project}) if err != nil { - return results.ErrorMessage(fmt.Errorf("failed to get project (name=%s): %w", check.Project, err)) + return results.Errorf("failed to get project (name=%s): %v", check.Project, err) } projectID := project.Id.String() @@ -68,7 +68,7 @@ func (t *AzureDevopsChecker) check(ctx *context.Context, check v1.AzureDevopsChe pipelineClient := pipelines.NewClient(ctx, connection) allPipelines, err := pipelineClient.ListPipelines(ctx, pipelines.ListPipelinesArgs{Project: &projectID}) if err != nil { - return results.ErrorMessage(fmt.Errorf("failed to get pipeline (project=%s): %w", check.Project, err)) + return results.Errorf("failed to get pipeline (project=%s): %v", check.Project, err) } for _, pipeline := range *allPipelines { @@ -89,7 +89,7 @@ func (t *AzureDevopsChecker) check(ctx *context.Context, check v1.AzureDevopsChe // https://learn.microsoft.com/en-us/rest/api/azure/devops/pipelines/runs/list?view=azure-devops-rest-7.1 runs, err := pipelineClient.ListRuns(ctx, pipelines.ListRunsArgs{PipelineId: pipeline.Id, Project: &projectID}) if err != nil { - return results.ErrorMessage(fmt.Errorf("failed to get runs (pipeline=%s): %w", check.Pipeline, err)) + return results.Errorf("failed to get runs (pipeline=%s): %v", check.Pipeline, err) } latestRun := getLatestCompletedRun(*runs) @@ -105,7 +105,7 @@ func (t *AzureDevopsChecker) check(ctx *context.Context, check v1.AzureDevopsChe // because the ListRuns API doesn't return Resources. latestRun, err = pipelineClient.GetRun(ctx, pipelines.GetRunArgs{Project: &projectID, PipelineId: pipeline.Id, RunId: (*runs)[0].Id}) if err != nil { - return results.ErrorMessage(fmt.Errorf("failed to get run (pipeline=%s): %w", check.Pipeline, err)) + return results.Errorf("failed to get run (pipeline=%s): %v", check.Pipeline, err) } if !matchBranchNames(check.Branches, latestRun.Resources) { diff --git a/checks/catalog.go b/checks/catalog.go index 46fa5e2e2..0aa428de7 100644 --- a/checks/catalog.go +++ b/checks/catalog.go @@ -30,7 +30,7 @@ func (c *CatalogChecker) Check(ctx *canaryContext.Context, check v1.CatalogCheck items, err := query.FindConfigsByResourceSelector(ctx.Context, check.Selector...) if err != nil { - return results.Failf("failed to fetch catalogs: %v", err) + return results.Errorf("failed to fetch catalogs: %v", err) } var configItems []map[string]any diff --git a/checks/cloudwatch.go b/checks/cloudwatch.go index e3c71d211..2f44c0d2f 100644 --- a/checks/cloudwatch.go +++ b/checks/cloudwatch.go @@ -39,12 +39,12 @@ func (c *CloudWatchChecker) Check(ctx *context.Context, extConfig external.Check results = append(results, result) if err := check.AWSConnection.Populate(ctx); err != nil { - return results.Failf("failed to populate aws connection: %v", err) + return results.Errorf("failed to populate aws connection: %v", err) } cfg, err := awsUtil.NewSession(ctx.Context, check.AWSConnection) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } client := cloudwatch.NewFromConfig(*cfg) maxRecords := int32(100) @@ -56,10 +56,10 @@ func (c *CloudWatchChecker) Check(ctx *context.Context, extConfig external.Check MaxRecords: &maxRecords, }) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if o, err := unstructure(alarms); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } else { result.AddDetails(o) } diff --git a/checks/database_backup.go b/checks/database_backup.go index 6ac541474..9029cdf41 100644 --- a/checks/database_backup.go +++ b/checks/database_backup.go @@ -1,7 +1,6 @@ package checks import ( - "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/flanksource/canary-checker/api/context" @@ -60,5 +59,5 @@ func FailDatabaseBackupParse(ctx *context.Context, check v1.DatabaseBackupCheck) result := pkg.Fail(check, ctx.Canary) var results pkg.Results results = append(results, result) - return results.ErrorMessage(errors.New("Could not parse databaseBackup input")) + return results.Errorf("Could not parse databaseBackup input") } diff --git a/checks/database_backup_gcp.go b/checks/database_backup_gcp.go index 642c1cd66..ced70e7b8 100644 --- a/checks/database_backup_gcp.go +++ b/checks/database_backup_gcp.go @@ -1,7 +1,6 @@ package checks import ( - "errors" "fmt" "strings" "time" @@ -30,13 +29,13 @@ func GCPDatabaseBackupCheck(ctx *context.Context, check v1.DatabaseBackupCheck) results = append(results, result) if err := check.GCP.HydrateConnection(ctx); err != nil { - return results.Failf("failed to populate GCP connection: %v", err) + return results.Errorf("failed to populate GCP connection: %v", err) } svc, err := gcp.NewSQLAdmin(ctx.Context, check.GCP.GCPConnection) if err != nil { databaseScanFailCount.WithLabelValues(check.GCP.Project, check.GCP.Instance).Inc() - return results.ErrorMessage(err) + return results.Error(err) } // Only checking one backup for now, but setting up the logic that this could maybe be configurable. @@ -44,7 +43,7 @@ func GCPDatabaseBackupCheck(ctx *context.Context, check v1.DatabaseBackupCheck) backupList, err := svc.BackupRuns.List(check.GCP.Project, check.GCP.Instance).MaxResults(1).Do() if err != nil { databaseScanFailCount.WithLabelValues(check.GCP.Project, check.GCP.Instance).Inc() - return results.ErrorMessage(err) + return results.Error(err) } var errorMessages []string for _, backup := range backupList.Items { @@ -103,7 +102,7 @@ func GCPDatabaseBackupCheck(ctx *context.Context, check v1.DatabaseBackupCheck) } if len(errorMessages) > 0 { databaseScanFailCount.WithLabelValues(check.GCP.Project, check.GCP.Instance).Inc() - return results.ErrorMessage(errors.New(strings.Join(errorMessages, ", "))) + return results.Errorf(strings.Join(errorMessages, ", ")) } backupRaw, err := backupList.Items[0].MarshalJSON() diff --git a/checks/dns.go b/checks/dns.go index 8dd9c6d88..c65bbebd7 100644 --- a/checks/dns.go +++ b/checks/dns.go @@ -54,7 +54,7 @@ func (c *DNSChecker) Check(ctx *canaryContext.Context, extConfig external.Check) if check.Server != "" { dialer, err := getDialer(check, timeout) if err != nil { - return results.Failf("Failed to get dialer, %v", err) + return results.Errorf("Failed to get dialer, %v", err) } r = net.Resolver{ PreferGo: true, @@ -71,7 +71,7 @@ func (c *DNSChecker) Check(ctx *canaryContext.Context, extConfig external.Check) resultCh := make(chan *pkg.CheckResult, 1) if fn, ok := resolvers[strings.ToUpper(queryType)]; !ok { - return results.Failf("unknown query type: %s", queryType) + return results.Errorf("unknown query type: %s", queryType) } else { go func() { pass, message, err := fn(ctx, &r, check) diff --git a/checks/dynatrace.go b/checks/dynatrace.go index 23d7399d6..7e54d8edc 100644 --- a/checks/dynatrace.go +++ b/checks/dynatrace.go @@ -33,7 +33,7 @@ func (t *DynatraceChecker) Check(ctx *context.Context, extConfig external.Check) apiKey, err := ctx.GetEnvValueFromCache(check.APIKey) if err != nil { - return results.Failf("error getting Dynatrace API key: %v", err) + return results.Errorf("error getting Dynatrace API key: %v", err) } config := dynatrace.NewConfiguration() @@ -46,7 +46,7 @@ func (t *DynatraceChecker) Check(ctx *context.Context, extConfig external.Check) apiClient := dynatrace.NewAPIClient(config) problems, apiResponse, err := apiClient.ProblemsApi.GetProblems(ctx).Execute() if err != nil { - return results.Failf("error getting Dynatrace problems: %s", err.Error()) + return results.Errorf("error getting Dynatrace problems: %s", err.Error()) } defer apiResponse.Body.Close() diff --git a/checks/elasticsearch.go b/checks/elasticsearch.go index b9b8b551e..3657a277d 100644 --- a/checks/elasticsearch.go +++ b/checks/elasticsearch.go @@ -2,7 +2,6 @@ package checks import ( "encoding/json" - "fmt" "strings" "github.com/flanksource/canary-checker/api/context" @@ -36,7 +35,7 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } cfg := elasticsearch.Config{ @@ -47,7 +46,7 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch es, err := elasticsearch.NewClient(cfg) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } body := strings.NewReader(check.Query) @@ -58,21 +57,19 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch ) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if res.IsError() { var e map[string]any if err := json.NewDecoder(res.Body).Decode(&e); err != nil { - return results.ErrorMessage( - fmt.Errorf("Error parsing the response body: %s", err), - ) + return results.Errorf("error parsing the response body: %s", err) } else { - return results.ErrorMessage(fmt.Errorf("Error from elasticsearch [%s]: %v, %v", + return results.Errorf("error from elasticsearch [%s]: %v, %v", res.Status(), e["error"].(map[string]any)["type"], e["error"].(map[string]any)["reason"], - )) + ) } } @@ -81,9 +78,7 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch defer res.Body.Close() var r map[string]any if err := json.NewDecoder(res.Body).Decode(&r); err != nil { - return results.ErrorMessage( - fmt.Errorf("Error parsing the response body: %s", err), - ) + return results.Errorf("Error parsing the response body: %s", err) } count := int(r["hits"].(map[string]any)["total"].(map[string]any)["value"].(float64)) diff --git a/checks/exec.go b/checks/exec.go index 4d4582ac3..03842d3f9 100644 --- a/checks/exec.go +++ b/checks/exec.go @@ -115,11 +115,14 @@ func (c *ExecChecker) prepareEnvironment(ctx *context.Context, check v1.ExecChec } func (c *ExecChecker) Check(ctx *context.Context, extConfig external.Check) pkg.Results { + var results pkg.Results + results = append(results, pkg.Success(extConfig, ctx.Canary)) + check := extConfig.(v1.ExecCheck) env, err := c.prepareEnvironment(ctx, check) if err != nil { - return pkg.New(check, ctx.Canary).AddDetails(ExecDetails{}).Invalidf(err.Error()) + return results.Error(err) } switch runtime.GOOS { @@ -132,9 +135,13 @@ func (c *ExecChecker) Check(ctx *context.Context, extConfig external.Check) pkg. func execPowershell(ctx *context.Context, check v1.ExecCheck, envParams *execEnv) pkg.Results { result := pkg.Success(check, ctx.Canary).AddDetails(ExecDetails{ExitCode: -1}) + + var results pkg.Results + results = append(results, result) + ps, err := exec.LookPath("powershell.exe") if err != nil { - result.Failf("powershell not found") + return results.Errorf("powershell not found") } args := []string{check.Script} @@ -150,10 +157,13 @@ func execPowershell(ctx *context.Context, check v1.ExecCheck, envParams *execEnv } func execBash(ctx *context.Context, check v1.ExecCheck, envParams *execEnv) pkg.Results { + var results pkg.Results + results = append(results, pkg.Success(check, ctx.Canary)) + result := pkg.Success(check, ctx.Canary).AddDetails(ExecDetails{ExitCode: -1}) fields := strings.Fields(check.Script) if len(fields) == 0 { - return result.Invalidf("no script provided") + return results.Invalidf("no script provided") } cmd := exec.CommandContext(ctx, "bash", "-c", check.Script) @@ -165,7 +175,7 @@ func execBash(ctx *context.Context, check v1.ExecCheck, envParams *execEnv) pkg. } if err := setupConnection(ctx, check, cmd); err != nil { - return result.Invalidf("failed to setup connection: %v", err) + return results.Errorf("failed to setup connection: %v", err) } return checkCmd(ctx, check, cmd, result) diff --git a/checks/folder.go b/checks/folder.go index e0e918c45..b2e50da3d 100644 --- a/checks/folder.go +++ b/checks/folder.go @@ -95,14 +95,14 @@ func checkLocalFolder(ctx *context.Context, check v1.FolderCheck) pkg.Results { // Form a dummy connection to get a local filesystem localFS, err := artifacts.GetFSForConnection(ctx.Context, models.Connection{Type: models.ConnectionTypeFolder}) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } folders, err := genericFolderCheck(localFS, check.Path, check.Recursive, check.Filter) result.AddDetails(folders) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if test := folders.Test(check.FolderTest); test != "" { diff --git a/checks/folder_gcs.go b/checks/folder_gcs.go index 7ec668885..e34fad54b 100644 --- a/checks/folder_gcs.go +++ b/checks/folder_gcs.go @@ -1,7 +1,6 @@ package checks import ( - "errors" "strings" gcs "cloud.google.com/go/storage" @@ -23,7 +22,7 @@ func CheckGCSBucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { results = append(results, result) if check.GCSConnection == nil { - return results.ErrorMessage(errors.New("missing GCS connection")) + return results.Invalidf("missing GCS connection") } var bucket string @@ -31,7 +30,7 @@ func CheckGCSBucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { connection, err := ctx.HydrateConnectionByURL(check.GCPConnection.ConnectionName) if err != nil { - return results.Failf("failed to populate GCS connection: %v", err) + return results.Errorf("failed to populate GCS connection: %v", err) } else if connection == nil { connection = &models.Connection{Type: models.ConnectionTypeGCS} if check.GCSConnection.Bucket == "" { @@ -40,18 +39,18 @@ func CheckGCSBucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { connection, err = connection.Merge(ctx, check.GCSConnection) if err != nil { - return results.Failf("failed to populate GCS connection: %v", err) + return results.Errorf("failed to populate GCS connection: %v", err) } } fs, err := artifacts.GetFSForConnection(ctx.Context, *connection) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } folders, err := genericFolderCheckWithoutPrecheck(fs, check.Path, check.Recursive, check.Filter) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } result.AddDetails(folders) diff --git a/checks/folder_s3.go b/checks/folder_s3.go index 562103747..563e1919e 100644 --- a/checks/folder_s3.go +++ b/checks/folder_s3.go @@ -3,7 +3,6 @@ package checks import ( - "errors" "strings" "github.com/aws/aws-sdk-go-v2/service/s3" @@ -25,7 +24,7 @@ func CheckS3Bucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { results = append(results, result) if check.S3Connection == nil { - return results.ErrorMessage(errors.New("missing AWS connection")) + return results.Errorf("missing AWS connection") } var bucket string @@ -33,7 +32,7 @@ func CheckS3Bucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { connection, err := ctx.HydrateConnectionByURL(check.AWSConnection.ConnectionName) if err != nil { - return results.Failf("failed to populate AWS connection: %v", err) + return results.Errorf("failed to populate AWS connection: %v", err) } else if connection == nil { connection = &models.Connection{Type: models.ConnectionTypeS3} if check.S3Connection.Bucket == "" { @@ -42,18 +41,18 @@ func CheckS3Bucket(ctx *context.Context, check v1.FolderCheck) pkg.Results { connection, err = connection.Merge(ctx, check.S3Connection) if err != nil { - return results.Failf("failed to populate AWS connection: %v", err) + return results.Errorf("failed to populate AWS connection: %v", err) } } fs, err := artifacts.GetFSForConnection(ctx.Context, *connection) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } folders, err := genericFolderCheckWithoutPrecheck(fs, check.Path, check.Recursive, check.Filter) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } result.AddDetails(folders) diff --git a/checks/folder_sftp.go b/checks/folder_sftp.go index eb315baf0..2a2855ff9 100644 --- a/checks/folder_sftp.go +++ b/checks/folder_sftp.go @@ -18,27 +18,27 @@ func CheckSFTP(ctx *context.Context, check v1.FolderCheck) pkg.Results { foundConn, err := check.SFTPConnection.HydrateConnection(ctx) if err != nil { - return results.Failf("failed to populate SFTP connection: %v", err) + return results.Errorf("failed to populate SFTP connection: %v", err) } auth := check.SFTPConnection.Authentication if !foundConn { auth, err = ctx.GetAuthValues(check.SFTPConnection.Authentication) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } } client, err := sftp.SSHConnect(fmt.Sprintf("%s:%d", check.SFTPConnection.Host, check.SFTPConnection.GetPort()), auth.GetUsername(), auth.GetPassword()) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } defer client.Close() session := artifacts.Filesystem(client) folders, err := genericFolderCheck(session, check.Path, check.Recursive, check.Filter) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } result.AddDetails(folders) diff --git a/checks/folder_smb.go b/checks/folder_smb.go index ded278ec3..6adcd5777 100644 --- a/checks/folder_smb.go +++ b/checks/folder_smb.go @@ -18,25 +18,25 @@ func CheckSmb(ctx *context.Context, check v1.FolderCheck) pkg.Results { var serverPath = strings.TrimPrefix(check.Path, "smb://") server, sharename, path, err := extractServerDetails(serverPath) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } foundConn, err := check.SMBConnection.HydrateConnection(ctx) if err != nil { - return results.Failf("failed to populate SMB connection: %v", err) + return results.Errorf("failed to populate SMB connection: %v", err) } auth := check.SMBConnection.Authentication if !foundConn { auth, err = ctx.GetAuthValues(check.SMBConnection.Authentication) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } } session, err := smb.SMBConnect(server, fmt.Sprintf("%d", check.SMBConnection.GetPort()), sharename, auth) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if session != nil { defer session.Close() @@ -44,7 +44,7 @@ func CheckSmb(ctx *context.Context, check v1.FolderCheck) pkg.Results { folders, err := genericFolderCheck(session, path, check.Recursive, check.Filter) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } var totalBlockCount, freeBlockCount, blockSize int // TODO: diff --git a/checks/git_protocol.go b/checks/git_protocol.go index 047b06e02..ad1ddbe2e 100644 --- a/checks/git_protocol.go +++ b/checks/git_protocol.go @@ -105,12 +105,12 @@ func (c *GitProtocolChecker) Check(ctx *context.Context, extConfig external.Chec // Fetching Git Username username, err := ctx.GetEnvValueFromCache(check.Username) if err != nil { - return results.Failf("error fetching git user from env cache: %v", err) + return results.Errorf("error fetching git user from env cache: %v", err) } // Fetching Git Password password, err := ctx.GetEnvValueFromCache(check.Password) if err != nil { - return results.Failf("error fetching git password from env cache: %v", err) + return results.Errorf("error fetching git password from env cache: %v", err) } if len(filename) == 0 { @@ -119,7 +119,7 @@ func (c *GitProtocolChecker) Check(ctx *context.Context, extConfig external.Chec // Push Changes if err := pushChanges(check.Repository, username, password, filename); err != nil { - return results.Failf("error pushing changes: %v", err) + return results.Errorf("error pushing changes: %v", err) } details := map[string]string{ diff --git a/checks/github.go b/checks/github.go index 9518d7a05..e91c489eb 100644 --- a/checks/github.go +++ b/checks/github.go @@ -38,13 +38,13 @@ func (c *GitHubChecker) Check(ctx *context.Context, extConfig external.Check) pk var githubToken string if connection, err := ctx.HydrateConnectionByURL(check.ConnectionName); err != nil { - return results.Failf("failed to find connection for github token %q: %v", check.ConnectionName, err) + return results.Errorf("failed to find connection for github token %q: %v", check.ConnectionName, err) } else if connection != nil { githubToken = connection.Password } else { githubToken, err = ctx.GetEnvValueFromCache(check.GithubToken) if err != nil { - return results.Failf("error fetching github token from env cache: %v", err) + return results.Errorf("error fetching github token from env cache: %v", err) } } @@ -56,13 +56,13 @@ func (c *GitHubChecker) Check(ctx *context.Context, extConfig external.Check) pk cmd.Env = append(cmd.Env, "GITHUB_TOKEN="+githubToken) output, err := cmd.CombinedOutput() if err != nil { - return results.Failf("error executing askgit command. output=%q: %v", output, err) + return results.Errorf("error executing askgit command. output=%q: %v", output, err) } var rowResults = make([]map[string]any, 0) err = json.Unmarshal(output, &rowResults) if err != nil { - return results.Failf("error parsing mergestat result: %v", err) + return results.Errorf("error parsing mergestat result: %v", err) } result.AddDetails(rowResults) diff --git a/checks/http.go b/checks/http.go index 972b44172..800454e8b 100644 --- a/checks/http.go +++ b/checks/http.go @@ -13,7 +13,6 @@ import ( "github.com/flanksource/commons/http/middlewares" "github.com/flanksource/duty/models" - "github.com/flanksource/canary-checker/api/external" "github.com/prometheus/client_golang/prometheus" v1 "github.com/flanksource/canary-checker/api/v1" @@ -115,16 +114,14 @@ func truncate(text string, max int) string { return text[0:max] } -func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg.Results { - check := extConfig.(v1.HTTPCheck) +func (c *HTTPChecker) Check(ctx *context.Context, check v1.HTTPCheck) pkg.Results { var results pkg.Results - var err error result := pkg.Success(check, ctx.Canary) results = append(results, result) //nolint:staticcheck if check.Endpoint != "" && check.URL != "" { - return results.Failf("cannot specify both endpoint and url") + return results.Invalidf("cannot specify both endpoint and url") } //nolint:staticcheck @@ -134,11 +131,17 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection %v", err) + return results.Errorf("error getting connection %v", err) } if connection.URL == "" { - return results.Failf("no url or connection specified") + return results.Invalidf("no url or connection specified") + } + + if parsed, err := url.Parse(connection.URL); err != nil { + return results.Invalidf("failed to parse url: %v", err) + } else if parsed.Scheme == "" { + return results.Invalidf("missing scheme in url") } if ntlm, ok := connection.Properties["ntlm"]; ok { @@ -147,14 +150,10 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. check.NTLMv2 = ntlm == "true" } - if _, err := url.Parse(connection.URL); err != nil { - return results.Failf("failed to parse url: %v", err) - } - templateEnv := map[string]any{} for _, env := range check.EnvVars { if val, err := ctx.GetEnvValueFromCache(env); err != nil { - return results.Failf("failed to get env value: %v", err) + return results.Errorf("failed to get env value: %v", err) } else { templateEnv[env.Name] = val } @@ -162,25 +161,25 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. check.URL, err = template(ctx.WithCheck(check).WithEnvValues(templateEnv), v1.Template{Template: check.URL}) if err != nil { - return results.Failf("failed to template request url: %v", err) + return results.Errorf("failed to template request url: %v", err) } body := check.Body if check.TemplateBody { body, err = template(ctx.WithCheck(check).WithEnvValues(templateEnv), v1.Template{Template: body}) if err != nil { - return results.Failf("failed to template request body: %v", err) + return results.Errorf("failed to template request body: %v", err) } } request, err := c.generateHTTPRequest(ctx, check, connection) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if body != "" { if err := request.Body(body); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } } @@ -188,7 +187,7 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. response, err := request.Do(check.GetMethod(), connection.URL) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } elapsed := time.Since(start) @@ -220,7 +219,7 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. responseBody, err := response.AsString() if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } data["content"] = responseBody @@ -229,7 +228,7 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. if err := json.Unmarshal([]byte(responseBody), &jsonContent); err == nil { data["json"] = jsonContent } else if check.Test.IsEmpty() { - return results.Failf("invalid json response: %v", err) + return results.Errorf("invalid json response: %v", err) } else { ctx.Tracef("ignoring invalid json response %v", err) } @@ -242,7 +241,11 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. } if ok := response.IsOK(check.ResponseCodes...); !ok { - return results.Failf("response code invalid %d != %v", status, check.ResponseCodes) + if len(check.ResponseCodes) > 0 { + return results.Failf("response code invalid. %d != %v", status, check.ResponseCodes) + } else { + return results.Failf("response code invalid. %d is not 2xx", status) + } } if check.ThresholdMillis > 0 && check.ThresholdMillis < int(elapsed.Milliseconds()) { diff --git a/checks/icmp.go b/checks/icmp.go index 691b81d67..5d846a8b0 100644 --- a/checks/icmp.go +++ b/checks/icmp.go @@ -60,13 +60,13 @@ func (c *IcmpChecker) Check(ctx *context.Context, extConfig external.Check) pkg. endpoint := check.Endpoint ips, err := dns.Lookup("A", endpoint) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } for _, urlObj := range ips { pingerStats, err := c.checkICMP(urlObj, check.PacketCount) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if pingerStats.PacketsSent == 0 { return results.Failf("Failed to check icmp, no packets sent") diff --git a/checks/jmeter.go b/checks/jmeter.go index 44b4c8531..b7542fa97 100644 --- a/checks/jmeter.go +++ b/checks/jmeter.go @@ -44,7 +44,7 @@ func (c *JmeterChecker) Check(ctx *context.Context, extConfig external.Check) pk //FIXME: the jmx file should not be cached value, err := ctx.GetEnvValueFromCache(check.Jmx) if err != nil { - return results.Failf("Failed to parse the jmx plan: %v", err) + return results.Errorf("Failed to parse the jmx plan: %v", err) } testPlanFilename := fmt.Sprintf("/tmp/jmx-%s-%s-%d.jmx", namespace, check.Jmx.Name, rand.Int()) @@ -52,7 +52,7 @@ func (c *JmeterChecker) Check(ctx *context.Context, extConfig external.Check) pk err = os.WriteFile(testPlanFilename, []byte(value), 0755) defer os.Remove(testPlanFilename) // nolint: errcheck if err != nil { - return results.Failf("unable to write test plan file") + return results.Errorf("unable to write test plan file") } var host string @@ -67,21 +67,21 @@ func (c *JmeterChecker) Check(ctx *context.Context, extConfig external.Check) pk _, ok := exec.SafeExec(jmeterCmd) defer os.Remove(logFilename) // nolint: errcheck if !ok { - return results.Failf("error running the jmeter command: %v", jmeterCmd) + return results.Errorf("error running the jmeter command: %v", jmeterCmd) } raw, err := os.ReadFile(logFilename) if err != nil { - return results.Failf("error opening the log file: %v", err) + return results.Errorf("error opening the log file: %v", err) } elapsedTime, err := checkLogs(raw) if err != nil { - return results.Failf("check failed: %v", err) + return results.Errorf("check failed: %v", err) } totalDuration := time.Duration(elapsedTime) * time.Millisecond if check.ResponseDuration != "" { resDuration, err := time.ParseDuration(check.ResponseDuration) if err != nil { - return results.Failf("error parsing response duration: %v", err) + return results.Errorf("error parsing response duration: %v", err) } if totalDuration > resDuration { return results.Failf("the response took %v longer than specified", (totalDuration - resDuration).String()) diff --git a/checks/junit.go b/checks/junit.go index 89095ccc9..899999c3c 100644 --- a/checks/junit.go +++ b/checks/junit.go @@ -144,14 +144,14 @@ func podExecf(ctx *context.Context, pod corev1.Pod, results pkg.Results, cmd str _cmd := fmt.Sprintf(cmd, args...) stdout, stderr, err := ctx.Kommons().ExecutePodf(pod.Namespace, pod.Name, containerName, "bash", "-c", _cmd) if stderr != "" || err != nil { - podFail(ctx, pod, results.Failf("error running %s: %v %v %v", _cmd, stdout, stderr, err)) + podFail(ctx, pod, results.Errorf("error running %s: %v %v %v", _cmd, stdout, stderr, err)) return "", false } return strings.TrimSpace(stdout), true } func podFail(ctx *context.Context, pod corev1.Pod, results pkg.Results) pkg.Results { - return results.ErrorMessage(fmt.Errorf("%s is %s\n %v", pod.Name, pod.Status.Phase, getLogs(ctx, pod))) + return results.Errorf("%s is %s\n %v", pod.Name, pod.Status.Phase, getLogs(ctx, pod)) } func cleanupExistingPods(ctx *context.Context, k8s kubernetes.Interface, selector string) (bool, error) { @@ -191,25 +191,25 @@ func (c *JunitChecker) Check(ctx *context.Context, extConfig external.Check) pkg results = append(results, result) if ctx.Kommons() == nil { - return results.Failf("Kubernetes is not initialized") + return results.Errorf("Kubernetes is not initialized") } k8s := ctx.Kubernetes() timeout := time.Duration(check.GetTimeout()) * time.Minute pod, err := newPod(ctx, check) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } pods := k8s.CoreV1().Pods(ctx.Namespace) if skip, err := cleanupExistingPods(ctx, k8s, fmt.Sprintf("%s=%s", junitCheckSelector, pod.Labels[junitCheckSelector])); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } else if skip { return nil } if _, err := k8s.CoreV1().Pods(ctx.Namespace).Create(ctx, pod, metav1.CreateOptions{}); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } defer deletePod(ctx, pod) @@ -229,7 +229,7 @@ func (c *JunitChecker) Check(ctx *context.Context, extConfig external.Check) pkg podObj, err := pods.Get(ctx, pod.Name, metav1.GetOptions{}) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if !kommons.IsPodHealthy(*podObj) { @@ -257,7 +257,7 @@ func (c *JunitChecker) Check(ctx *context.Context, extConfig external.Check) pkg return results } if suites, err = suites.Ingest(output); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } } diff --git a/checks/kubernetes.go b/checks/kubernetes.go index 1e003aade..9064ce686 100644 --- a/checks/kubernetes.go +++ b/checks/kubernetes.go @@ -42,13 +42,13 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check if check.KubeConfig != nil { val, err := ctx.GetEnvValueFromCache(*check.KubeConfig) if err != nil { - return results.Failf("failed to get kubeconfig from env: %v", err) + return results.Errorf("failed to get kubeconfig from env: %v", err) } if strings.HasPrefix(val, "/") { kClient, kube, err := pkg.NewKommonsClientWithConfigPath(val) if err != nil { - return results.Failf("failed to initialize kubernetes client from the provided kubeconfig: %v", err) + return results.Errorf("failed to initialize kubernetes client from the provided kubeconfig: %v", err) } ctx = ctx.WithDutyContext(ctx.WithKommons(kClient)) @@ -56,7 +56,7 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check } else { kClient, kube, err := pkg.NewKommonsClientWithConfig(val) if err != nil { - return results.Failf("failed to initialize kubernetes client from the provided kubeconfig: %v", err) + return results.Errorf("failed to initialize kubernetes client from the provided kubeconfig: %v", err) } ctx = ctx.WithDutyContext(ctx.WithKommons(kClient)) @@ -65,29 +65,29 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check } if ctx.Kommons() == nil { - return results.Failf("Kubernetes is not initialized") + return results.Errorf("Kubernetes is not initialized") } client, err := ctx.Kommons().GetClientByKind(check.Kind) if err != nil { - return results.Failf("Failed to get client for kind %s: %v", check.Kind, err) + return results.Errorf("Failed to get client for kind %s: %v", check.Kind, err) } namespaces, err := getNamespaces(ctx, check) if err != nil { - return results.Failf("Failed to get namespaces: %v", err) + return results.Errorf("Failed to get namespaces: %v", err) } var allResources []unstructured.Unstructured for _, namespace := range namespaces { resources, err := getResourcesFromNamespace(ctx, client, check, namespace) if err != nil { - return results.Failf("failed to get resources: %v. namespace: %v", err, namespace) + return results.Errorf("failed to get resources: %v. namespace: %v", err, namespace) } for _, filter := range check.Ignore { resources, err = filterResources(resources, filter) if err != nil { - results.Failf("failed to filter resources: %v. filter: %v", err, filter) + results.Errorf("failed to filter resources: %v. filter: %v", err, filter) return results } } @@ -97,7 +97,7 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check _resource := resource resourceHealth, err := health.GetResourceHealth(&_resource, nil) if err != nil { - results.Failf("error getting resource health (%s/%s/%s): %v", + results.Errorf("error getting resource health (%s/%s/%s): %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) } else { resource.Object["healthStatus"] = resourceHealth diff --git a/checks/kubernetes_resource.go b/checks/kubernetes_resource.go index b3f8f1a3f..b41ffa12b 100644 --- a/checks/kubernetes_resource.go +++ b/checks/kubernetes_resource.go @@ -63,49 +63,49 @@ func (c *KubernetesResourceChecker) Check(ctx *context.Context, check v1.Kuberne results = append(results, result) if err := c.validate(ctx, check); err != nil { - return results.Failf("validation: %v", err) + return results.Invalidf("validation: %v", err) } if check.Kubeconfig != nil { ctx, err = c.applyKubeconfig(ctx, *check.Kubeconfig) if err != nil { - return results.Failf("failed to apply kube config: %v", err) + return results.Errorf("failed to apply kube config: %v", err) } } if err := templateKubernetesResourceCheck(ctx.Canary.GetPersistedID(), ctx.Canary.GetCheckID(check.GetName()), &check); err != nil { - return results.Failf("templating error: %v", err) + return results.Errorf("templating error: %v", err) } for i := range check.StaticResources { resource := check.StaticResources[i] if err := ctx.Kommons().ApplyUnstructured(utils.Coalesce(resource.GetNamespace(), ctx.Namespace), &resource); err != nil { - return results.Failf("failed to apply static resource %s: %v", resource.GetName(), err) + return results.Errorf("failed to apply static resource %s: %v", resource.GetName(), err) } } defer func() { if err := DeleteResources(ctx, check, false); err != nil { - results.Failf(err.Error()) + results.Errorf(err.Error()) } }() if check.ClearResources { if err := DeleteResources(ctx, check, false); err != nil { - results.Failf(err.Error()) + results.Errorf(err.Error()) } } for i := range check.Resources { resource := check.Resources[i] if err := ctx.Kommons().ApplyUnstructured(utils.Coalesce(resource.GetNamespace(), ctx.Namespace), &resource); err != nil { - return results.Failf("failed to apply resource (%s/%s/%s): %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) + return results.Errorf("failed to apply resource (%s/%s/%s): %v", resource.GetKind(), resource.GetNamespace(), resource.GetName(), err) } } if !check.WaitFor.Disable { if err := c.evalWaitFor(ctx, check); err != nil { - return results.Failf("%v", err) + return results.Errorf("%v", err) } } @@ -128,7 +128,7 @@ func (c *KubernetesResourceChecker) Check(ctx *context.Context, check v1.Kuberne }, } if err := templater.Walk(&virtualCanary); err != nil { - return results.Failf("error templating checks: %v", err) + return results.Errorf("error templating checks: %v", err) } if wt, _ := check.CheckRetries.GetDelay(); wt > 0 { @@ -171,7 +171,7 @@ func (c *KubernetesResourceChecker) Check(ctx *context.Context, check v1.Kuberne return nil }) if retryErr != nil { - return results.Failf(retryErr.Error()) + return results.Errorf(retryErr.Error()) } } diff --git a/checks/ldap.go b/checks/ldap.go index 50cdae424..c75c27164 100644 --- a/checks/ldap.go +++ b/checks/ldap.go @@ -40,11 +40,11 @@ func (c *LdapChecker) Check(ctx *context.Context, extConfig external.Check) pkg. connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("failed to get connection: %v", err) + return results.Errorf("failed to get connection: %v", err) } if connection.URL == "" { - return results.Failf("Must specify a connection or URL") + return results.Invalidf("Must specify a connection or URL") } ld, err := ldap.DialURL(connection.URL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: check.SkipTLSVerify})) @@ -63,7 +63,7 @@ func (c *LdapChecker) Check(ctx *context.Context, extConfig external.Check) pkg. } res, err := ld.Search(req) if err != nil { - return results.Failf("Failed to search host %v error: %v", connection.URL, err) + return results.Errorf("Failed to search host %v error: %v", connection.URL, err) } if len(res.Entries) == 0 { diff --git a/checks/mongodb.go b/checks/mongodb.go index 9ef51e17c..59e356f4a 100644 --- a/checks/mongodb.go +++ b/checks/mongodb.go @@ -5,7 +5,6 @@ import ( "time" "github.com/flanksource/canary-checker/api/context" - "github.com/flanksource/canary-checker/api/external" v1 "github.com/flanksource/canary-checker/api/v1" "github.com/flanksource/canary-checker/pkg" @@ -29,16 +28,14 @@ func (c *MongoDBChecker) Run(ctx *context.Context) pkg.Results { return results } -func (c *MongoDBChecker) Check(ctx *context.Context, extConfig external.Check) pkg.Results { - check := extConfig.(v1.MongoDBCheck) +func (c *MongoDBChecker) Check(ctx *context.Context, check v1.MongoDBCheck) pkg.Results { result := pkg.Success(check, ctx.Canary) var results pkg.Results results = append(results, result) - var err error connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } opts := options.Client(). @@ -51,13 +48,13 @@ func (c *MongoDBChecker) Check(ctx *context.Context, extConfig external.Check) p client, err := mongo.Connect(_ctx, opts) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } defer client.Disconnect(ctx) //nolint: errcheck err = client.Ping(_ctx, readpref.Primary()) if err != nil { - return results.ErrorMessage(err) + return results.Failf("failed to ping: %v", err) } return results diff --git a/checks/namespace.go b/checks/namespace.go index ae3f4c182..bdbe460b6 100644 --- a/checks/namespace.go +++ b/checks/namespace.go @@ -120,7 +120,7 @@ func (c *NamespaceChecker) Check(ctx *context.Context, extConfig external.Check) logger.Debugf("Running namespace check %s", check.Name) five := int64(5) if _, err := c.k8s.CoreV1().Nodes().List(ctx, metav1.ListOptions{TimeoutSeconds: &five}); err != nil { - return results.Failf("cannot connect to API server: %v", err) + return results.Errorf("cannot connect to API server: %v", err) } namespaceName := c.ng.NamespaceName(check.NamespaceNamePrefix) @@ -134,7 +134,7 @@ func (c *NamespaceChecker) Check(ctx *context.Context, extConfig external.Check) }, } if _, err := namespaces.Create(ctx, ns, metav1.CreateOptions{}); err != nil { - return results.Failf("unable to create namespace: %v", err) + return results.Errorf("unable to create namespace: %v", err) } defer func() { c.Cleanup(ns) // nolint: errcheck @@ -142,20 +142,20 @@ func (c *NamespaceChecker) Check(ctx *context.Context, extConfig external.Check) pod, err := c.newPod(check, ns) if err != nil { - return results.Failf("invalid pod spec: %v", err) + return results.Errorf("invalid pod spec: %v", err) } pods := c.k8s.CoreV1().Pods(ns.Name) if _, err := pods.Create(ctx, pod, metav1.CreateOptions{}); err != nil { - return results.Failf("unable to create pod: %v", err) + return results.Errorf("unable to create pod: %v", err) } pod, _ = c.WaitForPod(ns.Name, pod.Name, time.Millisecond*time.Duration(check.ScheduleTimeout), v1.PodRunning) created := pod.GetCreationTimestamp() conditions, err := c.getConditionTimes(ns, pod) if err != nil { - return results.Failf("could not list conditions: %v", err) + return results.Errorf("could not list conditions: %v", err) } scheduled := diff(conditions, v1.PodInitialized, v1.PodScheduled) @@ -166,7 +166,7 @@ func (c *NamespaceChecker) Check(ctx *context.Context, extConfig external.Check) logger.Tracef("%v", conditions) if err := c.createServiceAndIngress(check, ns, pod); err != nil { - return results.Failf("failed to create ingress and service: %v", err) + return results.Errorf("failed to create ingress and service: %v", err) } deadline := time.Now().Add(time.Duration(check.Deadline) * time.Millisecond) @@ -176,7 +176,7 @@ func (c *NamespaceChecker) Check(ctx *context.Context, extConfig external.Check) deleteOk := true deletion := NewTimer() if err := pods.Delete(c.ctx, pod.Name, metav1.DeleteOptions{}); err != nil { - return results.Failf("failed to delete pod: %v", err) + return results.Errorf("failed to delete pod: %v", err) } result.Pass = ingressResult.Pass && deleteOk result.Message = ingressResult.Message diff --git a/checks/opensearch.go b/checks/opensearch.go index b3994c119..514a2afee 100644 --- a/checks/opensearch.go +++ b/checks/opensearch.go @@ -33,11 +33,11 @@ func (t *OpenSearchChecker) check(ctx *context.Context, check v1.OpenSearchCheck connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } if connection.URL == "" { - return results.Failf("Must specify a URL") + return results.Invalidf("Must specify a URL") } cfg := opensearch.Config{ @@ -48,7 +48,7 @@ func (t *OpenSearchChecker) check(ctx *context.Context, check v1.OpenSearchCheck osClient, err := opensearch.NewClient(cfg) if err != nil { - return results.Failf("error creating the openSearch client: %v", err) + return results.Errorf("error creating the openSearch client: %v", err) } body := strings.NewReader(check.Query) @@ -58,7 +58,7 @@ func (t *OpenSearchChecker) check(ctx *context.Context, check v1.OpenSearchCheck osClient.Search.WithBody(body), ) if err != nil { - return results.Failf("error searching: %v", err) + return results.Errorf("error searching: %v", err) } defer res.Body.Close() @@ -73,7 +73,7 @@ func (t *OpenSearchChecker) check(ctx *context.Context, check v1.OpenSearchCheck var response OpenSearchResponse if err := json.NewDecoder(res.Body).Decode(&response); err != nil { - return results.Failf("error parsing the response body: %s", err) + return results.Errorf("error parsing the response body: %s", err) } if response.Hits.Total.Value != check.Results { diff --git a/checks/pod.go b/checks/pod.go index 51c24a065..2c2f38b04 100644 --- a/checks/pod.go +++ b/checks/pod.go @@ -150,7 +150,7 @@ func (c *PodChecker) Check(ctx *context.Context, extConfig external.Check) pkg.R pods := c.k8s.CoreV1().Pods(podCheck.Namespace) if skip, err := cleanupExistingPods(ctx, c.k8s, c.podCheckSelector(podCheck)); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } else if skip { return nil } @@ -161,29 +161,29 @@ func (c *PodChecker) Check(ctx *context.Context, extConfig external.Check) pkg.R five := int64(5) nodes, err := c.k8s.CoreV1().Nodes().List(ctx, metav1.ListOptions{TimeoutSeconds: &five}) if err != nil { - return results.Failf("cannot connect to API server: %v", err) + return results.Errorf("cannot connect to API server: %v", err) } nextNode, newIndex := c.nextNode(nodes, c.latestNodeIndex) c.latestNodeIndex = newIndex pod, err := c.newPod(podCheck, nextNode) if err != nil { - return results.Failf("invalid pod spec: %v", err) + return results.Errorf("invalid pod spec: %v", err) } if _, err := c.k8s.CoreV1().Pods(podCheck.Namespace).Create(ctx, pod, metav1.CreateOptions{}); err != nil { - return results.ErrorMessage(err) + return results.Error(err) } pod, err = c.WaitForPod(podCheck.Namespace, pod.Name, time.Millisecond*time.Duration(podCheck.ScheduleTimeout), v1.PodRunning) if err != nil { - return results.Failf("unable to fetch pod details: %v", err) + return results.Errorf("unable to fetch pod details: %v", err) } created := pod.GetCreationTimestamp() conditions, err := c.getConditionTimes(podCheck, pod) if err != nil { - return results.Failf("could not list conditions: %v", err) + return results.Errorf("could not list conditions: %v", err) } scheduled := diff(conditions, v1.PodInitialized, v1.PodScheduled) @@ -193,7 +193,7 @@ func (c *PodChecker) Check(ctx *context.Context, extConfig external.Check) pkg.R ctx.Debugf("%s created=%s, scheduled=%d, started=%d, running=%d wall=%s nodeName=%s", pod.Name, created, scheduled, started, running, startTimer, nextNode) if err := c.createServiceAndIngress(ctx, podCheck, pod); err != nil { - return results.Failf("failed to create service or ingress: %v", err) + return results.Errorf("failed to create service or ingress: %v", err) } deadline := time.Now().Add(time.Duration(podCheck.Deadline) * time.Millisecond) diff --git a/checks/prometheus.go b/checks/prometheus.go index b60676de9..53c42a74e 100644 --- a/checks/prometheus.go +++ b/checks/prometheus.go @@ -34,7 +34,7 @@ func (c *PrometheusChecker) Check(ctx *context.Context, extConfig external.Check //nolint:staticcheck if check.Host != "" { - return results.Failf("host field is deprecated, use url field instead") + return results.Invalidf("host field is deprecated, use url field instead") } // Use global prometheus url if check's url is empty @@ -44,20 +44,20 @@ func (c *PrometheusChecker) Check(ctx *context.Context, extConfig external.Check connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } if connection.URL == "" { - return results.Failf("Must specify a URL") + return results.Invalidf("Must specify a URL") } promClient, err := prometheus.NewPrometheusAPI(connection.URL) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } modelValue, warning, err := promClient.Query(ctx.Context, check.Query, time.Now()) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if warning != nil { ctx.Debugf("warnings when running the query: %v", warning) diff --git a/checks/redis.go b/checks/redis.go index 95f2fba3d..7794e2ee6 100644 --- a/checks/redis.go +++ b/checks/redis.go @@ -48,7 +48,7 @@ func (c *RedisChecker) Check(ctx *context.Context, extConfig external.Check) pkg connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } redisOpts = &redis.Options{ diff --git a/checks/restic.go b/checks/restic.go index d7fd2883a..313c93713 100644 --- a/checks/restic.go +++ b/checks/restic.go @@ -50,7 +50,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk if check.ConnectionName != "" { connection, err := ctx.HydrateConnectionByURL(check.ConnectionName) if err != nil { - return results.Failf("error getting restic connection: %v", err) + return results.Errorf("error getting restic connection: %v", err) } envVars[resticPasswordEnvKey] = connection.Password @@ -69,7 +69,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk } else { password, err := ctx.GetEnvValueFromCache(*check.Password) if err != nil { - return results.Failf("error getting restic password from env: %v", err) + return results.Errorf("error getting restic password from env: %v", err) } envVars[resticPasswordEnvKey] = password } @@ -77,7 +77,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk if check.AWSConnectionName != "" { connection, err := ctx.HydrateConnectionByURL(check.AWSConnectionName) if err != nil { - return results.Failf("error getting aws connection: %v", err) + return results.Errorf("error getting aws connection: %v", err) } envVars[resticAwsAccessKeyIDEnvKey] = connection.Username @@ -86,7 +86,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk if !check.AccessKey.IsEmpty() { accessKey, err := ctx.GetEnvValueFromCache(*check.AccessKey) if err != nil { - return results.Failf("error getting aws access key from env: %v", err) + return results.Errorf("error getting aws access key from env: %v", err) } envVars[resticAwsAccessKeyIDEnvKey] = accessKey } @@ -94,7 +94,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk if !check.SecretKey.IsEmpty() { secretKey, err := ctx.GetEnvValueFromCache(*check.SecretKey) if err != nil { - return results.Failf("error getting aws secret key from env: %v", err) + return results.Errorf("error getting aws secret key from env: %v", err) } envVars[resticAwsSecretAccessKey] = secretKey } diff --git a/checks/runchecks.go b/checks/runchecks.go index 7bb0affff..95787253f 100644 --- a/checks/runchecks.go +++ b/checks/runchecks.go @@ -242,9 +242,15 @@ func ProcessResults(ctx *context.Context, results []*pkg.CheckResult) []*pkg.Che } func processTemplates(ctx *context.Context, r *pkg.CheckResult) *pkg.CheckResult { + // The check has to pass for the templates & test to be processed + if !r.Pass || r.Invalid { + return r + } + if r.Duration == 0 && r.GetDuration() > 0 { r.Duration = r.GetDuration() } + switch v := r.Check.(type) { case v1.DisplayTemplate: if !v.GetDisplayTemplate().IsEmpty() { @@ -268,7 +274,7 @@ func processTemplates(ctx *context.Context, r *pkg.CheckResult) *pkg.CheckResult if err != nil { r.ErrorMessage(err) } else if parsed, err := strconv.ParseBool(message); err != nil { - r.Failf("test expression did not return a boolean value. got %s", message) + r.Invalidf("test expression did not return a boolean value. got %s", message) } else if !parsed { r.Failf("") } diff --git a/checks/s3.go b/checks/s3.go index c95a4567a..caa71392d 100644 --- a/checks/s3.go +++ b/checks/s3.go @@ -72,12 +72,12 @@ func (c *S3Checker) Check(ctx *context.Context, extConfig external.Check) pkg.Re results = append(results, result) if err := check.AWSConnection.Populate(ctx); err != nil { - return results.Failf("failed to populate aws connection: %v", err) + return results.Errorf("failed to populate aws connection: %v", err) } cfg, err := GetAWSConfig(ctx, check.AWSConnection) if err != nil { - return results.Failf("Failed to get AWS config: %v", err) + return results.Errorf("failed to get AWS config: %v", err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { @@ -87,7 +87,7 @@ func (c *S3Checker) Check(ctx *context.Context, extConfig external.Check) pkg.Re listTimer := NewTimer() _, err = client.ListObjects(ctx, &s3.ListObjectsInput{Bucket: &check.BucketName}) if err != nil { - return results.Failf("Failed to list objects in bucket %s: %v", check.BucketName, err) + return results.Errorf("failed to list objects in bucket %s: %v", check.BucketName, err) } listHistogram.WithLabelValues(check.AWSConnection.Endpoint, check.BucketName).Observe(listTimer.Elapsed()) @@ -103,7 +103,7 @@ func (c *S3Checker) Check(ctx *context.Context, extConfig external.Check) pkg.Re Body: bytes.NewReader([]byte(data)), }) if err != nil { - return results.Failf("Failed to put object %s in bucket %s: %v", check.ObjectPath, check.BucketName, err) + return results.Errorf("failed to put object %s in bucket %s: %v", check.ObjectPath, check.BucketName, err) } updateHistogram.WithLabelValues(check.AWSConnection.Endpoint, check.BucketName).Observe(updateTimer.Elapsed()) @@ -112,7 +112,7 @@ func (c *S3Checker) Check(ctx *context.Context, extConfig external.Check) pkg.Re Key: &check.ObjectPath, }) if err != nil { - return results.Failf("Failed to get object %s in bucket %s: %v", check.ObjectPath, check.BucketName, err) + return results.Errorf("failed to get object %s in bucket %s: %v", check.ObjectPath, check.BucketName, err) } returnedData, _ := io.ReadAll(obj.Body) diff --git a/checks/sql.go b/checks/sql.go index edce2dac4..0b7c0f1cb 100644 --- a/checks/sql.go +++ b/checks/sql.go @@ -147,7 +147,7 @@ func CheckSQL(ctx *context.Context, checker SQLChecker) pkg.Results { // nolint: connection, err := ctx.GetConnection(check.Connection) if err != nil { - return results.Failf("error getting connection: %v", err) + return results.Errorf("error getting connection: %v", err) } query := check.GetQuery() @@ -157,7 +157,7 @@ func CheckSQL(ctx *context.Context, checker SQLChecker) pkg.Results { // nolint: Template: query, }) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } if ctx.IsDebug() { ctx.Infof("query: %s", query) @@ -165,11 +165,10 @@ 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) + return results.Error(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/checks/tcp.go b/checks/tcp.go index fbb38cb71..66e6cc0c8 100644 --- a/checks/tcp.go +++ b/checks/tcp.go @@ -39,7 +39,7 @@ func (t *TCPChecker) Check(ctx *context.Context, extConfig external.Check) pkg.R addr, port, err := extractAddrAndPort(c.Endpoint) if err != nil { - return results.ErrorMessage(err) + return results.Error(err) } timeout := time.Millisecond * time.Duration(c.ThresholdMillis) diff --git a/checks/utils.go b/checks/utils.go index cc3211652..fd4e6c185 100644 --- a/checks/utils.go +++ b/checks/utils.go @@ -3,21 +3,12 @@ package checks import ( "fmt" "sync" - "time" "github.com/flanksource/canary-checker/api/external" "github.com/flanksource/canary-checker/pkg" ) -func Error(check external.Check, err error) *pkg.CheckResult { - return &pkg.CheckResult{ - Check: check, - Pass: false, - Invalid: true, - Error: err.Error(), - } -} - +// Deprecated: Use the methods on checkresult. func Failf(check external.Check, msg string, args ...interface{}) *pkg.CheckResult { return &pkg.CheckResult{ Check: check, @@ -27,48 +18,7 @@ func Failf(check external.Check, msg string, args ...interface{}) *pkg.CheckResu } } -// TextFailf used for failure in case of text based results -func TextFailf(check external.Check, textResults bool, msg string, args ...interface{}) *pkg.CheckResult { - if textResults { - return &pkg.CheckResult{ - Check: check, - Pass: false, - Invalid: false, - DisplayType: "Text", - Message: fmt.Sprintf(msg, args...), - } - } - return Failf(check, msg, args...) -} -func Success(check external.Check, start time.Time) *pkg.CheckResult { - return &pkg.CheckResult{ - Check: check, - Pass: true, - Invalid: false, - Duration: time.Since(start).Milliseconds(), - } -} - -func Successf(check external.Check, start time.Time, textResults bool, msg string, args ...interface{}) *pkg.CheckResult { - if textResults { - return &pkg.CheckResult{ - Check: check, - Pass: true, - DisplayType: "Text", - Invalid: false, - Message: fmt.Sprintf(msg, args...), - Duration: time.Since(start).Milliseconds(), - } - } - return &pkg.CheckResult{ - Check: check, - Pass: true, - Invalid: false, - Message: fmt.Sprintf(msg, args...), - Duration: time.Since(start).Milliseconds(), - } -} - +// Deprecated: Use the methods on checkresult. func Passf(check external.Check, msg string, args ...interface{}) *pkg.CheckResult { return &pkg.CheckResult{ Check: check, diff --git a/pkg/cache/postgres.go b/pkg/cache/postgres.go index 06ff1c51e..be5ffa3e5 100644 --- a/pkg/cache/postgres.go +++ b/pkg/cache/postgres.go @@ -40,6 +40,10 @@ func (c *postgresCache) Add(check pkg.Check, statii ...pkg.CheckStatus) []string check.Status = "unhealthy" } + if status.Invalid { + check.Status = "invalid" + } + checkID, err := c.AddCheckFromStatus(check, status) if err != nil { logger.Errorf("error persisting check with canary %s: %v", check.CanaryID, err) diff --git a/pkg/results.go b/pkg/results.go index cb0ef1507..db4d889e1 100644 --- a/pkg/results.go +++ b/pkg/results.go @@ -68,13 +68,6 @@ func New(check external.Check, canary v1.Canary) *CheckResult { } } -func (result *CheckResult) ErrorMessage(err error) *CheckResult { - if err == nil { - return result - } - return result.Failf(err.Error()) -} - func (result *CheckResult) UpdateCheck(check external.Check) *CheckResult { result.Check = check return result @@ -122,10 +115,20 @@ func (result *CheckResult) Failf(message string, args ...interface{}) *CheckResu return result } -func (result *CheckResult) Invalidf(message string, args ...interface{}) Results { +func (result *CheckResult) Invalidf(message string, args ...interface{}) *CheckResult { result = result.Failf(message, args...) result.Invalid = true - return Results{result} + return result +} + +func (result *CheckResult) Errorf(msg string, args ...any) *CheckResult { + result = result.Failf(msg, args...) + // TODO: mark this is as error + return result +} + +func (result *CheckResult) ErrorMessage(err error) *CheckResult { + return result.Errorf(err.Error()) } func (result *CheckResult) AddDetails(detail interface{}) *CheckResult { @@ -162,7 +165,17 @@ func (r Results) Failf(msg string, args ...interface{}) Results { return r } -func (r Results) ErrorMessage(err error) Results { +func (r Results) Errorf(msg string, args ...any) Results { + r[0].Errorf(msg, args...) + return r +} + +func (r Results) Invalidf(msg string, args ...any) Results { + r[0].Invalidf(msg, args...) + return r +} + +func (r Results) Error(err error) Results { r[0].ErrorMessage(err) return r } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 56f391926..0387603b7 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -131,7 +131,7 @@ func UnfoldGlobs(paths ...string) []string { for _, path := range paths { matched, err := filepath.Glob(path) if err != nil { - logger.Warnf("invalid glob pattern. path=%s; %w", path, err) + logger.Warnf("invalid glob pattern. path=%s; %v", path, err) continue }