Skip to content
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

ClustersClientsPool Middleware #1627

Merged
merged 40 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b88782f
wip
luizbafilho Mar 7, 2022
ea8d751
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 7, 2022
1fb68b2
Add cluster tool
luizbafilho Mar 8, 2022
317b3c7
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 8, 2022
9587695
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 9, 2022
cc4c268
Using clusters clients
luizbafilho Mar 9, 2022
6e7292b
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 9, 2022
c140f95
Fixing bad merge
luizbafilho Mar 9, 2022
8ff4b39
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 9, 2022
0c0a7ef
Adding configmap fetcher
luizbafilho Mar 11, 2022
84ac4b5
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 11, 2022
30a25ec
Initial impl of single cluster fetcher
luizbafilho Mar 11, 2022
4f1d000
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 11, 2022
1ecf371
Add typescript type checking to ui lint job
jpellizzari Mar 11, 2022
b4662b1
Fix linting errors
jpellizzari Mar 11, 2022
7756483
Add text filtering to FilterableTable
jpellizzari Mar 11, 2022
ca6f777
Set min page height to account for filtering UI
jpellizzari Mar 11, 2022
1de3093
Removing unecessary tool
luizbafilho Mar 14, 2022
870db1e
Renaming clusters fetcher interface
luizbafilho Mar 14, 2022
a902511
fixing static type check
luizbafilho Mar 14, 2022
e337a69
Adding some comments
luizbafilho Mar 14, 2022
4c51480
Deleting ConfigmapClustersFetcher
luizbafilho Mar 14, 2022
ecc7cb3
Adding test to singlefetcher
luizbafilho Mar 14, 2022
be4cd2e
Adding pretty print tool
luizbafilho Mar 14, 2022
a7fabee
Remove SearchInput component
jpellizzari Mar 14, 2022
9ae3422
Merge pull request #1701 from weaveworks/1619-text-filtering
jpellizzari Mar 14, 2022
d03aa04
interceptor for grpc server in testing
luizbafilho Mar 14, 2022
5aeed4d
Removing pretty print
luizbafilho Mar 14, 2022
89d6eb2
go mod tidy
luizbafilho Mar 14, 2022
8378df0
Merge branch 'v2' of github.com:weaveworks/weave-gitops into lf/k8s-c…
luizbafilho Mar 14, 2022
3d281b6
Renaming package
luizbafilho Mar 16, 2022
cf685b7
Renaming remaning packages
luizbafilho Mar 16, 2022
6512b46
Removing quick fix
luizbafilho Mar 16, 2022
9ba4b23
Merge branch 'main' of github.com:weaveworks/weave-gitops into lf/k8s…
luizbafilho Mar 16, 2022
6b12911
Using rest config from core server config
luizbafilho Mar 16, 2022
561137b
Update core/clustersmngr/middleware_test.go
luizbafilho Mar 16, 2022
984021b
tweak signature
luizbafilho Mar 16, 2022
31d5976
Merge branch 'main' of github.com:weaveworks/weave-gitops into lf/k8s…
luizbafilho Mar 16, 2022
4fe9f23
Trigger build
luizbafilho Mar 16, 2022
10a3d64
Making ui linter happy
luizbafilho Mar 16, 2022
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
92 changes: 92 additions & 0 deletions core/clustersmngr/clustersmngr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package clustersmngr

import (
"context"
"fmt"

"github.com/weaveworks/weave-gitops/pkg/kube"
"github.com/weaveworks/weave-gitops/pkg/server/auth"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
)

//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate

type key int

const (
// Clusters Clients Pool context key
ClustersClientsPoolCtxKey key = iota
)

var (
scheme = kube.CreateScheme()
)

// Cluster defines a leaf cluster
type Cluster struct {
// Name defines the cluster name
Name string `yaml:"name"`
// Server defines cluster api address
Server string `yaml:"server"`

// SecretRef defines secret name that holds the cluster Bearer Token
SecretRef string `yaml:"secretRef"`
// BearerToken cluster access token read from SecretRef
BearerToken string

// TLSConfig holds configuration for TLS connection with the cluster values read from SecretRef
TLSConfig rest.TLSClientConfig
}

//ClusterFetcher fetches all leaf clusters
//counterfeiter:generate . ClusterFetcher
type ClusterFetcher interface {
Fetch(ctx context.Context) ([]Cluster, error)
}

// ClientsPool stores all clients to the leaf clusters
type ClientsPool interface {
Add(user *auth.UserPrincipal, cluster Cluster) error
Clients() map[string]client.Client
}

type clientsPool struct {
clients map[string]client.Client
}

// NewClustersClientsPool initializes a new ClientsPool
func NewClustersClientsPool() ClientsPool {
return &clientsPool{
clients: map[string]client.Client{},
}
}

// Add adds a cluster client to the clients pool with the given user impersonation
func (cp *clientsPool) Add(user *auth.UserPrincipal, cluster Cluster) error {
config := &rest.Config{
Host: cluster.Server,
BearerToken: cluster.BearerToken,
TLSClientConfig: cluster.TLSConfig,
Impersonate: rest.ImpersonationConfig{
UserName: user.ID,
Groups: user.Groups,
},
}

leafClient, err := client.New(config, client.Options{
Scheme: scheme,
})
if err != nil {
return fmt.Errorf("failed to create leaf client: %w", err)
}

cp.clients[cluster.Name] = leafClient

return nil
}

