mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-04-04 15:58:49 +02:00
* Add jqPathExpression field and implementation for ignoreDifferences Signed-off-by: Jake Utley <jutley@hiya.com> * Add a couple tests for jqPathExpression Signed-off-by: Jake Utley <jutley@hiya.com> * Add JQPathExpressions into relevant types in types.go Signed-off-by: Jake Utley <jutley@hiya.com> * Add documentation for jq path expressions Signed-off-by: Jake Utley <jutley@hiya.com> * Add Hiya to USERS.md Signed-off-by: Jake Utley <jutley@hiya.com> * Update generated code Signed-off-by: Jake Utley <jutley@hiya.com> * Do not require jsonPointers or jqPathExpressions Signed-off-by: Jake Utley <jutley@hiya.com> * Fix some linting issues Signed-off-by: Jake Utley <jutley@hiya.com> * Add test for valid jq that produces error Signed-off-by: Jake Utley <jutley@hiya.com> * Add test to correctly parse jqPathExpressions from split key configs Signed-off-by: Jake Utley <jutley@hiya.com> * regen Signed-off-by: Jake Utley <jutley@hiya.com>
191 lines
5.0 KiB
Go
191 lines
5.0 KiB
Go
package normalizers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/argoproj/gitops-engine/pkg/diff"
|
|
jsonpatch "github.com/evanphx/json-patch"
|
|
"github.com/itchyny/gojq"
|
|
log "github.com/sirupsen/logrus"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
|
"github.com/argoproj/argo-cd/v2/util/glob"
|
|
)
|
|
|
|
type normalizerPatch interface {
|
|
GetGroupKind() schema.GroupKind
|
|
GetNamespace() string
|
|
GetName() string
|
|
// Apply(un *unstructured.Unstructured) (error)
|
|
Apply(data []byte) ([]byte, error)
|
|
}
|
|
|
|
type baseNormalizerPatch struct {
|
|
groupKind schema.GroupKind
|
|
namespace string
|
|
name string
|
|
}
|
|
|
|
func (np *baseNormalizerPatch) GetGroupKind() schema.GroupKind {
|
|
return np.groupKind
|
|
}
|
|
|
|
func (np *baseNormalizerPatch) GetNamespace() string {
|
|
return np.namespace
|
|
}
|
|
|
|
func (np *baseNormalizerPatch) GetName() string {
|
|
return np.name
|
|
}
|
|
|
|
type jsonPatchNormalizerPatch struct {
|
|
baseNormalizerPatch
|
|
patch *jsonpatch.Patch
|
|
}
|
|
|
|
func (np *jsonPatchNormalizerPatch) Apply(data []byte) ([]byte, error) {
|
|
patchedData, err := np.patch.Apply(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return patchedData, nil
|
|
}
|
|
|
|
type jqNormalizerPatch struct {
|
|
baseNormalizerPatch
|
|
code *gojq.Code
|
|
}
|
|
|
|
func (np *jqNormalizerPatch) Apply(data []byte) ([]byte, error) {
|
|
dataJson := make(map[string]interface{})
|
|
err := json.Unmarshal(data, &dataJson)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
iter := np.code.Run(dataJson)
|
|
first, ok := iter.Next()
|
|
if !ok {
|
|
return nil, fmt.Errorf("JQ patch did not return any data")
|
|
}
|
|
_, ok = iter.Next()
|
|
if ok {
|
|
return nil, fmt.Errorf("JQ patch returned multiple objects")
|
|
}
|
|
|
|
patchedData, err := json.Marshal(first)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return patchedData, err
|
|
}
|
|
|
|
type ignoreNormalizer struct {
|
|
patches []normalizerPatch
|
|
}
|
|
|
|
// NewIgnoreNormalizer creates diff normalizer which removes ignored fields according to given application spec and resource overrides
|
|
func NewIgnoreNormalizer(ignore []v1alpha1.ResourceIgnoreDifferences, overrides map[string]v1alpha1.ResourceOverride) (diff.Normalizer, error) {
|
|
for key, override := range overrides {
|
|
group, kind, err := getGroupKindForOverrideKey(key)
|
|
if err != nil {
|
|
log.Warn(err)
|
|
}
|
|
if len(override.IgnoreDifferences.JSONPointers) > 0 || len(override.IgnoreDifferences.JQPathExpressions) > 0 {
|
|
resourceIgnoreDifference := v1alpha1.ResourceIgnoreDifferences{
|
|
Group: group,
|
|
Kind: kind,
|
|
}
|
|
if len(override.IgnoreDifferences.JSONPointers) > 0 {
|
|
resourceIgnoreDifference.JSONPointers = override.IgnoreDifferences.JSONPointers
|
|
}
|
|
if len(override.IgnoreDifferences.JQPathExpressions) > 0 {
|
|
resourceIgnoreDifference.JQPathExpressions = override.IgnoreDifferences.JQPathExpressions
|
|
}
|
|
ignore = append(ignore, resourceIgnoreDifference)
|
|
}
|
|
}
|
|
patches := make([]normalizerPatch, 0)
|
|
for i := range ignore {
|
|
for _, path := range ignore[i].JSONPointers {
|
|
patchData, err := json.Marshal([]map[string]string{{"op": "remove", "path": path}})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
patch, err := jsonpatch.DecodePatch(patchData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
patches = append(patches, &jsonPatchNormalizerPatch{
|
|
baseNormalizerPatch: baseNormalizerPatch{
|
|
groupKind: schema.GroupKind{Group: ignore[i].Group, Kind: ignore[i].Kind},
|
|
name: ignore[i].Name,
|
|
namespace: ignore[i].Namespace,
|
|
},
|
|
patch: &patch,
|
|
})
|
|
}
|
|
for _, pathExpression := range ignore[i].JQPathExpressions {
|
|
jqDeletionQuery, err := gojq.Parse(fmt.Sprintf("del(%s)", pathExpression))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
jqDeletionCode, err := gojq.Compile(jqDeletionQuery)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
patches = append(patches, &jqNormalizerPatch{
|
|
baseNormalizerPatch: baseNormalizerPatch{
|
|
groupKind: schema.GroupKind{Group: ignore[i].Group, Kind: ignore[i].Kind},
|
|
name: ignore[i].Name,
|
|
namespace: ignore[i].Namespace,
|
|
},
|
|
code: jqDeletionCode,
|
|
})
|
|
}
|
|
}
|
|
return &ignoreNormalizer{patches: patches}, nil
|
|
}
|
|
|
|
// Normalize removes fields from supplied resource using json paths from matching items of specified resources ignored differences list
|
|
func (n *ignoreNormalizer) Normalize(un *unstructured.Unstructured) error {
|
|
matched := make([]normalizerPatch, 0)
|
|
for _, patch := range n.patches {
|
|
groupKind := un.GroupVersionKind().GroupKind()
|
|
|
|
if glob.Match(patch.GetGroupKind().Group, groupKind.Group) &&
|
|
glob.Match(patch.GetGroupKind().Kind, groupKind.Kind) &&
|
|
(patch.GetName() == "" || patch.GetName() == un.GetName()) &&
|
|
(patch.GetNamespace() == "" || patch.GetNamespace() == un.GetNamespace()) {
|
|
|
|
matched = append(matched, patch)
|
|
}
|
|
}
|
|
if len(matched) == 0 {
|
|
return nil
|
|
}
|
|
|
|
docData, err := json.Marshal(un)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, patch := range matched {
|
|
patchedDocData, err := patch.Apply(docData)
|
|
if err != nil {
|
|
log.Debugf("Failed to apply normalization: %v", err)
|
|
continue
|
|
}
|
|
docData = patchedDocData
|
|
}
|
|
|
|
err = json.Unmarshal(docData, un)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|