Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a new, experimental variant of LookupResources as LookupResources2 #1905

Merged
merged 5 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
vroldanbet marked this conversation as resolved.
Show resolved Hide resolved
}

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
Loading