Skip to content

Commit

Permalink
Any backed storage support (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
alt-dima authored May 1, 2024
1 parent 37f5434 commit 4e6c708
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 28 deletions.
56 changes: 39 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ No need to manually create any `tfvars` or `variables` files/directives -> [empt

## Usage

Org with AWS resources and state stored in S3
```
./tofugu cook -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- init
./tofugu cook -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- plan
./tofugu cook -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- apply
./tofugu cook --config examples/.tofugu -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- init
./tofugu cook --config examples/.tofugu -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- plan
./tofugu cook --config examples/.tofugu -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- apply
```

Org with Google Cloud resources and state stored in Google Cloud Storage
```
./tofugu cook --config examples/.tofugu -o gcp-org -d account:free-tier -t free_instance -- init
./tofugu cook --config examples/.tofugu -o gcp-org -d account:free-tier -t free_instance -- plan
./tofugu cook --config examples/.tofugu -o gcp-org -d account:free-tier -t free_instance -- apply
```

- Everything after `--` will be passed as parameters to the `cmd_to_exec`
Expand All @@ -33,7 +41,7 @@ Currently only `dimensions` with list of the required/expecting dimensions (from
## Inventory (dimensions) Store

### Cloud Native Inventory Storage (Toaster-ToasterDB)
You could set env variable `toasterurl` to point to TofuGu-Toaster, like `export toasterurl='https://accountid:accountpass@toaster.example.com'`.
You could set env variable `toasterurl` to point to TofuGu-Toaster, like `export toasterurl='http://accountid:accountpass@toaster.altuhov.su'`.
Then TofuGu will connect and receive all the required dimension data from TofuGu-Toaster-ToasterDB.
Additional parameter could be passed to tofugu `-w workspacename`. In general `workspacename` is the branch name of the source repo where the dimension is stored. If TofuGu-Toaster will not find dimension with specified `workspacename` it will try to return dimension from `master` workspace/branch!

Expand Down Expand Up @@ -84,7 +92,7 @@ provider "aws" {

## $HOME/.tofugu

Config file path maybe provided by the `--config` flag, for example: `
Config file (in YAML format) path maybe provided by the `--config` flag, for example: `
```
./tofugu --config path_to_config/tofuguconfig cook -o demo-org -d account:test-account -d datacenter:staging1 -t vpc -- init
```
Expand All @@ -97,26 +105,31 @@ defaults:
shared_modules_path: examples/tofies/shared-modules
inventory_path: examples/inventory
cmd_to_exec: tofu
s3_bucket_name: default-tfstates
s3_bucket_region: us-east-2
demo-org:
s3_bucket_name: demo-org-tfstates
backend:
bucket: default-tfstates
key: $tofugu_state_path
region: us-east-2
gcp-org:
backend:
bucket: gcp-tfstates
prefix: $tofugu_state_path
```

- `tofies_path` = relative path to the folder with terraform code (`tofi`)
- `shared_modules_path` = relative path to the folder with shared TF modules maybe used by any `tofi`
- `inventory_path` = relative path to the folder with jsons
- `cmd_to_exec` = name of the binary to execute (`tofu` or `terraform`)
- `s3_bucket_name` = name of the S3 bucket to store state
- `s3_bucket_region` = region of the S3 bucket to store state
- `backend` = Config values for backend provider. All the child key:values will be provided to `init` and `$tofugu_state_path` will be replaced by generated path.
For example, it will look like `tofu init -backend-config=bucket=gcp-tfstates -backend-config=prefix=account_free-tier/free_instance.tfstate`

At least
```
defaults:
s3_bucket_name: default-tfstates
s3_bucket_region: us-east-2
backend:
bucket: default-tfstates
key: $tofugu_state_path
```
must be set in the config file!
must be set in the config file! With key:values specific for the backend provider being used in org!

Other options contain hard-coded defaults:
```
Expand All @@ -128,16 +141,25 @@ Other options contain hard-coded defaults:

## Remote state in S3

[Your terraform code (`tofi`) should contains at least:](examples/tofies/demo-org/vpc/versions.tf#L4):
AWS, Google Cloud and some other backends are supported! You could configure any backend provider in `tofugu config file`

[For AWS S3 your terraform code (`tofi`) should contains at least:](examples/tofies/demo-org/vpc/versions.tf#L4):
```
terraform {
backend "s3" {}
}
```

If for the `demo-org` config `s3_bucket_name` is set, then S3 key (path) will be generated like: `s3://demo-org-tfstates/dimName1_dimValue1/dimNameN_dimValueN/tofiName.tfstate`
[For Google Cloud Storage your terraform code (`tofi`) should contains at least:](examples/tofies/gcp-org/free_instance/versions.tf#L4):
```
terraform {
backend "gcs" {}
}
```

If for the `demo-org` config `bucket` is set, then `$tofugu_state_path` will be like: `dimName1_dimValue1/dimNameN_dimValueN/tofiName.tfstate`

If for the `demo-org` config `s3_bucket_name` is NOT set, then S3 key (path) will be generated like `s3://default-tfstates/org_demo-org/dimName1_dimValue1/dimNameN_dimValueN/tofiName.tfstate`
If for the `demo-org` config `bucket` is NOT set, then `$tofugu_state_path` will be like `org_demo-org/dimName1_dimValue1/dimNameN_dimValueN/tofiName.tfstate`

This could be useful, if you want to store by default tfstate for all the organisations in the same/default bucket `default-tfstates` but for some specific organisation you need to store tfstates in dedicated bucket `demo-org-tfstates`

Expand Down
8 changes: 2 additions & 6 deletions cmd/cook.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var cookCmd = &cobra.Command{
tofuguStruct.ParseTofiManifest("tofi_manifest.json")
tofuguStruct.ParseDimensions()

tofuguStruct.SetupStateS3Path()
backendConfig := tofuguStruct.SetupBackendConfig()

tofuguStruct.PrepareTemp()

Expand All @@ -49,14 +49,10 @@ var cookCmd = &cobra.Command{
tofuguStruct.GenerateVarsByEnvVars()

//Local variables for child execution
stateS3Region := tofuguStruct.GetStringFromViperByOrgOrDefault("s3_bucket_region")
stateS3Name := tofuguStruct.GetStringFromViperByOrgOrDefault("s3_bucket_name")
forceCleanTempDir, _ := cmd.Flags().GetBool("clean")
cmdArgs := args
if args[0] == "init" {
cmdArgs = append(cmdArgs, "-backend-config=bucket="+stateS3Name)
cmdArgs = append(cmdArgs, "-backend-config=key="+tofuguStruct.StateS3Path)
cmdArgs = append(cmdArgs, "-backend-config=region="+stateS3Region)
cmdArgs = append(cmdArgs, backendConfig...)
}
cmdToExec := tofuguStruct.GetStringFromViperByOrgOrDefault("cmd_to_exec")

Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func initConfig() {
viper.SetDefault("defaults.tofies_path", "examples/tofies")
viper.SetDefault("defaults.cmd_to_exec", "tofu")

viper.SetConfigType("yaml")
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
Expand All @@ -67,7 +68,6 @@ func initConfig() {

// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".tofugu")
}

Expand Down
10 changes: 8 additions & 2 deletions examples/.tofugu
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@ defaults:
shared_modules_path: examples/tofies/shared-modules
inventory_path: examples/inventory
cmd_to_exec: tofu
s3_bucket_name: default-tfstates
s3_bucket_region: us-east-2
backend:
bucket: default-tfstates
key: $tofugu_state_path
region: us-east-2
gcp-org:
backend:
bucket: gcp-tfstates
prefix: $tofugu_state_path
5 changes: 5 additions & 0 deletions examples/inventory/gcp-org/account/free-tier.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"project" : "test-project",
"region" : "us-central1",
"zone" : "us-central1-c"
}
26 changes: 26 additions & 0 deletions examples/tofies/gcp-org/free_instance/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
resource "google_compute_instance" "vm_instance" {
name = "free-instance"
machine_type = "e2-micro"

tags = [ "free-instance" ]

boot_disk {
initialize_params {
//size = 15
image = "ubuntu-os-cloud/ubuntu-minimal-2204-jammy-v20240430"
}
}

metadata = {
ssh-keys = "username:ssh-key"
block-project-ssh-keys = true
}

network_interface {
network = "default"
# A default network is created for all GCP projects
#network = google_compute_network.vpc_network.self_link
access_config {
}
}
}
Empty file.
10 changes: 10 additions & 0 deletions examples/tofies/gcp-org/free_instance/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# provider "aws" {
# //region = var.tofugu_account_manifest.region
# region = var.tofugu_envvar_awsregion
# }

provider "google" {
project = var.tofugu_account_data.project
region = var.tofugu_account_data.region
zone = var.tofugu_account_data.zone
}
3 changes: 3 additions & 0 deletions examples/tofies/gcp-org/free_instance/tofi_manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"dimensions": ["account"]
}
Empty file.
Empty file.
12 changes: 12 additions & 0 deletions examples/tofies/gcp-org/free_instance/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_version = ">= 1.1"

backend "gcs" {}

required_providers {
google = {
source = "hashicorp/google"
version = "5.27.0"
}
}
}
27 changes: 25 additions & 2 deletions utils/externals.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net/http"
"os"
"strings"

"github.com/spf13/viper"
)
Expand All @@ -26,15 +27,37 @@ func (tofuguStruct *Tofugu) GetStringFromViperByOrgOrDefault(keyName string) str
}
}

func (tofuguStruct *Tofugu) SetupStateS3Path() {
func (tofuguStruct *Tofugu) GetObjectFromViperByOrgOrDefault(keyName string) map[string]any {
if viper.IsSet(tofuguStruct.OrgName + "." + keyName) {
return viper.GetStringMap(tofuguStruct.OrgName + "." + keyName)
} else {
return viper.GetStringMap("defaults." + keyName)
}
}

func (tofuguStruct *Tofugu) SetupBackendConfig() []string {
var backendFinalConfig []string

var stateS3Path string
if !viper.IsSet(tofuguStruct.OrgName + ".s3_bucket_name") {
if !viper.IsSet(tofuguStruct.OrgName + ".backend") {
stateS3Path = stateS3Path + "org_" + tofuguStruct.OrgName + "/"
}

for _, dimension := range tofuguStruct.TofiManifest.Dimensions {
stateS3Path = stateS3Path + dimension + "_" + tofuguStruct.ParsedDimensions[dimension] + "/"
}
tofuguStruct.StateS3Path = stateS3Path + tofuguStruct.TofiName + ".tfstate"

backendTofuguConfig := tofuguStruct.GetObjectFromViperByOrgOrDefault("backend")
if len(backendTofuguConfig) == 0 {
log.Println("Tofugu: no backend config provied!")
}
for param, value := range backendTofuguConfig {
replacedVar := strings.Replace(value.(string), "$tofugu_state_path", tofuguStruct.StateS3Path, 1)
backendFinalConfig = append(backendFinalConfig, "-backend-config="+param+"="+replacedVar)
}

return backendFinalConfig
}

func (tofuguStruct *Tofugu) GetDimData(dimensionKey string, dimensionValue string, skipOnNotFound bool) map[string]interface{} {
Expand Down

0 comments on commit 4e6c708

Please sign in to comment.