Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension: fix pointer error #1323

Merged
merged 1 commit into from
Apr 20, 2023
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
31 changes: 22 additions & 9 deletions internal/extension/testutils/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
listenerV3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
routeV3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
tlsV3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
Expand All @@ -24,16 +25,20 @@ type XDSHookClient struct{}

// PostRouteModifyHook returns a modified version of the route using context info and the passed in extensionResources
func (c *XDSHookClient) PostRouteModifyHook(route *routeV3.Route, routeHostnames []string, extensionResources []*unstructured.Unstructured) (*routeV3.Route, error) {
// Simulate an error an extension may return
if route.Name == "extension-post-xdsroute-hook-error" {
return nil, errors.New("route hook resource error")
}

// Setup a new route to avoid operating directly on the passed in pointer for better test coverage that the
// route we are returning gets used properly
modifiedRoute := proto.Clone(route).(*routeV3.Route)
for _, extensionResource := range extensionResources {
// Simulate an error an extension may return
if route.Name == "extension-post-xdsroute-hook-error" {
return nil, errors.New("route hook resource error")
}
route.ResponseHeadersToAdd = append(route.ResponseHeadersToAdd,
modifiedRoute.ResponseHeadersToAdd = append(modifiedRoute.ResponseHeadersToAdd,
&coreV3.HeaderValueOption{
Header: &coreV3.HeaderValue{
Key: "mock-extension-was-here-route-name",
Value: route.Name,
Value: modifiedRoute.Name,
},
},
&coreV3.HeaderValueOption{
Expand Down Expand Up @@ -68,7 +73,7 @@ func (c *XDSHookClient) PostRouteModifyHook(route *routeV3.Route, routeHostnames
},
)
}
return route, nil
return modifiedRoute, nil
}

// PostVirtualHostModifyHook returns a modified version of the virtualhost with a new route injected
Expand All @@ -78,14 +83,18 @@ func (c *XDSHookClient) PostVirtualHostModifyHook(vh *routeV3.VirtualHost) (*rou
if vh.Name == "extension-post-xdsvirtualhost-hook-error" {
return nil, fmt.Errorf("extension post xds virtual host hook error")
} else if vh.Name == "extension-listener" {
vh.Routes = append(vh.Routes, &routeV3.Route{
// Setup a new VirtualHost to avoid operating directly on the passed in pointer for better test coverage that the
// VirtualHost we are returning gets used properly
modifiedVH := proto.Clone(vh).(*routeV3.VirtualHost)
modifiedVH.Routes = append(modifiedVH.Routes, &routeV3.Route{
Name: "mock-extension-inserted-route",
Action: &routeV3.Route_DirectResponse{
DirectResponse: &routeV3.DirectResponseAction{
Status: uint32(200),
},
},
})
return modifiedVH, nil
}
return vh, nil
}
Expand All @@ -100,7 +109,11 @@ func (c *XDSHookClient) PostHTTPListenerModifyHook(l *listenerV3.Listener) (*lis
if l.Name == "extension-post-xdslistener-hook-error" {
return nil, fmt.Errorf("extension post xds listener hook error")
} else if l.Name == "extension-listener" {
l.StatPrefix = "mock-extension-inserted-prefix"
// Setup a new Listener to avoid operating directly on the passed in pointer for better test coverage that the
// Listener we are returning gets used properly
modifiedListener := proto.Clone(l).(*listenerV3.Listener)
modifiedListener.StatPrefix = "mock-extension-inserted-prefix"
return modifiedListener, nil
}
return l, nil
}
Expand Down
42 changes: 33 additions & 9 deletions internal/xds/translator/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
package translator

import (
"errors"
"fmt"
"reflect"

clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
Expand All @@ -17,11 +21,10 @@ import (
resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/xds/types"

"github.com/envoyproxy/gateway/api/config/v1alpha1"
extensionTypes "github.com/envoyproxy/gateway/internal/extension/types"
"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/xds/types"
)

func processExtensionPostRouteHook(route *routev3.Route, vHost *routev3.VirtualHost, irRoute *ir.HTTPRoute, em *extensionTypes.Manager) error {
Expand Down Expand Up @@ -53,9 +56,9 @@ func processExtensionPostRouteHook(route *routev3.Route, vHost *routev3.VirtualH

// If the extension returned a modified Route, then copy its to the one that was passed in as a reference
if modifiedRoute != nil {
// Overwrite the pointer for the original route.
// Uses nolint because of the ineffectual assignment check
route = modifiedRoute //nolint
Copy link
Member

@Xunzhuo Xunzhuo Apr 18, 2023

Choose a reason for hiding this comment

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

why not just ?

*route = *modifiedRoute

And

*vHost = *modifiedVH

Copy link
Member Author

Choose a reason for hiding this comment

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

This is what I initially wanted to do, but unfortunately it causes a lint error because all the xDS resources have mutexes in the message state fields 😅

copylocks: assignment copies lock value to *route: github.com/envoyproxy/go-control-plane/envoy/config/route/v3.Route contains google.golang.org/protobuf/internal/impl.MessageState contains sync.Mutex

if err = deepCopyPtr(modifiedRoute, route); err != nil {
return err
}
}
return nil
}
Expand All @@ -81,9 +84,9 @@ func processExtensionPostVHostHook(vHost *routev3.VirtualHost, em *extensionType

// If the extension returned a modified Virtual Host, then copy its to the one that was passed in as a reference
if modifiedVH != nil {
// Overwrite the pointer for the original virtual host.
// Uses nolint because of the ineffectual assignment check
vHost = modifiedVH //nolint
if err = deepCopyPtr(modifiedVH, vHost); err != nil {
return err
}
}

return nil
Expand Down Expand Up @@ -168,3 +171,24 @@ func processExtensionPostTranslationHook(tCtx *types.ResourceVersionTable, em *e

return nil
}

func deepCopyPtr(src interface{}, dest interface{}) error {
if src == nil || dest == nil {
return errors.New("cannot deep copy nil pointer")
}
srcVal := reflect.ValueOf(src)
destVal := reflect.ValueOf(src)
if srcVal.Kind() == reflect.Ptr && destVal.Kind() == reflect.Ptr {
srcElem := srcVal.Elem()
destVal = reflect.New(srcElem.Type())
destElem := destVal.Elem()
destElem.Set(srcElem)
reflect.ValueOf(dest).Elem().Set(destVal.Elem())
} else {
return fmt.Errorf("cannot deep copy pointers to different kinds src %v != dest %v",
srcVal.Kind(),
destVal.Kind(),
)
}
return nil
}