Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions sdk/core/policy/v1alpha2/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type UpstreamRequestHeaderModifications struct {
// These are valid at the header phase because routing decisions do not require
// the request body to be available.
UpstreamName *string // route to a named upstream definition (nil = no change)
UpstreamSlot *UpstreamSlot // route to the API's main or sandbox upstream slot instead of the slot implied by the current vhost (nil = no change); requesting a slot the API doesn't have configured is a no-op (logged, not an error)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is not required right?

Path *string // rewrite the request path (nil = no change)
Host *string // rewrite the :authority header (nil = no change)
Method *string // rewrite the request method (nil = no change)
Expand Down Expand Up @@ -113,6 +114,7 @@ type UpstreamRequestModifications struct {

// Routing mutations — applied before the request is forwarded to upstream.
UpstreamName *string // route to a named upstream definition (nil = no change)
UpstreamSlot *UpstreamSlot // route to the API's main or sandbox upstream slot instead of the slot implied by the current vhost (nil = no change); requesting a slot the API doesn't have configured is a no-op (logged, not an error)
Path *string // rewrite the request path (nil = no change)
Host *string // rewrite the :authority header (nil = no change)
Method *string // rewrite the request method (nil = no change)
Expand Down
9 changes: 9 additions & 0 deletions sdk/core/policy/v1alpha2/context.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package policyv1alpha2

import policyenginev1 "github.com/wso2/api-platform/sdk/core/policyengine"

// Body represents HTTP request or response body data
type Body struct {
// Content is the body payload (may be nil)
Expand Down Expand Up @@ -90,6 +92,13 @@ type RequestContext struct {
Authority string
Scheme string
Vhost string

// UpstreamInfo identifies the route's resolved upstream target (cluster name, URL,
// base path) for this request. Nil if no upstream has been resolved for the route.
// Authority/Scheme above reflect the inbound client-facing request and must not be
// used to address the actual upstream (e.g. for request signing) — use
// UpstreamInfo.URL instead.
UpstreamInfo *policyenginev1.UpstreamInfo
}

// ─── Response-phase contexts ─────────────────────────────────────────────────
Expand Down
8 changes: 8 additions & 0 deletions sdk/core/policy/v1alpha2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ const (
APIKindWebSubApi APIKind = "WebSubApi"
)

// UpstreamSlot identifies one of an API's built-in upstream slots.
type UpstreamSlot string

const (
UpstreamSlotMain UpstreamSlot = "main"
UpstreamSlotSandbox UpstreamSlot = "sandbox"
)

// ParameterType defines the type of a policy parameter
type ParameterType string

Expand Down
39 changes: 39 additions & 0 deletions sdk/core/policyengine/upstream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package policyengine

// UpstreamInfo identifies the resolved upstream target for one of an API's
// upstream slots (main or sandbox) or a named upstream definition: the Envoy
// cluster name (used for cluster_header dynamic routing) and the raw resolved
// URL. This is the shared wire shape used on both sides of the xDS route-config
// channel between gateway-controller and policy-engine.
type UpstreamInfo struct {
ClusterName string `json:"cluster_name" yaml:"cluster_name"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ClusterName is an internal thing. Why do we need to expose it?

URL string `json:"url" yaml:"url"`
BasePath string `json:"base_path" yaml:"base_path"`
}

// ToMap converts UpstreamInfo into a structpb-compatible map for embedding as a
// nested object in xDS route-config metadata (structpb.Struct only accepts
// string/bool/number/nil/slice/map values, not arbitrary structs).
func (u UpstreamInfo) ToMap() map[string]interface{} {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need this func?

return map[string]interface{}{
"cluster_name": u.ClusterName,
"url": u.URL,
"base_path": u.BasePath,
}
}

// UpstreamInfoFromMap decodes an UpstreamInfo from the generic map produced when
// the xDS metadata struct is unmarshalled back to JSON on the consumer side.
func UpstreamInfoFromMap(m map[string]interface{}) UpstreamInfo {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And this func?

get := func(k string) string {
if s, ok := m[k].(string); ok {
return s
}
return ""
}
return UpstreamInfo{
ClusterName: get("cluster_name"),
URL: get("url"),
BasePath: get("base_path"),
}
}
33 changes: 33 additions & 0 deletions sdk/core/policyengine/upstream_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package policyengine

import "testing"

func TestUpstreamInfoRoundTrip(t *testing.T) {
info := UpstreamInfo{
ClusterName: "cluster_https_backend_example_com",
URL: "https://backend.example.com",
BasePath: "/v1",
}

got := UpstreamInfoFromMap(info.ToMap())

if got != info {
t.Errorf("round trip mismatch: got %+v, want %+v", got, info)
}
}

func TestUpstreamInfoFromMapMissingFields(t *testing.T) {
got := UpstreamInfoFromMap(map[string]interface{}{"cluster_name": "c1"})
want := UpstreamInfo{ClusterName: "c1"}
if got != want {
t.Errorf("got %+v, want %+v", got, want)
}
}

func TestUpstreamInfoFromMapWrongType(t *testing.T) {
got := UpstreamInfoFromMap(map[string]interface{}{"cluster_name": 42})
want := UpstreamInfo{}
if got != want {
t.Errorf("got %+v, want %+v", got, want)
}
}
Loading