// Clients returns the clusters clients
func (cp *clientsPool) Clients() map[string]client.Client {
return cp.clients
}
117 changes: 117 additions & 0 deletions core/clustersmngr/clustersmngrfakes/fake_cluster_fetcher.go

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

50 changes: 50 additions & 0 deletions core/clustersmngr/middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package clustersmngr

import (
"context"
"fmt"
"net/http"

"github.com/weaveworks/weave-gitops/pkg/server/auth"
)

// WithClustersClients creates clusters client for provided user in the context
func WithClustersClients(clustersFetcher ClusterFetcher, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := auth.Principal(r.Context())
if user == nil {
next.ServeHTTP(w, r)
return
}

clusters, err := clustersFetcher.Fetch(r.Context())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are punting the caching of the available clusters to another PR right?

tho i suppose the implementor of the interface can cache on that side. actually that is the better idea, nvm!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, not handling any of that now.

if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintln(w, "failed fetching clusters list: %w", err)
return
}

clientsPool := NewClustersClientsPool()
for _, c := range clusters {
if err := clientsPool.Add(user, c); err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "failed adding cluster client to the pool: %s", err)
return
}
}

ctx := context.WithValue(r.Context(), ClustersClientsPoolCtxKey, clientsPool)

next.ServeHTTP(w, r.WithContext(ctx))
})
}

// ClientsPoolFromCtx returns the ClusterClients pool stored in the context
func ClientsPoolFromCtx(ctx context.Context) ClientsPool {
pool, ok := ctx.Value(ClustersClientsPoolCtxKey).(*clientsPool)
if ok {
return pool
}

return nil
}
66 changes: 66 additions & 0 deletions core/clustersmngr/middleware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package clustersmngr_test

import (
"errors"
"net/http"
"net/http/httptest"
"testing"

. "github.com/onsi/gomega"

"github.com/weaveworks/weave-gitops/core/clustersmngr"
"github.com/weaveworks/weave-gitops/core/clustersmngr/clustersmngrfakes"
"github.com/weaveworks/weave-gitops/pkg/server/auth"
)

func TestWithClustersClientsMiddleware(t *testing.T) {
cluster := makeLeafCluster(t)
clustersFetcher := &clustersmngrfakes.FakeClusterFetcher{}
clustersFetcher.FetchReturns([]clustersmngr.Cluster{cluster}, nil)

g := NewGomegaWithT(t)

defaultHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientsPool := clustersmngr.ClientsPoolFromCtx(r.Context())

g.Expect(clientsPool.Clients()).To(HaveKey(cluster.Name))

next.ServeHTTP(w, r)
})
}(defaultHandler)

middleware = clustersmngr.WithClustersClients(clustersFetcher, middleware)
middleware = authMiddleware(middleware)

req := httptest.NewRequest(http.MethodGet, "http://www.foo.com/", nil)
res := httptest.NewRecorder()
middleware.ServeHTTP(res, req)

g.Expect(res).To(HaveHTTPStatus(http.StatusOK))
}

func TestWithClustersClientsMiddlewareFailsToFetchCluster(t *testing.T) {
defaultHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})

clustersFetcher := &clustersmngrfakes.FakeClusterFetcher{}
clustersFetcher.FetchReturns(nil, errors.New("error"))

middleware := clustersmngr.WithClustersClients(clustersFetcher, defaultHandler)
middleware = authMiddleware(middleware)

req := httptest.NewRequest(http.MethodGet, "http://www.foo.com/", nil)
res := httptest.NewRecorder()
middleware.ServeHTTP(res, req)

g := NewGomegaWithT(t)

g.Expect(res).To(HaveHTTPStatus(http.StatusInternalServerError))
}

func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r.WithContext(auth.WithPrincipal(r.Context(), &auth.UserPrincipal{ID: "user@weave.gitops", Groups: []string{"developers"}})))
})
}
28 changes: 28 additions & 0 deletions core/clustersmngr/single_fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package clustersmngr

import (
"context"

"k8s.io/client-go/rest"
)

type singleClusterFetcher struct {
restConfig *rest.Config
}

func NewSingleClusterFetcher(config *rest.Config) (ClusterFetcher, error) {
return singleClusterFetcher{
restConfig: config,
}, nil
}

func (cf singleClusterFetcher) Fetch(ctx context.Context) ([]Cluster, error) {
return []Cluster{
{
Name: "Default",
Server: cf.restConfig.Host,
BearerToken: cf.restConfig.BearerToken,
TLSConfig: cf.restConfig.TLSClientConfig,
},
}, nil
}
29 changes: 29 additions & 0 deletions core/clustersmngr/single_fetcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package clustersmngr_test

import (
"context"
"testing"

. "github.com/onsi/gomega"
"github.com/weaveworks/weave-gitops/core/clustersmngr"
"k8s.io/client-go/rest"
)

func TestSingleFetcher(t *testing.T) {
config := &rest.Config{
Host: "my-host",
BearerToken: "my-token",
}

g := NewGomegaWithT(t)

fetcher, err := clustersmngr.NewSingleClusterFetcher(config)
g.Expect(err).To(BeNil())

clusters, err := fetcher.Fetch(context.TODO())
g.Expect(err).To(BeNil())

g.Expect(clusters[0].Name).To(Equal("Default"))
g.Expect(clusters[0].Server).To(Equal(config.Host))
g.Expect(clusters[0].BearerToken).To(Equal(config.BearerToken))
}
Loading