Skip to content

Commit

Permalink
Plugin Support
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Jogeleit <frank.jogeleit@lovoo.com>
  • Loading branch information
Frank Jogeleit committed Jan 11, 2024
1 parent 1f6866c commit 91f2b48
Show file tree
Hide file tree
Showing 41 changed files with 1,205 additions and 429 deletions.
2 changes: 1 addition & 1 deletion backend/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func newRunCMD() *cobra.Command {
cmd.Flags().IntVar(&c.Server.Port, "port", 8080, "PolicyReporter UI port")
cmd.Flags().BoolVar(&c.Server.CORS, "dev", false, "Enable CORS Header for development")
cmd.Flags().BoolVar(&c.UI.Disabled, "no-ui", false, "Disable the embedded frontend")
cmd.Flags().BoolVar(&c.Cluster, "cluster", false, "use kube config to connect to cluster")
cmd.Flags().BoolVar(&c.Local, "local", false, "use kube config to connect to cluster")
flag.Parse()

return cmd
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package utils
package api

import (
"crypto/x509"
Expand Down
68 changes: 68 additions & 0 deletions backend/pkg/api/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package api

import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/url"
)

type Client struct {
baseURL string
http *http.Client
auth *BasicAuth
}

func (c *Client) Post(ctx context.Context, path string, payload any) (*http.Response, error) {
body := new(bytes.Buffer)

if err := json.NewEncoder(body).Encode(payload); err != nil {
return nil, err
}

req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+path, body)
if err != nil {
return nil, err
}

if c.auth != nil {
req.SetBasicAuth(c.auth.Username, c.auth.Password)
}

req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("User-Agent", "Policy Reporter UI")

return c.http.Do(req)
}

func (c *Client) Get(ctx context.Context, path string, query url.Values) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+path, nil)
if err != nil {
return nil, err
}

if c.auth != nil {
req.SetBasicAuth(c.auth.Username, c.auth.Password)
}

req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("User-Agent", "Policy Reporter UI")
req.URL.RawQuery = query.Encode()

return c.http.Do(req)
}

func New(options []ClientOption) (*Client, error) {
client := &Client{
http: NewHTTPClient(),
}

for _, o := range options {
if err := o(client); err != nil {
return nil, err
}
}

return client, nil
}
151 changes: 151 additions & 0 deletions backend/pkg/api/core/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package core

import (
"context"
"fmt"
"net/url"

"github.com/kyverno/policy-reporter-ui/pkg/api"
)

type Client struct {
*api.Client
}

func (c *Client) GetResource(ctx context.Context, id string) (*Resource, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/resource/%s", id), url.Values{})
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.Decode[Resource](resp.Body)
}

func (c *Client) GetResourceStatusCounts(ctx context.Context, id string, query url.Values) ([]ResourceStatusCount, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/resource/%s/status-counts", id), query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[ResourceStatusCount](resp.Body)
}

