Skip to content

Commit

Permalink
Fixes after review
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Kuziemko committed Mar 15, 2022
1 parent bbfc2b8 commit d00eed2
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 147 deletions.
8 changes: 8 additions & 0 deletions hack/images/jinja2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ postgres
Prefix for db was removed. This is Jinja limitation. It shouldn't be a big problem as long
as there is no need to render the template twice with the same prefix.

### Configuration

There is a possibility of pre-processing data by setting options in the configuration file.
List of supported operations:
| Name | Default | Description |
| ------------------------- | ------------ | ---------------------------------------------------------------------------------------------------|
| prefix | "" | Adds a prefix to inputted data. The data will be accessible using the set prefix. |
| unpackValue | False | If the `value` prefix is set in the inputted data then the data will be unpacked from that prefix. |

## Prerequisites

Expand Down
17 changes: 12 additions & 5 deletions hack/images/jinja2/jinja2-cli/jinja2cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,17 +312,13 @@ def cli(opts, args, config): # noqa: C901

out = codecs.getwriter("utf8")(out)

if config.get("prefix") is not None and len(parsed_data) != 0:
parsed_data = {config["prefix"]: parsed_data}
if config.get("strip-value") is True and len(parsed_data) != 0 and "value" in parsed_data:
parsed_data = parsed_data.get("value", {})
parsed_data = preprocessing_data(config, parsed_data)

template_path = os.path.abspath(template_path)
out.write(render(template_path, parsed_data, extensions, opts.filters, opts.strict))
out.flush()
return 0


def parse_kv_string(pairs):
dict_ = {}
for pair in pairs:
Expand All @@ -331,6 +327,17 @@ def parse_kv_string(pairs):
return dict_


def preprocessing_data(config, data):
'''Return preprocessed data based on the applied configuration.'''

if config.get("unpackValue") is True and len(data) != 0 and "value" in data:
data = data.get("value", {})

if config.get("prefix") is not None and len(data) != 0:
data = {config["prefix"]: data}

return data

class LazyHelpOption(Option):
"An Option class that resolves help from a callable"

Expand Down
59 changes: 46 additions & 13 deletions hack/images/jinja2/jinja2-cli/tests/test_jinja2cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,76 +12,82 @@


@dataclass
class TestCase:
class RenderTestCase:
name: str
template: str
data: typing.Dict[str, typing.Any]
result: str

@dataclass
class PreprocessingDataTestCase:
name: str
config: typing.Dict[str, typing.Any]
data: typing.Dict[str, typing.Any]
result: typing.Dict[str, typing.Any]

