From 4ae4cdfee4fef5f4d1240d31f682cdd78529fd0e Mon Sep 17 00:00:00 2001 From: Moshe Immerman Date: Mon, 3 Jul 2023 08:29:13 +0300 Subject: [PATCH 01/10] breaking: inline auth and use url consistently --- api/context/context.go | 83 +- api/v1/checks.go | 282 +-- api/v1/common.go | 22 +- api/v1/zz_generated.deepcopy.go | 48 +- checks/alertmanager.go | 9 +- checks/common.go | 57 - checks/elasticsearch.go | 13 +- checks/folder_sftp.go | 4 +- checks/folder_smb.go | 6 +- checks/http.go | 55 +- checks/jmeter.go | 4 - checks/ldap.go | 22 +- checks/mongodb.go | 13 +- checks/opensearch.go | 15 +- checks/prometheus.go | 16 +- checks/redis.go | 46 +- checks/restic.go | 2 +- checks/runchecks.go | 2 +- checks/sql.go | 21 +- config/deploy/crd.yaml | 1962 ++++++++--------- config/deploy/manifests.yaml | 1962 ++++++++--------- config/schemas/canary.schema.json | 225 +- config/schemas/component.schema.json | 225 +- .../schemas/health_alertmanager.schema.json | 25 +- config/schemas/health_awsconfig.schema.json | 55 +- .../schemas/health_awsconfigrule.schema.json | 55 +- config/schemas/health_cloudwatch.schema.json | 5 +- .../schemas/health_containerdPull.schema.json | 6 +- config/schemas/health_dockerPull.schema.json | 6 +- config/schemas/health_dockerPush.schema.json | 6 +- config/schemas/health_dynatrace.schema.json | 3 + .../schemas/health_elasticsearch.schema.json | 23 +- config/schemas/health_folder.schema.json | 47 +- config/schemas/health_helm.schema.json | 6 +- config/schemas/health_http.schema.json | 28 +- config/schemas/health_jmeter.schema.json | 3 - config/schemas/health_ldap.schema.json | 27 +- config/schemas/health_mongodb.schema.json | 29 +- config/schemas/health_mssql.schema.json | 29 +- config/schemas/health_mysql.schema.json | 29 +- config/schemas/health_opensearch.schema.json | 24 +- config/schemas/health_postgres.schema.json | 29 +- config/schemas/health_prometheus.schema.json | 69 +- config/schemas/health_redis.schema.json | 32 +- config/schemas/topology.schema.json | 225 +- fixtures/aws/ec2_pass.yaml | 2 +- fixtures/azure/devops.yaml | 3 +- fixtures/datasources/SFTP/sftp_pass.yaml | 11 +- fixtures/datasources/elasticsearch_fail.yaml | 21 +- fixtures/datasources/elasticsearch_pass.yaml | 21 +- fixtures/datasources/ldap_pass.yaml | 22 +- fixtures/datasources/mongo_fail.yaml | 11 +- fixtures/datasources/mongo_pass.yaml | 15 +- fixtures/datasources/mssql_fail.yaml | 2 +- fixtures/datasources/mssql_pass.yaml | 11 +- fixtures/datasources/mysql_fail.yaml | 11 +- fixtures/datasources/mysql_pass.yaml | 11 +- fixtures/datasources/opensearch_fail.yaml | 21 +- fixtures/datasources/opensearch_pass.yaml | 21 +- fixtures/datasources/postgres_fail.yaml | 11 +- fixtures/datasources/postgres_pass.yaml | 11 +- fixtures/datasources/prometheus.yaml | 6 +- fixtures/k8s/http_auth_configmap.yaml | 21 +- fixtures/k8s/http_auth_secret.yaml | 21 +- fixtures/minimal/http_auth.yaml | 9 +- .../minimal/http_pass_results_mode_pass.yaml | 4 +- fixtures/minimal/http_pass_single.yaml | 13 +- fixtures/quarantine/smb_pass.yaml | 21 +- 68 files changed, 3025 insertions(+), 3130 deletions(-) diff --git a/api/context/context.go b/api/context/context.go index 088bbb592..33b02b749 100644 --- a/api/context/context.go +++ b/api/context/context.go @@ -9,6 +9,7 @@ import ( v1 "github.com/flanksource/canary-checker/api/v1" "github.com/flanksource/commons/logger" + ctemplate "github.com/flanksource/commons/template" "github.com/flanksource/duty" "github.com/flanksource/duty/models" "github.com/flanksource/duty/types" @@ -55,12 +56,90 @@ func (ctx *Context) GetEnvValueFromCache(env types.EnvVar) (string, error) { return duty.GetEnvValueFromCache(ctx.Kubernetes, env, ctx.Namespace) } +func getDomain(username string) string { + parts := strings.Split(username, "@") + if len(parts) == 2 { + return parts[1] + } + return "" +} + +func (ctx *Context) GetConnection(conn v1.Connection) (*models.Connection, error) { + var _conn *models.Connection + var err error + + if _conn, err = ctx.HydrateConnectionByURL(conn.Connection); err != nil { + return nil, err + } + + if _conn == nil { + _conn = &models.Connection{ + URL: conn.URL, + } + } + + if conn.URL != "" { + // override the url specified at the connection level + _conn.URL = conn.URL + } + + if _conn.Username == "" || _conn.Password == "" { + // no username specified at connection level, use the one from inline connection + auth, err := ctx.GetAuthValues(conn.Authentication) + if err != nil { + return nil, err + } + _conn.Username = auth.Username.ValueStatic + _conn.Password = auth.Password.ValueStatic + } + + data := map[string]interface{}{ + "name": ctx.Canary.Name, + "namespace": ctx.Namespace, + "username": _conn.Username, + "password": _conn.Password, + "domain": getDomain(_conn.Username), + } + templater := ctemplate.StructTemplater{ + Values: data, + // access go values in template requires prefix everything with . + // to support $(username) instead of $(.username) we add a function for each var + ValueFunctions: true, + DelimSets: []ctemplate.Delims{ + {Left: "{{", Right: "}}"}, + {Left: "$(", Right: ")"}, + }, + RequiredTag: "template", + } + if err := templater.Walk(_conn); err != nil { + return nil, err + } + + return _conn, nil +} + +func (ctx Context) GetAuthValues(auth v1.Authentication) (v1.Authentication, error) { + // in case nil we are sending empty string values for username and password + if auth.IsEmpty() { + return auth, nil + } + var err error + + if auth.Username.ValueStatic, err = ctx.GetEnvValueFromCache(auth.Username); err != nil { + return auth, err + } + if auth.Password.ValueStatic, err = ctx.GetEnvValueFromCache(auth.Password); err != nil { + return auth, err + } + return auth, nil +} + func (ctx *Context) HydrateConnectionByURL(connectionName string) (*models.Connection, error) { - if !strings.HasPrefix(connectionName, "connection://") { + if connectionName == "" { return nil, nil } - if connectionName == "" { + if !strings.HasPrefix(connectionName, "connection://") { return nil, nil } diff --git a/api/v1/checks.go b/api/v1/checks.go index d73c7adf1..83f08d338 100644 --- a/api/v1/checks.go +++ b/api/v1/checks.go @@ -53,9 +53,8 @@ func (c Check) GetLabels() map[string]string { type HTTPCheck struct { Description `yaml:",inline" json:",inline"` Templatable `yaml:",inline" json:",inline"` - // Name of the connection that'll be used to derive the endpoint. - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - // HTTP endpoint to check. Mutually exclusive with Namespace + Connection `yaml:",inline" json:",inline"` + // Deprecated: Use url instead Endpoint string `yaml:"endpoint" json:"endpoint,omitempty" template:"true"` // Namespace to crawl for TLS endpoints. Mutually exclusive with Endpoint Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty" template:"true"` @@ -79,16 +78,10 @@ type HTTPCheck struct { Body string `yaml:"body,omitempty" json:"body,omitempty" template:"true"` // Header fields to be used in the query Headers []types.EnvVar `yaml:"headers,omitempty" json:"headers,omitempty"` - // Credentials for authentication headers - Authentication *Authentication `yaml:"authentication,omitempty" json:"authentication,omitempty"` //Template the request body TemplateBody bool `yaml:"templateBody,omitempty" json:"templateBody,omitempty"` } -func (c HTTPCheck) GetEndpoint() string { - return c.Endpoint -} - func (c HTTPCheck) GetType() string { return "http" } @@ -154,7 +147,7 @@ type CloudWatchCheck struct { Description `yaml:",inline" json:",inline"` AWSConnection `yaml:",inline" json:",inline"` Templatable `yaml:",inline" json:",inline"` - Filter CloudWatchFilter `yaml:"filter,omitempty" json:"filter,omitempty"` + Filter CloudWatchFilter `yaml:",inline" json:",inline"` } type CloudWatchFilter struct { @@ -211,8 +204,6 @@ func (c ResticCheck) GetType() string { type JmeterCheck struct { Description `yaml:",inline" json:",inline"` - // Name of the connection that'll be used to derive host and other connection details. - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` // Jmx defines the ConfigMap or Secret reference to get the JMX test plan Jmx types.EnvVar `yaml:"jmx" json:"jmx"` // Host is the server against which test plan needs to be executed @@ -235,28 +226,6 @@ func (c JmeterCheck) GetType() string { return "jmeter" } -// HydrateConnection will attempt to populate the host and port from the connection name. -func (c *JmeterCheck) HydrateConnection(ctx checkContext) (bool, error) { - connection, err := ctx.HydrateConnectionByURL(c.ConnectionName) - if err != nil { - return false, err - } - - if connection == nil { - return false, nil - } - - c.Host = connection.URL - - if portRaw, ok := connection.Properties["port"]; ok { - if port, err := strconv.Atoi(portRaw); nil == err { - c.Port = int32(port) - } - } - - return true, nil -} - type DockerPullCheck struct { Description `yaml:",inline" json:",inline"` Image string `yaml:"image" json:"image"` @@ -320,12 +289,10 @@ func (c ContainerdPushCheck) GetType() string { type RedisCheck struct { Description `yaml:",inline" json:",inline"` - // ConnectionName is the name of the connection. - // It is used to populate addr, db and auth. - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - Addr string `yaml:"addr" json:"addr" template:"true"` - Auth *Authentication `yaml:"auth,omitempty" json:"auth,omitempty"` - DB int `yaml:"db" json:"db"` + Connection `yaml:",inline" json:",inline"` + // Deprecated: Use url instead + Addr string `yaml:"addr,omitempty" json:"addr,omitempty" template:"true"` + DB *int `yaml:"db,omitempty" json:"db,omitempty"` } func (c RedisCheck) GetType() string { @@ -430,14 +397,12 @@ type Mongo struct { } type OpenSearchCheck struct { - Description `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - URL string `yaml:"url,omitempty" json:"url,omitempty"` - Auth Authentication `yaml:"auth" json:"auth"` - Query string `yaml:"query" json:"query"` - Index string `yaml:"index" json:"index"` - Results int64 `yaml:"results,omitempty" json:"results,omitempty"` + Description `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + Connection `yaml:",inline" json:",inline"` + Query string `yaml:"query" json:"query"` + Index string `yaml:"index" json:"index"` + Results int64 `yaml:"results,omitempty" json:"results,omitempty"` } func (c OpenSearchCheck) GetType() string { @@ -448,34 +413,6 @@ func (c OpenSearchCheck) GetEndpoint() string { return c.URL } -func (c *OpenSearchCheck) HydrateConnection(ctx checkContext) error { - connection, err := ctx.HydrateConnectionByURL(c.ConnectionName) - if err != nil { - return err - } - - if connection != nil { - c.URL = connection.URL - c.Auth.Username.ValueStatic = connection.Username - c.Auth.Password.ValueStatic = connection.Password - return nil - } - - if val, err := ctx.GetEnvValueFromCache(c.Auth.Username); err != nil { - return fmt.Errorf("failed to get username from cache: %w", err) - } else { - c.Auth.Username.ValueStatic = val - } - - if val, err := ctx.GetEnvValueFromCache(c.Auth.Password); err != nil { - return fmt.Errorf("failed to get username from cache: %w", err) - } else { - c.Auth.Password.ValueStatic = val - } - - return nil -} - /* [include:datasources/elasticsearch_pass.yaml] */ @@ -485,59 +422,26 @@ type Elasticsearch struct { } type ElasticsearchCheck struct { - Description `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - URL string `yaml:"url" json:"url,omitempty" template:"true"` - Auth *Authentication `yaml:"auth,omitempty" json:"auth,omitempty"` - Query string `yaml:"query" json:"query,omitempty" template:"true"` - Index string `yaml:"index" json:"index,omitempty" template:"true"` - Results int `yaml:"results" json:"results,omitempty" template:"true"` + Description `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + Connection `yaml:",inline" json:",inline"` + Query string `yaml:"query" json:"query,omitempty" template:"true"` + Index string `yaml:"index" json:"index,omitempty" template:"true"` + Results int `yaml:"results" json:"results,omitempty" template:"true"` } func (c ElasticsearchCheck) GetType() string { return "elasticsearch" } -func (c ElasticsearchCheck) GetEndpoint() string { - return c.URL -} - -func (c *ElasticsearchCheck) HydrateConnection(ctx checkContext) error { - connection, err := ctx.HydrateConnectionByURL(c.ConnectionName) - if err != nil { - return err - } - - if connection != nil { - c.URL = connection.URL - c.Auth.Username.ValueStatic = connection.Username - c.Auth.Password.ValueStatic = connection.Password - return nil - } - - if val, err := ctx.GetEnvValueFromCache(c.Auth.Username); err != nil { - return fmt.Errorf("failed to get username from cache: %w", err) - } else { - c.Auth.Username.ValueStatic = val - } - - if val, err := ctx.GetEnvValueFromCache(c.Auth.Password); err != nil { - return fmt.Errorf("failed to get password from cache: %w", err) - } else { - c.Auth.Password.ValueStatic = val - } - - return nil -} - type DynatraceCheck struct { - Description `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - Host string `yaml:"host" json:"host,omitempty" template:"true"` - Scheme string `yaml:"scheme" json:"scheme,omitempty"` - APIKey types.EnvVar `yaml:"apiKey" json:"apiKey,omitempty"` - Namespace string `yaml:"namespace" json:"namespace,omitempty" template:"true"` + Description `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` + Host string `yaml:"host" json:"host,omitempty" template:"true"` + Scheme string `yaml:"scheme" json:"scheme,omitempty"` + APIKey types.EnvVar `yaml:"apiKey" json:"apiKey,omitempty"` + Namespace string `yaml:"namespace" json:"namespace,omitempty" template:"true"` } func (t DynatraceCheck) GetType() string { @@ -557,24 +461,18 @@ type AlertManager struct { } type AlertManagerCheck struct { - Description `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - Host string `yaml:"host" json:"host,omitempty" template:"true"` - Auth *Authentication `yaml:"auth,omitempty" json:"auth,omitempty"` - Alerts []string `yaml:"alerts" json:"alerts,omitempty" template:"true"` - Filters map[string]string `yaml:"filters" json:"filters,omitempty" template:"true"` - Ignore []string `yaml:"ignore" json:"ignore,omitempty" template:"true"` + Description `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + Connection `yaml:",inline" json:",inline"` + Alerts []string `yaml:"alerts" json:"alerts,omitempty" template:"true"` + Filters map[string]string `yaml:"filters" json:"filters,omitempty" template:"true"` + Ignore []string `yaml:"ignore" json:"ignore,omitempty" template:"true"` } func (c AlertManagerCheck) GetType() string { return "alertmanager" } -func (c AlertManagerCheck) GetEndpoint() string { - return c.Host -} - type PodCheck struct { Description `yaml:",inline" json:",inline"` Namespace string `yaml:"namespace" json:"namespace,omitempty" template:"true"` @@ -609,45 +507,17 @@ func (c PodCheck) GetType() string { } type LDAPCheck struct { - Description `yaml:",inline" json:",inline"` - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - Host string `yaml:"host" json:"host" template:"true"` - Auth *Authentication `yaml:"auth" json:"auth"` - BindDN string `yaml:"bindDN" json:"bindDN"` - UserSearch string `yaml:"userSearch,omitempty" json:"userSearch,omitempty"` - SkipTLSVerify bool `yaml:"skipTLSVerify,omitempty" json:"skipTLSVerify,omitempty"` -} - -func (c LDAPCheck) GetEndpoint() string { - return c.Host + Description `yaml:",inline" json:",inline"` + Connection `yaml:",inline" json:",inline"` + BindDN string `yaml:"bindDN" json:"bindDN"` + UserSearch string `yaml:"userSearch,omitempty" json:"userSearch,omitempty"` + SkipTLSVerify bool `yaml:"skipTLSVerify,omitempty" json:"skipTLSVerify,omitempty"` } func (c LDAPCheck) GetType() string { return "ldap" } -func (c *LDAPCheck) HydrateConnection(ctx checkContext) (bool, error) { - connection, err := ctx.HydrateConnectionByURL(c.ConnectionName) - if err != nil { - return false, err - } - - if connection == nil { - return false, nil - } - - c.Host = connection.URL - c.Auth.Username.ValueStatic = connection.Username - c.Auth.Password.ValueStatic = connection.Password - - c.Auth = &Authentication{ - Username: types.EnvVar{ValueStatic: c.Auth.Username.ValueStatic}, - Password: types.EnvVar{ValueStatic: c.Auth.Password.ValueStatic}, - } - - return true, nil -} - type NamespaceCheck struct { Description `yaml:",inline" json:",inline"` NamespaceNamePrefix string `yaml:"namespaceNamePrefix,omitempty" json:"namespaceNamePrefix,omitempty"` @@ -768,16 +638,10 @@ type SMBConnection struct { // ConnectionName of the connection. It'll be used to populate the connection fields. ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` //Port on which smb server is running. Defaults to 445 - Port int `yaml:"port,omitempty" json:"port,omitempty"` - Auth *Authentication `yaml:"auth" json:"auth"` + Port int `yaml:"port,omitempty" json:"port,omitempty"` + Authentication `yaml:",inline" json:",inline"` //Domain... Domain string `yaml:"domain,omitempty" json:"domain,omitempty"` - // Workstation... - Workstation string `yaml:"workstation,omitempty" json:"workstation,omitempty"` - //Sharename to mount from the samba server - Sharename string `yaml:"sharename,omitempty" json:"sharename,omitempty"` - //SearchPath sub-path inside the mount location - SearchPath string `yaml:"searchPath,omitempty" json:"searchPath,omitempty" ` } func (c SMBConnection) GetPort() int { @@ -797,27 +661,15 @@ func (c *SMBConnection) HydrateConnection(ctx checkContext) (found bool, err err return false, nil } - c.Auth = &Authentication{ + c.Authentication = Authentication{ Username: types.EnvVar{ValueStatic: connection.Username}, Password: types.EnvVar{ValueStatic: connection.Password}, } - if workstation, ok := connection.Properties["workstation"]; ok { - c.Workstation = workstation - } - if domain, ok := connection.Properties["domain"]; ok { c.Domain = domain } - if sharename, ok := connection.Properties["sharename"]; ok { - c.Sharename = sharename - } - - if searchPath, ok := connection.Properties["searchPath"]; ok { - c.SearchPath = searchPath - } - if portRaw, ok := connection.Properties["port"]; ok { if port, err := strconv.Atoi(portRaw); nil == err { c.Port = port @@ -831,9 +683,9 @@ type SFTPConnection struct { // ConnectionName of the connection. It'll be used to populate the connection fields. ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` // Port for the SSH server. Defaults to 22 - Port int `yaml:"port,omitempty" json:"port,omitempty"` - Host string `yaml:"host" json:"host"` - Auth *Authentication `yaml:"auth" json:"auth"` + Port int `yaml:"port,omitempty" json:"port,omitempty"` + Host string `yaml:"host" json:"host"` + Authentication `yaml:",inline" json:",inline"` } func (c *SFTPConnection) HydrateConnection(ctx checkContext) (found bool, err error) { @@ -847,7 +699,7 @@ func (c *SFTPConnection) HydrateConnection(ctx checkContext) (found bool, err er } c.Host = connection.URL - c.Auth = &Authentication{ + c.Authentication = Authentication{ Username: types.EnvVar{ValueStatic: connection.Username}, Password: types.EnvVar{ValueStatic: connection.Password}, } @@ -876,11 +728,11 @@ type Prometheus struct { } type PrometheusCheck struct { - Description `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` - // Address of the prometheus server - Host string `yaml:"host" json:"host" template:"true" ` + Description `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + // Deprecated: use `url` instead + Host string `yaml:"host,omitempty" json:"host,omitempty"` + Connection `yaml:",inline" json:",inline"` // PromQL query Query string `yaml:"query" json:"query" template:"true"` } @@ -889,28 +741,9 @@ func (c PrometheusCheck) GetType() string { return "prometheus" } -func (c PrometheusCheck) GetEndpoint() string { - return fmt.Sprintf("%v/%v", c.Host, c.Description) -} - -func (c *PrometheusCheck) HydrateConnection(ctx checkContext) (bool, error) { - connection, err := ctx.HydrateConnectionByURL(c.ConnectionName) - if err != nil { - return false, err - } - - if connection == nil { - return false, nil - } - - c.Host = connection.URL - return true, nil -} - type MongoDBCheck struct { Description `yaml:",inline" json:",inline"` - // Monogodb connection string, e.g. mongodb://:27017/?authSource=admin, See https://docs.mongodb.com/manual/reference/connection-string/ - Connection `yaml:",inline" json:",inline"` + Connection `yaml:",inline" json:",inline"` } func (c MongoDBCheck) GetType() string { @@ -1011,7 +844,16 @@ func (t *AWSConnection) Populate(ctx checkContext, k8s kubernetes.Interface, nam t.AccessKey.ValueStatic = connection.Username t.SecretKey.ValueStatic = connection.Password - t.Endpoint = connection.URL + if t.Endpoint == "" { + t.Endpoint = connection.URL + } + + t.SkipTLSVerify = connection.InsecureTLS + if t.Region == "" { + if region, ok := connection.Properties["region"]; ok { + t.Region = region + } + } } if accessKey, err := duty.GetEnvValueFromCache(k8s, t.AccessKey, namespace); err != nil { @@ -1107,7 +949,7 @@ type AwsConfigCheck struct { Description `yaml:",inline" json:",inline"` Templatable `yaml:",inline" json:",inline"` Query string `yaml:"query" json:"query"` - *AWSConnection `yaml:"awsConnection,omitempty" json:"awsConnection,omitempty"` + *AWSConnection `yaml:",inline" json:",inline"` AggregatorName *string `yaml:"aggregatorName,omitempty" json:"aggregatorName,omitempty"` } @@ -1128,7 +970,7 @@ type AwsConfigRuleCheck struct { Rules []string `yaml:"rules,omitempty" json:"rules,omitempty"` // Filters the results by compliance. The allowed values are INSUFFICIENT_DATA, NON_COMPLIANT, NOT_APPLICABLE, COMPLIANT ComplianceTypes []string `yaml:"complianceTypes,omitempty" json:"complianceTypes,omitempty"` - *AWSConnection `yaml:"awsConnection,omitempty" json:"awsConnection,omitempty"` + *AWSConnection `yaml:",inline" json:",inline"` } func (c AwsConfigRuleCheck) GetType() string { diff --git a/api/v1/common.go b/api/v1/common.go index 82e303b36..f37f15876 100644 --- a/api/v1/common.go +++ b/api/v1/common.go @@ -169,8 +169,8 @@ type JSONCheck struct { } type Authentication struct { - Username types.EnvVar `yaml:"username" json:"username"` - Password types.EnvVar `yaml:"password" json:"password"` + Username types.EnvVar `yaml:"username,omitempty" json:"username,omitempty"` + Password types.EnvVar `yaml:"password,omitempty" json:"password,omitempty"` } func (auth Authentication) IsEmpty() bool { @@ -306,21 +306,15 @@ func (d Description) GetLabels() map[string]string { } type Connection struct { - Connection string `yaml:"connection" json:"connection" template:"true"` - Authentication Authentication `yaml:"auth,omitempty" json:"auth,omitempty"` -} - -// +k8s:deepcopy-gen=false -type Connectable interface { - GetConnection() string -} - -func (c Connection) GetConnection() string { - return c.Connection + // Connection name e.g. connection://http/google + Connection string `yaml:"connection,omitempty" json:"connection,omitempty"` + // Connection url, interpolated with username,password + URL string `yaml:"url,omitempty" json:"url,omitempty" template:"true"` + Authentication `yaml:",inline" json:",inline"` } func (c Connection) GetEndpoint() string { - return sanitizeEndpoints(c.Connection) + return sanitizeEndpoints(c.URL) } // Obfuscate passwords of the form ' password=xxxxx ' from connectionString since diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 4e19aebf1..e8724addd 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -66,11 +66,7 @@ func (in *AlertManagerCheck) DeepCopyInto(out *AlertManagerCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) out.Templatable = in.Templatable - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } + in.Connection.DeepCopyInto(&out.Connection) if in.Alerts != nil { in, out := &in.Alerts, &out.Alerts *out = make([]string, len(*in)) @@ -1514,11 +1510,7 @@ func (in *ElasticsearchCheck) DeepCopyInto(out *ElasticsearchCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) out.Templatable = in.Templatable - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } + in.Connection.DeepCopyInto(&out.Connection) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ElasticsearchCheck. @@ -1812,6 +1804,7 @@ func (in *HTTPCheck) DeepCopyInto(out *HTTPCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) out.Templatable = in.Templatable + in.Connection.DeepCopyInto(&out.Connection) if in.ResponseCodes != nil { in, out := &in.ResponseCodes, &out.ResponseCodes *out = make([]int, len(*in)) @@ -1829,11 +1822,6 @@ func (in *HTTPCheck) DeepCopyInto(out *HTTPCheck) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Authentication != nil { - in, out := &in.Authentication, &out.Authentication - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPCheck. @@ -2076,11 +2064,7 @@ func (in *LDAP) DeepCopy() *LDAP { func (in *LDAPCheck) DeepCopyInto(out *LDAPCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } + in.Connection.DeepCopyInto(&out.Connection) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LDAPCheck. @@ -2283,7 +2267,7 @@ func (in *OpenSearchCheck) DeepCopyInto(out *OpenSearchCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) out.Templatable = in.Templatable - in.Auth.DeepCopyInto(&out.Auth) + in.Connection.DeepCopyInto(&out.Connection) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenSearchCheck. @@ -2386,6 +2370,7 @@ func (in *PrometheusCheck) DeepCopyInto(out *PrometheusCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) out.Templatable = in.Templatable + in.Connection.DeepCopyInto(&out.Connection) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrometheusCheck. @@ -2479,10 +2464,11 @@ func (in *Redis) DeepCopy() *Redis { func (in *RedisCheck) DeepCopyInto(out *RedisCheck) { *out = *in in.Description.DeepCopyInto(&out.Description) - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) + in.Connection.DeepCopyInto(&out.Connection) + if in.DB != nil { + in, out := &in.DB, &out.DB + *out = new(int) + **out = **in } } @@ -2628,11 +2614,7 @@ func (in *S3Check) DeepCopy() *S3Check { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SFTPConnection) DeepCopyInto(out *SFTPConnection) { *out = *in - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } + in.Authentication.DeepCopyInto(&out.Authentication) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SFTPConnection. @@ -2648,11 +2630,7 @@ func (in *SFTPConnection) DeepCopy() *SFTPConnection { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SMBConnection) DeepCopyInto(out *SMBConnection) { *out = *in - if in.Auth != nil { - in, out := &in.Auth, &out.Auth - *out = new(Authentication) - (*in).DeepCopyInto(*out) - } + in.Authentication.DeepCopyInto(&out.Authentication) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SMBConnection. diff --git a/checks/alertmanager.go b/checks/alertmanager.go index 6ea68c9a3..533aec8b5 100644 --- a/checks/alertmanager.go +++ b/checks/alertmanager.go @@ -33,14 +33,13 @@ func (c *AlertManagerChecker) Check(ctx *context.Context, extConfig external.Che result := pkg.Success(check, ctx.Canary) results = append(results, result) - if connection, err := ctx.HydrateConnectionByURL(check.ConnectionName); err != nil { - return results.Failf("failed to find connection from %q: %v", check.ConnectionName, err) - } else if connection != nil { - check.Host = connection.URL + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) } client := alertmanagerClient.NewHTTPClientWithConfig(nil, &alertmanagerClient.TransportConfig{ - Host: check.GetEndpoint(), + Host: connection.URL, Schemes: []string{"http", "https"}, BasePath: alertmanagerClient.DefaultBasePath, }) diff --git a/checks/common.go b/checks/common.go index 6c927c242..40a63e4bc 100644 --- a/checks/common.go +++ b/checks/common.go @@ -12,66 +12,9 @@ import ( "github.com/flanksource/canary-checker/pkg" "github.com/flanksource/canary-checker/pkg/utils" "github.com/flanksource/canary-checker/templating" - ctemplate "github.com/flanksource/commons/template" "github.com/robfig/cron/v3" ) -func GetConnection(ctx *context.Context, conn *v1.Connection, namespace string) (string, error) { - // TODO: this function should not be necessary, each check should be templated out individual - // however, the walk method only support high level values, not values from siblings. - - if conn.Authentication.IsEmpty() { - return conn.Connection, nil - } - - auth, err := GetAuthValues(ctx, &conn.Authentication) - if err != nil { - return "", err - } - - clone := conn.DeepCopy() - - data := map[string]interface{}{ - "name": ctx.Canary.Name, - "namespace": namespace, - "username": auth.GetUsername(), - "password": auth.GetPassword(), - "domain": auth.GetDomain(), - } - templater := ctemplate.StructTemplater{ - Values: data, - // access go values in template requires prefix everything with . - // to support $(username) instead of $(.username) we add a function for each var - ValueFunctions: true, - DelimSets: []ctemplate.Delims{ - {Left: "{{", Right: "}}"}, - {Left: "$(", Right: ")"}, - }, - RequiredTag: "template", - } - if err := templater.Walk(clone); err != nil { - return "", err - } - - return clone.Connection, nil -} - -func GetAuthValues(ctx *context.Context, auth *v1.Authentication) (*v1.Authentication, error) { - // in case nil we are sending empty string values for username and password - if auth == nil { - return auth, nil - } - var err error - - if auth.Username.ValueStatic, err = ctx.GetEnvValueFromCache(auth.Username); err != nil { - return nil, err - } - if auth.Password.ValueStatic, err = ctx.GetEnvValueFromCache(auth.Password); err != nil { - return nil, err - } - return auth, nil -} - func age(t time.Time) string { return utils.Age(time.Since(t)) } diff --git a/checks/elasticsearch.go b/checks/elasticsearch.go index 688ebe411..c51840952 100644 --- a/checks/elasticsearch.go +++ b/checks/elasticsearch.go @@ -34,14 +34,15 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch var results pkg.Results results = append(results, result) - if err := check.HydrateConnection(ctx); err != nil { - return results.Failf("Failed to find connection for elastic search: %v", err) + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) } cfg := elasticsearch.Config{ - Addresses: []string{check.GetEndpoint()}, - Username: check.Auth.GetUsername(), - Password: check.Auth.GetPassword(), + Addresses: []string{connection.URL}, + Username: connection.Username, + Password: connection.Password, } es, err := elasticsearch.NewClient(cfg) @@ -67,7 +68,7 @@ func (c *ElasticsearchChecker) Check(ctx *context.Context, extConfig external.Ch fmt.Errorf("Error parsing the response body: %s", err), ) } else { - return results.ErrorMessage(fmt.Errorf("Error from elasticsearch.[%s]: %v, %v", + return results.ErrorMessage(fmt.Errorf("Error from elasticsearch [%s]: %v, %v", res.Status(), e["error"].(map[string]interface{})["type"], e["error"].(map[string]interface{})["reason"], diff --git a/checks/folder_sftp.go b/checks/folder_sftp.go index 77bdaf8fa..22df10b29 100644 --- a/checks/folder_sftp.go +++ b/checks/folder_sftp.go @@ -20,9 +20,9 @@ func CheckSFTP(ctx *context.Context, check v1.FolderCheck) pkg.Results { return results.Failf("failed to populate SFTP connection: %v", err) } - auth := check.SFTPConnection.Auth + auth := check.SFTPConnection.Authentication if !foundConn { - auth, err = GetAuthValues(ctx, check.SFTPConnection.Auth) + auth, err = ctx.GetAuthValues(check.SFTPConnection.Authentication) if err != nil { return results.ErrorMessage(err) } diff --git a/checks/folder_smb.go b/checks/folder_smb.go index c7d873698..92c23ebca 100644 --- a/checks/folder_smb.go +++ b/checks/folder_smb.go @@ -30,7 +30,7 @@ func (s *SMBSession) Close() error { return nil } -func smbConnect(server string, port int, share string, auth *v1.Authentication) (Filesystem, uint64, uint64, uint64, error) { +func smbConnect(server string, port int, share string, auth v1.Authentication) (Filesystem, uint64, uint64, uint64, error) { var err error var smb *SMBSession server = server + ":" + fmt.Sprintf("%d", port) @@ -83,9 +83,9 @@ func CheckSmb(ctx *context.Context, check v1.FolderCheck) pkg.Results { return results.Failf("failed to populate SMB connection: %v", err) } - auth := check.SMBConnection.Auth + auth := check.SMBConnection.Authentication if !foundConn { - auth, err = GetAuthValues(ctx, check.SMBConnection.Auth) + auth, err = ctx.GetAuthValues(check.SMBConnection.Authentication) if err != nil { return results.ErrorMessage(err) } diff --git a/checks/http.go b/checks/http.go index 23590f5b6..15a5251b6 100644 --- a/checks/http.go +++ b/checks/http.go @@ -8,6 +8,7 @@ import ( "github.com/flanksource/canary-checker/api/context" "github.com/flanksource/commons/text" + "github.com/flanksource/duty/models" "github.com/pkg/errors" "github.com/flanksource/canary-checker/api/external" @@ -60,7 +61,7 @@ func (c *HTTPChecker) Run(ctx *context.Context) pkg.Results { return results } -func (c *HTTPChecker) configure(req *http.HTTPRequest, ctx *context.Context, check v1.HTTPCheck) error { +func (c *HTTPChecker) configure(req *http.HTTPRequest, ctx *context.Context, check v1.HTTPCheck, connection *models.Connection) error { for _, header := range check.Headers { value, err := ctx.GetEnvValueFromCache(header) if err != nil { @@ -69,12 +70,8 @@ func (c *HTTPChecker) configure(req *http.HTTPRequest, ctx *context.Context, che req.Header(header.Name, value) } - auth, err := GetAuthValues(ctx, check.Authentication) - if err != nil { - return err - } - if auth != nil { - req.Auth(auth.Username.ValueStatic, auth.Password.ValueStatic) + if connection.Username != "" || connection.Password != "" { + req.Auth(connection.Username, connection.Password) } req.NTLM(check.NTLM) @@ -103,17 +100,35 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. result := pkg.Success(check, ctx.Canary) results = append(results, result) - if connection, err := ctx.HydrateConnectionByURL(check.ConnectionName); err != nil { - return results.Failf("failed to find HTTP connection %q: %v", check.ConnectionName, err) - } else if connection != nil { - check.Endpoint = connection.URL + //nolint:staticcheck + if check.Endpoint != "" && check.URL != "" { + return results.Failf("cannot specify both endpoint and url") } - if _, err := url.Parse(check.Endpoint); err != nil { - return results.ErrorMessage(err) + //nolint:staticcheck + if check.Endpoint != "" && check.URL == "" { + check.URL = check.Endpoint + } + + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection %v", err) + } + + if connection.URL == "" { + return results.Failf("no url or connection specified") + } + + if ntlm, ok := connection.Properties["ntlm"]; ok { + check.NTLM = ntlm == "true" + } else if ntlm, ok := connection.Properties["ntlmv2"]; ok { + check.NTLMv2 = ntlm == "true" + } + + if _, err := url.Parse(connection.URL); err != nil { + return results.Failf("failed to parse url: %v", err) } - endpoint := check.Endpoint body := check.Body if check.TemplateBody { body, err = text.Template(body, ctx.Canary) @@ -122,9 +137,9 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. } } - req := http.NewRequest(check.Endpoint).Method(check.GetMethod()) + req := http.NewRequest(connection.URL).Method(check.GetMethod()) - if err := c.configure(req, ctx, check); err != nil { + if err := c.configure(req, ctx, check, connection); err != nil { return results.ErrorMessage(err) } @@ -137,15 +152,15 @@ func (c *HTTPChecker) Check(ctx *context.Context, extConfig external.Check) pkg. Name: "response_code", Type: metrics.CounterType, Labels: map[string]string{ - "code": strconv.Itoa(status), - "endpoint": endpoint, + "code": strconv.Itoa(status), + "url": check.URL, }, }) result.Duration = elapsed.Milliseconds() - responseStatus.WithLabelValues(strconv.Itoa(status), statusCodeToClass(status), endpoint).Inc() + responseStatus.WithLabelValues(strconv.Itoa(status), statusCodeToClass(status), check.URL).Inc() age := resp.GetSSLAge() if age != nil { - sslExpiration.WithLabelValues(endpoint).Set(age.Hours() * 24) + sslExpiration.WithLabelValues(check.URL).Set(age.Hours() * 24) } body, _ = resp.AsString() diff --git a/checks/jmeter.go b/checks/jmeter.go index fdd87bb57..44b4c8531 100644 --- a/checks/jmeter.go +++ b/checks/jmeter.go @@ -55,10 +55,6 @@ func (c *JmeterChecker) Check(ctx *context.Context, extConfig external.Check) pk return results.Failf("unable to write test plan file") } - if _, err := check.HydrateConnection(ctx); err != nil { - return results.Failf("unable to populate JMeter connection: %v", err) - } - var host string var port string if check.Host != "" { diff --git a/checks/ldap.go b/checks/ldap.go index 125e89399..50cdae424 100644 --- a/checks/ldap.go +++ b/checks/ldap.go @@ -38,22 +38,22 @@ func (c *LdapChecker) Check(ctx *context.Context, extConfig external.Check) pkg. var err error results = append(results, result) - if ok, err := check.HydrateConnection(ctx); err != nil { - return results.Failf("failed to hydrate connection: %v", err) - } else if !ok { - check.Auth, err = GetAuthValues(ctx, check.Auth) - if err != nil { - return results.Failf("failed to fetch auth details: %v", err) - } + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("failed to get connection: %v", err) + } + + if connection.URL == "" { + return results.Failf("Must specify a connection or URL") } - ld, err := ldap.DialURL(check.Host, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: check.SkipTLSVerify})) + ld, err := ldap.DialURL(connection.URL, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: check.SkipTLSVerify})) if err != nil { return results.Failf("Failed to connect %v", err) } - if err := ld.Bind(check.Auth.Username.ValueStatic, check.Auth.Password.ValueStatic); err != nil { - return results.Failf("Failed to bind using %s %v", check.Auth.Username.ValueStatic, err) + if err := ld.Bind(connection.Username, connection.Password); err != nil { + return results.Failf("Failed to bind using %s %v", connection.Username, err) } req := &ldap.SearchRequest{ @@ -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", check.Host, err) + return results.Failf("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 8c20ad68c..9ef51e17c 100644 --- a/checks/mongodb.go +++ b/checks/mongodb.go @@ -36,20 +36,13 @@ func (c *MongoDBChecker) Check(ctx *context.Context, extConfig external.Check) p results = append(results, result) var err error - var dbConnectionString string - if connection, err := ctx.HydrateConnectionByURL(check.Connection.Connection); err != nil { + connection, err := ctx.GetConnection(check.Connection) + if err != nil { return results.Failf("error getting connection: %v", err) - } else if connection != nil { - dbConnectionString = connection.URL - } else { - dbConnectionString, err = GetConnection(ctx, &check.Connection, ctx.Namespace) - if err != nil { - return results.ErrorMessage(err) - } } opts := options.Client(). - ApplyURI(dbConnectionString). + ApplyURI(connection.URL). SetConnectTimeout(3 * time.Second). SetSocketTimeout(3 * time.Second) diff --git a/checks/opensearch.go b/checks/opensearch.go index 0d4b56cdf..b3994c119 100644 --- a/checks/opensearch.go +++ b/checks/opensearch.go @@ -31,14 +31,19 @@ func (t *OpenSearchChecker) check(ctx *context.Context, check v1.OpenSearchCheck var results pkg.Results results = append(results, result) - if err := check.HydrateConnection(ctx); err != nil { - return results.Failf("error hydrating connection: %v", err) + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) + } + + if connection.URL == "" { + return results.Failf("Must specify a URL") } cfg := opensearch.Config{ - Username: check.Auth.Username.ValueStatic, - Password: check.Auth.Password.ValueStatic, - Addresses: []string{check.GetEndpoint()}, + Username: connection.Username, + Password: connection.Password, + Addresses: []string{connection.URL}, } osClient, err := opensearch.NewClient(cfg) diff --git a/checks/prometheus.go b/checks/prometheus.go index b96930014..b14dde923 100644 --- a/checks/prometheus.go +++ b/checks/prometheus.go @@ -33,15 +33,21 @@ func (c *PrometheusChecker) Check(ctx *context.Context, extConfig external.Check var results pkg.Results results = append(results, result) - if _, err := check.HydrateConnection(ctx); err != nil { - return results.Failf("error hydrating connection: %v", err) + //nolint:staticcheck + if check.Host != "" && check.URL == "" { + check.URL = check.Host } - if check.Host == "" { - return results.Failf("Must specify a prometheus host") + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) + } + + if connection.URL == "" { + return results.Failf("Must specify a URL") } - promClient, err := prometheus.NewPrometheusAPI(check.Host) + promClient, err := prometheus.NewPrometheusAPI(connection.URL) if err != nil { return results.ErrorMessage(err) } diff --git a/checks/redis.go b/checks/redis.go index b1b16d369..95f2fba3d 100644 --- a/checks/redis.go +++ b/checks/redis.go @@ -40,36 +40,28 @@ func (c *RedisChecker) Check(ctx *context.Context, extConfig external.Check) pkg results = append(results, result) var redisOpts *redis.Options - if check.ConnectionName != "" { - connection, err := ctx.HydrateConnectionByURL(check.ConnectionName) - if err != nil { - return results.Failf("failed to fetch connection %q: %v", check.ConnectionName, err) - } - redisOpts = &redis.Options{ - Addr: connection.URL, - Username: connection.Username, - Password: connection.Password, - } + //nolint:staticcheck + if check.Addr != "" && check.URL == "" { + check.URL = check.Addr + } - if db, ok := connection.Properties["db"]; ok { - if dbInt, err := strconv.Atoi(db); nil == err { - redisOpts.DB = dbInt - } - } - } else { - auth, err := GetAuthValues(ctx, check.Auth) - if err != nil { - return results.Failf("failed to fetch auth details: %v", err) - } + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) + } - redisOpts = &redis.Options{ - Addr: check.Addr, - DB: check.DB, - } - if auth != nil { - redisOpts.Username = auth.GetUsername() - redisOpts.Password = auth.GetPassword() + redisOpts = &redis.Options{ + Addr: connection.URL, + Username: connection.Username, + Password: connection.Password, + } + + if check.DB != nil { + redisOpts.DB = *check.DB + } else if db, ok := connection.Properties["db"]; ok { + if dbInt, err := strconv.Atoi(db); nil == err { + redisOpts.DB = dbInt } } diff --git a/checks/restic.go b/checks/restic.go index fe4d77984..d7fd2883a 100644 --- a/checks/restic.go +++ b/checks/restic.go @@ -75,7 +75,7 @@ func (c *ResticChecker) Check(ctx *context.Context, extConfig external.Check) pk } if check.AWSConnectionName != "" { - connection, err := ctx.HydrateConnectionByURL(check.ConnectionName) + connection, err := ctx.HydrateConnectionByURL(check.AWSConnectionName) if err != nil { return results.Failf("error getting aws connection: %v", err) } diff --git a/checks/runchecks.go b/checks/runchecks.go index 9f365a452..7df76e095 100644 --- a/checks/runchecks.go +++ b/checks/runchecks.go @@ -115,7 +115,7 @@ func processTemplates(ctx *context.Context, r *pkg.CheckResult) *pkg.CheckResult if message != "false" { r.Failf("expecting either 'true' or 'false' but got '%v'", message) } else { - r.Failf("Test expression failed. Expecting true from: %v", tpl.Expression) + r.Failf("") } } } diff --git a/checks/sql.go b/checks/sql.go index 3dc0a3412..9ae59b73f 100644 --- a/checks/sql.go +++ b/checks/sql.go @@ -3,6 +3,7 @@ package checks import ( "database/sql" "fmt" + "strings" "github.com/flanksource/canary-checker/api/context" "github.com/flanksource/canary-checker/api/external" @@ -84,23 +85,17 @@ func CheckSQL(ctx *context.Context, checker SQLChecker) pkg.Results { // nolint: var results pkg.Results results = append(results, result) - var dbConnectionString string - if connection, err := ctx.HydrateConnectionByURL(check.Connection.Connection); err != nil { - return results.Failf("error getting connection: %v", err) - } else if connection != nil { - dbConnectionString = connection.URL - } else { - dbConnectionString, err = GetConnection(ctx, &check.Connection, ctx.Namespace) - if err != nil { - return results.ErrorMessage(err) - } + if check.Connection.Connection != "" && !strings.HasPrefix(check.Connection.Connection, "connection://") { + check.URL = check.Connection.Connection + check.Connection.Connection = "" } - if ctx.IsTrace() { - ctx.Tracef("connecting to %s", dbConnectionString) + connection, err := ctx.GetConnection(check.Connection) + if err != nil { + return results.Failf("error getting connection: %v", err) } - details, err := querySQL(checker.GetDriver(), dbConnectionString, check.GetQuery()) + details, err := querySQL(checker.GetDriver(), connection.URL, check.GetQuery()) if err != nil { return results.ErrorMessage(err) } diff --git a/config/deploy/crd.yaml b/config/deploy/crd.yaml index 4653f81f0..44a6df73e 100644 --- a/config/deploy/crd.yaml +++ b/config/deploy/crd.yaml @@ -64,69 +64,8 @@ spec: items: type: string type: array - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -146,8 +85,6 @@ spec: additionalProperties: type: string type: object - host: - type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -163,6 +100,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object test: properties: expr: @@ -185,6 +150,37 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - name type: object @@ -192,83 +188,39 @@ spec: awsConfig: items: properties: - aggregatorName: - type: string - awsConnection: + accessKey: properties: - accessKey: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. - type: string - endpoint: - type: string - objectPath: - description: glob path to restrict matches to a subset + name: type: string - region: + value: type: string - secretKey: + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - skipTLSVerify: - description: Skip TLS verify when connecting to aws - type: boolean - usePathStyle: - description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' - type: boolean type: object + aggregatorName: + type: string + connection: + description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. + type: string description: description: Description for the check type: string @@ -283,6 +235,8 @@ spec: template: type: string type: object + endpoint: + type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -294,8 +248,44 @@ spec: name: description: Name of the check type: string + objectPath: + description: glob path to restrict matches to a subset + type: string query: type: string + region: + type: string + secretKey: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + skipTLSVerify: + description: Skip TLS verify when connecting to aws + type: boolean test: properties: expr: @@ -318,6 +308,9 @@ spec: template: type: string type: object + usePathStyle: + description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' + type: boolean required: - name - query @@ -326,86 +319,42 @@ spec: awsConfigRule: items: properties: - awsConnection: + accessKey: properties: - accessKey: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. - type: string - endpoint: - type: string - objectPath: - description: glob path to restrict matches to a subset + name: type: string - region: + value: type: string - secretKey: + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - skipTLSVerify: - description: Skip TLS verify when connecting to aws - type: boolean - usePathStyle: - description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' - type: boolean type: object complianceTypes: description: Filters the results by compliance. The allowed values are INSUFFICIENT_DATA, NON_COMPLIANT, NOT_APPLICABLE, COMPLIANT items: type: string type: array + connection: + description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. + type: string description: description: Description for the check type: string @@ -420,6 +369,8 @@ spec: template: type: string type: object + endpoint: + type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -436,11 +387,47 @@ spec: name: description: Name of the check type: string + objectPath: + description: glob path to restrict matches to a subset + type: string + region: + type: string rules: description: Specify one or more Config rule names to filter the results by rule. items: type: string type: array + secretKey: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + skipTLSVerify: + description: Skip TLS verify when connecting to aws + type: boolean test: properties: expr: @@ -463,6 +450,9 @@ spec: template: type: string type: object + usePathStyle: + description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' + type: boolean required: - name type: object @@ -606,6 +596,14 @@ spec: type: object type: object type: object + actionPrefix: + type: string + alarmPrefix: + type: string + alarms: + items: + type: string + type: array connection: description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. type: string @@ -625,19 +623,6 @@ spec: type: object endpoint: type: string - filter: - properties: - actionPrefix: - type: string - alarmPrefix: - type: string - alarms: - items: - type: string - type: array - state: - type: string - type: object icon: description: Icon for overwriting default icon on the dashboard type: string @@ -685,6 +670,8 @@ spec: skipTLSVerify: description: Skip TLS verify when connecting to aws type: boolean + state: + type: string test: properties: expr: @@ -832,9 +819,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1092,9 +1076,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1183,9 +1164,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1239,6 +1217,8 @@ spec: type: object type: object type: object + connection: + type: string description: description: Description for the check type: string @@ -1414,69 +1394,8 @@ spec: elasticsearch: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -1505,6 +1424,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -1532,29 +1479,58 @@ spec: type: string type: object url: + description: Connection url, interpolated with username,password type: string - required: - - name - type: object - type: array - env: - additionalProperties: - description: VarSource represents a source for a value - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + username: properties: - key: - description: The key to select. - type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + required: + - name + type: object + type: array + env: + additionalProperties: + description: VarSource represents a source for a value + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic fieldRef: @@ -1826,186 +1802,162 @@ spec: type: string sftpConnection: properties: - auth: + connection: + description: ConnectionName of the connection. It'll be used to populate the connection fields. + type: string + host: + type: string + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the connection fields. - type: string - host: - type: string port: description: Port for the SSH server. Defaults to 22 type: integer - required: - - auth - - host - type: object - smbConnection: - properties: - auth: + username: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - host + type: object + smbConnection: + properties: connection: description: ConnectionName of the connection. It'll be used to populate the connection fields. type: string domain: description: Domain... type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object port: description: Port on which smb server is running. Defaults to 445 type: integer - searchPath: - description: SearchPath sub-path inside the mount location - type: string - sharename: - description: Sharename to mount from the samba server - type: string - workstation: - description: Workstation... - type: string - required: - - auth - type: object - test: - properties: - expr: - type: string - javascript: - type: string - jsonPath: - type: string - template: - type: string - type: object - totalSize: - description: TotalSize present on the filesystem - type: string - transform: - properties: - expr: - type: string - javascript: - type: string - jsonPath: + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + type: object + test: + properties: + expr: + type: string + javascript: + type: string + jsonPath: + type: string + template: + type: string + type: object + totalSize: + description: TotalSize present on the filesystem + type: string + transform: + properties: + expr: + type: string + javascript: + type: string + jsonPath: type: string template: type: string @@ -2164,9 +2116,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object cafile: type: string @@ -2195,74 +2144,11 @@ spec: http: items: properties: - authentication: - description: Credentials for authentication headers - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object body: description: Request Body Contents type: string connection: - description: Name of the connection that'll be used to derive the endpoint. + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2279,7 +2165,7 @@ spec: type: string type: object endpoint: - description: HTTP endpoint to check. Mutually exclusive with Namespace + description: 'Deprecated: Use url instead' type: string headers: description: Header fields to be used in the query @@ -2338,6 +2224,34 @@ spec: ntlmv2: description: NTLM when set to true will do authentication using NTLM v2 protocol type: boolean + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object responseCodes: description: Expected response codes for the HTTP Request. items: @@ -2385,6 +2299,37 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - name type: object @@ -2429,9 +2374,6 @@ spec: jmeter: items: properties: - connection: - description: Name of the connection that'll be used to derive host and other connection details. - type: string description: description: Description for the check type: string @@ -2649,77 +2591,14 @@ spec: ldap: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object bindDN: type: string connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check type: string - host: - type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -2731,83 +2610,79 @@ spec: name: description: Name of the check type: string - skipTLSVerify: - type: boolean - userSearch: - type: string - required: - - auth - - bindDN - - host - - name - type: object - type: array - mongodb: - items: - properties: - auth: + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - username: + type: object + skipTLSVerify: + type: boolean + url: + description: Connection url, interpolated with username,password + type: string + userSearch: + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - bindDN + - name + type: object + type: array + mongodb: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2823,77 +2698,74 @@ spec: name: description: Name of the check type: string - required: - - connection - - name - type: object - type: array - mssql: - items: - properties: - auth: + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - username: + type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - name + type: object + type: array + mssql: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2920,6 +2792,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -2947,77 +2847,46 @@ spec: template: type: string type: object - required: - - connection - - name - type: object - type: array - mysql: - items: - properties: - auth: + url: + description: Connection url, interpolated with username,password + type: string + username: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - name + type: object + type: array + mysql: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3044,6 +2913,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3071,8 +2968,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - connection - name type: object type: array @@ -3152,69 +3079,8 @@ spec: opensearch: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3243,6 +3109,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3271,9 +3165,37 @@ spec: type: string type: object url: + description: Connection url, interpolated with username,password type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - auth - index - name - query @@ -3327,91 +3249,30 @@ spec: type: string namespace: type: string - path: - type: string - port: - format: int64 - type: integer - priorityClass: - type: string - readyTimeout: - format: int64 - type: integer - scheduleTimeout: - format: int64 - type: integer - spec: - type: string - required: - - name - type: object - type: array - postgres: - items: - properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object + path: + type: string + port: + format: int64 + type: integer + priorityClass: + type: string + readyTimeout: + format: int64 + type: integer + scheduleTimeout: + format: int64 + type: integer + spec: + type: string + required: + - name + type: object + type: array + postgres: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3438,6 +3299,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3465,8 +3354,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - connection - name type: object type: array @@ -3474,6 +3393,7 @@ spec: items: properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3490,7 +3410,7 @@ spec: type: string type: object host: - description: Address of the prometheus server + description: 'Deprecated: use `url` instead' type: string icon: description: Icon for overwriting default icon on the dashboard @@ -3503,6 +3423,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: description: PromQL query type: string @@ -3528,8 +3476,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - host - name - query type: object @@ -3538,71 +3516,10 @@ spec: items: properties: addr: + description: 'Deprecated: Use url instead' type: string - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: - description: ConnectionName is the name of the connection. It is used to populate addr, db and auth. + description: Connection name e.g. connection://http/google type: string db: type: integer @@ -3620,9 +3537,66 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - addr - - db - name type: object type: array diff --git a/config/deploy/manifests.yaml b/config/deploy/manifests.yaml index ae2f36d78..bd3b22d37 100644 --- a/config/deploy/manifests.yaml +++ b/config/deploy/manifests.yaml @@ -64,69 +64,8 @@ spec: items: type: string type: array - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -146,8 +85,6 @@ spec: additionalProperties: type: string type: object - host: - type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -163,6 +100,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object test: properties: expr: @@ -185,6 +150,37 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - name type: object @@ -192,83 +188,39 @@ spec: awsConfig: items: properties: - aggregatorName: - type: string - awsConnection: + accessKey: properties: - accessKey: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. - type: string - endpoint: - type: string - objectPath: - description: glob path to restrict matches to a subset + name: type: string - region: + value: type: string - secretKey: + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - skipTLSVerify: - description: Skip TLS verify when connecting to aws - type: boolean - usePathStyle: - description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' - type: boolean type: object + aggregatorName: + type: string + connection: + description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. + type: string description: description: Description for the check type: string @@ -283,6 +235,8 @@ spec: template: type: string type: object + endpoint: + type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -294,8 +248,44 @@ spec: name: description: Name of the check type: string + objectPath: + description: glob path to restrict matches to a subset + type: string query: type: string + region: + type: string + secretKey: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + skipTLSVerify: + description: Skip TLS verify when connecting to aws + type: boolean test: properties: expr: @@ -318,6 +308,9 @@ spec: template: type: string type: object + usePathStyle: + description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' + type: boolean required: - name - query @@ -326,86 +319,42 @@ spec: awsConfigRule: items: properties: - awsConnection: + accessKey: properties: - accessKey: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. - type: string - endpoint: - type: string - objectPath: - description: glob path to restrict matches to a subset + name: type: string - region: + value: type: string - secretKey: + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - skipTLSVerify: - description: Skip TLS verify when connecting to aws - type: boolean - usePathStyle: - description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' - type: boolean type: object complianceTypes: description: Filters the results by compliance. The allowed values are INSUFFICIENT_DATA, NON_COMPLIANT, NOT_APPLICABLE, COMPLIANT items: type: string type: array + connection: + description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. + type: string description: description: Description for the check type: string @@ -420,6 +369,8 @@ spec: template: type: string type: object + endpoint: + type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -436,11 +387,47 @@ spec: name: description: Name of the check type: string + objectPath: + description: glob path to restrict matches to a subset + type: string + region: + type: string rules: description: Specify one or more Config rule names to filter the results by rule. items: type: string type: array + secretKey: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + skipTLSVerify: + description: Skip TLS verify when connecting to aws + type: boolean test: properties: expr: @@ -463,6 +450,9 @@ spec: template: type: string type: object + usePathStyle: + description: 'Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY' + type: boolean required: - name type: object @@ -606,6 +596,14 @@ spec: type: object type: object type: object + actionPrefix: + type: string + alarmPrefix: + type: string + alarms: + items: + type: string + type: array connection: description: ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. type: string @@ -625,19 +623,6 @@ spec: type: object endpoint: type: string - filter: - properties: - actionPrefix: - type: string - alarmPrefix: - type: string - alarms: - items: - type: string - type: array - state: - type: string - type: object icon: description: Icon for overwriting default icon on the dashboard type: string @@ -685,6 +670,8 @@ spec: skipTLSVerify: description: Skip TLS verify when connecting to aws type: boolean + state: + type: string test: properties: expr: @@ -832,9 +819,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1092,9 +1076,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1183,9 +1164,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object description: description: Description for the check @@ -1239,6 +1217,8 @@ spec: type: object type: object type: object + connection: + type: string description: description: Description for the check type: string @@ -1414,69 +1394,8 @@ spec: elasticsearch: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -1505,6 +1424,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -1532,29 +1479,58 @@ spec: type: string type: object url: + description: Connection url, interpolated with username,password type: string - required: - - name - type: object - type: array - env: - additionalProperties: - description: VarSource represents a source for a value - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + username: properties: - key: - description: The key to select. - type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + required: + - name + type: object + type: array + env: + additionalProperties: + description: VarSource represents a source for a value + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + required: + - key type: object x-kubernetes-map-type: atomic fieldRef: @@ -1826,186 +1802,162 @@ spec: type: string sftpConnection: properties: - auth: + connection: + description: ConnectionName of the connection. It'll be used to populate the connection fields. + type: string + host: + type: string + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object - connection: - description: ConnectionName of the connection. It'll be used to populate the connection fields. - type: string - host: - type: string port: description: Port for the SSH server. Defaults to 22 type: integer - required: - - auth - - host - type: object - smbConnection: - properties: - auth: + username: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - host + type: object + smbConnection: + properties: connection: description: ConnectionName of the connection. It'll be used to populate the connection fields. type: string domain: description: Domain... type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object port: description: Port on which smb server is running. Defaults to 445 type: integer - searchPath: - description: SearchPath sub-path inside the mount location - type: string - sharename: - description: Sharename to mount from the samba server - type: string - workstation: - description: Workstation... - type: string - required: - - auth - type: object - test: - properties: - expr: - type: string - javascript: - type: string - jsonPath: - type: string - template: - type: string - type: object - totalSize: - description: TotalSize present on the filesystem - type: string - transform: - properties: - expr: - type: string - javascript: - type: string - jsonPath: + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + type: object + test: + properties: + expr: + type: string + javascript: + type: string + jsonPath: + type: string + template: + type: string + type: object + totalSize: + description: TotalSize present on the filesystem + type: string + transform: + properties: + expr: + type: string + javascript: + type: string + jsonPath: type: string template: type: string @@ -2164,9 +2116,6 @@ spec: type: object type: object type: object - required: - - password - - username type: object cafile: type: string @@ -2195,74 +2144,11 @@ spec: http: items: properties: - authentication: - description: Credentials for authentication headers - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object body: description: Request Body Contents type: string connection: - description: Name of the connection that'll be used to derive the endpoint. + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2279,7 +2165,7 @@ spec: type: string type: object endpoint: - description: HTTP endpoint to check. Mutually exclusive with Namespace + description: 'Deprecated: Use url instead' type: string headers: description: Header fields to be used in the query @@ -2338,6 +2224,34 @@ spec: ntlmv2: description: NTLM when set to true will do authentication using NTLM v2 protocol type: boolean + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object responseCodes: description: Expected response codes for the HTTP Request. items: @@ -2385,6 +2299,37 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - name type: object @@ -2429,9 +2374,6 @@ spec: jmeter: items: properties: - connection: - description: Name of the connection that'll be used to derive host and other connection details. - type: string description: description: Description for the check type: string @@ -2649,77 +2591,14 @@ spec: ldap: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object bindDN: type: string connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check type: string - host: - type: string icon: description: Icon for overwriting default icon on the dashboard type: string @@ -2731,83 +2610,79 @@ spec: name: description: Name of the check type: string - skipTLSVerify: - type: boolean - userSearch: - type: string - required: - - auth - - bindDN - - host - - name - type: object - type: array - mongodb: - items: - properties: - auth: + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - username: + type: object + skipTLSVerify: + type: boolean + url: + description: Connection url, interpolated with username,password + type: string + userSearch: + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - bindDN + - name + type: object + type: array + mongodb: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2823,77 +2698,74 @@ spec: name: description: Name of the check type: string - required: - - connection - - name - type: object - type: array - mssql: - items: - properties: - auth: + password: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - username: + type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - name + type: object + type: array + mssql: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -2920,6 +2792,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -2947,77 +2847,46 @@ spec: template: type: string type: object - required: - - connection - - name - type: object - type: array - mysql: - items: - properties: - auth: + url: + description: Connection url, interpolated with username,password + type: string + username: properties: - password: + name: + type: string + value: + type: string + valueFrom: properties: - name: - type: string - value: - type: string - valueFrom: + configMapKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: + secretKeyRef: properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object + key: + type: string + name: + type: string + required: + - key type: object type: object - required: - - password - - username type: object + required: + - name + type: object + type: array + mysql: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3044,6 +2913,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3071,8 +2968,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - connection - name type: object type: array @@ -3152,69 +3079,8 @@ spec: opensearch: items: properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3243,6 +3109,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3271,9 +3165,37 @@ spec: type: string type: object url: + description: Connection url, interpolated with username,password type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - auth - index - name - query @@ -3327,91 +3249,30 @@ spec: type: string namespace: type: string - path: - type: string - port: - format: int64 - type: integer - priorityClass: - type: string - readyTimeout: - format: int64 - type: integer - scheduleTimeout: - format: int64 - type: integer - spec: - type: string - required: - - name - type: object - type: array - postgres: - items: - properties: - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object + path: + type: string + port: + format: int64 + type: integer + priorityClass: + type: string + readyTimeout: + format: int64 + type: integer + scheduleTimeout: + format: int64 + type: integer + spec: + type: string + required: + - name + type: object + type: array + postgres: + items: + properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3438,6 +3299,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: type: string results: @@ -3465,8 +3354,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - connection - name type: object type: array @@ -3474,6 +3393,7 @@ spec: items: properties: connection: + description: Connection name e.g. connection://http/google type: string description: description: Description for the check @@ -3490,7 +3410,7 @@ spec: type: string type: object host: - description: Address of the prometheus server + description: 'Deprecated: use `url` instead' type: string icon: description: Icon for overwriting default icon on the dashboard @@ -3503,6 +3423,34 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object query: description: PromQL query type: string @@ -3528,8 +3476,38 @@ spec: template: type: string type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - host - name - query type: object @@ -3538,71 +3516,10 @@ spec: items: properties: addr: + description: 'Deprecated: Use url instead' type: string - auth: - properties: - password: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - username: - properties: - name: - type: string - value: - type: string - valueFrom: - properties: - configMapKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - secretKeyRef: - properties: - key: - type: string - name: - type: string - required: - - key - type: object - type: object - type: object - required: - - password - - username - type: object connection: - description: ConnectionName is the name of the connection. It is used to populate addr, db and auth. + description: Connection name e.g. connection://http/google type: string db: type: integer @@ -3620,9 +3537,66 @@ spec: name: description: Name of the check type: string + password: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object + url: + description: Connection url, interpolated with username,password + type: string + username: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + required: + - key + type: object + type: object + type: object required: - - addr - - db - name type: object type: array diff --git a/config/schemas/canary.schema.json b/config/schemas/canary.schema.json index 2bdf053df..953269aa0 100644 --- a/config/schemas/canary.schema.json +++ b/config/schemas/canary.schema.json @@ -59,11 +59,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "alerts": { "items": { @@ -102,11 +105,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "AwsConfigCheck": { "properties": { @@ -134,8 +133,29 @@ "query": { "type": "string" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" }, "aggregatorName": { "type": "string" @@ -189,8 +209,29 @@ }, "type": "array" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" } }, "additionalProperties": false, @@ -663,14 +704,15 @@ "transform": { "$ref": "#/$defs/Template" }, - "filter": { + "Filter": { "$ref": "#/$defs/CloudWatchFilter" } }, "additionalProperties": false, "type": "object", "required": [ - "name" + "name", + "Filter" ] }, "CloudWatchFilter": { @@ -980,6 +1022,9 @@ "transform": { "$ref": "#/$defs/Template" }, + "connection": { + "type": "string" + }, "host": { "type": "string" }, @@ -1097,8 +1142,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -1376,6 +1424,15 @@ "connection": { "type": "string" }, + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "endpoint": { "type": "string" }, @@ -1418,9 +1475,6 @@ }, "type": "array" }, - "authentication": { - "$ref": "#/$defs/Authentication" - }, "templateBody": { "type": "boolean" } @@ -1527,9 +1581,6 @@ "labels": { "$ref": "#/$defs/Labels" }, - "connection": { - "type": "string" - }, "jmx": { "$ref": "#/$defs/EnvVar" }, @@ -1667,11 +1718,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "bindDN": { "type": "string" @@ -1687,8 +1741,6 @@ "type": "object", "required": [ "name", - "host", - "auth", "bindDN" ] }, @@ -1753,15 +1805,20 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MssqlCheck": { @@ -1790,8 +1847,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -1803,8 +1866,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MysqlCheck": { @@ -1833,8 +1895,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -1846,8 +1914,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "NamespaceCheck": { @@ -2053,8 +2120,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2070,7 +2140,6 @@ "type": "object", "required": [ "name", - "auth", "query", "index" ] @@ -2206,8 +2275,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2219,8 +2294,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "PrometheusCheck": { @@ -2246,12 +2320,21 @@ "transform": { "$ref": "#/$defs/Template" }, + "host": { + "type": "string" + }, "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "query": { "type": "string" } @@ -2260,7 +2343,6 @@ "type": "object", "required": [ "name", - "host", "query" ] }, @@ -2281,11 +2363,17 @@ "connection": { "type": "string" }, - "addr": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, + "addr": { + "type": "string" }, "db": { "type": "integer" @@ -2294,9 +2382,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "addr", - "db" + "name" ] }, "ResourceSelector": { @@ -2424,15 +2510,17 @@ "host": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "host", - "auth" + "host" ] }, "SMBConnection": { @@ -2443,27 +2531,18 @@ "port": { "type": "integer" }, - "auth": { - "$ref": "#/$defs/Authentication" - }, - "domain": { - "type": "string" - }, - "workstation": { - "type": "string" + "username": { + "$ref": "#/$defs/EnvVar" }, - "sharename": { - "type": "string" + "password": { + "$ref": "#/$defs/EnvVar" }, - "searchPath": { + "domain": { "type": "string" } }, "additionalProperties": false, - "type": "object", - "required": [ - "auth" - ] + "type": "object" }, "SecretKeySelector": { "properties": { diff --git a/config/schemas/component.schema.json b/config/schemas/component.schema.json index 8b8e2f68c..82b2ca9cd 100644 --- a/config/schemas/component.schema.json +++ b/config/schemas/component.schema.json @@ -59,11 +59,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "alerts": { "items": { @@ -102,11 +105,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "AwsConfigCheck": { "properties": { @@ -134,8 +133,29 @@ "query": { "type": "string" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" }, "aggregatorName": { "type": "string" @@ -189,8 +209,29 @@ }, "type": "array" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" } }, "additionalProperties": false, @@ -569,14 +610,15 @@ "transform": { "$ref": "#/$defs/Template" }, - "filter": { + "Filter": { "$ref": "#/$defs/CloudWatchFilter" } }, "additionalProperties": false, "type": "object", "required": [ - "name" + "name", + "Filter" ] }, "CloudWatchFilter": { @@ -1143,6 +1185,9 @@ "transform": { "$ref": "#/$defs/Template" }, + "connection": { + "type": "string" + }, "host": { "type": "string" }, @@ -1260,8 +1305,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -1572,6 +1620,15 @@ "connection": { "type": "string" }, + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "endpoint": { "type": "string" }, @@ -1614,9 +1671,6 @@ }, "type": "array" }, - "authentication": { - "$ref": "#/$defs/Authentication" - }, "templateBody": { "type": "boolean" } @@ -1723,9 +1777,6 @@ "labels": { "$ref": "#/$defs/Labels" }, - "connection": { - "type": "string" - }, "jmx": { "$ref": "#/$defs/EnvVar" }, @@ -1863,11 +1914,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "bindDN": { "type": "string" @@ -1883,8 +1937,6 @@ "type": "object", "required": [ "name", - "host", - "auth", "bindDN" ] }, @@ -1999,15 +2051,20 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MssqlCheck": { @@ -2036,8 +2093,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2049,8 +2112,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MysqlCheck": { @@ -2079,8 +2141,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2092,8 +2160,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "NamespaceCheck": { @@ -2299,8 +2366,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2316,7 +2386,6 @@ "type": "object", "required": [ "name", - "auth", "query", "index" ] @@ -2452,8 +2521,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2465,8 +2540,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "PrometheusCheck": { @@ -2492,12 +2566,21 @@ "transform": { "$ref": "#/$defs/Template" }, + "host": { + "type": "string" + }, "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "query": { "type": "string" } @@ -2506,7 +2589,6 @@ "type": "object", "required": [ "name", - "host", "query" ] }, @@ -2599,11 +2681,17 @@ "connection": { "type": "string" }, - "addr": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, + "addr": { + "type": "string" }, "db": { "type": "integer" @@ -2612,9 +2700,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "addr", - "db" + "name" ] }, "RelationshipSpec": { @@ -2760,15 +2846,17 @@ "host": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "host", - "auth" + "host" ] }, "SMBConnection": { @@ -2779,27 +2867,18 @@ "port": { "type": "integer" }, - "auth": { - "$ref": "#/$defs/Authentication" - }, - "domain": { - "type": "string" - }, - "workstation": { - "type": "string" + "username": { + "$ref": "#/$defs/EnvVar" }, - "sharename": { - "type": "string" + "password": { + "$ref": "#/$defs/EnvVar" }, - "searchPath": { + "domain": { "type": "string" } }, "additionalProperties": false, - "type": "object", - "required": [ - "auth" - ] + "type": "object" }, "SecretKeySelector": { "properties": { diff --git a/config/schemas/health_alertmanager.schema.json b/config/schemas/health_alertmanager.schema.json index e8b21a521..93a6c0009 100644 --- a/config/schemas/health_alertmanager.schema.json +++ b/config/schemas/health_alertmanager.schema.json @@ -29,11 +29,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "alerts": { "items": { @@ -62,22 +65,6 @@ "name" ] }, - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { diff --git a/config/schemas/health_awsconfig.schema.json b/config/schemas/health_awsconfig.schema.json index 3fc17bcba..b14fd834c 100644 --- a/config/schemas/health_awsconfig.schema.json +++ b/config/schemas/health_awsconfig.schema.json @@ -3,36 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/aws-config-check", "$ref": "#/$defs/AwsConfigCheck", "$defs": { - "AWSConnection": { - "properties": { - "connection": { - "type": "string" - }, - "accessKey": { - "$ref": "#/$defs/EnvVar" - }, - "secretKey": { - "$ref": "#/$defs/EnvVar" - }, - "region": { - "type": "string" - }, - "endpoint": { - "type": "string" - }, - "skipTLSVerify": { - "type": "boolean" - }, - "objectPath": { - "type": "string" - }, - "usePathStyle": { - "type": "boolean" - } - }, - "additionalProperties": false, - "type": "object" - }, "AwsConfigCheck": { "properties": { "description": { @@ -59,8 +29,29 @@ "query": { "type": "string" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" }, "aggregatorName": { "type": "string" diff --git a/config/schemas/health_awsconfigrule.schema.json b/config/schemas/health_awsconfigrule.schema.json index e3c0ba95e..245ec69bc 100644 --- a/config/schemas/health_awsconfigrule.schema.json +++ b/config/schemas/health_awsconfigrule.schema.json @@ -3,36 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/aws-config-rule-check", "$ref": "#/$defs/AwsConfigRuleCheck", "$defs": { - "AWSConnection": { - "properties": { - "connection": { - "type": "string" - }, - "accessKey": { - "$ref": "#/$defs/EnvVar" - }, - "secretKey": { - "$ref": "#/$defs/EnvVar" - }, - "region": { - "type": "string" - }, - "endpoint": { - "type": "string" - }, - "skipTLSVerify": { - "type": "boolean" - }, - "objectPath": { - "type": "string" - }, - "usePathStyle": { - "type": "boolean" - } - }, - "additionalProperties": false, - "type": "object" - }, "AwsConfigRuleCheck": { "properties": { "description": { @@ -74,8 +44,29 @@ }, "type": "array" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" } }, "additionalProperties": false, diff --git a/config/schemas/health_cloudwatch.schema.json b/config/schemas/health_cloudwatch.schema.json index e93212c60..821187ca1 100644 --- a/config/schemas/health_cloudwatch.schema.json +++ b/config/schemas/health_cloudwatch.schema.json @@ -50,14 +50,15 @@ "transform": { "$ref": "#/$defs/Template" }, - "filter": { + "Filter": { "$ref": "#/$defs/CloudWatchFilter" } }, "additionalProperties": false, "type": "object", "required": [ - "name" + "name", + "Filter" ] }, "CloudWatchFilter": { diff --git a/config/schemas/health_containerdPull.schema.json b/config/schemas/health_containerdPull.schema.json index d770e9562..073ebc88e 100644 --- a/config/schemas/health_containerdPull.schema.json +++ b/config/schemas/health_containerdPull.schema.json @@ -13,11 +13,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "ConfigMapKeySelector": { "properties": { diff --git a/config/schemas/health_dockerPull.schema.json b/config/schemas/health_dockerPull.schema.json index 2991e10a1..5bf82ae0f 100644 --- a/config/schemas/health_dockerPull.schema.json +++ b/config/schemas/health_dockerPull.schema.json @@ -13,11 +13,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "ConfigMapKeySelector": { "properties": { diff --git a/config/schemas/health_dockerPush.schema.json b/config/schemas/health_dockerPush.schema.json index 37eee6a5c..598c86e57 100644 --- a/config/schemas/health_dockerPush.schema.json +++ b/config/schemas/health_dockerPush.schema.json @@ -13,11 +13,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "ConfigMapKeySelector": { "properties": { diff --git a/config/schemas/health_dynatrace.schema.json b/config/schemas/health_dynatrace.schema.json index 07c64266b..8ca1b569f 100644 --- a/config/schemas/health_dynatrace.schema.json +++ b/config/schemas/health_dynatrace.schema.json @@ -41,6 +41,9 @@ "transform": { "$ref": "#/$defs/Template" }, + "connection": { + "type": "string" + }, "host": { "type": "string" }, diff --git a/config/schemas/health_elasticsearch.schema.json b/config/schemas/health_elasticsearch.schema.json index 7fbf163a7..16a9311df 100644 --- a/config/schemas/health_elasticsearch.schema.json +++ b/config/schemas/health_elasticsearch.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/elasticsearch-check", "$ref": "#/$defs/ElasticsearchCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -63,8 +47,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" diff --git a/config/schemas/health_folder.schema.json b/config/schemas/health_folder.schema.json index 080b7efe7..6f6ec1b01 100644 --- a/config/schemas/health_folder.schema.json +++ b/config/schemas/health_folder.schema.json @@ -33,22 +33,6 @@ "additionalProperties": false, "type": "object" }, - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -219,15 +203,17 @@ "host": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "host", - "auth" + "host" ] }, "SMBConnection": { @@ -238,27 +224,18 @@ "port": { "type": "integer" }, - "auth": { - "$ref": "#/$defs/Authentication" - }, - "domain": { - "type": "string" - }, - "workstation": { - "type": "string" + "username": { + "$ref": "#/$defs/EnvVar" }, - "sharename": { - "type": "string" + "password": { + "$ref": "#/$defs/EnvVar" }, - "searchPath": { + "domain": { "type": "string" } }, "additionalProperties": false, - "type": "object", - "required": [ - "auth" - ] + "type": "object" }, "SecretKeySelector": { "properties": { diff --git a/config/schemas/health_helm.schema.json b/config/schemas/health_helm.schema.json index 38c27395b..e7bb864df 100644 --- a/config/schemas/health_helm.schema.json +++ b/config/schemas/health_helm.schema.json @@ -13,11 +13,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "ConfigMapKeySelector": { "properties": { diff --git a/config/schemas/health_http.schema.json b/config/schemas/health_http.schema.json index 86f00743b..2a703be73 100644 --- a/config/schemas/health_http.schema.json +++ b/config/schemas/health_http.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/http-check", "$ref": "#/$defs/HTTPCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -87,6 +71,15 @@ "connection": { "type": "string" }, + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "endpoint": { "type": "string" }, @@ -129,9 +122,6 @@ }, "type": "array" }, - "authentication": { - "$ref": "#/$defs/Authentication" - }, "templateBody": { "type": "boolean" } diff --git a/config/schemas/health_jmeter.schema.json b/config/schemas/health_jmeter.schema.json index a05060192..dfbcb3c42 100644 --- a/config/schemas/health_jmeter.schema.json +++ b/config/schemas/health_jmeter.schema.json @@ -59,9 +59,6 @@ "labels": { "$ref": "#/$defs/Labels" }, - "connection": { - "type": "string" - }, "jmx": { "$ref": "#/$defs/EnvVar" }, diff --git a/config/schemas/health_ldap.schema.json b/config/schemas/health_ldap.schema.json index ecd98cdb4..715f818d1 100644 --- a/config/schemas/health_ldap.schema.json +++ b/config/schemas/health_ldap.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/ldap-check", "$ref": "#/$defs/LDAPCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -78,11 +62,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "bindDN": { "type": "string" @@ -98,8 +85,6 @@ "type": "object", "required": [ "name", - "host", - "auth", "bindDN" ] }, diff --git a/config/schemas/health_mongodb.schema.json b/config/schemas/health_mongodb.schema.json index edc955f9d..965c98918 100644 --- a/config/schemas/health_mongodb.schema.json +++ b/config/schemas/health_mongodb.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/mongo-db-check", "$ref": "#/$defs/MongoDBCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -86,15 +70,20 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "SecretKeySelector": { diff --git a/config/schemas/health_mssql.schema.json b/config/schemas/health_mssql.schema.json index 6bbe145e9..6c8d42c87 100644 --- a/config/schemas/health_mssql.schema.json +++ b/config/schemas/health_mssql.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/mssql-check", "$ref": "#/$defs/MssqlCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -95,8 +79,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -108,8 +98,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "SecretKeySelector": { diff --git a/config/schemas/health_mysql.schema.json b/config/schemas/health_mysql.schema.json index 412c22a59..987204f58 100644 --- a/config/schemas/health_mysql.schema.json +++ b/config/schemas/health_mysql.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/mysql-check", "$ref": "#/$defs/MysqlCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -95,8 +79,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -108,8 +98,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "SecretKeySelector": { diff --git a/config/schemas/health_opensearch.schema.json b/config/schemas/health_opensearch.schema.json index 62cf19442..b7aa49707 100644 --- a/config/schemas/health_opensearch.schema.json +++ b/config/schemas/health_opensearch.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/open-search-check", "$ref": "#/$defs/OpenSearchCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -98,8 +82,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -115,7 +102,6 @@ "type": "object", "required": [ "name", - "auth", "query", "index" ] diff --git a/config/schemas/health_postgres.schema.json b/config/schemas/health_postgres.schema.json index 196dce3b5..82bf8296a 100644 --- a/config/schemas/health_postgres.schema.json +++ b/config/schemas/health_postgres.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/postgres-check", "$ref": "#/$defs/PostgresCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -95,8 +79,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -108,8 +98,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "SecretKeySelector": { diff --git a/config/schemas/health_prometheus.schema.json b/config/schemas/health_prometheus.schema.json index 9e7a1de14..b4dcab392 100644 --- a/config/schemas/health_prometheus.schema.json +++ b/config/schemas/health_prometheus.schema.json @@ -3,6 +3,48 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/prometheus-check", "$ref": "#/$defs/PrometheusCheck", "$defs": { + "ConfigMapKeySelector": { + "properties": { + "name": { + "type": "string" + }, + "key": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "key" + ] + }, + "EnvVar": { + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "valueFrom": { + "$ref": "#/$defs/EnvVarSource" + } + }, + "additionalProperties": false, + "type": "object" + }, + "EnvVarSource": { + "properties": { + "configMapKeyRef": { + "$ref": "#/$defs/ConfigMapKeySelector" + }, + "secretKeyRef": { + "$ref": "#/$defs/SecretKeySelector" + } + }, + "additionalProperties": false, + "type": "object" + }, "Labels": { "patternProperties": { ".*": { @@ -34,12 +76,21 @@ "transform": { "$ref": "#/$defs/Template" }, + "host": { + "type": "string" + }, "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "query": { "type": "string" } @@ -48,10 +99,24 @@ "type": "object", "required": [ "name", - "host", "query" ] }, + "SecretKeySelector": { + "properties": { + "name": { + "type": "string" + }, + "key": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "key" + ] + }, "Template": { "properties": { "template": { diff --git a/config/schemas/health_redis.schema.json b/config/schemas/health_redis.schema.json index adc3a6bd6..153d4cc4b 100644 --- a/config/schemas/health_redis.schema.json +++ b/config/schemas/health_redis.schema.json @@ -3,22 +3,6 @@ "$id": "https://github.com/flanksource/canary-checker/api/v1/redis-check", "$ref": "#/$defs/RedisCheck", "$defs": { - "Authentication": { - "properties": { - "username": { - "$ref": "#/$defs/EnvVar" - }, - "password": { - "$ref": "#/$defs/EnvVar" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] - }, "ConfigMapKeySelector": { "properties": { "name": { @@ -86,11 +70,17 @@ "connection": { "type": "string" }, - "addr": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, + "addr": { + "type": "string" }, "db": { "type": "integer" @@ -99,9 +89,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "addr", - "db" + "name" ] }, "SecretKeySelector": { diff --git a/config/schemas/topology.schema.json b/config/schemas/topology.schema.json index daacb364b..480460cd2 100644 --- a/config/schemas/topology.schema.json +++ b/config/schemas/topology.schema.json @@ -59,11 +59,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "alerts": { "items": { @@ -102,11 +105,7 @@ } }, "additionalProperties": false, - "type": "object", - "required": [ - "username", - "password" - ] + "type": "object" }, "AwsConfigCheck": { "properties": { @@ -134,8 +133,29 @@ "query": { "type": "string" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" }, "aggregatorName": { "type": "string" @@ -189,8 +209,29 @@ }, "type": "array" }, - "awsConnection": { - "$ref": "#/$defs/AWSConnection" + "connection": { + "type": "string" + }, + "accessKey": { + "$ref": "#/$defs/EnvVar" + }, + "secretKey": { + "$ref": "#/$defs/EnvVar" + }, + "region": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "skipTLSVerify": { + "type": "boolean" + }, + "objectPath": { + "type": "string" + }, + "usePathStyle": { + "type": "boolean" } }, "additionalProperties": false, @@ -569,14 +610,15 @@ "transform": { "$ref": "#/$defs/Template" }, - "filter": { + "Filter": { "$ref": "#/$defs/CloudWatchFilter" } }, "additionalProperties": false, "type": "object", "required": [ - "name" + "name", + "Filter" ] }, "CloudWatchFilter": { @@ -1113,6 +1155,9 @@ "transform": { "$ref": "#/$defs/Template" }, + "connection": { + "type": "string" + }, "host": { "type": "string" }, @@ -1230,8 +1275,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -1542,6 +1590,15 @@ "connection": { "type": "string" }, + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "endpoint": { "type": "string" }, @@ -1584,9 +1641,6 @@ }, "type": "array" }, - "authentication": { - "$ref": "#/$defs/Authentication" - }, "templateBody": { "type": "boolean" } @@ -1693,9 +1747,6 @@ "labels": { "$ref": "#/$defs/Labels" }, - "connection": { - "type": "string" - }, "jmx": { "$ref": "#/$defs/EnvVar" }, @@ -1833,11 +1884,14 @@ "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "bindDN": { "type": "string" @@ -1853,8 +1907,6 @@ "type": "object", "required": [ "name", - "host", - "auth", "bindDN" ] }, @@ -1969,15 +2021,20 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MssqlCheck": { @@ -2006,8 +2063,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2019,8 +2082,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "MysqlCheck": { @@ -2049,8 +2111,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2062,8 +2130,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "NamespaceCheck": { @@ -2269,8 +2336,11 @@ "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2286,7 +2356,6 @@ "type": "object", "required": [ "name", - "auth", "query", "index" ] @@ -2422,8 +2491,14 @@ "connection": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "url": { + "type": "string" + }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" }, "query": { "type": "string" @@ -2435,8 +2510,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "connection" + "name" ] }, "PrometheusCheck": { @@ -2462,12 +2536,21 @@ "transform": { "$ref": "#/$defs/Template" }, + "host": { + "type": "string" + }, "connection": { "type": "string" }, - "host": { + "url": { "type": "string" }, + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, "query": { "type": "string" } @@ -2476,7 +2559,6 @@ "type": "object", "required": [ "name", - "host", "query" ] }, @@ -2569,11 +2651,17 @@ "connection": { "type": "string" }, - "addr": { + "url": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" + }, + "addr": { + "type": "string" }, "db": { "type": "integer" @@ -2582,9 +2670,7 @@ "additionalProperties": false, "type": "object", "required": [ - "name", - "addr", - "db" + "name" ] }, "RelationshipSpec": { @@ -2730,15 +2816,17 @@ "host": { "type": "string" }, - "auth": { - "$ref": "#/$defs/Authentication" + "username": { + "$ref": "#/$defs/EnvVar" + }, + "password": { + "$ref": "#/$defs/EnvVar" } }, "additionalProperties": false, "type": "object", "required": [ - "host", - "auth" + "host" ] }, "SMBConnection": { @@ -2749,27 +2837,18 @@ "port": { "type": "integer" }, - "auth": { - "$ref": "#/$defs/Authentication" - }, - "domain": { - "type": "string" - }, - "workstation": { - "type": "string" + "username": { + "$ref": "#/$defs/EnvVar" }, - "sharename": { - "type": "string" + "password": { + "$ref": "#/$defs/EnvVar" }, - "searchPath": { + "domain": { "type": "string" } }, "additionalProperties": false, - "type": "object", - "required": [ - "auth" - ] + "type": "object" }, "SecretKeySelector": { "properties": { diff --git a/fixtures/aws/ec2_pass.yaml b/fixtures/aws/ec2_pass.yaml index b70ea01ea..618dfe225 100644 --- a/fixtures/aws/ec2_pass.yaml +++ b/fixtures/aws/ec2_pass.yaml @@ -7,7 +7,7 @@ spec: spec: ec2: - description: test instance - accessKeyID: + accessKey: valueFrom: secretKeyRef: name: aws-credentials diff --git a/fixtures/azure/devops.yaml b/fixtures/azure/devops.yaml index 3c1f0062b..db6095f49 100644 --- a/fixtures/azure/devops.yaml +++ b/fixtures/azure/devops.yaml @@ -7,7 +7,8 @@ spec: azureDevops: - project: Demo1 pipeline: ^windows- - personalAccessToken: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + personalAccessToken: + value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx organization: flanksource variable: env: prod diff --git a/fixtures/datasources/SFTP/sftp_pass.yaml b/fixtures/datasources/SFTP/sftp_pass.yaml index 7f956fc4a..79d7c459f 100644 --- a/fixtures/datasources/SFTP/sftp_pass.yaml +++ b/fixtures/datasources/SFTP/sftp_pass.yaml @@ -9,9 +9,8 @@ spec: name: sample sftp check sftpConnection: host: 192.168.1.5 - auth: - username: - value: - password: - value: - maxCount: 10 \ No newline at end of file + username: + value: + password: + value: + maxCount: 10 diff --git a/fixtures/datasources/elasticsearch_fail.yaml b/fixtures/datasources/elasticsearch_fail.yaml index e2fac9271..db0ed8422 100644 --- a/fixtures/datasources/elasticsearch_fail.yaml +++ b/fixtures/datasources/elasticsearch_fail.yaml @@ -18,14 +18,13 @@ spec: } results: 1 name: elasticsearch-fail - auth: - username: - valueFrom: - secretKeyRef: - name: search - key: ELASTIC_SEARCH_USERNAME - password: - valueFrom: - secretKeyRef: - name: search - key: ELASTIC_SEARCH_PASSWORD + username: + valueFrom: + secretKeyRef: + name: search + key: ELASTIC_SEARCH_USERNAME + password: + valueFrom: + secretKeyRef: + name: search + key: ELASTIC_SEARCH_PASSWORD diff --git a/fixtures/datasources/elasticsearch_pass.yaml b/fixtures/datasources/elasticsearch_pass.yaml index 7cee8c31f..5981488c8 100644 --- a/fixtures/datasources/elasticsearch_pass.yaml +++ b/fixtures/datasources/elasticsearch_pass.yaml @@ -18,14 +18,13 @@ spec: } results: 1 name: elasticsearch_pass - auth: - username: - valueFrom: - secretKeyRef: - name: search - key: ELASTIC_SEARCH_USERNAME - password: - valueFrom: - secretKeyRef: - name: search - key: ELASTIC_SEARCH_PASSWORD \ No newline at end of file + username: + valueFrom: + secretKeyRef: + name: search + key: ELASTIC_SEARCH_USERNAME + password: + valueFrom: + secretKeyRef: + name: search + key: ELASTIC_SEARCH_PASSWORD diff --git a/fixtures/datasources/ldap_pass.yaml b/fixtures/datasources/ldap_pass.yaml index 4804ef707..199a2e282 100644 --- a/fixtures/datasources/ldap_pass.yaml +++ b/fixtures/datasources/ldap_pass.yaml @@ -5,21 +5,19 @@ metadata: spec: interval: 30 ldap: - - host: ldap://apacheds.ldap.svc.cluster.local:10389 + - url: ldap://apacheds.ldap.svc.cluster.local:10389 name: ldap user login - auth: - username: - value: uid=admin,ou=system - password: - value: secret + username: + value: uid=admin,ou=system + password: + value: secret bindDN: ou=users,dc=example,dc=com userSearch: "(&(objectClass=organizationalPerson))" - - host: ldap://apacheds.ldap.svc.cluster.local:10389 + - url: ldap://apacheds.ldap.svc.cluster.local:10389 name: ldap group login - auth: - username: - value: uid=admin,ou=system - password: - value: secret + username: + value: uid=admin,ou=system + password: + value: secret bindDN: ou=groups,dc=example,dc=com userSearch: "(&(objectClass=groupOfNames))" diff --git a/fixtures/datasources/mongo_fail.yaml b/fixtures/datasources/mongo_fail.yaml index e1ba27aca..69492e5c2 100644 --- a/fixtures/datasources/mongo_fail.yaml +++ b/fixtures/datasources/mongo_fail.yaml @@ -7,11 +7,10 @@ metadata: spec: interval: 30 mongodb: - - connection: mongodb://mongo2.default.svc.cluster.local:27017/?authSource=admin + - url: mongodb://mongo2.default.svc.cluster.local:27017/?authSource=admin name: mongo wrong password description: test mongo instance - auth: - username: - value: mongoadmin - password: - value: wronghere2 + username: + value: mongoadmin + password: + value: wronghere2 diff --git a/fixtures/datasources/mongo_pass.yaml b/fixtures/datasources/mongo_pass.yaml index 5c037a646..46e4dabd6 100644 --- a/fixtures/datasources/mongo_pass.yaml +++ b/fixtures/datasources/mongo_pass.yaml @@ -5,14 +5,9 @@ metadata: spec: interval: 30 mongodb: - - connection: mongodb://$(username):$(password)@mongo.default.svc.cluster.local:27017/?authSource=admin + - url: mongodb://$(username):$(password)@mongo.default.svc.cluster.local:27017/?authSource=admin name: mongo ping check - description: mongo ping - auth: - username: - value: mongoadmin - password: - value: secret - dns: - - query: mongo.default.svc.cluster.local - name: mongo dns check + username: + value: mongoadmin + password: + value: secret diff --git a/fixtures/datasources/mssql_fail.yaml b/fixtures/datasources/mssql_fail.yaml index 2027f9565..fb784396e 100644 --- a/fixtures/datasources/mssql_fail.yaml +++ b/fixtures/datasources/mssql_fail.yaml @@ -7,7 +7,7 @@ metadata: spec: interval: 30 mssql: - - connection: "server=mssql.platformsystem;user id=sa;password=S0m3p@sswd;port=32010;database=master" #wrong server name for failure + - url: "server=mssql.platformsystem;user id=sa;password=S0m3p@sswd;port=32010;database=master" #wrong server name for failure name: mssql servername query: "SELECT 1" results: 1 diff --git a/fixtures/datasources/mssql_pass.yaml b/fixtures/datasources/mssql_pass.yaml index d318f833b..032a1e867 100644 --- a/fixtures/datasources/mssql_pass.yaml +++ b/fixtures/datasources/mssql_pass.yaml @@ -5,12 +5,11 @@ metadata: spec: interval: 30 mssql: - - connection: "server=mssql.default.svc.cluster.local;user id=$(username);password=$(password);port=1433;database=master" + - url: "server=mssql.default.svc.cluster.local;user id=$(username);password=$(password);port=1433;database=master" name: mssql pass - auth: - username: - value: sa - password: - value: S0m3p@sswd + username: + value: sa + password: + value: S0m3p@sswd query: "SELECT 1" results: 1 diff --git a/fixtures/datasources/mysql_fail.yaml b/fixtures/datasources/mysql_fail.yaml index 4b1f947e0..e11bcf9a6 100644 --- a/fixtures/datasources/mysql_fail.yaml +++ b/fixtures/datasources/mysql_fail.yaml @@ -5,12 +5,11 @@ metadata: spec: interval: 30 mysql: - - connection: "$(username):$(password)@tcp(mysql.default.svc.cluster.local:3306)/mysqldb" + - url: "$(username):$(password)@tcp(mysql.default.svc.cluster.local:3306)/mysqldb" name: mysql wrong password - auth: - username: - value: mysqladmin - password: - value: wrongpassword + username: + value: mysqladmin + password: + value: wrongpassword query: "SELECT 1" results: 1 diff --git a/fixtures/datasources/mysql_pass.yaml b/fixtures/datasources/mysql_pass.yaml index ba8d9a5cb..4d785721e 100644 --- a/fixtures/datasources/mysql_pass.yaml +++ b/fixtures/datasources/mysql_pass.yaml @@ -5,12 +5,11 @@ metadata: spec: interval: 30 mysql: - - connection: "$(username):$(password)@tcp(mysql.default.svc.cluster.local:3306)/mysqldb" + - url: "$(username):$(password)@tcp(mysql.default.svc.cluster.local:3306)/mysqldb" name: mysql ping check - auth: - username: - value: mysqladmin - password: - value: admin123 + username: + value: mysqladmin + password: + value: admin123 query: "SELECT 1" results: 1 diff --git a/fixtures/datasources/opensearch_fail.yaml b/fixtures/datasources/opensearch_fail.yaml index 58269133f..8b3659bf6 100644 --- a/fixtures/datasources/opensearch_fail.yaml +++ b/fixtures/datasources/opensearch_fail.yaml @@ -21,14 +21,13 @@ spec: } } results: 100 - auth: - username: - valueFrom: - secretKeyRef: - name: search - key: OPENSEARCH_USERNAME - password: - valueFrom: - secretKeyRef: - name: search - key: OPENSEARCH_PASSWORD + username: + valueFrom: + secretKeyRef: + name: search + key: OPENSEARCH_USERNAME + password: + valueFrom: + secretKeyRef: + name: search + key: OPENSEARCH_PASSWORD diff --git a/fixtures/datasources/opensearch_pass.yaml b/fixtures/datasources/opensearch_pass.yaml index cd88474bd..14b1dbe51 100644 --- a/fixtures/datasources/opensearch_pass.yaml +++ b/fixtures/datasources/opensearch_pass.yaml @@ -20,14 +20,13 @@ spec: } } results: 1 - auth: - username: - valueFrom: - secretKeyRef: - name: search - key: OPENSEARCH_USERNAME - password: - valueFrom: - secretKeyRef: - name: search - key: OPENSEARCH_PASSWORD + username: + valueFrom: + secretKeyRef: + name: search + key: OPENSEARCH_USERNAME + password: + valueFrom: + secretKeyRef: + name: search + key: OPENSEARCH_PASSWORD diff --git a/fixtures/datasources/postgres_fail.yaml b/fixtures/datasources/postgres_fail.yaml index 1eb171236..07bedc172 100644 --- a/fixtures/datasources/postgres_fail.yaml +++ b/fixtures/datasources/postgres_fail.yaml @@ -8,12 +8,11 @@ metadata: spec: interval: 30 postgres: - - connection: "user=$(username) dbname=pqgotest sslmode=verify-full" + - url: "user=$(username) dbname=pqgotest sslmode=verify-full" name: postgres blank password - auth: - username: - value: pqgotest - password: - value: "" + username: + value: pqgotest + password: + value: "" query: "SELECT 1" results: 1 diff --git a/fixtures/datasources/postgres_pass.yaml b/fixtures/datasources/postgres_pass.yaml index b3a4909b3..1663cbed7 100644 --- a/fixtures/datasources/postgres_pass.yaml +++ b/fixtures/datasources/postgres_pass.yaml @@ -5,13 +5,12 @@ metadata: spec: interval: 30 postgres: - - connection: "postgres://$(username):$(password)@postgres.default.svc.cluster.local:5432/postgres?sslmode=disable" + - url: "postgres://$(username):$(password)@postgres.default.svc.cluster.local:5432/postgres?sslmode=disable" name: postgres schemas check - auth: - username: - value: postgresadmin - password: - value: admin123 + username: + value: postgresadmin + password: + value: admin123 query: SELECT current_schemas(true) display: template: | diff --git a/fixtures/datasources/prometheus.yaml b/fixtures/datasources/prometheus.yaml index f87ed8974..85ca519e0 100644 --- a/fixtures/datasources/prometheus.yaml +++ b/fixtures/datasources/prometheus.yaml @@ -5,10 +5,8 @@ metadata: spec: interval: 30 prometheus: - - host: https://prometheus.demo.aws.flanksource.com/ + - url: https://prometheus.demo.aws.flanksource.com/ name: prometheus-check query: kubernetes_build_info{job!~"kube-dns|coredns"} display: - template: "{{ (index .results 0).git_version }}" - test: - template: "true" + expr: results[0].git_version diff --git a/fixtures/k8s/http_auth_configmap.yaml b/fixtures/k8s/http_auth_configmap.yaml index 6080740b9..803ec0cc2 100644 --- a/fixtures/k8s/http_auth_configmap.yaml +++ b/fixtures/k8s/http_auth_configmap.yaml @@ -6,14 +6,13 @@ spec: http: - endpoint: https://httpbin.demo.aws.flanksource.com/basic-auth/hello/world responseCodes: [200] - authentication: - username: - valueFrom: - configMapKeyRef: - name: basic-auth - key: user - password: - valueFrom: - configMapKeyRef: - name: basic-auth - key: pass + username: + valueFrom: + configMapKeyRef: + name: basic-auth + key: user + password: + valueFrom: + configMapKeyRef: + name: basic-auth + key: pass diff --git a/fixtures/k8s/http_auth_secret.yaml b/fixtures/k8s/http_auth_secret.yaml index 431c960c2..20290d7d4 100644 --- a/fixtures/k8s/http_auth_secret.yaml +++ b/fixtures/k8s/http_auth_secret.yaml @@ -8,14 +8,13 @@ spec: http: - endpoint: https://httpbin.demo.aws.flanksource.com/basic-auth/hello/world responseCodes: [200] - authentication: - username: - valueFrom: - secretKeyRef: - name: basic-auth - key: user - password: - valueFrom: - secretKeyRef: - name: basic-auth - key: pass + username: + valueFrom: + secretKeyRef: + name: basic-auth + key: user + password: + valueFrom: + secretKeyRef: + name: basic-auth + key: pass diff --git a/fixtures/minimal/http_auth.yaml b/fixtures/minimal/http_auth.yaml index 562aca01e..d557779f1 100644 --- a/fixtures/minimal/http_auth.yaml +++ b/fixtures/minimal/http_auth.yaml @@ -8,8 +8,7 @@ spec: responseCodes: [401] - endpoint: https://httpbin.demo.aws.flanksource.com/basic-auth/hello/world responseCodes: [200] - authentication: - username: - value: hello - password: - value: world + username: + value: hello + password: + value: world diff --git a/fixtures/minimal/http_pass_results_mode_pass.yaml b/fixtures/minimal/http_pass_results_mode_pass.yaml index eb68efac3..95335ff03 100644 --- a/fixtures/minimal/http_pass_results_mode_pass.yaml +++ b/fixtures/minimal/http_pass_results_mode_pass.yaml @@ -6,14 +6,14 @@ spec: resultMode: "junit" interval: 30 http: - - endpoint: https://httpbin.demo.aws.flanksource.com/status/200 + - url: https://httpbin.demo.aws.flanksource.com/status/200 name: http pass response 200 status code thresholdMillis: 30000 responseCodes: [201, 301, 200] responseContent: "" maxSSLExpiry: 7 description: "HTTP dummy test 2" - - endpoint: https://httpbin.demo.aws.flanksource.com/status/201 + - url: https://httpbin.demo.aws.flanksource.com/status/201 name: http pass response 201 status code thresholdMillis: 30000 responseCodes: [201] diff --git a/fixtures/minimal/http_pass_single.yaml b/fixtures/minimal/http_pass_single.yaml index 0781868a2..a3619f58c 100644 --- a/fixtures/minimal/http_pass_single.yaml +++ b/fixtures/minimal/http_pass_single.yaml @@ -8,10 +8,17 @@ spec: interval: 30 http: - endpoint: https://httpbin.demo.aws.flanksource.com/status/200 - name: sample-check - thresholdMillis: 3000 + name: http-deprecated-endpoint + - name: http-minimal-check + url: https://httpbin.demo.aws.flanksource.com/status/200 + - name: http-param-tests + url: https://httpbin.demo.aws.flanksource.com/status/200 responseCodes: [201, 200, 301] responseContent: "" maxSSLExpiry: 7 + - name: http-expr-tests + url: https://httpbin.demo.aws.flanksource.com/status/200 test: - expr: "code == 200" + expr: "code in [200,201,301] and sslAge > Duration('7d')" + display: + template: "code={{.code}}, age={{.sslAge}}" diff --git a/fixtures/quarantine/smb_pass.yaml b/fixtures/quarantine/smb_pass.yaml index 6733a4e3c..612dde49b 100644 --- a/fixtures/quarantine/smb_pass.yaml +++ b/fixtures/quarantine/smb_pass.yaml @@ -8,17 +8,16 @@ spec: # Check for any backup not older than 7 days and min size 25 bytes - path: \\windows-server\sharename\folder smbConnection: - auth: - username: - valueFrom: - secretKeyRef: - name: smb-credentials - key: USERNAME - password: - valueFrom: - secretKeyRef: - name: ssmb-credentials - key: PASSWORD + username: + valueFrom: + secretKeyRef: + name: smb-credentials + key: USERNAME + password: + valueFrom: + secretKeyRef: + name: ssmb-credentials + key: PASSWORD filter: regex: "(.*)backup.zip$" maxAge: 7d From 2dbc3a8b683425a01895bfa4ae334071b0f40ced Mon Sep 17 00:00:00 2001 From: Moshe Immerman Date: Mon, 3 Jul 2023 10:02:35 +0300 Subject: [PATCH 02/10] chore: inline Cloudwatch.filter --- api/v1/checks.go | 16 ++++++++-------- api/v1/zz_generated.deepcopy.go | 2 +- checks/cloudwatch.go | 8 ++++---- config/schemas/canary.schema.json | 18 ++++-------------- config/schemas/component.schema.json | 18 ++++-------------- config/schemas/health_cloudwatch.schema.json | 18 ++++-------------- config/schemas/topology.schema.json | 18 ++++-------------- 7 files changed, 29 insertions(+), 69 deletions(-) diff --git a/api/v1/checks.go b/api/v1/checks.go index 83f08d338..206a10dc6 100644 --- a/api/v1/checks.go +++ b/api/v1/checks.go @@ -144,10 +144,10 @@ func (c S3Check) GetType() string { } type CloudWatchCheck struct { - Description `yaml:",inline" json:",inline"` - AWSConnection `yaml:",inline" json:",inline"` - Templatable `yaml:",inline" json:",inline"` - Filter CloudWatchFilter `yaml:",inline" json:",inline"` + Description `yaml:",inline" json:",inline"` + AWSConnection `yaml:",inline" json:",inline"` + Templatable `yaml:",inline" json:",inline"` + CloudWatchFilter `yaml:",inline" json:",inline"` } type CloudWatchFilter struct { @@ -159,11 +159,11 @@ type CloudWatchFilter struct { func (c CloudWatchCheck) GetEndpoint() string { endpoint := c.Region - if c.Filter.ActionPrefix != nil { - endpoint += "-" + *c.Filter.ActionPrefix + if c.CloudWatchFilter.ActionPrefix != nil { + endpoint += "-" + *c.CloudWatchFilter.ActionPrefix } - if c.Filter.AlarmPrefix != nil { - endpoint += "-" + *c.Filter.AlarmPrefix + if c.CloudWatchFilter.AlarmPrefix != nil { + endpoint += "-" + *c.CloudWatchFilter.AlarmPrefix } return endpoint } diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index e8724addd..838328380 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -741,7 +741,7 @@ func (in *CloudWatchCheck) DeepCopyInto(out *CloudWatchCheck) { in.Description.DeepCopyInto(&out.Description) in.AWSConnection.DeepCopyInto(&out.AWSConnection) out.Templatable = in.Templatable - in.Filter.DeepCopyInto(&out.Filter) + in.CloudWatchFilter.DeepCopyInto(&out.CloudWatchFilter) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudWatchCheck. diff --git a/checks/cloudwatch.go b/checks/cloudwatch.go index 2b4a85d45..aeb745aae 100644 --- a/checks/cloudwatch.go +++ b/checks/cloudwatch.go @@ -49,10 +49,10 @@ func (c *CloudWatchChecker) Check(ctx *context.Context, extConfig external.Check client := cloudwatch.NewFromConfig(*cfg) maxRecords := int32(100) alarms, err := client.DescribeAlarms(ctx, &cloudwatch.DescribeAlarmsInput{ - AlarmNames: check.Filter.Alarms, - AlarmNamePrefix: check.Filter.AlarmPrefix, - ActionPrefix: check.Filter.ActionPrefix, - StateValue: types.StateValue(check.Filter.State), + AlarmNames: check.CloudWatchFilter.Alarms, + AlarmNamePrefix: check.CloudWatchFilter.AlarmPrefix, + ActionPrefix: check.CloudWatchFilter.ActionPrefix, + StateValue: types.StateValue(check.CloudWatchFilter.State), MaxRecords: &maxRecords, }) if err != nil { diff --git a/config/schemas/canary.schema.json b/config/schemas/canary.schema.json index 953269aa0..225d7af56 100644 --- a/config/schemas/canary.schema.json +++ b/config/schemas/canary.schema.json @@ -704,19 +704,6 @@ "transform": { "$ref": "#/$defs/Template" }, - "Filter": { - "$ref": "#/$defs/CloudWatchFilter" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "name", - "Filter" - ] - }, - "CloudWatchFilter": { - "properties": { "actionPrefix": { "type": "string" }, @@ -734,7 +721,10 @@ } }, "additionalProperties": false, - "type": "object" + "type": "object", + "required": [ + "name" + ] }, "ConfigDBCheck": { "properties": { diff --git a/config/schemas/component.schema.json b/config/schemas/component.schema.json index 82b2ca9cd..bb155fe66 100644 --- a/config/schemas/component.schema.json +++ b/config/schemas/component.schema.json @@ -610,19 +610,6 @@ "transform": { "$ref": "#/$defs/Template" }, - "Filter": { - "$ref": "#/$defs/CloudWatchFilter" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "name", - "Filter" - ] - }, - "CloudWatchFilter": { - "properties": { "actionPrefix": { "type": "string" }, @@ -640,7 +627,10 @@ } }, "additionalProperties": false, - "type": "object" + "type": "object", + "required": [ + "name" + ] }, "Component": { "properties": { diff --git a/config/schemas/health_cloudwatch.schema.json b/config/schemas/health_cloudwatch.schema.json index 821187ca1..dac455c88 100644 --- a/config/schemas/health_cloudwatch.schema.json +++ b/config/schemas/health_cloudwatch.schema.json @@ -50,19 +50,6 @@ "transform": { "$ref": "#/$defs/Template" }, - "Filter": { - "$ref": "#/$defs/CloudWatchFilter" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "name", - "Filter" - ] - }, - "CloudWatchFilter": { - "properties": { "actionPrefix": { "type": "string" }, @@ -80,7 +67,10 @@ } }, "additionalProperties": false, - "type": "object" + "type": "object", + "required": [ + "name" + ] }, "ConfigMapKeySelector": { "properties": { diff --git a/config/schemas/topology.schema.json b/config/schemas/topology.schema.json index 480460cd2..2db544f6b 100644 --- a/config/schemas/topology.schema.json +++ b/config/schemas/topology.schema.json @@ -610,19 +610,6 @@ "transform": { "$ref": "#/$defs/Template" }, - "Filter": { - "$ref": "#/$defs/CloudWatchFilter" - } - }, - "additionalProperties": false, - "type": "object", - "required": [ - "name", - "Filter" - ] - }, - "CloudWatchFilter": { - "properties": { "actionPrefix": { "type": "string" }, @@ -640,7 +627,10 @@ } }, "additionalProperties": false, - "type": "object" + "type": "object", + "required": [ + "name" + ] }, "ComponentCheck": { "properties": { From a182ee70bc8b08fd67af9bf47ce87f25141315af Mon Sep 17 00:00:00 2001 From: Yash Mehrotra Date: Wed, 5 Jul 2023 12:28:58 +0530 Subject: [PATCH 03/10] feat: add test for component check relationships --- pkg/topology/checks/component_check_test.go | 73 ++++++++++++++++ pkg/topology/checks/suite_test.go | 96 +++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 pkg/topology/checks/component_check_test.go create mode 100644 pkg/topology/checks/suite_test.go diff --git a/pkg/topology/checks/component_check_test.go b/pkg/topology/checks/component_check_test.go new file mode 100644 index 000000000..5f9b6d64a --- /dev/null +++ b/pkg/topology/checks/component_check_test.go @@ -0,0 +1,73 @@ +package checks + +import ( + v1 "github.com/flanksource/canary-checker/api/v1" + "github.com/flanksource/canary-checker/pkg" + "github.com/flanksource/canary-checker/pkg/db" + "github.com/flanksource/duty/models" + "github.com/google/uuid" + ginkgo "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = ginkgo.Describe("Test component check sync job", ginkgo.Ordered, func() { + component := pkg.Component{ + Name: "Component", + ComponentChecks: []v1.ComponentCheck{{ + Selector: v1.ResourceSelector{ + Name: "ComponentCheckSelector", + LabelSelector: "check-target=api", + }, + }}, + } + canary := models.Canary{ + ID: uuid.New(), + Name: "Canary", + Spec: []byte(`{"spec": {}}`), + } + + ginkgo.BeforeAll(func() { + err := db.Gorm.Create(&component).Error + Expect(err).To(BeNil()) + + err = db.Gorm.Create(&canary).Error + Expect(err).To(BeNil()) + + check1 := pkg.Check{ + Name: "Check-1", + CanaryID: canary.ID, + Labels: map[string]string{ + "check-target": "api", + "name": "check-1", + }, + } + check2 := pkg.Check{ + Name: "Check-2", + CanaryID: canary.ID, + Labels: map[string]string{ + "check-target": "ui", + "name": "check-2", + }, + } + check3 := pkg.Check{ + Name: "Check-3", + CanaryID: canary.ID, + Labels: map[string]string{ + "check-target": "api", + "name": "check-3", + }, + } + + err = db.Gorm.Create([]pkg.Check{check1, check2, check3}).Error + Expect(err).To(BeNil()) + }) + + ginkgo.It("should create check component relationships", func() { + ComponentCheckRun() + cr, err := db.GetCheckRelationshipsForComponent(component.ID) + Expect(err).To(BeNil()) + + // Check-1 and Check-3 should be present but not Check-2 + Expect(len(cr)).To(Equal(2)) + }) +}) diff --git a/pkg/topology/checks/suite_test.go b/pkg/topology/checks/suite_test.go new file mode 100644 index 000000000..a464679fc --- /dev/null +++ b/pkg/topology/checks/suite_test.go @@ -0,0 +1,96 @@ +package checks + +import ( + "context" + "fmt" + "io" + "os" + "testing" + + embeddedPG "github.com/fergusstrange/embedded-postgres" + "github.com/flanksource/canary-checker/pkg/db" + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "gorm.io/gorm" +) + +var ( + postgresServer *embeddedPG.EmbeddedPostgres +) + +func TestComponentCheckRun(t *testing.T) { + RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Test component check runs") +} + +var _ = ginkgo.BeforeSuite(func() { + port := 9841 + config := GetPGConfig("test_component_check", port) + postgresServer = embeddedPG.NewDatabase(config) + if err := postgresServer.Start(); err != nil { + ginkgo.Fail(err.Error()) + } + logger.Infof("Started postgres on port: %d", port) + + db.Gorm, db.Pool = setupDB(fmt.Sprintf("postgres://postgres:postgres@localhost:%d/test_component_check?sslmode=disable", port)) +}) + +var _ = ginkgo.AfterSuite(func() { + logger.Infof("Stopping postgres") + if err := postgresServer.Stop(); err != nil { + ginkgo.Fail(err.Error()) + } +}) + +func setupDB(connectionString string) (*gorm.DB, *pgxpool.Pool) { + pgxpool, err := duty.NewPgxPool(connectionString) + if err != nil { + ginkgo.Fail(err.Error()) + } + + conn, err := pgxpool.Acquire(context.Background()) + if err != nil { + ginkgo.Fail(err.Error()) + } + defer conn.Release() + + gormDB, err := duty.NewGorm(connectionString, duty.DefaultGormConfig()) + if err != nil { + ginkgo.Fail(err.Error()) + } + + if err = duty.Migrate(connectionString, nil); err != nil { + ginkgo.Fail(err.Error()) + } + + return gormDB, pgxpool +} + +func GetPGConfig(database string, port int) embeddedPG.Config { + // We are firing up multiple instances of the embedded postgres server at once when running tests in parallel. + // + // By default fergusstrange/embedded-postgres directly extracts the Postgres binary to a set location + // (/home/runner/.embedded-postgres-go/extracted/bin/initdb) and starts it. + // If two instances try to do this at the same time, they conflict, and throw the error + // "unable to extract postgres archive: open /home/runner/.embedded-postgres-go/extracted/bin/initdb: text file busy." + // + // This is a way to have separate instances of the running postgres servers. + + var runTimePath string + homeDir, err := os.UserHomeDir() + if err != nil { + logger.Errorf("error getting user home dir: %v", err) + runTimePath = fmt.Sprintf("/tmp/.embedded-postgres-go/extracted-%d", port) + } else { + runTimePath = fmt.Sprintf("%s/.embedded-postgres-go/extracted-%d", homeDir, port) + } + + return embeddedPG.DefaultConfig(). + Database(database). + Port(uint32(port)). + RuntimePath(runTimePath). + Logger(io.Discard) +} From 611aa51908644bac6b4b0646a9d5b8278539bbbb Mon Sep 17 00:00:00 2001 From: Yash Mehrotra Date: Wed, 5 Jul 2023 19:29:08 +0530 Subject: [PATCH 04/10] feat: add status for pods (#1123) * feat: add status for pods in kubernetes topology * chore: update status and status reason fields for k8s --- api/v1/checks.go | 7 ------- checks/kubernetes.go | 20 +++++--------------- go.mod | 1 + go.sum | 4 +++- templating/js/k8s.js | 33 ++------------------------------- 5 files changed, 11 insertions(+), 54 deletions(-) diff --git a/api/v1/checks.go b/api/v1/checks.go index d73c7adf1..83063bc1b 100644 --- a/api/v1/checks.go +++ b/api/v1/checks.go @@ -978,13 +978,6 @@ func (c KubernetesCheck) GetEndpoint() string { return fmt.Sprintf("%v/%v/%v", c.Kind, c.Description.Description, c.Namespace.Name) } -func (c KubernetesCheck) CheckReady() bool { - if c.Ready == nil { - return true - } - return *c.Ready -} - type AWSConnection struct { // ConnectionName of the connection. It'll be used to populate the endpoint, accessKey and secretKey. ConnectionName string `yaml:"connection,omitempty" json:"connection,omitempty"` diff --git a/checks/kubernetes.go b/checks/kubernetes.go index 4d1272472..7367aef78 100644 --- a/checks/kubernetes.go +++ b/checks/kubernetes.go @@ -7,7 +7,7 @@ import ( "github.com/flanksource/canary-checker/api/external" v1 "github.com/flanksource/canary-checker/api/v1" "github.com/flanksource/canary-checker/pkg" - "github.com/flanksource/kommons" + "github.com/flanksource/is-healthy/pkg/health" "github.com/gobwas/glob" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -50,8 +50,6 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check } var allResources []unstructured.Unstructured - message := "" - for _, namespace := range namespaces { resources, err := getResourcesFromNamespace(ctx, client, check, namespace) if err != nil { @@ -65,15 +63,10 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check } } ctx.Tracef("Found %d resources in namespace %s with label=%s field=%s", len(resources), namespace, check.Resource.LabelSelector, check.Resource.FieldSelector) - if check.CheckReady() { - for _, resource := range resources { - ready, msg := ctx.Kommons.IsReady(&resource) - if !ready { - if message != "" { - message += ", " - } - message += fmt.Sprintf("%s is not ready: %v", kommons.GetName(resource), msg) - } + for _, resource := range resources { + resourceHealth, err := health.GetResourceHealth(&resource, nil) + if err == nil { + resource.Object["healthStatus"] = resourceHealth } } allResources = append(allResources, resources...) @@ -82,9 +75,6 @@ func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check return results.Failf("no resources found") } result.AddDetails(allResources) - if message != "" { - return results.Failf(message) - } return results } diff --git a/go.mod b/go.mod index b8473af16..73afdf050 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/fergusstrange/embedded-postgres v1.21.0 github.com/flanksource/commons v1.10.0 github.com/flanksource/duty v1.0.113 + github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc github.com/flanksource/kommons v0.31.1 github.com/friendsofgo/errors v0.9.2 github.com/go-ldap/ldap/v3 v3.4.1 diff --git a/go.sum b/go.sum index deb0c11f5..966e764d7 100644 --- a/go.sum +++ b/go.sum @@ -1096,6 +1096,8 @@ github.com/flanksource/duty v1.0.113/go.mod h1:RJ/kcZ7dbL8/52tem757szVIA3IomS8bO github.com/flanksource/gomplate/v3 v3.20.1/go.mod h1:LPpzujBIg9HBXRUngDKK/zNmEjHpEUieKa/2oRjkCzI= github.com/flanksource/gomplate/v3 v3.20.3 h1:mnNaO37uwvv8Kvi4Xswj1tHKP5UMre3FLNaKvAwqhSw= github.com/flanksource/gomplate/v3 v3.20.3/go.mod h1:l4iCvp30TdhZk89eRGuNkPwlRjJLXdUSblr+VyuZfAk= +github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc h1:CPUNUw2pHnlF4ucBHx44vLTcCa4FlEEu6PkNo5rCvD4= +github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc/go.mod h1:4pQhmF+TnVqJroQKY8wSnSp+T18oLson6YQ2M0qPHfQ= github.com/flanksource/kommons v0.31.1 h1:WZjrIYbSAR7xzIvP2uSRQcn5wPi4W9gAasuOM5CD854= github.com/flanksource/kommons v0.31.1/go.mod h1:kJfMnFWiqsfE8koab54W2GsmgvrlESDrcUtwvbQS9aY= github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= @@ -2292,8 +2294,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= diff --git a/templating/js/k8s.js b/templating/js/k8s.js index 7f91018b4..4f53b5156 100644 --- a/templating/js/k8s.js +++ b/templating/js/k8s.js @@ -1,21 +1,7 @@ k8s = { conditions: { getMessage: function(v) { - message = "" - if (v.status == null) { - return "No status found" - } - status = v.status - if (status.conditions == null) { - return "no conditions found" - } - status.conditions.forEach(function(state) { - if (state.status != "True") { - message += state.type - message += " " - } - }) - return message.trim() + return v.healthStatus.message.trim(); }, getError: function(v) { active = [] @@ -45,22 +31,7 @@ k8s = { return errorMessage }, isReady: function(v) { - if (v.status == null) { - return false - } - status = v.status - if (status.conditions == null) { - return false - } - ready = true - status.conditions.forEach(function(state) { - if (state.type == "Ready") { - if (state.status != "True") { - ready = false - } - } - }) - return ready + return v.healthStatus.status.toLowerCase() === "healthy" ? true : false; }, }, getAlertName: function(v) { From 87d0ae029642f49d5f612da61d334ca68663f9ae Mon Sep 17 00:00:00 2001 From: moshloop Date: Thu, 6 Jul 2023 05:42:34 +0000 Subject: [PATCH 05/10] chore: bump duty to v1.0.121 --- go.mod | 2 +- go.sum | 4 ++-- hack/generate-schemas/go.mod | 2 +- hack/generate-schemas/go.sum | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 73afdf050..2cfd94cff 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/elastic/go-elasticsearch/v8 v8.1.0 github.com/fergusstrange/embedded-postgres v1.21.0 github.com/flanksource/commons v1.10.0 - github.com/flanksource/duty v1.0.113 + github.com/flanksource/duty v1.0.121 github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc github.com/flanksource/kommons v0.31.1 github.com/friendsofgo/errors v0.9.2 diff --git a/go.sum b/go.sum index 966e764d7..1260ca9fc 100644 --- a/go.sum +++ b/go.sum @@ -1091,8 +1091,8 @@ github.com/fergusstrange/embedded-postgres v1.21.0 h1:Sups0nR31+OB4iOZ0ZU4IwUDsB github.com/fergusstrange/embedded-postgres v1.21.0/go.mod h1:wL562t1V+iuFwq0UcgMi2e9rp8CROY9wxWZEfP8Y874= github.com/flanksource/commons v1.10.0 h1:Mc+fzxq1rOJ08lapF0cc0bo9ZKNDxA7/81q2D/5jt0Q= github.com/flanksource/commons v1.10.0/go.mod h1:HpVjPtNe7v0UPk97kO/uUhOrYQ8yFD/mGglrTCkc9Ag= -github.com/flanksource/duty v1.0.113 h1:iQeMhn/Vjz+Rap4TpniGEwLOXcFeg9+X1GtcUsWp3+g= -github.com/flanksource/duty v1.0.113/go.mod h1:RJ/kcZ7dbL8/52tem757szVIA3IomS8bOAZIK0xb4rk= +github.com/flanksource/duty v1.0.121 h1:pSFCbwV+ic9gKklwCtJxKsrR5M9CJuooK37l34gja6o= +github.com/flanksource/duty v1.0.121/go.mod h1:RJ/kcZ7dbL8/52tem757szVIA3IomS8bOAZIK0xb4rk= github.com/flanksource/gomplate/v3 v3.20.1/go.mod h1:LPpzujBIg9HBXRUngDKK/zNmEjHpEUieKa/2oRjkCzI= github.com/flanksource/gomplate/v3 v3.20.3 h1:mnNaO37uwvv8Kvi4Xswj1tHKP5UMre3FLNaKvAwqhSw= github.com/flanksource/gomplate/v3 v3.20.3/go.mod h1:l4iCvp30TdhZk89eRGuNkPwlRjJLXdUSblr+VyuZfAk= diff --git a/hack/generate-schemas/go.mod b/hack/generate-schemas/go.mod index 276823dba..04357b847 100644 --- a/hack/generate-schemas/go.mod +++ b/hack/generate-schemas/go.mod @@ -73,7 +73,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/flanksource/duty v1.0.113 // indirect + github.com/flanksource/duty v1.0.121 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-openapi/inflect v0.19.0 // indirect diff --git a/hack/generate-schemas/go.sum b/hack/generate-schemas/go.sum index cd4aa426e..bade3e1af 100644 --- a/hack/generate-schemas/go.sum +++ b/hack/generate-schemas/go.sum @@ -811,8 +811,8 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fergusstrange/embedded-postgres v1.21.0 h1:Sups0nR31+OB4iOZ0ZU4IwUDsB+dVGmcqj4S2ko0qTI= github.com/flanksource/commons v1.10.0 h1:Mc+fzxq1rOJ08lapF0cc0bo9ZKNDxA7/81q2D/5jt0Q= github.com/flanksource/commons v1.10.0/go.mod h1:HpVjPtNe7v0UPk97kO/uUhOrYQ8yFD/mGglrTCkc9Ag= -github.com/flanksource/duty v1.0.113 h1:iQeMhn/Vjz+Rap4TpniGEwLOXcFeg9+X1GtcUsWp3+g= -github.com/flanksource/duty v1.0.113/go.mod h1:RJ/kcZ7dbL8/52tem757szVIA3IomS8bOAZIK0xb4rk= +github.com/flanksource/duty v1.0.121 h1:pSFCbwV+ic9gKklwCtJxKsrR5M9CJuooK37l34gja6o= +github.com/flanksource/duty v1.0.121/go.mod h1:RJ/kcZ7dbL8/52tem757szVIA3IomS8bOAZIK0xb4rk= github.com/flanksource/gomplate/v3 v3.20.1/go.mod h1:LPpzujBIg9HBXRUngDKK/zNmEjHpEUieKa/2oRjkCzI= github.com/flanksource/gomplate/v3 v3.20.3 h1:mnNaO37uwvv8Kvi4Xswj1tHKP5UMre3FLNaKvAwqhSw= github.com/flanksource/gomplate/v3 v3.20.3/go.mod h1:l4iCvp30TdhZk89eRGuNkPwlRjJLXdUSblr+VyuZfAk= @@ -1481,8 +1481,8 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= From c84e7521bf527ca6c099caf026daff59627c24a1 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Thu, 6 Jul 2023 16:39:46 +0545 Subject: [PATCH 06/10] feat: add aggregation to graph api * feat: aggregate check statuses into windows * calculate the window duration * do not peform aggregation if the number of check statuses in the time range is within the limit. * chore: added notes * feat: select the closest range * feat: limit the time window we select to a few options * chore: renamed func * chore: use total runs intead of uptime.Total() * refactor: improve the partitioning window calculation --- pkg/api.go | 5 ++ pkg/api/api.go | 80 +++++++++++++++++++++++++++++++- pkg/api/api_test.go | 40 ++++++++++++++++ pkg/api/utils.go | 11 +++++ pkg/cache/cache.go | 13 ++++-- pkg/cache/postgres.go | 4 +- pkg/cache/postgres_query.go | 92 ++++++++++++++++++++----------------- 7 files changed, 195 insertions(+), 50 deletions(-) create mode 100644 pkg/api/api_test.go diff --git a/pkg/api.go b/pkg/api.go index 2d61d7b7d..3a0b4aadb 100644 --- a/pkg/api.go +++ b/pkg/api.go @@ -202,6 +202,11 @@ type Check struct { DeletedAt *time.Time `json:"deletedAt,omitempty"` SilencedAt *time.Time `json:"silencedAt,omitempty"` Canary *v1.Canary `json:"-" gorm:"-"` + + // These are calculated for the selected date range + EarliestRuntime *time.Time `json:"earliestRuntime,omitempty" gorm:"-"` + LatestRuntime *time.Time `json:"latestRuntime,omitempty" gorm:"-"` + TotalRuns int `json:"totalRuns,omitempty" gorm:"-"` } func FromExternalCheck(canary Canary, check external.Check) Check { diff --git a/pkg/api/api.go b/pkg/api/api.go index 5b72e7474..4e4465dd5 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -12,7 +12,27 @@ import ( "github.com/flanksource/canary-checker/pkg" ) -var DefaultWindow = "1h" +// The number of data points that should be strived for +// when aggregating check statuses. +const desiredNumOfCheckStatuses = 100 + +var ( + DefaultWindow = "1h" + + // allowed list of window durations that are used when aggregating check statuses. + allowedWindows = []time.Duration{ + time.Minute, // 1m + time.Minute * 5, // 5m + time.Minute * 15, // 15m + time.Minute * 30, // 30m + time.Hour, // 1h + time.Hour * 3, // 3h + time.Hour * 6, // 6h + time.Hour * 12, // 12h + time.Hour * 24, // 24h + time.Hour * 24 * 7, // 1w + } +) type Response struct { Duration int `json:"duration,omitempty"` @@ -20,10 +40,13 @@ type Response struct { Checks pkg.Checks `json:"checks"` ChecksSummary models.Checks `json:"checks_summary,omitempty"` } + type DetailResponse struct { Duration int `json:"duration,omitempty"` RunnerName string `json:"runnerName"` Status []pkg.Timeseries `json:"status"` + Latency pkg.Latency `json:"latency"` + Uptime pkg.Uptime `json:"uptime"` } func About(c echo.Context) error { @@ -41,15 +64,34 @@ func CheckDetails(c echo.Context) error { } start := time.Now() - results, err := cache.PostgresCache.QueryStatus(*q) + + summary, err := cache.PostgresCache.Query(*q) + if err != nil { + return errorResonse(c, err, http.StatusInternalServerError) + } + if len(summary) == 0 { + return c.JSON(http.StatusOK, DetailResponse{}) + } + + checkSummary := summary[0] + totalChecks := checkSummary.TotalRuns + + rangeDuration := checkSummary.LatestRuntime.Sub(*checkSummary.EarliestRuntime) + q.WindowDuration = getBestPartitioner(totalChecks, rangeDuration) + + results, err := cache.PostgresCache.QueryStatus(c.Request().Context(), *q) if err != nil { return errorResonse(c, err, http.StatusInternalServerError) } + apiResponse := &DetailResponse{ RunnerName: runner.RunnerName, Status: results, Duration: int(time.Since(start).Milliseconds()), + Latency: checkSummary.Latency, + Uptime: checkSummary.Uptime, } + return c.JSON(http.StatusOK, apiResponse) } @@ -87,3 +129,37 @@ func HealthSummary(c echo.Context) error { } return c.JSON(http.StatusOK, apiResponse) } + +func getBestPartitioner(totalChecks int, rangeDuration time.Duration) time.Duration { + if totalChecks <= desiredNumOfCheckStatuses { + return 0 // No need to perform window aggregation + } + + bestDelta := 100000000 // sufficiently large delta to begin with + bestWindow := allowedWindows[0] + + for _, wp := range allowedWindows { + numWindows := int(rangeDuration / wp) + delta := abs(desiredNumOfCheckStatuses - numWindows) + + if delta < bestDelta { + bestDelta = delta + bestWindow = wp + } else { + // as soon as we notice that the delta gets worse, we break the loop + break + } + } + + numWindows := int(rangeDuration / bestWindow) + if abs(desiredNumOfCheckStatuses-totalChecks) <= abs(desiredNumOfCheckStatuses-numWindows) { + // If this best partition creates windows such that the resulting number of data points deviate more + // from the desired data points than the actual data points, then we do not aggregate. + // Example: if there are 144 checks for the duration of 6 days, + // then the best partition, 1 hour, would generate 144 data points. + // But the original data points (120) are closer to 100, so we do not aggregate. + return 0 + } + + return bestWindow +} diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go new file mode 100644 index 000000000..9721e3653 --- /dev/null +++ b/pkg/api/api_test.go @@ -0,0 +1,40 @@ +package api + +import ( + "testing" + "time" +) + +func Test_getMostSuitableWindowDuration(t *testing.T) { + day := time.Hour * 24 + + tests := []struct { + schedule time.Duration // how often the check is run + rangeDuration time.Duration + expected time.Duration // the best duration to partition the range + }{ + {time.Second * 30, time.Minute * 5, 0}, + {time.Second * 30, time.Minute * 30, 0}, + {time.Second * 30, time.Hour * 2, time.Minute}, + {time.Second * 30, time.Hour * 12, time.Minute * 5}, + {time.Second * 30, day * 2, time.Minute * 30}, + {time.Hour, day * 4, 0}, + {time.Hour, day * 5, 0}, + {time.Hour, day * 6, 0}, + {time.Hour, day * 12, time.Hour * 3}, + {time.Second * 30, day * 8, time.Hour * 3}, + {time.Second * 30, day * 30, time.Hour * 6}, + {time.Second * 30, day * 90, day}, + {time.Second * 30, day * 365, day * 7}, + } + + for _, td := range tests { + t.Run(td.rangeDuration.String(), func(t *testing.T) { + totalChecks := int(td.rangeDuration / td.schedule) + result := getBestPartitioner(totalChecks, td.rangeDuration) + if result != td.expected { + t.Errorf("expected %v, but got %v", td.expected, result) + } + }) + } +} diff --git a/pkg/api/utils.go b/pkg/api/utils.go index bf8e2b9dc..242cc276d 100644 --- a/pkg/api/utils.go +++ b/pkg/api/utils.go @@ -8,3 +8,14 @@ func errorResonse(c echo.Context, err error, code int) error { e := map[string]string{"error": err.Error()} return c.JSON(code, e) } + +// abs returns the absolute value of i. +// math.Abs only supports float64 and this avoids the needless type conversions +// and ugly expression. +func abs(n int) int { + if n > 0 { + return n + } + + return -n +} diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 1e360090e..699e7de16 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -28,6 +28,7 @@ type QueryParams struct { StatusCount int Labels map[string]string Trace bool + WindowDuration time.Duration } func (q QueryParams) Validate() error { @@ -57,10 +58,10 @@ func (q QueryParams) GetStartTime() *time.Time { func (q QueryParams) GetEndTime() *time.Time { if q._end != nil || q.End == "" { - return q._start + return q._end } - q._start, _ = timeV(q.Start) - return q._start + q._end, _ = timeV(q.End) + return q._end } func (q QueryParams) String() string { @@ -80,6 +81,7 @@ func ParseQuery(c echo.Context) (*QueryParams, error) { } else { cacheCount = int64(DefaultCacheCount) } + since := queryParams.Get("since") if since == "" { since = queryParams.Get("start") @@ -87,10 +89,15 @@ func ParseQuery(c echo.Context) (*QueryParams, error) { if since == "" { since = DefaultWindow } + until := queryParams.Get("until") if until == "" { until = queryParams.Get("end") } + if until == "" { + until = "0s" + } + q := QueryParams{ Start: since, End: until, diff --git a/pkg/cache/postgres.go b/pkg/cache/postgres.go index 40665c4fa..3e1d9c0cb 100644 --- a/pkg/cache/postgres.go +++ b/pkg/cache/postgres.go @@ -111,8 +111,8 @@ func (c *postgresCache) QuerySummary() (models.Checks, error) { return duty.QueryCheckSummary(context.Background(), db.Pool) } -func (c *postgresCache) QueryStatus(q QueryParams) ([]pkg.Timeseries, error) { - return q.ExecuteDetails(db.Pool) +func (c *postgresCache) QueryStatus(ctx context.Context, q QueryParams) ([]pkg.Timeseries, error) { + return q.ExecuteDetails(ctx, db.Pool) } func (c *postgresCache) GetDetails(checkkey string, time string) interface{} { diff --git a/pkg/cache/postgres_query.go b/pkg/cache/postgres_query.go index 9ee342754..fb4fa43c9 100644 --- a/pkg/cache/postgres_query.go +++ b/pkg/cache/postgres_query.go @@ -79,33 +79,38 @@ func (q QueryParams) GetWhereClause() (string, map[string]interface{}, error) { return strings.TrimSpace(clause), args, nil } -func (q QueryParams) ExecuteDetails(db Querier) ([]pkg.Timeseries, error) { - clause, namedArgs, err := q.GetWhereClause() - if err != nil { - return nil, err - } - namedArgs["limit"] = q.StatusCount - keyIndex := 3 - messageIndex := 4 - errorIndex := 5 +func (q QueryParams) ExecuteDetails(ctx context.Context, db Querier) ([]pkg.Timeseries, error) { + start := q.GetStartTime().Format(time.RFC3339) + end := q.GetEndTime().Format(time.RFC3339) - sql := "SELECT time,duration,status " - if q.Check == "" { - sql += ", check_key" - } - if q.IncludeMessages { - sql += ", message, error" - if q.Check != "" { - messageIndex -= 1 - errorIndex -= 1 - } - } - sql += fmt.Sprintf(` + query := ` +With grouped_by_window AS ( + SELECT + duration, + status, + to_timestamp(floor((extract(epoch FROM time) + $1) / $2) * $2) AS time FROM check_statuses - WHERE %s - LIMIT :limit -`, clause) - rows, err := exec(db, q, sql, namedArgs) + WHERE + time >= $3 AND + time <= $4 AND + check_id = $5 +) +SELECT + time, + bool_and(status), + AVG(duration)::integer as duration +FROM + grouped_by_window +GROUP BY time +` + args := []any{q.WindowDuration.Seconds() / 2, q.WindowDuration.Seconds(), start, end, q.Check} + + if q.WindowDuration == 0 { + query = `SELECT time, status, duration FROM check_statuses WHERE time >= $1 AND time <= $2 AND check_id = $3` + args = []any{start, end, q.Check} + } + + rows, err := db.Query(ctx, query, args...) if err != nil { return nil, err } @@ -113,24 +118,16 @@ func (q QueryParams) ExecuteDetails(db Querier) ([]pkg.Timeseries, error) { var results []pkg.Timeseries for rows.Next() { - vals, err := rows.Values() - if err != nil { + var datapoint pkg.Timeseries + var ts time.Time + if err := rows.Scan(&ts, &datapoint.Status, &datapoint.Duration); err != nil { return nil, err } - result := pkg.Timeseries{ - Time: vals[0].(time.Time).Format(time.RFC3339), - Duration: intV(vals[1]), - Status: vals[2].(bool), - } - if q.Check == "" { - result.Key = vals[keyIndex].(string) - } - if q.IncludeMessages { - result.Message = vals[messageIndex].(string) - result.Error = vals[errorIndex].(string) - } - results = append(results, result) + + datapoint.Time = ts.Format(time.RFC3339) + results = append(results, datapoint) } + return results, nil } @@ -193,9 +190,11 @@ SELECT checks.created_at, checks.updated_at, checks.deleted_at, - status + status, + stats.max_time, + stats.min_time, + stats.total_checks FROM checks checks - RIGHT JOIN ( SELECT check_id, @@ -203,7 +202,10 @@ RIGHT JOIN ( percentile_disc(0.97) within group (order by filtered_check_status.duration) as p97, percentile_disc(0.05) within group (order by filtered_check_status.duration) as p95, COUNT(*) FILTER (WHERE filtered_check_status.status = TRUE) as passed, - COUNT(*) FILTER (WHERE filtered_check_status.status = FALSE) as failed + COUNT(*) FILTER (WHERE filtered_check_status.status = FALSE) as failed, + COUNT(*) total_checks, + MIN(filtered_check_status.time) as min_time, + MAX(filtered_check_status.time) as max_time FROM filtered_check_status GROUP BY check_id @@ -273,6 +275,10 @@ WHERE (stats.passed > 0 OR stats.failed > 0) %s check.DeletedAt, _ = timeV(vals[20]) } check.Status = vals[21].(string) + check.LatestRuntime, _ = timeV(vals[22]) + check.EarliestRuntime, _ = timeV(vals[23]) + check.TotalRuns = intV(vals[24]) + if vals[7] != nil { for _, status := range vals[7].([]interface{}) { s := status.(map[string]interface{}) From c47ce6243e2e36e6e08e2b739a604cb4ba0e8a7d Mon Sep 17 00:00:00 2001 From: Moshe Immerman Date: Thu, 6 Jul 2023 17:14:38 +0300 Subject: [PATCH 07/10] major: update docs --- README.md | 210 ++-- docs/API.md | 1268 -------------------- docs/canary-checker.png | Bin 69574 -> 0 bytes docs/dev-guide.md | 68 -- docs/images/cc-blue.svg | 3 - docs/images/cc-white.svg | 3 - docs/images/dashboard-http-pass-canary.png | Bin 30244 -> 0 bytes docs/images/ui01.png | Bin 161597 -> 0 bytes docs/index.md | 1 - docs/installation.md | 33 - docs/introduction.md | 31 - docs/operator.md | 133 -- docs/prereqs.md | 103 -- docs/reference.md | 20 - docs/run.md | 57 - docs/server.md | 58 - docs/swagger.yaml | 296 ----- 17 files changed, 104 insertions(+), 2180 deletions(-) delete mode 100644 docs/API.md delete mode 100644 docs/canary-checker.png delete mode 100644 docs/dev-guide.md delete mode 100644 docs/images/cc-blue.svg delete mode 100644 docs/images/cc-white.svg delete mode 100644 docs/images/dashboard-http-pass-canary.png delete mode 100644 docs/images/ui01.png delete mode 120000 docs/index.md delete mode 100644 docs/installation.md delete mode 100644 docs/introduction.md delete mode 100644 docs/operator.md delete mode 100644 docs/prereqs.md delete mode 100644 docs/reference.md delete mode 100644 docs/run.md delete mode 100644 docs/server.md delete mode 100644 docs/swagger.yaml diff --git a/README.md b/README.md index e476f4a00..629b50c2c 100644 --- a/README.md +++ b/README.md @@ -1,141 +1,139 @@ -
+

Kubernetes operator for executing synthetic tests

- +

--- +Canary checker is a kubernetes-native platform for monitoring health across application and infrastructure using both passive and active (synthetic) mechanisms. -# Introduction +## Features -Canary Checker is a Kubernetes native multi-tenant synthetic monitoring system. To learn more, see the [docs](https://docs.flanksource.com/canary-checker/overview/). +* **Batteries Included** - 35+ built-in check types +* **Kubernetes Native** - Health checks (or canaries) are CRD's that reflect health via the `status` field, making them compatible with GitOps, [Flux Health Checks](https://fluxcd.io/flux/components/kustomize/kustomization/#health-checks), Argo, Helm, etc.. +* **Secret Management** - Leverage K8S secrets and configmaps for authentication and connection details +* **Prometheus** - Prometheus compatible metrics are exposed at `/metrics`. A Grafana Dashboard is also available. +* **Dependency Free** - Runs an embedded postgres instance by default, can also be configured to use an external database. +* **JUnit Export (CI/CD)** - Export health check results to JUnit format for integration into CI/CD pipelines +* **JUnit Import (k6/newman/puppeter/etc)** - Use any container that creates JUnit test results +* **Scriptable** - Go templates, Javascript and [Expr](https://github.com/antonmedv/expr) can be used to: + * Evaluate whether a check is passing and severity to use when failing + * Extract a user friendly error message + * Transform and filter check responses into individual check results +* **Multi-Modal** - While designed as a Kubernetes Operator, canary checker can also run as a CLI and a server without K8s -# Features +## Getting Started -* Built-in UI/Dashboard -* CRD based configuration and status reporting -* Prometheus Integration -* Runnable as a CLI for once-off checks or as a standalone server outside kubernetes -* Junit formatting for CI/CD integration -* Many built-in check types +1. Install canary checker: -## Getting started - -The easiest way to get started with canary-checker is to run it as CLI, it will take specifications in a YAML / CRD format and execute them before returning. The CLI can be used within CI/CD platforms and also exports to JUnit XML reports. - -1. Install the CLI - -```bash -wget https://github.com/flanksource/canary-checker/releases/latest/download/canary-checker_linux_amd64 \ - -O /usr/bin/canary-checker && \ - chmod +x /usr/bin/canary-checker -``` + ```shell +helm repo add flanksource https://flanksource.github.io/charts +helm repo update +helm install canary-checker + ``` -2. Create a new spec called `http.yaml` +2. Create a new check: -```yaml + ```yaml title="canary.yaml" apiVersion: canaries.flanksource.com/v1 kind: Canary metadata: - name: http-pass + name: http-check spec: interval: 30 http: - - endpoint: https://httpstat.us/200 - thresholdMillis: 3000 - responseCodes: [201, 200, 301] - responseContent: "" - maxSSLExpiry: 7 -``` - -3. Run the canary using: - -```bash -canary-checker run http.yaml -``` - -[![asciicast](https://asciinema.org/a/N3jELGSn8HoRQHPpCdeK7MDBV.svg)](https://asciinema.org/a/N3jELGSn8HoRQHPpCdeK7MDBV) - -### Junit Formating - -Canary checker can export JUnit formatted results for use in CI/CD pipelines - -```bash -canary-checker run http.yaml -j -o results.xml -``` - -## Deploying as Kubernetes Operator + - name: basic-check + url: https://httpbin.demo.aws.flanksource.com/status/200 + - name: failing-check + url: https://httpbin.demo.aws.flanksource.com/status/500 + ``` -1. Deploy the operator +2a. Run the check locally (Optional) -```bash -helm repo add flanksource https://flanksource.github.io/charts -helm repo update -helm install canary-checker-demo \ - --wait \ - -n canary-checker \ - --create-namespace flanksource/canary-checker \ - -f values.yaml +```shell +canary-checker run canary.yaml ``` -```yaml title="values.yaml" -flanksource-ui: - ingress: - host: canary-checker.127.0.0.1.nip.io - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - tls: - - secretName: canary-checker-tls - hosts: - - canary-checker.127.0.0.1.nip.io -``` - -2. Install a canary +[![asciicast](https://asciinema.org/a/cYS6hlmX516JQeECHH7za3IDG.svg)](https://asciinema.org/a/cYS6hlmX516JQeECHH7za3IDG) -```bash -kubectl apply -f https://raw.githubusercontent.com/flanksource/canary-checker/master/fixtures/minimal/http_pass_single.yaml -``` + ```shell + kubectl apply -f canary.yaml + ``` -3. Check the results via the CLI +3. Check the status of the health check: -```bash + ```shell kubectl get canary -``` + ``` ``` title="sample output" NAME INTERVAL STATUS LAST CHECK UPTIME 1H LATENCY 1H LAST TRANSITIONED -http-pass-single 30 Passed 13s 18/18 (100.0%) 480ms 13s +http-check. 30 Passed 13s 18/18 (100.0%) 480ms 13s ``` -### Dashboard - -Canary checker comes with a built-in dashboard for displaying canary results, it can be turned off using `--set flanksource-ui.enabled=false` - -![](https://github.com/flanksource/docs/blob/85bdd4875d0d3ded16b7aa6c132d423852fcad90/docs/images/dashboard-http-pass-canary.png?raw=true) - -### Prometheus - -The helm chart can install a `ServiceMonitor` for the prometheus operator, by enabling the serviceMonitor flag `--set serviceMonitor=true` - -Metrics exposed by canary-checker: - -| Metric | Type | Description | -| ---------------------------------------------- | --------- | ------------------------------------------- | -| canary_check | Guage | Set to 0 when passing and 1 when failing | -| canary_check_success_count | Counter | | -| canary_check_failed_count | Counter | | -| canary_check_info | Info | | -| canary_check_duration | Histogram | Histogram of canary durations | -| **Pod Check Metrics** | | | -| canary_check_histogram{metric="creation_time"} | Histogram | Time for pod to be `Pending` | -| canary_check_histogram{metric="delete_time"} | Histogram | Time to delete pod | -| canary_check_histogram{metric="ingress_time"} | Histogram | Time until ingress is returning requests | -| canary_check_histogram{metric="request_time"} | Histogram | Duration of http request once ingress is up | -| canary_check_histogram{metric="schedule_time"} | Histogram | Time for pod to be `Running` | -| **HTTP Check Metrics** | | | -| canary_check_http_response_status | Counter | Response code counter for each endpoint | +## Getting Help + +If you have any questions about canary checker: + +* Read the [docs](https://canarychecker.io) +* Invite yourself to the [CNCF community slack](https://slack.cncf.io/)and join the [#canary-checker](https://cloud-native.slack.com/messages/canary-checker/) channel. +* Check out the [Youtube Playlist](https://www.youtube.com/playlist?list=PLz4F_KggvA58D6krlw433TNr8qMbu1aIU). +* File an [issue](https://github.com/flanksource/canary-checker/issues/new) - (We do provide user support via Github Issues, so don't worry if your issue a real bug or not) + +Your feedback is always welcome! + +## Check Types + +| Protocol | Status | Checks | +| ------------------------------------------------------------ | ---------- | ------------------------------------------------------------ | +| [HTTP(s)](https://canarychecker.io/reference/http) | GA | Response body, headers and duration | +| [DNS](https://canarychecker.io/reference/dns) | GA | Response and duration | +| [Ping/ICMP](https://canarychecker.io/reference/icmp) | GA | Duration and packet loss | +| [TCP](https://canarychecker.io/reference/tcp) | GA | Port is open and connectable | +| **Data Sources** | | | +| SQL ([MySQL](https://canarychecker.io/reference/mysql), [Postgres](https://canarychecker.io/reference/postgres), [SQL Server](https://canarychecker.io/reference/mssql)) | GA | Ability to login, results, duration, health exposed via stored procedures | +| [LDAP](https://canarychecker.io/reference/ldap) | GA | Ability to login, response time | +| [ElasticSearch / Opensearch](https://canarychecker.io/reference/elasticsearch) | GA | Ability to login, response time, size of search results | +| [Mongo](https://canarychecker.io/reference/mongo) | Beta | Ability to login, results, duration, | +| [Redis](https://canarychecker.io/reference/redis) | GA | Ability to login, results, duration, | +| [Prometheus](https://canarychecker.io/reference/prometheus) | GA | Ability to login, results, duration, | +| **Alerts** | | Prometheus | +| [Prometheus Alert Manager](https://canarychecker.io/reference/alert-manager) | GA | Pending and firing alerts | +| [AWS Cloudwatch Alarms](https://canarychecker.io/reference/cloudwatch) | GA | Pending and firing alarms | +| [Dynatrace Problems](./reference/dynatrace.md) | Beta | Problems deteced | +| **DevOps** | | | +| [Git](https://canarychecker.io/reference/git) | GA | Query Git and Github repositories via SQL | +| [Azure Devops](https://canarychecker.io/reference) | | | +| **Integration Testing** | | | +| [JMeter](https://canarychecker.io/reference/jmeter) | Beta | Runs and checks the result of a JMeter test | +| [JUnit / BYO](https://canarychecker.io/reference/junit) | Beta | Run a pod that saves Junit test results | +| **File Systems / Batch** | | | +| [Local Disk / NFS](https://canarychecker.io/reference/folder) | GA | Check folders for files that are: too few/many, too old/new, too small/large | +| [S3](https://canarychecker.io/reference/s3-bucket) | GA | Check contents of AWS S3 Buckets | +| [GCS](https://canarychecker.io/reference/gcs-bucket) | GA | Check contents of Google Cloud Storage Buckets | +| [SFTP](https://canarychecker.io/reference/sftp) | GA | Check contents of folders over SFTP | +| [SMB / CIFS](../smb) | GA | Check contents of folders over SMB/CIFS | +| **Config** | | | +| [AWS Config](https://canarychecker.io/reference/aws-config) | GA | Query AWS config using SQL | +| [AWS Config Rule](https://canarychecker.io/reference/aws-config-rule) | GA | AWS Config Rules that are firing, Custom AWS Config queries | +| [Config DB](https://canarychecker.io/reference/configdb) | GA | Custom config queries for Mission Control Config D | +| [Kubernetes Resources](https://canarychecker.io/reference/kubernetes) | GA | Kubernetes resources that are missing or are in a non-ready state | +| **Backups** | | | +| [GCP Databases](..refere) | GA | Backup freshness | +| [Restic](https://canarychecker.io/reference/restic) | Beta | Backup freshness and integrity | +| **Infrastructure** | | | +| [EC2](https://canarychecker.io/reference/ec2) | GA | Ability to launch new EC2 instances | +| [Kubernetes Ingress](https://canarychecker.io/reference/pod) | GA | Ability to schedule and then route traffic via an ingress to a pod | +| [Docker/Containerd](https://canarychecker.io/reference/containerd) | Deprecated | Ability to push and pull containers via docker/containerd | +| [Helm](https://canarychecker.io/reference/helm) | Deprecated | Ability to push and pull helm charts | +| [S3 Protocol](https://canarychecker.io/reference/s3-protocol) | GA | Ability to read/write/list objects on an S3 compatible object store | + +## License + +Canary Checker core (the code in this repository) is licensed under [Apache 2.0](https://raw.githubusercontent.com/flanksource/canary-checker/main/LICENSE) and accepts contributions via GitHub pull requests after signing a CLA. + +The UI (Dashboard) is free to use with canary checker under a license exception of [Flanksource UI](https://github.com/flanksource/flanksource-ui/blob/main/LICENSE#L7) diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index d38d55646..000000000 --- a/docs/API.md +++ /dev/null @@ -1,1268 +0,0 @@ ---- -title: Canary Types -hide: - - toc ---- - -## Canary - -Canary is the Schema for the canaries API - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **ObjectMeta** | | [metav1.ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#objectmeta-v1-meta) | Yes | -| **Spec** | | [CanarySpec](#canaryspec) | Yes | -| **Status** | | [CanaryStatus](#canarystatus) | Yes | -| **TypeMeta** | | metav1.TypeMeta | Yes | - - -## CanarySpec - -CanarySpec defines the desired state of Canary - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| cloudwatch | | \[\][CloudWatchCheck](#cloudwatchcheck) | | -| containerd | | \[\][ContainerdPullCheck](#containerdpullcheck) | | -| containerdPush | | \[\][ContainerdPushCheck](#containerdpushcheck) | | -| dns | | \[\][DNSCheck](#dnscheck) | | -| docker | | \[\][DockerPullCheck](#dockerpullcheck) | | -| dockerPush | | \[\][DockerPushCheck](#dockerpushcheck) | | -| ec2 | | \[\][EC2Check](#ec2check) | | -| env | | map[string][VarSource](#varsource) | | -| gcsBucket | | \[\][GCSBucketCheck](#gcsbucketcheck) | | -| helm | | \[\][HelmCheck](#helmcheck) | | -| http | | \[\][HTTPCheck](#httpcheck) | | -| icmp | | \[\][ICMPCheck](#icmpcheck) | | -| icon | | string | | -| interval | interval (in seconds) to run checks on Deprecated in favor of Schedule | uint64 | | -| jmeter | | \[\][JmeterCheck](#jmetercheck) | | -| junit | | \[\][JunitCheck](#junitcheck) | | -| ldap | | \[\][LDAPCheck](#ldapcheck) | | -| mongodb | | \[\][MongoDBCheck](#mongodbcheck) | | -| mssql | | \[\][MssqlCheck](#mssqlcheck) | | -| namespace | | \[\][NamespaceCheck](#namespacecheck) | | -| owner | | string | | -| pod | | \[\][PodCheck](#podcheck) | | -| postgres | | \[\][PostgresCheck](#postgrescheck) | | -| prometheus | | \[\][PrometheusCheck](#prometheuscheck) | | -| redis | | \[\][RedisCheck](#redischeck) | | -| restic | | \[\][ResticCheck](#resticcheck) | | -| s3 | | \[\][S3Check](#s3check) | | -| s3Bucket | | \[\][S3BucketCheck](#s3bucketcheck) | | -| schedule | Schedule to run checks on. Supports all cron expression, example: '30 3-6,20-23 * * *'. For more info about cron expression syntax see https://en.wikipedia.org/wiki/Cron - Also supports golang duration, can be set as '@every 1m30s' which runs the check every 1 minute and 30 seconds. | string | | -| severity | | string | | -| smb | | \[\][SmbCheck](#smbcheck) | | -| tcp | | \[\][TCPCheck](#tcpcheck) | | - - -## CanaryStatus - -CanaryStatus defines the observed state of Canary - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **ChecksStatus** | | map[string]*[CheckStatus](#checkstatus) | Yes | -| **ErrorMessage** | | *string | Yes | -| **LastCheck** | | *metav1.Time | Yes | -| **LastTransitionedTime** | | *metav1.Time | Yes | -| **Latency1H** | Average latency to complete all checks | string | Yes | -| **Message** | | *string | Yes | -| **ObservedGeneration** | | int64 | Yes | -| **Status** | | *CanaryStatusCondition | Yes | -| **Uptime1H** | Availibility over a rolling 1h period | string | Yes | - - -## CanaryList - -CanaryList contains a list of Canary - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **Items** | | \[\][Canary](#canary) | Yes | -| **ListMeta** | | [metav1.ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#listmeta-v1-meta) | Yes | -| **TypeMeta** | | metav1.TypeMeta | Yes | - - -## CloudWatch - -This checks the cloudwatch for all the Active alarm and response with the reason -??? example - ```yaml - cloudwatch: - - accessKey: - valueFrom: - secretKeyRef: - key: aws - name: access-key - secretKey: - valueFrom: - secretKeyRef: - key: aws - name: secrey-key - region: "us-east-1" - #skipTLSVerify: true - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **accessKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| endpoint | | string | | -| filter | | [CloudWatchFilter](#cloudwatchfilter) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| region | | string | | -| **secretKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| skipTLSVerify | Skip TLS verify when connecting to aws | bool | | -| test | | [Template](#template) | | - - -## ContainerdPull - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: containerd-pull-pass - spec: - interval: 30 - containerd: - - image: docker.io/library/busybox:1.31.1 - expectedDigest: sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 - expectedSize: 764556 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | [Authentication](#authentication) | | -| description | Description for the check | string | | -| expectedDigest | | string | | -| expectedSize | | int64 | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **image** | | string | Yes | -| name | Name of the check | string | | - - -## ContainerdPush - -??? example - ```yaml - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **image** | | string | Yes | -| name | Name of the check | string | | -| **password** | | string | Yes | -| **username** | | string | Yes | - - -## DNS - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: dns-pass - spec: - interval: 30 - dns: - - server: 8.8.8.8 - port: 53 - query: "1.2.3.4.nip.io" - querytype: "A" - minrecords: 1 - exactreply: ["1.2.3.4"] - timeout: 10 - thresholdMillis: 1000 - - server: 8.8.8.8 - port: 53 - query: "8.8.8.8" - querytype: "PTR" - minrecords: 1 - exactreply: ["dns.google."] - timeout: 10 - thresholdMillis: 100 - - server: 8.8.8.8 - port: 53 - query: "dns.google" - querytype: "CNAME" - minrecords: 1 - exactreply: ["dns.google."] - timeout: 10 - thresholdMillis: 1000 - - server: 8.8.8.8 - port: 53 - query: "flanksource.com" - querytype: "MX" - minrecords: 1 - exactreply: - - "aspmx.l.google.com. 1" - - "alt1.aspmx.l.google.com. 5" - - "alt2.aspmx.l.google.com. 5" - - "aspmx3.googlemail.com. 10" - - "aspmx2.googlemail.com. 10" - timeout: 10 - thresholdMillis: 1000 - - server: 8.8.8.8 - port: 53 - query: "flanksource.com" - querytype: "TXT" - minrecords: 1 - exactreply: ["google-site-verification=IIE1aJuvqseLUKSXSIhu2O2lgdU_d8csfJjjIQVc-q0"] - timeout: 10 - thresholdMillis: 1000 - - server: 8.8.8.8 - port: 53 - query: "flanksource.com" - querytype: "NS" - minrecords: 1 - exactreply: - - "ns-91.awsdns-11.com." - - "ns-908.awsdns-49.net." - - "ns-1450.awsdns-53.org." - - "ns-1896.awsdns-45.co.uk." - timeout: 10 - thresholdMillis: 1000 - # - server: 8.8.8.8 - # port: 53 - # querytype: "SRV" - # query: "_test._tcp.test" - # timeout: 10 - # srvReply: - # target: "" - # port: 0 - # priority: 0 - # weight: 0* - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| exactreply | | \[\]string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| minrecords | | int | | -| name | Name of the check | string | | -| port | | int | | -| query | | string | | -| querytype | | string | | -| **server** | | string | Yes | -| thresholdMillis | | int | | -| timeout | | int | | - - -## DockerPull - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: docker-pass - spec: - interval: 30 - docker: - - image: docker.io/library/busybox:1.31.1@sha256:b20c55f6bfac8828690ec2f4e2da29790c80aa3d7801a119f0ea6b045d2d2da1 - expectedDigest: sha256:b20c55f6bfac8828690ec2f4e2da29790c80aa3d7801a119f0ea6b045d2d2da1 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | *[Authentication](#authentication) | | -| description | Description for the check | string | | -| **expectedDigest** | | string | Yes | -| **expectedSize** | | int64 | Yes | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **image** | | string | Yes | -| name | Name of the check | string | | - - -## DockerPush - -DockerPush check will try to push a Docker image to specified registry. -/* -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: docker-push0-pass - spec: - interval: 30 - dockerPush: - - image: ttl.sh/flanksource-busybox:1.30 - auth: - username: - value: test - password: - value: pass - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | *[Authentication](#authentication) | | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **image** | | string | Yes | -| name | Name of the check | string | | - - -## EC2 - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: ec2-pass - spec: - interval: 30 - spec: - ec2: - - description: test instance - accessKeyID: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_ACCESS_KEY_ID - secretKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_SECRET_ACCESS_KEY - region: af-south-1 - userData: | - #!/bin/bash - yum install -y httpd - systemctl start httpd - systemctl enable httpd - usermod -a -G apache ec2-user - chown -R ec2-user:apache /var/www - chmod 2775 /var/www - find /var/www -type d -exec chmod 2775 {} \; - find /var/www -type f -exec chmod 0664 {} \; - securityGroup: WebAccess - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **accessKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| ami | | string | | -| canaryRef | | \[\][v1.LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#localobjectreference-v1-core) | | -| description | Description for the check | string | | -| endpoint | | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| keepAlive | | bool | | -| name | Name of the check | string | | -| region | | string | | -| **secretKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| securityGroup | | string | | -| skipTLSVerify | Skip TLS verify when connecting to aws | bool | | -| timeOut | | int | | -| userData | | string | | -| waitTime | | int | | - - -## GCSBucket - -??? example - ```yaml - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **bucket** | | string | Yes | -| **credentials** | | *[kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| **endpoint** | | string | Yes | -| filter | | [FolderFilter](#folderfilter) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| maxAge | MaxAge the latest object should be younger than defined age | Duration | | -| maxCount | MinCount the minimum number of files inside the searchPath | *int | | -| maxSize | MaxSize of the files inside the searchPath | Size | | -| minAge | MinAge the latest object should be older than defined age | Duration | | -| minCount | MinCount the minimum number of files inside the searchPath | *int | | -| minSize | MinSize of the files inside the searchPath | Size | | -| name | Name of the check | string | | -| test | | [Template](#template) | | - - -## HTTP - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: http-pass - spec: - interval: 30 - http: - - endpoint: http://status.savanttools.com/?code=200 - thresholdMillis: 3000 - responseCodes: [201, 200, 301] - responseContent: "" - maxSSLExpiry: 7 - - endpoint: http://status.savanttools.com/?code=404 - thresholdMillis: 3000 - responseCodes: [404] - responseContent: "" - maxSSLExpiry: 7 - - endpoint: http://status.savanttools.com/?code=500 - thresholdMillis: 3000 - responseCodes: [500] - responseContent: "" - maxSSLExpiry: 7 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| authentication | Credentials for authentication headers | *[Authentication](#authentication) | | -| body | Request Body Contents | string | | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| **endpoint** | HTTP endpoint to check. Mutually exclusive with Namespace | string | Yes | -| headers | Header fields to be used in the query | \[\][kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| maxSSLExpiry | Maximum number of days until the SSL Certificate expires. | int | | -| method | Method to use - defaults to GET | string | | -| name | Name of the check | string | | -| namespace | Namespace to crawl for TLS endpoints. Mutually exclusive with Endpoint | string | | -| ntlm | NTLM when set to true will do authentication using NTLM v1 protocol | bool | | -| ntlmv2 | NTLM when set to true will do authentication using NTLM v2 protocol | bool | | -| responseCodes | Expected response codes for the HTTP Request. | \[\]int | | -| responseContent | Exact response content expected to be returned by the endpoint. | string | | -| responseJSONContent | Path and value to of expect JSON response by the endpoint | [JSONCheck](#jsoncheck) | | -| test | | [Template](#template) | | -| thresholdMillis | Maximum duration in milliseconds for the HTTP request. It will fail the check if it takes longer. | int | | - - -## Helm - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: helm-pass - spec: - interval: 30 - helm: - - chartmuseum: http://chartmuseum.default:8080 - project: library - auth: - username: - value: admin - password: - value: passwd - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | *[Authentication](#authentication) | | -| cafile | | string | | -| **chartmuseum** | | string | Yes | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| project | | string | | - - -## ICMP - -This test will check ICMP packet loss and duration. - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: icmp - spec: - interval: 30 - icmp: - - endpoint: https://api.github.com - thresholdMillis: 600 - packetLossThreshold: 10 - packetCount: 2 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| **endpoint** | | string | Yes | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| packetCount | | int | | -| packetLossThreshold | | int64 | | -| thresholdMillis | | int64 | | - - -## Jmeter - -Jmeter check will run jmeter cli against the supplied host -??? example - ```yaml - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| host | Host is the server against which test plan needs to be executed | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **jmx** | Jmx defines tge ConfigMap or Secret reference to get the JMX test plan | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| name | Name of the check | string | | -| port | Port on which the server is running | int32 | | -| properties | Properties defines the local Jmeter properties | \[\]string | | -| responseDuration | ResponseDuration under which the all the test should pass | string | | -| systemProperties | SystemProperties defines the java system property | \[\]string | | - - -## Junit - -Junit check will wait for the given pod to be completed than parses all the xml files present in the defined testResults directory - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: junit-pass - annotations: - trace: "true" - spec: - interval: 120 - owner: DBAdmin - severity: high - junit: - - testResults: "/tmp/junit-results/" - display: - template: | - ✅ {{.results.passed}} ❌ {{.results.failed}} in 🕑 {{.results.duration}} - {{ range $r := .results.suites}} - {{- if gt (conv.ToInt $r.failed) 0 }} - {{$r.name}} ✅ {{$r.passed}} ❌ {{$r.failed}} in 🕑 {{$r.duration}} - {{- end }} - {{- end }} - spec: - containers: - - name: jes - image: docker.io/tarun18/junit-test-pass - command: ["/start.sh"] - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| **spec** | | [v1.PodSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podspec-v1-core) | Yes | -| test | | [Template](#template) | | -| **testResults** | | string | Yes | -| timeout | Timeout in minutes to wait for specified container to finish its job. Defaults to 5 minutes | int | | - - -## LDAP - -The LDAP check will: - -* bind using provided user/password to the ldap host. Supports ldap/ldaps protocols. -* search an object type in the provided bind DN.s - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: ldap-pass - spec: - interval: 30 - ldap: - - host: ldap://apacheds.ldap.svc:10389 - auth: - username: - value: uid=admin,ou=system - password: - value: secret - bindDN: ou=users,dc=example,dc=com - userSearch: "(&(objectClass=organizationalPerson))" - - host: ldap://apacheds.ldap.svc:10389 - auth: - username: - value: uid=admin,ou=system - password: - value: secret - bindDN: ou=groups,dc=example,dc=com - userSearch: "(&(objectClass=groupOfNames))" - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **auth** | | *[Authentication](#authentication) | Yes | -| **bindDN** | | string | Yes | -| description | Description for the check | string | | -| **host** | | string | Yes | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| skipTLSVerify | | bool | | -| userSearch | | string | | - - -## Mongo - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: mongo - spec: - interval: 30 - mongodb: - - connection: mongodb://$(username):$(password)@mongo.default.svc:27017/?authSource=admin - description: mongo ping - auth: - username: - value: mongoadmin - password: - value: secret - dns: - - query: mongo.default.svc - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | [Authentication](#authentication) | | -| **connection** | | string | Yes | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | - - -## MsSQL - -This check will try to connect to a specified MsSQL database, run a query against it and verify the results. - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: mssql-pass - spec: - interval: 30 - mssql: - - connection: "server=mssql.default.svc;user id=$(username);password=$(password);port=1433;database=master" - auth: - username: - value: sa - password: - value: S0m3p@sswd - query: "SELECT 1" - results: 1 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | [Authentication](#authentication) | | -| **connection** | | string | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| **query** | | string | Yes | -| **results** | Number rows to check for | int | Yes | -| test | | [Template](#template) | | - - -## Namespace - -The Namespace check will: - -* create a new namespace using the labels/annotations provided - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: namespace-pass - spec: - interval: 30 - namespace: - - checkName: check - namespaceNamePrefix: "test-foo-" - podSpec: | - apiVersion: v1 - kind: Pod - metadata: - name: test-namespace - namespace: default - labels: - app: hello-world-golang - spec: - containers: - - name: hello - image: quay.io/toni0/hello-webserver-golang:latest - port: 8080 - path: /foo/bar - ingressName: test-namespace-pod - ingressHost: "test-namespace-pod.127.0.0.1.nip.io" - readyTimeout: 5000 - httpTimeout: 15000 - deleteTimeout: 12000 - ingressTimeout: 20000 - deadline: 29000 - httpRetryInterval: 200 - expectedContent: bar - expectedHttpStatuses: [200, 201, 202] - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **checkName** | | string | Yes | -| deadline | | int64 | | -| deleteTimeout | | int64 | | -| description | Description for the check | string | | -| expectedContent | | string | | -| expectedHttpStatuses | | \[\]int64 | | -| httpRetryInterval | | int64 | | -| httpTimeout | | int64 | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| ingressHost | | string | | -| ingressName | | string | | -| ingressTimeout | | int64 | | -| name | Name of the check | string | | -| namespaceAnnotations | | map[string]string | | -| namespaceLabels | | map[string]string | | -| namespaceNamePrefix | | string | | -| path | | string | | -| **podSpec** | | string | Yes | -| port | | int64 | | -| priorityClass | | string | | -| readyTimeout | | int64 | | -| scheduleTimeout | | int64 | | - - -## Pod - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: pod-pass - spec: - interval: 30 - pod: - - name: golang - namespace: default - spec: | - apiVersion: v1 - kind: Pod - metadata: - name: hello-world-golang - namespace: default - labels: - app: hello-world-golang - spec: - containers: - - name: hello - image: quay.io/toni0/hello-webserver-golang:latest - port: 8080 - path: /foo/bar - ingressName: hello-world-golang - ingressHost: "hello-world-golang.127.0.0.1.nip.io" - scheduleTimeout: 20000 - readyTimeout: 10000 - httpTimeout: 7000 - deleteTimeout: 12000 - ingressTimeout: 10000 - deadline: 60000 - httpRetryInterval: 200 - expectedContent: bar - expectedHttpStatuses: [200, 201, 202] - priorityClass: canary-checker-priority - - name: ruby - namespace: default - spec: | - apiVersion: v1 - kind: Pod - metadata: - name: hello-world-ruby - namespace: default - labels: - app: hello-world-ruby - spec: - containers: - - name: hello - image: quay.io/toni0/hello-webserver-ruby:latest - imagePullPolicy: Always - port: 8080 - path: /foo/bar - ingressName: hello-world-ruby - ingressHost: "hello-world-ruby.127.0.0.1.nip.io" - scheduleTimeout: 30000 - readyTimeout: 12000 - httpTimeout: 7000 - deleteTimeout: 12000 - ingressTimeout: 10000 - deadline: 29000 - httpRetryInterval: 200 - expectedContent: hello, you've hit /foo/bar - expectedHttpStatuses: [200, 201, 202] - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| deadline | | int64 | | -| deleteTimeout | | int64 | | -| description | Description for the check | string | | -| expectedContent | | string | | -| expectedHttpStatuses | | \[\]int | | -| httpRetryInterval | | int64 | | -| httpTimeout | | int64 | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **ingressHost** | | string | Yes | -| **ingressName** | | string | Yes | -| ingressTimeout | | int64 | | -| name | Name of the check | string | | -| **namespace** | | string | Yes | -| path | | string | | -| port | | int64 | | -| priorityClass | | string | | -| readyTimeout | | int64 | | -| scheduleTimeout | | int64 | | -| **spec** | | string | Yes | - - -## Postgres - -This check will try to connect to a specified Postgresql database, run a query against it and verify the results. - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: postgres-succeed - spec: - interval: 30 - postgres: - - connection: "postgres://$(username):$(password)@postgres.default.svc:5432/postgres?sslmode=disable" - auth: - username: - value: postgresadmin - password: - value: admin123 - query: SELECT current_schemas(true) - display: - template: | - {{- range $r := .results.rows }} - {{- $r.current_schemas}} - {{- end}} - results: 1 - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | [Authentication](#authentication) | | -| **connection** | | string | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| **query** | | string | Yes | -| **results** | Number rows to check for | int | Yes | -| test | | [Template](#template) | | - - -## Prometheus - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: prometheus - spec: - interval: 30 - prometheus: - - host: http://prometheus-k8s.monitoring.svc:9090 - query: kubernetes_build_info{job!~"kube-dns|coredns"} - display: - template: "{{ (index .results 0).git_version }}" - test: - template: "true" - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| **host** | Address of the prometheus server | string | Yes | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| **query** | PromQL query | string | Yes | -| test | | [Template](#template) | | - - -## Redis - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **addr** | | string | Yes | -| auth | | *[Authentication](#authentication) | | -| **db** | | int | Yes | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | - - -## Restic - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| accessKey | AccessKey access key id for connection with aws s3, minio, wasabi, alibaba oss | *[kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | | -| caCert | CaCert path to the root cert. In case of self-signed certificates | string | | -| checkIntegrity | CheckIntegrity when enabled will check the Integrity and consistency of the restic reposiotry | bool | | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| **maxAge** | MaxAge for backup freshness | string | Yes | -| name | Name of the check | string | | -| **password** | Password for the restic repository | *[kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| **repository** | Repository The restic repository path eg: rest:https://user:pass@host:8000/ or rest:https://host:8000/ or s3:s3.amazonaws.com/bucket_name | string | Yes | -| secretKey | SecretKey secret access key for connection with aws s3, minio, wasabi, alibaba oss | *[kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | | - - -## S3 - -S3 check will: - -* list objects in the bucket to check for Read permissions -* PUT an object into the bucket for Write permissions -* download previous uploaded object to check for Get permissions - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: s3-bucket-pass - annotations: - trace: "false" - spec: - interval: 30 - s3Bucket: - # Check for any backup not older than 7 days and min size 25 bytes - - bucket: flanksource-public - region: eu-central-1 - minSize: 50M - maxAge: 10d - filter: - regex: .*.ova - minSize: 100M - # maxAge: 18760h - display: - template: | - {{- range $f := .results.Files }} - {{- if gt $f.Size 0 }} - Name: {{$f.Name}} {{$f.ModTime | humanizeTime }} {{ $f.Size | humanizeBytes}} - {{- end}} - {{- end }} - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **accessKey** | | string | Yes | -| **bucket** | | [Bucket](#bucket) | Yes | -| description | Description for the check | string | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| name | Name of the check | string | | -| **objectPath** | | string | Yes | -| **secretKey** | | string | Yes | -| skipTLSVerify | Skip TLS verify when connecting to s3 | bool | | - - -## S3Bucket - -This check will - -- search objects matching the provided object path pattern -- check that latest object is no older than provided MaxAge value in seconds -- check that latest object size is not smaller than provided MinSize value in bytes. - -??? example - ```yaml - apiVersion: canaries.flanksource.com/v1 - kind: Canary - metadata: - name: s3-bucket-pass - spec: - interval: 30 - s3Bucket: - # Check for any backup not older than 7 days and min size 25 bytes - - bucket: tests-e2e-1 - accessKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_ACCESS_KEY_ID - secretKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_SECRET_ACCESS_KEY - region: "minio" - endpoint: "http://minio.minio:9000" - filter: - regex: "(.*)backup.zip$" - maxAge: 7d - minSize: 25b - usePathStyle: true - skipTLSVerify: true - # Check for any mysql backup not older than 7 days and min size 25 bytes - - bucket: tests-e2e-1 - accessKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_ACCESS_KEY_ID - secretKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_SECRET_ACCESS_KEY - region: "minio" - endpoint: "http://minio.minio:9000" - filter: - regex: "mysql\\/backups\\/(.*)\\/mysql.zip$" - maxAge: 7d - minSize: 25b - usePathStyle: true - skipTLSVerify: true - # Check for any pg backup not older than 7 days and min size 50 bytes - - bucket: tests-e2e-1 - accessKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_ACCESS_KEY_ID - secretKey: - valueFrom: - secretKeyRef: - name: aws-credentials - key: AWS_SECRET_ACCESS_KEY - region: "minio" - endpoint: "http://minio.minio:9000" - filter: - regex: "pg\\/backups\\/(.*)\\/backup.zip$" - maxAge: 7d - minSize: 25b - usePathStyle: true - skipTLSVerify: true - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **accessKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| **bucket** | | string | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| endpoint | | string | | -| filter | | [FolderFilter](#folderfilter) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| maxAge | MaxAge the latest object should be younger than defined age | Duration | | -| maxCount | MinCount the minimum number of files inside the searchPath | *int | | -| maxSize | MaxSize of the files inside the searchPath | Size | | -| minAge | MinAge the latest object should be older than defined age | Duration | | -| minCount | MinCount the minimum number of files inside the searchPath | *int | | -| minSize | MinSize of the files inside the searchPath | Size | | -| name | Name of the check | string | | -| objectPath | glob path to restrict matches to a subset | string | | -| region | | string | | -| **secretKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| skipTLSVerify | Skip TLS verify when connecting to aws | bool | | -| test | | [Template](#template) | | -| usePathStyle | Use path style path: http://s3.amazonaws.com/BUCKET/KEY instead of http://BUCKET.s3.amazonaws.com/KEY | bool | | - - -## Smb - -Smb check will connect to the given samba server with given credentials -find the age of the latest updated file and compare it with minAge -count the number of file present and compare with minCount if defined - -??? example - ```yaml - - ``` - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **auth** | | *[Authentication](#authentication) | Yes | -| description | Description for the check | string | | -| display | | [Template](#template) | | -| domain | Domain... | string | | -| filter | | [FolderFilter](#folderfilter) | | -| icon | Icon for overwriting default icon on the dashboard | string | | -| maxAge | MaxAge the latest object should be younger than defined age | Duration | | -| maxCount | MinCount the minimum number of files inside the searchPath | *int | | -| maxSize | MaxSize of the files inside the searchPath | Size | | -| minAge | MinAge the latest object should be older than defined age | Duration | | -| minCount | MinCount the minimum number of files inside the searchPath | *int | | -| minSize | MinSize of the files inside the searchPath | Size | | -| name | Name of the check | string | | -| port | Port on which smb server is running. Defaults to 445 | int | | -| searchPath | SearchPath sub-path inside the mount location | string | | -| **server** | Server location of smb server. Can be `hostname/ip` or in `\\server\e$\a\b\c` syntax -Where server is the `hostname` `e$`` is the sharename and `a/b/c` is the searchPath location | string | Yes | -| sharename | Sharename to mount from the samba server | string | | -| test | | [Template](#template) | | -| workstation | Workstation... | string | | - - -## Template - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| jsonPath | | string | | -| template | | string | | - - -## Connection - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| auth | | [Authentication](#authentication) | | -| **connection** | | string | Yes | - - -## AWSConnection - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **accessKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| endpoint | | string | | -| region | | string | | -| **secretKey** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| skipTLSVerify | Skip TLS verify when connecting to aws | bool | | - - -## Bucket - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **endpoint** | | string | Yes | -| **name** | | string | Yes | -| **region** | | string | Yes | - - -## FolderFilter - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| maxAge | | Duration | | -| maxSize | | Size | | -| minAge | | Duration | | -| minSize | | Size | | -| regex | | string | | - - -## GCPConnection - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **credentials** | | *[kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| **endpoint** | | string | Yes | - - -## Authentication - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **password** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | -| **username** | | [kommons.EnvVar](https://pkg.go.dev/github.com/flanksource/kommons#EnvVar) | Yes | - - -## Display - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | - - -## VarSource - -VarSource represents a source for a value - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| **ConfigMapKeyRef** | Selects a key of a ConfigMap. | *corev1.ConfigMapKeySelector | Yes | -| **FieldRef** | Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, -spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. | *corev1.ObjectFieldSelector | Yes | -| **SecretKeyRef** | Selects a key of a secret in the pod's namespace | *corev1.SecretKeySelector | Yes | -| **Value** | | string | Yes | - - -## CloudWatchFilter - - - -| Field | Description | Scheme | Required | -| ----- | ----------- | ------ | -------- | -| actionPrefix | | *string | | -| alarmPrefix | | *string | | -| alarms | | \[\]string | | -| state | | string | | - diff --git a/docs/canary-checker.png b/docs/canary-checker.png deleted file mode 100644 index 85cae7f3d6ab0650117348be4e8c50100692fc69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69574 zcmZs?byyT(`~JPObSaHkNS7krAp#PLbT`tlbT>#UDc!krcZagDNK5CkbS<5~!RPxt z|Ge+Pa4gL1%-l0`&wXFl=RB`(%8D}hI8-y{}BGrBSzDwuad9IOyHvI z@Jl(JHSCGQ1oPu;iJ~W3&YVprxGCJP*w~{3th%~l`bq*LYRU=91D;)=tgdh7ZN8wt z<6x={J6dj}JJ=WAUqq3}9pZ*qq5e53NtWyF`{<&I*P4SR`A96=1VH%=IzxXU;v6L$9{98{cBcvD6yY^c5`^Ar)aHw4xE&;C4 zTk8T9WUrx7)t@doteTu+VkKgkFA9ZT&)2F4-6Lw$%B!Y?FL5RLy-kyMxMjX~zDRub zNe0J~F*}kur*=C}1_xd*uo(ZOxk&mr?X|_n~yT&6{}}w_2XrhFLh(1t<~UD`zACz5z1;N!Pv}# z?;~gj<|hi82Fqldx?oSMy|ojR4=U=vsh5|DC|H!wng?0})7x%63nrHcbIcr@W!^I! z0nZ21NM1$?bpP;^)m9h>tiiVbpa}+na7iD2Q9vna6u?R>Cs_q)taWV4N3YqoNU)kf zpl2XiDe*6EX}jsJZjUFgE|%W5Fa?oW{@VZiid$T+S{$8~QE&IWBrHcAgIWSd`V+DC z?h|4cwD7^c5Dbp(C)DZ~@dYNXC|wm|7Qgq$v{9d%y21`(&RzG-cxQ&JmtGwp+>foz zHiq~QhSHMHTM?}YVbG_3i=p2R0vxTJvv3kAuo!h&Ky~%;F%1EXA8T1>*OXNYP716J z*Agc&25D-JGRHg%V|>Q#Tnc{{R`8)WwKX}}-5rZS$N7mdsQPBFp)SUu-K%NRl7|}4 zc;LMjL4XCpg?DyTf2&X-@@j{TtVXl&`dB_cvbqL&OcVX%r70!IU~g6#qeg4ktW9sG zA2v7$t(kK@=qHqX|Go%ll5u|pepI@;zE}2y6xl$6o=H>)xS{3jX+NmmR&`K)*rDI! z>?A4rSqf|wPtVrNP^%u@AhK%UsTE&3qHl0;aJX(R)AtUM zN!bD?i`!~B@HlN=#qpD8pQ*E25Xe8K7`K?WEt?t-Dj;ui|DKSLdEm6@s$U9MmNzrA zE$`v7(<_A&p>taI@gqh=4-ObDr&6e@Z0g9!sPJNh&8mJ5d)!VGtLv)j-1HD`%Y$3q zQ6*WO+n$;eDWJjl^=lcYJ~RGEPEJBNnVG-sr|6t0u1b_4I^nB@CZv^))>^{b)FG!f zZ_Ld$5OjR@zv<;Jzab|Gtng`5;@0e)_NXFv(MP|0I(ba}<`u($E!~@5*ZZlmingiz zh{`fbq&IP&i_r@))hA-k@5Cem5MFg0whIM983-475;56tjQUJ;z%yiQ{}cO4Hgd+= zXfAV)`RuyAXvud8GQBwpnybgBTWk{qT6NHI--QKgSoBy4PUS@#N^|eQ*KM9)Q%^L# zyl?DA9If@xS9}&3Etd%oz)1Nq;Cem5R2xxJ z!kQ@N)91d}@nT6j!qM^d$K{*r0|te`Uyp831KY|Ckhmrk>?!c~a_!1H3Rwd+)sBvG z@}8$ScQ>8uPk3rO62Df$4ceEyNOzHVsT>UzS!B?mWK1-Wm}7O{sw@Na-8)m$Q@H)A zlnW0??CaK_>^o?cMux9m1ymrZ?5nTS?4@Ws(^#qUmB*N~0Y|R$W^YrSo2|;RYrVufYs%u= ziB^M=%9>xfJ?dZmhIik$E_?)O)e?0qHqr41;6tk4wBAJGExomRsb}3kTaRC8BWye0 z=#XZ`o%2;o|Mvky>G{f@rA zPjb@0W$(8nJ?2t%FL4&;!_yD4#rly8E$>o7!mH>ZYdV)82Em7IP60^>pDc^vNSSFGDNsvK98{Z=~jw zy3PYFxHmQg(xHi(tiizo3ylt=u}Tv9n*&ct1Mr^eXk8r*fIzCMi+z`#KQLa@xmESq z$ztDylWRt*CMB&=+RPcXw{ui;U}Iaho6pR|h}t%Uj<}S{_pBZLc)0yzkg!I`rfBJQ=Wbmha-N3x?zR#eyDeB% zQd0W`fW52c`78y-6V}9 zrCUAQ<7))o-;bDq$4MjS5!(lt5l(|!FIB$g>atITe#-4ut(sh1`jdC&WnbCncw&*& z_G=|^8g3McEZ3D5+1~kN55J^mVc{6ry4kG>#`(yt|bPc$7M# z&rvtK#8TdPe>Vx?zh8~GJ~)_)LU!C;O?P}&;pbq#TaV+h;mZ^}Pt<-NmE%77BVeP9 zS5abrnDk=jiy@w0l?+|q*Pk41dGVRQ%nz66=h3h*@%~s)#KCRmykfAJc|VB~PZLRf z!M+j?-ez1$kLmH(7D(@KvWUZ@G^$Ddh}o0K*7fV-M80~(&5dqLZ0v09iV!`#7u!p3 zux2~#;?+8boq(gmD^Y0Pesz=f>*ZfYLH)C+S%Sgh?Lu>9q1qITA^!ytZU9f9AT7ke;4-U~gjfzCkD8SvStR zbnF+$Qks&a=)|%wL}*O7H{kpl>o=zU;KX+vLI#I&x7atEE*_&c}E*{iJNZ&CuG_O-w<9WWEN5A^y zXdXZ0*s+Y~PW11Q03PH*0*fkDUP|b4CA7Mlr2_rCX+NLuVV8K|=g&NL1g$jsQIQc- zdvk@a2!Sg`&9KQg!vMhsqCC%L0W^nxTv z$a_qG^SQolyZiQ+IP7J35}l{@6e}pg6qXi^)V$k4lNDgd?ZZqZX(V_Dd9w!yUprP;~-VrA9nRDNYS>7s{FvTfgf#cs52GjZvA zzo5s&*5ji_7M6tLh>2TOh`&8ka?XpT+t}Wptc27XEuuPfF8O45Pt`jlJSrW7TOSi3 zE*fkX(p}H?aKER`H*mB!c3a`Hpc&_A2FuT$?9Nmf-?Eroe7+B-ZgH8hE-nVm%=li< zWlQ?&pcsShZ_J;WgL=!=-|Bg&ROL)Pbvu!>$6o zK_Zcav{XVD4NDoG>}!X=plR932qI>!V(x2p8QyA~AMo8`{l-(mIVf?%X1;a}8)td$S$B(iol5w9?XL+Gs*&e~VQ*?r;vT4}ya!DY^A%*)a(-gqmu< zR7_jcYROj5{ie%uAU+E&ONhRfN2F$WHRYl$APS_3dg#;8O7%JFZ{9g@Q)w!G_@H#2 zBwxwlv3yK|-$T(kRd#W2RWa&qJCA<5j7gC8#`ONqWsih}o2pFhH@KjhWN&V#+&B^p zt7szw`Bi-#j~~p&c~7*BJwB_JL(YAI(SULCf>dfb=(nUSr0@OlSbjUM_L|n&V!M8t z-P}fMw4Omg*z@;md9sS;a&mcIIct5A;m@c@#jPxKKC<783dpLEN*!`Hkz>8PPI=ep zeAH(o>dp8l;8|F|6sZnhU{$jo1YMj6eYDZSF&qzZrQ`GoNQWM_D_c0?P_;DfTxFa6 zi^l(S__CV3c-SKL?V4-GVXZ%EO(%DzO^|JP>#;xni>S$P<0*>f?+-iYDEz9zN6!{8 ze8=9fS7UzUq4F73R6VG%oR)GLjlYH%I3q}$(E_hHGog>)I8B@NZtNXRgcK_gmBnBF zksPM?Yqeh$%^Pg1cO4x>oS79zd)ck~%bP8`gUz`|bLioP6ylGSc^+`Ux_MH+=2(nt{P?Nl#rog3JC( zysT{Qdxf+{S35^XO-+c}w{PYn;ZByR6sH`ZSN)aRb;qv4c}3VjR|$EfDbAujl!nHt ziMm|&6QiqqR7*!kbrM3}+52s8jziONElNI-MpVQh2XV1AJ`jC*5GMEWF+4tjDPg?{ z$8T-GhNnie;^0r0zCdfnR9Ql~@vrY`G%eo0z7MC8vvN{MB{gXTx=yi1^jS}B>3PVe zJ7tq@Rx|vq0p4Vu)S1$8d}=jcpRUN6HRhxC2zdzo+qb;aD-@y`T>T)CJB>Z}|4bWd zUrgRO?_FIV_$N|Bc7*S5=5<=yB(ahrxccSf?CpALa48_g#8zmz^G3flMm#A}Dni_l zv9S`v9F>ke>b}&>16!r#{X!%pH~F~dr>m}pv)nKgKHoL`{1a7GTEPsV{Kjw1G|GU?A-%fQl8cH#ftgwE!?F+;m+-=QbI=Nq4adh^?X1ukEQvts zDwQil5orc+04NMQ^G%iYxLWr<%0Hosnk$<$GYR@C3_0XbD~rDmBlf;3)xt1%qwv%I zdJn|UA3^SWjNKW4F|F%5X9EINr~LqOr?zW`OmuAz$uDZFtY71YExc|VTD%v9Ad^6z zP@r_qaF56=%+qni?o{Aa{ZgvA)+8#Q7)d(^Ax9uLoVSjl97#GxHBZR4ojPtvuz&Bw z$@>{g@-A5exj$x4wQ2i6RDT*xA+j4+yP>?OsDzpMnD=#3a&oFw10fx(>3z@TXl^R+ zNaj-APPwlC;7c<71}%lx>HE|AXu@>ottYXsP&m&PJ;~$;qPMmLUCqp%3VSSf;`1(_ z`rI{jcuh_%xi#snk1LH9uK8ol@2{`-_No-t&eoVO?0+LY!cHCDrs~frl=0rwS@N63X+OTqcy;D)m3Qbc=pUsi}Oo9+}`Jy&8@nI}ZX_Yqp z9YmxF^zrpWhuSGr#6P3Yp4E@EwSZbe+nOD(CN42i?uut7x1R?<0`9HWdXrVn@Vu}xMYuO*CxTM zy+>9NRSo=ZSgt(%e6nx(-k4Q|8m%g5uOtLi~!Vl#8%;CKB3NCrQkOv|A2@pJ$QK- za%6gEx*S8J@vwfbKL!i*LE*IBy4FNEDRS`RCE4y`*(eBPVlr|5bGB~Wf(q8Nbm`Az zR&(i(g-M9{3+j1$%BC%JtzMSG6swEgSbz7 zF2kQ(WZttYh#R-*L7ck+%5@_mywkbSkjY^MMr80_nVvE7QDZ+x&&N)cQn`AQYD1&1 zZ}5T9oHVi4PM9z{0QIH}lGP*q2Zw6Fh=30B(50yPV%6vN?pojPXe|_9yOpBT!fS)F zED>C$mP_oCrMILE^E}I1-I=LbIUAm-x%jJ9ThuWtKw}ZJdD?w;I^u`R`q?MF2RMJi-Qi!dQK`m{1WWPdrR?BARJCW-ES z9ipYkXA+Hhzw?=t*XZK#>$uCoMjBG35Oi~|#X2HdG48x=QYCcXsz>;4EevvP^zTs4SCt( zo;J~qkBF2V^FxsfFDd@>rMzLK{LhFeFBK9gFZTy3Iqd!;o+0*>cdE!42lt6niPt5( zS%wp&ng=C!Q-Yid+1;$3ZmnGsY^(#|G6NDHbKnyhq&dy|T(8)evQ++N z3KU7rYqwaUYV<+O>G@N^0>y&niKWl9LVwkdj8jkR=Vt5o{)iW9*YBQetql;;ZcoR6 zHC|bJ1u(O8|WgE z`Xy9a7O%E(jg1E!fzk4ffL+yfLuneBz zmCZTz#06Wwj=OFq2l=GL1X|&3CwAR!&-Pbexk0yAOmHiJwIn1G(1cY?OqF$iEl{9_ zseC45UDlaDzseXKO!%wnb-u9Dyl+3QQ$DRv{z?xqsXie0w2HR;ikjmBnK5w9Vdkpi zM9dQ(9)UtaIzEn~TopdSp7bcaw0(ZI221FiJiR5u{XHY|m#@@3ujpxoQ&P6m$(EP9 zl%Lf2UZPS590^yPoyNy+I=v7hn@VzBvKhyGMh{WQFqUZgj$gr`TC5b{gJZTYgvH>j*ev~|7P2>Kum=UuR{X_Osh<7$AwPd@wPLGl#t5O_0Z+! zC|}tjBGZ_jT%6nVMLFjm?2YWuN{r({mT=Tt-@->QeuUTHrFLdO`4Q4@u@dpd$W@@ z?BG^EGVg)t#*eh+j;J6%NNk#`9-j2ehcu~Q@_`{QJiTac2M&QwEWc`I&8rjS`R;K5 z(Dbm^4)8jKhqevVejfVw1)1+guOD{g%SmS3p!1EaLw{JE9b%UKlYgm(Vd~Apw}Yrs zf{o0q$MQ6wVh{gYTAYlBMm}g#;1vlV=FAjoTA9Eibc9hLVub#~Jg*OAWaJ*ssVpc(Ze;H;hpHU0=GD<0iqP2^qN99`@5|Bxi}aL77r4K6@|0Vt!@^sF_l;IDDVy3}`&ZaiiHnoq@0@}x#I_%{W} zc)U=X)vQL5*G?}B)u7#KYI&-U9LvL}*08kss?)z$D#CO$XLmOxMQp^baiz)-jd_#! zr9?v9LspU&w0jdwwnS}b@?R;)as;WS+{LoqD+Oa=34WE3pv5MpfG?KBjt$Tdk1;MF zLyA+v)Q+Q>`1fJ>8Fd%aw4onLO&&u6~de$ATL zEs?&T?ushBL>JrJ#HtTg`1HxpaJqcSN6^zn5ynzs?=vEd*@Hy~V%2h5m9k|Q^Js?F zxpDw1ikh}6#^G-U^N~eYI==;&hLMq@qeIp2i56E?Ro$jzByBXCwsIFC)#D5^GAJ+x;0g$1JGQ!Wk;-?SxkF#4&p0QLSDoTjCOQpN5Q3%_AP z(n#ub4FiHfv2g(aQ^*Zk{x`;$od+FBN#^EgH;0m$BI-IQK&*d#=6%B@CM)ag++dre zr}f2z$8~R8vwf8A$G%aa^t*>%b@d0+b9H+X10%9Yr?Ky+t9se`q;$F7b8s|{{O4UG zQKw&UIeD}{TO=ej1b(WJK`2KH-g3IDQ?uYc=x-gQq{P+E&-=Or;&~#*L;=W7J%}WotL)eaFr@ z;nid+Ums*sC24d@Tk=^lan=dZR%b#RA_wCa9atEFGvlMJtfMZff=a z?YPUIW_ZPyzKxWb{M+uG7!xCsP%N!N*UjBH1xSX zn@oD2xa6@jCa>LacZpDzN5(Wh?WaKdDe&&(<`|g{i(tnIwf@&l=q)2OUeVLP`sFAm zXJGDpun#%p>OW0n>j|l|ZZed-Sf>`^1A_B|afU|BkeSGi$;rEMd)OsM2Rg^|2e?D? z$UTyug$2lbFLpTcov3++(&y@csI-y3wULF{zC{`r?ihpttkf`P73gvJ__d47>YzW+1t6d_$Q5A?#mQ1 z8j9Z^2F{LEg55PfJ2w2+3($o+!h`Tn<9J-yfT~*YUHXHfU2y4BR`ri>A4bbZb}!Zw z_PUHTYd;hMgm=l|%O7>UI!#TLg7`!ND)g+;4`YdJmLvZK?7oheb6}!Ya>2!hp`lzELYj*! zE4(g`)5&d^vp|(jo5-Q#k4=5MJx6`-CkFLE*XbHTVaJaMvx(BH-SwxMY7HxvPJ z195c5kz8k2J7f{R^TvfQ46-hnlE{;YVXA`#K%_J`VzF~6c@`|r{E@4URd zIb0k}JPV7W>g?n4j;GM;Ra|HD8EbKtiqg_zm2|~iVzI=YQ3oqP+48s@pd+N=GabCU zUGmNRo;7Mw2Pgva_iHf(GA=F)w`=`vH^&W2s5-uFa|^jxofzaomnJsk)%w2P`%TU} z0G`sS{q^gSR5w33Je<$>sBc76)QiI4Y}fY=xee)2KU(xYzq__;{)Y72XJQl!t@%ab zk>qJYMIcemVA}D3u#ppCCz=x$pX8N2i?%s^DE&K^v+;UK_1S9Oj2UyVbga z$?AUTcxwIz^G|MQqi9!B3?f$o`(*cUt)lSL-YJHs@I#1EP@tx|a+&E94v(UvD?a`{ zG(^-~ZK~OXaky3Jzi~A{hfD5q;IkSuhTpN@JX4dzfmQh*!Tb9w+3rT;P(r{YAR!?) zI3kLs!3y4>5!$@O*Lm(~fVT`2FcZ(jby;MkOtX&VvE07!e zMw)S(q|4*HU2l`7?`}E}t=)jhp#F@}`%ka;`FVu-Q-M4kz;u%7T@X(vvNEr;R5Kam zp&Ed+nd8GU*eyu4w4Aq1f_QtvDC6>A>0H6XYUeaCO3%HzPWg!2QyhK@YC$!PAX{EU ztka@9vMy}M!w%zSA(sT2<=9DD_GFKO8s<|Qb_N|7y z_t8i4GX3Y*E$kqr%{Ar0^UMq`7_ZH-db@>&k=Ba*V*$nySb8IT>+6Y6OakoO^_E9l z^1OzmT$wV-6fUcq>79JEdRmP8H-%|B_=rTuoRT#etw=BDlwFE+GHAB);9UP~= z33obJtXkV&06~*Re)-QSX`vTVbcL-6Vj?ZYrnHZEs`9zlFnpwS99y^074Y(;ttBSsi zK)ZJAlN-QN5~g;G-v)8o{?c+gCeNQ_+Ha|}`56B-@SY>>P(rmtref^ck7x%!3D7~G z;FHC4_kfh{$nUWq*G-2W3>0RrUOOO%jXGYT{Hi`q!P3;^=g-e?^T_^PZ`r&p18lABZl}WgoRBI$)2;zaWQy{j_xjXmq$~VM zS4K>iGvJo;I_`D7eWJmNL{Q^F)Fy?y`4RVbu349+CNu(hTFw!jrP*5w6s}`UWcu&+xoH0&9r{q@_G6GZmcOZ zkJ4?()E>dtbTheV%6*E20Y`EKQvsBZl!~SV+Mo7aFK|4AOWvYVkgjLq) zW8y(G%Y|d$%T>J@R6y@t+Da2h0OY4kUZp+v?y*u_ty=%F=F=+UB70Jj!#YKU(~Mv? zva!vvd>?7w4Lr3BGPYJxTv^%1Hc#H@AQKbr69HOLpG{@QH)5)j$#1Ff#XLMlGV1pH z;ZXCT#?2Rg@*Eh;IuKPy9Gu6kA)#~x>OSoTNwX!YfAb5>h@RDR_BXNm-tqh3y#Dy; zhOi(AF0_1Iw zZgZ^_!X7;~PT8s3@iZOjdByGXRIBdzjtB=%FN79+^#o@osm5GVJ374M2NIB==3=>QZ`n@?rP3Xa(Lt|kJkMHo|Bej(XmuYfk#^oZcRx^ z!TZ=~THF&a$fU^|3bUcGAnQ78*V`!d7i|Kx9=!Kw)I3fvRKvCSG4aIs>+Q&|CDYI7 z4FDs{*0{r^?x}EnRrdkK+mKly`>cYE3kQH4!j#e_TF8C9(^GtI9L{#d13|A9X+&Bp zIUfh_MpOiEYkM=Z964YI-*m{wZ7XpQ>5k@DX+;ue3Iul?5vT9m-;JzL@Ns@_3awyK zhQQOR#y&6IDmc$Tfd=0-bB;0%K2v)Y zb{L4JghWK_T_q_D4<6?M^WcnmF1@}A8nu%%q9Nj!_Glu*mx>!@13b3%4~(1r-S-{# zF(g!PH-5-W0l2to^=P2Q1%r`Ev#tc7KrYv-tpP7bizQE@Nq)Xm`Fo?d+&_d3sw z(_S!qk`|J~{zi?Hdz#QWE1NSA3r+iCzwi2b>HdBD&DE<_omdLU0_Y?W>WgUU_-YoF z-)?c_zm7=#*suTy_^-#qh;Of_d2y8}v%h<{C1B)0H2y9v>P|q^+K%Ns1Uh#r==iJM zxH{X;ExF?u&~Db~&KJxZrjzu~_wfZaFJ1hvRenmeB_&OZE-?M!viK39JkcF{7FwjU zNS9t(IzZ>jw=ps>FSH{bI;P4zpCT2+1QTAkUwq=S_CE5%TNi6bM+sEpgap0~w}Upf z8+ej8oQP{=$K!Nr(M7;i6Oxek@xs^i`-W3n z$;XFo*wohB zI{yDSsHC0q$PH34JxA5B@{TQhf%I2RI@PVN2m1?OzbY0&;s5Na*o#(G z6q0M%#rhmmN#cPwwB$dsb6Awtx9wW9PTGxM0uXTS!M&DW{TVW8xv(!8>P4-{_v+}^ z%N3R+E*PEH;qFX$`1W@7oB72CB}S7|bqWeb_4EBx`7f?indk1YL2@%oMshPVY2fnF z$K>FQpFcD7bfaANlk^YW7AA>b4&Q&lG1hIihyATItTCV4qZOozdic!vn-!U#UwkwR zDVc5IeU4(#72tV5%Yz4KAG=(kXu!vfLnzvE2}I9#!PeY*t-rfl#5?|pf{Tk@Mvs5C zCeE%mr!V7{^`}H<*2t(W(NX3n_?yNP?DSCD)JO@I~+%!wjJk=>uMHVb|E5!=0 z@2)#xF;9ngJa}zS7D)GgDF=K2dT_2wq#dz}CT$v8>y>3C{?!#es;P?f#F`is_Y3>d z?I70#5Hc-Juil=jJLfm+jN1A!i&XBEr*d!2+40l8-B|yP$UyEcwKkbTtBg^s>l4`q zVnn^ETKHgsO0`+>$M{YHzehxsGDQqPVsrKB0)~8O`Q>2$jxALqyx9imTOIe zcH1eFjsWy7MvkPsT5i9gp||ED*LY$TBft1O$+En!Vd;<(&};)QMF|9jn`&1;)Oq`F z6<{iWIKv0e_E30ittYqlTTpzi%TyuQqX?fx-LgWXXXhpXT6 z9DSd80xT?p+A4r_0AK)a>%`W{%x#s0O1S~H3$yw-N8%qoEVovZEZ@1M+#~ zMs%c7J?Pu{ayZG)UMTgFRXy6pupNEzhY;O^{%+5%YcTX8Gn)+>IZ3chSLBuV{hJ#m zVqhvgWHrVjrixe!{MwRj$$o?mR~;&>2Y(S}@gLVZEF(&`SHp~Z)Qaz*P*3uHZsy8VL-##+0@k1vUczv zH=gaCKkflxT21HRZ)3L=Prac9%02yvEDr9^IdzQS7Z9f160Z3dCu-kA<&LPc{D_)W(43U+_7<9o9?oma@