Skip to content

Commit

Permalink
feat: Add support for OCI Registry feed (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
denys-octopus authored Oct 21, 2024
1 parent d832a3b commit 4b7caef
Show file tree
Hide file tree
Showing 11 changed files with 365 additions and 7 deletions.
4 changes: 2 additions & 2 deletions docs/data-sources/feeds.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data "octopusdeploy_feeds" "example" {

### Optional

- `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`.
- `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, `OciRegistry` or `OctopusProject`.
- `ids` (List of String) A filter to search by a list of IDs.
- `name` (String) The name of this resource.
- `partial_name` (String) A filter to search by a partial name.
Expand All @@ -49,7 +49,7 @@ Read-Only:
- `delete_unreleased_packages_after_days` (Number)
- `download_attempts` (Number) The number of times a deployment should attempt to download a package from this feed before failing.
- `download_retry_backoff_seconds` (Number) The number of seconds to apply as a linear back off between download attempts.
- `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`.
- `feed_type` (String) A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, `OciRegistry` or `OctopusProject`.
- `feed_uri` (String)
- `id` (String) The unique ID for this resource.
- `is_enhanced_mode` (Boolean)
Expand Down
48 changes: 48 additions & 0 deletions docs/resources/oci_registry_feed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "octopusdeploy_oci_registry_feed Resource - terraform-provider-octopusdeploy"
subcategory: ""
description: |-
This resource manages a OCI Registry feed in Octopus Deploy.
---

# octopusdeploy_oci_registry_feed (Resource)

This resource manages a OCI Registry feed in Octopus Deploy.

## Example Usage

```terraform
resource "octopusdeploy_oci_registry_feed" "example" {
feed_uri = "oci://test-registry.docker.io"
password = "test-password"
name = "Test oci Registry Feed (OK to Delete)"
username = "test-username"
}
```

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

### Required

- `feed_uri` (String)
- `name` (String) The name of this resource.

### Optional

- `password` (String, Sensitive) The password associated with this resource.
- `space_id` (String) The space ID associated with this OCI registry.
- `username` (String, Sensitive) The username associated with this resource.

### Read-Only

- `id` (String) The unique ID for this resource.

## Import

Import is supported using the following syntax:

```shell
terraform import [options] octopusdeploy_oci_registry_feed.<name> <feed-id>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import [options] octopusdeploy_oci_registry_feed.<name> <feed-id>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "octopusdeploy_oci_registry_feed" "example" {
feed_uri = "oci://test-registry.docker.io"
password = "test-password"
name = "Test oci Registry Feed (OK to Delete)"
username = "test-username"
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/OctopusDeploy/terraform-provider-octopusdeploy
go 1.21

require (
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.53.1
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ github.com/Microsoft/hcsshim v0.12.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/
github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ=
github.com/OctopusDeploy/go-octodiff v1.0.0 h1:U+ORg6azniwwYo+O44giOw6TiD5USk8S4VDhOQ0Ven0=
github.com/OctopusDeploy/go-octodiff v1.0.0/go.mod h1:Mze0+EkOWTgTmi8++fyUc6r0aLZT7qD9gX+31t8MmIU=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.1 h1:GeWNIPn59JZggkjZD/VKpt3oJNuYezdJPbIqyl+MVRw=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.52.1/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.53.1 h1:9c5qKji5R/sFmjqVQ1Nxt+vKITsj42CCCs0bfqJvETc=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.53.1/go.mod h1:ggvOXzMnq+w0pLg6C9zdjz6YBaHfO3B3tqmmB7JQdaw=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4 h1:QfbVf0bOIRMp/WHAWsuVDB7KHoWnRsGbvDuOf2ua7k4=
github.com/OctopusSolutionsEngineering/OctopusTerraformTestFramework v0.0.0-20240729041805-46db6fb717b4/go.mod h1:Oq9KbiRNDBB5jFmrwnrgLX0urIqR/1ptY18TzkqXm7M=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
Expand Down
1 change: 1 addition & 0 deletions octopusdeploy_framework/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func()
NewSpaceResource,
NewProjectGroupResource,
NewMavenFeedResource,
NewOCIRegistryFeedResource,
NewLifecycleResource,
NewEnvironmentResource,
NewStepTemplateResource,
Expand Down
168 changes: 168 additions & 0 deletions octopusdeploy_framework/resource_oci_registry_feed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package octopusdeploy_framework

import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework/path"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

type ociRegistryFeedTypeResource struct {
*Config
}

func NewOCIRegistryFeedResource() resource.Resource {
return &ociRegistryFeedTypeResource{}
}

var _ resource.ResourceWithImportState = &ociRegistryFeedTypeResource{}

func (r *ociRegistryFeedTypeResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = util.GetTypeName("oci_registry_feed")
}

func (r *ociRegistryFeedTypeResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schemas.OCIRegistryFeedSchema{}.GetResourceSchema()
}

func (r *ociRegistryFeedTypeResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.Config = ResourceConfiguration(req, resp)
}

func (r *ociRegistryFeedTypeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data *schemas.OCIRegistryFeedTypeResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

feed, err := createOCIRegistryResourceFromData(data)
if err != nil {
return
}

tflog.Info(ctx, fmt.Sprintf("creating OCI Registry feed: %s", feed.GetName()))

client := r.Config.Client
createdFeed, err := feeds.Add(client, feed)
if err != nil {
resp.Diagnostics.AddError("unable to create OCI Registry feed", err.Error())
return
}

updateDataFromOCIRegistryFeed(data, data.SpaceID.ValueString(), createdFeed.(*feeds.OCIRegistryFeed))

tflog.Info(ctx, fmt.Sprintf("OCI Registry feed created (%s)", data.ID))
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ociRegistryFeedTypeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *schemas.OCIRegistryFeedTypeResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

tflog.Info(ctx, fmt.Sprintf("reading OCI Registry feed (%s)", data.ID))

client := r.Config.Client
feed, err := feeds.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString())
if err != nil {
if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "OCI Registry feed"); err != nil {
resp.Diagnostics.AddError("unable to load OCI Registry feed", err.Error())
}
return
}

loadedFeed := feed.(*feeds.OCIRegistryFeed)
updateDataFromOCIRegistryFeed(data, data.SpaceID.ValueString(), loadedFeed)

tflog.Info(ctx, fmt.Sprintf("OCI Registry feed read (%s)", loadedFeed.GetID()))
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ociRegistryFeedTypeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data, state *schemas.OCIRegistryFeedTypeResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

tflog.Debug(ctx, fmt.Sprintf("updating OCI Registry feed '%s'", data.ID.ValueString()))

feed, err := createOCIRegistryResourceFromData(data)
feed.ID = state.ID.ValueString()
if err != nil {
resp.Diagnostics.AddError("unable to load OCI Registry feed", err.Error())
return
}

tflog.Info(ctx, fmt.Sprintf("updating OCI Registry feed (%s)", data.ID))

client := r.Config.Client
updatedFeed, err := feeds.Update(client, feed)
if err != nil {
resp.Diagnostics.AddError("unable to update OCI Registry feed", err.Error())
return
}

updateDataFromOCIRegistryFeed(data, state.SpaceID.ValueString(), updatedFeed.(*feeds.OCIRegistryFeed))

tflog.Info(ctx, fmt.Sprintf("OCI Registry feed updated (%s)", data.ID))

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ociRegistryFeedTypeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data schemas.OCIRegistryFeedTypeResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

if err := feeds.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil {
resp.Diagnostics.AddError("unable to delete OCI Registry feed", err.Error())
return
}
}

func createOCIRegistryResourceFromData(data *schemas.OCIRegistryFeedTypeResourceModel) (*feeds.OCIRegistryFeed, error) {
feed, err := feeds.NewOCIRegistryFeed(data.Name.ValueString())
if err != nil {
return nil, err
}

feed.ID = data.ID.ValueString()
feed.FeedURI = data.FeedUri.ValueString()

feed.Username = data.Username.ValueString()
feed.Password = core.NewSensitiveValue(data.Password.ValueString())
feed.SpaceID = data.SpaceID.ValueString()

return feed, nil
}

func updateDataFromOCIRegistryFeed(data *schemas.OCIRegistryFeedTypeResourceModel, spaceId string, feed *feeds.OCIRegistryFeed) {
data.FeedUri = types.StringValue(feed.FeedURI)
data.Name = types.StringValue(feed.Name)
data.SpaceID = types.StringValue(spaceId)
if feed.Username != "" {
data.Username = types.StringValue(feed.Username)
}

data.ID = types.StringValue(feed.ID)
}

func (*ociRegistryFeedTypeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
91 changes: 91 additions & 0 deletions octopusdeploy_framework/resource_oci_registry_feed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package octopusdeploy_framework

import (
"fmt"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/feeds"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"testing"
)

type ociRegistryFeedTestData struct {
name string
uri string
username string
password string
}

func TestAccOctopusDeployOCIRegistryFeed(t *testing.T) {
localName := acctest.RandStringFromCharSet(20, acctest.CharSetAlpha)
prefix := "octopusdeploy_oci_registry_feed." + localName
createData := ociRegistryFeedTestData{
name: acctest.RandStringFromCharSet(20, acctest.CharSetAlpha),
uri: "oci://integration-test-registry.docker.io",
username: acctest.RandStringFromCharSet(20, acctest.CharSetAlpha),
password: acctest.RandStringFromCharSet(20, acctest.CharSetAlphaNum),
}
updateData := ociRegistryFeedTestData{
name: createData.name + "-updated",
uri: "oci://integration-test-registry-updated.docker.io",
username: createData.username + "-changed",
password: createData.password + "-generated",
}

resource.Test(t, resource.TestCase{
CheckDestroy: func(s *terraform.State) error { return testOCIRegistryFeedCheckDestroy(s) },
PreCheck: func() { TestAccPreCheck(t) },
ProtoV6ProviderFactories: ProtoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testOCIRegistryFeedBasic(createData, localName),
Check: testAssertOCIRegistryAttributes(createData, prefix),
},
{
Config: testOCIRegistryFeedBasic(updateData, localName),
Check: testAssertOCIRegistryAttributes(updateData, prefix),
},
},
})
}

func testAssertOCIRegistryAttributes(expected ociRegistryFeedTestData, prefix string) resource.TestCheckFunc {
return resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(prefix, "name", expected.name),
resource.TestCheckResourceAttr(prefix, "feed_uri", expected.uri),
resource.TestCheckResourceAttr(prefix, "username", expected.username),
resource.TestCheckResourceAttr(prefix, "password", expected.password),
)
}

