diff --git a/images/kube-api-proxy/cmd/kube-api-proxy/main.go b/images/kube-api-proxy/cmd/kube-api-proxy/main.go index ef111b1f1..12bf46781 100644 --- a/images/kube-api-proxy/cmd/kube-api-proxy/main.go +++ b/images/kube-api-proxy/cmd/kube-api-proxy/main.go @@ -24,6 +24,7 @@ import ( logutil "kube-api-proxy/pkg/log" "kube-api-proxy/pkg/proxy" "kube-api-proxy/pkg/rewriter" + "kube-api-proxy/pkg/rewriter/rules" "kube-api-proxy/pkg/server" "kube-api-proxy/pkg/target" ) @@ -70,9 +71,10 @@ func main() { }) // Load rules from file or use default kubevirt rules. + // WARNING: loading of declarative rules is just a PoC and not working yet. rewriteRules := kubevirt.KubevirtRewriteRules if os.Getenv("RULES_PATH") != "" { - rulesFromFile, err := rewriter.LoadRules(os.Getenv("RULES_PATH")) + rulesFromFile, err := rules.LoadRules(os.Getenv("RULES_PATH")) if err != nil { log.Error("Load rules from %s: %v", os.Getenv("RULES_PATH"), err) os.Exit(1) diff --git a/images/kube-api-proxy/pkg/kubevirt/kubevirt_rules.go b/images/kube-api-proxy/pkg/kubevirt/kubevirt_rules.go index 8c8bf9bad..213b2cda9 100644 --- a/images/kube-api-proxy/pkg/kubevirt/kubevirt_rules.go +++ b/images/kube-api-proxy/pkg/kubevirt/kubevirt_rules.go @@ -17,7 +17,7 @@ limitations under the License. package kubevirt import ( - . "kube-api-proxy/pkg/rewriter" + . "kube-api-proxy/pkg/rewriter/rules" ) const ( diff --git a/images/kube-api-proxy/pkg/proxy/handler.go b/images/kube-api-proxy/pkg/proxy/handler.go index b312ca65e..ea1b782e5 100644 --- a/images/kube-api-proxy/pkg/proxy/handler.go +++ b/images/kube-api-proxy/pkg/proxy/handler.go @@ -31,6 +31,7 @@ import ( logutil "kube-api-proxy/pkg/log" "kube-api-proxy/pkg/rewriter" + "kube-api-proxy/pkg/rewriter/rules" ) type ProxyMode string @@ -42,18 +43,18 @@ const ( ToRenamed ProxyMode = "renamed" ) -func ToTargetAction(proxyMode ProxyMode) rewriter.Action { +func ToTargetAction(proxyMode ProxyMode) rules.Action { if proxyMode == ToRenamed { - return rewriter.Rename + return rules.Rename } - return rewriter.Restore + return rules.Restore } -func FromTargetAction(proxyMode ProxyMode) rewriter.Action { +func FromTargetAction(proxyMode ProxyMode) rules.Action { if proxyMode == ToRenamed { - return rewriter.Restore + return rules.Restore } - return rewriter.Rename + return rules.Rename } type Handler struct { diff --git a/images/kube-api-proxy/pkg/proxy/stream_handler.go b/images/kube-api-proxy/pkg/proxy/stream_handler.go index afa565de5..f12921e60 100644 --- a/images/kube-api-proxy/pkg/proxy/stream_handler.go +++ b/images/kube-api-proxy/pkg/proxy/stream_handler.go @@ -36,6 +36,7 @@ import ( logutil "kube-api-proxy/pkg/log" "kube-api-proxy/pkg/rewriter" + "kube-api-proxy/pkg/rewriter/rules" ) // StreamHandler reads a stream from the target, transforms events @@ -206,7 +207,7 @@ func (s *StreamHandler) transformWatchEvent(ev *metav1.WatchEvent) (*metav1.Watc s.log.Debug(fmt.Sprintf("Receive '%s' watch event with %s/%s %s/%s object", ev.Type, group, kind, ns, name)) // Restore object in the event. Watch responses are always from the Kubernetes API server, so rename is not needed. - rwrObjBytes, err := s.rewriter.RewriteJSONPayload(s.targetReq, ev.Object.Raw, rewriter.Restore) + rwrObjBytes, err := s.rewriter.RewriteJSONPayload(s.targetReq, ev.Object.Raw, rules.Restore) if err != nil { return nil, fmt.Errorf("error rewriting object: %w", err) } diff --git a/images/kube-api-proxy/pkg/rewriter/3rdparty.go b/images/kube-api-proxy/pkg/rewriter/3rdparty.go index 915d73ef0..56f940d0c 100644 --- a/images/kube-api-proxy/pkg/rewriter/3rdparty.go +++ b/images/kube-api-proxy/pkg/rewriter/3rdparty.go @@ -16,17 +16,20 @@ limitations under the License. package rewriter +import ( + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" +) + // Rewrite routines for 3rd party resources, i.e. ServiceMonitor. const ( - PrometheusRuleKind = "PrometheusRule" - PrometheusRuleListKind = "PrometheusRuleList" ServiceMonitorKind = "ServiceMonitor" ServiceMonitorListKind = "ServiceMonitorList" ) -func RewriteServiceMonitorOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return TransformObject(obj, "spec.selector", func(obj []byte) ([]byte, error) { - return rewriteLabelSelector(rules, obj, action) +func RewriteServiceMonitor(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { + return transform.Object(obj, "spec.selector", func(obj []byte) ([]byte, error) { + return RewriteLabelSelector(rwRules, obj, action) }) } diff --git a/images/kube-api-proxy/pkg/rewriter/admission_configuration.go b/images/kube-api-proxy/pkg/rewriter/admission_configuration.go index 81b0eb1b0..3ad5bafdb 100644 --- a/images/kube-api-proxy/pkg/rewriter/admission_configuration.go +++ b/images/kube-api-proxy/pkg/rewriter/admission_configuration.go @@ -16,7 +16,12 @@ limitations under the License. package rewriter -import "github.com/tidwall/gjson" +import ( + "github.com/tidwall/gjson" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" +) const ( ValidatingWebhookConfigurationKind = "ValidatingWebhookConfiguration" @@ -25,62 +30,40 @@ const ( MutatingWebhookConfigurationListKind = "MutatingWebhookConfigurationList" ) -func RewriteValidatingOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - if action == Rename { - return RewriteResourceOrList(obj, ValidatingWebhookConfigurationListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "webhooks", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return renameRoleRule(rules, item) - }) - }) - }) - } - return RewriteResourceOrList(obj, ValidatingWebhookConfigurationListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "webhooks", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return restoreRoleRule(rules, item) - }) +func RewriteValidating(rwRules *rules.RewriteRules, validatingObj []byte, action rules.Action) ([]byte, error) { + return transform.Array(validatingObj, "webhooks", func(webhookObj []byte) ([]byte, error) { + return transform.Array(webhookObj, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, action) }) }) } -func RewriteMutatingOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - if action == Rename { - return RewriteResourceOrList(obj, MutatingWebhookConfigurationListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "webhooks", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return renameRoleRule(rules, item) - }) - }) - }) - } - return RewriteResourceOrList(obj, MutatingWebhookConfigurationListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "webhooks", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return restoreRoleRule(rules, item) - }) +func RewriteMutating(rwRules *rules.RewriteRules, mutatingObj []byte, action rules.Action) ([]byte, error) { + return transform.Array(mutatingObj, "webhooks", func(webhookObj []byte) ([]byte, error) { + return transform.Array(webhookObj, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, action) }) }) } -func RenameWebhookConfigurationPatch(rules *RewriteRules, obj []byte) ([]byte, error) { - obj, err := RenameMetadataPatch(rules, obj) +func RenameWebhookConfigurationPatch(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { + obj, err := RenameMetadataPatch(rwRules, obj) if err != nil { return nil, err } - return TransformPatch(obj, func(mergePatch []byte) ([]byte, error) { - return RewriteArray(mergePatch, "webhooks", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return restoreRoleRule(rules, item) + return transform.Patch(obj, func(mergePatch []byte) ([]byte, error) { + return transform.Array(mergePatch, "webhooks", func(webhook []byte) ([]byte, error) { + return transform.Array(webhook, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, rules.Rename) }) }) }, func(jsonPatch []byte) ([]byte, error) { path := gjson.GetBytes(jsonPatch, "path").String() if path == "/webhooks" { - return RewriteArray(jsonPatch, "value", func(webhook []byte) ([]byte, error) { - return RewriteArray(webhook, "rules", func(item []byte) ([]byte, error) { - return renameRoleRule(rules, item) + return transform.Array(jsonPatch, "value", func(webhook []byte) ([]byte, error) { + return transform.Array(webhook, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, rules.Rename) }) }) } diff --git a/images/kube-api-proxy/pkg/rewriter/admission_configuration_test.go b/images/kube-api-proxy/pkg/rewriter/admission_configuration_test.go index 42c7f6388..3d680c113 100644 --- a/images/kube-api-proxy/pkg/rewriter/admission_configuration_test.go +++ b/images/kube-api-proxy/pkg/rewriter/admission_configuration_test.go @@ -20,6 +20,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "kube-api-proxy/pkg/rewriter/rules" ) func TestValidatingRename(t *testing.T) { @@ -30,8 +32,8 @@ func TestValidatingRename(t *testing.T) { }{ { "mixed resources", - `{"webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["original.group.io"], "resources": ["someresources"]}]}]}`, - `{"webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["prefixed.resources.group.io"], "resources": ["prefixedsomeresources"]}]}]}`, + `{"kind":"ValidatingWebhookConfiguration","webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["original.group.io"], "resources": ["someresources"]}]}]}`, + `{"kind":"ValidatingWebhookConfiguration","webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["prefixed.resources.group.io"], "resources": ["prefixedsomeresources"]}]}]}`, }, { "empty object", @@ -44,7 +46,7 @@ func TestValidatingRename(t *testing.T) { t.Run(tt.name, func(t *testing.T) { rwr := createTestRewriter() - resBytes, err := RewriteValidatingOrList(rwr.Rules, []byte(tt.manifest), Rename) + resBytes, err := RewriteValidating(rwr.Rules, []byte(tt.manifest), rules.Rename) require.NoError(t, err, "should rename validating webhook configuration") actual := string(resBytes) @@ -61,8 +63,8 @@ func TestValidatingRestore(t *testing.T) { }{ { "mixed resources", - `{"webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["prefixed.resources.group.io"], "resources": ["prefixedsomeresources"]}]}]}`, - `{"webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["original.group.io"], "resources": ["someresources"]}]}]}`, + `{"kind":"ValidatingWebhookConfiguration","webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["prefixed.resources.group.io"], "resources": ["prefixedsomeresources"]}]}]}`, + `{"kind":"ValidatingWebhookConfiguration","webhooks":[{"rules":[{"apiGroups":[""],"resources":["pods"]},{"apiGroups": ["original.group.io"], "resources": ["someresources"]}]}]}`, }, { "empty object", @@ -75,7 +77,7 @@ func TestValidatingRestore(t *testing.T) { t.Run(tt.name, func(t *testing.T) { rwr := createTestRewriter() - resBytes, err := RewriteValidatingOrList(rwr.Rules, []byte(tt.manifest), Restore) + resBytes, err := RewriteValidating(rwr.Rules, []byte(tt.manifest), rules.Restore) require.NoError(t, err, "should rename validating webhook configuration") actual := string(resBytes) diff --git a/images/kube-api-proxy/pkg/rewriter/admission_review.go b/images/kube-api-proxy/pkg/rewriter/admission_review.go index b850130f1..2baec01b6 100644 --- a/images/kube-api-proxy/pkg/rewriter/admission_review.go +++ b/images/kube-api-proxy/pkg/rewriter/admission_review.go @@ -19,13 +19,15 @@ package rewriter import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" + + "kube-api-proxy/pkg/rewriter/rules" ) // RewriteAdmissionReview rewrites AdmissionReview request and response. // NOTE: only one rewrite direction is supported for now: // - Restore object in AdmissionReview request. // - Do nothing for AdmissionReview response. -func RewriteAdmissionReview(rules *RewriteRules, obj []byte, origGroup string) ([]byte, error) { +func RewriteAdmissionReview(rwRules *rules.RewriteRules, obj []byte, origGroup string) ([]byte, error) { response := gjson.GetBytes(obj, "response") if response.Exists() { // TODO rewrite response with the Patch. @@ -34,7 +36,7 @@ func RewriteAdmissionReview(rules *RewriteRules, obj []byte, origGroup string) ( request := gjson.GetBytes(obj, "request") if request.Exists() { - newRequest, err := RestoreAdmissionReviewRequest(rules, []byte(request.Raw), origGroup) + newRequest, err := RestoreAdmissionReviewRequest(rwRules, []byte(request.Raw), origGroup) if err != nil { return nil, err } @@ -51,7 +53,7 @@ func RewriteAdmissionReview(rules *RewriteRules, obj []byte, origGroup string) ( // RestoreAdmissionReviewRequest restores apiVersion, kind and other fields in an AdmissionReview request. // Only restoring is required, as AdmissionReview request only comes from API Server. -func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup string) ([]byte, error) { +func RestoreAdmissionReviewRequest(rwRules *rules.RewriteRules, obj []byte, origGroup string) ([]byte, error) { var err error // Rewrite "resource" field and find rules. @@ -60,10 +62,10 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st group := resourceObj.Get("group") resource := resourceObj.Get("resource") // Ignore reviews for unknown renamed group. - if group.String() != rules.RenamedGroup { + if group.String() != rwRules.RenamedGroup { return nil, nil } - newResource := rules.RestoreResource(resource.String()) + newResource := rwRules.RestoreResource(resource.String()) obj, err = sjson.SetBytes(obj, "resource.resource", newResource) if err != nil { return nil, err @@ -80,10 +82,10 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st group := fieldObj.Get("group") resource := fieldObj.Get("resource") // Ignore reviews for unknown renamed group. - if group.String() != rules.RenamedGroup { + if group.String() != rwRules.RenamedGroup { return nil, nil } - newResource := rules.RestoreResource(resource.String()) + newResource := rwRules.RestoreResource(resource.String()) obj, err = sjson.SetBytes(obj, "requestResource.resource", newResource) if err != nil { return nil, err @@ -106,7 +108,7 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st { fieldObj := gjson.GetBytes(obj, "kind") kind := fieldObj.Get("kind") - newKind := rules.RestoreKind(kind.String()) + newKind := rwRules.RestoreKind(kind.String()) obj, err = sjson.SetBytes(obj, "kind.kind", newKind) if err != nil { return nil, err @@ -121,7 +123,7 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st { fieldObj := gjson.GetBytes(obj, "requestKind") kind := fieldObj.Get("kind") - newKind := rules.RestoreKind(kind.String()) + newKind := rwRules.RestoreKind(kind.String()) obj, err = sjson.SetBytes(obj, "requestKind.kind", newKind) if err != nil { return nil, err @@ -136,7 +138,7 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st { fieldObj := gjson.GetBytes(obj, "object") if fieldObj.Exists() { - newField, err := RestoreResource(rules, []byte(fieldObj.Raw), origGroup) + newField, err := RestoreResource(rwRules, []byte(fieldObj.Raw), origGroup) if err != nil { return nil, err } @@ -153,7 +155,7 @@ func RestoreAdmissionReviewRequest(rules *RewriteRules, obj []byte, origGroup st { fieldObj := gjson.GetBytes(obj, "oldObject") if fieldObj.Exists() { - newField, err := RestoreResource(rules, []byte(fieldObj.Raw), origGroup) + newField, err := RestoreResource(rwRules, []byte(fieldObj.Raw), origGroup) if err != nil { return nil, err } diff --git a/images/kube-api-proxy/pkg/rewriter/affinity.go b/images/kube-api-proxy/pkg/rewriter/affinity.go index 34cbc912d..15004626d 100644 --- a/images/kube-api-proxy/pkg/rewriter/affinity.go +++ b/images/kube-api-proxy/pkg/rewriter/affinity.go @@ -16,26 +16,31 @@ limitations under the License. package rewriter +import ( + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" +) + // RewriteAffinity renames or restores labels in labelSelector of affinity structure. // See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity -func RewriteAffinity(rules *RewriteRules, obj []byte, path string, action Action) ([]byte, error) { - return TransformObject(obj, path, func(affinity []byte) ([]byte, error) { - rwrAffinity, err := TransformObject(affinity, "nodeAffinity", func(item []byte) ([]byte, error) { - return rewriteNodeAffinity(rules, item, action) +func RewriteAffinity(rwRules *rules.RewriteRules, obj []byte, path string, action rules.Action) ([]byte, error) { + return transform.Object(obj, path, func(affinity []byte) ([]byte, error) { + rwrAffinity, err := transform.Object(affinity, "nodeAffinity", func(item []byte) ([]byte, error) { + return rewriteNodeAffinity(rwRules, item, action) }) if err != nil { return nil, err } - rwrAffinity, err = TransformObject(rwrAffinity, "podAffinity", func(item []byte) ([]byte, error) { - return rewritePodAffinity(rules, item, action) + rwrAffinity, err = transform.Object(rwrAffinity, "podAffinity", func(item []byte) ([]byte, error) { + return rewritePodAffinity(rwRules, item, action) }) if err != nil { return nil, err } - return TransformObject(rwrAffinity, "podAntiAffinity", func(item []byte) ([]byte, error) { - return rewritePodAffinity(rules, item, action) + return transform.Object(rwrAffinity, "podAntiAffinity", func(item []byte) ([]byte, error) { + return rewritePodAffinity(rwRules, item, action) }) }) @@ -49,12 +54,12 @@ func RewriteAffinity(rules *RewriteRules, obj []byte, path string, action Action // preferredDuringSchedulingIgnoredDuringExecution: -> array of PreferredSchedulingTerm: // preference NodeSelector -> rewrite key in each matchExpressions and matchFields // weight: -func rewriteNodeAffinity(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { +func rewriteNodeAffinity(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { // Rewrite an array of nodeSelectorTerms in requiredDuringSchedulingIgnoredDuringExecution field. var err error - obj, err = TransformObject(obj, "requiredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { - return RewriteArray(affinityTerm, "nodeSelectorTerms", func(item []byte) ([]byte, error) { - return rewriteNodeSelectorTerm(rules, item, action) + obj, err = transform.Object(obj, "requiredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { + return transform.Array(affinityTerm, "nodeSelectorTerms", func(item []byte) ([]byte, error) { + return rewriteNodeSelectorTerm(rwRules, item, action) }) }) if err != nil { @@ -62,29 +67,29 @@ func rewriteNodeAffinity(rules *RewriteRules, obj []byte, action Action) ([]byte } // Rewrite an array of weightedNodeSelectorTerms in preferredDuringSchedulingIgnoredDuringExecution field. - return RewriteArray(obj, "preferredDuringSchedulingIgnoredDuringExecution", func(item []byte) ([]byte, error) { - return TransformObject(item, "preference", func(preference []byte) ([]byte, error) { - return rewriteNodeSelectorTerm(rules, preference, action) + return transform.Array(obj, "preferredDuringSchedulingIgnoredDuringExecution", func(item []byte) ([]byte, error) { + return transform.Object(item, "preference", func(preference []byte) ([]byte, error) { + return rewriteNodeSelectorTerm(rwRules, preference, action) }) }) } // rewriteNodeSelectorTerm renames or restores key fields in matchLabels or matchExpressions of NodeSelectorTerm. -func rewriteNodeSelectorTerm(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - obj, err := RewriteArray(obj, "matchLabels", func(item []byte) ([]byte, error) { - return rewriteSelectorRequirement(rules, item, action) +func rewriteNodeSelectorTerm(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { + obj, err := transform.Array(obj, "matchLabels", func(item []byte) ([]byte, error) { + return rewriteSelectorRequirement(rwRules, item, action) }) if err != nil { return nil, err } - return RewriteArray(obj, "matchExpressions", func(item []byte) ([]byte, error) { - return rewriteSelectorRequirement(rules, item, action) + return transform.Array(obj, "matchExpressions", func(item []byte) ([]byte, error) { + return rewriteSelectorRequirement(rwRules, item, action) }) } -func rewriteSelectorRequirement(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return TransformString(obj, "key", func(field string) string { - return rules.LabelsRewriter().Rewrite(field, action) +func rewriteSelectorRequirement(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { + return transform.String(obj, "key", func(field string) string { + return rwRules.LabelsRewriter().Rewrite(field, action) }) } @@ -100,52 +105,52 @@ func rewriteSelectorRequirement(rules *RewriteRules, obj []byte, action Action) // preferredDuringSchedulingIgnoredDuringExecution -> array of WeightedPodAffinityTerm: // weight // podAffinityTerm PodAffinityTerm -> rewrite as described above -func rewritePodAffinity(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { +func rewritePodAffinity(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { // Rewrite an array of PodAffinityTerms in requiredDuringSchedulingIgnoredDuringExecution field. - obj, err := RewriteArray(obj, "requiredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { - return rewritePodAffinityTerm(rules, affinityTerm, action) + obj, err := transform.Array(obj, "requiredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { + return rewritePodAffinityTerm(rwRules, affinityTerm, action) }) if err != nil { return nil, err } // Rewrite an array of WeightedPodAffinityTerms in requiredDuringSchedulingIgnoredDuringExecution field. - return RewriteArray(obj, "preferredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { - return TransformObject(affinityTerm, "podAffinityTerm", func(podAffinityTerm []byte) ([]byte, error) { - return rewritePodAffinityTerm(rules, podAffinityTerm, action) + return transform.Array(obj, "preferredDuringSchedulingIgnoredDuringExecution", func(affinityTerm []byte) ([]byte, error) { + return transform.Object(affinityTerm, "podAffinityTerm", func(podAffinityTerm []byte) ([]byte, error) { + return rewritePodAffinityTerm(rwRules, podAffinityTerm, action) }) }) } -func rewritePodAffinityTerm(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - obj, err := TransformObject(obj, "labelSelector", func(labelSelector []byte) ([]byte, error) { - return rewriteLabelSelector(rules, labelSelector, action) +func rewritePodAffinityTerm(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { + obj, err := transform.Object(obj, "labelSelector", func(labelSelector []byte) ([]byte, error) { + return RewriteLabelSelector(rwRules, labelSelector, action) }) if err != nil { return nil, err } - obj, err = TransformString(obj, "topologyKey", func(field string) string { - return rules.LabelsRewriter().Rewrite(field, action) + obj, err = transform.String(obj, "topologyKey", func(field string) string { + return rwRules.LabelsRewriter().Rewrite(field, action) }) if err != nil { return nil, err } - return TransformObject(obj, "namespaceSelector", func(selector []byte) ([]byte, error) { - return rewriteLabelSelector(rules, selector, action) + return transform.Object(obj, "namespaceSelector", func(selector []byte) ([]byte, error) { + return RewriteLabelSelector(rwRules, selector, action) }) } -// rewriteLabelSelector rewrites matchLabels and matchExpressions. It is similar to rewriteNodeSelectorTerm +// RewriteLabelSelector rewrites matchLabels and matchExpressions. It is similar to rewriteNodeSelectorTerm // but matchLabels is a map here, not an array of requirements. -func rewriteLabelSelector(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - obj, err := RewriteLabelsMap(rules, obj, "matchLabels", action) +func RewriteLabelSelector(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { + obj, err := RewriteLabelsMap(rwRules, obj, "matchLabels", action) if err != nil { return nil, err } - return RewriteArray(obj, "matchExpressions", func(item []byte) ([]byte, error) { - return rewriteSelectorRequirement(rules, item, action) + return transform.Array(obj, "matchExpressions", func(item []byte) ([]byte, error) { + return rewriteSelectorRequirement(rwRules, item, action) }) } diff --git a/images/kube-api-proxy/pkg/rewriter/api_endpoint.go b/images/kube-api-proxy/pkg/rewriter/api_endpoint.go index 3e3d99a5e..830ea6a8a 100644 --- a/images/kube-api-proxy/pkg/rewriter/api_endpoint.go +++ b/images/kube-api-proxy/pkg/rewriter/api_endpoint.go @@ -93,7 +93,7 @@ func ParseAPIEndpoint(apiURL *url.URL) *APIEndpoint { cleanedPath := strings.Trim(apiURL.Path, "/") pathItems := strings.Split(cleanedPath, "/") - if len(pathItems) == 0 { + if cleanedPath == "" || len(pathItems) == 0 { return &APIEndpoint{ IsRoot: true, IsWatch: isWatch, diff --git a/images/kube-api-proxy/pkg/rewriter/app.go b/images/kube-api-proxy/pkg/rewriter/app.go index 23a1ae20c..2ce6b4e49 100644 --- a/images/kube-api-proxy/pkg/rewriter/app.go +++ b/images/kube-api-proxy/pkg/rewriter/app.go @@ -16,7 +16,12 @@ limitations under the License. package rewriter -import "github.com/tidwall/gjson" +import ( + "github.com/tidwall/gjson" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" +) const ( DeploymentKind = "Deployment" @@ -27,36 +32,30 @@ const ( StatefulSetListKind = "StatefulSetList" ) -func RewriteDeploymentOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, DeploymentListKind, func(singleObj []byte) ([]byte, error) { - return RewriteSpecTemplateLabelsAnno(rules, singleObj, "spec", action) - }) +func RewriteDeployment(rwRules *rules.RewriteRules, deploymentObj []byte, action rules.Action) ([]byte, error) { + return RewriteSpecTemplateLabelsAnno(rwRules, deploymentObj, "spec", action) } -func RewriteDaemonSetOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, DaemonSetListKind, func(singleObj []byte) ([]byte, error) { - return RewriteSpecTemplateLabelsAnno(rules, singleObj, "spec", action) - }) +func RewriteDaemonSet(rwRules *rules.RewriteRules, daemonSetObj []byte, action rules.Action) ([]byte, error) { + return RewriteSpecTemplateLabelsAnno(rwRules, daemonSetObj, "spec", action) } -func RewriteStatefulSetOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, StatefulSetListKind, func(singleObj []byte) ([]byte, error) { - return RewriteSpecTemplateLabelsAnno(rules, singleObj, "spec", action) - }) +func RewriteStatefulSet(rwRules *rules.RewriteRules, stsObj []byte, action rules.Action) ([]byte, error) { + return RewriteSpecTemplateLabelsAnno(rwRules, stsObj, "spec", action) } -func RenameSpecTemplatePatch(rules *RewriteRules, obj []byte) ([]byte, error) { - obj, err := RenameMetadataPatch(rules, obj) +func RenameSpecTemplatePatch(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { + obj, err := RenameMetadataPatch(rwRules, obj) if err != nil { return nil, err } - return TransformPatch(obj, func(mergePatch []byte) ([]byte, error) { - return RewriteSpecTemplateLabelsAnno(rules, mergePatch, "spec", Rename) + return transform.Patch(obj, func(mergePatch []byte) ([]byte, error) { + return RewriteSpecTemplateLabelsAnno(rwRules, mergePatch, "spec", rules.Rename) }, func(jsonPatch []byte) ([]byte, error) { path := gjson.GetBytes(jsonPatch, "path").String() if path == "/spec" { - return RewriteSpecTemplateLabelsAnno(rules, jsonPatch, "value", Rename) + return RewriteSpecTemplateLabelsAnno(rwRules, jsonPatch, "value", rules.Rename) } return jsonPatch, nil }) @@ -68,24 +67,24 @@ func RenameSpecTemplatePatch(rules *RewriteRules, obj []byte) ([]byte, error) { // - template.metadata.annotations as annotations map // - template.affinity as Affinity // - template.nodeSelector as labels map. -func RewriteSpecTemplateLabelsAnno(rules *RewriteRules, obj []byte, path string, action Action) ([]byte, error) { - return TransformObject(obj, path, func(obj []byte) ([]byte, error) { - obj, err := RewriteLabelsMap(rules, obj, "template.metadata.labels", action) +func RewriteSpecTemplateLabelsAnno(rwRules *rules.RewriteRules, obj []byte, path string, action rules.Action) ([]byte, error) { + return transform.Object(obj, path, func(obj []byte) ([]byte, error) { + obj, err := RewriteLabelsMap(rwRules, obj, "template.metadata.labels", action) if err != nil { return nil, err } - obj, err = RewriteLabelsMap(rules, obj, "selector.matchLabels", action) + obj, err = RewriteLabelsMap(rwRules, obj, "selector.matchLabels", action) if err != nil { return nil, err } - obj, err = RewriteLabelsMap(rules, obj, "template.spec.nodeSelector", action) + obj, err = RewriteLabelsMap(rwRules, obj, "template.spec.nodeSelector", action) if err != nil { return nil, err } - obj, err = RewriteAffinity(rules, obj, "template.spec.affinity", action) + obj, err = RewriteAffinity(rwRules, obj, "template.spec.affinity", action) if err != nil { return nil, err } - return RewriteAnnotationsMap(rules, obj, "template.metadata.annotations", action) + return RewriteAnnotationsMap(rwRules, obj, "template.metadata.annotations", action) }) } diff --git a/images/kube-api-proxy/pkg/rewriter/app_test.go b/images/kube-api-proxy/pkg/rewriter/app_test.go index 9a75ab84d..07f40c408 100644 --- a/images/kube-api-proxy/pkg/rewriter/app_test.go +++ b/images/kube-api-proxy/pkg/rewriter/app_test.go @@ -24,6 +24,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + . "kube-api-proxy/pkg/rewriter/rules" ) func createTestRewriterForApp() *RuleBasedRewriter { diff --git a/images/kube-api-proxy/pkg/rewriter/core.go b/images/kube-api-proxy/pkg/rewriter/core.go index 1bf1b08f8..c44ca6a1b 100644 --- a/images/kube-api-proxy/pkg/rewriter/core.go +++ b/images/kube-api-proxy/pkg/rewriter/core.go @@ -18,6 +18,9 @@ package rewriter import ( "github.com/tidwall/gjson" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" ) const ( @@ -31,82 +34,74 @@ const ( PersistentVolumeClaimListKind = "PersistentVolumeClaimList" ) -func RewritePodOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, PodListKind, func(singleObj []byte) ([]byte, error) { - singleObj, err := RewriteLabelsMap(rules, singleObj, "spec.nodeSelector", action) - if err != nil { - return nil, err - } - return RewriteAffinity(rules, singleObj, "spec.affinity", action) - }) +func RewritePod(rwRules *rules.RewriteRules, podObj []byte, action rules.Action) ([]byte, error) { + podObj, err := RewriteLabelsMap(rwRules, podObj, "spec.nodeSelector", action) + if err != nil { + return nil, err + } + return RewriteAffinity(rwRules, podObj, "spec.affinity", action) } -func RewriteServiceOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, ServiceListKind, func(singleObj []byte) ([]byte, error) { - return RewriteLabelsMap(rules, singleObj, "spec.selector", action) - }) +func RewriteService(rwRules *rules.RewriteRules, serviceObj []byte, action rules.Action) ([]byte, error) { + return RewriteLabelsMap(rwRules, serviceObj, "spec.selector", action) } -// RewriteJobOrList transforms known fields in the Job manifest. -func RewriteJobOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, JobListKind, func(singleObj []byte) ([]byte, error) { - return RewriteSpecTemplateLabelsAnno(rules, singleObj, "spec", action) - }) +// RewriteJob transforms known fields in the Job manifest. +func RewriteJob(rwRules *rules.RewriteRules, jobObj []byte, action rules.Action) ([]byte, error) { + return RewriteSpecTemplateLabelsAnno(rwRules, jobObj, "spec", action) } -// RewritePVCOrList transforms known fields in the PersistentVolumeClaim manifest. -func RewritePVCOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, PersistentVolumeClaimListKind, func(singleObj []byte) ([]byte, error) { - singleObj, err := TransformObject(singleObj, "spec.dataSource", func(specDataSource []byte) ([]byte, error) { - return RewriteAPIGroupAndKind(rules, specDataSource, action) - }) - if err != nil { - return nil, err - } - return TransformObject(singleObj, "spec.dataSourceRef", func(specDataSourceRef []byte) ([]byte, error) { - return RewriteAPIGroupAndKind(rules, specDataSourceRef, action) - }) +// RewritePVC transforms known fields in the PersistentVolumeClaim manifest. +func RewritePVC(rwRules *rules.RewriteRules, pvcObj []byte, action rules.Action) ([]byte, error) { + pvcObj, err := transform.Object(pvcObj, "spec.dataSource", func(specDataSource []byte) ([]byte, error) { + return RewriteAPIGroupAndKind(rwRules, specDataSource, action) + }) + if err != nil { + return nil, err + } + return transform.Object(pvcObj, "spec.dataSourceRef", func(specDataSourceRef []byte) ([]byte, error) { + return RewriteAPIGroupAndKind(rwRules, specDataSourceRef, action) }) } -func RenameServicePatch(rules *RewriteRules, obj []byte) ([]byte, error) { - obj, err := RenameMetadataPatch(rules, obj) +func RenameServicePatch(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { + obj, err := RenameMetadataPatch(rwRules, obj) if err != nil { return nil, err } // Also rename patch on spec field. - return TransformPatch(obj, nil, func(jsonPatch []byte) ([]byte, error) { + return transform.Patch(obj, nil, func(jsonPatch []byte) ([]byte, error) { path := gjson.GetBytes(jsonPatch, "path").String() switch path { case "/spec": - return RewriteLabelsMap(rules, jsonPatch, "value.selector", Rename) + return RewriteLabelsMap(rwRules, jsonPatch, "value.selector", rules.Rename) } return jsonPatch, nil }) } -func RewriteAPIGroupAndKind(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { +func RewriteAPIGroupAndKind(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { var err error kind := gjson.GetBytes(obj, "kind").String() - obj, err = TransformString(obj, "kind", func(field string) string { - if action == Rename { - return rules.RenameKind(field) + obj, err = transform.String(obj, "kind", func(field string) string { + if action == rules.Rename { + return rwRules.RenameKind(field) } - return rules.RestoreKind(field) + return rwRules.RestoreKind(field) }) if err != nil { return nil, err } - return TransformString(obj, "apiGroup", func(apiGroup string) string { - if action == Rename { - return rules.RenamedGroup + return transform.String(obj, "apiGroup", func(apiGroup string) string { + if action == rules.Rename { + return rwRules.RenamedGroup } // Renamed to original is a one-to-many relation, so we // need an original kind to get proper group for Restore action. - groupRule, _ := rules.GroupResourceRulesByKind(rules.RestoreKind(kind)) + groupRule, _ := rwRules.GroupResourceRulesByKind(rwRules.RestoreKind(kind)) if groupRule == nil { return apiGroup } diff --git a/images/kube-api-proxy/pkg/rewriter/core_test.go b/images/kube-api-proxy/pkg/rewriter/core_test.go index f80da4c12..43120d7cd 100644 --- a/images/kube-api-proxy/pkg/rewriter/core_test.go +++ b/images/kube-api-proxy/pkg/rewriter/core_test.go @@ -24,6 +24,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + . "kube-api-proxy/pkg/rewriter/rules" ) func createTestRewriterForCore() *RuleBasedRewriter { diff --git a/images/kube-api-proxy/pkg/rewriter/crd.go b/images/kube-api-proxy/pkg/rewriter/crd.go index 8140de5e2..5085a916a 100644 --- a/images/kube-api-proxy/pkg/rewriter/crd.go +++ b/images/kube-api-proxy/pkg/rewriter/crd.go @@ -22,6 +22,8 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" + + "kube-api-proxy/pkg/rewriter/rules" ) const ( @@ -29,18 +31,11 @@ const ( CRDListKind = "CustomResourceDefinitionList" ) -func RewriteCRDOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - // CREATE, UPDATE, or PATCH requests. - if action == Rename { - return RewriteResourceOrList(obj, CRDListKind, func(singleObj []byte) ([]byte, error) { - return RenameCRD(rules, singleObj) - }) +func RewriteCRD(rwRules *rules.RewriteRules, crdObj []byte, action rules.Action) ([]byte, error) { + if action == rules.Rename { + return RenameCRD(rwRules, crdObj) } - - // Responses of GET, LIST, DELETE requests. Also, rewrite in watch events. - return RewriteResourceOrList(obj, CRDListKind, func(singleObj []byte) ([]byte, error) { - return RestoreCRD(rules, singleObj) - }) + return RestoreCRD(rwRules, crdObj) } // RestoreCRD restores fields in CRD to original. @@ -56,20 +51,20 @@ func RewriteCRDOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, e // plural prefixedvirtualmachines -> virtualmachines // singular prefixedvirtualmachine -> virtualmachine // shortNames [xvm xvms] -> [vm vms] -func RestoreCRD(rules *RewriteRules, obj []byte) ([]byte, error) { +func RestoreCRD(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { crdName := gjson.GetBytes(obj, "metadata.name").String() resource, group, found := strings.Cut(crdName, ".") if !found { return nil, fmt.Errorf("malformed CRD name: should be resourcetype.group, got %s", crdName) } // Do not restore CRDs in unknown groups. - if group != rules.RenamedGroup { + if group != rwRules.RenamedGroup { return nil, nil } - origResource := rules.RestoreResource(resource) + origResource := rwRules.RestoreResource(resource) - groupRule, resourceRule := rules.GroupResourceRules(origResource) + groupRule, resourceRule := rwRules.GroupResourceRules(origResource) if resourceRule == nil { return nil, nil } @@ -87,27 +82,27 @@ func RestoreCRD(rules *RewriteRules, obj []byte) ([]byte, error) { names := []byte(gjson.GetBytes(obj, "spec.names").Raw) - names, err = sjson.SetBytes(names, "categories", rules.RestoreCategories(resourceRule)) + names, err = sjson.SetBytes(names, "categories", rwRules.RestoreCategories(resourceRule)) if err != nil { return nil, err } - names, err = sjson.SetBytes(names, "kind", rules.RestoreKind(resourceRule.Kind)) + names, err = sjson.SetBytes(names, "kind", rwRules.RestoreKind(resourceRule.Kind)) if err != nil { return nil, err } - names, err = sjson.SetBytes(names, "listKind", rules.RestoreKind(resourceRule.ListKind)) + names, err = sjson.SetBytes(names, "listKind", rwRules.RestoreKind(resourceRule.ListKind)) if err != nil { return nil, err } - names, err = sjson.SetBytes(names, "plural", rules.RestoreResource(resourceRule.Plural)) + names, err = sjson.SetBytes(names, "plural", rwRules.RestoreResource(resourceRule.Plural)) if err != nil { return nil, err } - names, err = sjson.SetBytes(names, "singular", rules.RestoreResource(resourceRule.Singular)) + names, err = sjson.SetBytes(names, "singular", rwRules.RestoreResource(resourceRule.Singular)) if err != nil { return nil, err } - names, err = sjson.SetBytes(names, "shortNames", rules.RestoreShortNames(resourceRule.ShortNames)) + names, err = sjson.SetBytes(names, "shortNames", rwRules.RestoreShortNames(resourceRule.ShortNames)) if err != nil { return nil, err } @@ -133,36 +128,36 @@ func RestoreCRD(rules *RewriteRules, obj []byte) ([]byte, error) { // plural virtualmachines -> prefixedvirtualmachines // singular virtualmachine -> prefixedvirtualmachine // shortNames [vm vms] -> [xvm xvms] -func RenameCRD(rules *RewriteRules, obj []byte) ([]byte, error) { +func RenameCRD(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { crdName := gjson.GetBytes(obj, "metadata.name").String() resource, group, found := strings.Cut(crdName, ".") if !found { return nil, fmt.Errorf("malformed CRD name: should be resourcetype.group, got %s", crdName) } - _, resourceRule := rules.ResourceRules(group, resource) + _, resourceRule := rwRules.ResourceRules(group, resource) if resourceRule == nil { return nil, nil } - newName := rules.RenameResource(resource) + "." + rules.RenamedGroup + newName := rwRules.RenameResource(resource) + "." + rwRules.RenamedGroup obj, err := sjson.SetBytes(obj, "metadata.name", newName) if err != nil { return nil, err } spec := gjson.GetBytes(obj, "spec") - newSpec, err := renameCRDSpec(rules, resourceRule, []byte(spec.Raw)) + newSpec, err := renameCRDSpec(rwRules, resourceRule, []byte(spec.Raw)) if err != nil { return nil, err } return sjson.SetRawBytes(obj, "spec", newSpec) } -func renameCRDSpec(rules *RewriteRules, resourceRule *ResourceRule, spec []byte) ([]byte, error) { +func renameCRDSpec(rwRules *rules.RewriteRules, resourceRule *rules.ResourceRule, spec []byte) ([]byte, error) { var err error - spec, err = sjson.SetBytes(spec, "group", rules.RenamedGroup) + spec, err = sjson.SetBytes(spec, "group", rwRules.RenamedGroup) if err != nil { return nil, err } @@ -171,37 +166,37 @@ func renameCRDSpec(rules *RewriteRules, resourceRule *ResourceRule, spec []byte) names := []byte(gjson.GetBytes(spec, "names").Raw) if gjson.GetBytes(names, "categories").Exists() { - names, err = sjson.SetBytes(names, "categories", rules.RenameCategories(resourceRule.Categories)) + names, err = sjson.SetBytes(names, "categories", rwRules.RenameCategories(resourceRule.Categories)) if err != nil { return nil, err } } if gjson.GetBytes(names, "kind").Exists() { - names, err = sjson.SetBytes(names, "kind", rules.RenameKind(resourceRule.Kind)) + names, err = sjson.SetBytes(names, "kind", rwRules.RenameKind(resourceRule.Kind)) if err != nil { return nil, err } } if gjson.GetBytes(names, "listKind").Exists() { - names, err = sjson.SetBytes(names, "listKind", rules.RenameKind(resourceRule.ListKind)) + names, err = sjson.SetBytes(names, "listKind", rwRules.RenameKind(resourceRule.ListKind)) if err != nil { return nil, err } } if gjson.GetBytes(names, "plural").Exists() { - names, err = sjson.SetBytes(names, "plural", rules.RenameResource(resourceRule.Plural)) + names, err = sjson.SetBytes(names, "plural", rwRules.RenameResource(resourceRule.Plural)) if err != nil { return nil, err } } if gjson.GetBytes(names, "singular").Exists() { - names, err = sjson.SetBytes(names, "singular", rules.RenameResource(resourceRule.Singular)) + names, err = sjson.SetBytes(names, "singular", rwRules.RenameResource(resourceRule.Singular)) if err != nil { return nil, err } } if gjson.GetBytes(names, "shortNames").Exists() { - names, err = sjson.SetBytes(names, "shortNames", rules.RenameShortNames(resourceRule.ShortNames)) + names, err = sjson.SetBytes(names, "shortNames", rwRules.RenameShortNames(resourceRule.ShortNames)) if err != nil { return nil, err } @@ -215,7 +210,7 @@ func renameCRDSpec(rules *RewriteRules, resourceRule *ResourceRule, spec []byte) return spec, nil } -func RenameCRDPatch(rules *RewriteRules, resourceRule *ResourceRule, obj []byte) ([]byte, error) { +func RenameCRDPatch(rwRules *rules.RewriteRules, resourceRule *rules.ResourceRule, obj []byte) ([]byte, error) { var err error patches := gjson.ParseBytes(obj).Array() @@ -234,7 +229,7 @@ func RenameCRDPatch(rules *RewriteRules, resourceRule *ResourceRule, obj []byte) if (op == "replace" || op == "add") && path == "/spec" { isRenamed = true value := []byte(gjson.GetBytes(newPatch, "value").Raw) - newValue, err := renameCRDSpec(rules, resourceRule, value) + newValue, err := renameCRDSpec(rwRules, resourceRule, value) if err != nil { return nil, err } diff --git a/images/kube-api-proxy/pkg/rewriter/crd_test.go b/images/kube-api-proxy/pkg/rewriter/crd_test.go index 1792f45ef..db542b63a 100644 --- a/images/kube-api-proxy/pkg/rewriter/crd_test.go +++ b/images/kube-api-proxy/pkg/rewriter/crd_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + . "kube-api-proxy/pkg/rewriter/rules" ) func createRewriterForCRDTest() *RuleBasedRewriter { @@ -94,6 +96,10 @@ func createRewriterForCRDTest() *RuleBasedRewriter { // TestCRDRename - rename of a single CRD. func TestCRDRename(t *testing.T) { + crdHTTPRequest := `POST /apis/apiextensions.k8s.io/v1/customresourcedefinitions/someresources.original.group.io HTTP/1.1 +Host: 127.0.0.1 + +` origGroup := "original.group.io" reqBody := `{ "apiVersion": "apiextensions.k8s.io/v1", @@ -115,35 +121,41 @@ func TestCRDRename(t *testing.T) { "versions": {} } }` + req, err := http.ReadRequest(bufio.NewReader(bytes.NewBufferString(crdHTTPRequest + reqBody))) + require.NoError(t, err, "should parse hardcoded http request") + require.NotNil(t, req.URL, "should parse url in hardcoded http request") + rwr := createRewriterForCRDTest() - testCRDRules := rwr.Rules + targetReq := NewTargetRequest(rwr, req) + require.NotNil(t, targetReq, "should get TargetRequest") + require.Equal(t, origGroup, targetReq.OrigGroup(), "should set proper orig group") - restored, err := RewriteCRDOrList(testCRDRules, []byte(reqBody), Rename) + resultBytes, err := rwr.RewriteJSONPayload(targetReq, []byte(reqBody), Rename) if err != nil { t.Fatalf("should rename CRD without error: %v", err) } - if restored == nil { + if resultBytes == nil { t.Fatalf("should rename CRD: %v", err) } - resRule := testCRDRules.Rules[origGroup].ResourceRules["someresources"] + resRule := rwr.Rules.Rules[origGroup].ResourceRules["someresources"] tests := []struct { path string expected string }{ - {"metadata.name", testCRDRules.RenameResource(resRule.Plural) + "." + testCRDRules.RenamedGroup}, - {"spec.group", testCRDRules.RenamedGroup}, - {"spec.names.kind", testCRDRules.RenameKind(resRule.Kind)}, - {"spec.names.listKind", testCRDRules.RenameKind(resRule.ListKind)}, - {"spec.names.plural", testCRDRules.RenameResource(resRule.Plural)}, - {"spec.names.singular", testCRDRules.RenameResource(resRule.Singular)}, + {"metadata.name", rwr.Rules.RenameResource(resRule.Plural) + "." + rwr.Rules.RenamedGroup}, + {"spec.group", rwr.Rules.RenamedGroup}, + {"spec.names.kind", rwr.Rules.RenameKind(resRule.Kind)}, + {"spec.names.listKind", rwr.Rules.RenameKind(resRule.ListKind)}, + {"spec.names.plural", rwr.Rules.RenameResource(resRule.Plural)}, + {"spec.names.singular", rwr.Rules.RenameResource(resRule.Singular)}, {"spec.names.shortNames", `["psr","psrs"]`}, } for _, tt := range tests { t.Run(tt.path, func(t *testing.T) { - actual := gjson.GetBytes(restored, tt.path).String() + actual := gjson.GetBytes(resultBytes, tt.path).String() if actual != tt.expected { t.Fatalf("%s value should be %s, got %s", tt.path, tt.expected, actual) } diff --git a/images/kube-api-proxy/pkg/rewriter/discovery.go b/images/kube-api-proxy/pkg/rewriter/discovery.go index cc5fd94f5..cde317247 100644 --- a/images/kube-api-proxy/pkg/rewriter/discovery.go +++ b/images/kube-api-proxy/pkg/rewriter/discovery.go @@ -23,6 +23,9 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" "k8s.io/apimachinery/pkg/runtime" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" ) // RewriteAPIGroupList restores groups and kinds in /apis/ response. @@ -40,15 +43,15 @@ import ( // ], // "preferredVersion":{"groupVersion":"x.virtualization.deckhouse.io/v1","version":"v1"} // } -func RewriteAPIGroupList(rules *RewriteRules, objBytes []byte) ([]byte, error) { +func RewriteAPIGroupList(rwRules *rules.RewriteRules, objBytes []byte) ([]byte, error) { groups := gjson.GetBytes(objBytes, "groups").Array() // TODO get rid of RawExtension, use SetRawBytes. rwrGroups := make([]interface{}, 0) for _, group := range groups { groupName := gjson.Get(group.Raw, "name").String() // Replace renamed group with groups from rules. - if groupName == rules.RenamedGroup { - rwrGroups = append(rwrGroups, rules.GetAPIGroupList()...) + if groupName == rwRules.RenamedGroup { + rwrGroups = append(rwrGroups, rwRules.GetAPIGroupList()...) continue } rwrGroups = append(rwrGroups, runtime.RawExtension{Raw: []byte(group.Raw)}) @@ -107,8 +110,8 @@ func RewriteAPIGroupList(rules *RewriteRules, objBytes []byte) ([]byte, error) { // "groupVersion":"clone.kubevirt.io/v1alpha1", // "version":"v1alpha1"} // } -func RewriteAPIGroup(rules *RewriteRules, obj []byte, origGroup string) ([]byte, error) { - apiGroupRule, ok := rules.Rules[origGroup] +func RewriteAPIGroup(rwRules *rules.RewriteRules, obj []byte, origGroup string) ([]byte, error) { + apiGroupRule, ok := rwRules.Rules[origGroup] if !ok { return nil, fmt.Errorf("no APIGroup rewrites for group '%s'", origGroup) } @@ -197,9 +200,9 @@ func RewriteAPIGroup(rules *RewriteRules, obj []byte, origGroup string) ([]byte, // "verbs":["get","patch","update"] // }] // } -func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ([]byte, error) { +func RewriteAPIResourceList(rwRules *rules.RewriteRules, obj []byte, origGroup string) ([]byte, error) { // Ignore apiGroups not in rules. - apiGroupRule, ok := rules.Rules[origGroup] + apiGroupRule, ok := rwRules.Rules[origGroup] if !ok { return obj, nil } @@ -214,22 +217,22 @@ func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ( for _, resource := range gjson.GetBytes(obj, "resources").Array() { name := resource.Get("name").String() nameParts := strings.Split(name, "/") - resourceName := rules.RestoreResource(nameParts[0]) + resourceName := rwRules.RestoreResource(nameParts[0]) - _, resourceRule := rules.ResourceRules(origGroup, resourceName) + _, resourceRule := rwRules.ResourceRules(origGroup, resourceName) if resourceRule == nil { continue } // Rewrite name and kind. - resBytes, err := sjson.SetBytes([]byte(resource.Raw), "name", rules.RestoreResource(name)) + resBytes, err := sjson.SetBytes([]byte(resource.Raw), "name", rwRules.RestoreResource(name)) if err != nil { return nil, err } kind := gjson.GetBytes(resBytes, "kind").String() if kind != "" { - resBytes, err = sjson.SetBytes(resBytes, "kind", rules.RestoreKind(kind)) + resBytes, err = sjson.SetBytes(resBytes, "kind", rwRules.RestoreKind(kind)) if err != nil { return nil, err } @@ -237,7 +240,7 @@ func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ( singular := gjson.GetBytes(resBytes, "singularName").String() if singular != "" { - resBytes, err = sjson.SetBytes(resBytes, "singularName", rules.RestoreResource(singular)) + resBytes, err = sjson.SetBytes(resBytes, "singularName", rwRules.RestoreResource(singular)) if err != nil { return nil, err } @@ -249,7 +252,7 @@ func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ( for _, shortName := range shortNames { strShortNames = append(strShortNames, shortName.String()) } - newShortNames := rules.RestoreShortNames(strShortNames) + newShortNames := rwRules.RestoreShortNames(strShortNames) resBytes, err = sjson.SetBytes(resBytes, "shortNames", newShortNames) if err != nil { return nil, err @@ -258,7 +261,7 @@ func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ( categories := gjson.GetBytes(resBytes, "categories") if categories.Exists() { - restoredCategories := rules.RestoreCategories(resourceRule) + restoredCategories := rwRules.RestoreCategories(resourceRule) resBytes, err = sjson.SetBytes(resBytes, "categories", restoredCategories) if err != nil { return nil, err @@ -296,9 +299,9 @@ func RewriteAPIResourceList(rules *RewriteRules, obj []byte, origGroup string) ( // }, ... // ] // -// NOTE: Can't use RewriteArray here, because one APIGroupDiscovery with renamed +// NOTE: Can't use transform.Array here, because one APIGroupDiscovery with renamed // resource produces many APIGroupDiscovery objects with restored resource. -func RewriteAPIGroupDiscoveryList(rules *RewriteRules, obj []byte) ([]byte, error) { +func RewriteAPIGroupDiscoveryList(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { items := gjson.GetBytes(obj, "items").Array() if len(items) == 0 { return obj, nil @@ -311,7 +314,7 @@ func RewriteAPIGroupDiscoveryList(rules *RewriteRules, obj []byte) ([]byte, erro groupName := gjson.GetBytes(itemBytes, "metadata.name").String() - if groupName != rules.RenamedGroup { + if groupName != rwRules.RenamedGroup { // No transform for non-renamed groups. rwrItems, err = sjson.SetRawBytes(rwrItems, "-1", itemBytes) if err != nil { @@ -320,7 +323,7 @@ func RewriteAPIGroupDiscoveryList(rules *RewriteRules, obj []byte) ([]byte, erro continue } - newItems, err := RestoreAggregatedGroupDiscovery(rules, itemBytes) + newItems, err := RestoreAggregatedGroupDiscovery(rwRules, itemBytes) if err != nil { return nil, err } @@ -362,7 +365,7 @@ func RewriteAPIGroupDiscoveryList(rules *RewriteRules, obj []byte) ([]byte, erro // Renamed resources in one version may belong to different original groups, // so this method indexes and restores all resources in APIResourceDiscovery // and then produces APIGroupDiscovery for each restored group. -func RestoreAggregatedGroupDiscovery(rules *RewriteRules, obj []byte) ([][]byte, error) { +func RestoreAggregatedGroupDiscovery(rwRules *rules.RewriteRules, obj []byte) ([][]byte, error) { // restoredResources holds restored resources indexed by group and version to construct final APIGroupDiscovery items later. // A APIGroupDiscovery "metadata" object field and a version item "version" field are not stored and will be reconstructed. restoredResources := make(map[string]map[string][][]byte) @@ -394,7 +397,7 @@ func RestoreAggregatedGroupDiscovery(rules *RewriteRules, obj []byte) ([][]byte, } for _, resource := range resources { - restoredGroup, restoredResource, err := RestoreAggregatedDiscoveryResource(rules, []byte(resource.Raw)) + restoredGroup, restoredResource, err := RestoreAggregatedDiscoveryResource(rwRules, []byte(resource.Raw)) if err != nil { return nil, nil } @@ -483,14 +486,14 @@ func RestoreAggregatedGroupDiscovery(rules *RewriteRules, obj []byte) ([][]byte, // } // ] // } -func RestoreAggregatedDiscoveryResource(rules *RewriteRules, obj []byte) (string, []byte, error) { +func RestoreAggregatedDiscoveryResource(rwRules *rules.RewriteRules, obj []byte) (string, []byte, error) { var err error // Get resource plural. resource := gjson.GetBytes(obj, "resource").String() - origResource := rules.RestoreResource(resource) + origResource := rwRules.RestoreResource(resource) - groupRule, resRule := rules.GroupResourceRules(origResource) + groupRule, resRule := rwRules.GroupResourceRules(origResource) // Ignore resource without rules. if resRule == nil { @@ -519,7 +522,7 @@ func RestoreAggregatedDiscoveryResource(rules *RewriteRules, obj []byte) (string singular := gjson.GetBytes(obj, "singularResource").String() if singular != "" { - obj, err = sjson.SetBytes(obj, "singularResource", rules.RestoreResource(singular)) + obj, err = sjson.SetBytes(obj, "singularResource", rwRules.RestoreResource(singular)) if err != nil { return "", nil, err } @@ -531,7 +534,7 @@ func RestoreAggregatedDiscoveryResource(rules *RewriteRules, obj []byte) (string for _, shortName := range shortNames { strShortNames = append(strShortNames, shortName.String()) } - newShortNames := rules.RestoreShortNames(strShortNames) + newShortNames := rwRules.RestoreShortNames(strShortNames) obj, err = sjson.SetBytes(obj, "shortNames", newShortNames) if err != nil { return "", nil, err @@ -540,14 +543,14 @@ func RestoreAggregatedDiscoveryResource(rules *RewriteRules, obj []byte) (string categories := gjson.GetBytes(obj, "categories") if categories.Exists() { - restoredCategories := rules.RestoreCategories(resRule) + restoredCategories := rwRules.RestoreCategories(resRule) obj, err = sjson.SetBytes(obj, "categories", restoredCategories) if err != nil { return "", nil, err } } - obj, err = RewriteArray(obj, "subresources", func(item []byte) ([]byte, error) { + obj, err = transform.Array(obj, "subresources", func(item []byte) ([]byte, error) { // Reconstruct group and kind in responseKind field. responseKind := gjson.GetBytes(item, "responseKind") if responseKind.IsObject() { diff --git a/images/kube-api-proxy/pkg/rewriter/discovery_test.go b/images/kube-api-proxy/pkg/rewriter/discovery_test.go index 18830b140..430eb153b 100644 --- a/images/kube-api-proxy/pkg/rewriter/discovery_test.go +++ b/images/kube-api-proxy/pkg/rewriter/discovery_test.go @@ -27,6 +27,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + . "kube-api-proxy/pkg/rewriter/rules" ) func createRewriterForDiscoveryTest() *RuleBasedRewriter { diff --git a/images/kube-api-proxy/pkg/rewriter/list.go b/images/kube-api-proxy/pkg/rewriter/list.go deleted file mode 100644 index 1bdc8b695..000000000 --- a/images/kube-api-proxy/pkg/rewriter/list.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2024 Flant JSC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rewriter - -import ( - "strings" - - "github.com/tidwall/gjson" - "github.com/tidwall/sjson" -) - -// TODO merge this file into transformers.go - -// RewriteResourceOrList is a helper to transform a single resource or a list of resources. -func RewriteResourceOrList(payload []byte, listKind string, transformFn func(singleObj []byte) ([]byte, error)) ([]byte, error) { - kind := gjson.GetBytes(payload, "kind").String() - - // Not a list, transform a single resource. - if kind != listKind { - return transformFn(payload) - } - - return RewriteArray(payload, "items", transformFn) -} - -// RewriteResourceOrList2 is a helper to transform a single resource or a list of resources. -func RewriteResourceOrList2(payload []byte, transformFn func(singleObj []byte) ([]byte, error)) ([]byte, error) { - kind := gjson.GetBytes(payload, "kind").String() - if !strings.HasSuffix(kind, "List") { - return transformFn(payload) - } - return RewriteArray(payload, "items", transformFn) -} - -// RewriteArray gets array by path and transforms each item using transformFn. -// Use Root path to transform object itself. -func RewriteArray(obj []byte, arrayPath string, transformFn func(item []byte) ([]byte, error)) ([]byte, error) { - // Transform each item in list. Put back original items if transformFn returns nil bytes. - items := GetBytes(obj, arrayPath).Array() - if len(items) == 0 { - return obj, nil - } - rwrItems := []byte(`[]`) - for _, item := range items { - rwrItem, err := transformFn([]byte(item.Raw)) - if err != nil { - return nil, err - } - // Put original item back. - if rwrItem == nil { - rwrItem = []byte(item.Raw) - } - rwrItems, err = sjson.SetRawBytes(rwrItems, "-1", rwrItem) - if err != nil { - return nil, err - } - } - - return SetRawBytes(obj, arrayPath, rwrItems) -} diff --git a/images/kube-api-proxy/pkg/rewriter/map.go b/images/kube-api-proxy/pkg/rewriter/map.go deleted file mode 100644 index 83d51db77..000000000 --- a/images/kube-api-proxy/pkg/rewriter/map.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2024 Flant JSC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rewriter - -import ( - "github.com/tidwall/gjson" - "github.com/tidwall/sjson" -) - -// TODO merge this file into transformers.go - -// RewriteMapStringString transforms map[string]string value addressed by path. -func RewriteMapStringString(obj []byte, mapPath string, transformFn func(k, v string) (string, string)) ([]byte, error) { - m := gjson.GetBytes(obj, mapPath).Map() - if len(m) == 0 { - return obj, nil - } - newMap := make(map[string]string, len(m)) - for k, v := range m { - newK, newV := transformFn(k, v.String()) - newMap[newK] = newV - } - - return sjson.SetBytes(obj, mapPath, newMap) -} diff --git a/images/kube-api-proxy/pkg/rewriter/metadata.go b/images/kube-api-proxy/pkg/rewriter/metadata.go index 476bf548b..c1ce6e1bb 100644 --- a/images/kube-api-proxy/pkg/rewriter/metadata.go +++ b/images/kube-api-proxy/pkg/rewriter/metadata.go @@ -16,19 +16,24 @@ limitations under the License. package rewriter -import "github.com/tidwall/gjson" +import ( + "github.com/tidwall/gjson" -func RewriteMetadata(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" +) + +func RewriteMetadata(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { //var err error - obj, err := RewriteLabelsMap(rules, obj, "metadata.labels", action) + obj, err := RewriteLabelsMap(rwRules, obj, "metadata.labels", action) if err != nil { return nil, err } - obj, err = RewriteAnnotationsMap(rules, obj, "metadata.annotations", action) + obj, err = RewriteAnnotationsMap(rwRules, obj, "metadata.annotations", action) if err != nil { return nil, err } - return RewriteFinalizers(rules, obj, "metadata.finalizers", action) + return RewriteFinalizers(rwRules, obj, "metadata.finalizers", action) } // RenameMetadataPatch transforms known metadata fields in patches. @@ -39,39 +44,39 @@ func RewriteMetadata(rules *RewriteRules, obj []byte, action Action) ([]byte, er // [{"op":"test", "path":"/metadata/labels", "value":{"label":"value"}}, // // {"op":"replace", "path":"/metadata/labels", "value":{"label":"newValue"}}] -func RenameMetadataPatch(rules *RewriteRules, patch []byte) ([]byte, error) { - return TransformPatch(patch, +func RenameMetadataPatch(rwRules *rules.RewriteRules, patch []byte) ([]byte, error) { + return transform.Patch(patch, func(mergePatch []byte) ([]byte, error) { - return RewriteMetadata(rules, mergePatch, Rename) + return RewriteMetadata(rwRules, mergePatch, rules.Rename) }, func(jsonPatch []byte) ([]byte, error) { path := gjson.GetBytes(jsonPatch, "path").String() switch path { case "/metadata/labels": - return RewriteLabelsMap(rules, jsonPatch, "value", Rename) + return RewriteLabelsMap(rwRules, jsonPatch, "value", rules.Rename) case "/metadata/annotations": - return RewriteAnnotationsMap(rules, jsonPatch, "value", Rename) + return RewriteAnnotationsMap(rwRules, jsonPatch, "value", rules.Rename) case "/metadata/finalizers": - return RewriteFinalizers(rules, jsonPatch, "value", Rename) + return RewriteFinalizers(rwRules, jsonPatch, "value", rules.Rename) } return jsonPatch, nil }) } -func RewriteLabelsMap(rules *RewriteRules, obj []byte, path string, action Action) ([]byte, error) { - return RewriteMapStringString(obj, path, func(k, v string) (string, string) { +func RewriteLabelsMap(rules *rules.RewriteRules, obj []byte, path string, action rules.Action) ([]byte, error) { + return transform.MapStringString(obj, path, func(k, v string) (string, string) { return rules.LabelsRewriter().Rewrite(k, action), v }) } -func RewriteAnnotationsMap(rules *RewriteRules, obj []byte, path string, action Action) ([]byte, error) { - return RewriteMapStringString(obj, path, func(k, v string) (string, string) { +func RewriteAnnotationsMap(rules *rules.RewriteRules, obj []byte, path string, action rules.Action) ([]byte, error) { + return transform.MapStringString(obj, path, func(k, v string) (string, string) { return rules.AnnotationsRewriter().Rewrite(k, action), v }) } -func RewriteFinalizers(rules *RewriteRules, obj []byte, path string, action Action) ([]byte, error) { - return TransformArrayOfStrings(obj, path, func(finalizer string) string { +func RewriteFinalizers(rules *rules.RewriteRules, obj []byte, path string, action rules.Action) ([]byte, error) { + return transform.ArrayOfStrings(obj, path, func(finalizer string) string { return rules.FinalizersRewriter().Rewrite(finalizer, action) }) } diff --git a/images/kube-api-proxy/pkg/rewriter/path.go b/images/kube-api-proxy/pkg/rewriter/path.go deleted file mode 100644 index 712d20864..000000000 --- a/images/kube-api-proxy/pkg/rewriter/path.go +++ /dev/null @@ -1,191 +0,0 @@ -/* -Copyright 2024 Flant JSC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rewriter - -// RewritePath return rewritten TargetPath along with original group and resource type. -// TODO: this rewriter is not conform to S in SOLID. Should split to ParseAPIEndpoint and RewriteAPIEndpoint. -//func (rw *RuleBasedRewriter) RewritePath(urlPath string) (*TargetRequest, error) { -// // Is it a webhook? -// if webhookRule, ok := rw.Rules.Webhooks[urlPath]; ok { -// return &TargetRequest{ -// Webhook: &webhookRule, -// }, nil -// } -// -// // Is it an API request? -// if strings.HasPrefix(urlPath, "/apis/") || urlPath == "/apis" { -// // TODO refactor RewriteAPIPath to produce a TargetPath, not an array in PathItems. -// cleanedPath := strings.Trim(urlPath, "/") -// pathItems := strings.Split(cleanedPath, "/") -// -// // First, try to rewrite CRD request. -// res := RewriteCRDPath(pathItems, rw.Rules) -// if res != nil { -// return res, nil -// } -// // Next, rewrite usual request. -// res, err := RewriteAPIsPath(pathItems, rw.Rules) -// if err != nil { -// return nil, err -// } -// if res == nil { -// // e.g. no rewrite rule find. -// return nil, nil -// } -// if len(res.PathItems) > 0 { -// res.TargetPath = "/" + path.Join(res.PathItems...) -// } -// return res, nil -// } -// -// if strings.HasPrefix(urlPath, "/api/") || urlPath == "/api" { -// return &TargetRequest{ -// IsCoreAPI: true, -// }, nil -// } -// -// return nil, nil -//} - -// Constants with indices of API endpoints portions. -// Request cluster scoped resource: -// - /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE -// | | | | -// APISIdx | | | -// GroupIDx | | -// VersionIDx ---+ | -// ClusterResourceIdx ---+ - -// -// Request namespaced resource: -// - /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE -// | | | -// NamespacesIdx --------+ | | -// NamespaceIdx --------------------+ | -// NamespacedResourceIdx----------------------+ -// -// Request CRD: -// - /apis/apiextensions.k8s.io/v1/customresourcedefinitions/RESOURCETYPE.GROUP -// | | | -// GroupIdx | | -// ClusterResourceIdx -------------+ | -// CRDNameIdx -----------------------------------------------+ - -//const ( -// APISIdx = 0 -// GroupIdx = 1 -// VersionIdx = 2 -// NamespacesIdx = 3 -// NamespaceIdx = 4 -// ClusterResourceIdx = 3 -// NamespacedResourceIdx = 5 -//) - -// RewriteAPIsPath rewrites GROUP and RESOURCETYPE in these API calls: -// - /apis/GROUP -// - /apis/GROUP/VERSION -// - /apis/GROUP/VERSION/RESOURCETYPE -// - /apis/GROUP/VERSION/RESOURCETYPE/NAME -// - /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE -// -// - /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE -// - /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME -// - /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE -//func RewriteAPIsPath(pathItems []string, rules *RewriteRules) (*TargetRequest, error) { -// if len(pathItems) == 0 { -// return nil, nil -// } -// -// res := &TargetRequest{ -// PathItems: make([]string, 0, len(pathItems)), -// } -// -// if len(pathItems) == 1 { -// if pathItems[APISIdx] == "apis" { -// // Do not rewrite URL, but rewrite response later. -// res.PathItems = append(res.PathItems, pathItems[APISIdx]) -// return res, nil -// } -// // The single path item should be "apis". -// return nil, nil -// } -// -// res.PathItems = append(res.PathItems, pathItems[APISIdx]) -// -// // Check if the GROUP portion match Rules. -// apiGroupName := "" -// apiGroupMatch := false -// group := pathItems[GroupIdx] -// for groupName, apiGroupRule := range rules.Rules { -// if apiGroupRule.GroupRule.Group == group { -// res.OrigGroup = group -// res.PathItems = append(res.PathItems, rules.RenamedGroup) -// apiGroupName = groupName -// apiGroupMatch = true -// break -// } -// } -// -// if !apiGroupMatch { -// return nil, nil -// } -// // Stop if GROUP is the last item in path. -// if len(pathItems) <= GroupIdx+1 { -// return res, nil -// } -// -// // Add VERSION portion. -// res.PathItems = append(res.PathItems, pathItems[VersionIdx]) -// // Stop if VERSION is the last item in path. -// if len(pathItems) <= VersionIdx+1 { -// return res, nil -// } -// -// // Check is namespaced resource is requested. -// resourceTypeIdx := ClusterResourceIdx -// if pathItems[NamespacesIdx] == "namespaces" { -// res.PathItems = append(res.PathItems, pathItems[NamespacesIdx]) -// res.PathItems = append(res.PathItems, pathItems[NamespaceIdx]) -// resourceTypeIdx = NamespacedResourceIdx -// } -// -// // Check if the RESOURCETYPE portion match Rules. -// resourceType := pathItems[resourceTypeIdx] -// resourceTypeMatched := true -// for _, rule := range rules.Rules[apiGroupName].ResourceRules { -// if rule.Plural == resourceType { -// res.OrigResourceType = resourceType -// res.PathItems = append(res.PathItems, rules.RenameResource(rule.Plural)) -// resourceTypeMatched = true -// break -// } -// } -// if !resourceTypeMatched { -// return nil, nil -// } -// // Return if RESOURCETYPE is the last item in path. -// if len(pathItems) == resourceTypeIdx+1 { -// return res, nil -// } -// -// // Copy remaining items: NAME and SUBRESOURCE. -// for i := resourceTypeIdx + 1; i < len(pathItems); i++ { -// res.PathItems = append(res.PathItems, pathItems[i]) -// } -// -// return res, nil -//} diff --git a/images/kube-api-proxy/pkg/rewriter/policy.go b/images/kube-api-proxy/pkg/rewriter/policy.go index 68b3b178f..39150a89c 100644 --- a/images/kube-api-proxy/pkg/rewriter/policy.go +++ b/images/kube-api-proxy/pkg/rewriter/policy.go @@ -16,13 +16,13 @@ limitations under the License. package rewriter +import "kube-api-proxy/pkg/rewriter/rules" + const ( PodDisruptionBudgetKind = "PodDisruptionBudget" PodDisruptionBudgetListKind = "PodDisruptionBudgetList" ) -func RewritePDBOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - return RewriteResourceOrList(obj, PodDisruptionBudgetListKind, func(singleObj []byte) ([]byte, error) { - return RewriteLabelsMap(rules, singleObj, "spec.selector", action) - }) +func RewritePDB(rwRules *rules.RewriteRules, pdbObj []byte, action rules.Action) ([]byte, error) { + return RewriteLabelsMap(rwRules, pdbObj, "spec.selector", action) } diff --git a/images/kube-api-proxy/pkg/rewriter/rbac.go b/images/kube-api-proxy/pkg/rewriter/rbac.go index 1d928076c..54fe9a1fe 100644 --- a/images/kube-api-proxy/pkg/rewriter/rbac.go +++ b/images/kube-api-proxy/pkg/rewriter/rbac.go @@ -21,53 +21,40 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" ) const ( - ClusterRoleKind = "ClusterRole" - ClusterRoleListKind = "ClusterRoleList" - RoleKind = "Role" - RoleListKind = "RoleList" - RoleBindingKind = "RoleBinding" - RoleBindingListKind = "RoleBindingList" - ControllerRevisionKind = "ControllerRevision" - ControllerRevisionListKind = "ControllerRevisionList" - ClusterRoleBindingKind = "ClusterRoleBinding" - ClusterRoleBindingListKind = "ClusterRoleBindingList" - APIServiceKind = "APIService" - APIServiceListKind = "APIServiceList" + ClusterRoleKind = "ClusterRole" + ClusterRoleListKind = "ClusterRoleList" + RoleKind = "Role" + RoleListKind = "RoleList" ) -func RewriteClusterRoleOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - if action == Rename { - return RewriteResourceOrList(obj, ClusterRoleListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "rules", func(item []byte) ([]byte, error) { - return renameRoleRule(rules, item) - }) - }) - } - return RewriteResourceOrList(obj, ClusterRoleListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "rules", func(item []byte) ([]byte, error) { - return restoreRoleRule(rules, item) - }) +// RewriteClusterRole rewrites rules array in a ClusterRole. +func RewriteClusterRole(rwRules *rules.RewriteRules, clusterRoleObj []byte, action rules.Action) ([]byte, error) { + return transform.Array(clusterRoleObj, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, action) }) } -func RewriteRoleOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { - if action == Rename { - return RewriteResourceOrList(obj, RoleListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "rules", func(item []byte) ([]byte, error) { - return renameRoleRule(rules, item) - }) - }) - } - return RewriteResourceOrList(obj, RoleListKind, func(singleObj []byte) ([]byte, error) { - return RewriteArray(singleObj, "rules", func(item []byte) ([]byte, error) { - return restoreRoleRule(rules, item) - }) +// RewriteRole rewrites rules array in a namespaced Role. +func RewriteRole(rwRules *rules.RewriteRules, roleObj []byte, action rules.Action) ([]byte, error) { + return transform.Array(roleObj, "rules", func(ruleObj []byte) ([]byte, error) { + return RewriteRoleRule(rwRules, ruleObj, action) }) } +// RewriteRoleRule rewrites apiGroups and resources in a single rule. +func RewriteRoleRule(rwRules *rules.RewriteRules, ruleObj []byte, action rules.Action) ([]byte, error) { + if action == rules.Rename { + return renameRoleRule(rwRules, ruleObj) + } + return restoreRoleRule(rwRules, ruleObj) +} + // renameRoleRule renames apiGroups and resources in a single rule. // Rule examples: // - apiGroups: @@ -87,7 +74,7 @@ func RewriteRoleOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, // - watch // - list // - create -func renameRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { +func renameRoleRule(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { var err error apiGroups := gjson.GetBytes(obj, "apiGroups").Array() @@ -97,8 +84,8 @@ func renameRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { group := apiGroup.String() if group == "*" { shouldRename = true - } else if rules.HasGroup(group) { - group = rules.RenamedGroup + } else if rwRules.HasGroup(group) { + group = rwRules.RenamedGroup shouldRename = true } @@ -120,10 +107,10 @@ func renameRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { resourceType, _, _ = strings.Cut(resource.String(), "/") } if resourceType != "*" { - _, resRule := rules.GroupResourceRules(resourceType) + _, resRule := rwRules.GroupResourceRules(resourceType) if resRule != nil { // TODO(future) make it work with suffix and subresource. - resourceType = rules.RenameResource(resource.String()) + resourceType = rwRules.RenameResource(resource.String()) } } @@ -141,7 +128,7 @@ func renameRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { } // restoreRoleRule restores apiGroups and resources in a single rule. -func restoreRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { +func restoreRoleRule(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { var err error apiGroups := gjson.GetBytes(obj, "apiGroups").Array() @@ -153,7 +140,7 @@ func restoreRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { if group == "*" { shouldRestore = true } - if group == rules.RenamedGroup { + if group == rwRules.RenamedGroup { shouldRestore = true shouldAddGroup = true // Group will be restored later, do not add now. @@ -184,14 +171,14 @@ func restoreRoleRule(rules *RewriteRules, obj []byte) ([]byte, error) { } if resourceType != "*" { // Restore resourceType to get rules. - originalResourceType := rules.RestoreResource(resourceType) - groupRule, resRule := rules.GroupResourceRules(originalResourceType) + originalResourceType := rwRules.RestoreResource(resourceType) + groupRule, resRule := rwRules.GroupResourceRules(originalResourceType) if groupRule != nil && resRule != nil { shouldRestore = true groupToAdd = groupRule.Group // NOTE: Restore resource with subresource. // TODO(future) make it work with suffixes. - newResource = rules.RestoreResource(resource.String()) + newResource = rwRules.RestoreResource(resource.String()) } } diff --git a/images/kube-api-proxy/pkg/rewriter/resource.go b/images/kube-api-proxy/pkg/rewriter/resource.go index 8eef7ade8..1bf9027dd 100644 --- a/images/kube-api-proxy/pkg/rewriter/resource.go +++ b/images/kube-api-proxy/pkg/rewriter/resource.go @@ -21,38 +21,40 @@ import ( "github.com/tidwall/gjson" "github.com/tidwall/sjson" + + "kube-api-proxy/pkg/rewriter/rules" ) -func RewriteCustomResourceOrList(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { +func RewriteCustomResourceOrList(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { kind := gjson.GetBytes(obj, "kind").String() - if action == Restore { - kind = rules.RestoreKind(kind) + if action == rules.Restore { + kind = rwRules.RestoreKind(kind) } - origGroupName, origResName, isList := rules.ResourceByKind(kind) + origGroupName, origResName, isList := rwRules.ResourceByKind(kind) if origGroupName == "" && origResName == "" { // Return as-is if kind is not in rules. return obj, nil } if isList { - if action == Restore { - return RestoreResourcesList(rules, obj, origGroupName) + if action == rules.Restore { + return RestoreResourcesList(rwRules, obj, origGroupName) } - return RenameResourcesList(rules, obj) + return RenameResourcesList(rwRules, obj) } // Responses of GET, LIST, DELETE requests. // AdmissionReview requests from API Server. - if action == Restore { - return RestoreResource(rules, obj, origGroupName) + if action == rules.Restore { + return RestoreResource(rwRules, obj, origGroupName) } // CREATE, UPDATE, PATCH requests. // TODO need to implement for - return RenameResource(rules, obj) + return RenameResource(rwRules, obj) } -func RenameResourcesList(rules *RewriteRules, obj []byte) ([]byte, error) { - obj, err := RenameAPIVersionAndKind(rules, obj) +func RenameResourcesList(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { + obj, err := RenameAPIVersionAndKind(rwRules, obj) if err != nil { return nil, err } @@ -61,7 +63,7 @@ func RenameResourcesList(rules *RewriteRules, obj []byte) ([]byte, error) { items := gjson.GetBytes(obj, "items").Array() rwrItems := []byte(`[]`) for _, item := range items { - rwrItem, err := RenameResource(rules, []byte(item.Raw)) + rwrItem, err := RenameResource(rwRules, []byte(item.Raw)) if err != nil { return nil, err } @@ -76,8 +78,8 @@ func RenameResourcesList(rules *RewriteRules, obj []byte) ([]byte, error) { return obj, nil } -func RestoreResourcesList(rules *RewriteRules, obj []byte, origGroupName string) ([]byte, error) { - obj, err := RestoreAPIVersionAndKind(rules, obj, origGroupName) +func RestoreResourcesList(rwRules *rules.RewriteRules, obj []byte, origGroupName string) ([]byte, error) { + obj, err := RestoreAPIVersionAndKind(rwRules, obj, origGroupName) if err != nil { return nil, err } @@ -86,7 +88,7 @@ func RestoreResourcesList(rules *RewriteRules, obj []byte, origGroupName string) items := gjson.GetBytes(obj, "items").Array() rwrItems := []byte(`[]`) for _, item := range items { - rwrItem, err := RestoreResource(rules, []byte(item.Raw), origGroupName) + rwrItem, err := RestoreResource(rwRules, []byte(item.Raw), origGroupName) if err != nil { return nil, err } @@ -101,60 +103,60 @@ func RestoreResourcesList(rules *RewriteRules, obj []byte, origGroupName string) return obj, nil } -func RenameResource(rules *RewriteRules, obj []byte) ([]byte, error) { - obj, err := RenameAPIVersionAndKind(rules, obj) +func RenameResource(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { + obj, err := RenameAPIVersionAndKind(rwRules, obj) if err != nil { return nil, err } // Rewrite apiVersion in each managedFields. - obj, err = RenameManagedFields(rules, obj) + obj, err = RenameManagedFields(rwRules, obj) if err != nil { return nil, err } - return RenameOwnerReferences(rules, obj) + return RenameOwnerReferences(rwRules, obj) } -func RestoreResource(rules *RewriteRules, obj []byte, origGroupName string) ([]byte, error) { - obj, err := RestoreAPIVersionAndKind(rules, obj, origGroupName) +func RestoreResource(rwRules *rules.RewriteRules, obj []byte, origGroupName string) ([]byte, error) { + obj, err := RestoreAPIVersionAndKind(rwRules, obj, origGroupName) if err != nil { return nil, err } // Rewrite apiVersion in each managedFields. - obj, err = RestoreManagedFields(rules, obj, origGroupName) + obj, err = RestoreManagedFields(rwRules, obj, origGroupName) if err != nil { return nil, err } - return RestoreOwnerReferences(rules, obj, origGroupName) + return RestoreOwnerReferences(rwRules, obj, origGroupName) } -func RenameAPIVersionAndKind(rules *RewriteRules, obj []byte) ([]byte, error) { +func RenameAPIVersionAndKind(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { apiVersion := gjson.GetBytes(obj, "apiVersion").String() - obj, err := sjson.SetBytes(obj, "apiVersion", rules.RenameApiVersion(apiVersion)) + obj, err := sjson.SetBytes(obj, "apiVersion", rwRules.RenameApiVersion(apiVersion)) if err != nil { return nil, err } kind := gjson.GetBytes(obj, "kind").String() - return sjson.SetBytes(obj, "kind", rules.RenameKind(kind)) + return sjson.SetBytes(obj, "kind", rwRules.RenameKind(kind)) } -func RestoreAPIVersionAndKind(rules *RewriteRules, obj []byte, origGroupName string) ([]byte, error) { +func RestoreAPIVersionAndKind(rwRules *rules.RewriteRules, obj []byte, origGroupName string) ([]byte, error) { apiVersion := gjson.GetBytes(obj, "apiVersion").String() - apiVersion = rules.RestoreApiVersion(apiVersion, origGroupName) + apiVersion = rwRules.RestoreApiVersion(apiVersion, origGroupName) obj, err := sjson.SetBytes(obj, "apiVersion", apiVersion) if err != nil { return nil, err } kind := gjson.GetBytes(obj, "kind").String() - return sjson.SetBytes(obj, "kind", rules.RestoreKind(kind)) + return sjson.SetBytes(obj, "kind", rwRules.RestoreKind(kind)) } -func RewriteOwnerReferences(rules *RewriteRules, obj []byte, action Action) ([]byte, error) { +func RewriteOwnerReferences(rwRules *rules.RewriteRules, obj []byte, action rules.Action) ([]byte, error) { ownerRefs := gjson.GetBytes(obj, "metadata.ownerReferences").Array() if len(ownerRefs) == 0 { return obj, nil @@ -164,11 +166,11 @@ func RewriteOwnerReferences(rules *RewriteRules, obj []byte, action Action) ([]b rewritten := false for _, ownerRef := range ownerRefs { kind := ownerRef.Get("kind").String() - if action == Restore { - kind = rules.RestoreKind(kind) + if action == rules.Restore { + kind = rwRules.RestoreKind(kind) } // Find if kind should be rewritten. - origGroup, origResource, _ := rules.ResourceByKind(kind) + origGroup, origResource, _ := rwRules.ResourceByKind(kind) if origGroup == "" && origResource == "" { // There is no rewrite rule for kind, append ownerRef as is. var err error @@ -178,8 +180,8 @@ func RewriteOwnerReferences(rules *RewriteRules, obj []byte, action Action) ([]b } continue } - if action == Rename { - kind = rules.RenameKind(kind) + if action == rules.Rename { + kind = rwRules.RenameKind(kind) } rwrOwnerRef := []byte(ownerRef.Raw) @@ -190,11 +192,11 @@ func RewriteOwnerReferences(rules *RewriteRules, obj []byte, action Action) ([]b } apiVersion := ownerRef.Get("apiVersion").String() - if action == Restore { - apiVersion = rules.RestoreApiVersion(apiVersion, origGroup) + if action == rules.Restore { + apiVersion = rwRules.RestoreApiVersion(apiVersion, origGroup) } - if action == Rename { - apiVersion = rules.RenameApiVersion(apiVersion) + if action == rules.Rename { + apiVersion = rwRules.RenameApiVersion(apiVersion) } rwrOwnerRef, err = sjson.SetBytes(rwrOwnerRef, "apiVersion", apiVersion) if err != nil { @@ -212,7 +214,7 @@ func RewriteOwnerReferences(rules *RewriteRules, obj []byte, action Action) ([]b } // RenameOwnerReferences renames kind and apiVersion to send request to server. -func RenameOwnerReferences(rules *RewriteRules, obj []byte) ([]byte, error) { +func RenameOwnerReferences(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { ownerRefs := gjson.GetBytes(obj, "metadata.ownerReferences").Array() if len(ownerRefs) == 0 { return obj, nil @@ -226,15 +228,15 @@ func RenameOwnerReferences(rules *RewriteRules, obj []byte) ([]byte, error) { rwrOwnerRef := []byte(ownerRef.Raw) - _, resRule := rules.KindRules(apiVersion, kind) + _, resRule := rwRules.KindRules(apiVersion, kind) if resRule != nil { // Rename apiVersion and kind if resource has renaming rules. - rwrOwnerRef, err = sjson.SetBytes(rwrOwnerRef, "kind", rules.RenameKind(kind)) + rwrOwnerRef, err = sjson.SetBytes(rwrOwnerRef, "kind", rwRules.RenameKind(kind)) if err != nil { return nil, err } - rwrOwnerRef, err = sjson.SetBytes(rwrOwnerRef, "apiVersion", rules.RenameApiVersion(apiVersion)) + rwrOwnerRef, err = sjson.SetBytes(rwrOwnerRef, "apiVersion", rwRules.RenameApiVersion(apiVersion)) if err != nil { return nil, err } @@ -265,7 +267,7 @@ func RenameOwnerReferences(rules *RewriteRules, obj []byte) ([]byte, error) { // kind: VirtualMachine <--- restore kind // name: cloud-alpine // uid: 4c74c3ff-2199-4f20-a71c-3b0e5fb505ca -func RestoreOwnerReferences(rules *RewriteRules, obj []byte, groupName string) ([]byte, error) { +func RestoreOwnerReferences(rwRules *rules.RewriteRules, obj []byte, groupName string) ([]byte, error) { ownerRefs := gjson.GetBytes(obj, "metadata.ownerReferences").Array() if len(ownerRefs) == 0 { return obj, nil @@ -276,13 +278,13 @@ func RestoreOwnerReferences(rules *RewriteRules, obj []byte, groupName string) ( apiVersion := ownerRef.Get("apiVersion").String() rOwnerRef := []byte(ownerRef.Raw) var err error - if strings.HasPrefix(apiVersion, rules.RenamedGroup) { - rOwnerRef, err = sjson.SetBytes([]byte(ownerRef.Raw), "apiVersion", rules.RestoreApiVersion(apiVersion, groupName)) + if strings.HasPrefix(apiVersion, rwRules.RenamedGroup) { + rOwnerRef, err = sjson.SetBytes([]byte(ownerRef.Raw), "apiVersion", rwRules.RestoreApiVersion(apiVersion, groupName)) if err != nil { return nil, err } kind := gjson.GetBytes(rOwnerRef, "kind").String() - rOwnerRef, err = sjson.SetBytes(rOwnerRef, "kind", rules.RestoreKind(kind)) + rOwnerRef, err = sjson.SetBytes(rOwnerRef, "kind", rwRules.RestoreKind(kind)) if err != nil { return nil, err } @@ -305,7 +307,7 @@ func RestoreOwnerReferences(rules *RewriteRules, obj []byte, groupName string) ( // { "apiVersion":"x.virtualization.deckhouse.io/v1", "fieldsType":"FieldsV1", "fieldsV1":{ ... }}, "manager": "Go-http-client", ...}, // { "apiVersion":"x.virtualization.deckhouse.io/v1", "fieldsType":"FieldsV1", "fieldsV1":{ ... }}, "manager": "kubectl-edit", ...} // ], -func RestoreManagedFields(rules *RewriteRules, obj []byte, origGroupName string) ([]byte, error) { +func RestoreManagedFields(rwRules *rules.RewriteRules, obj []byte, origGroupName string) ([]byte, error) { mgFields := gjson.GetBytes(obj, "metadata.managedFields") if !mgFields.Exists() || len(mgFields.Array()) == 0 { return obj, nil @@ -314,7 +316,7 @@ func RestoreManagedFields(rules *RewriteRules, obj []byte, origGroupName string) newFields := []byte(`[]`) for _, mgField := range mgFields.Array() { apiVersion := mgField.Get("apiVersion").String() - restoredAPIVersion := rules.RestoreApiVersion(apiVersion, origGroupName) + restoredAPIVersion := rwRules.RestoreApiVersion(apiVersion, origGroupName) newField, err := sjson.SetBytes([]byte(mgField.Raw), "apiVersion", restoredAPIVersion) if err != nil { return nil, err @@ -336,7 +338,7 @@ func RestoreManagedFields(rules *RewriteRules, obj []byte, origGroupName string) // { "apiVersion":"kubevirt.io/v1", "fieldsType":"FieldsV1", "fieldsV1":{ ... }}, "manager": "Go-http-client", ...}, // { "apiVersion":"kubevirt.io/v1", "fieldsType":"FieldsV1", "fieldsV1":{ ... }}, "manager": "kubectl-edit", ...} // ], -func RenameManagedFields(rules *RewriteRules, obj []byte) ([]byte, error) { +func RenameManagedFields(rwRules *rules.RewriteRules, obj []byte) ([]byte, error) { mgFields := gjson.GetBytes(obj, "metadata.managedFields") if !mgFields.Exists() || len(mgFields.Array()) == 0 { return obj, nil @@ -345,7 +347,7 @@ func RenameManagedFields(rules *RewriteRules, obj []byte) ([]byte, error) { newFields := []byte(`[]`) for _, mgField := range mgFields.Array() { apiVersion := mgField.Get("apiVersion").String() - renamedAPIVersion := rules.RenameApiVersion(apiVersion) + renamedAPIVersion := rwRules.RenameApiVersion(apiVersion) newField, err := sjson.SetBytes([]byte(mgField.Raw), "apiVersion", renamedAPIVersion) if err != nil { return nil, err @@ -358,6 +360,6 @@ func RenameManagedFields(rules *RewriteRules, obj []byte) ([]byte, error) { return sjson.SetRawBytes(obj, "metadata.managedFields", newFields) } -func RenameResourcePatch(rules *RewriteRules, patch []byte) ([]byte, error) { - return RenameMetadataPatch(rules, patch) +func RenameResourcePatch(rwRules *rules.RewriteRules, patch []byte) ([]byte, error) { + return RenameMetadataPatch(rwRules, patch) } diff --git a/images/kube-api-proxy/pkg/rewriter/resource_test.go b/images/kube-api-proxy/pkg/rewriter/resource_test.go index 05201aff9..0d692c7dc 100644 --- a/images/kube-api-proxy/pkg/rewriter/resource_test.go +++ b/images/kube-api-proxy/pkg/rewriter/resource_test.go @@ -24,6 +24,8 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" + + "kube-api-proxy/pkg/rewriter/rules" ) func TestRewriteMetadata(t *testing.T) { @@ -31,12 +33,12 @@ func TestRewriteMetadata(t *testing.T) { name string obj client.Object newObj client.Object - action Action + action rules.Action expectLabels map[string]string expectAnnotations map[string]string }{ { - "", + "rename labels on Pod", &corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", @@ -46,18 +48,23 @@ func TestRewriteMetadata(t *testing.T) { Name: "foo", Namespace: "bar", Labels: map[string]string{ - "original.label.io": "labelvalue", - "original.prefix/labelkey": "labelvalue", + "labelgroup.io": "labelvalue", + "component.labelgroup.io/labelkey": "labelvalue", }, Annotations: map[string]string{ - "original.annotation.io": "annovalue", + "annogroup.io": "annovalue", }, }, }, &corev1.Pod{}, - Rename, - map[string]string{"rewrite.label.io": "labelvalue", "rewrite.prefix/labelkey": "labelvalue"}, - map[string]string{"rewrite.annotation.io": "annovalue"}, + rules.Rename, + map[string]string{ + "replacedlabelgroup.io": "labelvalue", + "component.replacedlabelgroup.io/labelkey": "labelvalue", + }, + map[string]string{ + "replacedanno.io": "annovalue", + }, }, } diff --git a/images/kube-api-proxy/pkg/rewriter/rule_rewriter.go b/images/kube-api-proxy/pkg/rewriter/rule_rewriter.go index ed8b68212..1c9e94277 100644 --- a/images/kube-api-proxy/pkg/rewriter/rule_rewriter.go +++ b/images/kube-api-proxy/pkg/rewriter/rule_rewriter.go @@ -23,21 +23,15 @@ import ( "github.com/tidwall/gjson" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "kube-api-proxy/pkg/rewriter/rules" + "kube-api-proxy/pkg/rewriter/transform" ) type RuleBasedRewriter struct { - Rules *RewriteRules + Rules *rules.RewriteRules } -type Action string - -const ( - // Restore is an action to restore resources to original. - Restore Action = "restore" - // Rename is an action to rename original resources. - Rename Action = "rename" -) - // RewriteAPIEndpoint renames group and resource in /apis/* endpoints. // It assumes that ep contains original group and resourceType. // Restoring of path is not implemented. @@ -146,7 +140,7 @@ func (rw *RuleBasedRewriter) rewriteFieldSelector(rawQuery string) string { return metadataNameRe.ReplaceAllString(rawQuery, newSelector) } -// rewriteLabelSelector rewrites labels in labelSelector +// rewriteLabelSelector rewrites labels in a labelSelector query // Example request: // https:///apis/apps/v1/namespaces//deployments?labelSelector=app%3Dsomething func (rw *RuleBasedRewriter) rewriteLabelSelector(rawQuery string) string { @@ -195,28 +189,13 @@ func (rw *RuleBasedRewriter) rewriteLabelSelector(rawQuery string) string { } // RewriteJSONPayload does rewrite based on kind. -func (rw *RuleBasedRewriter) RewriteJSONPayload(targetReq *TargetRequest, obj []byte, action Action) ([]byte, error) { +func (rw *RuleBasedRewriter) RewriteJSONPayload(targetReq *TargetRequest, obj []byte, action rules.Action) ([]byte, error) { // Detect Kind kind := gjson.GetBytes(obj, "kind").String() var rwrBytes []byte var err error - //// Handle core resources: rewrite only for specific kind. - //if targetReq.IsCore() { - // pass := true - // switch kind { - // case "APIGroupList": - // case "APIGroup": - // case "APIResourceList": - // default: - // pass = shouldPassCoreResource(kind) - // } - // if pass { - // return obj, nil - // } - //} - switch kind { case "APIGroupList": rwrBytes, err = RewriteAPIGroupList(rw.Rules, obj) @@ -234,40 +213,76 @@ func (rw *RuleBasedRewriter) RewriteJSONPayload(targetReq *TargetRequest, obj [] rwrBytes, err = RewriteAdmissionReview(rw.Rules, obj, targetReq.OrigGroup()) case CRDKind, CRDListKind: - rwrBytes, err = RewriteCRDOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteCRD(rw.Rules, resourceObj, action) + }) case MutatingWebhookConfigurationKind, MutatingWebhookConfigurationListKind: - rwrBytes, err = RewriteMutatingOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteMutating(rw.Rules, resourceObj, action) + }) case ValidatingWebhookConfigurationKind, ValidatingWebhookConfigurationListKind: - rwrBytes, err = RewriteValidatingOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteValidating(rw.Rules, resourceObj, action) + }) case ClusterRoleKind, ClusterRoleListKind: - rwrBytes, err = RewriteClusterRoleOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteClusterRole(rw.Rules, resourceObj, action) + }) case RoleKind, RoleListKind: - rwrBytes, err = RewriteRoleOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteRole(rw.Rules, resourceObj, action) + }) + case DeploymentKind, DeploymentListKind: - rwrBytes, err = RewriteDeploymentOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteDeployment(rw.Rules, resourceObj, action) + }) + case StatefulSetKind, StatefulSetListKind: - rwrBytes, err = RewriteStatefulSetOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteStatefulSet(rw.Rules, resourceObj, action) + }) + case DaemonSetKind, DaemonSetListKind: - rwrBytes, err = RewriteDaemonSetOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteDaemonSet(rw.Rules, resourceObj, action) + }) + case PodKind, PodListKind: - rwrBytes, err = RewritePodOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewritePod(rw.Rules, resourceObj, action) + }) + case PodDisruptionBudgetKind, PodDisruptionBudgetListKind: - rwrBytes, err = RewritePDBOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewritePDB(rw.Rules, resourceObj, action) + }) + case JobKind, JobListKind: - rwrBytes, err = RewriteJobOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteJob(rw.Rules, resourceObj, action) + }) + case ServiceKind, ServiceListKind: - rwrBytes, err = RewriteServiceOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteService(rw.Rules, resourceObj, action) + }) + case PersistentVolumeClaimKind, PersistentVolumeClaimListKind: - rwrBytes, err = RewritePVCOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewritePVC(rw.Rules, resourceObj, action) + }) case ServiceMonitorKind, ServiceMonitorListKind: - rwrBytes, err = RewriteServiceMonitorOrList(rw.Rules, obj, action) + rwrBytes, err = transform.ResourceOrList(obj, func(resourceObj []byte) ([]byte, error) { + return RewriteServiceMonitor(rw.Rules, resourceObj, action) + }) default: // TODO Add rw.Rules.IsKnownKind() to rewrite only known kinds. @@ -280,13 +295,13 @@ func (rw *RuleBasedRewriter) RewriteJSONPayload(targetReq *TargetRequest, obj [] // Always rewrite metadata: labels, annotations, finalizers, ownerReferences. // TODO: add rewriter for managedFields. - return RewriteResourceOrList2(rwrBytes, func(singleObj []byte) ([]byte, error) { + return transform.ResourceOrList(rwrBytes, func(resourceObj []byte) ([]byte, error) { var err error - singleObj, err = RewriteMetadata(rw.Rules, singleObj, action) + resourceObj, err = RewriteMetadata(rw.Rules, resourceObj, action) if err != nil { return nil, err } - return RewriteOwnerReferences(rw.Rules, singleObj, action) + return RewriteOwnerReferences(rw.Rules, resourceObj, action) }) } @@ -320,32 +335,3 @@ func (rw *RuleBasedRewriter) RewritePatch(targetReq *TargetRequest, patchBytes [ return patchBytes, nil } - -func shouldRewriteOwnerReferences(resourceType string) bool { - switch resourceType { - case CRDKind, CRDListKind, - RoleKind, RoleListKind, - RoleBindingKind, RoleBindingListKind, - PodDisruptionBudgetKind, PodDisruptionBudgetListKind, - ControllerRevisionKind, ControllerRevisionListKind, - ClusterRoleKind, ClusterRoleListKind, - ClusterRoleBindingKind, ClusterRoleBindingListKind, - APIServiceKind, APIServiceListKind, - DeploymentKind, DeploymentListKind, - DaemonSetKind, DaemonSetListKind, - StatefulSetKind, StatefulSetListKind, - PodKind, PodListKind, - JobKind, JobListKind, - ValidatingWebhookConfigurationKind, - ValidatingWebhookConfigurationListKind, - MutatingWebhookConfigurationKind, - MutatingWebhookConfigurationListKind, - ServiceKind, ServiceListKind, - PersistentVolumeClaimKind, PersistentVolumeClaimListKind, - PrometheusRuleKind, PrometheusRuleListKind, - ServiceMonitorKind, ServiceMonitorListKind: - return true - } - - return false -} diff --git a/images/kube-api-proxy/pkg/rewriter/rule_rewriter_test.go b/images/kube-api-proxy/pkg/rewriter/rule_rewriter_test.go index e2ca2d8d7..95057c095 100644 --- a/images/kube-api-proxy/pkg/rewriter/rule_rewriter_test.go +++ b/images/kube-api-proxy/pkg/rewriter/rule_rewriter_test.go @@ -25,6 +25,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + + . "kube-api-proxy/pkg/rewriter/rules" ) func createTestRewriter() *RuleBasedRewriter { diff --git a/images/kube-api-proxy/pkg/rewriter/rules/action.go b/images/kube-api-proxy/pkg/rewriter/rules/action.go new file mode 100644 index 000000000..ba90b141f --- /dev/null +++ b/images/kube-api-proxy/pkg/rewriter/rules/action.go @@ -0,0 +1,8 @@ +package rules + +type Action string + +const ( + Rename Action = "Rename" + Restore Action = "Restore" +) diff --git a/images/kube-api-proxy/pkg/rewriter/load.go b/images/kube-api-proxy/pkg/rewriter/rules/load.go similarity index 93% rename from images/kube-api-proxy/pkg/rewriter/load.go rename to images/kube-api-proxy/pkg/rewriter/rules/load.go index f44514abb..ea6dd16e0 100644 --- a/images/kube-api-proxy/pkg/rewriter/load.go +++ b/images/kube-api-proxy/pkg/rewriter/rules/load.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package rewriter +package rules import ( "os" @@ -22,6 +22,8 @@ import ( "sigs.k8s.io/yaml" ) +// WARNING: just a PoC, not working yet. + func LoadRules(filename string) (*RewriteRules, error) { data, err := os.ReadFile(filename) if err != nil { diff --git a/images/kube-api-proxy/pkg/rewriter/prefixed_name_rewriter.go b/images/kube-api-proxy/pkg/rewriter/rules/prefixed_name_rewriter.go similarity index 99% rename from images/kube-api-proxy/pkg/rewriter/prefixed_name_rewriter.go rename to images/kube-api-proxy/pkg/rewriter/rules/prefixed_name_rewriter.go index 191553747..49cbf0fbf 100644 --- a/images/kube-api-proxy/pkg/rewriter/prefixed_name_rewriter.go +++ b/images/kube-api-proxy/pkg/rewriter/rules/prefixed_name_rewriter.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package rewriter +package rules import "strings" diff --git a/images/kube-api-proxy/pkg/rewriter/rules.go b/images/kube-api-proxy/pkg/rewriter/rules/rules.go similarity index 99% rename from images/kube-api-proxy/pkg/rewriter/rules.go rename to images/kube-api-proxy/pkg/rewriter/rules/rules.go index 0bb1b1f84..f9c317dd8 100644 --- a/images/kube-api-proxy/pkg/rewriter/rules.go +++ b/images/kube-api-proxy/pkg/rewriter/rules/rules.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package rewriter +package rules import ( "strings" diff --git a/images/kube-api-proxy/pkg/rewriter/target_request.go b/images/kube-api-proxy/pkg/rewriter/target_request.go index f4467bc30..5fc72b6be 100644 --- a/images/kube-api-proxy/pkg/rewriter/target_request.go +++ b/images/kube-api-proxy/pkg/rewriter/target_request.go @@ -19,13 +19,15 @@ package rewriter import ( "fmt" "net/http" + + "kube-api-proxy/pkg/rewriter/rules" ) type TargetRequest struct { originEndpoint *APIEndpoint targetEndpoint *APIEndpoint - webhookRule *WebhookRule + webhookRule *rules.WebhookRule } func NewTargetRequest(rwr *RuleBasedRewriter, req *http.Request) *TargetRequest { diff --git a/images/kube-api-proxy/pkg/rewriter/transform/json.go b/images/kube-api-proxy/pkg/rewriter/transform/json.go new file mode 100644 index 000000000..883651344 --- /dev/null +++ b/images/kube-api-proxy/pkg/rewriter/transform/json.go @@ -0,0 +1,34 @@ +package transform + +import ( + "encoding/json" + + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +// Helpers for traversing JSON objects with support for root path. +// gjson supports @this, but sjson don't, so unique alias is used. + +const Root = "@ROOT" + +func GetBytes(obj []byte, path string) gjson.Result { + if path == Root { + return gjson.ParseBytes(obj) + } + return gjson.GetBytes(obj, path) +} + +func SetBytes(obj []byte, path string, value interface{}) ([]byte, error) { + if path == Root { + return json.Marshal(value) + } + return sjson.SetBytes(obj, path, value) +} + +func SetRawBytes(obj []byte, path string, value []byte) ([]byte, error) { + if path == Root { + return value, nil + } + return sjson.SetRawBytes(obj, path, value) +} diff --git a/images/kube-api-proxy/pkg/rewriter/transform/kubernetes.go b/images/kube-api-proxy/pkg/rewriter/transform/kubernetes.go new file mode 100644 index 000000000..f42cf54a0 --- /dev/null +++ b/images/kube-api-proxy/pkg/rewriter/transform/kubernetes.go @@ -0,0 +1,43 @@ +package transform + +import ( + "strings" + + "github.com/tidwall/gjson" +) + +// ResourceOrList is a helper to transform a single resource or a list of resources. +// It assumes that obj has a "kind" field and a list kind has "List" suffix. +func ResourceOrList(obj []byte, transformFn func(singleObj []byte) ([]byte, error)) ([]byte, error) { + kind := gjson.GetBytes(obj, "kind").String() + if kind == "" { + return obj, nil + } + if !strings.HasSuffix(kind, "List") { + return transformFn(obj) + } + return Array(obj, "items", transformFn) +} + +// Patch treats obj as a JSON patch or Merge patch and calls +// a corresponding transformFn. +func Patch( + obj []byte, + transformMerge func(mergePatch []byte) ([]byte, error), + transformJSON func(jsonPatch []byte) ([]byte, error)) ([]byte, error) { + if len(obj) == 0 { + return obj, nil + } + // Merge patch for Kubernetes resource is always starts with the curly bracket. + if string(obj[0]) == "{" && transformMerge != nil { + return transformMerge(obj) + } + + // JSON patch should start with the square bracket. + if string(obj[0]) == "[" && transformJSON != nil { + return Array(obj, Root, transformJSON) + } + + // Return patch as-is in other cases. + return obj, nil +} diff --git a/images/kube-api-proxy/pkg/rewriter/transform/traverse.go b/images/kube-api-proxy/pkg/rewriter/transform/traverse.go new file mode 100644 index 000000000..12d227828 --- /dev/null +++ b/images/kube-api-proxy/pkg/rewriter/transform/traverse.go @@ -0,0 +1,92 @@ +package transform + +// String transforms string value addressed by the path. +func String(obj []byte, path string, transformFn func(field string) string) ([]byte, error) { + pathStr := GetBytes(obj, path) + if !pathStr.Exists() { + return obj, nil + } + rwrString := transformFn(pathStr.String()) + return SetBytes(obj, path, rwrString) +} + +// Object transforms object value addressed by the path. +func Object(obj []byte, path string, transformFn func(item []byte) ([]byte, error)) ([]byte, error) { + pathObj := GetBytes(obj, path) + if !pathObj.IsObject() { + return obj, nil + } + rwrObj, err := transformFn([]byte(pathObj.Raw)) + if err != nil { + return nil, err + } + return SetRawBytes(obj, path, rwrObj) +} + +// MapStringString transforms map[string]string value addressed by path. +func MapStringString(obj []byte, mapPath string, transformFn func(k, v string) (string, string)) ([]byte, error) { + m := GetBytes(obj, mapPath).Map() + if len(m) == 0 { + return obj, nil + } + newMap := make(map[string]string, len(m)) + for k, v := range m { + newK, newV := transformFn(k, v.String()) + newMap[newK] = newV + } + + return SetBytes(obj, mapPath, newMap) +} + +// Array gets array by the path and transforms each item using transformFn. +// Use Root path to transform object itself. +func Array(obj []byte, arrayPath string, transformFn func(item []byte) ([]byte, error)) ([]byte, error) { + // Transform each item in list. Put back original items if transformFn returns nil bytes. + items := GetBytes(obj, arrayPath).Array() + if len(items) == 0 { + return obj, nil + } + rwrItems := []byte(`[]`) + for _, item := range items { + rwrItem, err := transformFn([]byte(item.Raw)) + if err != nil { + return nil, err + } + // Put original item back. + if rwrItem == nil { + rwrItem = []byte(item.Raw) + } + rwrItems, err = SetRawBytes(rwrItems, "-1", rwrItem) + if err != nil { + return nil, err + } + } + + return SetRawBytes(obj, arrayPath, rwrItems) +} + +// ArrayOfStrings transforms array of strings addressed by the path. +func ArrayOfStrings(obj []byte, arrayPath string, transformFn func(item string) string) ([]byte, error) { + // Transform each item in list. Put back original items if transformFn returns nil bytes. + items := GetBytes(obj, arrayPath).Array() + if len(items) == 0 { + return obj, nil + } + rwrItems := make([]string, len(items)) + for i, item := range items { + rwrItems[i] = transformFn(item.String()) + } + + return SetBytes(obj, arrayPath, rwrItems) +} + +func Apply(obj []byte, transformFns ...func(obj []byte) ([]byte, error)) ([]byte, error) { + var err error + for _, fn := range transformFns { + obj, err = fn(obj) + if err != nil { + return nil, err + } + } + return obj, nil +} diff --git a/images/kube-api-proxy/pkg/rewriter/transformers.go b/images/kube-api-proxy/pkg/rewriter/transformers.go deleted file mode 100644 index ef68ec873..000000000 --- a/images/kube-api-proxy/pkg/rewriter/transformers.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2024 Flant JSC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rewriter - -import ( - "encoding/json" - - "github.com/tidwall/gjson" - "github.com/tidwall/sjson" -) - -// TransformString transforms string value addressed by path. -func TransformString(obj []byte, path string, transformFn func(field string) string) ([]byte, error) { - pathStr := gjson.GetBytes(obj, path) - if !pathStr.Exists() { - return obj, nil - } - rwrString := transformFn(pathStr.String()) - return sjson.SetBytes(obj, path, rwrString) -} - -// TransformObject transforms object value addressed by path. -func TransformObject(obj []byte, path string, transformFn func(item []byte) ([]byte, error)) ([]byte, error) { - pathObj := gjson.GetBytes(obj, path) - if !pathObj.IsObject() { - return obj, nil - } - rwrObj, err := transformFn([]byte(pathObj.Raw)) - if err != nil { - return nil, err - } - return sjson.SetRawBytes(obj, path, rwrObj) -} - -// TransformArrayOfStrings transforms array value addressed by path. -func TransformArrayOfStrings(obj []byte, arrayPath string, transformFn func(item string) string) ([]byte, error) { - // Transform each item in list. Put back original items if transformFn returns nil bytes. - items := gjson.GetBytes(obj, arrayPath).Array() - if len(items) == 0 { - return obj, nil - } - rwrItems := make([]string, len(items)) - for i, item := range items { - rwrItems[i] = transformFn(item.String()) - } - - return sjson.SetBytes(obj, arrayPath, rwrItems) -} - -// TransformPatch treats obj as a JSON patch or Merge patch and calls -// a corresponding transformFn. -func TransformPatch( - obj []byte, - transformMerge func(mergePatch []byte) ([]byte, error), - transformJSON func(jsonPatch []byte) ([]byte, error)) ([]byte, error) { - if len(obj) == 0 { - return obj, nil - } - // Merge patch for Kubernetes resource is always starts with the curly bracket. - if string(obj[0]) == "{" && transformMerge != nil { - return transformMerge(obj) - } - - // JSON patch should start with the square bracket. - if string(obj[0]) == "[" && transformJSON != nil { - return RewriteArray(obj, Root, transformJSON) - } - - // Return patch as-is in other cases. - return obj, nil -} - -// Helpers for traversing JSON objects with support for root path. -// gjson supports @this, but sjson don't, so unique alias is used. - -const Root = "@ROOT" - -func GetBytes(obj []byte, path string) gjson.Result { - if path == Root { - return gjson.ParseBytes(obj) - } - return gjson.GetBytes(obj, path) -} - -func SetBytes(obj []byte, path string, value interface{}) ([]byte, error) { - if path == Root { - return json.Marshal(value) - } - return sjson.SetBytes(obj, path, value) -} - -func SetRawBytes(obj []byte, path string, value []byte) ([]byte, error) { - if path == Root { - return value, nil - } - return sjson.SetRawBytes(obj, path, value) -} diff --git a/images/kube-api-proxy/pkg/rewriter/webhook.go b/images/kube-api-proxy/pkg/rewriter/webhook.go deleted file mode 100644 index dfa3c6270..000000000 --- a/images/kube-api-proxy/pkg/rewriter/webhook.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2024 Flant JSC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rewriter