Skip to content

Commit

Permalink
revisions to sdk api for traffic policy following our discussion
Browse files Browse the repository at this point in the history
  • Loading branch information
rkolavo committed Feb 5, 2024
1 parent d82702e commit af3c542
Show file tree
Hide file tree
Showing 8 changed files with 399 additions and 268 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 1.8.1
Enhancements:

- Provides access to structs for building a Traffic Policy configuration

Breaking changes:

- Renames pre-release option `WithPolicyConfig` to `WithPolicyString`
- Changes signature of `WithPolicy` option to accept the newly exposed structs for building a Traffic Policy configuration

## 1.8.0
- Adds the `WithPolicy` and `WithPolicyConfig` options for applying a Traffic Policy to an endpoint.

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.8.0
1.8.1
196 changes: 20 additions & 176 deletions config/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,22 @@ package config

import (
"encoding/json"
"fmt"

"golang.ngrok.com/ngrok/internal/pb"
"golang.ngrok.com/ngrok/trafficpolicy"
)

type policy struct {
Inbound inboundRules `json:"inbound,omitempty"`
Outbound outboundRules `json:"outbound,omitempty"`
}

type inboundRules []policyRule
type outboundRules []policyRule

type policyRule struct {
Name string `json:"name,omitempty"`
Expressions []string `json:"expressions,omitempty"`
Actions []action `json:"actions"`
}
type action struct {
Type string `json:"type"`
Config json.RawMessage `json:"config,omitempty"`
}

type PolicyActionOption = option[*action]
type PolicyRuleOption = option[*policyRule]
type PolicyRuleSetOption = option[*[]policyRule]
type PolicyOption = option[*policy]

// Supports conversion to a json string
type JsonConvertible interface {
ToJSON() string
}

func (p *policy) ToJSON() string {
return toJSON(p)
}

func (p policyRule) ToJSON() string {
return toJSON(p)
}

func (p action) ToJSON() string {
return toJSON(p)
}

func toJSON(o any) string {
bytes, err := json.Marshal(o)
type policy trafficpolicy.Policy
type rule trafficpolicy.Rule
type action trafficpolicy.Action

if err != nil {
panic(fmt.Sprintf("failed to convert to json with error: %s", err.Error()))
}

return string(bytes)
}

