-
Notifications
You must be signed in to change notification settings - Fork 513
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 Expand Headers Feature #117
Conversation
See discussion of the feature here: fullstorydev#116
@jhump As discussed here is the PR. Note I've made a few decisions regarding the functionality:
Open to changing the above. |
grpcurl.go
Outdated
@@ -161,6 +163,37 @@ func MetadataFromHeaders(headers []string) metadata.MD { | |||
return md | |||
} | |||
|
|||
/* Expands environmental variables contained in the header string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note I went with this comment format, as golint was complaining.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please stick with //
, for consistency in this file.
To appease go lint, you need the comment to start with the named of the doc'ed thing, in this case "Expand Headers":
// ExpandHeaders expands environment variable references contained in the string.
// etc...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
cmd/grpcurl/grpcurl.go
Outdated
@@ -313,6 +319,13 @@ func main() { | |||
return cc | |||
} | |||
|
|||
var headers []string | |||
if *expandHeaders { | |||
headers = grpcurl.ExpandHeaders(addlHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to expand rpcHeaders
and reflHeaders
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
grpcurl.go
Outdated
@@ -161,6 +163,37 @@ func MetadataFromHeaders(headers []string) metadata.MD { | |||
return md | |||
} | |||
|
|||
/* Expands environmental variables contained in the header string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please stick with //
, for consistency in this file.
To appease go lint, you need the comment to start with the named of the doc'ed thing, in this case "Expand Headers":
// ExpandHeaders expands environment variable references contained in the string.
// etc...
grpcurl.go
Outdated
expandedHeaders := make([]string, len(headers)) | ||
for idx, header := range headers { | ||
if header != "" { | ||
regex := regexp.MustCompile(`\${\w+}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely don't compile regex's in the loop. I know it's a simple and fast tool, so performance at this step may not usually be a concern. But we can at least hoist this out and only compile it once (make it an unexported package var
) in case anyone ends up using this API in an unexpected way in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Sorry for oversight.
grpcurl.go
Outdated
envVarValue := os.Getenv(result[2 : len(result)-1]) | ||
replacementValue := envVarValue | ||
// If no corresponding env var is found, leave the header as is. | ||
if len(envVarValue) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will be confusing. Based on how other tools generally work with env vars, I would expect either an error that my input is bad or missing env vars to interpolate the empty string.
I'm a bit partial to the former since it is more likely catch bugs.
Although this does bring up an interesting point: if you want to use a literal ${
in the value, this scheme is too simple and would result in an error. I think we need a way to escape the $
. For example, $$
or \$
meaning literal dollar sign, not the start of an env var reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Escaping can probably be left as a TODO since, by default, grpcurl does not try to do any variable expansion. So you can either do variable expansion or use literal dollar signs, which is likely sufficient for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, on the confusing behavior. Fixed.
Thanks for the great feedback, I will update shortly. |
Expand Headers should throw an error for unset environmental variables. Also address additional PR feedback.
@@ -313,6 +320,22 @@ func main() { | |||
return cc | |||
} | |||
|
|||
if *expandHeaders { | |||
var err error | |||
addlHeaders, err = grpcurl.ExpandHeaders(addlHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you wanted this DRY'd, was on the fence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking pretty good. I left several style nits that should hopefully be easy to address. After they are taken care of, I'll merge.
cmd/grpcurl/grpcurl.go
Outdated
rpcHeaders multiString | ||
reflHeaders multiString | ||
expandHeaders = flags.Bool("expand-headers", false, prettify(` | ||
If set any environmental variables contained contained in any additional, rpc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think some of the wording in here is a little awkward. How about something like this instead:
If set, headers may use '${NAME}' syntax to reference environment variables. These
will be expanded to the actual environment variable value before sending to the server.
For example, if there is an environment variable defined like FOO=bar, then a header of
'key: ${FOO}' would expand to 'key: bar'. This applies to -H, -rpc-header, and
-reflect-header options. No other expansion/escaping is performed. This can be used
to supply credentials/secrets without having to put them in command-line arguments.
cmd/grpcurl/grpcurl.go
Outdated
var err error | ||
addlHeaders, err = grpcurl.ExpandHeaders(addlHeaders) | ||
if err != nil { | ||
fail(err, "Failed to expand additional headers, missing environmental variable") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "environment variable" instead of "environmental variable"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the error returned from grpcurl.ExpandHeaders
should suffice so that you don't need the "missing environment variable" bit -- it just makes the error that gets printed unnecessarily longer.
grpcurl.go
Outdated
var envVarRegex = regexp.MustCompile(`\${\w+}`) | ||
|
||
// ExpandHeaders expands environmental variables contained in the header string. | ||
// If no corresponding environmental variable is found an error is thrown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "environment" instead of "environmental". Also "an error is returned" (not thrown).
grpcurl.go
Outdated
envVarName := result[2 : len(result)-1] | ||
envVarValue := os.Getenv(envVarName) | ||
replacementValue := envVarValue | ||
if len(envVarValue) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is best tested using os.LookupEnv
instead of os.GetEnv
. That way, an explicitly blank environment variable is allowed -- but an absent variable will result in an error:
envVarValue, ok := os.LookupEnv(envVarName)
if !ok {
return nil, fmt.Errorf("no variable %q is present in environment", envVarName)
}
grpcurl_test.go
Outdated
} | ||
for _, expandedHeader := range outHeaders { | ||
if _, ok := expectedHeaders[expandedHeader]; !ok { | ||
t.Errorf("The ExpandHeaders function has generated an unexpected header. Recieved unexpected header %s", expandedHeader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "Received" not "Recieved"
grpcurl.go
Outdated
for idx, header := range headers { | ||
if header != "" { | ||
results := envVarRegex.FindAllString(header, -1) | ||
if results != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is almost always best to check len(results) > 0
instead of testing against nil (nil and empty slices should be treated the same).
grpcurl.go
Outdated
func ExpandHeaders(headers []string) ([]string, error) { | ||
expandedHeaders := make([]string, len(headers)) | ||
for idx, header := range headers { | ||
if header != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of deepening the logic with indentation under an if
block, how about invert the condition:
if header == "" {
continue
}
Similar for below:
if len(results) == 0 {
expandedHeaders[idx] = headers[idx]
continue
}
grpcurl.go
Outdated
if results != nil { | ||
expandedHeader := header | ||
for _, result := range results { | ||
envVarName := result[2 : len(result)-1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a comment to clarify that these indices are to cut out the '${' in the beginning and the '}' at the end.
@@ -313,6 +320,22 @@ func main() { | |||
return cc | |||
} | |||
|
|||
if *expandHeaders { | |||
var err error | |||
addlHeaders, err = grpcurl.ExpandHeaders(addlHeaders) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine.
grpcurl.go
Outdated
envVarValue := os.Getenv(envVarName) | ||
replacementValue := envVarValue | ||
if len(envVarValue) == 0 { | ||
return nil, fmt.Errorf("environmental variable '%s' is not set", envVarName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to reference the actual bad input. Maybe instead this:
return nil, fmt.Errorf("header %q refers to missing environment variable %q", header, envVarName)
Also fix all other PR feedback.
See discussion of the feature here:
#116