github.com/hugorut/terraform@v1.1.3/src/backend/remote-state/pg/backend.go (about) 1 package pg 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 8 "github.com/hugorut/terraform/src/backend" 9 "github.com/hugorut/terraform/src/legacy/helper/schema" 10 "github.com/lib/pq" 11 ) 12 13 const ( 14 statesTableName = "states" 15 statesIndexName = "states_by_name" 16 ) 17 18 // New creates a new backend for Postgres remote state. 19 func New() backend.Backend { 20 s := &schema.Backend{ 21 Schema: map[string]*schema.Schema{ 22 "conn_str": { 23 Type: schema.TypeString, 24 Required: true, 25 Description: "Postgres connection string; a `postgres://` URL", 26 }, 27 28 "schema_name": { 29 Type: schema.TypeString, 30 Optional: true, 31 Description: "Name of the automatically managed Postgres schema to store state", 32 Default: "terraform_remote_state", 33 }, 34 35 "skip_schema_creation": { 36 Type: schema.TypeBool, 37 Optional: true, 38 Description: "If set to `true`, Terraform won't try to create the Postgres schema", 39 Default: false, 40 }, 41 42 "skip_table_creation": { 43 Type: schema.TypeBool, 44 Optional: true, 45 Description: "If set to `true`, Terraform won't try to create the Postgres table", 46 }, 47 48 "skip_index_creation": { 49 Type: schema.TypeBool, 50 Optional: true, 51 Description: "If set to `true`, Terraform won't try to create the Postgres index", 52 }, 53 }, 54 } 55 56 result := &Backend{Backend: s} 57 result.Backend.ConfigureFunc = result.configure 58 return result 59 } 60 61 type Backend struct { 62 *schema.Backend 63 64 // The fields below are set from configure 65 db *sql.DB 66 configData *schema.ResourceData 67 connStr string 68 schemaName string 69 } 70 71 func (b *Backend) configure(ctx context.Context) error { 72 // Grab the resource data 73 b.configData = schema.FromContextBackendConfig(ctx) 74 data := b.configData 75 76 b.connStr = data.Get("conn_str").(string) 77 b.schemaName = pq.QuoteIdentifier(data.Get("schema_name").(string)) 78 79 db, err := sql.Open("postgres", b.connStr) 80 if err != nil { 81 return err 82 } 83 84 // Prepare database schema, tables, & indexes. 85 var query string 86 87 if !data.Get("skip_schema_creation").(bool) { 88 // list all schemas to see if it exists 89 var count int 90 query = `select count(1) from information_schema.schemata where schema_name = $1` 91 if err := db.QueryRow(query, data.Get("schema_name").(string)).Scan(&count); err != nil { 92 return err 93 } 94 95 // skip schema creation if schema already exists 96 // `CREATE SCHEMA IF NOT EXISTS` is to be avoided if ever 97 // a user hasn't been granted the `CREATE SCHEMA` privilege 98 if count < 1 { 99 // tries to create the schema 100 query = `CREATE SCHEMA IF NOT EXISTS %s` 101 if _, err := db.Exec(fmt.Sprintf(query, b.schemaName)); err != nil { 102 return err 103 } 104 } 105 } 106 107 if !data.Get("skip_table_creation").(bool) { 108 if _, err := db.Exec("CREATE SEQUENCE IF NOT EXISTS public.global_states_id_seq AS bigint"); err != nil { 109 return err 110 } 111 112 query = `CREATE TABLE IF NOT EXISTS %s.%s ( 113 id bigint NOT NULL DEFAULT nextval('public.global_states_id_seq') PRIMARY KEY, 114 name text UNIQUE, 115 data text 116 )` 117 if _, err := db.Exec(fmt.Sprintf(query, b.schemaName, statesTableName)); err != nil { 118 return err 119 } 120 } 121 122 if !data.Get("skip_index_creation").(bool) { 123 query = `CREATE UNIQUE INDEX IF NOT EXISTS %s ON %s.%s (name)` 124 if _, err := db.Exec(fmt.Sprintf(query, statesIndexName, b.schemaName, statesTableName)); err != nil { 125 return err 126 } 127 } 128 129 // Assign db after its schema is prepared. 130 b.db = db 131 132 return nil 133 }