Skip to content

Commit

Permalink
introduce accesses caching (#46)
Browse files Browse the repository at this point in the history
* introduce accesses caching

Signed-off-by: Francesco Ilario <[email protected]>

* add serviceaccount support

Signed-off-by: Francesco Ilario <[email protected]>

* rename files, add tests

Signed-off-by: Francesco Ilario <[email protected]>

* disable performance cluster deletion

Signed-off-by: Francesco Ilario <[email protected]>

* making ginkgo tests parallel

Signed-off-by: Francesco Ilario <[email protected]>

---------

Signed-off-by: Francesco Ilario <[email protected]>
  • Loading branch information
filariow authored Jan 31, 2025
1 parent df42cd7 commit 3a25aa9
Show file tree
Hide file tree
Showing 28 changed files with 993 additions and 61 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fmt: ## Run go fmt against code.

.PHONY: test
test: ## Run go test against code.
$(GINKGO) --label-filter='!perf'
$(GINKGO) --label-filter='!perf' ./pkg/... ./

.PHONY: test-perf
test-perf: ## Run performance tests
Expand All @@ -61,6 +61,7 @@ test-perf: ## Run performance tests
--name namespace-lister-perf-test $(PERF_CLUSTER_PROVIDER_FLAGS)
KUBECONFIG=$(PERF_CLUSTER_KUBECONFIG) $(GINKGO) --label-filter='perf' \
--keep-going --procs=1 --flake-attempts 2 --output-dir=$(PERF_OUT_DIR)
# -$(PERF_CLUSTER_PROVIDER) delete cluster --name namespace-lister-perf-test

.PHONY: image-build
image-build:
Expand Down
5 changes: 5 additions & 0 deletions acceptance/config/patches/with_cache_resyncperiod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: CACHE_RESYNC_PERIOD
value: '20s'
74 changes: 42 additions & 32 deletions acceptance/pkg/suite/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@ package suite
import (
"context"
"fmt"
"log"
"slices"
"time"

"github.com/cucumber/godog"

corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"

tcontext "github.com/konflux-ci/namespace-lister/acceptance/pkg/context"
arest "github.com/konflux-ci/namespace-lister/acceptance/pkg/rest"
)

func InjectSteps(ctx *godog.ScenarioContext) {
//read
// read
ctx.Step(`^ServiceAccount has access to a namespace$`,
func(ctx context.Context) (context.Context, error) { return UserInfoHasAccessToNNamespaces(ctx, 1) })
ctx.Step(`^User has access to a namespace$`,
Expand Down Expand Up @@ -92,49 +95,56 @@ func TheUserCanRetrieveOnlyTheNamespacesTheyHaveAccessTo(ctx context.Context) (c
return ctx, err
}

ann := corev1.NamespaceList{}
if err := cli.List(ctx, &ann); err != nil {
return ctx, err
}

enn := tcontext.Namespaces(ctx)
if expected, actual := len(enn), len(ann.Items); expected != actual {
return ctx, fmt.Errorf("expected %d namespaces, actual %d", expected, actual)
}
return ctx, wait.PollUntilContextTimeout(ctx, 2*time.Second, 1*time.Minute, true, func(ctx context.Context) (done bool, err error) {
ann := corev1.NamespaceList{}
if err := cli.List(ctx, &ann); err != nil {
log.Printf("error listing namespaces: %v", err)
return false, nil
}

for _, en := range enn {
if !slices.ContainsFunc(ann.Items, func(an corev1.Namespace) bool {
return en.Name == an.Name
}) {
return ctx, fmt.Errorf("expected namespace %s not found in actual namespace list: %v", en.Name, ann.Items)
enn := tcontext.Namespaces(ctx)
if expected, actual := len(enn), len(ann.Items); expected != actual {
log.Printf("expected %d namespaces, actual %d", expected, actual)
return false, nil
}
}

return ctx, nil
for _, en := range enn {
if !slices.ContainsFunc(ann.Items, func(an corev1.Namespace) bool {
return en.Name == an.Name
}) {
log.Printf("expected namespace %s not found in actual namespace list: %v", en.Name, ann.Items)
return false, nil
}
}
return true, nil
})
}

func TheUserCanRetrieveTheNamespace(ctx context.Context) (context.Context, error) {
run := tcontext.RunId(ctx)

cli, err := tcontext.InvokeBuildUserClientFunc(ctx)
if err != nil {
return ctx, err
}

n := corev1.Namespace{}
k := types.NamespacedName{Name: fmt.Sprintf("run-%s-0", run)}
if err := cli.Get(ctx, k, &n); err != nil {
return ctx, err
}

enn := tcontext.Namespaces(ctx)
if expected, actual := 1, len(enn); expected != actual {
return ctx, fmt.Errorf("expected %d namespaces, actual %d: %v", expected, actual, enn)
}
return ctx, wait.PollUntilContextTimeout(ctx, 2*time.Second, 1*time.Minute, true, func(ctx context.Context) (done bool, err error) {
n := corev1.Namespace{}
k := types.NamespacedName{Name: fmt.Sprintf("run-%s-0", run)}
if err := cli.Get(ctx, k, &n); err != nil {
log.Printf("error getting namespace %v: %v", k, err)
return false, nil
}

if expected, actual := n.Name, enn[0].Name; actual != expected {
return ctx, fmt.Errorf("expected namespace %s, actual %s", expected, actual)
}
enn := tcontext.Namespaces(ctx)
if expected, actual := 1, len(enn); expected != actual {
log.Printf("expected %d namespaces, actual %d: %v", expected, actual, enn)
return false, nil
}

return ctx, nil
if expected, actual := n.Name, enn[0].Name; actual != expected {
log.Printf("expected namespace %s, actual %s", expected, actual)
return false, nil
}
return true, nil
})
}
14 changes: 14 additions & 0 deletions acceptance/test/dumb-proxy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ deploy-namespace-lister: $(OUT_DIR)
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
cp "$(ROOT_DIR)/../config/patches/with_log_debug.yaml" . && \
kustomize edit add patch \
--path "with_log_debug.yaml" \
--group "apps" \
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
cp "$(ROOT_DIR)/config/patches/with_cache_resyncperiod.yaml" . && \
kustomize edit add patch \
--path "with_cache_resyncperiod.yaml" \
--group "apps" \
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
kustomize build | $(KUBECTL) apply -f - ; \
)

Expand Down
18 changes: 16 additions & 2 deletions acceptance/test/smart-proxy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ deploy-namespace-lister: $(OUT_DIR)
cd $(OUT_DIR)/config && \
kustomize init && \
kustomize edit add base "../../../../../config" && \
kustomize edit set namespace namespace-lister && \
kustomize edit set image "namespace-lister:latest=$(IMG)" && \
cp "$(ROOT_DIR)/../config/patches/with_header_auth.yaml" . && \
kustomize edit add patch \
--path "with_header_auth.yaml" \
Expand All @@ -40,8 +42,20 @@ deploy-namespace-lister: $(OUT_DIR)
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
kustomize edit set namespace namespace-lister && \
kustomize edit set image "namespace-lister:latest=$(IMG)" && \
cp "$(ROOT_DIR)/../config/patches/with_log_debug.yaml" . && \
kustomize edit add patch \
--path "with_log_debug.yaml" \
--group "apps" \
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
cp "$(ROOT_DIR)/config/patches/with_cache_resyncperiod.yaml" . && \
kustomize edit add patch \
--path "with_cache_resyncperiod.yaml" \
--group "apps" \
--kind "Deployment" \
--name "namespace-lister" \
--version "v1" && \
kustomize build | $(KUBECTL) apply -f - ; \
)

Expand Down
72 changes: 72 additions & 0 deletions access_cache_startup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"context"
"os"
"time"

authcache "github.com/konflux-ci/namespace-lister/pkg/auth/cache"

corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
toolscache "k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
crcache "sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func buildAndStartAccessCache(ctx context.Context, resourceCache crcache.Cache) (*authcache.SynchronizedAccessCache, error) {
aur := &CRAuthRetriever{resourceCache, ctx, getLoggerFromContext(ctx)}
sae := rbac.NewSubjectAccessEvaluator(aur, aur, aur, aur, "")
synchCache := authcache.NewSynchronizedAccessCache(
sae,
resourceCache, authcache.CacheSynchronizerOptions{
Logger: getLoggerFromContext(ctx),
ResyncPeriod: getResyncPeriodFromEnvOrZero(ctx),
},
)

// register event handlers on resource cache
oo := []client.Object{
&corev1.Namespace{},
&rbacv1.RoleBinding{},
&rbacv1.ClusterRole{},
&rbacv1.ClusterRoleBinding{},
&rbacv1.Role{},
}
for _, o := range oo {
i, err := resourceCache.GetInformer(ctx, o)
if err != nil {
return nil, err
}

if _, err := i.AddEventHandler(
toolscache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { synchCache.Request() },
UpdateFunc: func(oldObj, newObj interface{}) { synchCache.Request() },
DeleteFunc: func(obj interface{}) { synchCache.Request() },
}); err != nil {
return nil, err
}
}
synchCache.Start(ctx)

