Skip to content

Commit

Permalink
feat: project versioning strategy resource (#834)
Browse files Browse the repository at this point in the history
  • Loading branch information
domenicsim1 authored Dec 4, 2024
1 parent 9361f05 commit d009e07
Show file tree
Hide file tree
Showing 7 changed files with 424 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/resources/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ resource "octopusdeploy_project" "example" {
- `space_id` (String) The space ID associated with this project.
- `template` (Block List) (see [below for nested schema](#nestedblock--template))
- `tenanted_deployment_participation` (String) The tenanted deployment mode of the resource. Valid account types are `Untenanted`, `TenantedOrUntenanted`, or `Tenanted`.
- `versioning_strategy` (Block List) (see [below for nested schema](#nestedblock--versioning_strategy))
- `versioning_strategy` (Block List, Deprecated) (see [below for nested schema](#nestedblock--versioning_strategy))

### Read-Only

Expand Down
100 changes: 100 additions & 0 deletions docs/resources/project_versioning_strategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "octopusdeploy_project_versioning_strategy Resource - terraform-provider-octopusdeploy"
subcategory: ""
description: |-
---

# octopusdeploy_project_versioning_strategy (Resource)



## Example Usage

```terraform
resource "octopusdeploy_project_group" "tp" {
name = "DevOps Projects"
description = "My DevOps projects group"
}
resource "octopusdeploy_project" "tp" {
name = "My DevOps Project"
description = "test project"
lifecycle_id = "Lifecycles-1"
project_group_id = octopusdeploy_project_group.tp.id
depends_on = [octopusdeploy_project_group.tp]
}
resource "octopusdeploy_deployment_process" "process" {
project_id = octopusdeploy_project.tp.id
step {
name = "Hello World"
target_roles = [ "hello-world" ]
start_trigger = "StartAfterPrevious"
package_requirement = "LetOctopusDecide"
condition = "Success"
run_script_action {
name = "Hello World"
is_disabled = false
is_required = true
script_body = "Write-Host 'hello world'"
script_syntax = "PowerShell"
can_be_used_for_project_versioning = true
sort_order = 1
package {
name = "Package"
feed_id = "feeds-builtin"
package_id = "myExpressApp"
acquisition_location = "Server"
extract_during_deployment = true
}
}
}
depends_on = [octopusdeploy_project.tp]
}
resource "octopusdeploy_project_versioning_strategy" "tp" {
project_id = octopusdeploy_project.tp.id
space_id = octopusdeploy_project.tp.space_id
donor_package_step_id = octopusdeploy_deployment_process.process.step[0].run_script_action[0].id
donor_package = {
deployment_action = "Hello World"
package_reference = "Package"
}
depends_on = [
octopusdeploy_project_group.tp,
octopusdeploy_deployment_process.process
]
}
```

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

### Required

- `donor_package` (Attributes) Donor Packages. (see [below for nested schema](#nestedatt--donor_package))
- `project_id` (String) The associated project ID.
- `space_id` (String) Space ID of the associated project.

### Optional

- `donor_package_step_id` (String) The associated donor package step ID.
- `template` (String)

<a id="nestedatt--donor_package"></a>
### Nested Schema for `donor_package`

Optional:

- `deployment_action` (String) Deployment action.
- `package_reference` (String) Package reference.


Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
resource "octopusdeploy_project_group" "tp" {
name = "DevOps Projects"
description = "My DevOps projects group"
}

resource "octopusdeploy_project" "tp" {
name = "My DevOps Project"
description = "test project"
lifecycle_id = "Lifecycles-1"
project_group_id = octopusdeploy_project_group.tp.id

depends_on = [octopusdeploy_project_group.tp]
}

resource "octopusdeploy_deployment_process" "process" {
project_id = octopusdeploy_project.tp.id

step {
name = "Hello World"
target_roles = [ "hello-world" ]
start_trigger = "StartAfterPrevious"
package_requirement = "LetOctopusDecide"
condition = "Success"

run_script_action {
name = "Hello World"
is_disabled = false
is_required = true
script_body = "Write-Host 'hello world'"
script_syntax = "PowerShell"
can_be_used_for_project_versioning = true
sort_order = 1


package {
name = "Package"
feed_id = "feeds-builtin"
package_id = "myExpressApp"
acquisition_location = "Server"
extract_during_deployment = true
}
}
}

depends_on = [octopusdeploy_project.tp]
}

resource "octopusdeploy_project_versioning_strategy" "tp" {
project_id = octopusdeploy_project.tp.id
space_id = octopusdeploy_project.tp.space_id
donor_package_step_id = octopusdeploy_deployment_process.process.step[0].run_script_action[0].id
donor_package = {
deployment_action = "Hello World"
package_reference = "Package"
}
depends_on = [
octopusdeploy_project_group.tp,
octopusdeploy_deployment_process.process
]
}
1 change: 1 addition & 0 deletions octopusdeploy_framework/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func()
NewLibraryVariableSetFeedResource,
NewVariableResource,
NewProjectResource,
NewProjectVersioningStrategyResource,
NewMachineProxyResource,
NewTagResource,
NewDockerContainerRegistryFeedResource,
Expand Down
190 changes: 190 additions & 0 deletions octopusdeploy_framework/resource_project_versioning_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package octopusdeploy_framework

import (
"context"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects"
"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"
"log"
"net/http"
)

var _ resource.Resource = &projectVersioningStrategyResource{}

type projectVersioningStrategyResource struct {
*Config
}

func NewProjectVersioningStrategyResource() resource.Resource {
return &projectVersioningStrategyResource{}
}

func (r *projectVersioningStrategyResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = util.GetTypeName(schemas.ProjectVersioningStrategyResourceName)
}

func (r *projectVersioningStrategyResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schemas.ProjectVersioningStrategySchema{}.GetResourceSchema()
}

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

func (r *projectVersioningStrategyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan schemas.ProjectVersioningStrategyModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

project, err := projects.GetByID(r.Client, plan.SpaceID.ValueString(), plan.ProjectID.ValueString())
if err != nil {
if apiError, ok := err.(*core.APIError); ok {
if apiError.StatusCode == http.StatusNotFound {
log.Printf("[INFO] associated project (%s) not found; deleting version strategy from state", plan.ProjectID.ValueString())
resp.State.RemoveResource(ctx)
}
} else {
resp.Diagnostics.AddError("Failed to read associated project", err.Error())
}
return
}
versioningStrategy := mapStateToProjectVersioningStrategy(&plan)
project.VersioningStrategy = versioningStrategy

_, err = projects.Update(r.Client, project)
if err != nil {
resp.Diagnostics.AddError("Error updating associated project", err.Error())
return
}

updatedProject, err := projects.GetByID(r.Client, plan.SpaceID.ValueString(), plan.ProjectID.ValueString())
if err != nil {
if apiError, ok := err.(*core.APIError); ok {
if apiError.StatusCode == http.StatusNotFound {
log.Printf("[INFO] associated project (%s) not found; deleting version strategy from state", plan.ProjectID.ValueString())
resp.State.RemoveResource(ctx)
}
} else {
resp.Diagnostics.AddError("Failed to read associated project", err.Error())
}
return
}

mapProjectVersioningStrategyToState(updatedProject.VersioningStrategy, &plan)
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}

func (r *projectVersioningStrategyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state schemas.ProjectVersioningStrategyModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

project, err := projects.GetByID(r.Client, state.SpaceID.ValueString(), state.ProjectID.ValueString())
if err != nil {
if apiError, ok := err.(*core.APIError); ok {
if apiError.StatusCode == http.StatusNotFound {
log.Printf("[INFO] associated project (%s) not found; deleting version strategy from state", state.ProjectID.ValueString())
resp.State.RemoveResource(ctx)
}
} else {
resp.Diagnostics.AddError("Failed to read associated project", err.Error())
}
return
}
mapProjectVersioningStrategyToState(project.VersioningStrategy, &state)

resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
}

func (r *projectVersioningStrategyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan schemas.ProjectVersioningStrategyModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

existingProject, err := projects.GetByID(r.Client, plan.SpaceID.ValueString(), plan.ProjectID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Error retrieving associated project", err.Error())
return
}

versioningStrategy := mapStateToProjectVersioningStrategy(&plan)
existingProject.VersioningStrategy = versioningStrategy

_, err = projects.Update(r.Client, existingProject)
if err != nil {
resp.Diagnostics.AddError("Error updating associated project", err.Error())
return
}

updatedProject, err := projects.GetByID(r.Client, plan.SpaceID.ValueString(), plan.ProjectID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Error retrieving associated project", err.Error())
return
}

mapProjectVersioningStrategyToState(updatedProject.VersioningStrategy, &plan)
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}

func (r *projectVersioningStrategyResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state schemas.ProjectVersioningStrategyModel
diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

project, err := projects.GetByID(r.Client, state.SpaceID.ValueString(), state.ProjectID.ValueString())
if err != nil {
resp.Diagnostics.AddError("Error retrieving project", err.Error())
return
}

project.VersioningStrategy = &projects.VersioningStrategy{}
_, err = projects.Update(r.Client, project)
if err != nil {
resp.Diagnostics.AddError("Error updating project to remove versioning strategy", err.Error())
return
}

resp.State.RemoveResource(ctx)
}

func mapStateToProjectVersioningStrategy(state *schemas.ProjectVersioningStrategyModel) *projects.VersioningStrategy {
var donorPackageStepID *string
donorPackageStepIDString := state.DonorPackageStepID.ValueString()
if donorPackageStepIDString != "" {
donorPackageStepID = &donorPackageStepIDString
}

return &projects.VersioningStrategy{
Template: state.Template.ValueString(),
DonorPackageStepID: donorPackageStepID,
DonorPackage: &packages.DeploymentActionPackage{
DeploymentAction: state.DonorPackage.DeploymentAction.ValueString(),
PackageReference: state.DonorPackage.PackageReference.ValueString(),
},
}
}

func mapProjectVersioningStrategyToState(versioningStrategy *projects.VersioningStrategy, state *schemas.ProjectVersioningStrategyModel) {
if versioningStrategy.DonorPackageStepID != nil {
state.DonorPackageStepID = types.StringValue(*versioningStrategy.DonorPackageStepID)
}
state.Template = types.StringValue(versioningStrategy.Template)
state.DonorPackage.PackageReference = types.StringValue(versioningStrategy.DonorPackage.PackageReference)
state.DonorPackage.DeploymentAction = types.StringValue(versioningStrategy.DonorPackage.DeploymentAction)
}
2 changes: 1 addition & 1 deletion octopusdeploy_framework/schemas/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ func (p ProjectSchema) GetResourceSchema() resourceSchema.Schema {
},
},
"versioning_strategy": resourceSchema.ListNestedBlock{
DeprecationMessage: "versioning_strategy is deprecated in favor of resource project_versioning strategy",
NestedObject: resourceSchema.NestedBlockObject{

Attributes: map[string]resourceSchema.Attribute{
"donor_package_step_id": util.ResourceString().Optional().Build(),
"template": util.ResourceString().Optional().Computed().Build(),
Expand Down
Loading

0 comments on commit d009e07

Please sign in to comment.