Skip to content

Commit

Permalink
Refactor discovery implementation
Browse files Browse the repository at this point in the history
These changes refactor the discovery implementation a bit to improve
test coverage and remove duplication of common logic shared with the
bundle plugin.

Specifically, the downloading logic has been moved into a separate
package that is shared by bundle and discovery. Second, test coverage in
the discovery implementation is increased from ~15% to ~85%.

These changes also include a few functional improvements:

- The default decision paths can be updated dynamically
- The decision logger can be enabled dynamically
- Discovery downloading errors are reported in status updates
- Discovery bundle is evaluated with all runtime params
- Custom plugins can be created dynamically
- Status updates include both discovery and bundle status

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
  • Loading branch information
tsandall committed Dec 7, 2018
1 parent a73e3ba commit 2d42549
Show file tree
Hide file tree
Showing 30 changed files with 2,218 additions and 2,498 deletions.
9 changes: 4 additions & 5 deletions cmd/plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"reflect"
"strings"
"testing"

"os"

"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/runtime"
"github.com/open-policy-agent/opa/types"
Expand Down Expand Up @@ -156,7 +155,7 @@ func (t *Tester) Stop(ctx context.Context) {
return
}
func (t *Tester) Reconfigure(config interface{}) {
func (t *Tester) Reconfigure(ctx context.Context, config interface{}) {
return
}
Expand Down Expand Up @@ -230,7 +229,7 @@ func TestRegisterPlugin(t *testing.T) {

// make sure starting the manager kicks the plugin in
emptyInitChan()
if err := rt.Discovery.Start(context.Background()); err != nil {
if err := rt.Manager.Start(context.Background()); err != nil {
t.Fatalf("Unable to initialize plugins: %v", err.Error())
}

Expand All @@ -257,7 +256,7 @@ func TestPluginDoesNotStartWithoutConfig(t *testing.T) {

// make sure starting the manager kicks the plugin in
emptyInitChan()
if err := rt.Discovery.Start(context.Background()); err != nil {
if err := rt.Manager.Start(context.Background()); err != nil {
t.Fatalf("Unable to initialize plugins: %v", err.Error())
}
if len(initChan) != 0 {
Expand Down
96 changes: 96 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

// Package config implements OPA configuration file parsing and validation.
package config

import (
"encoding/json"
"strings"

"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/util"
)

// Config represents the configuration file that OPA can be started with.
type Config struct {
Services json.RawMessage `json:"services"`
Labels map[string]string `json:"labels"`
Discovery json.RawMessage `json:"discovery"`
Bundle json.RawMessage `json:"bundle"`
DecisionLogs json.RawMessage `json:"decision_logs"`
Status json.RawMessage `json:"status"`
Plugins map[string]json.RawMessage `json:"plugins"`
DefaultDecision *string `json:"default_decision"`
DefaultAuthorizationDecision *string `json:"default_authorization_decision"`
}

// ParseConfig returns a valid Config object with defaults injected. The id
// parameter will be set in the labels map.
func ParseConfig(raw []byte, id string) (*Config, error) {
var result Config
if err := util.Unmarshal(raw, &result); err != nil {
return nil, err
}
return &result, result.validateAndInjectDefaults(id)
}

// PluginsEnabled returns true if one or more plugin features are enabled.
func (c Config) PluginsEnabled() bool {
return c.Bundle != nil || c.DecisionLogs != nil || c.Status != nil || len(c.Plugins) > 0
}

// DefaultDecisionRef returns the default decision as a reference.
func (c Config) DefaultDecisionRef() ast.Ref {
ref, _ := parsePathToRef(*c.DefaultDecision)
return ref
}

// DefaultAuthorizationDecisionRef returns the default authorization decision
// as a reference.
func (c Config) DefaultAuthorizationDecisionRef() ast.Ref {
ref, _ := parsePathToRef(*c.DefaultAuthorizationDecision)
return ref
}

func (c *Config) validateAndInjectDefaults(id string) error {

if c.DefaultDecision == nil {
s := defaultDecisionPath
c.DefaultDecision = &s
}

_, err := parsePathToRef(*c.DefaultDecision)
if err != nil {
return err
}

if c.DefaultAuthorizationDecision == nil {
s := defaultAuthorizationDecisionPath
c.DefaultAuthorizationDecision = &s
}

_, err = parsePathToRef(*c.DefaultAuthorizationDecision)
if err != nil {
return err
}

if c.Labels == nil {
c.Labels = map[string]string{}
}

c.Labels["id"] = id

return nil
}

func parsePathToRef(s string) (ast.Ref, error) {
s = strings.Replace(strings.Trim(s, "/"), "/", ".", -1)
return ast.ParseRef("data." + s)
}

const (
defaultDecisionPath = "/system/main"
defaultAuthorizationDecisionPath = "/system/authz/allow"
)
Loading

0 comments on commit 2d42549

Please sign in to comment.