Skip to content

Commit

Permalink
[Feature] Add databricks_budget resource (#3955)
Browse files Browse the repository at this point in the history
## Changes
Add support for account-level
[Budget](https://docs.databricks.com/api/account/budgets) resource

Example structure
```
resource "databricks_budget" "this" {
    display_name = "data-science-budget"

    alert_configurations {
        time_period         = "MONTH"
        trigger_type        = "CUMULATIVE_SPENDING_EXCEEDED" 
        quantity_type       = "LIST_PRICE_DOLLARS_USD"
        quantity_threshold  = "840"

        action_configurations {
            action_type = "EMAIL_NOTIFICATION"
            target      = "me@databricks.com"
        }
    }

    filter {
        workspace_id {
            operator = "IN"
            values   = [
                1234567890098765
            ]                              
        }
        tags {
            key   = "Databricks"
            value {
                operator = "IN"
                values = ["Data Science"]
            }
        }
    }
}
```

Resolves #3887

## Tests
- [x] `make test` run locally
- [x] relevant change in `docs/` folder
- [x] covered with integration tests in `internal/acceptance`
- [ ] relevant acceptance tests are passing
- [x] using Go SDK

---------

Co-authored-by: Alex Ott <alexott@gmail.com>
  • Loading branch information
840 and alexott authored Oct 2, 2024
1 parent 1e89ad4 commit f757db0
Show file tree
Hide file tree
Showing 5 changed files with 507 additions and 0 deletions.
103 changes: 103 additions & 0 deletions docs/resources/budget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
subcategory: "FinOps"
---
# databricks_budget Resource

-> **Note** Initialize provider with `alias = "account"`, and `host` pointing to the account URL, like, `host = "https://accounts.cloud.databricks.com"`. Use `provider = databricks.account` for all account-level resources.

-> **Public Preview** This feature is in [Public Preview](https://docs.databricks.com/release-notes/release-types.html).

This resource allows you to manage [Databricks Budgets](https://docs.databricks.com/en/admin/account-settings/budgets.html).

## Example Usage

```hcl
resource "databricks_budget" "this" {
display_name = "databricks-workspace-budget"
alert_configurations {
time_period = "MONTH"
trigger_type = "CUMULATIVE_SPENDING_EXCEEDED"
quantity_type = "LIST_PRICE_DOLLARS_USD"
quantity_threshold = "840"
action_configurations {
action_type = "EMAIL_NOTIFICATION"
target = "abc@gmail.com"
}
}
filter {
workspace_id {
operator = "IN"
values = [
1234567890098765
]
}
tags {
key = "Team"
value {
operator = "IN"
values = ["Data Science"]
}
}
tags {
key = "Environment"
value {
operator = "IN"
values = ["Development"]
}
}
}
}
```

## Argument Reference

The following arguments are available:

* `display_name` - (Required) Name of the budget in Databricks Account.

### alert_configurations Configuration Block (Required)

* `time_period` - (Required, String Enum) The time window of usage data for the budget. (Enum: `MONTH`)
* `trigger_type` - (Required, String Enum) The evaluation method to determine when this budget alert is in a triggered state. (Enum: `CUMULATIVE_SPENDING_EXCEEDED`)
* `quantity_type` - (Required, String Enum) The way to calculate cost for this budget alert. This is what quantity_threshold is measured in. (Enum: `LIST_PRICE_DOLLARS_USD`)
* `quantity_threshold` - (Required, String) The threshold for the budget alert to determine if it is in a triggered state. The number is evaluated based on `quantity_type`.
* `action_configurations` - (Required) List of action configurations to take when the budget alert is triggered. Consists of the following fields:
* `action_type` - (Required, String Enum) The type of action to take when the budget alert is triggered. (Enum: `EMAIL_NOTIFICATION`)
* `target` - (Required, String) The target of the action. For `EMAIL_NOTIFICATION`, this is the email address to send the notification to.

### filter Configuration Block (Optional)

* `workspace_id` - (Optional) Filter by workspace ID (if empty, include usage all usage for this account). Consists of the following fields:
* `operator` - (Required, String Enum) The operator to use for the filter. (Enum: `IN`)
* `values` - (Required, List of numbers) The values to filter by.
* `tags` - (Optional) List of tags to filter by. Consists of the following fields:
* `key` - (Required, String) The key of the tag.
* `value` - (Required) Consists of the following fields:
* `operator` - (Required, String Enum) The operator to use for the filter. (Enum: `IN`)
* `values` - (Required, List of strings) The values to filter by.

## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `budget_configuration_id` - The ID of the budget configuration.
* `account_id` - The ID of the Databricks Account.

## Import

This resource can be imported by Databricks account ID and Budget.

```sh
terraform import databricks_budget.this '<account_id>|<budget_configuration_id>'
```

## Related Resources

The following resources are used in the context:

* [databricks_mws_workspaces](mws_workspaces.md) to set up Databricks workspaces.
102 changes: 102 additions & 0 deletions finops/resource_budget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package finops

import (
"context"
"strings"

"github.com/databricks/databricks-sdk-go/service/billing"
"github.com/databricks/terraform-provider-databricks/common"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func ResourceBudget() common.Resource {
s := common.StructToSchema(billing.BudgetConfiguration{}, func(m map[string]*schema.Schema) map[string]*schema.Schema {
common.CustomizeSchemaPath(m, "display_name").SetValidateFunc(validation.StringLenBetween(1, 128))
for _, p := range []string{"account_id", "budget_configuration_id", "create_time", "update_time"} {
common.CustomizeSchemaPath(m, p).SetComputed()
}
common.CustomizeSchemaPath(m, "alert_configurations", "alert_configuration_id").SetComputed()
common.CustomizeSchemaPath(m, "alert_configurations", "action_configurations", "action_configuration_id").SetComputed()
// We need SuppressDiff because API returns a string representation of BigDecimal with a lot
// of trailing 0s, etc.
common.CustomizeSchemaPath(m, "alert_configurations", "quantity_threshold").SetCustomSuppressDiff(func(k, old, new string, d *schema.ResourceData) bool {
normalize := func(v string) string {
if strings.Contains(v, ".") {
v = strings.TrimRight(v, "0")
v = strings.TrimSuffix(v, ".")
}
return v
}
return normalize(old) == normalize(new)
})
return m
})
p := common.NewPairID("account_id", "budget_configuration_id")
return common.Resource{
Create: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var create billing.CreateBudgetConfigurationBudget
common.DataToStructPointer(d, s, &create)
acc, err := c.AccountClient()
if err != nil {
return err
}
budget, err := acc.Budgets.Create(ctx, billing.CreateBudgetConfigurationRequest{Budget: create})
if err != nil {
return err
}
d.Set("budget_configuration_id", budget.Budget.BudgetConfigurationId)
d.Set("account_id", c.Config.AccountID)
common.StructToData(budget.Budget, s, d)
p.Pack(d)
return nil
},
Read: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
_, id, err := p.Unpack(d)
if err != nil {
return err
}
acc, err := c.AccountClient()
if err != nil {
return err
}
budget, err := acc.Budgets.GetByBudgetId(ctx, id)
if err != nil {
return err
}
return common.StructToData(budget.Budget, s, d)
},
Update: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
var update billing.UpdateBudgetConfigurationBudget
_, id, err := p.Unpack(d)
if err != nil {
return err
}
common.DataToStructPointer(d, s, &update)
acc, err := c.AccountClient()
if err != nil {
return err
}
budget, err := acc.Budgets.Update(ctx, billing.UpdateBudgetConfigurationRequest{
Budget: update,
BudgetId: id,
})
if err != nil {
return err
}
return common.StructToData(budget.Budget, s, d)
},
Delete: func(ctx context.Context, d *schema.ResourceData, c *common.DatabricksClient) error {
_, id, err := p.Unpack(d)
if err != nil {
return err
}
acc, err := c.AccountClient()
if err != nil {
return err
}
return acc.Budgets.DeleteByBudgetId(ctx, id)
},
Schema: s,
}
}
Loading

0 comments on commit f757db0

Please sign in to comment.