func (c *Client) ListSourceCategoryTree(ctx context.Context, query url.Values) ([]SourceCategoryTree, error) {
resp, err := c.Get(ctx, "/v2/sources/categories", query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[SourceCategoryTree](resp.Body)
}

func (c *Client) ListResourceCategories(ctx context.Context, id string, query url.Values) ([]SourceCategoryTree, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/resource/%s/source-categories", id), query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[SourceCategoryTree](resp.Body)
}

func (c *Client) GetFindings(ctx context.Context, query url.Values) (*Findings, error) {
resp, err := c.Get(ctx, "/v2/findings", query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.Decode[Findings](resp.Body)
}

func (c *Client) GetNamespaceStatusCounts(ctx context.Context, source string, query url.Values) (NamespaceStatusCounts, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/namespace-scoped/%s/status-counts", source), query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeMap[string, map[string]int](resp.Body)
}

func (c *Client) GetClusterStatusCounts(ctx context.Context, source string, query url.Values) (map[string]int, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/cluster-scoped/%s/status-counts", source), query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeMap[string, int](resp.Body)
}

func (c *Client) ListSources(ctx context.Context, query url.Values) ([]string, error) {
resp, err := c.Get(ctx, "/v2/sources", query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[string](resp.Body)
}

func (c *Client) UseResources(ctx context.Context, source string, query url.Values) (bool, error) {
resp, err := c.Get(ctx, fmt.Sprintf("/v2/sources/%s/use-resources", source), query)
if err != nil {
return false, err
}
defer resp.Body.Close()

result, err := api.DecodeMap[string, bool](resp.Body)
if err != nil {
return false, err
}

return result["resources"], nil
}

func (c *Client) ListNamespaces(ctx context.Context, query url.Values) ([]string, error) {
resp, err := c.Get(ctx, "/v1/namespaces", query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[string](resp.Body)
}

func (c *Client) ListPolicies(ctx context.Context, query url.Values) ([]Policy, error) {
resp, err := c.Get(ctx, "/v2/policies", query)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[Policy](resp.Body)
}

func (c *Client) ResolveNamespaceSelector(ctx context.Context, selector map[string]string) ([]string, error) {
resp, err := c.Post(ctx, "/v2/namespaces/resolve-selector", selector)
if err != nil {
return nil, err
}
defer resp.Body.Close()

return api.DecodeList[string](resp.Body)
}

func New(options []api.ClientOption) (*Client, error) {
baseClient, err := api.New(options)
if err != nil {
return nil, err
}

client := &Client{
Client: baseClient,
}

return client, nil
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package client
package core

type Category struct {
Name string `json:"name"`
Expand All @@ -9,6 +9,14 @@ type Category struct {
Fail int `json:"fail"`
}

type Policy struct {
Source string `json:"source,omitempty"`
Category string `json:"category,omitempty"`
Name string `json:"policy"`
Severity string `json:"severity,omitempty"`
Results map[string]int `json:"results"`
}

type Resource struct {
ID string `json:"id,omitempty"`
UID string `json:"uid,omitempty"`
Expand Down
27 changes: 27 additions & 0 deletions backend/pkg/api/decoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package api

import (
"encoding/json"
"io"
)

func DecodeList[T any](r io.Reader) ([]T, error) {
list := make([]T, 0)
err := json.NewDecoder(r).Decode(&list)

return list, err
}

func DecodeMap[R comparable, T any](r io.Reader) (map[R]T, error) {
mapping := make(map[R]T)
err := json.NewDecoder(r).Decode(&mapping)

return mapping, err
}

func Decode[T any](r io.Reader) (*T, error) {
model := new(T)
err := json.NewDecoder(r).Decode(model)

return model, err
}
25 changes: 25 additions & 0 deletions backend/pkg/api/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package api

import (
"crypto/tls"
"net"
"net/http"
"time"
)

func NewHTTPClient() *http.Client {
return &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 60 * time.Second,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{},
},
Timeout: 10 * time.Second,
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package client
package api

import (
"fmt"
Expand All @@ -8,7 +8,7 @@ import (
"go.uber.org/zap"
)

func newLoggingRoundTripper(roundTripper http.RoundTripper) http.RoundTripper {
func NewLoggingRoundTripper(roundTripper http.RoundTripper) http.RoundTripper {
return &logRoundTripper{roundTripper: roundTripper}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package client
package api

import (
"fmt"
"net/http"

"github.com/kyverno/policy-reporter-ui/pkg/core/utils"
)

type BasicAuth struct {
Username string
Password string
}

type ClientOption = func(*Client) error

func WithBaseURL(url string) ClientOption {
Expand All @@ -27,7 +30,7 @@ func WithBaseAuth(auth BasicAuth) ClientOption {

func WithCertificate(path string) ClientOption {
return func(client *Client) error {
certs, err := utils.LoadCerts(path)
certs, err := LoadCerts(path)
if err != nil {
return fmt.Errorf("with certificate failed: %w", err)
}
Expand All @@ -48,7 +51,7 @@ func WithSkipTLS() ClientOption {

func WithLogging() ClientOption {
return func(client *Client) error {
client.http.Transport = newLoggingRoundTripper(client.http.Transport)
client.http.Transport = NewLoggingRoundTripper(client.http.Transport)

return nil
}
Expand Down
Loading

0 comments on commit 91f2b48

Please sign in to comment.