Skip to content

fix(kubernetes): support namespace wildcards #2582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/v1/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,13 +1027,15 @@ 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"`
Search string `json:"search,omitempty" yaml:"search,omitempty"`
}

func (rs ResourceSelector) ToDutySelector() types.ResourceSelector {
return types.ResourceSelector{
Name: rs.Name,
LabelSelector: rs.LabelSelector,
FieldSelector: rs.FieldSelector,
Search: rs.Search,
}
}

Expand Down
19 changes: 1 addition & 18 deletions checks/junit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"

"github.com/flanksource/artifacts"
"github.com/flanksource/canary-checker/api/context"
"github.com/flanksource/canary-checker/pkg/utils"
dutyKubernetes "github.com/flanksource/duty/kubernetes"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -281,21 +278,7 @@ func (c *JunitChecker) Check(ctx *context.Context, extConfig external.Check) pkg
return results.Failf("")
}

for _, artifactConfig := range check.Artifacts {
paths := utils.UnfoldGlobs(artifactConfig.Path)
for _, path := range paths {
file, err := os.Open(path)
if err != nil {
ctx.Error(err, "error opening file. path=%s", path)
continue
}

result.Artifacts = append(result.Artifacts, artifacts.Artifact{
Path: path,
Content: file,
})
}
}
//FIXME: junit.artifacts should be extracted from the pod, not from canary-checker

return results
}
Expand Down
34 changes: 23 additions & 11 deletions checks/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,43 @@ func (c *KubernetesChecker) Check(ctx context.Context, extConfig external.Check)
if err != nil {
return results.Failf("Kubernetes is not initialized: %v", err)
}

namespaces, err := k8sClient.QueryResources(ctx, check.Namespace.ToDutySelector().Type("Namespace"))

