diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 52b837e6be..114dfdda45 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -2,6 +2,7 @@ package dclient import ( "context" + "errors" "fmt" "time" @@ -32,6 +33,7 @@ type Interface interface { Discovery() IDiscovery // SetDiscovery sets the discovery client implementation SetDiscovery(discoveryClient IDiscovery) + RawAbsPath(path string) ([]byte, error) // GetResource returns the resource in unstructured/json format GetResource(apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) // PatchResource patches the resource @@ -55,6 +57,7 @@ type client struct { discoveryClient IDiscovery clientConfig *rest.Config kclient kubernetes.Interface + restClient rest.Interface } // NewClient creates new instance of client @@ -67,6 +70,7 @@ func NewClient(config *rest.Config, kclient *kubernetes.Clientset, resync time.D client: dclient, clientConfig: config, kclient: kclient, + restClient: kclient.RESTClient(), } // Set discovery client discoveryClient := &serverPreferredResources{ @@ -129,6 +133,13 @@ func (c *client) GetResource(apiVersion string, kind string, namespace string, n return c.getResourceInterface(apiVersion, kind, namespace).Get(context.TODO(), name, metav1.GetOptions{}, subresources...) } +func (c *client) RawAbsPath(path string) ([]byte, error) { + if c.restClient == nil { + return nil, errors.New("rest client not supported") + } + return c.restClient.Get().AbsPath(path).DoRaw(context.TODO()) +} + // PatchResource patches the resource func (c *client) PatchResource(apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) { return c.getResourceInterface(apiVersion, kind, namespace).Patch(context.TODO(), name, types.JSONPatchType, patch, metav1.PatchOptions{}) diff --git a/pkg/engine/apiPath.go b/pkg/engine/apiPath.go index 8d6d889b5f..897dda6262 100644 --- a/pkg/engine/apiPath.go +++ b/pkg/engine/apiPath.go @@ -12,6 +12,7 @@ type APIPath struct { ResourceType string Name string Namespace string + Raw string } // NewAPIPath validates and parses an API path. @@ -20,6 +21,12 @@ func NewAPIPath(path string) (*APIPath, error) { trimmedPath := strings.Trim(path, "/ ") paths := strings.Split(trimmedPath, "/") + if paths[0] == "apis" && len(paths) > 7 { + return &APIPath{ + Raw: path, + }, nil + } + if len(paths) < 3 || len(paths) > 7 { return nil, fmt.Errorf("invalid path length %s", path) } diff --git a/pkg/engine/apiPath_test.go b/pkg/engine/apiPath_test.go index 8942c12b47..ae7a837132 100644 --- a/pkg/engine/apiPath_test.go +++ b/pkg/engine/apiPath_test.go @@ -4,6 +4,22 @@ import ( "testing" ) +func Test_Raw(t *testing.T) { + f := func(path string) { + p, err := NewAPIPath(path) + if err != nil { + t.Error(err) + return + } + + if p.Raw != path { + t.Errorf("expected raw path %s got %s", path, p.Raw) + } + } + + f("/apis/cluster.karmada.io/v1alpha1/clusters/member1/proxy/api/v1/namespaces/{{ request.namespace }}/pods") +} + func Test_Paths(t *testing.T) { f := func(path, expected string) { p, err := NewAPIPath(path) diff --git a/pkg/engine/jsonContext.go b/pkg/engine/jsonContext.go index a42417a481..9e8895caf5 100644 --- a/pkg/engine/jsonContext.go +++ b/pkg/engine/jsonContext.go @@ -313,6 +313,11 @@ func fetchAPIData(log logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyCont if err != nil { return nil, fmt.Errorf("failed to add resource with urlPath: %s: %v", p, err) } + } else if p.Raw != "" { + jsonData, err = getResource(ctx, p) + if err != nil { + return nil, fmt.Errorf("failed to get resource with raw url\n: %s: %v", p, err) + } } else { jsonData, err = loadResourceList(ctx, p) if err != nil { @@ -340,15 +345,17 @@ func loadResource(ctx *PolicyContext, p *APIPath) ([]byte, error) { if ctx.Client == nil { return nil, fmt.Errorf("API client is not available") } - r, err := ctx.Client.GetResource(p.Version, p.ResourceType, p.Namespace, p.Name) if err != nil { return nil, err } - return r.MarshalJSON() } +func getResource(ctx *PolicyContext, p *APIPath) ([]byte, error) { + return ctx.Client.RawAbsPath(p.Raw) +} + func loadConfigMap(logger logr.Logger, entry kyvernov1.ContextEntry, ctx *PolicyContext) error { data, err := fetchConfigMap(logger, entry, ctx) if err != nil {