-
Notifications
You must be signed in to change notification settings - Fork 16
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
Extract env var value read into a util #160
Conversation
pkg/utils/environment_variable.go
Outdated
} | ||
|
||
envVarValue := envVarValueOrDefault(envVarName, defaultValue) | ||
envVarValueDuration, err := time.ParseDuration(envVarValue) |
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.
why is this needed when you can just get it via 6*time.seconds
?
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.
but what if the value is from the env and not the default value? in that case it will be a 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.
that's why i think it's more simple to have them both as strings. this will be the case for every env var regardless to the expected type. The value is always a string and the logic that converts it to the required type is in the util. it will create a uniformity throughout configurable values in this repo and in our other k8s projects.
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.
the 6 would be from the env and timeseconds would be from the code
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 prefer not to do that
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.
yep, this won't work in the current product. i get a ERROR: 2020/09/10 11:56:22 config.go:128: CAKC010E Failed to parse CONJUR_TOKEN_TIMEOUT. Reason: time: missing unit in duration 6
error.
@izgeri when this env var was created how did you intend to set it? with 6m0s
? do you know who defined this?
do you see a problem with changing this to an int input so it will be CONJUR_TOKEN_TIMEOUT="6"
instead of CONJUR_TOKEN_TIMEOUT="6m0s"
?
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.
once we get to a decision i will update the README with the valid value pattern.
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.
aha, i can see that this env var was used before as:
- name: CONJUR_TOKEN_TIMEOUT
+ value: "15s"
(saw it in Slack) so i wouldn't change the UX to be of int to not break changes.
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.
@izgeri can you please review the commit i added with the readme change?
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'm pretty sure that CONJUR_TOKEN_TIMEOUT
was added for dev troubleshooting, though I see it is documented in the README
I think it is wise to set a standard and enforce it, we should just make sure that the standard (and the default, btw) is clear
pkg/utils/environment_variable.go
Outdated
defaultValue string, | ||
envVarValueOrDefaultFunc EnvVarValueOrDefaultFunc, | ||
) (int, error) { | ||
if envVarValueOrDefaultFunc == 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.
when would this not be nil
? an example would help me understand what we are trying to accomplish here (:
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.
IMO this breaking up of parameters style is really hard for my 👀 to follow
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.
look at the UT :)
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 the way we can pass mocks to the functions in go
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.
IMO this breaking up of parameters style is really hard for my 👀 to follow
i understand. i had a conversation about this with @sgnn7 yesterday. the alternative is a line that is too long which is an issue as well.
|
||
// DefaultClientCertRetryCountLimit is the amount of times we wait after successful | ||
// login for the client certificate file to exist, where each time we wait for a second. | ||
DefaultClientCertRetryCountLimit = 10 | ||
DefaultClientCertRetryCountLimit = "10" |
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.
are there minimum or maximum values for this limit? Can they put -5
for example? Would the logic hold up against negative numbers?
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.
In SP we added a check for envValueInt < minValue
with a minValue being 0 in order to avoid this
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.
hmmmm. do you think it is needed or is it too low level?
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.
do we want to not let users set (for example) CONJUR_TOKEN_TIMEOUT=-5
? looks like too much of an effort to me.
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 can totally see this being an issue especially because it is configurable no? It is coming from an environment and I don't trust user input 🙃
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.
yes, but it adds another param that the function consumer needs to set where for most of the times it will be 0
and not interesting. i'll let @izgeri give her take on this before going forward.
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 we should have error checking to ensure sane and acceptable values are input for this param. is the param currently documented? in the README or just in the CONTRIB? if it's just for dev purposes / dev troubleshooting, we could just leave undocumented for now. if we're adding it AND documenting it, then we need to be more careful (and this would need a changelog entry)
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 don't want to hold up this PR, but we should really consider doing a followup to allow for minimum/maximum range validation. In most cases, I'd bet that we can define hard-coded values for min and max values that are sensible and are liberal enough to cover all cases. For example, for certificate retries, we could set the minimum to 0 and the maximum to something astronomical (2**32 - 1
?) If we don't add range checks, I don't think we know what would happen if someone sets retries or some other configured numbered value to -1 (I'm sure it's not in our integration testing).
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.
by adding a minimum value we raise a few questions:
- what is the minimum value of
CONJUR_TOKEN_TIMEOUT
? - what is the minimum value of
CONJUR_CLIENT_CERT_RETRY_COUNT_LIMIT
? - what do we do in case the given value is less than the minimum? Do we fail the app or use the default value?
- does the answer to question kubernetes authenticator client works with conjur v5 #3 changes how we handle cases where the user gave an invalid value for
CONJUR_TOKEN_TIMEOUT
?
while these are good questions, i don't want to ask them in this PR and in this release that we are preparing. Once this is merged and the release is out there, we can look back at these questions and decide properly.
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.
created an issue for this: #162
93dbbff
to
f3dcfda
Compare
pkg/authenticator/config/config.go
Outdated
} | ||
defaultConfig.ClientCertRetryCountLimit = clientCertRetryCountLimit | ||
|
||
return defaultConfig, 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.
if we are updating this, can we change the name of this var? it's not the default config, it's the config that the client will use based on the default config and the user-provided config
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.
sure, nice catch.
f3dcfda
to
0f936ed
Compare
pkg/utils/environment_variable.go
Outdated
"github.com/cyberark/conjur-authn-k8s-client/pkg/log" | ||
) | ||
|
||
var defaultEnvVarValueOrDefault = envVarValueOrDefault |
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.
what is happening on this line?
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.
we set the function as a variable so it's clearer to the reader that the function being used in the one defined at the env of the file
0f936ed
to
b036163
Compare
CHANGELOG.md
Outdated
- Username formatting now correctly only prints FullUsername field ([#126](https://github.com/cyberark/conjur-authn-k8s-client/issues/126)) | ||
- Timeout for token retrievals is now correctly reset in consequent failures | ||
([cyberark/conjur-authn-k8s-client#158](https://github.com/cyberark/conjur-authn-k8s-client/issues/158)) |
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.
- Username formatting now correctly only prints FullUsername field ([#126](https://github.com/cyberark/conjur-authn-k8s-client/issues/126)) | |
- Timeout for token retrievals is now correctly reset in consequent failures | |
([cyberark/conjur-authn-k8s-client#158](https://github.com/cyberark/conjur-authn-k8s-client/issues/158)) | |
- Username formatting now correctly only prints `FullUsername` field. | |
[cyberark/conjur-authn-k8s-client#126](https://github.com/cyberark/conjur-authn-k8s-client/issues/126)) | |
- Timeout for token retrievals is now correctly reset in consecutive authentication failures. | |
[cyberark/conjur-authn-k8s-client#158](https://github.com/cyberark/conjur-authn-k8s-client/issues/158) |
I am not sure the user impact from these changes is clear
for the first, is it that the log messages now print a more succinct name or a more detailed name? I'd call that out specifically
Logs now correctly print only the Conjur identity without the policy branch prefix.
for the second option, it's more like
When authentication fails, the exponential backoff retry is correctly reset so that it will continue to attempt to authenticate until backoff is exhausted.
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.
thanks, that is much better
CHANGELOG.md
Outdated
- Wait slightly for the client certificate file to exist after login before | ||
raising an error. |
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.
What is the user impact of this change? How does it affect the UX?
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.
the client will fail less frequently as we now wait for up to 10 seconds to read the cert instead of immediately reading it. We are also logging our efforts so that's another customer-facing change. would you change the entry according to this?
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.
left a handful of comments, still would be good for @sgnn7 to weigh in since he chatted with you about some of the implementation details
README.md
Outdated
@@ -38,7 +38,7 @@ questions, please contact us on [Discourse](https://discuss.cyberarkcommons.org/ | |||
- `CONJUR_AUTHN_URL`: URL pointing to authenticator service endpoint | |||
- `CONJUR_AUTHN_LOGIN`: Host login for pod e.g. `namespace/service_account/some_service_account` | |||
- `CONJUR_SSL_CERTIFICATE`: Public SSL cert for Conjur connection | |||
- `CONJUR_TOKEN_TIMEOUT`: Timeout for fetching a new token (defaults to 6 minutes). In most cases, this variable should not be modified. | |||
- `CONJUR_TOKEN_TIMEOUT`: Timeout for fetching a new token (defaults to 6 minutes). In most cases, this variable should not be modified. The value should be in a signed sequence of decimal numbers with optional fraction and unit suffix (e.g "6m0s") |
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 should be broken into multiple lines
a signed sequence of decimal numbers with optional fraction and unit suffix
I don't know what this means without the example. is there a standard we could link to here?
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 took it from the golang docs for time
: https://golang.org/pkg/time/#ParseDuration. i'll add a link
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.
how about The value should be in a format that can be parsed with [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) (e.g "6m0s")
?
design/templates/solution_design.md
Outdated
@@ -0,0 +1,71 @@ | |||
# Solution Design - Template |
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.
does this indicate your branch may need a rebase?
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.
correct
|
||
// DefaultClientCertRetryCountLimit is the amount of times we wait after successful | ||
// login for the client certificate file to exist, where each time we wait for a second. | ||
DefaultClientCertRetryCountLimit = 10 | ||
DefaultClientCertRetryCountLimit = "10" |
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 we should have error checking to ensure sane and acceptable values are input for this param. is the param currently documented? in the README or just in the CONTRIB? if it's just for dev purposes / dev troubleshooting, we could just leave undocumented for now. if we're adding it AND documenting it, then we need to be more careful (and this would need a changelog entry)
pkg/utils/environment_variable.go
Outdated
} | ||
|
||
envVarValue := envVarValueOrDefault(envVarName, defaultValue) | ||
envVarValueDuration, err := time.ParseDuration(envVarValue) |
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'm pretty sure that CONJUR_TOKEN_TIMEOUT
was added for dev troubleshooting, though I see it is documented in the README
I think it is wise to set a standard and enforce it, we should just make sure that the standard (and the default, btw) is clear
b036163
to
b45b147
Compare
|
we enforce it in the error will be |
b45b147
to
6eb0e94
Compare
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 good stuff, thanks for moving this logic to a re-usable util package!
I've included some suggestions on how the code might be refactored to simplify and maybe help readability and code coverage.
pkg/utils/environment_variable.go
Outdated
if err != nil { | ||
// We return -1 as we can't return nil for int. This value will not be used | ||
// as err is not nil. | ||
return -1, log.RecordedError(log.CAKC010E, envVarName, err.Error()) |
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.
The convention that I've seen is that when returning an error along with other return values, the other return values should be set to their zero values (i.e. 0
for int returns).
I can't find anything official in Golang docs, but I did find this:
https://www.digitalocean.com/community/tutorials/handling-errors-in-go#:~:text=When%20a%20function%20returns%20multiple,value%20to%20a%20zero%20value.
When a function returns multiple values, including an error, convention requests that we return the error as the last item. When returning an error from a function with multiple return values, idiomatic Go code also will set each non-error value to a zero value. Zero values are, for example, an empty string for strings, 0 for integers, an empty struct for struct types, and nil for interface and pointer types, to name a few.
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.
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.
So I am understanding correctly if we return an error (for example return -1, log.RecordedError(log.CAKC010E, envVarName, err.Error())
), no matter what, Golang will zero the return values? So for example, -1 will be 0 because it is an integer?
@diverdane @orenbm
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 haven't verified it but in any case we will return 0 to maintain the standard
} | ||
|
||
func TestEnvironmentVariable(t *testing.T) { | ||
Convey("IntFromEnvOrDefault", t, func() { |
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 we're deprecating the use of Convey
? But personally I'm fine with it... it tells the story of what you're testing.
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.
@diverdane what are we moving to if not Convey
|
||
// DefaultClientCertRetryCountLimit is the amount of times we wait after successful | ||
// login for the client certificate file to exist, where each time we wait for a second. | ||
DefaultClientCertRetryCountLimit = 10 | ||
DefaultClientCertRetryCountLimit = "10" |
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 don't want to hold up this PR, but we should really consider doing a followup to allow for minimum/maximum range validation. In most cases, I'd bet that we can define hard-coded values for min and max values that are sensible and are liberal enough to cover all cases. For example, for certificate retries, we could set the minimum to 0 and the maximum to something astronomical (2**32 - 1
?) If we don't add range checks, I don't think we know what would happen if someone sets retries or some other configured numbered value to -1 (I'm sure it's not in our integration testing).
b206292
to
bbea837
Compare
We would like to better UT the logic of reading a value from the env and defaulting to a default value if it's not present. We also have the logic that transforms this env var value into an int or a duration (and maybe more in the future) and it is best that this logic is UTed and is implemented in its own util. This improves the readability of the config.go file and of the util functions themselves. This will also let us use these utils in other projects that consume the authn-client without implementing them there as well.
bbea837
to
18302b6
Compare
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.
LGTM great work!
We would like to better UT the logic of reading a value from the env
and defaulting to a default value if it's not present. We also have the
logic that transforms this env var value into an int or a duration (and
maybe more in the future) and it is best that this logic is UTed and is
implemented in its own util. This improves the readability of the
config.go file and of the util functions themselves.
This will also let us use these utils in other projects that consume the
authn-client without implementing them there as well.