Skip to content

Commit

Permalink
Merge pull request #337 from flanksource/kubernetes-check
Browse files Browse the repository at this point in the history
feat: add kubernetes check
  • Loading branch information
moshloop authored Oct 1, 2021
2 parents e8d0eaf + 6b899ee commit 3c3b051
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/v1/canary_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type CanarySpec struct {
MongoDB []MongoDBCheck `yaml:"mongodb,omitempty" json:"mongodb,omitempty"`
CloudWatch []CloudWatchCheck `yaml:"cloudwatch,omitempty" json:"cloudwatch,omitempty"`
GitHub []GitHubCheck `yaml:"github,omitempty" json:"github,omitempty"`
Kubernetes []KubernetesCheck `yaml:"kubernetes,omitempty" json:"kubernetes,omitempty"`
// interval (in seconds) to run checks on Deprecated in favor of Schedule
Interval uint64 `yaml:"interval,omitempty" json:"interval,omitempty"`
// 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
Expand Down Expand Up @@ -155,6 +156,9 @@ func (spec CanarySpec) GetAllChecks() []external.Check {
for _, check := range spec.GitHub {
checks = append(checks, check)
}
for _, check := range spec.Kubernetes {
checks = append(checks, check)
}
return checks
}

Expand Down
38 changes: 38 additions & 0 deletions api/v1/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,36 @@ func (c GitHubCheck) GetEndpoint() string {
return strings.ReplaceAll(c.Query, " ", "-")
}

type ResourceSelector struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
LabelSelector string `json:"labelSelector,omitempty" yaml:"labelSelector,omitempty"`
FieldSelector string `json:"fieldSelector,omitempty" yaml:"fieldSelector,omitempty"`
}

type KubernetesCheck struct {
Description `yaml:",inline" json:",inline"`
Templatable `yaml:",inline" json:",inline"`
Namespace ResourceSelector `yaml:"namespace,omitempty" json:"namespace,omitempty"`
Resource ResourceSelector `yaml:"resource,omitempty" json:"resource,omitempty"`
Kind string `yaml:"kind" json:"kind"`
Ready *bool `yaml:"ready,omitempty" json:"ready,omitempty"`
}

func (c KubernetesCheck) GetType() string {
return "kubernetes"
}

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
}

/*
[include:minimal/http_pass.yaml]
*/
Expand Down Expand Up @@ -883,6 +913,13 @@ type GCSBucket struct {
GCSBucketCheck `yaml:",inline" json:",inline"`
}

/*
[include:k8s/kuberenetes_pass.yaml]
*/
type Kubernetes struct {
KubernetesCheck `yaml:",inline" json:",inline"`
}

/*
[include:aws/ec2_pass.yaml]
*/
Expand Down Expand Up @@ -937,4 +974,5 @@ var AllChecks = []external.Check{
MongoDBCheck{},
CloudWatchCheck{},
GitHubCheck{},
Kubernetes{},
}
62 changes: 62 additions & 0 deletions api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions checks/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var All = []Checker{
&GCSBucketChecker{},
&CloudWatchChecker{},
&GitHubChecker{},
&KubernetesChecker{},
NewPodChecker(),
NewNamespaceChecker(),
NewTCPChecker(),
Expand Down
113 changes: 113 additions & 0 deletions checks/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package checks

import (
"fmt"

"github.com/flanksource/canary-checker/api/context"
"github.com/flanksource/canary-checker/api/external"
v1 "github.com/flanksource/canary-checker/api/v1"
"github.com/flanksource/canary-checker/pkg"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
)

type KubernetesChecker struct{}

func (c *KubernetesChecker) Type() string {
return "kubernetes"
}

// Run: Check every entry from config according to Checker interface
// Returns check result and metrics
func (c *KubernetesChecker) Run(ctx *context.Context) []*pkg.CheckResult {
var results []*pkg.CheckResult
for _, conf := range ctx.Canary.Spec.Kubernetes {
results = append(results, c.Check(ctx, conf))
}
return results
}

// CheckConfig : Check every ldap entry for lookup and auth
// Returns check result and metrics
func (c *KubernetesChecker) Check(ctx *context.Context, extConfig external.Check) *pkg.CheckResult {
check := extConfig.(v1.KubernetesCheck)
result := pkg.Success(check, ctx.Canary)
client, err := ctx.Kommons.GetClientByKind(check.Kind)
if err != nil {
return result.Failf("Failed to get client for kind %s: %v", check.Kind, err)
}
namespaces, err := getNamespaces(ctx, check)
if err != nil {
return result.Failf("Failed to get namespaces: %v", err)
}
var allResources []unstructured.Unstructured
differentReadyStatus := make(map[string]string)
for _, namespace := range namespaces {
resources, err := getResourcesFromNamespace(ctx, client, check, namespace)
if err != nil {
return result.Failf("failed to get resources: %v. namespace: %v", err, namespace)
}
for _, resource := range resources {
ready, msg := ctx.Kommons.IsReady(&resource)
if ready != check.CheckReady() {
differentReadyStatus[fmt.Sprintf("The resource %v-%v-%v is expected Ready: %v but Ready is %v", resource.GetName(), resource.GetNamespace(), resource.GetKind(), ready, check.CheckReady())] = msg
}
}
allResources = append(allResources, resources...)
}
if allResources == nil {
return result.Failf("no resources found")
}
result.AddDetails(allResources)
if len(differentReadyStatus) > 0 {
message := "The following resources found with differednt ready status\n"
for key, value := range differentReadyStatus {
message += fmt.Sprintf("%v: %v\n", key, value)
}
return result.Failf(message)
}
return result
}

func getResourcesFromNamespace(ctx *context.Context, client dynamic.NamespaceableResourceInterface, check v1.KubernetesCheck, namespace string) ([]unstructured.Unstructured, error) {
var resources []unstructured.Unstructured
if check.Resource.Name != "" {
resource, err := client.Namespace(namespace).Get(ctx, check.Resource.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
return []unstructured.Unstructured{*resource}, nil
}
resourceList, err := client.Namespace(namespace).List(ctx, metav1.ListOptions{
LabelSelector: check.Resource.LabelSelector,
FieldSelector: check.Resource.FieldSelector,
})
if err != nil {
return nil, err
}
resources = append(resources, resourceList.Items...)
return resources, nil
}

func getNamespaces(ctx *context.Context, check v1.KubernetesCheck) ([]string, error) {
var namespaces []string
if check.Namespace.Name != "" {
return []string{check.Namespace.Name}, nil
}
k8sClient, err := ctx.Kommons.GetClientset()
if err != nil {
return nil, err
}
namespeceList, err := k8sClient.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
LabelSelector: check.Namespace.LabelSelector,
FieldSelector: check.Namespace.FieldSelector,
})
if err != nil {
return nil, err
}
for _, namespace := range namespeceList.Items {
namespaces = append(namespaces, namespace.Name)
}
return namespaces, nil
}
56 changes: 56 additions & 0 deletions config/deploy/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4979,6 +4979,62 @@ spec:
- testResults
type: object
type: array
kubernetes:
items:
properties:
description:
description: Description for the check
type: string
display:
properties:
expr:
type: string
jsonPath:
type: string
template:
type: string
type: object
icon:
description: Icon for overwriting default icon on the dashboard
type: string
kind:
type: string
name:
description: Name of the check
type: string
namespace:
properties:
fieldSelector:
type: string
labelSelector:
type: string
name:
type: string
type: object
ready:
type: boolean
resource:
properties:
fieldSelector:
type: string
labelSelector:
type: string
name:
type: string
type: object
test:
properties:
expr:
type: string
jsonPath:
type: string
template:
type: string
type: object
required:
- kind
type: object
type: array
ldap:
items:
properties:
Expand Down
Loading

0 comments on commit 3c3b051

Please sign in to comment.