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

[HPR-859] Add HCP Packer bucket channel resource #435

Merged
merged 5 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/435.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
New resource `hcp_packer_channel` to create, or update an existing, channel with or without an assigned iteration.
```
118 changes: 118 additions & 0 deletions docs/resources/packer_channel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
page_title: "Resource hcp_packer_channel - terraform-provider-hcp"
subcategory: ""
description: |-
The Packer Channel resource allows you to manage image bucket channels within an active HCP Packer Registry.
nywilken marked this conversation as resolved.
Show resolved Hide resolved
---

# hcp_packer_channel (Resource)

The Packer Channel resource allows you to manage image bucket channels within an active HCP Packer Registry.
nywilken marked this conversation as resolved.
Show resolved Hide resolved

## Example Usage

To create a channel with no assigned iteration.
```terraform
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
}
```

To create, or update an existing, channel with an assigned iteration.
```terraform
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
id = "iteration-id"
}
}

# Update assigned iteration using an iteration fingerprint
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
fingerprint = "fingerprint-associated-to-iteration"
}
}

# Update assigned iteration using an iteration incremental version
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
// incremental_version is the version number assigned to a completed iteration.
incremental_version = 1
}
}
```

Using the latest channel to create a new channel with an assigned iteration.
```terraform
data "hcp_packer_image_iteration" "latest" {
bucket_name = "alpine"
channel = "latest"
}

