Skip to content

Commit

Permalink
doc/md: guide for named dbs with tf (#1574)
Browse files Browse the repository at this point in the history
  • Loading branch information
rotemtam authored Apr 4, 2023
1 parent e37eae4 commit c7f9010
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 11 deletions.
3 changes: 3 additions & 0 deletions doc/md/guides/guides.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ Importing from [`pressly/goose`](migration-tools/goose-import.md)

[GORM](orms/gorm.md)

## Terraform

[Provisioning schemas (named databases)](terraform/named-databases.mdx)

## Cloud

Expand Down
4 changes: 2 additions & 2 deletions doc/md/guides/sqlite/descending-indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,10 @@ atlas schema inspect -u "sqlite://telecom_data.db" | grep -A5 index

Amazing! Our new descending index is now created!

## Wrapping up
## Wrapping up
In this guide, we demonstrated how to create and use descending indexes in SQLite to optimize queries with the ORDER BY clause, and how we can use Atlas to easily manage descending indexes in a SQLite database.

## Need More Help?​​
## Need More Help?
[Join the Atlas Discord Server](https://discord.com/invite/zZ6sWVg6NT) for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.

[Sign up](https://www.getrevue.co/profile/ariga) to our newsletter to stay up to date about [Atlas](https://atlasgo.io/), our OSS database schema management tool, and our cloud platform [Atlas Cloud](https://atlasgo.cloud/).
244 changes: 244 additions & 0 deletions doc/md/guides/terraform/named-databases.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
---
id: named-databases
title: Provisioning schemas (named databases) with Terraform using Atlas
slug: /guides/terraform/named-databases
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

[Terraform](https://www.terraform.io/) is an Infrastructure as Code (IaC) tool that allows teams
to automate and manage their infrastructure through code. It streamlines the process of provisioning,
updating, and maintaining infrastructure resources while reducing human error.

Many teams rely on managed SQL databases, such as [Amazon RDS](https://aws.amazon.com/rds/) on
AWS or [Cloud SQL](https://cloud.google.com/sql) on GCP, and use the relevant Terraform provider
([AWS](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance),
[GCP](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/sql_database_instance))
to provision these databases. For example:

<Tabs
defaultValue="AWS"
values={[
{label: 'RDS (AWS)', value: 'AWS'},
{label: 'Cloud SQL (GCP)', value: 'GCP'},
]}>
<TabItem value="AWS">

```hcl
resource "aws_db_instance" "default" {
allocated_storage = 10
db_name = "mydb"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
username = "foo"
password = "foobarbaz"
parameter_group_name = "default.mysql8.0"
skip_final_snapshot = true
}
```

</TabItem>
<TabItem value={"GCP"}>

```hcl
resource "google_sql_database_instance" "main" {
name = "main-instance"
database_version = "POSTGRES_14"
region = "us-central1"
settings {
tier = "db-f1-micro"
}
}
```
</TabItem>
</Tabs>

When preparing the infrastructure for
the deployment of applications, IaC need to ensure the required schemas (named databases) exist on the
database instance in which tables and other database resources will be created.

This can be done manually by connecting to the database instance and running a command such as:

```sql
CREATE SCHEMA "users";
CREATE SCHEMA "products";
CREATE SCHEMA "admin";
```

Such manual interactions with infrastructure is undesired in the context of IaC pipelines which
aim to eliminate all manual provisioning steps and replace them with automation.

To achieve the same goal, Terraform users may use the
[Atlas Terraform Provider](https://registry.terraform.io/providers/ariga/atlas/latest/docs)
which allows teams to provision database resources as part of their IaC pipelines. Let's see how.

Start by adding the Atlas provider as a dependency of your Terraform project:

```hcl
terraform {
required_providers {
// highlight-start
atlas = {
source = "ariga/atlas"
version = "~> 0.4.5"
}
// highlight-end
}
}
```

When storing schema definitions, many database engines perform some form of normalization.
Meaning, despite us providing a specific definition of some aspect of the schema, the database
will store it in another, equivalent form. Therefore, in certain situations it may appear to
Atlas as if some diff exists between the desired and inspected schemas, whereas in reality
there is none.

To overcome these situations, we use the `atlas_schema` data source to provide Atlas
with a connection string to a [_Dev-Database_](https://atlasgo.io/concepts/dev-database).
This database is used to normalize the schema prior to planning migrations and for simulating
changes to ensure their applicability before execution.

Before running `terraform apply` for this project, make sure you have a locally running,
*empty* database. You can use Docker to spin one up:

```shell
docker run --rm --name devdb -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass mysql:8
```

Next, normalize the schema definition containing three schemas:
`users`, `products` and `admin`:

```hcl
// Load (and normalize) the desired schema from an SQL file.
data "atlas_schema" "db" {
src = <<EOF
schema "users" {}
schema "products" {}
schema "admin" {}
EOF
dev_db_url = "mysql://root:pass@:3306/"
}
```

The `src` attribute defines the desired schema we wish to apply to our
target database. Next let's see how we can apply this schema to our target
database:

```hcl
// Sync the state of the target database with the desired state.
resource "atlas_schema" "db" {
hcl = data.atlas_schema.db.hcl
url = "mysql://root:pass@localhost:3306/"
exclude = [ "users.*", "products.*", "admin.*" ]
dev_db_url = "mysql://root:pass@:3306/"
}
```

Let's unpack what's going on here:
* `hcl` - sets the normalized schema from the data source as the desired state for the database.
* `url` - is the connection string to the target database that we want to manage. To learn how to
construct the connection string from an existing managed database see
[this guide](https://atlasgo.io/guides/mysql/terraform#connect-everything-together).
* `exclude` - tells Atlas to ignore any sub-resources inside the three schemas `users`,
`products`, and `admin`. This way if tables are later created under these schemas they
will not be dropped by Terraform in future `terraform apply` runs.

### See it in action

Since we added a new provider to our project, let's first re-initialize the project:
```shell
terraform init
```

Finally, let's run `apply`:

```shell
terraform apply
data.atlas_schema.db: Reading...
data.atlas_schema.db: Read complete after 0s [id=e9CBNHJqQOfAb7WRlAt83w]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# atlas_schema.market will be created
+ resource "atlas_schema" "market" {
+ exclude = [
+ "users.*",
+ "products.*",
+ "admin.*",
]
+ hcl = <<-EOT
schema "admin" {
charset = "utf8mb4"
collate = "utf8mb4_0900_ai_ci"
}
schema "products" {
charset = "utf8mb4"
collate = "utf8mb4_0900_ai_ci"
}
schema "users" {
charset = "utf8mb4"
collate = "utf8mb4_0900_ai_ci"
}
EOT
+ id = (known after apply)
+ url = (sensitive value)
}
Plan: 1 to add, 0 to change, 0 to destroy.
│ Warning: Atlas Plan
│ with atlas_schema.market,
│ on main.tf line 23, in resource "atlas_schema" "market":
│ 23: resource "atlas_schema" "market" {
│ The following SQL statements will be executed:
│ -- add new schema named "admin"
│ CREATE DATABASE `admin`
│ -- add new schema named "products"
│ CREATE DATABASE `products`
│ -- add new schema named "users"
│ CREATE DATABASE `users`
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
```
We type `yes` to apply our plan:
```shell
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```
Let's re-run apply, to make sure everything is in order:
```
terraform apply
data.atlas_schema.db: Reading...
data.atlas_schema.db: Read complete after 0s [id=e9CBNHJqQOfAb7WRlAt83w]
atlas_schema.market: Refreshing state... [id=mysql://localhost:3306/]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
```
Great! Everything is in sync.
### Wrapping up
In this guide, we demonstrated how to use Terraform and the Atlas Terraform Provider
to provision empty schemas (named databases) in existing DB instances as part
of your Infrastructure-as-Code pipelines.
Have questions? Feedback? Find our team [on our Discord server](https://discord.gg/zZ6sWVg6NT).
30 changes: 21 additions & 9 deletions doc/website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ module.exports = {
label: 'CHECK Constraint'
},
{
type: 'doc',
id: 'guides/mysql/generated-columns',
label: 'Generated Columns'
type: 'doc',
id: 'guides/mysql/generated-columns',
label: 'Generated Columns'
},
{
type: 'doc',
Expand All @@ -202,6 +202,18 @@ module.exports = {
},
],
},
{
type: 'category',
label: 'Terraform',
collapsed: false,
items: [
{
type: 'doc',
id: 'guides/terraform/named-databases',
label: 'Named Databases'
}
]
},
{
type: 'category',
label: 'PostgreSQL',
Expand Down Expand Up @@ -273,12 +285,12 @@ module.exports = {
items: [
{
type: 'doc',
id:'guides/migration-tools/golang-migrate',
id: 'guides/migration-tools/golang-migrate',
label: 'golang-migrate'
},
{
type: 'doc',
id:'guides/migration-tools/goose-import',
id: 'guides/migration-tools/goose-import',
label: 'Importing from goose'
}
]
Expand All @@ -290,17 +302,17 @@ module.exports = {
items: [
{
type: 'doc',
id:'guides/orms/gorm',
id: 'guides/orms/gorm',
label: 'GORM'
},
{
type: 'doc',
id:'guides/frameworks/sqlc-declarative',
id: 'guides/frameworks/sqlc-declarative',
label: 'Declarative migrations for sqlc'
},
{
type: 'doc',
id:'guides/frameworks/sqlc-versioned',
id: 'guides/frameworks/sqlc-versioned',
label: 'Versioned migrations for sqlc'
}
]
Expand All @@ -312,7 +324,7 @@ module.exports = {
items: [
{
type: 'doc',
id:'guides/cloud/explore-inspection',
id: 'guides/cloud/explore-inspection',
label: 'How to inspect a local database in the Cloud'
}
]
Expand Down

0 comments on commit c7f9010

Please sign in to comment.