github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/roachprod/vm/aws/terraformgen/terraformgen.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Command terraformgen generate the terraform file used to configure AWS for 12 // multiregion support. 13 package main 14 15 import ( 16 "fmt" 17 "io" 18 "os" 19 "text/template" 20 21 "github.com/spf13/cobra" 22 ) 23 24 var templates = []struct { 25 name, template string 26 }{ 27 {"header", 28 `# --------------------------------------------------------------------------------------------------------------------- 29 # TERRAFORM SETTINGS 30 # --------------------------------------------------------------------------------------------------------------------- 31 terraform { 32 required_version = ">= 0.11.8" 33 backend "s3" { 34 key = "terraform/{{ .ResourcePrefix }}" 35 bucket = "{{ .ResourcePrefix }}-cloud-state" 36 region = "us-east-2" 37 } 38 } 39 40 # --------------------------------------------------------------------------------------------------------------------- 41 # Variable names that should not change beyond initial config. 42 # --------------------------------------------------------------------------------------------------------------------- 43 locals { 44 account_number = {{ printf "%q" .AccountNumber }} 45 label = {{ printf "%q" .ResourcePrefix }} 46 } 47 48 # --------------------------------------------------------------------------------------------------------------------- 49 # AWS 50 # ---------------------------------------------------------------------------------------------------------------------`}, 51 52 {"regions", 53 `{{ range .Regions }} 54 provider "aws" { 55 alias = "{{ $.Resource . }}" 56 region = "{{ . }}" 57 58 # Fixed fields, DO NOT MODIFY. 59 version = "~> 1.41" 60 } 61 62 module "aws_{{ $.Resource . }}" { 63 providers { 64 aws = "aws.{{ $.Resource . }}" 65 } 66 region = {{ . | printf "%q" }} 67 source = "aws-region" 68 label = {{ $.ResourcePrefix | printf "%q" }} 69 } 70 {{ end }}`}, 71 72 {"peerings", 73 `{{ range .Peerings }} 74 module "vpc_peer_{{index . 0 }}-{{ index . 1 }}" { 75 providers { 76 aws.owner = "aws.{{ index . 0 }}" 77 aws.peer = "aws.{{ index . 1 }}" 78 } 79 owner_vpc_info = "${module.aws_{{index . 0}}.vpc_info}" 80 peer_vpc_info = "${module.aws_{{index . 1}}.vpc_info}" 81 82 label = {{ $.ResourcePrefix | printf "%q" }} 83 source = "aws-vpc-peer" 84 } 85 {{ end }} 86 `}, 87 88 {"output", 89 `output "regions" { 90 value = "${list({{- range $index, $el := .Regions }}{{ if $index }},{{end}} 91 "${module.aws_{{ $.Resource . }}.region_info}" 92 {{- end }} 93 )}" 94 } 95 `}, 96 {"terraform", 97 `{{ template "header" . }} 98 {{ template "regions" . }} 99 {{ template "peerings" . }} 100 {{ template "output" . }} 101 `}, 102 } 103 104 var tmpl = func() *template.Template { 105 cur := template.New("base") 106 for _, t := range templates { 107 cur = template.Must(cur.New(t.name).Parse(t.template)) 108 } 109 return cur 110 }() 111 112 type data struct { 113 AccountNumber string 114 ResourcePrefix string 115 Regions []string 116 } 117 118 func (d *data) Resource(s string) string { 119 return d.ResourcePrefix + "-" + s 120 } 121 122 func (d *data) Peerings() (peerings [][2]string) { 123 for i := 0; i < len(d.Regions); i++ { 124 for j := i + 1; j < len(d.Regions); j++ { 125 peerings = append(peerings, [2]string{ 126 d.Resource(d.Regions[i]), 127 d.Resource(d.Regions[j]), 128 }) 129 } 130 } 131 return peerings 132 } 133 134 // Defeat the unused linter because it's not smart enough to know about template 135 // calls. 136 var _ = (*data)(nil).Peerings 137 var _ = (*data)(nil).Resource 138 139 var defaultData = data{ 140 Regions: []string{ 141 "ap-northeast-1", 142 "ap-northeast-2", 143 "ap-south-1", 144 "ap-southeast-1", 145 "ap-southeast-2", 146 "ca-central-1", 147 "eu-central-1", 148 "eu-west-1", 149 "eu-west-2", 150 "eu-west-3", 151 "sa-east-1", 152 "us-east-1", 153 "us-east-2", 154 "us-west-1", 155 "us-west-2", 156 }, 157 AccountNumber: "541263489771", 158 ResourcePrefix: "roachprod", 159 } 160 161 func main() { 162 data := defaultData 163 output := "-" 164 rootCmd := &cobra.Command{ 165 Use: "terraformgen", 166 Short: "terraformgen generates a terraform file for use with roachprod on aws.", 167 Long: ` 168 terraformgen generates a terraform main file which, when combined with the 169 modules defined in the sibling terraform directory to this programs source code 170 will set up VPCs and security groups for each of specified regions for 171 the provided account number. The json artifact created as a result of running 172 terraform apply is consumed by roachprod. 173 `, 174 Run: func(_ *cobra.Command, _ []string) { 175 out := io.Writer(os.Stderr) 176 if output != "-" { 177 f, err := os.Create(output) 178 exitIfError(err) 179 defer f.Close() 180 out = f 181 } 182 exitIfError(tmpl.Execute(out, &data)) 183 }, 184 } 185 rootCmd.Flags().StringSliceVar(&data.Regions, "regions", data.Regions, 186 "list of regions to operate in") 187 rootCmd.Flags().StringVar(&data.AccountNumber, "account-number", data.AccountNumber, 188 "AWS account number to use") 189 rootCmd.Flags().StringVarP(&output, "output", "o", output, 190 "path to output the generated file, \"-\" for stderr") 191 192 exitIfError(rootCmd.Execute()) 193 } 194 195 func exitIfError(err error) { 196 if err == nil { 197 return 198 } 199 fmt.Fprintf(os.Stderr, "%v\n", err) 200 os.Exit(1) 201 }