resource "hcp_packer_channel" "staging" {
name = staging
bucket_name = alpine
iteration {
id = data.hcp_packer_image_iteration.latest.id
}
}
```


<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `bucket_name` (String) The slug of the HCP Packer Registry image bucket where the channel should be managed in.
- `name` (String) The name of the channel being managed.

### Optional

- `iteration` (Block List, Max: 1) The iteration assigned to the channel. (see [below for nested schema](#nestedblock--iteration))
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))

### Read-Only

- `author_id` (String) The author of the channel.
- `created_at` (String) Creation time of this build.
- `id` (String) The ID of this resource.
- `organization_id` (String) The ID of the organization this HCP Packer registry is located in.
- `project_id` (String) The ID of the project this HCP Packer registry is located in.
- `updated_at` (String) The author of the channel.

<a id="nestedblock--iteration"></a>
### Nested Schema for `iteration`

Optional:

- `fingerprint` (String) The fingerprint of the iteration assigned to the channel.
- `id` (String) The ID of the iteration assigned to the channel.
- `incremental_version` (Number) The incremental_version of the iteration assigned to the channel.


<a id="nestedblock--timeouts"></a>
### Nested Schema for `timeouts`

Optional:

- `create` (String)
- `default` (String)
- `delete` (String)
- `update` (String)

## Import

Import is supported using the following syntax:

```shell
# The import ID requires the bucket and channel name in the following format {bucket_name}:{name}
terraform import hcp_packer_channel.staging alpine:staging
```
2 changes: 2 additions & 0 deletions examples/resources/hcp_packer_channel/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The import ID requires the bucket and channel name in the following format {bucket_name}:{name}
terraform import hcp_packer_channel.staging alpine:staging
4 changes: 4 additions & 0 deletions examples/resources/hcp_packer_channel/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
}
27 changes: 27 additions & 0 deletions examples/resources/hcp_packer_channel/resource_assignment.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
id = "iteration-id"
}
}

# Update assigned iteration using an iteration fingerprint
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
fingerprint = "fingerprint-associated-to-iteration"
}
}

# Update assigned iteration using an iteration incremental version
resource "hcp_packer_channel" "staging" {
name = "staging"
bucket_name = "alpine"
iteration {
// incremental_version is the version number assigned to a completed iteration.
incremental_version = 1
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
data "hcp_packer_image_iteration" "latest" {
bucket_name = "alpine"
channel = "latest"
}

resource "hcp_packer_channel" "staging" {
name = staging
bucket_name = alpine
iteration {
id = data.hcp_packer_image_iteration.latest.id
}
}
97 changes: 96 additions & 1 deletion internal/clients/packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func GetPackerChannelBySlug(ctx context.Context, client *Client, loc *sharedmode
return getResp.Payload.Channel, nil
}

// GetIteration queries the HCP Packer registry for an existing bucket iteration.
// GetIterationFromID queries the HCP Packer registry for an existing bucket iteration.
func GetIterationFromID(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation,
bucketslug string, iterationID string) (*packermodels.HashicorpCloudPackerIteration, error) {
params := packer_service.NewPackerServiceGetIterationParamsWithContext(ctx)
Expand All @@ -48,6 +48,101 @@ func GetIterationFromID(ctx context.Context, client *Client, loc *sharedmodels.H
return it.Payload.Iteration, nil
}

// CreateBucketChannel creates a channel on the named bucket.
func CreateBucketChannel(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, bucketSlug string, channelSlug string,
iteration *packermodels.HashicorpCloudPackerIteration) (*packermodels.HashicorpCloudPackerChannel, error) {
params := packer_service.NewPackerServiceCreateChannelParamsWithContext(ctx)
params.LocationOrganizationID = loc.OrganizationID
params.LocationProjectID = loc.ProjectID
params.BucketSlug = bucketSlug
params.Body.Slug = channelSlug

if iteration != nil {
switch {
case iteration.ID != "":
params.Body.IterationID = iteration.ID
case iteration.Fingerprint != "":
params.Body.Fingerprint = iteration.Fingerprint
case iteration.IncrementalVersion > 0:
params.Body.IncrementalVersion = iteration.IncrementalVersion
}
}

channel, err := client.Packer.PackerServiceCreateChannel(params, nil)
if err != nil {
err := err.(*packer_service.PackerServiceCreateChannelDefault)
return nil, errors.New(err.Payload.Message)
}

return channel.GetPayload().Channel, nil
}

// UpdateBucketChannel updates the named channel.
func UpdateBucketChannel(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, bucketSlug string, channelSlug string,
iteration *packermodels.HashicorpCloudPackerIteration) (*packermodels.HashicorpCloudPackerChannel, error) {
params := packer_service.NewPackerServiceUpdateChannelParamsWithContext(ctx)
params.LocationOrganizationID = loc.OrganizationID
params.LocationProjectID = loc.ProjectID
params.BucketSlug = bucketSlug
params.Slug = channelSlug

if iteration != nil {
switch {
case iteration.ID != "":
params.Body.IterationID = iteration.ID
case iteration.Fingerprint != "":
params.Body.Fingerprint = iteration.Fingerprint
case iteration.IncrementalVersion > 0:
params.Body.IncrementalVersion = iteration.IncrementalVersion
}
}

channel, err := client.Packer.PackerServiceUpdateChannel(params, nil)
if err != nil {
err := err.(*packer_service.PackerServiceUpdateChannelDefault)
return nil, errors.New(err.Payload.Message)
}

return channel.GetPayload().Channel, nil
}

// DeleteBucketChannel deletes a channel from the named bucket.
func DeleteBucketChannel(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, bucketSlug, channelSlug string) (*packermodels.HashicorpCloudPackerChannel, error) {
params := packer_service.NewPackerServiceDeleteChannelParamsWithContext(ctx)
params.LocationOrganizationID = loc.OrganizationID
params.LocationProjectID = loc.ProjectID
params.BucketSlug = bucketSlug
params.Slug = channelSlug

req, err := client.Packer.PackerServiceDeleteChannel(params, nil)
if err != nil {
err := err.(*packer_service.PackerServiceDeleteChannelDefault)
return nil, errors.New(err.Payload.Message)
}

if !req.IsSuccess() {
nywilken marked this conversation as resolved.
Show resolved Hide resolved
return nil, errors.New("failed to delete channel")
}

return nil, nil
}

// ListBucketChannels queries the HCP Packer registry for channels associated to the specified bucket.
func ListBucketChannels(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, bucketSlug string) (*packermodels.HashicorpCloudPackerListChannelsResponse, error) {
params := packer_service.NewPackerServiceListChannelsParams()
params.LocationOrganizationID = loc.OrganizationID
params.LocationProjectID = loc.ProjectID
params.BucketSlug = bucketSlug

req, err := client.Packer.PackerServiceListChannels(params, nil)
if err != nil {
err := err.(*packer_service.PackerServiceListChannelsDefault)
return nil, errors.New(err.Payload.Message)
}

return req.Payload, nil
}

// handleGetChannelError returns a formatted error for the GetChannel error.
// The upstream API does a good job of providing detailed error messages so we just display the error message, with no status code.
func handleGetChannelError(err *packer_service.PackerServiceGetChannelDefault) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
var (
acctestAlpineBucket = fmt.Sprintf("alpine-acc-%s", time.Now().Format("200601021504"))
acctestUbuntuBucket = fmt.Sprintf("ubuntu-acc-%s", time.Now().Format("200601021504"))
acctestProductionChannel = "production"
acctestProductionChannel = fmt.Sprintf("packer-acc-channel-%s", time.Now().Format("200601021504"))
Copy link
Member Author

Choose a reason for hiding this comment

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

Validated all test pass as expected

~>  make testacc TESTARGS='-run=Packer'
==> Checking that code complies with gofmt requirements...
golangci-lint run --config ./golangci-config.yml
TF_ACC=1 go test ./internal/... -v -run=Packer -timeout 210m -parallel=10
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/terraform-provider-hcp/internal/clients	0.216s [no tests to run]
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/terraform-provider-hcp/internal/consul	0.302s [no tests to run]
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/terraform-provider-hcp/internal/input	0.283s [no tests to run]
=== RUN   TestAcc_dataSourcePacker
--- PASS: TestAcc_dataSourcePacker (12.48s)
=== RUN   TestAcc_dataSourcePacker_revokedIteration
--- PASS: TestAcc_dataSourcePacker_revokedIteration (16.62s)
=== RUN   TestAcc_dataSourcePackerImage
--- PASS: TestAcc_dataSourcePackerImage (11.65s)
=== RUN   TestAcc_dataSourcePackerImage_revokedIteration
--- PASS: TestAcc_dataSourcePackerImage_revokedIteration (14.44s)
=== RUN   TestAcc_dataSourcePackerImage_channelAndIterationIDReject
--- PASS: TestAcc_dataSourcePackerImage_channelAndIterationIDReject (5.60s)
=== RUN   TestAcc_dataSourcePackerImage_channelAccept
--- PASS: TestAcc_dataSourcePackerImage_channelAccept (10.24s)
=== RUN   TestAcc_dataSourcePackerIteration
--- PASS: TestAcc_dataSourcePackerIteration (9.56s)
=== RUN   TestAcc_dataSourcePackerIteration_revokedIteration
--- PASS: TestAcc_dataSourcePackerIteration_revokedIteration (16.30s)
=== RUN   TestAccPackerChannel
--- PASS: TestAccPackerChannel (12.18s)
=== RUN   TestAccPackerChannel_AssignedIteration
--- PASS: TestAccPackerChannel_AssignedIteration (13.25s)
=== RUN   TestAccPackerChannel_UpdateAssignedIteration
--- PASS: TestAccPackerChannel_UpdateAssignedIteration (21.26s)
=== RUN   TestAccPackerChannel_UpdateAssignedIterationWithFingerprint
--- PASS: TestAccPackerChannel_UpdateAssignedIterationWithFingerprint (10.44s)
PASS
ok  	github.com/hashicorp/terraform-provider-hcp/internal/provider	154.408s

Copy link
Contributor

Choose a reason for hiding this comment

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

Same!

)

var (
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func New() func() *schema.Provider {
"hcp_hvn": resourceHvn(),
"hcp_hvn_peering_connection": resourceHvnPeeringConnection(),
"hcp_hvn_route": resourceHvnRoute(),
"hcp_packer_channel": resourcePackerChannel(),
"hcp_vault_cluster": resourceVaultCluster(),
"hcp_vault_cluster_admin_token": resourceVaultClusterAdminToken(),
},
Expand Down
Loading