get.porter.sh/porter@v1.3.0/pkg/exec/builder/output_jsonpath.go (about)

     1  package builder
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  
     9  	"get.porter.sh/porter/pkg/runtime"
    10  	"get.porter.sh/porter/pkg/tracing"
    11  	"github.com/PaesslerAG/jsonpath"
    12  )
    13  
    14  type OutputJsonPath interface {
    15  	Output
    16  	GetJsonPath() string
    17  }
    18  
    19  // ProcessJsonPathOutputs evaluates the specified output buffer as JSON, looks through the outputs for
    20  // any that implement the OutputJsonPath and extracts their output.
    21  func ProcessJsonPathOutputs(ctx context.Context, cfg runtime.RuntimeConfig, step StepWithOutputs, stdout string) error {
    22  	_, span := tracing.StartSpan(ctx)
    23  	defer span.EndSpan()
    24  
    25  	outputs := step.GetOutputs()
    26  
    27  	if len(outputs) == 0 {
    28  		return nil
    29  	}
    30  
    31  	var outputJson interface{}
    32  
    33  	for _, o := range outputs {
    34  		output, ok := o.(OutputJsonPath)
    35  		if !ok {
    36  			continue
    37  		}
    38  
    39  		outputName := output.GetName()
    40  		outputPath := output.GetJsonPath()
    41  		if outputPath == "" {
    42  			continue
    43  		}
    44  
    45  		if cfg.DebugMode {
    46  			fmt.Fprintf(cfg.Err, "Processing jsonpath output %s using query %s against document\n%s\n", outputName, outputPath, stdout)
    47  		}
    48  
    49  		var valueB []byte
    50  
    51  		if outputJson == nil {
    52  			if stdout != "" {
    53  				d := json.NewDecoder(bytes.NewBuffer([]byte(stdout)))
    54  				d.UseNumber()
    55  				err := d.Decode(&outputJson)
    56  				if err != nil {
    57  					return span.Error(fmt.Errorf("error unmarshaling stdout as json %s: %w", stdout, err))
    58  				}
    59  			}
    60  		}
    61  
    62  		// Always write an output, even when there isn't json output to parse (like when stdout is empty)
    63  		if outputJson != nil {
    64  			value, err := jsonpath.Get(outputPath, outputJson)
    65  			if err != nil {
    66  				return span.Error(fmt.Errorf("error evaluating jsonpath %q for output %q against %s: %w", outputPath, outputName, stdout, err))
    67  			}
    68  
    69  			// Only marshal complex types to json, leave strings, numbers and booleans alone
    70  			switch t := value.(type) {
    71  			case map[string]interface{}, []interface{}:
    72  				valueB, err = json.Marshal(value)
    73  				if err != nil {
    74  					return span.Error(fmt.Errorf("error marshaling jsonpath result %v for output %q: %w", valueB, outputName, err))
    75  				}
    76  			default:
    77  				valueB = []byte(fmt.Sprintf("%v", t))
    78  			}
    79  		}
    80  
    81  		err := cfg.WriteMixinOutputToFile(outputName, valueB)
    82  		if err != nil {
    83  			return span.Error(fmt.Errorf("error writing mixin output for %q: %w", outputName, err))
    84  		}
    85  	}
    86  
    87  	return nil
    88  }