Skip to content

Commit

Permalink
Support multinamespace informer filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
jkhelil committed Jul 2, 2024
1 parent 4929c41 commit be28a22
Show file tree
Hide file tree
Showing 7 changed files with 359 additions and 88 deletions.
21 changes: 17 additions & 4 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ package main

import (
"flag"
"fmt"
"regexp"
"strings"

"github.com/tektoncd/chains/pkg/reconciler/pipelinerun"
"github.com/tektoncd/chains/pkg/reconciler/taskrun"
Expand All @@ -41,7 +44,18 @@ import (

func main() {
flag.IntVar(&controller.DefaultThreadsPerController, "threads-per-controller", controller.DefaultThreadsPerController, "Threads (goroutines) to create per controller")
namespace := flag.String("namespace", "", "Namespace to restrict informer to. Optional, defaults to all namespaces.")
namespaceList := flag.String("namespaces", "", "Comma-separated list of namespaces to restrict informer to. Optional, if empty defaults to all namespaces.")
flag.Parse()

var namespaces []string
if *namespaceList != "" {
// Remove any whitespace from the namespaces string
space := regexp.MustCompile(`\s+`)
namespaces = strings.Split(space.ReplaceAllString(*namespaceList, ""), ",")
fmt.Printf("controller is scopped to the following namespaces: %s\n", namespaces)
} else {
namespaces = nil // Default to all namespaces if the list is empty
}

// This also calls flag.Parse().
cfg := injection.ParseAndGetRESTConfigOrDie()
Expand All @@ -57,8 +71,7 @@ func main() {
cfg.QPS = 2 * cfg.QPS
cfg.Burst = 2 * cfg.Burst

flag.Parse()
ctx := injection.WithNamespaceScope(signals.NewContext(), *namespace)
ctx := injection.WithNamespaceScope(signals.NewContext(), "")

sharedmain.MainWithConfig(ctx, "watcher", cfg, taskrun.NewController, pipelinerun.NewController)
sharedmain.MainWithConfig(ctx, "watcher", cfg, taskrun.NewNamespacesScoppedController(namespaces), pipelinerun.NewNamespacesScoppedController(namespaces))
}
80 changes: 80 additions & 0 deletions pkg/reconciler/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2024 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package reconciler

import (
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"knative.dev/pkg/controller"
)

// PipelineRunInformerFilterFunc returns a filter function
// for PipelineRuns ensuring PipelineRuns are filtered by list of namespaces membership
func PipelineRunInformerFilterFunc(namespaces []string) func(obj interface{}) bool {
return func(obj interface{}) bool {
// Namespace filter
if len(namespaces) == 0 {
return true
}
if pr, ok := obj.(*v1.PipelineRun); ok {
for _, ns := range namespaces {
if pr.Namespace == ns {
return true
}
}
}
return false
}
}

// TaskRunInformerFilterFunc returns a filter function
// for TaskRuns ensuring TaskRuns are filtered by list of namespaces membership
func TaskRunInformerFilterFunc(namespaces []string) func(obj interface{}) bool {
return func(obj interface{}) bool {
// Namespace filter
if len(namespaces) == 0 {
return true
}
if tr, ok := obj.(*v1.TaskRun); ok {
for _, ns := range namespaces {
if tr.Namespace == ns {
return true
}
}
}
return false
}
}

// TaskRunInformerFilterFunc returns a filter function

Check warning on line 59 in pkg/reconciler/filter.go

View workflow job for this annotation

GitHub Actions / lint

exported: comment on exported function TaskRunInformerFilterFuncWithOwnership should be of the form "TaskRunInformerFilterFuncWithOwnership ..." (revive)
// for TaskRuns ensuring Ownership by a PipelineRun and filtered by list of namespaces membership and
func TaskRunInformerFilterFuncWithOwnership(namespaces []string) func(obj interface{}) bool {
return func(obj interface{}) bool {
// Ownership filter
if !controller.FilterController(&v1.PipelineRun{})(obj) {
return false
}
// Namespace filter
if len(namespaces) == 0 {
return true
}
if tr, ok := obj.(*v1.TaskRun); ok {
for _, ns := range namespaces {
if tr.Namespace == ns {
return true
}
}
}
return false
}
}
159 changes: 159 additions & 0 deletions pkg/reconciler/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package reconciler

import (
"testing"

v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TestPipelineRunInformerFilterFunc tests the PipelineRunInformerFilterFunc
func TestPipelineRunInformerFilterFunc(t *testing.T) {

Check failure on line 11 in pkg/reconciler/filter_test.go

View workflow job for this annotation

GitHub Actions / lint

11-53 lines are duplicate of `pkg/reconciler/filter_test.go:56-98` (dupl)
tests := []struct {
name string
namespaces []string
obj interface{}
expected bool
}{
{
name: "Empty namespaces, should match",
namespaces: []string{},
obj: &v1.PipelineRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: true,
},
{
name: "Matching namespace",
namespaces: []string{"default", "test"},
obj: &v1.PipelineRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: true,
},
{
name: "Non-matching namespace",
namespaces: []string{"test"},
obj: &v1.PipelineRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: false,
},
{
name: "Non PipelineRun object",
namespaces: []string{"default"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filterFunc := PipelineRunInformerFilterFunc(tt.namespaces)
result := filterFunc(tt.obj)
if result != tt.expected {
t.Errorf("Reconciler.PipelineRunInformerFilterFunc() result = %v, wanted %v", result, tt.expected)
}
})
}
}

// TestTaskRunInformerFilterFunc tests the TaskRunInformerFilterFunc
func TestTaskRunInformerFilterFunc(t *testing.T) {

Check failure on line 56 in pkg/reconciler/filter_test.go

View workflow job for this annotation

GitHub Actions / lint

56-98 lines are duplicate of `pkg/reconciler/filter_test.go:11-53` (dupl)
tests := []struct {
name string
namespaces []string
obj interface{}
expected bool
}{
{
name: "Empty namespaces, should match",
namespaces: []string{},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: true,
},
{
name: "Matching namespace",
namespaces: []string{"default", "test"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: true,
},
{
name: "Non-matching namespace",
namespaces: []string{"test"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: false,
},
{
name: "Non TaskRun object",
namespaces: []string{"default"},
obj: &v1.PipelineRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filterFunc := TaskRunInformerFilterFunc(tt.namespaces)
result := filterFunc(tt.obj)
if result != tt.expected {
t.Errorf("Reconciler.TaskRunInformerFilterFunc() result = %v, wanted %v", result, tt.expected)
}
})
}
}

// TestTaskRunInformerFilterFuncWithOwnership tests the TaskRunInformerFilterFuncWithOwnership
func TestTaskRunInformerFilterFuncWithOwnership(t *testing.T) {
boolValue := true
tests := []struct {
name string
namespaces []string
obj interface{}
expected bool
}{
{
name: "Empty namespaces and ownership, should match",
namespaces: []string{},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{APIVersion: "tekton.dev/v1", Kind: "PipelineRun", Controller: &boolValue},
},
}},
expected: true,
},
{
name: "Matching namespace and ownership",
namespaces: []string{"default", "test"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{APIVersion: "tekton.dev/v1", Kind: "PipelineRun", Controller: &boolValue},
},
}},
expected: true,
},
{
name: "Non-matching namespace and ownership",
namespaces: []string{"test"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{APIVersion: "tekton.dev/v1", Kind: "PipelineRun", Controller: &boolValue},
},
}},
expected: false,
},
{
name: "No ownership",
namespaces: []string{"default"},
obj: &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "default"}},
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
filterFunc := TaskRunInformerFilterFuncWithOwnership(tt.namespaces)
result := filterFunc(tt.obj)
if result != tt.expected {
t.Errorf("Reconciler.TaskRunInformerFilterFuncWithOwnership() result = %v, wanted %v", result, tt.expected)
}
})
}
}
Loading

0 comments on commit be28a22

Please sign in to comment.