Skip to content

Commit

Permalink
Add support for resource gocd_backup_config and gocd_backup_schedule
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhilsbhat committed Mar 12, 2023
1 parent c2aa61b commit 419a642
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 2 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ issues:
linters-settings:
funlen:
lines: 190
statements: 60

lll:
line-length: 165
Expand Down
2 changes: 1 addition & 1 deletion examples/agents_config.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
data "gocd_agent_config" "sample_agent" {
uuid = "4c92e7e3-8abd-4b02-a7eb-c46b2c7ac674"
uuid = "3bc47e62-eb8f-4c37-b082-1517f10ef7aa"
}
8 changes: 8 additions & 0 deletions examples/backup_config.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "gocd_backup_config" "nightly_backup" {
schedule = "0 0 2 * * ?"
post_backup_script = "path/to/postbackup_script.sh"
}

resource "gocd_backup_schedule" "now" {
schedule = true
}
3 changes: 2 additions & 1 deletion examples/environment.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ resource "gocd_environment" "sample_environment" {
}

data "gocd_environment" "sample_environment" {
name = gocd_environment.sample_environment.id
depends_on = [gocd_environment.sample_environment]
name = gocd_environment.sample_environment.id
}
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ func Provider() *schema.Provider {
"gocd_environment": resourceEnvironment(),
"gocd_encrypt_value": resourceEncryptValue(),
"gocd_secret_config": resourceSecretConfig(),
"gocd_backup_config": resourceBackupConfig(),
"gocd_backup_schedule": resourceBackupSchedule(),
// "gocd_agent": resourceAgentConfig(),
},

Expand Down
149 changes: 149 additions & 0 deletions internal/provider/resource_backup_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package provider

import (
"context"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/nikhilsbhat/gocd-sdk-go"
"github.com/nikhilsbhat/terraform-provider-gocd/pkg/utils"
)

func resourceBackupConfig() *schema.Resource {
return &schema.Resource{
CreateContext: resourceBackupConfigCreate,
ReadContext: resourceBackupConfigRead,
UpdateContext: resourceBackupConfigUpdate,
DeleteContext: resourceBackupConfigDelete,
Schema: map[string]*schema.Schema{
"schedule": {
Type: schema.TypeString,
Required: true,
Computed: false,
Description: "The backup schedule. See the quartz documentation for syntax and examples.",
},
"post_backup_script": {
Type: schema.TypeString,
Optional: true,
Computed: false,
Description: "The script that will be executed once the backup finishes. See the gocd documentation for details.",
},
"email_on_success": {
Type: schema.TypeBool,
Optional: true,
Computed: false,
Description: "If set to true, an email will be sent when backup completes successfully.",
},
"email_on_failure": {
Type: schema.TypeBool,
Optional: true,
Computed: false,
Description: "If set to true, an email will be sent when backup fails.",
},
},
}
}

func resourceBackupConfigCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

if !d.IsNewResource() {
return nil
}

id := d.Id()

if len(id) == 0 {
resourceID := utils.String(d.Get(utils.TerraformResourceSchedule))
id = resourceID
}

cfg := gocd.BackupConfig{
Schedule: utils.String(d.Get(utils.TerraformResourceSchedule)),
PostBackupScript: utils.String(d.Get(utils.TerraformResourcePostBackupScript)),
EmailOnSuccess: utils.Bool(d.Get(utils.TerraformResourceEmailOnSuccess)),
EmailOnFailure: utils.Bool(d.Get(utils.TerraformResourceEmailOnFailure)),
}

if err := defaultConfig.CreateOrUpdateBackupConfig(cfg); err != nil {
return diag.Errorf("creating backup configuration errored with %v", err)
}

d.SetId(id)

return resourceBackupConfigRead(ctx, d, meta)
}

func resourceBackupConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

response, err := defaultConfig.GetBackupConfig()
if err != nil {
return diag.Errorf("getting backup configuration errored with: %v", err)
}

if err = d.Set(utils.TerraformResourceSchedule, response.Schedule); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceSchedule, err)
}

if err = d.Set(utils.TerraformResourcePostBackupScript, response.PostBackupScript); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourcePostBackupScript, err)
}

if err = d.Set(utils.TerraformResourceEmailOnSuccess, response.EmailOnSuccess); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceEmailOnSuccess, err)
}

if err = d.Set(utils.TerraformResourceEmailOnFailure, response.EmailOnFailure); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceEmailOnFailure, err)
}

return nil
}

func resourceBackupConfigUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

if !d.HasChanges(
utils.TerraformResourceSchedule,
utils.TerraformResourcePostBackupScript,
utils.TerraformResourceEmailOnSuccess,
utils.TerraformResourceEmailOnFailure,
) {
log.Printf("nothing to update so skipping")

return nil
}

cfg := gocd.BackupConfig{
Schedule: utils.String(d.Get(utils.TerraformResourceSchedule)),
PostBackupScript: utils.String(d.Get(utils.TerraformResourcePostBackupScript)),
EmailOnSuccess: utils.Bool(d.Get(utils.TerraformResourceEmailOnSuccess)),
EmailOnFailure: utils.Bool(d.Get(utils.TerraformResourceEmailOnFailure)),
}

if err := defaultConfig.CreateOrUpdateBackupConfig(cfg); err != nil {
return diag.Errorf("updating backup configuration errored with %v", err)
}

return resourceBackupConfigRead(ctx, d, meta)
}

func resourceBackupConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

id := d.Id()
if len(d.Id()) == 0 {
return diag.Errorf("resource with the ID '%s' not found", id)
}

err := defaultConfig.DeleteBackupConfig()
if err != nil {
return diag.Errorf("deleting backup configuration errored with: %v", err)
}

d.SetId("")

return nil
}
167 changes: 167 additions & 0 deletions internal/provider/resource_backup_schedule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package provider

import (
"context"
"log"
"strconv"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/nikhilsbhat/gocd-sdk-go"
"github.com/nikhilsbhat/terraform-provider-gocd/pkg/utils"
)

func resourceBackupSchedule() *schema.Resource {
return &schema.Resource{
CreateContext: resourceBackupScheduleCreate,
ReadContext: resourceBackupScheduleRead,
DeleteContext: resourceBackupScheduleDelete,
Schema: map[string]*schema.Schema{
"schedule": {
Type: schema.TypeBool,
Required: true,
Computed: false,
ForceNew: true,
Description: "Enable to trigger the backup, (would be unset post backup is successful).",
},
"retry": {
Type: schema.TypeInt,
Optional: true,
Computed: false,
Default: defaultRetry,
ForceNew: true,
Description: "Number of times to retry to get the ID of latest successful backup taken.",
},
"delay": {
Type: schema.TypeInt,
Optional: true,
Computed: false,
Default: defaultDelay,
ForceNew: true,
Description: "Time delay between each retries that would be made to get backup stats (in seconds ex: 5).",
},
"retry_after": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
Description: "This would be set to handle the backup scheduling internally.",
},
"backup_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "Id of the backup that was taken successfully",
},
},
}
}

var (
defaultRetry = 30
defaultDelay = 5
)

func resourceBackupScheduleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

if !d.IsNewResource() {
return nil
}

var id string

newID, err := utils.GetRandomID()
if err != nil {
d.SetId("")

return diag.Errorf("errored while fetching randomID %v", err)
}
id = newID

if !utils.Bool(d.Get(utils.TerraformResourceSchedule)) {
return diag.Errorf("scheduling backup is disabled, set attribute '%s' to true to schedule a backup", utils.TerraformResourceSchedule)
}

response, err := defaultConfig.ScheduleBackup()
if err != nil {
return diag.Errorf("scheduling backup errored with: %v", err)
}

retryAfter, err := strconv.Atoi(response["RetryAfter"])
if err != nil {
return diag.Errorf("%v", err)
}

if err = d.Set(utils.TerraformResourceBackupID, response["BackUpID"]); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceBackupID, err)
}

if err = d.Set(utils.TerraformResourceRetryAfter, retryAfter); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceRetryAfter, err)
}

d.SetId(id)

return resourceBackupScheduleRead(ctx, d, meta)
}

func resourceBackupScheduleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
defaultConfig := meta.(gocd.GoCd)

retryAfter := d.Get(utils.TerraformResourceRetryAfter).(int)
backupRetry := d.Get(utils.TerraformResourceRetry).(int)
delayCount := d.Get(utils.TerraformResourceDelay).(int)
backupID := utils.String(d.Get(utils.TerraformResourceBackupID))

delay := time.Duration(delayCount) * time.Second

time.Sleep(time.Duration(retryAfter) * time.Second)
currentRetryCount := 0
var latestBackupStatus string
for {
if currentRetryCount > backupRetry {
return diag.Errorf("maximum retry count of '%d' crossed with current count '%d', still backup is not ready yet with status '%s'. Exiting",
backupRetry, currentRetryCount, latestBackupStatus)
}

response, err := defaultConfig.GetBackup(backupID)
if err != nil {
return diag.Errorf("getting last configured backup ID errored with: %v", err)
}

retryRemaining := backupRetry - currentRetryCount
if response.Status == "IN_PROGRESS" {
log.Printf("the backup stats is still in IN_PROGRESS status, retrying... '%d' more to go", retryRemaining)
}

if response.Status == "COMPLETED" {
if err = d.Set(utils.TerraformResourceSchedule, false); err != nil {
return diag.Errorf(settingAttrErrorTmp, utils.TerraformResourceSchedule, err)
}

break
}

if response.Status != "COMPLETED" && response.Status != "IN_PROGRESS" {
return diag.Errorf("looks like backup status is neither IN_PROGRESS nor COMPLETED rather it is %s", response.Status)
}

latestBackupStatus = response.Status
time.Sleep(delay)
currentRetryCount++
}

return nil
}

func resourceBackupScheduleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
id := d.Id()
if len(d.Id()) == 0 {
return diag.Errorf("resource with the ID '%s' not found", id)
}

d.SetId("")

return nil
}
8 changes: 8 additions & 0 deletions pkg/utils/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,12 @@ const (
TerraformResourceEnvironments = "environments"
TerraformResourceBuildState = "build_state"
TerraformResourceBuildDetails = "build_details"
TerraformResourceSchedule = "schedule"
TerraformResourcePostBackupScript = "post_backup_script"
TerraformResourceEmailOnFailure = "email_on_failure"
TerraformResourceEmailOnSuccess = "email_on_success"
TerraformResourceRetryAfter = "retry_after"
TerraformResourceRetry = "retry"
TerraformResourceDelay = "delay"
TerraformResourceBackupID = "backup_id"
)

0 comments on commit 419a642

Please sign in to comment.