func testOCIRegistryFeedBasic(data ociRegistryFeedTestData, localName string) string {
return fmt.Sprintf(`
resource "octopusdeploy_oci_registry_feed" "%s" {
name = "%s"
feed_uri = "%s"
username = "%s"
password = "%s"
}
`,
localName,
data.name,
data.uri,
data.username,
data.password,
)
}

func testOCIRegistryFeedCheckDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "octopusdeploy_oci_registry_feed" {
continue
}

feed, err := feeds.GetByID(octoClient, octoClient.GetSpaceID(), rs.Primary.ID)
if err == nil && feed != nil {
return fmt.Errorf("OCI Registry feed (%s) still exists", rs.Primary.ID)
}
}

return nil
}
6 changes: 4 additions & 2 deletions octopusdeploy_framework/schemas/feeds.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (f FeedsSchema) GetDatasourceSchema() datasourceSchema.Schema {
Description: "Provides information about existing feeds.",
Attributes: map[string]datasourceSchema.Attribute{
"feed_type": datasourceSchema.StringAttribute{
Description: "A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`.",
Description: "A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, `OciRegistry` or `OctopusProject`.",
Optional: true,
Validators: []validator.String{
stringvalidator.OneOf(
Expand All @@ -32,6 +32,7 @@ func (f FeedsSchema) GetDatasourceSchema() datasourceSchema.Schema {
"Helm",
"Maven",
"NuGet",
"OciRegistry",
"OctopusProject"),
},
},
Expand All @@ -50,7 +51,7 @@ func (f FeedsSchema) GetDatasourceSchema() datasourceSchema.Schema {
NestedObject: datasourceSchema.NestedAttributeObject{
Attributes: map[string]datasourceSchema.Attribute{
"feed_type": datasourceSchema.StringAttribute{
Description: "A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, or `OctopusProject`.",
Description: "A filter to search by feed type. Valid feed types are `AwsElasticContainerRegistry`, `BuiltIn`, `Docker`, `GitHub`, `Helm`, `Maven`, `NuGet`, `OciRegistry` or `OctopusProject`.",
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(
Expand All @@ -61,6 +62,7 @@ func (f FeedsSchema) GetDatasourceSchema() datasourceSchema.Schema {
"Helm",
"Maven",
"NuGet",
"OciRegistry",
"OctopusProject"),
},
},
Expand Down
Loading

0 comments on commit 4b7caef

Please sign in to comment.