// WithPolicyConfig configures this edge with the provided policy configuration
// passed as a json string and overwrites any prevously-set traffic policy
// WithPolicyString configures this edge with the provided policy configuration
// passed as a json string and overwrites any previously-set traffic policy
// https://ngrok.com/docs/http/traffic-policy
func WithPolicyConfig(jsonStr string) interface {
func WithPolicyString(jsonStr string) interface {
HTTPEndpointOption
TLSEndpointOption
TCPEndpointOption
JsonConvertible
} {
p := &policy{}
if err := json.Unmarshal([]byte(jsonStr), p); err != nil {
Expand All @@ -74,130 +27,17 @@ func WithPolicyConfig(jsonStr string) interface {
return p
}

// WithPolicy configures this edge with the given traffic and overwrites any
// WithPolicy configures this edge with the given traffic policy and overwrites any
// previously-set traffic policy
// https://ngrok.com/docs/http/traffic-policy/
func WithPolicy(opts ...PolicyOption) interface {
func WithPolicy(tp trafficpolicy.Policy) interface {
HTTPEndpointOption
TLSEndpointOption
TCPEndpointOption
JsonConvertible
} {
cfg := &policy{}
applyOpts(cfg, opts...)

return cfg
}

// WithInboundRules adds the provided policy rules to the inbound
// set of the given policy.
// The order in which policies are specified is observed.
func WithInboundRules(opts ...PolicyRuleSetOption) PolicyOption {
rules := []policyRule{}
applyOpts(&rules, opts...)

return inboundRules(rules)
}

// WithOutboundRules adds the provided policy to be outbound
// set of the given policy.
// The order in which policies are specified is observed.
func WithOutboundRules(opts ...PolicyRuleSetOption) PolicyOption {
rules := []policyRule{}
applyOpts(&rules, opts...)

return outboundRules(rules)
}

// WithPolicyRule provides a policy rule built from the given options.
func WithPolicyRule(opts ...PolicyRuleOption) interface {
PolicyRuleSetOption
JsonConvertible
} {
pr := policyRule{}
applyOpts(&pr, opts...)
p := policy(tp)

return pr
}

// WithPolicyName sets the provided name on a policy rule.
func WithPolicyName(n string) PolicyRuleOption {
return optionFunc[*policyRule](
func(r *policyRule) {
r.Name = n
})
}

// WithPolicyExpression appends the provided CEL statement to a policy rule's expressions.
func WithPolicyExpression(e string) PolicyRuleOption {
return optionFunc[*policyRule](
func(r *policyRule) {
r.Expressions = append(r.Expressions, e)
})
}

// WithPolicyAction appends the provided action to the set of the policy rule.
// The order the actions are specified is observed.
func WithPolicyAction(opts ...PolicyActionOption) interface {
PolicyRuleOption
JsonConvertible
} {
a := action{}
applyOpts(&a, opts...)

return a
}

// WithActionType sets the provided type for this action. Type must be specified.
func WithPolicyActionType(t string) PolicyActionOption {
return optionFunc[*action](func(a *action) { a.Type = t })
}

// WithConfig sets the provided json string as the configuration for this action
func WithPolicyActionConfig(cfg string) PolicyActionOption {
return optionFunc[*action](
func(a *action) {
a.Config = []byte(cfg)
})
}

// supports inbound rules as an a policy option
func (ib inboundRules) apply(p *policy) {
p.Inbound = append(p.Inbound, ib...)
}

// supports outbound rules as a policy option
func (ib outboundRules) apply(p *policy) {
p.Outbound = append(p.Outbound, ib...)
}

// supports policy rule as an option of a collection of
// rules, which can be used for inbound or outbound
func (pr policyRule) apply(r *[]policyRule) {
*r = append(*r, pr)
}

// supports action as an option of a policy rule
func (a action) apply(p *policyRule) {
p.Actions = append(p.Actions, a)
}

// an option that is applicable to the specified type
type option[T any] interface {
apply(T)
}

type optionFunc[T any] func(T)

func (f optionFunc[T]) apply(a T) {
f(a)
}

// applies the set of options to the specified target
func applyOpts[T any](target T, opts ...option[T]) {
for _, o := range opts {
o.apply(target)
}
return &p
}

func (p *policy) ApplyTLS(opts *tlsOptions) {
Expand All @@ -218,31 +58,35 @@ func (p *policy) toProtoConfig() *pb.MiddlewareConfiguration_Policy {
}
inbound := make([]*pb.MiddlewareConfiguration_PolicyRule, len(p.Inbound))
for i, inP := range p.Inbound {
inbound[i] = inP.toProtoConfig()
inbound[i] = rule(inP).toProtoConfig()
}

outbound := make([]*pb.MiddlewareConfiguration_PolicyRule, len(p.Outbound))
for i, outP := range p.Outbound {
outbound[i] = outP.toProtoConfig()
outbound[i] = rule(outP).toProtoConfig()
}
return &pb.MiddlewareConfiguration_Policy{
Inbound: inbound,
Outbound: outbound,
}
}

func (pr policyRule) toProtoConfig() *pb.MiddlewareConfiguration_PolicyRule {
func (pr rule) toProtoConfig() *pb.MiddlewareConfiguration_PolicyRule {
actions := make([]*pb.MiddlewareConfiguration_PolicyAction, len(pr.Actions))
for i, act := range pr.Actions {
actions[i] = act.toProtoConfig()
actions[i] = action(act).toProtoConfig()
}

return &pb.MiddlewareConfiguration_PolicyRule{Name: pr.Name, Expressions: pr.Expressions, Actions: actions}
}

func (a action) toProtoConfig() *pb.MiddlewareConfiguration_PolicyAction {
var cfgBytes []byte = nil
if a.Config != "" {
cfgBytes = []byte(a.Config)
}
return &pb.MiddlewareConfiguration_PolicyAction{
Type: a.Type,
Config: []byte(a.Config),
Config: cfgBytes,
}
}
Loading

0 comments on commit af3c542

Please sign in to comment.