github.com/etecs-ru/gnomock@v0.13.2/preset/cockroachdb/preset.go (about)

     1  // Package cockroachdb includes CockroachDB implementation of Gnomock Preset interface.
     2  // This Preset can be passed to gnomock.Start() function to create a configured
     3  // CockroachDB container to use in tests.
     4  //
     5  // Containers created with this preset use `root` user without a password for
     6  // authentication. There is currently no way to setup an initial user at create
     7  // time.
     8  //
     9  // By default, a new database "mydb" is created, and all the provided queries
    10  // are executed against it.
    11  package cockroachdb
    12  
    13  import (
    14  	"context"
    15  	"database/sql"
    16  	"fmt"
    17  	"io/ioutil"
    18  
    19  	"github.com/etecs-ru/gnomock"
    20  	"github.com/etecs-ru/gnomock/internal/registry"
    21  	_ "github.com/lib/pq" // postgres driver
    22  )
    23  
    24  const (
    25  	defaultVersion  = "v20.1.10"
    26  	defaultPort     = 26257
    27  	defaultDatabase = "mydb"
    28  )
    29  
    30  func init() {
    31  	registry.Register("cockroachdb", func() gnomock.Preset { return &P{} })
    32  }
    33  
    34  // Preset creates a new Gmomock CockroachDB preset. This preset includes a CockroachDB
    35  // specific healthcheck function and default CockroachDB image and port.
    36  func Preset(opts ...Option) gnomock.Preset {
    37  	p := &P{}
    38  
    39  	for _, opt := range opts {
    40  		opt(p)
    41  	}
    42  
    43  	return p
    44  }
    45  
    46  // P is a Gnomock Preset implementation for CockroachDB.
    47  type P struct {
    48  	Version      string   `json:"version"`
    49  	DB           string   `json:"db"`
    50  	Queries      []string `json:"queries"`
    51  	QueriesFiles []string `json:"queries_files"`
    52  }
    53  
    54  // Image returns an image that should be pulled to create this container.
    55  func (p *P) Image() string {
    56  	return fmt.Sprintf("docker.io/cockroachdb/cockroach:%s", p.Version)
    57  }
    58  
    59  // Ports returns ports that should be used to access this container.
    60  func (p *P) Ports() gnomock.NamedPorts {
    61  	return gnomock.DefaultTCP(defaultPort)
    62  }
    63  
    64  // Options returns a list of options to configure this container.
    65  func (p *P) Options() []gnomock.Option {
    66  	p.setDefaults()
    67  
    68  	opts := []gnomock.Option{
    69  		gnomock.WithHealthCheck(healthcheck),
    70  		gnomock.WithCommand("start-single-node", "--insecure"),
    71  		gnomock.WithInit(p.initf()),
    72  	}
    73  
    74  	return opts
    75  }
    76  
    77  func (p *P) setDefaults() {
    78  	if p.Version == "" {
    79  		p.Version = defaultVersion
    80  	}
    81  
    82  	if p.DB == "" {
    83  		p.DB = defaultDatabase
    84  	}
    85  }
    86  
    87  func healthcheck(ctx context.Context, c *gnomock.Container) error {
    88  	db, err := connect(c, "")
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	defer func() {
    94  		_ = db.Close()
    95  	}()
    96  
    97  	var one int
    98  
    99  	return db.QueryRow(`select 1`).Scan(&one)
   100  }
   101  
   102  func (p *P) initf() gnomock.InitFunc {
   103  	return func(ctx context.Context, c *gnomock.Container) error {
   104  		db, err := connect(c, "")
   105  		if err != nil {
   106  			return err
   107  		}
   108  
   109  		_, err = db.Exec("create database " + p.DB)
   110  		if err != nil {
   111  			return err
   112  		}
   113  
   114  		_ = db.Close()
   115  
   116  		db, err = connect(c, p.DB)
   117  		if err != nil {
   118  			return err
   119  		}
   120  
   121  		defer func() { _ = db.Close() }()
   122  
   123  		if len(p.QueriesFiles) > 0 {
   124  			for _, f := range p.QueriesFiles {
   125  				bs, err := ioutil.ReadFile(f) // nolint:gosec
   126  				if err != nil {
   127  					return fmt.Errorf("can't read queries file '%s': %w", f, err)
   128  				}
   129  
   130  				p.Queries = append([]string{string(bs)}, p.Queries...)
   131  			}
   132  		}
   133  
   134  		for _, q := range p.Queries {
   135  			_, err = db.Exec(q)
   136  			if err != nil {
   137  				return err
   138  			}
   139  		}
   140  
   141  		return nil
   142  	}
   143  }
   144  
   145  func connect(c *gnomock.Container, db string) (*sql.DB, error) {
   146  	connStr := fmt.Sprintf(
   147  		"host=%s port=%d sslmode=disable user=root dbname=%s",
   148  		c.Host, c.Port(gnomock.DefaultPort), db,
   149  	)
   150  
   151  	conn, err := sql.Open("postgres", connStr)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	return conn, conn.Ping()
   157  }