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
Service ID lookup to include fallback to environment variable #320
Conversation
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.
@triblondon hopefully this helps...
@@ -35,7 +35,7 @@ func NewCreateCommand(parent cmd.Registerer, globals *config.Data) *CreateComman | |||
c.manifest.File.SetOutput(c.Globals.Output) | |||
c.manifest.File.Read(manifest.Filename) | |||
c.CmdClause = parent.Command("create", "Create a backend on a Fastly service version").Alias("add") | |||
c.CmdClause.Flag("service-id", "Service ID").Short('s').StringVar(&c.manifest.Flag.ServiceID) | |||
c.RegisterServiceIDFlag(&c.manifest.Flag.ServiceID) |
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.
- Example of new register function.
@@ -16,6 +16,14 @@ import ( | |||
"github.com/fastly/kingpin" | |||
) | |||
|
|||
// RegisterServiceIDFlag defines a --service-id flag that will attempt to |
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.
- Implementation of new register function.
@@ -80,6 +84,10 @@ func (d *Data) ServiceID() (string, Source) { | |||
return d.Flag.ServiceID, SourceFlag | |||
} | |||
|
|||
if sid := os.Getenv(env.ServiceID); sid != "" { |
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.
- New env var lookup option.
@@ -159,3 +160,42 @@ func TestManifestPrepend(t *testing.T) { | |||
t.Fatal(err) | |||
} | |||
} | |||
|
|||
func TestDataServiceID(t *testing.T) { |
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.
- Tests for the lookup behaviour.
@@ -10,4 +10,7 @@ const ( | |||
|
|||
// Endpoint is the env var we look in for the API endpoint. | |||
Endpoint = "FASTLY_API_ENDPOINT" | |||
|
|||
// ServiceID is the env var we look in for the required Service ID. | |||
ServiceID = "FASTLY_SERVICE_ID" |
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 new env var.
0ccec4e
to
a869c58
Compare
c.CmdClause.Flag("service-id", "Service ID").Short('s').Required().StringVar(&c.manifest.Flag.ServiceID) | ||
c.RegisterServiceIDFlag(&c.manifest.Flag.ServiceID) |
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 represents a change in behavior — a previously Required
flag is no longer required. It's not a... huge? problem, but it does have an IMO noticeable negative effect on user experience. If a command or subcommand requires a service ID, and I forget to provide one by any means, it's much better to get an error message from the CLI telling me I omitted a required parameter than from the API (or whatever) telling me my request was invalid.
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 understand that adding the fastly.toml option makes it tricky, but IMO validating required flags/params at the CLI layer is actually reasonably important...
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's much better to get an error message from the CLI telling me I omitted a required parameter than from the API (or whatever) telling me my request was invalid.
It's not the API telling you the Service ID is missing, but the CLI.
In each command's Exec
function we call a function which subsequently triggers:
Lines 125 to 128 in a869c58
serviceID, source := opts.Manifest.ServiceID() | |
if source == manifest.SourceUndefined { | |
return serviceID, serviceVersion, errors.ErrNoServiceID | |
} |
The logic for the ServiceID
function was updated as part of this PR to include an env var lookup:
cli/pkg/compute/manifest/manifest.go
Lines 81 to 96 in a869c58
// ServiceID yields a ServiceID. | |
func (d *Data) ServiceID() (string, Source) { | |
if d.Flag.ServiceID != "" { | |
return d.Flag.ServiceID, SourceFlag | |
} | |
if sid := os.Getenv(env.ServiceID); sid != "" { | |
return sid, SourceEnv | |
} | |
if d.File.ServiceID != "" { | |
return d.File.ServiceID, SourceFile | |
} | |
return "", SourceUndefined | |
} |
The --service-id
is not required in the sense that you aren't required to set the flag if you've got either the appropriate environment variable set, or you have a fastly.toml file.
The problem is that even when using something like Envar
to enable a failover to an environment variable (if set by the user), the help output would (unless I'm mistaken) still indicate that the flag itself was required, and that's not strictly speaking true (the Service ID is what's required, but how you provide it is flexible).
This is made harder due to the additional need to fallback to the fastly.toml manifest file.
An alternative, would be to move the fallback logic from our CLI into our fork of Kingpin so that we implement something similar to Envar
but which worked specifically for our use case. For example implementing a Fallback
chained function like:
- c.CmdClause.Flag("service-id", "Service ID").Short('s').Envar(env.ServiceID).Required().StringVar(&c.Input.ServiceID)
+ c.CmdClause.Flag("service-id", "Service ID").Short('s').Fallback(...).Required().StringVar(&c.Input.ServiceID)
...where Fallback()
excepted a slice of func types that could be executed in order. That way you still get the help output to show --service-id
is required, and then you could omit the flag and have it failover to one of the alternatives. I still don't like the fact that it might confuse users who see the help output tell them the flag is required and so they don't question it and feel obliged to always provide the flag.
It would be nice to signify that a 'value' is required without coupling it to a flag. AFAIK this sort of thing would be communicated via man cli
but as we're not generating a manual we have to rely on the Kingpin parser to indicate what data is required on a per command basis. So having it indicated via a flag is easier in that sense.
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 see I see, so the Required
bit in the clause is specifically expressing the semantic of: the literal flag must be provided. That's no good. It would be better if we could catch that during some kind of validation phase, before we got into the command code itself, but IMO that's "nice to have" rather than "important". Thanks for the detailed explanation 👍
An alternative . . .
Sure! An additional alternative — based on my intuition and not verified to actually be viable — might be to leverage the Pre-whatever hooks of the CmdClause to do this and perhaps additional validation as well.
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.
might be to leverage the Pre-whatever hooks of the CmdClause to do this and perhaps additional validation as well.
ah ok, I'm still very green when it comes to Kingpin so there could well be something for me to explore there 👍🏻
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.
Looks like from a brief investigation that none of the PreAction
s are able to execute before the Required()
validation (once the command has a flag defined as required that validation completes first). So at this point I don't think I want to spend too much more time on this. Would be good to revisit but only when it's more of a priority.
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.
Ah, no, I agree you can't use Required
. I was suggesting to drop all the Requireds (as you have done) and use the PreAction "stage" as a way to perform validation of flags, rather than in the command function itself. But again I haven't verified this will work and I don't think it's critical!! Just a suggestion for the great and unknowable future ⛵
* failover to env var * move manifest read up a line
* failover to env var * move manifest read up a line
* failover to env var * move manifest read up a line
* Service ID lookup to include fallback to environment variable (#320) * failover to env var * move manifest read up a line * fix snippet list * add new test scenarios for snippet describe * fix snippet update * ignore vendor directory * fix code to use go go-fastly 3.7.1 * update to latest go-fastly version 3.8.0
Problem: a Service ID must be explicitly set on each CLI invocation using the
--service-id
flag which is tedious.Solution: add an env var lookup between the flag and file lookup options.
Notes: to avoid having to duplicate the updated flag description (which is where I wanted to highlight to the user the lookup options) I've moved the flag to a separate function that each command calls.
Addresses: #301