Skip to content

Commit

Permalink
feat: Automatically inject the plan object if the first argument of t…
Browse files Browse the repository at this point in the history
…he main function is `plan` (#716)

## Description:
Automatically inject the plan object if the first argument of the main
function is `plan`

## Is this change user-facing?
YES

## References (if applicable):
<!-- Add relevant Github Issues, Discord threads, or other helpful
information. -->

---------

Co-authored-by: Peeeekay <15133250+Peeeekay@users.noreply.github.com>
Co-authored-by: Guillaume Bouvignies <guillaume.bouvignies@kurtosistech.com>
Co-authored-by: Anders Schwartz <adschwartz@users.noreply.github.com>
  • Loading branch information
4 people committed Jun 14, 2023
1 parent b599ed4 commit 142ce42
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 15 deletions.
1 change: 0 additions & 1 deletion core/server/api_container/server/api_container_service.go
Expand Up @@ -759,7 +759,6 @@ func (apicService ApiContainerService) getServiceInfo(ctx context.Context, servi
return serviceInfoResponse, nil
}

//"/services/jvm/icon/src/node-setup/contract-deploy.star"
func (apicService ApiContainerService) runStarlarkPackageSetup(
packageId string,
clonePackage bool,
Expand Down
Expand Up @@ -97,14 +97,12 @@ func (interpreter *StartosisInterpreter) Interpret(
logrus.Debugf("Successfully interpreted Starlark code into instruction queue: \n%s", instructionsQueue)

var isUsingDefaultMainFunction bool
var shouldInjectPlanArg bool
// if the user sends "" or "run" we isUsingDefaultMainFunction to true
if mainFunctionName == "" || mainFunctionName == runFunctionName {
mainFunctionName = runFunctionName
isUsingDefaultMainFunction = true
} else {
shouldInjectPlanArg = true //TODO now we inject the plan arg by default as the first argument on any random function, we have to make it optional and set by the users
}

if !globalVariables.Has(mainFunctionName) {
return missingMainFunctionReturnValue(packageId, mainFunctionName)
}
Expand All @@ -124,16 +122,21 @@ func (interpreter *StartosisInterpreter) Interpret(
var argsTuple starlark.Tuple
var kwArgs []starlark.Tuple

if shouldInjectPlanArg || (isUsingDefaultMainFunction && mainFunction.NumParams() >= minimumParamsRequiredForPlan) {
if paramName, _ := mainFunction.Param(planParamIndex); paramName != planParamName {
return "", nil, startosis_errors.NewInterpretationError(unexpectedArgNameError, planParamIndex, planParamName, paramName).ToAPIType()
mainFuncParamsNum := mainFunction.NumParams()

if mainFuncParamsNum >= minimumParamsRequiredForPlan {
// the plan object will always be injected if the first argument name is 'plan'
firstParamName, _ := mainFunction.Param(planParamIndex)
if firstParamName == planParamName {
kurtosisPlanInstructions := KurtosisPlanInstructions(interpreter.serviceNetwork, interpreter.recipeExecutor, interpreter.moduleContentProvider)
planModule := plan_module.PlanModule(&instructionsQueue, kurtosisPlanInstructions)
argsTuple = append(argsTuple, planModule)
}
kurtosisPlanInstructions := KurtosisPlanInstructions(interpreter.serviceNetwork, interpreter.recipeExecutor, interpreter.moduleContentProvider)
planModule := plan_module.PlanModule(&instructionsQueue, kurtosisPlanInstructions)
argsTuple = append(argsTuple, planModule)
}

mainFuncParamsNum := mainFunction.NumParams()
if firstParamName != planParamName && isUsingDefaultMainFunction {
return "", nil, startosis_errors.NewInterpretationError(unexpectedArgNameError, planParamIndex, planParamName, firstParamName).ToAPIType()
}
}

if (isUsingDefaultMainFunction && mainFuncParamsNum == paramsRequiredForArgs) ||
(!isUsingDefaultMainFunction && mainFuncParamsNum > 0) {
Expand Down
Expand Up @@ -58,7 +58,7 @@ def run(plan):
validateScriptOutputFromPrintInstructions(t, instructions, expectedOutput)
}

func TestStartosisInterpreter_RandomMainFunctionAndParams(t *testing.T) {
func TestStartosisInterpreter_RandomMainFunctionAndParamsWithPlan(t *testing.T) {

packageContentProvider := mock_package_content_provider.NewMockPackageContentProvider()
runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
Expand Down Expand Up @@ -86,6 +86,31 @@ def deploy_contract(plan,service_name,contract_name,init_message,args):
validateScriptOutputFromPrintInstructions(t, instructions, expectedOutput)
}

func TestStartosisInterpreter_RandomMainFunctionAndParams(t *testing.T) {

packageContentProvider := mock_package_content_provider.NewMockPackageContentProvider()
runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
defer packageContentProvider.RemoveAll()
startosisInterpreter := NewStartosisInterpreter(testServiceNetwork, packageContentProvider, runtimeValueStore)
interpreter := startosisInterpreter
script := `
def my_func(my_arg1, my_arg2, args):
all_arg_values = my_arg1 + "--" + my_arg2 + "--" + args["arg1"] + ":" + args["arg2"]
return all_arg_values
`
mainFunctionName := "my_func"
inputArgs := `{"my_arg1": "foo", "my_arg2": "bar", "args": {"arg1": "arg1-value", "arg2": "arg2-value"}}`

result, instructions, interpretationError := interpreter.Interpret(context.Background(), startosis_constants.PackageIdPlaceholderForStandaloneScript, mainFunctionName, script, inputArgs)
require.Nil(t, interpretationError)
require.Len(t, instructions, 0) // There are no instructions to execute
require.NotNil(t, result)
expectedResult := "\"foo--bar--arg1-value:arg2-value\""
require.Equal(t, expectedResult, result)
}

func TestStartosisInterpreter_Test(t *testing.T) {
packageContentProvider := mock_package_content_provider.NewMockPackageContentProvider()
runtimeValueStore := runtime_value_store.NewRuntimeValueStore()
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/cli-reference/run-starlark.md
Expand Up @@ -46,14 +46,14 @@ If the flag `--main-function-name` is set, the JSON-serialized object will be us

For example, if the main function signature (inside this file github.com/my-org/my-package/src/entry.star) has this shape:
```python
# the plan object is injected always as the first argument
# the plan object will automatically be injected if the first argument name is 'plan'
def my_main_function(plan, first_argument, second_argument, their_argument):
# your code
```

It can be called like this:
```bash
# you don't have to pass the plan object as an argument because it will be automatically injected by default at the first position
# you don't have to pass the plan object as an argument because it will automatically be injected by default if the first argument name is 'plan'
kurtosis run main.star '{"first_argument": "Foo", "second_argument": "Bar", "their_argument": {"first-key:"first-value", "second-key":"second-value"}}' --main-file src/entry.star --main-function-name my_main_function
```

Expand Down

0 comments on commit 142ce42

Please sign in to comment.