if err := synchCache.Synch(ctx); err != nil {
return nil, err
}
return synchCache, nil
}

func getResyncPeriodFromEnvOrZero(ctx context.Context) time.Duration {
var zero time.Duration
rps, ok := os.LookupEnv(EnvCacheResyncPeriod)
if !ok {
return zero
}
rp, err := time.ParseDuration(rps)
if err != nil {
getLoggerFromContext(ctx).Warn("can not parse duration from environment variable", "error", err)
return zero
}
return rp
}
5 changes: 5 additions & 0 deletions config/patches/with_cache_resyncperiod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: CACHE_RESYNC_PERIOD
value: '10m'
5 changes: 5 additions & 0 deletions config/patches/with_log_debug.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- op: replace
path: /spec/template/spec/containers/0/env/0
value:
name: LOG_LEVEL
value: "-4"
7 changes: 4 additions & 3 deletions const.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package main

const (
EnvLogLevel string = "LOG_LEVEL"
EnvUsernameHeader string = "AUTH_USERNAME_HEADER"
EnvAddress string = "ADDRESS"
EnvLogLevel string = "LOG_LEVEL"
EnvUsernameHeader string = "AUTH_USERNAME_HEADER"
EnvAddress string = "ADDRESS"
EnvCacheResyncPeriod string = "CACHE_RESYNC_PERIOD"

DefaultAddr string = ":8080"
DefaultHeaderUsername string = "X-Email"
Expand Down
7 changes: 7 additions & 0 deletions interfaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package main_test

import (
"k8s.io/client-go/rest"

namespacelister "github.com/konflux-ci/namespace-lister"
)

//go:generate mockgen -source=interfaces_test.go -destination=mocks/rest_interface.go -package=mocks

type FakeInterface interface {
rest.Interface
}

type FakeSubjectNamespacesLister interface {
namespacelister.SubjectNamespacesLister
}
21 changes: 13 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"syscall"

"github.com/go-logr/logr"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log"
)
Expand Down Expand Up @@ -69,20 +68,26 @@ func run(l *slog.Logger) error {

ctx = setLoggerIntoContext(ctx, l)

// create cache
l.Info("creating cache")
cacheCfg, err := NewCacheConfigFromEnv(cfg)
// create resource cache
l.Info("creating resource cache")
cacheCfg, err := NewResourceCacheConfigFromEnv(cfg)
if err != nil {
return err
}
cache, err := BuildAndStartCache(ctx, cacheCfg)
resourceCache, err := BuildAndStartResourceCache(ctx, cacheCfg)
if err != nil {
return err
}

// create access cache
l.Info("creating access cache")
accessCache, err := buildAndStartAccessCache(ctx, resourceCache)
if err != nil {
return err
}

// create the authorizer and the namespace lister
auth := NewAuthorizer(ctx, cache)
nsl := NewNamespaceLister(cache, auth)
// create the namespace lister
nsl := NewNamespaceListerForSubject(accessCache)

// build http server
l.Info("building server")
Expand Down
40 changes: 40 additions & 0 deletions mocks/rest_interface.go

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

Loading

0 comments on commit 3a25aa9

Please sign in to comment.