if err != nil {
return results.Failf("Failed to get namespaces: %v", err)
var namespaces = []string{}
var nsSelector = check.Namespace.ToDutySelector()
if nsSelector.Wildcard() {
namespaces = append(namespaces, "")
} else if !nsSelector.IsEmpty() {
list, err := k8sClient.QueryResources(ctx, check.Namespace.ToDutySelector().Type("Namespace").MetadataOnly())
if err != nil {
return results.Failf("Failed to get namespaces: %v", err)
}
for _, v := range list {
namespaces = append(namespaces, v.GetName())
}
} else {
namespaces = append(namespaces, ctx.GetNamespace())
}

var allResources []unstructured.Unstructured

for _, namespace := range namespaces {
selector := check.Resource.ToDutySelector()
selector.Namespace = namespace.GetName()
if namespace != "" {
selector.Namespace = namespace
}
selector.Types = []string{check.Kind}
resources, err := k8sClient.QueryResources(ctx, selector)

if err != nil {
return results.Failf("failed to get resources: %v. namespace: %v", err, namespace)
return results.Failf("failed to get resources (%s): %v", selector, err)
}
for _, filter := range check.Ignore {
resources, err = filterResources(resources, filter)
if err != nil {
results.Failf("failed to filter resources: %v. filter: %v", err, filter)
results.Failf("failed to filter resources: %v, filter: %v", err, filter)
return results
}
}

ctx.Tracef("Found %d %s in namespace %s with label=%s field=%s", len(resources), check.Kind, namespace, check.Resource.LabelSelector, check.Resource.FieldSelector)
for _, resource := range resources {
_resource := resource
resourceHealth, err := health.GetResourceHealth(&_resource, nil)
Expand All @@ -75,12 +87,12 @@ func (c *KubernetesChecker) Check(ctx context.Context, extConfig external.Check)
resource.Object["healthStatus"] = resourceHealth

if check.Healthy && resourceHealth.Health != health.HealthHealthy {
results.Failf("%s/%s/%s is not healthy (health: %s, status: %s): %s",
results.Failf("%s/%s/%s is not healthy (health: %s, status: %s): %s\n",
resource.GetKind(), resource.GetNamespace(), resource.GetName(), resourceHealth.Health, resourceHealth.Status, resourceHealth.Message)
}

if check.Ready && !resourceHealth.Ready {
results.Failf("%s/%s/%s is not ready (status: %s): %s", resource.GetKind(),
results.Failf("%s/%s/%s is not ready (status: %s): %s\n", resource.GetKind(),
resource.GetNamespace(), resource.GetName(), resourceHealth.Status, resourceHealth.Message)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func InitContext() (context.Context, error) {
if err != nil {
return ctx, fmt.Errorf("failed to initialize db: %v", err.Error())
}
shutdown.AddHook(closer)
shutdown.AddHookWithPriority("db", 0, closer)

if err := properties.LoadFile(propertiesFile); err != nil {
return ctx, fmt.Errorf("failed to load properties: %v", err)
Expand Down
3 changes: 2 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ var Run = &cobra.Command{
if err != nil {
logger.Fatalf("Failed to initialize db: %v", err.Error())
}
shutdown.AddHook(closer)
shutdown.AddHookWithPriority("db", 0, closer)
defer closer()

apicontext.DefaultContext = ctx

Expand Down
4 changes: 4 additions & 0 deletions config/deploy/Canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5345,6 +5345,8 @@ spec:
type: string
name:
type: string
search:
type: string
type: object
ready:
description: Fail the check if any resources are not ready
Expand All @@ -5360,6 +5362,8 @@ spec:
type: string
name:
type: string
search:
type: string
type: object
test:
properties:
Expand Down
4 changes: 4 additions & 0 deletions config/deploy/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5344,6 +5344,8 @@ spec:
type: string
name:
type: string
search:
type: string
type: object
ready:
description: Fail the check if any resources are not ready
Expand All @@ -5359,6 +5361,8 @@ spec:
type: string
name:
type: string
search:
type: string
type: object
test:
properties:
Expand Down
3 changes: 3 additions & 0 deletions config/schemas/canary.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3775,6 +3775,9 @@
},
"fieldSelector": {
"type": "string"
},
"search": {
"type": "string"
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions config/schemas/component.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4225,6 +4225,9 @@
},
"fieldSelector": {
"type": "string"
},
"search": {
"type": "string"
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions config/schemas/health_kubernetes.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@
},
"fieldSelector": {
"type": "string"
},
"search": {
"type": "string"
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions config/schemas/topology.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4231,6 +4231,9 @@
},
"fieldSelector": {
"type": "string"
},
"search": {
"type": "string"
}
},
"additionalProperties": false,
Expand Down
9 changes: 6 additions & 3 deletions fixtures/k8s/kubernetes-minimal_pass.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ spec:
kubernetes:
- name: kube-system
kind: Pod
# ready: true
healthy: true
# resource:
# labelSelector: k8s-app=kube-dns
# search: labels.app=test
# OR
# labelSelector: k8s-app=kube-dns
namespaceSelector:
name: kube-system
name: kube-*,!*lease
# name: "*"
display:
expr: |
dyn(results).
Expand Down
10 changes: 10 additions & 0 deletions fixtures/minimal/schedule_invalid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: canaries.flanksource.com/v1
kind: Canary
metadata:
name: schedule-invalid
spec:
schedule: "8 46 *"
tcp:
- name: "flanksource website"
endpoint: www.flanksource.com:80
thresholdMillis: 1200
10 changes: 10 additions & 0 deletions fixtures/minimal/tcp_invalid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: canaries.flanksource.com/v1
kind: Canary
metadata:
name: tcp-invalid
spec:
schedule: "*/1 * * * *"
tcp:
- name: "flanksource website"
endpoint: https://www.flanksource.com:80
thresholdMillis: 1200
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -323,9 +323,6 @@ require (
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect
Expand Down Expand Up @@ -387,7 +384,7 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)

// replace github.com/flanksource/duty => ../duty
//replace github.com/flanksource/duty => ../duty

// replace github.com/flanksource/artifacts => ../artifacts

Expand Down
6 changes: 0 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1753,12 +1753,6 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
Expand Down
18 changes: 0 additions & 18 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import (
"encoding/json"
"fmt"
"net"
"path/filepath"
"sync"
"time"

"github.com/flanksource/commons/logger"

"github.com/google/uuid"
"golang.org/x/sync/semaphore"
"k8s.io/apimachinery/pkg/util/duration"
Expand Down Expand Up @@ -124,21 +121,6 @@ func MapKeys[T any](m map[string]T) []string {
return keys
}

func UnfoldGlobs(paths ...string) []string {
unfoldedPaths := make([]string, 0, len(paths))
for _, path := range paths {
matched, err := filepath.Glob(path)
if err != nil {
logger.Warnf("invalid glob pattern. path=%s; %w", path, err)
continue
}

unfoldedPaths = append(unfoldedPaths, matched...)
}

return unfoldedPaths
}

func FreePort() int {
// Bind to port 0 to let the OS choose a free port
listener, err := net.Listen("tcp", ":0")
Expand Down
Loading