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

Add support for mapped dependency outputs #3058

Merged
merged 8 commits into from
May 15, 2024
32 changes: 31 additions & 1 deletion pkg/runtime/runtime_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,30 @@ func (m *RuntimeManifest) buildSourceData() (map[string]interface{}, error) {
return data, nil
}

func (m *RuntimeManifest) buildAndResolveMappedDependencyOutputs(sourceData map[string]interface{}) error {
for _, manifestDep := range m.Dependencies.Requires {
var depBun = sourceData["bundle"].(map[string]interface{})["dependencies"].(map[string]interface{})[manifestDep.Name].(map[string]interface{})
var depOutputs map[string]interface{}
if depBun["outputs"] == nil {
depOutputs = make(map[string]interface{})
depBun["outputs"] = depOutputs
} else {
depOutputs = depBun["outputs"].(map[string]interface{})
}

for outputName, mappedOutput := range manifestDep.Outputs {
mappedOutputTemplate := m.GetTemplatePrefix() + mappedOutput
renderedOutput, err := mustache.RenderRaw(mappedOutputTemplate, true, sourceData)
if err != nil {
return fmt.Errorf("unable to render dependency %s output template %s: %w", manifestDep.Name, mappedOutput, err)
}
depOutputs[outputName] = renderedOutput
}
}

return nil
}

// ResolveStep will walk through the Step's data and resolve any placeholder
// data using the definitions in the manifest, like parameters or credentials.
func (m *RuntimeManifest) ResolveStep(ctx context.Context, stepIndex int, step *manifest.Step) error {
Expand All @@ -428,6 +452,13 @@ func (m *RuntimeManifest) ResolveStep(ctx context.Context, stepIndex int, step *
return log.Error(fmt.Errorf("unable to build step template data: %w", err))
}

mustache.AllowMissingVariables = false

err = m.buildAndResolveMappedDependencyOutputs(sourceData)
schristoff marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return log.Errorf("unable to build and resolve mapped dependency outputs: %w", err)
}

// Get the original yaml for the current step
stepPath := fmt.Sprintf("%s[%d]", m.Action, stepIndex)
stepTemplate, err := m.getStepTemplate(stepPath)
Expand All @@ -439,7 +470,6 @@ func (m *RuntimeManifest) ResolveStep(ctx context.Context, stepIndex int, step *
//fmt.Fprintf(m.Err, "=== Step Data ===\n%v\n", sourceData)
m.debugf(log, "=== Step Template ===\n%v\n", stepTemplate)

mustache.AllowMissingVariables = false
rendered, err := mustache.RenderRaw(stepTemplate, true, sourceData)
if err != nil {
return log.Errorf("unable to render step template %s: %w", stepTemplate, err)
Expand Down
89 changes: 89 additions & 0 deletions pkg/runtime/runtime_manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,95 @@ install:
assert.Equal(t, []string{"mysql-password", "password"}, gotSensitiveValues, "Incorrect values were marked as sensitive")
}

func TestResolveStep_DependencyMappedOutput(t *testing.T) {
schristoff marked this conversation as resolved.
Show resolved Hide resolved
ctx := context.Background()
pCtx := portercontext.NewTestContext(t)

mContent := `schemaVersion: 1.0.0
dependencies:
requires:
- name: mysql
bundle:
reference: "getporter/porter-mysql"
outputs:
mappedOutput: Mapped

install:
- mymixin:
Arguments:
- ${ bundle.dependencies.mysql.outputs.mappedOutput }
`
rm := runtimeManifestFromStepYaml(t, pCtx, mContent)
rm.bundles = map[string]cnab.ExtendedBundle{
"mysql": cnab.NewBundle(bundle.Bundle{}),
}

s := rm.Install[0]
err := rm.ResolveStep(ctx, 0, s)
require.NoError(t, err)

require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type")
mixin := s.Data["mymixin"].(map[string]interface{})
require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type")
args := mixin["Arguments"].([]interface{})

assert.Equal(t, []interface{}{"Mapped"}, args, "Incorrect template args passed to the mixin step")
}

func TestResolveStep_DependencyTemplatedMappedOutput(t *testing.T) {
ctx := context.Background()
pCtx := portercontext.NewTestContext(t)
pCtx.Setenv("PORTER_MYSQL_PASSWORD_DEP_OUTPUT", "password")

mContent := `schemaVersion: 1.0.0
dependencies:
requires:
- name: mysql
bundle:
reference: "getporter/porter-mysql"
outputs:
mappedOutput: Mapped ${ bundle.dependencies.mysql.outputs.password }
schristoff marked this conversation as resolved.
Show resolved Hide resolved

install:
- mymixin:
Arguments:
- ${ bundle.dependencies.mysql.outputs.mappedOutput }
`
rm := runtimeManifestFromStepYaml(t, pCtx, mContent)
ps := cnab.ParameterSources{}
ps.SetParameterFromDependencyOutput("porter-mysql-password", "mysql", "password")
rm.bundle = cnab.NewBundle(bundle.Bundle{
Custom: map[string]interface{}{
cnab.ParameterSourcesExtensionKey: ps,
},
RequiredExtensions: []string{cnab.ParameterSourcesExtensionKey},
})

rm.bundles = map[string]cnab.ExtendedBundle{
"mysql": cnab.NewBundle(bundle.Bundle{
Outputs: map[string]bundle.Output{
"password": {
Definition: "password",
},
},
Definitions: map[string]*definition.Schema{
"password": {WriteOnly: makeBoolPtr(true)},
},
}),
}

s := rm.Install[0]
err := rm.ResolveStep(ctx, 0, s)
require.NoError(t, err)

require.IsType(t, map[string]interface{}{}, s.Data["mymixin"], "Data.mymixin has incorrect type")
mixin := s.Data["mymixin"].(map[string]interface{})
require.IsType(t, mixin["Arguments"], []interface{}{}, "Data.mymixin.Arguments has incorrect type")
args := mixin["Arguments"].([]interface{})

assert.Equal(t, []interface{}{"Mapped password"}, args, "Incorrect template args passed to the mixin step")
}

func TestResolveInMainDict(t *testing.T) {
ctx := context.Background()
c := config.NewTestConfig(t)
Expand Down
Loading