mirror of
https://github.com/alibaba/higress.git
synced 2026-02-27 22:20:57 +08:00
516 lines
12 KiB
Go
516 lines
12 KiB
Go
package pkg
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/higress-group/wasm-go/pkg/log"
|
|
"github.com/tidwall/gjson"
|
|
)
|
|
|
|
const (
|
|
commandTypeSet = "set"
|
|
commandTypeConcat = "concat"
|
|
commandTypeCopy = "copy"
|
|
commandTypeDelete = "delete"
|
|
commandTypeRename = "rename"
|
|
)
|
|
|
|
var (
|
|
commandFactories = map[string]func(gjson.Result) (Command, error){
|
|
"set": newSetCommand,
|
|
"concat": newConcatCommand,
|
|
"copy": newCopyCommand,
|
|
"delete": newDeleteCommand,
|
|
"rename": newRenameCommand,
|
|
}
|
|
)
|
|
|
|
type CommandSet struct {
|
|
DisableReroute bool `json:"disableReroute"`
|
|
Commands []Command `json:"commands,omitempty"`
|
|
RelatedStages map[Stage]bool `json:"-"`
|
|
}
|
|
|
|
func (s *CommandSet) FromJson(json gjson.Result) error {
|
|
relatedStages := map[Stage]bool{}
|
|
if commandsJson := json.Get("commands"); commandsJson.Exists() && commandsJson.IsArray() {
|
|
for _, item := range commandsJson.Array() {
|
|
if command, err := NewCommand(item); err != nil {
|
|
return fmt.Errorf("failed to create command from json: %v\n %v", err, item)
|
|
} else {
|
|
s.Commands = append(s.Commands, command)
|
|
for _, ref := range command.GetRefs() {
|
|
relatedStages[ref.GetStage()] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
s.RelatedStages = relatedStages
|
|
if disableReroute := json.Get("disableReroute"); disableReroute.Exists() {
|
|
s.DisableReroute = disableReroute.Bool()
|
|
} else {
|
|
s.DisableReroute = false
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *CommandSet) CreatExecutors() []Executor {
|
|
executors := make([]Executor, 0, len(s.Commands))
|
|
for _, command := range s.Commands {
|
|
executor := command.CreateExecutor()
|
|
executors = append(executors, executor)
|
|
}
|
|
return executors
|
|
}
|
|
|
|
type ConditionalCommandSet struct {
|
|
ConditionSet
|
|
CommandSet
|
|
}
|
|
|
|
func (s *ConditionalCommandSet) FromJson(json gjson.Result) error {
|
|
if err := s.ConditionSet.FromJson(json); err != nil {
|
|
return err
|
|
}
|
|
if err := s.CommandSet.FromJson(json); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Command interface {
|
|
GetType() string
|
|
GetRefs() []*Ref
|
|
CreateExecutor() Executor
|
|
}
|
|
|
|
type Executor interface {
|
|
GetCommand() Command
|
|
Run(editorContext EditorContext, stage Stage) error
|
|
}
|
|
|
|
func NewCommand(json gjson.Result) (Command, error) {
|
|
t := json.Get("type").String()
|
|
if t == "" {
|
|
return nil, errors.New("command type is required")
|
|
}
|
|
if constructor, ok := commandFactories[t]; ok && constructor != nil {
|
|
return constructor(json)
|
|
} else {
|
|
return nil, errors.New("unknown command type: " + t)
|
|
}
|
|
}
|
|
|
|
type baseExecutor struct {
|
|
finished bool
|
|
}
|
|
|
|
// setCommand
|
|
func newSetCommand(json gjson.Result) (Command, error) {
|
|
var targetRef *Ref
|
|
var err error
|
|
if t := json.Get("target"); !t.Exists() {
|
|
return nil, errors.New("setCommand: target field is required")
|
|
} else {
|
|
targetRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setCommand: failed to create ref from target field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
var value string
|
|
if v := json.Get("value"); !v.Exists() {
|
|
return nil, errors.New("setCommand: value field is required")
|
|
} else {
|
|
value = v.String()
|
|
if value == "" {
|
|
return nil, errors.New("setCommand: value cannot be empty")
|
|
}
|
|
}
|
|
return &setCommand{
|
|
targetRef: targetRef,
|
|
value: value,
|
|
}, nil
|
|
}
|
|
|
|
type setCommand struct {
|
|
targetRef *Ref
|
|
value string
|
|
}
|
|
|
|
func (c *setCommand) GetType() string {
|
|
return commandTypeSet
|
|
}
|
|
|
|
func (c *setCommand) GetRefs() []*Ref {
|
|
return []*Ref{c.targetRef}
|
|
}
|
|
|
|
func (c *setCommand) CreateExecutor() Executor {
|
|
return &setExecutor{command: c}
|
|
}
|
|
|
|
type setExecutor struct {
|
|
baseExecutor
|
|
command *setCommand
|
|
}
|
|
|
|
func (e *setExecutor) GetCommand() Command {
|
|
return e.command
|
|
}
|
|
|
|
func (e *setExecutor) Run(editorContext EditorContext, stage Stage) error {
|
|
if e.finished {
|
|
return nil
|
|
}
|
|
|
|
command := e.command
|
|
log.Debugf("setCommand: checking stage %s for target %s", Stage2String[stage], command.targetRef)
|
|
if command.targetRef.GetStage() == stage {
|
|
log.Debugf("setCommand: set %s to %s", command.targetRef, command.value)
|
|
editorContext.SetRefValue(command.targetRef, command.value)
|
|
e.finished = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// concatCommand
|
|
func newConcatCommand(json gjson.Result) (Command, error) {
|
|
var targetRef *Ref
|
|
var err error
|
|
if t := json.Get("target"); !t.Exists() {
|
|
return nil, errors.New("concatCommand: target field is required")
|
|
} else {
|
|
targetRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("concatCommand: failed to create ref from target field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
|
|
valuesJson := json.Get("values")
|
|
if !valuesJson.Exists() || !valuesJson.IsArray() {
|
|
return nil, errors.New("concatCommand: values field is required and must be an array")
|
|
}
|
|
|
|
values := make([]interface{}, 0, len(valuesJson.Array()))
|
|
for _, item := range valuesJson.Array() {
|
|
var value interface{}
|
|
if item.IsObject() {
|
|
valueRef, err := NewRef(item)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("concatCommand: failed to create ref from values field: %v\n %v", err, item.Raw)
|
|
}
|
|
if valueRef.GetStage() > targetRef.GetStage() {
|
|
return nil, fmt.Errorf("concatCommand: the processing stage of value [%s] cannot be after the stage of target [%s]", Stage2String[valueRef.GetStage()], Stage2String[targetRef.GetStage()])
|
|
}
|
|
value = valueRef
|
|
} else {
|
|
value = item.String()
|
|
}
|
|
values = append(values, value)
|
|
}
|
|
|
|
return &concatCommand{
|
|
targetRef: targetRef,
|
|
values: values,
|
|
}, nil
|
|
}
|
|
|
|
type concatCommand struct {
|
|
targetRef *Ref
|
|
values []interface{}
|
|
}
|
|
|
|
func (c *concatCommand) GetType() string {
|
|
return commandTypeConcat
|
|
}
|
|
|
|
func (c *concatCommand) GetRefs() []*Ref {
|
|
refs := []*Ref{c.targetRef}
|
|
if c.values != nil && len(c.values) != 0 {
|
|
for _, value := range c.values {
|
|
if ref, ok := value.(*Ref); ok {
|
|
refs = append(refs, ref)
|
|
}
|
|
}
|
|
}
|
|
return refs
|
|
}
|
|
|
|
func (c *concatCommand) CreateExecutor() Executor {
|
|
return &concatExecutor{command: c}
|
|
}
|
|
|
|
type concatExecutor struct {
|
|
baseExecutor
|
|
command *concatCommand
|
|
values []string
|
|
}
|
|
|
|
func (e *concatExecutor) GetCommand() Command {
|
|
return e.command
|
|
}
|
|
|
|
func (e *concatExecutor) Run(editorContext EditorContext, stage Stage) error {
|
|
if e.finished {
|
|
return nil
|
|
}
|
|
|
|
command := e.command
|
|
|
|
if e.values == nil {
|
|
e.values = make([]string, len(command.values))
|
|
}
|
|
|
|
for i, value := range command.values {
|
|
if value == nil || e.values[i] != "" {
|
|
continue
|
|
}
|
|
v := ""
|
|
if s, ok := value.(string); ok {
|
|
v = s
|
|
} else if ref, ok := value.(*Ref); ok && ref.GetStage() == stage {
|
|
v = editorContext.GetRefValue(ref)
|
|
}
|
|
e.values[i] = v
|
|
}
|
|
|
|
if command.targetRef.GetStage() == stage {
|
|
result := ""
|
|
for _, v := range e.values {
|
|
if v == "" {
|
|
continue
|
|
}
|
|
result += v
|
|
}
|
|
log.Debugf("concatCommand: set %s to %s", command.targetRef, result)
|
|
editorContext.SetRefValue(command.targetRef, result)
|
|
e.finished = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// copyCommand
|
|
func newCopyCommand(json gjson.Result) (Command, error) {
|
|
var sourceRef *Ref
|
|
var targetRef *Ref
|
|
var err error
|
|
if t := json.Get("source"); !t.Exists() {
|
|
return nil, errors.New("copyCommand: source field is required")
|
|
} else {
|
|
sourceRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("copyCommand: failed to create ref from source field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
if t := json.Get("target"); !t.Exists() {
|
|
return nil, errors.New("copyCommand: target field is required")
|
|
} else {
|
|
targetRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("copyCommand: failed to create ref from target field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
if sourceRef.GetStage() > targetRef.GetStage() {
|
|
return nil, fmt.Errorf("copyCommand: the processing stage of source [%s] cannot be after the stage of target [%s]", Stage2String[sourceRef.GetStage()], Stage2String[targetRef.GetStage()])
|
|
}
|
|
return ©Command{
|
|
sourceRef: sourceRef,
|
|
targetRef: targetRef,
|
|
}, nil
|
|
}
|
|
|
|
type copyCommand struct {
|
|
sourceRef *Ref
|
|
targetRef *Ref
|
|
}
|
|
|
|
func (c *copyCommand) GetType() string {
|
|
return commandTypeCopy
|
|
}
|
|
|
|
func (c *copyCommand) GetRefs() []*Ref {
|
|
return []*Ref{c.sourceRef, c.targetRef}
|
|
}
|
|
|
|
func (c *copyCommand) CreateExecutor() Executor {
|
|
return ©Executor{command: c}
|
|
}
|
|
|
|
type copyExecutor struct {
|
|
baseExecutor
|
|
command *copyCommand
|
|
valueToCopy string
|
|
}
|
|
|
|
func (e *copyExecutor) GetCommand() Command {
|
|
return e.command
|
|
}
|
|
|
|
func (e *copyExecutor) Run(editorContext EditorContext, stage Stage) error {
|
|
if e.finished {
|
|
return nil
|
|
}
|
|
|
|
command := e.command
|
|
|
|
if command.sourceRef.GetStage() == stage {
|
|
e.valueToCopy = editorContext.GetRefValue(command.sourceRef)
|
|
log.Debugf("copyCommand: valueToCopy=%s", e.valueToCopy)
|
|
}
|
|
|
|
if e.valueToCopy == "" {
|
|
log.Debug("copyCommand: valueToCopy is empty. skip.")
|
|
e.finished = true
|
|
return nil
|
|
}
|
|
|
|
if command.targetRef.GetStage() == stage {
|
|
editorContext.SetRefValue(command.targetRef, e.valueToCopy)
|
|
log.Debugf("copyCommand: set %s to %s", e.valueToCopy, command.targetRef)
|
|
e.finished = true
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// deleteCommand
|
|
func newDeleteCommand(json gjson.Result) (Command, error) {
|
|
var targetRef *Ref
|
|
var err error
|
|
if t := json.Get("target"); !t.Exists() {
|
|
return nil, errors.New("deleteCommand: target field is required")
|
|
} else {
|
|
targetRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("deleteCommand: failed to create ref from target field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
return &deleteCommand{
|
|
targetRef: targetRef,
|
|
}, nil
|
|
}
|
|
|
|
type deleteCommand struct {
|
|
targetRef *Ref
|
|
}
|
|
|
|
func (c *deleteCommand) GetType() string {
|
|
return commandTypeDelete
|
|
}
|
|
|
|
func (c *deleteCommand) GetRefs() []*Ref {
|
|
return []*Ref{c.targetRef}
|
|
}
|
|
|
|
func (c *deleteCommand) CreateExecutor() Executor {
|
|
return &deleteExecutor{command: c}
|
|
}
|
|
|
|
type deleteExecutor struct {
|
|
baseExecutor
|
|
command *deleteCommand
|
|
}
|
|
|
|
func (e *deleteExecutor) GetCommand() Command {
|
|
return e.command
|
|
}
|
|
|
|
func (e *deleteExecutor) Run(editorContext EditorContext, stage Stage) error {
|
|
if e.finished {
|
|
return nil
|
|
}
|
|
|
|
command := e.command
|
|
log.Debugf("deleteCommand: checking stage %s for target %s", Stage2String[stage], command.targetRef)
|
|
|
|
if command.targetRef.GetStage() == stage {
|
|
log.Debugf("deleteCommand: delete %s", command.targetRef)
|
|
editorContext.DeleteRefValues(command.targetRef)
|
|
e.finished = true
|
|
log.Debugf("deleteCommand: finished deleting %s", command.targetRef)
|
|
} else {
|
|
log.Debugf("deleteCommand: stage %s does not match targetRef stage %s, skipping.", Stage2String[stage], Stage2String[command.targetRef.GetStage()])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// renameCommand
|
|
func newRenameCommand(json gjson.Result) (Command, error) {
|
|
var targetRef *Ref
|
|
var err error
|
|
if t := json.Get("target"); !t.Exists() {
|
|
return nil, errors.New("renameCommand: target field is required")
|
|
} else {
|
|
targetRef, err = NewRef(t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("renameCommand: failed to create ref from target field: %v\n %v", err, t.Raw)
|
|
}
|
|
}
|
|
newName := json.Get("newName").String()
|
|
if newName == "" {
|
|
return nil, errors.New("renameCommand: newName field is required")
|
|
}
|
|
return &renameCommand{
|
|
targetRef: targetRef,
|
|
newName: newName,
|
|
}, nil
|
|
}
|
|
|
|
type renameCommand struct {
|
|
targetRef *Ref
|
|
newName string
|
|
}
|
|
|
|
func (c *renameCommand) GetType() string {
|
|
return commandTypeRename
|
|
}
|
|
|
|
func (c *renameCommand) GetRefs() []*Ref {
|
|
return []*Ref{c.targetRef}
|
|
}
|
|
|
|
func (c *renameCommand) CreateExecutor() Executor {
|
|
return &renameExecutor{command: c}
|
|
}
|
|
|
|
type renameExecutor struct {
|
|
baseExecutor
|
|
command *renameCommand
|
|
}
|
|
|
|
func (e *renameExecutor) GetCommand() Command {
|
|
return e.command
|
|
}
|
|
|
|
func (e *renameExecutor) Run(editorContext EditorContext, stage Stage) error {
|
|
if e.finished {
|
|
return nil
|
|
}
|
|
|
|
command := e.command
|
|
log.Debugf("renameCommand: checking stage %s for target %s", Stage2String[stage], command.targetRef)
|
|
|
|
if command.targetRef.GetStage() == stage {
|
|
if command.newName == command.targetRef.Name {
|
|
log.Debugf("renameCommand: skip renaming %s to itself", command.targetRef)
|
|
} else {
|
|
values := editorContext.GetRefValues(command.targetRef)
|
|
log.Debugf("renameCommand: rename %s to %s value=%v", command.targetRef, command.newName, values)
|
|
editorContext.SetRefValues(&Ref{
|
|
Type: command.targetRef.Type,
|
|
Name: command.newName,
|
|
}, values)
|
|
editorContext.DeleteRefValues(command.targetRef)
|
|
log.Debugf("renameCommand: finished renaming %s to %s", command.targetRef, command.newName)
|
|
}
|
|
e.finished = true
|
|
} else {
|
|
log.Debugf("renameCommand: stage %s does not match targetRef stage %s, skipping.", Stage2String[stage], Stage2String[command.targetRef.GetStage()])
|
|
}
|
|
|
|
return nil
|
|
}
|