From 79b53a3deea6b75d836e82a10ce4a49fc1fe3275 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Wed, 16 Jul 2025 11:48:24 +1000 Subject: [PATCH 01/11] add PATCH request operation --- api/types/load_traffic.go | 34 +++++++++++++++ request/random.go | 91 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/api/types/load_traffic.go b/api/types/load_traffic.go index 009fdef9..fc13d981 100644 --- a/api/types/load_traffic.go +++ b/api/types/load_traffic.go @@ -87,6 +87,8 @@ type WeightedRequest struct { QuorumGet *RequestGet `json:"quorumGet,omitempty" yaml:"quorumGet,omitempty"` // Put means this is mutating request. Put *RequestPut `json:"put,omitempty" yaml:"put,omitempty"` + // Patch means this is mutating request to update resource. + Patch *RequestPatch `json:"patch,omitempty" yaml:"patch,omitempty"` // GetPodLog means this is to get log from target pod. GetPodLog *RequestGetPodLog `json:"getPodLog,omitempty" yaml:"getPodLog,omitempty"` } @@ -146,6 +148,19 @@ type RequestPut struct { ValueSize int `json:"valueSize" yaml:"valueSize"` } +// RequestPatch defines PATCH request for target resource type. +type RequestPatch struct { + KubeGroupVersionResource `yaml:",inline"` + // Namespace is object's namespace. + Namespace string `json:"namespace" yaml:"namespace"` + // Name is object's prefix name. + Name string `json:"name" yaml:"name"` + // PatchType is the type of patch, e.g. "json", "merge", "strategic-merge". + PatchType string `json:"patchType" yaml:"patchType"` + // Body is the request body, for fields to be changed. + Body string `json:"body" yaml:"body"` +} + // RequestGetPodLog defines GetLog request for target pod. type RequestGetPodLog struct { // Namespace is pod's namespace. @@ -221,6 +236,8 @@ func (r WeightedRequest) Validate() error { return r.QuorumGet.Validate() case r.Put != nil: return r.Put.Validate() + case r.Patch != nil: + return r.Patch.Validate() case r.GetPodLog != nil: return r.GetPodLog.Validate() default: @@ -284,12 +301,29 @@ func (r *RequestPut) Validate() error { // Validate validates RequestGetPodLog type. func (r *RequestGetPodLog) Validate() error { + if r.Namespace == "" { + return fmt.Errorf("namespace ias required") + } + if r.Name == "" { + return fmt.Errorf("name is required") + } + return nil +} + +// Validate validates RequestPost type. +func (r *RequestPatch) Validate() error { + if err := r.KubeGroupVersionResource.Validate(); err != nil { + return fmt.Errorf("kube metadata: %v", err) + } if r.Namespace == "" { return fmt.Errorf("namespace is required") } if r.Name == "" { return fmt.Errorf("name is required") } + if r.Body == "" { + return fmt.Errorf("body is required") + } return nil } diff --git a/request/random.go b/request/random.go index 1cd29e18..04e05e92 100644 --- a/request/random.go +++ b/request/random.go @@ -6,8 +6,10 @@ package request import ( "context" "crypto/rand" + "encoding/json" "fmt" "math/big" + "strings" "sync" "github.com/Azure/kperf/api/types" @@ -15,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + apitypes "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" ) @@ -56,6 +59,8 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ builder = newRequestGetBuilder(r.QuorumGet, "", spec.MaxRetries) case r.GetPodLog != nil: builder = newRequestGetPodLogBuilder(r.GetPodLog, spec.MaxRetries) + case r.Patch != nil: + builder = newRequestPatchBuilder(r.Patch, "", spec.MaxRetries) default: return nil, fmt.Errorf("not implement for PUT yet") } @@ -354,6 +359,92 @@ func (b *requestGetPodLogBuilder) Build(cli rest.Interface) Requester { } } +type requestPatchBuilder struct { + version schema.GroupVersion + resource string + resourceVersion string + namespace string + name string + patchType apitypes.PatchType + body interface{} + maxRetries int +} + +func getPatchType(patchType string) apitypes.PatchType { + switch patchType { + case "json": + // JSON Patch: exact field edits (RFC 6902), array of ops + // [{"op": "replace", "path": "/spec/replicas", "value": 3}] + return apitypes.JSONPatchType + case "merge": + // Merge Patch: partial object, simple merge (RFC 7386) + // - Input is a partial object (e.g., {"spec": {"replicas": 2}}) + return apitypes.MergePatchType + case "strategic": + // Strategic Merge: smart merge for native K8s types + // - Input is a partial object (like merge patch) + return apitypes.StrategicMergePatchType + default: + // Default to strategic merge patch if not specified or unknown. + return apitypes.StrategicMergePatchType + } +} + +func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) *requestPatchBuilder { + + var body interface{} + + // Check if Body field is specified + if src.Body != "" { + trimmed := strings.TrimSpace(src.Body) + if json.Valid([]byte(trimmed)) { + body = []byte(trimmed) // send raw JSON + } else { + body = trimmed // fallback to raw string + } + } else { + // Use the entire request as body data (current behavior for backward compatibility) + body = src + } + return &requestPatchBuilder{ + version: schema.GroupVersion{ + Group: src.Group, + Version: src.Version, + }, + resource: src.Resource, + resourceVersion: resourceVersion, + namespace: src.Namespace, + name: src.Name, + patchType: getPatchType(src.PatchType), + body: body, + maxRetries: maxRetries, + } +} + +// Build implements RequestBuilder.Build. +func (b *requestPatchBuilder) Build(cli rest.Interface) Requester { + // https://kubernetes.io/docs/reference/using-api/#api-groups + comps := make([]string, 0, 5) + if b.version.Group == "" { + comps = append(comps, "api", b.version.Version) + } else { + comps = append(comps, "apis", b.version.Group, b.version.Version) + } + if b.namespace != "" { + comps = append(comps, "namespaces", b.namespace) + } + comps = append(comps, b.resource, b.name) + + return &DiscardRequester{ + BaseRequester: BaseRequester{ + method: "PATCH", + req: cli.Patch(b.patchType).AbsPath(comps...). + Body(b.body). + MaxRetries(b.maxRetries), + }, + } +} + func toPtr[T any](v T) *T { return &v } From fb708ca383196064323272d3c244621b1d7edd6d Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Wed, 16 Jul 2025 11:59:41 +1000 Subject: [PATCH 02/11] fix comments --- api/types/load_traffic.go | 28 +++++++++---------- .../manifests/loadprofile/test_mutation.yaml | 27 ++++++++++++++++++ 2 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 contrib/internal/manifests/loadprofile/test_mutation.yaml diff --git a/api/types/load_traffic.go b/api/types/load_traffic.go index fc13d981..10a48618 100644 --- a/api/types/load_traffic.go +++ b/api/types/load_traffic.go @@ -302,7 +302,7 @@ func (r *RequestPut) Validate() error { // Validate validates RequestGetPodLog type. func (r *RequestGetPodLog) Validate() error { if r.Namespace == "" { - return fmt.Errorf("namespace ias required") + return fmt.Errorf("namespace is required") } if r.Name == "" { return fmt.Errorf("name is required") @@ -310,7 +310,19 @@ func (r *RequestGetPodLog) Validate() error { return nil } -// Validate validates RequestPost type. +// Validate validates KubeGroupVersionResource. +func (m *KubeGroupVersionResource) Validate() error { + if m.Version == "" { + return fmt.Errorf("version is required") + } + + if m.Resource == "" { + return fmt.Errorf("resource is required") + } + return nil +} + +// Validate validates RequestPatch type. func (r *RequestPatch) Validate() error { if err := r.KubeGroupVersionResource.Validate(); err != nil { return fmt.Errorf("kube metadata: %v", err) @@ -326,15 +338,3 @@ func (r *RequestPatch) Validate() error { } return nil } - -// Validate validates KubeGroupVersionResource. -func (m *KubeGroupVersionResource) Validate() error { - if m.Version == "" { - return fmt.Errorf("version is required") - } - - if m.Resource == "" { - return fmt.Errorf("resource is required") - } - return nil -} diff --git a/contrib/internal/manifests/loadprofile/test_mutation.yaml b/contrib/internal/manifests/loadprofile/test_mutation.yaml new file mode 100644 index 00000000..f20718cf --- /dev/null +++ b/contrib/internal/manifests/loadprofile/test_mutation.yaml @@ -0,0 +1,27 @@ +version: 1 +description: "test=mutation" +spec: + rate: 10 + total: 1 + conns: 10 + client: 10 + contentType: json + disableHTTP2: false + maxRetries: 0 + requests: + - patch: + version: v1 + resource: pods + namespace: kperf + name: mutate1 + patchType: merge + body: | + { + "metadata": { + "labels": { + "key": "value" + } + } + } + shares: 1000 + \ No newline at end of file From 7af3742a594859587d1a1c00055ec344b00ba754 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Wed, 16 Jul 2025 12:01:00 +1000 Subject: [PATCH 03/11] delete testing file --- .../manifests/loadprofile/test_mutation.yaml | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 contrib/internal/manifests/loadprofile/test_mutation.yaml diff --git a/contrib/internal/manifests/loadprofile/test_mutation.yaml b/contrib/internal/manifests/loadprofile/test_mutation.yaml deleted file mode 100644 index f20718cf..00000000 --- a/contrib/internal/manifests/loadprofile/test_mutation.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: 1 -description: "test=mutation" -spec: - rate: 10 - total: 1 - conns: 10 - client: 10 - contentType: json - disableHTTP2: false - maxRetries: 0 - requests: - - patch: - version: v1 - resource: pods - namespace: kperf - name: mutate1 - patchType: merge - body: | - { - "metadata": { - "labels": { - "key": "value" - } - } - } - shares: 1000 - \ No newline at end of file From 6bc293a8ad935be8183f224b72068070382949f6 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Wed, 16 Jul 2025 12:24:41 +1000 Subject: [PATCH 04/11] clean up --- request/random.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/request/random.go b/request/random.go index 04e05e92..5fec0d50 100644 --- a/request/random.go +++ b/request/random.go @@ -394,18 +394,13 @@ func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, max var body interface{} - // Check if Body field is specified - if src.Body != "" { - trimmed := strings.TrimSpace(src.Body) - if json.Valid([]byte(trimmed)) { - body = []byte(trimmed) // send raw JSON - } else { - body = trimmed // fallback to raw string - } + trimmed := strings.TrimSpace(src.Body) + if json.Valid([]byte(trimmed)) { + body = []byte(trimmed) // send raw JSON } else { - // Use the entire request as body data (current behavior for backward compatibility) - body = src + body = trimmed // fallback to raw string } + return &requestPatchBuilder{ version: schema.GroupVersion{ Group: src.Group, From 2ca1048cab63478eeae22f9cd476d419f2e44067 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Fri, 18 Jul 2025 13:55:09 +1000 Subject: [PATCH 05/11] add error for invalid patch types --- request/random.go | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/request/random.go b/request/random.go index 5fec0d50..a36a126c 100644 --- a/request/random.go +++ b/request/random.go @@ -46,6 +46,7 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ shares = append(shares, r.Shares) var builder RESTRequestBuilder + var err error switch { case r.StaleList != nil: builder = newRequestListBuilder(r.StaleList, "0", spec.MaxRetries) @@ -60,7 +61,10 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ case r.GetPodLog != nil: builder = newRequestGetPodLogBuilder(r.GetPodLog, spec.MaxRetries) case r.Patch != nil: - builder = newRequestPatchBuilder(r.Patch, "", spec.MaxRetries) + builder, err = newRequestPatchBuilder(r.Patch, "", spec.MaxRetries) + if err != nil { + return nil, fmt.Errorf("failed to create patch request builder: %v", err) + } default: return nil, fmt.Errorf("not implement for PUT yet") } @@ -370,35 +374,25 @@ type requestPatchBuilder struct { maxRetries int } -func getPatchType(patchType string) apitypes.PatchType { - switch patchType { - case "json": - // JSON Patch: exact field edits (RFC 6902), array of ops - // [{"op": "replace", "path": "/spec/replicas", "value": 3}] - return apitypes.JSONPatchType - case "merge": - // Merge Patch: partial object, simple merge (RFC 7386) - // - Input is a partial object (e.g., {"spec": {"replicas": 2}}) - return apitypes.MergePatchType - case "strategic": - // Strategic Merge: smart merge for native K8s types - // - Input is a partial object (like merge patch) - return apitypes.StrategicMergePatchType - default: - // Default to strategic merge patch if not specified or unknown. - return apitypes.StrategicMergePatchType - } +var patchTypes = map[string]apitypes.PatchType{ + "json": apitypes.JSONPatchType, + "merge": apitypes.MergePatchType, + "strategic": apitypes.StrategicMergePatchType, } -func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) *requestPatchBuilder { +func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) (*requestPatchBuilder, error) { var body interface{} trimmed := strings.TrimSpace(src.Body) - if json.Valid([]byte(trimmed)) { - body = []byte(trimmed) // send raw JSON - } else { - body = trimmed // fallback to raw string + if !json.Valid([]byte(trimmed)) { + return nil, fmt.Errorf("invalid JSON in patch body: %q", src.Body) + } + body = []byte(trimmed) + + patchType, ok := patchTypes[src.PatchType] + if !ok { + return nil, fmt.Errorf("unknown patch type: %s", src.PatchType) } return &requestPatchBuilder{ @@ -410,10 +404,10 @@ func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, max resourceVersion: resourceVersion, namespace: src.Namespace, name: src.Name, - patchType: getPatchType(src.PatchType), + patchType: patchType, body: body, maxRetries: maxRetries, - } + }, nil } // Build implements RequestBuilder.Build. From dfd45b07d6f4c33d386d150f248eb8159b56693f Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Fri, 18 Jul 2025 14:45:57 +1000 Subject: [PATCH 06/11] add comments for patch types --- request/random.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/request/random.go b/request/random.go index a36a126c..656cb5cb 100644 --- a/request/random.go +++ b/request/random.go @@ -375,9 +375,14 @@ type requestPatchBuilder struct { } var patchTypes = map[string]apitypes.PatchType{ - "json": apitypes.JSONPatchType, - "merge": apitypes.MergePatchType, - "strategic": apitypes.StrategicMergePatchType, + // json: Array of operations like [{"op": "replace", "path": "/spec/replicas", "value": 3}] + "json": apitypes.JSONPatchType, + + // merge: Simple object merge like {"spec": {"replicas": 3}} + "merge": apitypes.MergePatchType, + + // strategic-merge: Smart merge for Kubernetes resources that preserves arrays + "strategic-merge": apitypes.StrategicMergePatchType, } func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) (*requestPatchBuilder, error) { @@ -385,11 +390,12 @@ func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, max var body interface{} trimmed := strings.TrimSpace(src.Body) + // validate that the patch body contains valid json if !json.Valid([]byte(trimmed)) { return nil, fmt.Errorf("invalid JSON in patch body: %q", src.Body) } body = []byte(trimmed) - + // validate patch type patchType, ok := patchTypes[src.PatchType] if !ok { return nil, fmt.Errorf("unknown patch type: %s", src.PatchType) From 67a531cfdae6d7b581f09d03b666842c953696a6 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Fri, 18 Jul 2025 15:18:39 +1000 Subject: [PATCH 07/11] cleaning up --- request/random.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/request/random.go b/request/random.go index 656cb5cb..643ffcf3 100644 --- a/request/random.go +++ b/request/random.go @@ -377,10 +377,8 @@ type requestPatchBuilder struct { var patchTypes = map[string]apitypes.PatchType{ // json: Array of operations like [{"op": "replace", "path": "/spec/replicas", "value": 3}] "json": apitypes.JSONPatchType, - // merge: Simple object merge like {"spec": {"replicas": 3}} "merge": apitypes.MergePatchType, - // strategic-merge: Smart merge for Kubernetes resources that preserves arrays "strategic-merge": apitypes.StrategicMergePatchType, } From 6a954a73a883c83349539915773dab0b0b2078d6 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Fri, 18 Jul 2025 15:20:33 +1000 Subject: [PATCH 08/11] cleaning up --- .../manifests/loadprofile/test_mutation.yaml | 28 +++++++++++++++++++ .../manifests/workload/test-template.yaml | 10 +++++++ request/random.go | 4 +-- 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 contrib/internal/manifests/loadprofile/test_mutation.yaml create mode 100644 contrib/internal/manifests/workload/test-template.yaml diff --git a/contrib/internal/manifests/loadprofile/test_mutation.yaml b/contrib/internal/manifests/loadprofile/test_mutation.yaml new file mode 100644 index 00000000..0153fa20 --- /dev/null +++ b/contrib/internal/manifests/loadprofile/test_mutation.yaml @@ -0,0 +1,28 @@ +version: 1 +description: "test_mutation" +spec: + rate: 10 + conns: 10 + client: 10 + total: 1 + contentType: json + requests: + - patch: + version: v1 + resource: pods + namespace: kperf + patchType: strategic-mergedd + name: test-pod4 + body: | + { + "spec": { + "containers": [ + { + "name": "fake-container", + "image": "busybox:1.36" + } + ] + } + } + shares: 100 + diff --git a/contrib/internal/manifests/workload/test-template.yaml b/contrib/internal/manifests/workload/test-template.yaml new file mode 100644 index 00000000..ffc30d1c --- /dev/null +++ b/contrib/internal/manifests/workload/test-template.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod4 + namespace: kperf +spec: + containers: + - name: fake-container + image: busybox + command: ["sleep", "3600"] diff --git a/request/random.go b/request/random.go index 643ffcf3..1ac1fbde 100644 --- a/request/random.go +++ b/request/random.go @@ -375,9 +375,9 @@ type requestPatchBuilder struct { } var patchTypes = map[string]apitypes.PatchType{ - // json: Array of operations like [{"op": "replace", "path": "/spec/replicas", "value": 3}] + // json: Array of operations [{"op": "replace", "path": "/spec/replicas", "value": 3}] "json": apitypes.JSONPatchType, - // merge: Simple object merge like {"spec": {"replicas": 3}} + // merge: Simple object merge {"spec": {"replicas": 3}} "merge": apitypes.MergePatchType, // strategic-merge: Smart merge for Kubernetes resources that preserves arrays "strategic-merge": apitypes.StrategicMergePatchType, From 50e5745d8076cf64a02a0ce7a23903626e9d216e Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Fri, 18 Jul 2025 15:21:02 +1000 Subject: [PATCH 09/11] clean up --- .../manifests/loadprofile/test_mutation.yaml | 28 ------------------- .../manifests/workload/test-template.yaml | 10 ------- 2 files changed, 38 deletions(-) delete mode 100644 contrib/internal/manifests/loadprofile/test_mutation.yaml delete mode 100644 contrib/internal/manifests/workload/test-template.yaml diff --git a/contrib/internal/manifests/loadprofile/test_mutation.yaml b/contrib/internal/manifests/loadprofile/test_mutation.yaml deleted file mode 100644 index 0153fa20..00000000 --- a/contrib/internal/manifests/loadprofile/test_mutation.yaml +++ /dev/null @@ -1,28 +0,0 @@ -version: 1 -description: "test_mutation" -spec: - rate: 10 - conns: 10 - client: 10 - total: 1 - contentType: json - requests: - - patch: - version: v1 - resource: pods - namespace: kperf - patchType: strategic-mergedd - name: test-pod4 - body: | - { - "spec": { - "containers": [ - { - "name": "fake-container", - "image": "busybox:1.36" - } - ] - } - } - shares: 100 - diff --git a/contrib/internal/manifests/workload/test-template.yaml b/contrib/internal/manifests/workload/test-template.yaml deleted file mode 100644 index ffc30d1c..00000000 --- a/contrib/internal/manifests/workload/test-template.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: test-pod4 - namespace: kperf -spec: - containers: - - name: fake-container - image: busybox - command: ["sleep", "3600"] From b0c508ec7a6dd61a54ec565a79bdc993ed94a146 Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Mon, 21 Jul 2025 13:25:33 +1000 Subject: [PATCH 10/11] move validation to load profile --- api/types/load_traffic.go | 30 ++++++++++++++++++++++++++---- request/random.go | 39 +++++---------------------------------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/api/types/load_traffic.go b/api/types/load_traffic.go index 10a48618..395b7824 100644 --- a/api/types/load_traffic.go +++ b/api/types/load_traffic.go @@ -3,7 +3,11 @@ package types -import "fmt" +import ( + "encoding/json" + "fmt" + "strings" +) // ContentType represents the format of response. type ContentType string @@ -322,19 +326,37 @@ func (m *KubeGroupVersionResource) Validate() error { return nil } +var PatchTypeMapping = map[string]string{ + "json": "application/json-patch+json", // RFC 6902 JSON Patch + "merge": "application/merge-patch+json", // RFC 7396 JSON Merge Patch + "strategic-merge": "application/strategic-merge-patch+json", // Kubernetes Strategic Merge +} + // Validate validates RequestPatch type. func (r *RequestPatch) Validate() error { if err := r.KubeGroupVersionResource.Validate(); err != nil { return fmt.Errorf("kube metadata: %v", err) } - if r.Namespace == "" { - return fmt.Errorf("namespace is required") - } if r.Name == "" { return fmt.Errorf("name is required") } if r.Body == "" { return fmt.Errorf("body is required") } + + // Validate patch type + _, ok := PatchTypeMapping[r.PatchType] + if !ok { + return fmt.Errorf("unknown patch type: %s (valid types: json, merge, strategic-merge)", r.PatchType) + } + + // Validate JSON body and trim it + trimmed := strings.TrimSpace(r.Body) + if !json.Valid([]byte(trimmed)) { + return fmt.Errorf("invalid JSON in patch body: %q", r.Body) + } + + r.Body = trimmed // Store the trimmed body + return nil } diff --git a/request/random.go b/request/random.go index 1ac1fbde..4d0486ff 100644 --- a/request/random.go +++ b/request/random.go @@ -6,10 +6,8 @@ package request import ( "context" "crypto/rand" - "encoding/json" "fmt" "math/big" - "strings" "sync" "github.com/Azure/kperf/api/types" @@ -46,7 +44,6 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ shares = append(shares, r.Shares) var builder RESTRequestBuilder - var err error switch { case r.StaleList != nil: builder = newRequestListBuilder(r.StaleList, "0", spec.MaxRetries) @@ -61,10 +58,7 @@ func NewWeightedRandomRequests(spec *types.LoadProfileSpec) (*WeightedRandomRequ case r.GetPodLog != nil: builder = newRequestGetPodLogBuilder(r.GetPodLog, spec.MaxRetries) case r.Patch != nil: - builder, err = newRequestPatchBuilder(r.Patch, "", spec.MaxRetries) - if err != nil { - return nil, fmt.Errorf("failed to create patch request builder: %v", err) - } + builder = newRequestPatchBuilder(r.Patch, "", spec.MaxRetries) default: return nil, fmt.Errorf("not implement for PUT yet") } @@ -374,30 +368,7 @@ type requestPatchBuilder struct { maxRetries int } -var patchTypes = map[string]apitypes.PatchType{ - // json: Array of operations [{"op": "replace", "path": "/spec/replicas", "value": 3}] - "json": apitypes.JSONPatchType, - // merge: Simple object merge {"spec": {"replicas": 3}} - "merge": apitypes.MergePatchType, - // strategic-merge: Smart merge for Kubernetes resources that preserves arrays - "strategic-merge": apitypes.StrategicMergePatchType, -} - -func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) (*requestPatchBuilder, error) { - - var body interface{} - - trimmed := strings.TrimSpace(src.Body) - // validate that the patch body contains valid json - if !json.Valid([]byte(trimmed)) { - return nil, fmt.Errorf("invalid JSON in patch body: %q", src.Body) - } - body = []byte(trimmed) - // validate patch type - patchType, ok := patchTypes[src.PatchType] - if !ok { - return nil, fmt.Errorf("unknown patch type: %s", src.PatchType) - } +func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, maxRetries int) *requestPatchBuilder { return &requestPatchBuilder{ version: schema.GroupVersion{ @@ -408,10 +379,10 @@ func newRequestPatchBuilder(src *types.RequestPatch, resourceVersion string, max resourceVersion: resourceVersion, namespace: src.Namespace, name: src.Name, - patchType: patchType, - body: body, + patchType: apitypes.PatchType(types.PatchTypeMapping[src.PatchType]), + body: []byte(src.Body), maxRetries: maxRetries, - }, nil + } } // Build implements RequestBuilder.Build. From 889bc6f96342a28ff6b23178a1ba61edd7aeef2c Mon Sep 17 00:00:00 2001 From: vittoriasalim Date: Mon, 21 Jul 2025 13:28:34 +1000 Subject: [PATCH 11/11] fix lint --- api/types/load_traffic.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/types/load_traffic.go b/api/types/load_traffic.go index 395b7824..65dbe314 100644 --- a/api/types/load_traffic.go +++ b/api/types/load_traffic.go @@ -327,8 +327,8 @@ func (m *KubeGroupVersionResource) Validate() error { } var PatchTypeMapping = map[string]string{ - "json": "application/json-patch+json", // RFC 6902 JSON Patch - "merge": "application/merge-patch+json", // RFC 7396 JSON Merge Patch + "json": "application/json-patch+json", // RFC 6902 JSON Patch + "merge": "application/merge-patch+json", // RFC 7396 JSON Merge Patch "strategic-merge": "application/strategic-merge-patch+json", // Kubernetes Strategic Merge } @@ -356,7 +356,7 @@ func (r *RequestPatch) Validate() error { return fmt.Errorf("invalid JSON in patch body: %q", r.Body) } - r.Body = trimmed // Store the trimmed body + r.Body = trimmed // Store the trimmed body return nil }