render_testcases = [
TestCase(name="empty", template="", data={}, result=""),
TestCase(
RenderTestCase(name="empty", template="", data={}, result=""),
RenderTestCase(
name="simple",
template="<@ title @>",
data={"title": b"\xc3\xb8".decode("utf8")},
result=b"\xc3\xb8".decode("utf8"),
),
TestCase(
RenderTestCase(
name="prefix",
template="<@ input.key @>",
data={"input": {"key": "value"}},
result="value",
),
TestCase(
RenderTestCase(
name="two prefixes but one provided",
template="<@ input.key @>/<@ additionalinput.key @>",
data={"input": {"key": "value"}},
result="value/<@ additionalinput.key @>",
),
TestCase(
RenderTestCase(
name="missing prefix",
template="<@ input.key @>",
data={},
result="<@ input.key @>",
),
TestCase(
RenderTestCase(
name="items before attrs",
template="<@ input.values.key @>",
data={"input": {"values": {"key": "value"}}},
result="value",
),
TestCase(
RenderTestCase(
name="attrs still working",
template="<@ input.values() @>",
data={"input": {}},
result="dict_values([])",
),
TestCase(
RenderTestCase(
name="key with dot",
template="<@ input['foo.bar'] @>",
data={"input": {"foo.bar": "value"}},
result="value",
),
TestCase(
RenderTestCase(
name="missing key with dot",
template='<@ input["foo.bar"] @>',
data={},
result='<@ input["foo.bar"] @>',
),
TestCase(
RenderTestCase(
name="use default value",
template='<@ input["foo.bar"] | default("hello") @>',
data={},
result="hello",
),
TestCase(
RenderTestCase(
name="multiple dotted values",
template='<@ input.key.key["foo.bar/baz"] | default("hello") @>',
data={},
result="hello",
),
TestCase(
RenderTestCase(
name="multiline strings",
template="""<@ input.key.key["foo.bar/baz"] | default('hello
hello') @>""",
Expand All @@ -100,6 +106,33 @@ def test_render(tmp_path, case):
assert output == case.result


preprocessing_data_testcases = [
PreprocessingDataTestCase(
name="set prefix in the config should prefix the data",
config={"prefix": "testprefix"},
data = {"test": "test"},
result={"testprefix": {"test": "test"}}
),
PreprocessingDataTestCase(
name="set unpackValue in the config should remove the value prefix",
config={"unpackValue": True},
data = {"value": {"test": "test"}},
result={"test": "test"}
),
PreprocessingDataTestCase(
name="set unpackValue and prefix should output correct results",
config={"prefix": "testprefix", "unpackValue": True},
data = {"value": {"test": "test"}},
result={"testprefix": {"test": "test"}}
)
]

@pytest.mark.parametrize("case", preprocessing_data_testcases)
def test_preprocessing_data(case):
output = cli.preprocessing_data(case.config,case.data)
assert output == case.result


def test_random_password(tmp_path):
random_pass_path = tmp_path / "random.template"
random_pass_path.write_text("<@ random_password(length=4) @>")
Expand Down
3 changes: 1 addition & 2 deletions hack/images/merger/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

This folder contains the Docker image which merges multiple input YAML files into a single one.

The Docker image contains the `merger.sh` helper script. The script is an entrypoint of the image, and it is used to prefix and merge all YAML files found in `$SRC` directory.
Each file is prefixed with a file name without extension.
The Docker image contains the `merger.sh` helper script. The script is an entrypoint of the image, and it is used to prefix and merge all YAML files found in `$SRC` directory. Additionally, if the YAML file contains the `value` key, then it is unpacked from that key. Each file is prefixed with a file name without extension.

## Installation

Expand Down
15 changes: 4 additions & 11 deletions internal/installation/capact_register.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,22 +177,19 @@ func (i *CapactRegister) produceHelmReleaseTypeInstance(helmRelease *release.Rel
return nil, errors.Wrap(err, "while producing Helm release definition")
}

var unmarshalled map[string]interface{}
var unmarshalled interface{}
err = yaml.Unmarshal(releaseOut, &unmarshalled)
if err != nil {
return nil, errors.Wrap(err, "while unmarshaling bytes")
}
if _, ok := unmarshalled["value"]; !ok {
return nil, errors.Wrap(err, "while getting value from unmarshalled additional output")
}

return &gqllocalapi.CreateTypeInstanceInput{
Alias: ptr.String(helmRelease.Name),
TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{
Path: helmReleaseTypeRefPath,
Revision: "0.1.0",
},
Value: unmarshalled["value"],
Value: unmarshalled,
}, nil
}

Expand All @@ -210,22 +207,18 @@ func (i *CapactRegister) produceConfigTypeInstance(ownerName string, helmRelease
return nil, errors.Wrap(err, "while producing additional info")
}

var unmarshalled map[string]interface{}
var unmarshalled interface{}
err = yaml.Unmarshal(data, &unmarshalled)
if err != nil {
return nil, errors.Wrap(err, "while unmarshaling bytes")
}

if _, ok := unmarshalled["value"]; !ok {
return nil, errors.Wrap(err, "while getting value from unmarshalled additional output")
}

return &gqllocalapi.CreateTypeInstanceInput{
Alias: ptr.String(ownerName),
TypeRef: &gqllocalapi.TypeInstanceTypeReferenceInput{
Path: capactTypeRefPath,
Revision: "0.1.0",
},
Value: unmarshalled["value"],
Value: unmarshalled,
}, nil
}
2 changes: 1 addition & 1 deletion pkg/argo-actions/download_type_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (d *Download) Do(ctx context.Context) error {
return fmt.Errorf("failed to find TypeInstance with ID %q", config.ID)
}

data, err := yaml.Marshal(typeInstance.LatestResourceVersion.Spec.Value)
data, err := yaml.Marshal(typeInstance.LatestResourceVersion.Spec)
if err != nil {
return errors.Wrap(err, "while marshaling TypeInstance to YAML")
}
Expand Down
5 changes: 0 additions & 5 deletions pkg/argo-actions/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,3 @@ func ErrMissingTypeInstanceValue(typeInstanceName string) error {
func ErrMissingResourceVersion() error {
return errors.Errorf("resourceVersion is missing")
}

// ErrTypeConversion returns an error indicating incorrect type conversion.
func ErrTypeConversion(field string, targetType string, typeInstanceName string) error {
return errors.Errorf("%s cannot be converted to %s for TypeInstances %s", field, targetType, typeInstanceName)
}
24 changes: 23 additions & 1 deletion pkg/argo-actions/update_type_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package argoactions

import (
"context"
"encoding/json"
"io/ioutil"
"path"
"path/filepath"
Expand Down Expand Up @@ -116,7 +117,28 @@ func (u *Update) render(payload []graphqllocal.UpdateTypeInstancesInput, values
return ErrMissingTypeInstanceValue(typeInstance.ID)
}

typeInstance.TypeInstance.Value = value
if _, ok = value["value"]; !ok {
// for backward compatibility, if there is an artifact without value/backend syntax,
// treat it as a value for TypeInstance
typeInstance.TypeInstance.Value = value
continue
}

data, err := json.Marshal(value)
if err != nil {
return errors.Wrap(err, "while marshaling TypeInstance")
}

unmarshalledTI := graphqllocal.UpdateTypeInstanceInput{}
err = json.Unmarshal(data, &unmarshalledTI)
if err != nil {
return errors.Wrap(err, "while unmarshaling TypeInstance")
}

typeInstance.TypeInstance.Value = unmarshalledTI.Value
if unmarshalledTI.Backend != nil {
typeInstance.TypeInstance.Backend = unmarshalledTI.Backend
}
}
return nil
}
Expand Down
35 changes: 16 additions & 19 deletions pkg/argo-actions/upload_type_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package argoactions

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
Expand Down Expand Up @@ -119,31 +120,27 @@ func (u *Upload) render(payload *graphqllocal.CreateTypeInstancesInput, values m
return ErrMissingTypeInstanceValue(*typeInstance.Alias)
}

// render value
if _, ok = value["value"]; ok {
typeInstanceValue, ok := value["value"].(map[string]interface{})
if !ok {
return ErrTypeConversion("value", "map[string]interface{}", *typeInstance.Alias)
}
typeInstance.Value = typeInstanceValue
} else {
if _, ok = value["value"]; !ok {
// for backward compatibility, if there is an artifact without value/backend syntax,
// treat it as a value for TypeInstance
typeInstance.Value = value
continue
}

// render backend
if _, ok := value["backend"]; ok {
typeInstanceBackend, ok := value["backend"].(map[string]interface{})
if !ok {
return ErrTypeConversion("backend", "map[string]interface{}", *typeInstance.Alias)
}
backendContext, ok := typeInstanceBackend["context"].(map[string]interface{})
if !ok {
return ErrTypeConversion("backend.context", "map[string]interface{}", *typeInstance.Alias)
}
typeInstance.Backend.Context = backendContext
data, err := json.Marshal(value)
if err != nil {
return errors.Wrap(err, "while marshaling TypeInstance")
}

unmarshalledTI := graphqllocal.CreateTypeInstanceInput{}
err = json.Unmarshal(data, &unmarshalledTI)
if err != nil {
return errors.Wrap(err, "while unmarshaling TypeInstance")
}

typeInstance.Value = unmarshalledTI.Value
if unmarshalledTI.Backend != nil {
typeInstance.Backend.Context = unmarshalledTI.Backend.Context
}
}
return nil
Expand Down
12 changes: 10 additions & 2 deletions pkg/runner/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ func (r *helmRunner) readCommandData(in runner.StartInput) (Input, error) {

func (r *helmRunner) saveOutput(out Output) error {
r.log.Debug("Saving Helm release output", zap.String("path", r.cfg.Output.HelmReleaseFilePath))
err := runner.SaveToFile(r.cfg.Output.HelmReleaseFilePath, out.Release)
bytesRelease, err := runner.NestingOutputUnderValue(out.Release)
if err != nil {
return errors.Wrap(err, "while nesting Terrafrom release under value")
}
err = runner.SaveToFile(r.cfg.Output.HelmReleaseFilePath, bytesRelease)
if err != nil {
return errors.Wrap(err, "while saving Helm release output")
}
Expand All @@ -132,7 +136,11 @@ func (r *helmRunner) saveOutput(out Output) error {
}

r.log.Debug("Saving additional output", zap.String("path", r.cfg.Output.AdditionalFilePath))
err = runner.SaveToFile(r.cfg.Output.AdditionalFilePath, out.Additional)
bytesAdditional, err := runner.NestingOutputUnderValue(out.Additional)
if err != nil {
return errors.Wrap(err, "while nesting Terrafrom additional under value")
}
err = runner.SaveToFile(r.cfg.Output.AdditionalFilePath, bytesAdditional)
if err != nil {
return errors.Wrap(err, "while saving default output")
}
Expand Down
Loading

0 comments on commit d00eed2

Please sign in to comment.