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  }