-
Notifications
You must be signed in to change notification settings - Fork 50
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
hcp_packer_image channel attribute support #339
Conversation
f81915f
to
c7460a6
Compare
@@ -251,6 +251,35 @@ func getIterationIDFromFingerPrint(t *testing.T, bucketSlug string, fingerprint | |||
return ok.Payload.Iteration.ID, nil | |||
} | |||
|
|||
func getBuildIDFromIteration(t *testing.T, bucketSlug, iterID, cloudProvider string) (string, 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.
Where is this function called from?
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 called in the channelAccept test: https://github.com/hashicorp/terraform-provider-hcp/pull/339/files#diff-812fcf1f8adbdbf65c25344beec1814ebf9448de4982f3e52479c3700880fdd5R202
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 a little concerned about the optional
fields in the docs that are not really optional and also the fact that terraform validate
can validate those properly.
In regards to hcp_packer_iteration
I think we should keep it valid for now while we observe people migrating to use just hcp_packer_image
. The iteration data source returns information about the iteration that the image doesn't, I can't tell right now how useful those information are for the user, so I definitely not deprecate right now 🤔
docs/data-sources/packer_image.md
Outdated
- `channel` (String) Channel that promotes the latest iteration of the image. Either this or `iteration_id` must be specified. | ||
- `iteration_id` (String) HCP ID of this image. Either this or `channel' must be specified. |
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 concerned about these being optional here. If they are not set this data source won't work and the user will figure out about this requirement with the error message. Is it possible to write this file manually and skip the generation of these two?
This is the plugin used to generate the docs https://github.com/hashicorp/terraform-plugin-docs
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.
Not certain this is possible, the CI enforces the generated docs being the same as the ones committed :/
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 written based on this template https://github.com/hashicorp/terraform-provider-hcp/blob/main/templates/data-sources/packer_image.md.tmpl.
On top, there's a Description
which is populated with the schema description. So we need to update that description as well and mention that this data source needs an iteration id or a channel to query the propper image.
Description: "HCP ID of this image. Either this or `channel' must be specified.", | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ExactlyOneOf: []string{"channel"}, |
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: on terraform validate
this one works when none is set but it doesn't when both are set.
➜ terraform validate
Success! The configuration is valid.
➜ terraform validate
╷
│ Error: Invalid combination of arguments
│
│ with data.hcp_packer_image.source_ami,
│ on main.tf line 16, in data "hcp_packer_image" "source_ami":
│ 16: data "hcp_packer_image" "source_ami" {
│
│ "iteration_id": one of `channel,iteration_id` must be specified
╵
➜ tf-modules-local git:(tfc_run_task_example) ✗
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.
Here I would expect that the provider would fail if both fields are unset. I think we can have both fields optional and possibly computed (need to think through this a bit more) and fail in the read if neither is set. If both are set then you would fail as well since the fields need to be mutually exclusive.
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 problem is slightly more complex here, it fails at plan/apply time (there's a test for the literals-thing, I'm adding one for the static + reference) but not necessarily at validation-time currently, as it only works when none of the fields are computed.
According to @bflad the terraform-plugin-framework will let us write custom validators that we can use for this kind of use-case, but as it stands we cannot do better I'm afraid.
} | ||
} | ||
|
||
var diags diag.Diagnostics |
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 variable is never being set
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.
True, that's an artifact from when both were allowed so long as there wasn't a conflict.
I'll remove this
"iteration_id": { | ||
Description: "HCP ID of this image. Either this or `channel' must be specified.", | ||
Type: schema.TypeString, | ||
Optional: true, |
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 do you think about making this value Compute
and Optional
and setting this as an output of this data source as well? This way, users are able to know what's the iteration being queried
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 actually already set but in the output from the build response, in this case, it makes sense to make this one compute.
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 agree this one can be promoted to both Optional
and Compute
since it's either copied from the input, or derived from the channel, I'll do this if the Schema lets me
docs/data-sources/packer_image.md
Outdated
- `region` (String) Region this image is stored in, if any. | ||
|
||
### Optional | ||
|
||
- `channel` (String) Channel that promotes the latest iteration of the image. Either this or `iteration_id` must be specified. |
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 data source is not actually promoting an image. So this description is misleading
- `channel` (String) Channel that promotes the latest iteration of the image. Either this or `iteration_id` must be specified. | |
- `channel` (String) The channel associated to this image. |
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 was already corrected by @sylviamoss in a previous review, the latest reads as "The channel that points to the version of the image being retrieved.", are you OK with their phrasing?
"region": { | ||
Description: "Region this image is stored in, if any.", | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
// Optional inputs |
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 can remove this comment as optional inputs have the Optional field set to true.
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 comment is just here for the sake of consistency as we already have some at lines 25 (// Required inputs
), and 54 (// computed outputs
), I think if we remove this one, the rest should go too for the same consistency reasons, what do you think?
Description: "HCP ID of this image. Either this or `channel' must be specified.", | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ExactlyOneOf: []string{"channel"}, |
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.
Could we not use ConflictsWith 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.
Not in this case as ConflictsWith would not raise an error if both are missing
Description: "HCP ID of this image. Either this or `channel' must be specified.", | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ExactlyOneOf: []string{"channel"}, |
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.
Here I would expect that the provider would fail if both fields are unset. I think we can have both fields optional and possibly computed (need to think through this a bit more) and fail in the read if neither is set. If both are set then you would fail as well since the fields need to be mutually exclusive.
channelName := d.Get("channel") | ||
iterationID := d.Get("iteration_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.
I believe these need to be type asserted as they are stored as interface{} values.
channelName := d.Get("channel") | |
iterationID := d.Get("iteration_id") | |
channelName := d.Get("channel").(string) | |
iterationID := d.Get("iteration_id").(string) |
That said I would recommend creating a new helper function
The following is a mixture of psuedo and Go code.
func getIteration(ctx context.Context, d *schema.ResourceData) (iteration, error) {
// validate values for channel and iteration_id
// only one can be set.
/// if both are unset fail,
// if both are set fail.
if iterationID is not empty {
return clients.GetIterationFromId(
ctx,
client,
loc,
bucketName,
iterID)
}
channel, err = clients.GetPackerChannelBySlug(
ctx,
client,
loc,
bucketName,
chanSlug)
if err != nil {
return nil, err
}
channel.Iteration, nil
}
if channel != nil && iteration != nil { | ||
return diag.FromErr(fmt.Errorf( | ||
"iteration mismatch: channel %s's iteration (%s) is different from the explicitely specified iteration: %s", | ||
channel.Slug, | ||
channel.Iteration.ID, | ||
iteration.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.
I'm a little confused by this as channel and iteration appear to be mutually exclusive. Is this check needed?
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.
Well, it used to be before we had the ExactlyOneOf
in the Schema, now you're right it's not needed as the configuration attributes cannot be both set to something other than the empty string.
// Assuming we passed the above check, the rest of the channel is not | ||
// used after that, | ||
if channel != nil { | ||
iteration = channel.Iteration | ||
} |
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 using the helper function you can get rid of this as we will already have the iteration information we are looking for.
39e4f15
to
1e6ba48
Compare
testAccPackerImageBothChanAndIter, | ||
testAccPackerImageBothChanAndIterRef, |
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.
Nice!
A hcp_packer_image used to rely on the iteration_id, which it could get from a second resource in the plan. However, this information is redundant, as the image's ID can be inferred directly by the image block, if we specify its channel. Therefore, we let a user specify either an iteration_id for compatibility, or a channel so they won't need both information in a single plan.
1e6ba48
to
1809ea9
Compare
docs/data-sources/packer_image.md
Outdated
``` | ||
|
||
~> **Note:** This data source only returns the first found image's metadata filtered by the given schema values, from the returned list of images associated with the specified iteration. Therefore, if multiple images exist in the same region, it will only pick one of them. If that's the case, you may consider separating your builds into different buckets. | ||
|
||
~> **Note:** The `channel` attribute in this data source will incur a billable request to HCP Packer. This attribute is intended for convenience when using a single image. When sourcing multiple images from a single iteration, the separate data source object for the iteration alternative is preferred for saving costs. |
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 do you think? I changed to may
because there are limited free requests in the standard tier 🤔
~> **Note:** The `channel` attribute in this data source will incur a billable request to HCP Packer. This attribute is intended for convenience when using a single image. When sourcing multiple images from a single iteration, the separate data source object for the iteration alternative is preferred for saving costs. | |
~> **Note:** The `channel` attribute in this data source may incur a billable request to HCP Packer. This attribute is intended for convenience when using a single image. When sourcing multiple images from a single iteration, the `hcp_packer_iteration` data source | |
is the alternative for querying a channel just once. |
docs/data-sources/packer_image.md
Outdated
# If sourcing multiple images | ||
|
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 sourcing multiple images |
docs/data-sources/packer_image.md
Outdated
# Alternatively if sourcing a single image | ||
|
||
data "hcp_packer_image" "baz" { | ||
bucket_name = "hardened-ubuntu-16-04" | ||
cloud_provider = "aws" | ||
channel = "production" | ||
region = "us-east-1" | ||
} | ||
|
||
output "packer-registry-ubuntu-east-1-alt" { | ||
value = data.hcp_packer_image.baz.cloud_image_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.
# Alternatively if sourcing a single image | |
data "hcp_packer_image" "baz" { | |
bucket_name = "hardened-ubuntu-16-04" | |
cloud_provider = "aws" | |
channel = "production" | |
region = "us-east-1" | |
} | |
output "packer-registry-ubuntu-east-1-alt" { | |
value = data.hcp_packer_image.baz.cloud_image_id | |
} | |
### Alternatively, if sourcing a single image | |
data "hcp_packer_image" "baz" { | |
bucket_name = "hardened-ubuntu-16-04" | |
cloud_provider = "aws" | |
channel = "production" | |
region = "us-east-1" | |
} | |
output "packer-registry-ubuntu-east-1-alt" { | |
value = data.hcp_packer_image.baz.cloud_image_id | |
} |
I'm having a hard time adding the code block because the markdown is getting confused with this comment markdown. I'm suggesting moving the alternative into a separated sub-section and keeping both examples separated because all together looks big and hard to quickly see how it works.
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.
Separating both source examples is a valid alternative, I'll do 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.
I suggested some changes to the doc, otherwise LGTM 👍🏼
Great work!
With the introduction of `channel' in hcp_packer_image, we add some more examples in the documentation for the data_source.
1809ea9
to
83dac3e
Compare
A hcp_packer_image used to rely on the iteration_id, which it could get from a second resource in the plan. However, this information is redundant, as the image's ID can be inferred directly by the image block, if we specify its channel. Therefore, we let a user specify either an iteration_id for compatibility, or a channel so they won't need both information in a single plan. This does not make the hcp_iteration entity redundant however, as one can reuse its data for several images, hence why we keep it for now.
🛠️ Description
Add a new channel attribute to the
hcp_packer_image
data source, so a user can fetch the latest image directly, without having to specify a companionhcp_packer_iteration
data source to fetch the build ID from.🚢 Release Note
🏗️ Acceptance tests
Output from acceptance testing: