diff --git a/docs/data-sources/chaos_infrastructure.md b/docs/data-sources/chaos_infrastructure.md new file mode 100644 index 000000000..07a45a737 --- /dev/null +++ b/docs/data-sources/chaos_infrastructure.md @@ -0,0 +1,37 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "harness_chaos_infrastructure Data Source - terraform-provider-harness" +subcategory: "Next Gen" +description: |- + Data source for retrieving a chaos infrastructure. +--- + +# harness_chaos_infrastructure (Data Source) + +Data source for retrieving a chaos infrastructure. + +## Example Usage + +```terraform + data "harness_chaos_infrastructure" "example" { + identifier = "identifier" + org_id = "org_id" + project_id = "project_id" + environment_id = "env_id" + } +``` + + + +## Schema + +### Required + +- `environment_id` (String) Environment identifier of the chaos infrastructure. +- `identifier` (String) Identifier of the chaos infrastructure. +- `org_id` (String) Identifier of the organization in which the chaos infrastructure is configured. +- `project_id` (String) Identifier of the project in which the chaos infrastructure is configured. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/chaos_infrastructure.md b/docs/resources/chaos_infrastructure.md new file mode 100644 index 000000000..88ad429ab --- /dev/null +++ b/docs/resources/chaos_infrastructure.md @@ -0,0 +1,61 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "harness_chaos_infrastructure Resource - terraform-provider-harness" +subcategory: "Next Gen" +description: |- + Resource for creating a Chaos Infrastructure. +--- + +# harness_chaos_infrastructure (Resource) + +Resource for creating a Chaos Infrastructure. + +### References: + +- For details on how to onboard with Terraform, please see [Harness Terraform Provider Overview](https://developer.harness.io/docs/platform/automation/terraform/harness-terraform-provider-overview/) + +## Example to create Chaos Infrastructure + +```terraform +resource "harness_chaos_infrastructure" "example" { + identifier = "identifier" + name = "name" + org_id = "org_id" + project_id = "project_id" + environment_id = "env_id" + namespace = "namespace" + service_account = "service_acc_name" +} +``` + + + +## Schema + +### Required + +- `org_id` (String) Unique identifier of the organization. +- `project_id` (String) Unique identifier of the project. +- `environment_id` (String) Environment ID of the chaos infrastructure. +- `identifier` (String) Unique identifier of the resource. +- `name` (String) Name of the resource. +- `namespace` (String) Namespace of the chaos infrastructure. +- `service_account` (String) Service Account of the chaos infrastructure. + +### Optional + +- `description` (String) Description of the resource. +- `tags` (Set of String) Tags to associate with the resource. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +# Import using the Chaos Infra ID. +terraform import harness_chaos_infrastructure.example +``` diff --git a/examples/data-sources/harness_chaos_infrastructure/data-source.tf b/examples/data-sources/harness_chaos_infrastructure/data-source.tf new file mode 100644 index 000000000..e4ee32f47 --- /dev/null +++ b/examples/data-sources/harness_chaos_infrastructure/data-source.tf @@ -0,0 +1,6 @@ + data "harness_chaos_infrastructure" "example" { + identifier = "identifier" + org_id = "org_id" + project_id = "project_id" + environment_id= "env_id" + } \ No newline at end of file diff --git a/examples/resources/harness_chaos_infrastructure/import.sh b/examples/resources/harness_chaos_infrastructure/import.sh new file mode 100644 index 000000000..7db91bcc7 --- /dev/null +++ b/examples/resources/harness_chaos_infrastructure/import.sh @@ -0,0 +1,2 @@ +# Import using the Harness chaos_infra_id. +terraform import harness_chaos_infrastructure.example \ No newline at end of file diff --git a/examples/resources/harness_chaos_infrastructure/resource.tf b/examples/resources/harness_chaos_infrastructure/resource.tf new file mode 100644 index 000000000..f8029d6d3 --- /dev/null +++ b/examples/resources/harness_chaos_infrastructure/resource.tf @@ -0,0 +1,11 @@ + +# Sample resource for chaos infrastructure +resource "harness_chaos_infrastructure" "example" { + identifier = "identifier" + name = "name" + org_id = "org_id" + project_id = "project_id" + environment_id = "env_id" + namespace = "namespace" + service_account = "service_acc_name" +} \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 18115b80a..ad1498d76 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -5,7 +5,9 @@ import ( "fmt" "log" + "github.com/harness/harness-go-sdk/harness/chaos" cdng_service "github.com/harness/terraform-provider-harness/internal/service/cd_nextgen/service" + "github.com/harness/terraform-provider-harness/internal/service/chaos/infrastructure" "github.com/harness/terraform-provider-harness/internal/service/platform/module_registry" "github.com/harness/terraform-provider-harness/internal/service/platform/service_account" @@ -297,6 +299,7 @@ func Provider(version string) func() *schema.Provider { "harness_governance_rule_set": governance_rule_set.DatasourceRuleSet(), "harness_cluster_orchestrator": cluster_orchestrator.DataSourceClusterOrchestrator(), "harness_platform_infra_module": module_registry.DataSourceInfraModule(), + "harness_chaos_infrastructure": infrastructure.DataSourceChaosInfrastructureService(), }, ResourcesMap: map[string]*schema.Resource{ "harness_platform_template": pipeline_template.ResourceTemplate(), @@ -446,6 +449,7 @@ func Provider(version string) func() *schema.Provider { "harness_governance_rule_set": governance_rule_set.ResourceRuleSet(), "harness_cluster_orchestrator": cluster_orchestrator.ResourceClusterOrchestrator(), "harness_platform_infra_module": module_registry.ResourceInfraModule(), + "harness_chaos_infrastructure": infrastructure.ResourceChaosInfrastructure(), }, } @@ -540,6 +544,17 @@ func getCodeClient(d *schema.ResourceData, version string) *code.APIClient { return client } +func getChaosClient(d *schema.ResourceData, version string) *chaos.APIClient { + client := chaos.NewAPIClient(&chaos.Configuration{ + AccountId: d.Get("account_id").(string), + BasePath: d.Get("endpoint").(string) + "/chaos/manager/api", // check if this can be taken from sdk + ApiKey: d.Get("platform_api_key").(string), + UserAgent: fmt.Sprintf("terraform-provider-harness-platform-%s", version), + DefaultHeader: map[string]string{"X-Api-Key": d.Get("platform_api_key").(string)}, + }) + return client +} + // Setup the client for interacting with the Harness API func configure(version string, p *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) { return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { @@ -551,6 +566,7 @@ func configure(version string, p *schema.Provider) func(context.Context, *schema Client: getClient(d, version), CodeClient: getCodeClient(d, version), DBOpsClient: getDBOpsClient(d, version), + ChaosClient: getChaosClient(d, version), }, nil } } diff --git a/internal/service/chaos/infrastructure/data_source_infrastructure.go b/internal/service/chaos/infrastructure/data_source_infrastructure.go new file mode 100644 index 000000000..07c289096 --- /dev/null +++ b/internal/service/chaos/infrastructure/data_source_infrastructure.go @@ -0,0 +1,74 @@ +package infrastructure + +import ( + "context" + + "github.com/harness/terraform-provider-harness/helpers" + "github.com/harness/terraform-provider-harness/internal" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceChaosInfrastructureService() *schema.Resource { + resource := &schema.Resource{ + Description: "Data source for retrieving a chaos infrastructure.", + + ReadContext: dataSourceChaosInfrastructureRead, + + Schema: map[string]*schema.Schema{ + "org_id": { + Description: "Identifier of the organization in which the chaos infrastructure is configured.", + Type: schema.TypeString, + Required: true, + }, + "project_id": { + Description: "Identifier of the project in which the chaos infrastructure is configured.", + Type: schema.TypeString, + Required: true, + }, + "identifier": { + Description: "Identifier of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "environment_id": { + Description: "Environment identifier of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + }, + } + + return resource +} + +func dataSourceChaosInfrastructureRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetChaosClientWithContext(ctx) + var accountIdentifier, orgIdentifier, projectIdentifier, identifier, envIdentifier string + accountIdentifier = c.AccountId + if attr, ok := d.GetOk("org_id"); ok { + orgIdentifier = attr.(string) + } + if attr, ok := d.GetOk("project_id"); ok { + projectIdentifier = attr.(string) + } + if attr, ok := d.GetOk("identifier"); ok { + identifier = attr.(string) + } + if attr, ok := d.GetOk("environment_id"); ok { + envIdentifier = attr.(string) + } + resp, httpResp, err := c.ChaosSdkApi.GetInfraV2(ctx, identifier, accountIdentifier, orgIdentifier, projectIdentifier, envIdentifier) + + if err != nil { + if err.Error() == "404 Not Found" { + d.SetId("") + d.MarkNewResource() + return nil + } + return helpers.HandleReadApiError(err, d, httpResp) + } + readChaosInfrastructure(d, resp) + + return nil +} diff --git a/internal/service/chaos/infrastructure/resource_infrastructure.go b/internal/service/chaos/infrastructure/resource_infrastructure.go new file mode 100644 index 000000000..0e8d17a16 --- /dev/null +++ b/internal/service/chaos/infrastructure/resource_infrastructure.go @@ -0,0 +1,272 @@ +package infrastructure + +import ( + "context" + "log" + + "github.com/harness/harness-go-sdk/harness/chaos" + hh "github.com/harness/harness-go-sdk/harness/helpers" + "github.com/harness/terraform-provider-harness/helpers" + "github.com/harness/terraform-provider-harness/internal" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceChaosInfrastructure() *schema.Resource { + resource := &schema.Resource{ + Description: "Resource for creating a Chaos Infrastructure.", + + ReadContext: resourceChaosInfrastructureRead, + UpdateContext: resourceChaosInfrastructureUpdate, + DeleteContext: resourceChaosInfrastructureDelete, + CreateContext: resourceChaosInfrastructureCreate, + Importer: helpers.MultiLevelResourceImporter, + + Schema: map[string]*schema.Schema{ + "org_id": { + Description: "Identifier of the organization in which the chaos infrastructure is configured.", + Type: schema.TypeString, + Required: true, + }, + "project_id": { + Description: "Identifier of the project in which the chaos infrastructure is configured.", + Type: schema.TypeString, + Required: true, + }, + "identifier": { + Description: "Identifier of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "name": { + Description: "Name of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "description": { + Description: "Description of the chaos infrastructure.", + Type: schema.TypeString, + Optional: true, + }, + "environment_id": { + Description: "Environment ID of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "namespace": { + Description: "Namespace of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "service_account": { + Description: "Service Account of the chaos infrastructure.", + Type: schema.TypeString, + Required: true, + }, + "tags": { + Description: "Tags of the chaos infrastructure.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } + + helpers.SetMultiLevelResourceSchema(resource.Schema) + + return resource +} + +func resourceChaosInfrastructureRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetChaosClientWithContext(ctx) + var accountIdentifier, orgIdentifier, projectIdentifier, identifier, envIdentifier string + accountIdentifier = c.AccountId + if attr, ok := d.GetOk("org_id"); ok { + orgIdentifier = attr.(string) + } + if attr, ok := d.GetOk("project_id"); ok { + projectIdentifier = attr.(string) + } + if attr, ok := d.GetOk("identifier"); ok { + identifier = attr.(string) + } + if attr, ok := d.GetOk("environment_id"); ok { + envIdentifier = attr.(string) + } + resp, httpResp, err := c.ChaosSdkApi.GetInfraV2(ctx, identifier, accountIdentifier, orgIdentifier, projectIdentifier, envIdentifier) + + if err != nil { + if err.Error() == "404 Not Found" { + d.SetId("") + d.MarkNewResource() + return nil + } + return helpers.HandleReadApiError(err, d, httpResp) + } + readChaosInfrastructure(d, resp) + + return nil +} + +func resourceChaosInfrastructureUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetChaosClientWithContext(ctx) + var accountIdentifier, orgIdentifier, projectIdentifier string + accountIdentifier = c.AccountId + if attr, ok := d.GetOk("org_id"); ok { + orgIdentifier = attr.(string) + } + if attr, ok := d.GetOk("project_id"); ok { + projectIdentifier = attr.(string) + } + + updateInfraRequest := buildUpdateInfraRequest(d) + _, httpRespUpdate, errUpdate := c.ChaosSdkApi.UpdateInfraV2(ctx, *updateInfraRequest, accountIdentifier, orgIdentifier, projectIdentifier, &chaos.ChaosSdkApiUpdateInfraV2Opts{}) + + if errUpdate != nil { + return helpers.HandleApiError(errUpdate, d, httpRespUpdate) + } + readInfraUpdate(d, updateInfraRequest.Identity, updateInfraRequest.EnvironmentID) + return nil +} + +func buildUpdateInfraRequest(d *schema.ResourceData) *chaos.InfraV2UpdateKubernetesInfrastructureV2Request { + infraReq := &chaos.InfraV2UpdateKubernetesInfrastructureV2Request{} + + if attr, ok := d.GetOk("identifier"); ok { + infraReq.Identity = attr.(string) + } + + infraReq.Name = d.Get("name").(string) + infraReq.Description = d.Get("description").(string) + + tags := []string{} + for _, t := range d.Get("tags").(*schema.Set).List() { + tagStr := t.(string) + tags = append(tags, tagStr) + } + infraReq.Tags = tags + infraReq.InfraNamespace = d.Get("namespace").(string) + if attr, ok := d.GetOk("environment_id"); ok { + infraReq.EnvironmentID = attr.(string) + } + if attr, ok := d.GetOk("service_account"); ok { + infraReq.ServiceAccount = attr.(string) + } + + return infraReq +} + +func readInfraUpdate(d *schema.ResourceData, identifier string, envID string) { + + // can we use corr ID or do we need to return identity? + d.SetId(identifier) + d.Set("environment_id", envID) + +} + +func resourceChaosInfrastructureDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetChaosClientWithContext(ctx) + + var accountIdentifier, orgIdentifier, projectIdentifier, identifier, environmentID string + accountIdentifier = c.AccountId + if attr, ok := d.GetOk("org_id"); ok { + orgIdentifier = attr.(string) + } + if attr, ok := d.GetOk("project_id"); ok { + projectIdentifier = attr.(string) + } + if attr, ok := d.GetOk("identifier"); ok { + identifier = attr.(string) + } + if attr, ok := d.GetOk("environment_id"); ok { + environmentID = attr.(string) + } + _, httpResp, err := c.ChaosSdkApi.DeleteInfraV2(ctx, identifier, environmentID, accountIdentifier, orgIdentifier, projectIdentifier) + + if err != nil { + return helpers.HandleApiError(err, d, httpResp) + } + + return nil +} + +func readInfraCreate(d *schema.ResourceData, infraResponse *chaos.InfraV2RegisterInfrastructureV2Response, envID string) { + d.SetId(infraResponse.Identity) + d.Set("name", infraResponse.Name) + d.Set("environment_id", envID) +} + +func resourceChaosInfrastructureCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetChaosClientWithContext(ctx) + log.Printf("Debugging message: %v", c) + + ctx = context.WithValue(ctx, chaos.ContextAccessToken, hh.EnvVars.BearerToken.Get()) // do we need it + var accountIdentifier, orgIdentifier, projectIdentifier string + createInfraRequest := buildInfraCreateRequest(d) + identifiers := chaos.InfraV2Identifiers{} + createInfraRequest.Identifier = &identifiers + createInfraRequest.Identifier.AccountIdentifier = c.AccountId + accountIdentifier = c.AccountId + if attr, ok := d.GetOk("org_id"); ok { + orgIdentifier = attr.(string) + createInfraRequest.Identifier.OrgIdentifier = attr.(string) + } + if attr, ok := d.GetOk("project_id"); ok { + projectIdentifier = attr.(string) + createInfraRequest.Identifier.ProjectIdentifier = attr.(string) + } + + resp, httpRespUpdate, errUpdate := c.ChaosSdkApi.RegisterInfraV2(ctx, createInfraRequest, accountIdentifier, orgIdentifier, projectIdentifier, &chaos.ChaosSdkApiRegisterInfraV2Opts{}) + if errUpdate != nil { + return helpers.HandleApiError(errUpdate, d, httpRespUpdate) + } + readInfraCreate(d, &resp, createInfraRequest.EnvironmentID) + defer httpRespUpdate.Body.Close() + return nil +} + +func buildInfraCreateRequest(d *schema.ResourceData) chaos.InfraV2RegisterInfrastructureV2Request { + infraReq := chaos.InfraV2RegisterInfrastructureV2Request{} + + if attr, ok := d.GetOk("identifier"); ok { + infraReq.Identity = attr.(string) + infraReq.InfraID = attr.(string) + } + + infraReq.Name = d.Get("name").(string) + infraReq.Description = d.Get("description").(string) + + tags := []string{} + for _, t := range d.Get("tags").(*schema.Set).List() { + tagStr := t.(string) + tags = append(tags, tagStr) + } + infraReq.Tags = tags + + infraReq.InfraNamespace = d.Get("namespace").(string) + if attr, ok := d.GetOk("environment_id"); ok { + infraReq.EnvironmentID = attr.(string) + } + if attr, ok := d.GetOk("service_account"); ok { + infraReq.ServiceAccount = attr.(string) + } + scope := chaos.CLUSTER_InfraV2InfraScope + infraReq.InfraScope = &scope + infraType := chaos.KUBERNETES_InfraV2InfraType + infraReq.InfraType = &infraType + + return infraReq +} + +func readChaosInfrastructure(d *schema.ResourceData, infra chaos.InfraV2KubernetesInfrastructureV2Details) { + d.SetId(infra.Identity) + d.Set("org_id", infra.Identifier.OrgIdentifier) + d.Set("project_id", infra.Identifier.ProjectIdentifier) + d.Set("identifier", infra.Identity) + d.Set("name", infra.Name) + d.Set("description", infra.Description) + d.Set("infra_id", infra.InfraID) + d.Set("environment_id", infra.EnvironmentID) +} diff --git a/internal/session.go b/internal/session.go index 07b7c50e6..4c61e6aa1 100644 --- a/internal/session.go +++ b/internal/session.go @@ -2,8 +2,8 @@ package internal import ( "context" - "github.com/harness/harness-go-sdk/harness/cd" + "github.com/harness/harness-go-sdk/harness/chaos" "github.com/harness/harness-go-sdk/harness/code" "github.com/harness/harness-go-sdk/harness/dbops" "github.com/harness/harness-go-sdk/harness/nextgen" @@ -19,6 +19,7 @@ type Session struct { DBOpsClient *dbops.APIClient Client *openapi_client_nextgen.APIClient CodeClient *code.APIClient + ChaosClient *chaos.APIClient } func (s *Session) GetPlatformClient() (*nextgen.APIClient, context.Context) { @@ -63,3 +64,10 @@ func (s *Session) GetCodeClientWithContext(ctx context.Context) (*code.APIClient return s.CodeClient.WithAuthContext(ctx) } + +func (s *Session) GetChaosClientWithContext(ctx context.Context) (*chaos.APIClient, context.Context) { + if ctx == nil { + ctx = context.Background() + } + return s.ChaosClient.WithAuthContext(ctx) +}