97 lines
2.6 KiB
Go
97 lines
2.6 KiB
Go
|
package resourcemodifiers
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
jsonpatch "github.com/evanphx/json-patch"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
|
)
|
||
|
|
||
|
type JSONPatch struct {
|
||
|
Operation string `json:"operation"`
|
||
|
From string `json:"from,omitempty"`
|
||
|
Path string `json:"path"`
|
||
|
Value string `json:"value,omitempty"`
|
||
|
}
|
||
|
|
||
|
func (p *JSONPatch) ToString() string {
|
||
|
if addQuotes(p.Value) {
|
||
|
return fmt.Sprintf(`{"op": "%s", "from": "%s", "path": "%s", "value": "%s"}`, p.Operation, p.From, p.Path, p.Value)
|
||
|
}
|
||
|
return fmt.Sprintf(`{"op": "%s", "from": "%s", "path": "%s", "value": %s}`, p.Operation, p.From, p.Path, p.Value)
|
||
|
}
|
||
|
|
||
|
func addQuotes(value string) bool {
|
||
|
if value == "" {
|
||
|
return true
|
||
|
}
|
||
|
// if value is null, then don't add quotes
|
||
|
if value == "null" {
|
||
|
return false
|
||
|
}
|
||
|
// if value is a boolean, then don't add quotes
|
||
|
if _, err := strconv.ParseBool(value); err == nil {
|
||
|
return false
|
||
|
}
|
||
|
// if value is a json object or array, then don't add quotes.
|
||
|
if strings.HasPrefix(value, "{") || strings.HasPrefix(value, "[") {
|
||
|
return false
|
||
|
}
|
||
|
// if value is a number, then don't add quotes
|
||
|
if _, err := strconv.ParseFloat(value, 64); err == nil {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
type JSONPatcher struct {
|
||
|
patches []JSONPatch `yaml:"patches"`
|
||
|
}
|
||
|
|
||
|
func (p *JSONPatcher) Patch(u *unstructured.Unstructured, logger logrus.FieldLogger) (*unstructured.Unstructured, error) {
|
||
|
modifiedObjBytes, err := p.applyPatch(u)
|
||
|
if err != nil {
|
||
|
if errors.Is(err, jsonpatch.ErrTestFailed) {
|
||
|
logger.Infof("Test operation failed for JSON Patch %s", err.Error())
|
||
|
return u.DeepCopy(), nil
|
||
|
}
|
||
|
return nil, fmt.Errorf("error in applying JSON Patch %s", err.Error())
|
||
|
}
|
||
|
|
||
|
updated := &unstructured.Unstructured{}
|
||
|
err = updated.UnmarshalJSON(modifiedObjBytes)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error in unmarshalling modified object %s", err.Error())
|
||
|
}
|
||
|
|
||
|
return updated, nil
|
||
|
}
|
||
|
|
||
|
func (p *JSONPatcher) applyPatch(u *unstructured.Unstructured) ([]byte, error) {
|
||
|
patchBytes := p.patchArrayToByteArray()
|
||
|
jsonPatch, err := jsonpatch.DecodePatch(patchBytes)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error in decoding json patch %s", err.Error())
|
||
|
}
|
||
|
|
||
|
objBytes, err := u.MarshalJSON()
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("error in marshaling object %s", err.Error())
|
||
|
}
|
||
|
|
||
|
return jsonPatch.Apply(objBytes)
|
||
|
}
|
||
|
|
||
|
func (p *JSONPatcher) patchArrayToByteArray() []byte {
|
||
|
var patches []string
|
||
|
for _, patch := range p.patches {
|
||
|
patches = append(patches, patch.ToString())
|
||
|
}
|
||
|
patchesStr := strings.Join(patches, ",\n\t")
|
||
|
return []byte(fmt.Sprintf(`[%s]`, patchesStr))
|
||
|
}
|