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

Initial attempt to wire-up parameters #66

Merged
merged 6 commits into from
Nov 30, 2018
Merged

Initial attempt to wire-up parameters #66

merged 6 commits into from
Nov 30, 2018

Conversation

jeremyrickard
Copy link
Contributor

@jeremyrickard jeremyrickard commented Nov 29, 2018

This uses mitchellh's reflectwalk to walk the step's Data map. It currently
handles replacing things of the form source: bundle.parameter.name in a map
or an Array. We can implement additional callbacks later on if needed to handle
other datatypes that might be in the YAML.

For example:

name: HELLO
version: 0.1.0
invocationImage: jeremyrickard/porter-hello:latest

parameters:
  - name: hello
    default: jeremy

install:
- description: "Say Hello World"
  exec:
    command: bash
    arguments:
    - -c
    - echo Hello World
    parameters:
      thing:
        source: bundle.parameters.hello
      something: source
      hello: 5
- description: "Say another"
  exec:
    command: echo
    arguments:
    - "source: bundle.parameters.hello"
    - "something else"

Both the thing entry in parameters and the first entry of the arguments array will be updated to the value of the "hello" parameter.

This uses mitchellh's reflectwalk to walk the step's Data map. It currently
handles replacing things of the form source: bundle.parameter.name in a map
or an Array. We can implement additional callbacks later on if needed to handle
other datatypes that might be in the YAML.
return nil
}

func (p *Porter) Slice(val reflect.Value) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the map function are empty just to implement the interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to implement this interface because we are passing in an instance of porter to reflectwalk.Walk, yeah?

You're closer to this than me, do you think it's a small enough change to have this pass in just the manifest instead?

}

func (p *Porter) resolveValue(key string) (interface{}, error) {
source := strings.Split(key, ".")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the handling of this probably needs to change as we get more complicated "sourcing" like when dependencies are introduced.

v = v.Elem()
}
if kind := v.Kind(); kind == reflect.Map {
if len(v.MapKeys()) == 1 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if something has more than one key, it seems like not something to replace so this is short circuiting that logic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, probably a good idea to put what you just said in a comment so that we don't forget what's going on here. 😀

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really good to have because in another part of the manifest I want to use source again, and this lets me get away with it. e.g.

dependencies:
  - name: mysql
     connections:
        - source: bundle.parameters.foo
           destination: bundle.dependencies.mysql.parameters.foo

@jeremyrickard
Copy link
Contributor Author

Right now, if I encounter a "source: <...>" thing that isn't resolvable, that's an error condition. After thinking about it a bit, it seems like we should maybe just log that that status occured but not error to cover situations where someone wanted to use that as a map key or it's in a string that happens to match that regex?

Copy link
Member

@carolynvs carolynvs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good to merge as-is if you want. Everything I have in here is just fine as follow-ups.

@@ -290,6 +290,14 @@
pruneopts = "NUT"
revision = "95e2e42e3ece7421c14512f79f796fe3f5d7e5dc"

[[projects]]
digest = "1:44e35ced240c6d8316dc8d604a73774ab052d40ebe636fd8f2ba5934f6329034"
name = "github.com/mitchellh/reflectwalk"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this package, so useful. 💕

v = v.Elem()
}
if kind := v.Kind(); kind == reflect.Map {
if len(v.MapKeys()) == 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, probably a good idea to put what you just said in a comment so that we don't forget what's going on here. 😀

value := kv.String()
replacement, err := p.resolveValue(value)
if err != nil {
return errors.Wrap(err, "unable to source value")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have enough context here that the message could say which value it couldn't find? Or is that baked in the error that we are wrapping?

return nil
}

func (p *Porter) Slice(val reflect.Value) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to implement this interface because we are passing in an instance of porter to reflectwalk.Walk, yeah?

You're closer to this than me, do you think it's a small enough change to have this pass in just the manifest instead?

v = v.Elem()
}
if kind := v.Kind(); kind == reflect.Map {
if len(v.MapKeys()) == 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really good to have because in another part of the manifest I want to use source again, and this lets me get away with it. e.g.

dependencies:
  - name: mysql
     connections:
        - source: bundle.parameters.foo
           destination: bundle.dependencies.mysql.parameters.foo

source := matches[1]
r, err := p.resolveValue(source)
if err != nil {
return errors.Wrap(err, "unable to source value")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, can we make this error message provide a bit more context into exactly which source node isn't resolving?

} else if param.Destination == nil && param.Destination.Path != "" {
replacement = param.Destination.Path
} else {
return nil, errors.New("unknown parameter definition")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the problem here that the parameter that it's trying to source from isn't declared? Maybe this error should be reference to declared parameter bundle.parameters.foo in <insert some context so we know which ones is borked.

} else if cred.EnvironmentVariable != "" {
replacement = os.Getenv(cred.EnvironmentVariable)
} else {
return nil, errors.New("unknown credential definition")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, if we can, let's provide more context

}
}
} else {
return nil, errors.New(fmt.Sprintf("unknown parameter source: %s", source[1]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this error case? I can't quite tell from the error message. Is it that the key used is invalid, e.g. not path or environment variable?

},
}

os.Setenv("PERSON", "Ralpha")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:excellent:

@carolynvs
Copy link
Member

carolynvs commented Nov 29, 2018

After thinking about it a bit, it seems like we should maybe just log that that status occured but not error to cover situations where someone wanted to use that as a map key or it's in a string that happens to match that regex

Do you have an example of when we could get a false positive? I would hate to make it super easy for a typo in the source path to go unnoticed. Like I fat finger bundle.dependencies.icantspell and then don't see the warning in the scrollback, and then wonder why the heck nothing works days later when I try to use the bundle I built. 😀

If we do have cases where it doesn't resolve, and that's expected, maybe let people add a flag for strict resolution vs. loose? So that we fail immediately, then let them know that if we are being too aggressive in our matching, use a flag when building to ignore the problem?

@jeremyrickard
Copy link
Contributor Author

After thinking about it a bit, it seems like we should maybe just log that that status occured but not error to cover situations where someone wanted to use that as a map key or it's in a string that happens to match that regex

Do you have an example of when we could get a false positive? I would hate to make it super easy for a typo in the source path to go unnoticed. Like I fat finger bundle.dependencies.icantspell and then don't see the warning in the scrollback, and then wonder why the heck nothing works days later when I try to use the bundle I built.

If we do have cases where it doesn't resolve, and that's expected, maybe let people add a flag for strict resolution vs. loose? So that we fail immediately, then let them know that if we are being too aggressive in our matching, use a flag when building to ignore the problem?

No, I was mostly thinking proactively, but I agree with that case beacuse I can't spell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants