Skip to content
This repository has been archived by the owner on Jul 7, 2022. It is now read-only.

Commit

Permalink
initial addition of policies
Browse files Browse the repository at this point in the history
  • Loading branch information
josephlewis42 committed Jan 10, 2019
1 parent 2fe0539 commit ba1167e
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 0 deletions.
93 changes: 93 additions & 0 deletions pkg/broker/policy/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2019 the Service Broker Project 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 policy

import (
"fmt"
"reflect"
)

// Conditions are a set of values that can be compared with a base truth and
// return true if all of the facets of the condition match.
//
// Blank facets are assumed to match everything.
type Condition struct {
ServiceId string `json:"service_id"`
ServiceName string `json:"service_name"`
}

// AppliesTo returns true if all the facets of this condition match the given
// truth.
func (cond *Condition) AppliesTo(truth Condition) bool {
if cond.ServiceId != "" && cond.ServiceId != truth.ServiceId {
return false
}

if cond.ServiceName != "" && cond.ServiceName != truth.ServiceName {
return false
}

return true
}

// Policy combines a condition with several sets of values that are set if
// the condition holds true.
type Policy struct {
Comment string `json:"//"`
Condition Condition `json:"if"`

Actions map[string]interface{} `json:"then"`
}

type PolicyList struct {
Policies []Policy `json:"policy"`
Assertions []Policy `json:"assert"`
}

// CheckAssertions tests each assertion in the Assertions list against the
// policies list. The conditoin is used as the ground truth and the
// actions are used as the expected output. If the actions don't match then
// an error is returned.
func (pl *PolicyList) CheckAssertions() error {
for i, assertion := range pl.Assertions {
expected := assertion.Actions
actual := pl.Apply(assertion.Condition)

if !reflect.DeepEqual(actual, expected) {
return fmt.Errorf("Error in assertion %d, comment: %q, expected: %v, actual: %v", i+1, assertion.Comment, expected, actual)
}
}

return nil
}

// Apply runs through the list of policies, first to last, and cascades the
// values of each if they match the given condition, returning the merged
// map at the end.
func (pl *PolicyList) Apply(groundTruth Condition) map[string]interface{} {
out := make(map[string]interface{})

for _, policy := range pl.Policies {
if !policy.Condition.AppliesTo(groundTruth) {
continue
}

for k, v := range policy.Actions {
out[k] = v
}
}

return out
}
80 changes: 80 additions & 0 deletions pkg/broker/policy/policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2019 the Service Broker Project 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 policy

import "testing"

func TestCondition_AppliesTo(t *testing.T) {
cases := map[string]struct {
Condition Condition
Truth Condition
Expected bool
}{
"matches everything": {
Condition: Condition{},
Truth: Condition{},
Expected: true,
},
}

for tn, tc := range cases {
t.Run(tn, func(t *testing.T) {
actual := tc.Condition.AppliesTo(tc.Truth)

if tc.Expected != actual {
t.Errorf("Expected condition to apply? %t but was: %t", tc.Expected, actual)
}
})
}
}

const examplePolicy = `
{
"policies":[
{
"//":"always applies",
"if":{},
"then":{
"cascade-true": true,
"cascade-false": true
}
},
{
"//":"some comment here",
"if":{
"service_name": "cloud-storage"
},
"then":{
"cascade-false": false
}
}
],
"assert":[
{
"//":"cascading works correctly",
"if":{
"service_name": "cloud-storage"
},
"then":{
"cascade-true": true,
"cascade-false": false
}
}
],
}
`

0 comments on commit ba1167e

Please sign in to comment.