Skip to content

Commit

Permalink
Merge pull request #1905 from josephschorr/lrv2
Browse files Browse the repository at this point in the history
Implement a new, experimental variant of LookupResources as LookupResources2
  • Loading branch information
vroldanbet authored Jul 24, 2024
2 parents f1dca2c + d889495 commit c31f34f
Show file tree
Hide file tree
Showing 63 changed files with 10,150 additions and 1,101 deletions.
240 changes: 178 additions & 62 deletions internal/caveats/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,26 @@ type ExpressionResult interface {
}

type syntheticResult struct {
value bool
contextValues map[string]any
exprString string
value bool
contextValues map[string]any
exprString string
missingContextParams []string
}

func (sr syntheticResult) Value() bool {
return sr.value
}

func (sr syntheticResult) IsPartial() bool {
return false
return len(sr.missingContextParams) > 0
}

func (sr syntheticResult) MissingVarNames() ([]string, error) {
return nil, fmt.Errorf("not a partial value")
if len(sr.missingContextParams) == 0 {
return nil, fmt.Errorf("not a partial value")
}

return sr.missingContextParams, nil
}

func (sr syntheticResult) ContextValues() map[string]any {
Expand Down Expand Up @@ -156,6 +161,14 @@ func (lc loadedCaveats) Get(caveatDefName string) (*core.CaveatDefinition, *cave
return caveat, justDeserialized, nil
}

func isFalseResult(result ExpressionResult) bool {
return !result.Value() && !result.IsPartial()
}

func isTrueResult(result ExpressionResult) bool {
return result.Value() && !result.IsPartial()
}

func runExpressionWithCaveats(
ctx context.Context,
env *caveats.Environment,
Expand Down Expand Up @@ -203,15 +216,30 @@ func runExpressionWithCaveats(
}

cop := expr.GetOperation()
boolResult := false
if cop.Op == core.CaveatOperation_AND {
boolResult = true
}

var contextValues map[string]any
var exprStringPieces []string

var currentResult ExpressionResult = syntheticResult{
value: false,
contextValues: nil,
exprString: "",
missingContextParams: nil,
}
if cop.Op == core.CaveatOperation_AND {
currentResult = syntheticResult{
value: true,
contextValues: nil,
exprString: "",
missingContextParams: nil,
}
}

buildExprString := func() (string, error) {
if debugOption != RunCaveatExpressionWithDebugInformation {
return "", nil
}

switch cop.Op {
case core.CaveatOperation_AND:
return strings.Join(exprStringPieces, " && "), nil
Expand All @@ -227,97 +255,185 @@ func runExpressionWithCaveats(
}
}

for _, child := range cop.Children {
childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption)
if err != nil {
addDebugInfo := func(found ExpressionResult) error {
if debugOption == RunCaveatExpressionWithDebugInformation {
contextValues = combineMaps(contextValues, found.ContextValues())
exprString, err := found.ExpressionString()
if err != nil {
return err
}

exprStringPieces = append(exprStringPieces, exprString)
}

return nil
}

and := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) {
if err := addDebugInfo(found); err != nil {
return nil, err
}

if childResult.IsPartial() {
return childResult, nil
var missingContextParams []string
if existing.IsPartial() {
params, err := existing.MissingVarNames()
if err != nil {
return nil, err
}

missingContextParams = params
} else if !existing.Value() {
return existing, nil
}

switch cop.Op {
case core.CaveatOperation_AND:
boolResult = boolResult && childResult.Value()
if found.IsPartial() {
params, err := found.MissingVarNames()
if err != nil {
return nil, err
}

if debugOption == RunCaveatExpressionWithDebugInformation {
contextValues = combineMaps(contextValues, childResult.ContextValues())
exprString, err := childResult.ExpressionString()
if err != nil {
return nil, err
}
missingContextParams = append(missingContextParams, params...)
} else if !found.Value() {
return found, nil
}

exprStringPieces = append(exprStringPieces, exprString)
exprString, err := buildExprString()
if err != nil {
return nil, err
}

return syntheticResult{
value: existing.Value() && found.Value(),
contextValues: contextValues,
exprString: exprString,
missingContextParams: missingContextParams,
}, nil
}

or := func(existing ExpressionResult, found ExpressionResult) (ExpressionResult, error) {
if err := addDebugInfo(found); err != nil {
return nil, err
}

var missingContextParams []string
if existing.IsPartial() {
params, err := existing.MissingVarNames()
if err != nil {
return nil, err
}

if !boolResult {
built, err := buildExprString()
if err != nil {
return nil, err
}
missingContextParams = params
} else if existing.Value() {
return existing, nil
}

return syntheticResult{false, contextValues, built}, nil
if found.IsPartial() {
params, err := found.MissingVarNames()
if err != nil {
return nil, err
}

case core.CaveatOperation_OR:
boolResult = boolResult || childResult.Value()
missingContextParams = append(missingContextParams, params...)
} else if found.Value() {
return found, nil
}

exprString, err := buildExprString()
if err != nil {
return nil, err
}

if debugOption == RunCaveatExpressionWithDebugInformation {
contextValues = combineMaps(contextValues, childResult.ContextValues())
exprString, err := childResult.ExpressionString()
if err != nil {
return nil, err
}
return syntheticResult{
value: existing.Value() || found.Value(),
contextValues: contextValues,
exprString: exprString,
missingContextParams: missingContextParams,
}, nil
}

exprStringPieces = append(exprStringPieces, exprString)
invert := func(existing ExpressionResult) (ExpressionResult, error) {
if debugOption == RunCaveatExpressionWithDebugInformation {
contextValues = combineMaps(contextValues, existing.ContextValues())
exprString, err := existing.ExpressionString()
if err != nil {
return nil, err
}

if boolResult {
built, err := buildExprString()
if err != nil {
return nil, err
}
exprStringPieces = append(exprStringPieces, "!("+exprString+")")
}

value := !existing.Value()

var missingContextParams []string
if existing.IsPartial() {
value = false // partials always have a value of false.
missingContextParams, _ = existing.MissingVarNames()
}

return syntheticResult{true, contextValues, built}, nil
exprString, err := buildExprString()
if err != nil {
return nil, err
}

return syntheticResult{
value: value,
contextValues: contextValues,
exprString: exprString,
missingContextParams: missingContextParams,
}, nil
}

for _, child := range cop.Children {
childResult, err := runExpressionWithCaveats(ctx, env, child, context, loadedCaveats, debugOption)
if err != nil {
return nil, err
}

switch cop.Op {
case core.CaveatOperation_AND:
cr, err := and(currentResult, childResult)
if err != nil {
return nil, err
}

case core.CaveatOperation_NOT:
if debugOption == RunCaveatExpressionWithDebugInformation {
contextValues = combineMaps(contextValues, childResult.ContextValues())
exprString, err := childResult.ExpressionString()
if err != nil {
return nil, err
}

exprStringPieces = append(exprStringPieces, "!("+exprString+")")
currentResult = cr

if isFalseResult(currentResult) {
return currentResult, nil
}

built, err := buildExprString()
case core.CaveatOperation_OR:
cr, err := or(currentResult, childResult)
if err != nil {
return nil, err
}

return syntheticResult{!childResult.Value(), contextValues, built}, nil
currentResult = cr

if isTrueResult(currentResult) {
return currentResult, nil
}

case core.CaveatOperation_NOT:
return invert(childResult)

default:
return nil, spiceerrors.MustBugf("unknown caveat operation: %v", cop.Op)
}
}

built, err := buildExprString()
if err != nil {
return nil, err
}

return syntheticResult{boolResult, contextValues, built}, nil
return currentResult, nil
}

func combineMaps(first map[string]any, second map[string]any) map[string]any {
if first == nil {
first = make(map[string]any, len(second))
}

if second == nil {
return first
}

cloned := maps.Clone(first)
maps.Copy(cloned, second)
return cloned
Expand Down
Loading

0 comments on commit c31f34f

Please sign in to comment.