<commit_before>package aws

import (
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/neptune"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsNeptuneClusterInstance() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsNeptuneClusterInstanceCreate,
		Read:   resourceAwsNeptuneClusterInstanceRead,
		Update: resourceAwsNeptuneClusterInstanceUpdate,
		Delete: resourceAwsNeptuneClusterInstanceDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(90 * time.Minute),
			Update: schema.DefaultTimeout(90 * time.Minute),
			Delete: schema.DefaultTimeout(90 * time.Minute),
		},

		Schema: map[string]*schema.Schema{

			"apply_immediately": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"availability_zone": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"identifier": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"identifier_prefix"},
				ValidateFunc:  validateNeptuneIdentifier,
			},

			"identifier_prefix": {
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ForceNew:     true,
				ValidateFunc: validateNeptuneIdentifier,
			},

			"neptune_subnet_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"writer": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"cluster_identifier": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"port": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  8182,
				ForceNew: true,
			},

			"publicly_accessible": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"instance_class": {
				Type:     schema.TypeString,
				Required: true,
			},

			"engine": {
				Type:         schema.TypeString,
				Optional:     true,
				Default:      "neptune",
				ForceNew:     true,
				ValidateFunc: validateNeptuneEngine(),
			},

			"engine_version": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"neptune_parameter_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "default.neptune1",
			},

			"kms_key_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"storage_encrypted": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"dbi_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"auto_minor_version_upgrade": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},

			"monitoring_role_arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"preferred_backup_window": {
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ValidateFunc: validateOnceADayWindowFormat,
			},

			"preferred_maintenance_window": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				StateFunc: func(val interface{}) string {
					if val == nil {
						return ""
					}
					return strings.ToLower(val.(string))
				},
				ValidateFunc: validateOnceAWeekWindowFormat,
			},

			"monitoring_interval": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
			},

			"promotion_tier": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsNeptuneClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).neptuneconn
	tags := tagsFromMapNeptune(d.Get("tags").(map[string]interface{}))

	createOpts := &neptune.CreateDBInstanceInput{
		DBInstanceClass:         aws.String(d.Get("instance_class").(string)),
		DBClusterIdentifier:     aws.String(d.Get("cluster_identifier").(string)),
		Engine:                  aws.String(d.Get("engine").(string)),
		PubliclyAccessible:      aws.Bool(d.Get("publicly_accessible").(bool)),
		PromotionTier:           aws.Int64(int64(d.Get("promotion_tier").(int))),
		AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)),
		Tags: tags,
	}

	if attr, ok := d.GetOk("availability_zone"); ok {
		createOpts.AvailabilityZone = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("neptune_parameter_group_name"); ok {
		createOpts.DBParameterGroupName = aws.String(attr.(string))
	}

	if v, ok := d.GetOk("identifier"); ok {
		createOpts.DBInstanceIdentifier = aws.String(v.(string))
	} else {
		if v, ok := d.GetOk("identifier_prefix"); ok {
			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string)))
		} else {
			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-"))
		}
	}

	if attr, ok := d.GetOk("neptune_subnet_group_name"); ok {
		createOpts.DBSubnetGroupName = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("engine_version"); ok {
		createOpts.EngineVersion = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("monitoring_role_arn"); ok {
		createOpts.MonitoringRoleArn = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("preferred_backup_window"); ok {
		createOpts.PreferredBackupWindow = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("preferred_maintenance_window"); ok {
		createOpts.PreferredMaintenanceWindow = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("monitoring_interval"); ok {
		createOpts.MonitoringInterval = aws.Int64(int64(attr.(int)))
	}

	log.Printf("[DEBUG] Creating Neptune Instance: %s", createOpts)

	var resp *neptune.CreateDBInstanceOutput
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = conn.CreateDBInstance(createOpts)
		if err != nil {
			if isAWSErr(err, "InvalidParameterValue", "IAM role ARN value is invalid or does not include the required permissions") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("error creating Neptune Instance: %s", err)
	}

	d.SetId(aws.StringValue(resp.DBInstance.DBInstanceIdentifier))

	stateConf := &resource.StateChangeConf{
		Pending:    resourceAwsNeptuneClusterInstanceCreateUpdatePendingStates,
		Target:     []string{"available"},
		Refresh:    resourceAwsNeptuneInstanceStateRefreshFunc(d.Id(), conn),
		Timeout:    d.Timeout(schema.TimeoutCreate),
		MinTimeout: 10 * time.Second,
		Delay:      30 * time.Second,
	}

	// Wait, catching any errors
	_, err = stateConf.WaitForState()
	if err != nil {
		return err
	}

	return resourceAwsNeptuneClusterInstanceRead(d, meta)
}

var resourceAwsNeptuneClusterInstanceCreateUpdatePendingStates = []string{
	"backing-up",
	"configuring-enhanced-monitoring",
	"creating",
	"maintenance",
	"modifying",
	"rebooting",
	"renaming",
	"starting",
	"upgrading",
}

func resourceAwsNeptuneInstanceStateRefreshFunc(id string, conn *neptune.Neptune) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		v, err := resourceAwsNeptuneInstanceRetrieve(id, conn)

		if err != nil {
			log.Printf("Error on retrieving Neptune Instance when waiting: %s", err)
			return nil, "", err
		}

		if v == nil {
			return nil, "", nil
		}

		if v.DBInstanceStatus != nil {
			log.Printf("[DEBUG] Neptune Instance status for instance %s: %s", id, aws.StringValue(v.DBInstanceStatus))
		}

		return v, aws.StringValue(v.DBInstanceStatus), nil
	}
}

func resourceAwsNeptuneInstanceRetrieve(id string, conn *neptune.Neptune) (*neptune.DBInstance, error) {
	opts := neptune.DescribeDBInstancesInput{
		DBInstanceIdentifier: aws.String(id),
	}

	log.Printf("[DEBUG] Neptune Instance describe configuration: %#v", opts)

	resp, err := conn.DescribeDBInstances(&opts)
	if err != nil {
		if isAWSErr(err, neptune.ErrCodeDBInstanceNotFoundFault, "") {
			return nil, nil
		}
		return nil, fmt.Errorf("Error retrieving Neptune Instances: %s", err)
	}

	if len(resp.DBInstances) != 1 ||
		aws.StringValue(resp.DBInstances[0].DBInstanceIdentifier) != id {
		if err != nil {
			return nil, nil
		}
	}

	return resp.DBInstances[0], nil
}
<commit_msg>add resource read func for neptune instance<commit_after>package aws

import (
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/neptune"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/validation"
)

func resourceAwsNeptuneClusterInstance() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsNeptuneClusterInstanceCreate,
		Read:   resourceAwsNeptuneClusterInstanceRead,
		Update: resourceAwsNeptuneClusterInstanceUpdate,
		Delete: resourceAwsNeptuneClusterInstanceDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Timeouts: &schema.ResourceTimeout{
			Create: schema.DefaultTimeout(90 * time.Minute),
			Update: schema.DefaultTimeout(90 * time.Minute),
			Delete: schema.DefaultTimeout(90 * time.Minute),
		},

		Schema: map[string]*schema.Schema{

			"apply_immediately": {
				Type:     schema.TypeBool,
				Optional: true,
				Computed: true,
			},

			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"availability_zone": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"identifier": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ForceNew:      true,
				ConflictsWith: []string{"identifier_prefix"},
				ValidateFunc:  validateNeptuneIdentifier,
			},

			"identifier_prefix": {
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ForceNew:     true,
				ValidateFunc: validateNeptuneIdentifier,
			},

			"neptune_subnet_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"writer": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"cluster_identifier": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"endpoint": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"port": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  8182,
				ForceNew: true,
			},

			"publicly_accessible": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  false,
			},

			"instance_class": {
				Type:     schema.TypeString,
				Required: true,
			},

			"engine": {
				Type:         schema.TypeString,
				Optional:     true,
				Default:      "neptune",
				ForceNew:     true,
				ValidateFunc: validateNeptuneEngine(),
			},

			"engine_version": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Computed: true,
			},

			"neptune_parameter_group_name": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "default.neptune1",
			},

			"kms_key_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"storage_encrypted": {
				Type:     schema.TypeBool,
				Computed: true,
			},

			"dbi_resource_id": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"auto_minor_version_upgrade": {
				Type:     schema.TypeBool,
				Optional: true,
				Default:  true,
			},

			"monitoring_role_arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"preferred_backup_window": {
				Type:         schema.TypeString,
				Optional:     true,
				Computed:     true,
				ValidateFunc: validateOnceADayWindowFormat,
			},

			"preferred_maintenance_window": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				StateFunc: func(val interface{}) string {
					if val == nil {
						return ""
					}
					return strings.ToLower(val.(string))
				},
				ValidateFunc: validateOnceAWeekWindowFormat,
			},

			"monitoring_interval": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
			},

			"promotion_tier": {
				Type:     schema.TypeInt,
				Optional: true,
				Default:  0,
			},

			"tags": tagsSchema(),
		},
	}
}

func resourceAwsNeptuneClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).neptuneconn
	tags := tagsFromMapNeptune(d.Get("tags").(map[string]interface{}))

	createOpts := &neptune.CreateDBInstanceInput{
		DBInstanceClass:         aws.String(d.Get("instance_class").(string)),
		DBClusterIdentifier:     aws.String(d.Get("cluster_identifier").(string)),
		Engine:                  aws.String(d.Get("engine").(string)),
		PubliclyAccessible:      aws.Bool(d.Get("publicly_accessible").(bool)),
		PromotionTier:           aws.Int64(int64(d.Get("promotion_tier").(int))),
		AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)),
		Tags: tags,
	}

	if attr, ok := d.GetOk("availability_zone"); ok {
		createOpts.AvailabilityZone = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("neptune_parameter_group_name"); ok {
		createOpts.DBParameterGroupName = aws.String(attr.(string))
	}

	if v, ok := d.GetOk("identifier"); ok {
		createOpts.DBInstanceIdentifier = aws.String(v.(string))
	} else {
		if v, ok := d.GetOk("identifier_prefix"); ok {
			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string)))
		} else {
			createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-"))
		}
	}

	if attr, ok := d.GetOk("neptune_subnet_group_name"); ok {
		createOpts.DBSubnetGroupName = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("engine_version"); ok {
		createOpts.EngineVersion = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("monitoring_role_arn"); ok {
		createOpts.MonitoringRoleArn = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("preferred_backup_window"); ok {
		createOpts.PreferredBackupWindow = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("preferred_maintenance_window"); ok {
		createOpts.PreferredMaintenanceWindow = aws.String(attr.(string))
	}

	if attr, ok := d.GetOk("monitoring_interval"); ok {
		createOpts.MonitoringInterval = aws.Int64(int64(attr.(int)))
	}

	log.Printf("[DEBUG] Creating Neptune Instance: %s", createOpts)

	var resp *neptune.CreateDBInstanceOutput
	err := resource.Retry(1*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = conn.CreateDBInstance(createOpts)
		if err != nil {
			if isAWSErr(err, "InvalidParameterValue", "IAM role ARN value is invalid or does not include the required permissions") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("error creating Neptune Instance: %s", err)
	}

	d.SetId(aws.StringValue(resp.DBInstance.DBInstanceIdentifier))

	stateConf := &resource.StateChangeConf{
		Pending:    resourceAwsNeptuneClusterInstanceCreateUpdatePendingStates,
		Target:     []string{"available"},
		Refresh:    resourceAwsNeptuneInstanceStateRefreshFunc(d.Id(), conn),
		Timeout:    d.Timeout(schema.TimeoutCreate),
		MinTimeout: 10 * time.Second,
		Delay:      30 * time.Second,
	}

	// Wait, catching any errors
	_, err = stateConf.WaitForState()
	if err != nil {
		return err
	}

	return resourceAwsNeptuneClusterInstanceRead(d, meta)
}

func resourceAwsNeptuneClusterInstanceRead(d *schema.ResourceData, meta interface{}) error {
	db, err := resourceAwsNeptuneInstanceRetrieve(d.Id(), meta.(*AWSClient).neptuneconn)
	if err != nil {
		return fmt.Errorf("Error on retrieving Neptune Cluster Instance (%s): %s", d.Id(), err)
	}

	if db == nil {
		log.Printf("[WARN] Neptune Cluster Instance (%s): not found, removing from state.", d.Id())
		d.SetId("")
		return nil
	}

	if db.DBClusterIdentifier == nil {
		return fmt.Errorf("Cluster identifier is missing from instance (%s)", d.Id())
	}

	conn := meta.(*AWSClient).neptuneconn
	resp, err := conn.DescribeDBClusters(&neptune.DescribeDBClustersInput{
		DBClusterIdentifier: db.DBClusterIdentifier,
	})

	var dbc *neptune.DBCluster
	for _, c := range resp.DBClusters {
		if aws.StringValue(c.DBClusterIdentifier) == aws.StringValue(db.DBClusterIdentifier) {
			dbc = c
		}
	}
	if dbc == nil {
		return fmt.Errorf("Error finding Neptune Cluster (%s) for Cluster Instance (%s): %s",
			aws.StringValue(db.DBClusterIdentifier), aws.StringValue(db.DBInstanceIdentifier), err)
	}
	for _, m := range dbc.DBClusterMembers {
		if aws.StringValue(db.DBInstanceIdentifier) == aws.StringValue(m.DBInstanceIdentifier) {
			if aws.BoolValue(m.IsClusterWriter) == true {
				d.Set("writer", true)
			} else {
				d.Set("writer", false)
			}
		}
	}

	if db.Endpoint != nil {
		d.Set("endpoint", db.Endpoint.Address)
		d.Set("port", db.Endpoint.Port)
	}

	if db.DBSubnetGroup != nil {
		d.Set("neptune_subnet_group_name", db.DBSubnetGroup.DBSubnetGroupName)
	}

	d.Set("arn", db.DBInstanceArn)
	d.Set("auto_minor_version_upgrade", db.AutoMinorVersionUpgrade)
	d.Set("availability_zone", db.AvailabilityZone)
	d.Set("cluster_identifier", db.DBClusterIdentifier)
	d.Set("dbi_resource_id", db.DbiResourceId)
	d.Set("engine_version", db.EngineVersion)
	d.Set("engine", db.Engine)
	d.Set("identifier", db.DBInstanceIdentifier)
	d.Set("instance_class", db.DBInstanceClass)
	d.Set("kms_key_arn", db.KmsKeyId)
	d.Set("preferred_backup_window", db.PreferredBackupWindow)
	d.Set("preferred_maintenance_window", db.PreferredMaintenanceWindow)
	d.Set("promotion_tier", db.PromotionTier)
	d.Set("publicly_accessible", db.PubliclyAccessible)
	d.Set("storage_encrypted", db.StorageEncrypted)

	if db.MonitoringInterval != nil {
		d.Set("monitoring_interval", db.MonitoringInterval)
	}

	if db.MonitoringRoleArn != nil {
		d.Set("monitoring_role_arn", db.MonitoringRoleArn)
	}

	if len(db.DBParameterGroups) > 0 {
		d.Set("neptune_parameter_group_name", db.DBParameterGroups[0].DBParameterGroupName)
	}

	if err := saveTagsNeptune(conn, d, aws.StringValue(db.DBInstanceArn)); err != nil {
		log.Printf("[WARN] Failed to save tags for Neptune Cluster Instance (%s): %s", aws.StringValue(db.DBInstanceIdentifier), err)
	}

	return nil
}

var resourceAwsNeptuneClusterInstanceCreateUpdatePendingStates = []string{
	"backing-up",
	"configuring-enhanced-monitoring",
	"creating",
	"maintenance",
	"modifying",
	"rebooting",
	"renaming",
	"starting",
	"upgrading",
}

func resourceAwsNeptuneInstanceStateRefreshFunc(id string, conn *neptune.Neptune) resource.StateRefreshFunc {
	return func() (interface{}, string, error) {
		v, err := resourceAwsNeptuneInstanceRetrieve(id, conn)

		if err != nil {
			log.Printf("Error on retrieving Neptune Instance when waiting: %s", err)
			return nil, "", err
		}

		if v == nil {
			return nil, "", nil
		}

		if v.DBInstanceStatus != nil {
			log.Printf("[DEBUG] Neptune Instance status for instance %s: %s", id, aws.StringValue(v.DBInstanceStatus))
		}

		return v, aws.StringValue(v.DBInstanceStatus), nil
	}
}

func resourceAwsNeptuneInstanceRetrieve(id string, conn *neptune.Neptune) (*neptune.DBInstance, error) {
	opts := neptune.DescribeDBInstancesInput{
		DBInstanceIdentifier: aws.String(id),
	}

	log.Printf("[DEBUG] Neptune Instance describe configuration: %#v", opts)

	resp, err := conn.DescribeDBInstances(&opts)
	if err != nil {
		if isAWSErr(err, neptune.ErrCodeDBInstanceNotFoundFault, "") {
			return nil, nil
		}
		return nil, fmt.Errorf("Error retrieving Neptune Instances: %s", err)
	}

	if len(resp.DBInstances) != 1 ||
		aws.StringValue(resp.DBInstances[0].DBInstanceIdentifier) != id {
		if err != nil {
			return nil, nil
		}
	}

	return resp.DBInstances[0], nil
}
<|endoftext|>


<commit_before>package rwdb

import (
	"database/sql"
	"errors"
	"sync"
)

var cplock sync.RWMutex

// CPool holds the  master and slave connection pools
type CPool struct {
	pool []*sql.DB
	next int
}

func (c *CPool) nextInPool() int {
	if len(c.pool) == 0 {
		return -1
	}

	c.next = (c.next + 1) % len(c.pool)

	return c.next
}

// AddReader append a db connection to the pool
func (c *CPool) AddReader(db *sql.DB) {
	cplock.RLock()

	if len(c.pool) > 1 {
		for i, po := range c.pool[1:] {
			if po == nil {
				c.pool[i+1] = db
				return
			}
		}
	}

	c.pool = append(c.pool, db)
	cplock.RUnlock()
}

// AddWriter prepend a db connection to the pool
// Previous writer automatically become a reader
func (c *CPool) AddWriter(db *sql.DB) {
	c.pool = append([]*sql.DB{db}, c.pool...)
}

// Reader gets the reader connection next in line
func (c *CPool) Reader() (*sql.DB, error) {
	pos := c.nextInPool()

	if pos < 0 {
		return nil, errors.New("no reader db available")
	}

	cplock.RLock()

	conn := c.pool[pos]

	var count int

	for conn == nil {
		if count = c.nextInPool(); count == pos {
			return nil, errors.New("no reader db available")
		}

		conn = c.pool[count]
	}

	cplock.RUnlock()

	return conn, nil
}

// Writer gets the writer connection
func (c *CPool) Writer() (*sql.DB, error) {
	if len(c.pool) == 0 {
		return nil, errors.New("no writer db available")
	}

	db := c.pool[0]

	if db == nil {
		return nil, errors.New("no writer db available")
	}

	return db, nil
}
<commit_msg>introduce sync atomic to add synchronization on the counter tweak the lock<commit_after>package rwdb

import (
	"database/sql"
	"errors"
	"sync"
	"sync/atomic"
)

// CPool holds the  master and slave connection pools
type CPool struct {
	pool []*sql.DB
	next uint64
	lock sync.RWMutex
}

func (c *CPool) nextInPool() int {
	if len(c.pool) == 0 {
		return -1
	}

	c.next = atomic.AddUint64(&c.next, 1) % uint64(len(c.pool))
	return int(c.next)
}

// AddReader append a db connection to the pool
func (c *CPool) AddReader(db *sql.DB) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	if len(c.pool) > 1 {
		for i, po := range c.pool[1:] {
			if po == nil {
				c.pool[i+1] = db
				return
			}
		}
	}

	c.pool = append(c.pool, db)
}

// AddWriter prepend a db connection to the pool
// Previous writer automatically become a reader
func (c *CPool) AddWriter(db *sql.DB) {
	c.lock.Lock()
	c.pool = append([]*sql.DB{db}, c.pool...)
	c.lock.Unlock()
}

// Reader gets the reader connection next in line
func (c *CPool) Reader() (*sql.DB, error) {
	pos := c.nextInPool()

	if pos < 0 {
		return nil, errors.New("no reader db available")
	}

	c.lock.RLock()
	defer c.lock.RUnlock()

	conn := c.pool[pos]

	var count int

	for conn == nil {
		if count = c.nextInPool(); count == pos {
			return nil, errors.New("no reader db available")
		}

		conn = c.pool[count]
	}

	return conn, nil
}

// Writer gets the writer connection
func (c *CPool) Writer() (*sql.DB, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	if len(c.pool) == 0 {
		return nil, errors.New("no writer db available")
	}

	db := c.pool[0]

	if db == nil {
		return nil, errors.New("no writer db available")
	}

	return db, nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"net/http"
)

func commentList(commenterHex string, domain string, path string, includeUnapproved bool) ([]comment, map[string]commenter, error) {
	// path can be empty
	if commenterHex == "" || domain == "" {
		return nil, nil, errorMissingField
	}

	statement := `
		SELECT
			commentHex,
			commenterHex,
			markdown,
			html,
			parentHex,
			score,
			state,
			creationDate
		FROM comments
		WHERE
			comments.domain = $1 AND
			comments.path = $2
	`

	if !includeUnapproved {
		if commenterHex == "anonymous" {
			statement += `AND state = 'approved'`
		} else {
			statement += `AND (state = 'approved' OR commenterHex = $3)`
		}
	}

	statement += `;`

	var rows *sql.Rows
	var err error

	if !includeUnapproved && commenterHex != "anonymous" {
		rows, err = db.Query(statement, domain, path, commenterHex)
	} else {
		rows, err = db.Query(statement, domain, path)
	}

	if err != nil {
		logger.Errorf("cannot get comments: %v", err)
		return nil, nil, errorInternal
	}
	defer rows.Close()

	commenters := make(map[string]commenter)
	commenters["anonymous"] = commenter{CommenterHex: "anonymous", Email: "undefined", Name: "Anonymous", Link: "undefined", Photo: "undefined", Provider: "undefined"}

	comments := []comment{}
	for rows.Next() {
		c := comment{}
		if err = rows.Scan(
			&c.CommentHex,
			&c.CommenterHex,
			&c.Markdown,
			&c.Html,
			&c.ParentHex,
			&c.Score,
			&c.State,
			&c.CreationDate); err != nil {
			return nil, nil, errorInternal
		}

		if commenterHex != "anonymous" {
			statement = `
				SELECT direction
				FROM votes
				WHERE commentHex=$1 AND commenterHex=$2;
			`
			row := db.QueryRow(statement, c.CommentHex, commenterHex)

			if err = row.Scan(&c.Direction); err != nil {
				// TODO: is the only error here that there is no such entry?
				c.Direction = 0
			}
		}

		if !includeUnapproved {
			c.State = ""
		}

		comments = append(comments, c)

		if _, ok := commenters[c.CommenterHex]; !ok {
			commenters[c.CommenterHex], err = commenterGetByHex(c.CommenterHex)
			if err != nil {
				logger.Errorf("cannot retrieve commenter: %v", err)
				return nil, nil, errorInternal
			}
		}
	}

	return comments, commenters, nil
}

func commentListHandler(w http.ResponseWriter, r *http.Request) {
	type request struct {
		CommenterToken *string `json:"CommenterToken"`
		Domain         *string `json:"domain"`
		Path           *string `json:"path"`
	}

	var x request
	if err := bodyUnmarshal(r, &x); err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	domain := domainStrip(*x.Domain)
	path := *x.Path

	d, err := domainGet(domain)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	p, err := pageGet(domain, path)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	commenterHex := "anonymous"
	c, err := commenterGetByCommenterToken(*x.CommenterToken)
	if err != nil {
		if err == errorNoSuchToken {
			commenterHex = "anonymous"
		} else {
			bodyMarshal(w, response{"success": false, "message": err.Error()})
			return
		}
	} else {
		commenterHex = c.CommenterHex
	}

	isModerator := false
	modList := map[string]bool{}
	for _, mod := range d.Moderators {
		modList[mod.Email] = true
		if mod.Email == c.Email {
			isModerator = true
		}
	}

	domainViewRecord(domain, commenterHex)

	comments, commenters, err := commentList(commenterHex, domain, path, isModerator)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	_commenters := map[string]commenter{}
	for commenterHex, cr := range commenters {
		if _, ok := modList[cr.Email]; ok {
			cr.IsModerator = true
		}
		cr.Email = ""
		_commenters[commenterHex] = cr
	}

	bodyMarshal(w, response{
		"success":               true,
		"domain":                domain,
		"comments":              comments,
		"commenters":            _commenters,
		"requireModeration":     d.RequireModeration,
		"requireIdentification": d.RequireIdentification,
		"isFrozen":              d.State == "frozen",
		"isModerator":           isModerator,
		"attributes":            p,
		"configuredOauths": map[string]bool{
			"commento": d.CommentoProvider,
			"google":   googleConfigured && d.GoogleProvider,
			"twitter":  twitterConfigured && d.TwitterProvider,
			"github":   githubConfigured && d.GithubProvider,
			"gitlab":   gitlabConfigured && d.GitlabProvider,
			"sso":      d.SsoProvider,
		},
	})
}
<commit_msg>comment_list.go: check for moderator status conditionally<commit_after>package main

import (
	"database/sql"
	"net/http"
)

func commentList(commenterHex string, domain string, path string, includeUnapproved bool) ([]comment, map[string]commenter, error) {
	// path can be empty
	if commenterHex == "" || domain == "" {
		return nil, nil, errorMissingField
	}

	statement := `
		SELECT
			commentHex,
			commenterHex,
			markdown,
			html,
			parentHex,
			score,
			state,
			creationDate
		FROM comments
		WHERE
			comments.domain = $1 AND
			comments.path = $2
	`

	if !includeUnapproved {
		if commenterHex == "anonymous" {
			statement += `AND state = 'approved'`
		} else {
			statement += `AND (state = 'approved' OR commenterHex = $3)`
		}
	}

	statement += `;`

	var rows *sql.Rows
	var err error

	if !includeUnapproved && commenterHex != "anonymous" {
		rows, err = db.Query(statement, domain, path, commenterHex)
	} else {
		rows, err = db.Query(statement, domain, path)
	}

	if err != nil {
		logger.Errorf("cannot get comments: %v", err)
		return nil, nil, errorInternal
	}
	defer rows.Close()

	commenters := make(map[string]commenter)
	commenters["anonymous"] = commenter{CommenterHex: "anonymous", Email: "undefined", Name: "Anonymous", Link: "undefined", Photo: "undefined", Provider: "undefined"}

	comments := []comment{}
	for rows.Next() {
		c := comment{}
		if err = rows.Scan(
			&c.CommentHex,
			&c.CommenterHex,
			&c.Markdown,
			&c.Html,
			&c.ParentHex,
			&c.Score,
			&c.State,
			&c.CreationDate); err != nil {
			return nil, nil, errorInternal
		}

		if commenterHex != "anonymous" {
			statement = `
				SELECT direction
				FROM votes
				WHERE commentHex=$1 AND commenterHex=$2;
			`
			row := db.QueryRow(statement, c.CommentHex, commenterHex)

			if err = row.Scan(&c.Direction); err != nil {
				// TODO: is the only error here that there is no such entry?
				c.Direction = 0
			}
		}

		if !includeUnapproved {
			c.State = ""
		}

		comments = append(comments, c)

		if _, ok := commenters[c.CommenterHex]; !ok {
			commenters[c.CommenterHex], err = commenterGetByHex(c.CommenterHex)
			if err != nil {
				logger.Errorf("cannot retrieve commenter: %v", err)
				return nil, nil, errorInternal
			}
		}
	}

	return comments, commenters, nil
}

func commentListHandler(w http.ResponseWriter, r *http.Request) {
	type request struct {
		CommenterToken *string `json:"CommenterToken"`
		Domain         *string `json:"domain"`
		Path           *string `json:"path"`
	}

	var x request
	if err := bodyUnmarshal(r, &x); err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	domain := domainStrip(*x.Domain)
	path := *x.Path

	d, err := domainGet(domain)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	p, err := pageGet(domain, path)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	commenterHex := "anonymous"
	isModerator := false
	modList := map[string]bool{}

	if *x.CommenterToken != "anonymous" {
		c, err := commenterGetByCommenterToken(*x.CommenterToken)
		if err != nil {
			if err == errorNoSuchToken {
				commenterHex = "anonymous"
			} else {
				bodyMarshal(w, response{"success": false, "message": err.Error()})
				return
			}
		} else {
			commenterHex = c.CommenterHex
		}

		for _, mod := range d.Moderators {
			modList[mod.Email] = true
			if mod.Email == c.Email {
				isModerator = true
			}
		}
	} else {
		for _, mod := range d.Moderators {
			modList[mod.Email] = true
		}
	}

	domainViewRecord(domain, commenterHex)

	comments, commenters, err := commentList(commenterHex, domain, path, isModerator)
	if err != nil {
		bodyMarshal(w, response{"success": false, "message": err.Error()})
		return
	}

	_commenters := map[string]commenter{}
	for commenterHex, cr := range commenters {
		if _, ok := modList[cr.Email]; ok {
			cr.IsModerator = true
		}
		cr.Email = ""
		_commenters[commenterHex] = cr
	}

	bodyMarshal(w, response{
		"success":               true,
		"domain":                domain,
		"comments":              comments,
		"commenters":            _commenters,
		"requireModeration":     d.RequireModeration,
		"requireIdentification": d.RequireIdentification,
		"isFrozen":              d.State == "frozen",
		"isModerator":           isModerator,
		"attributes":            p,
		"configuredOauths": map[string]bool{
			"commento": d.CommentoProvider,
			"google":   googleConfigured && d.GoogleProvider,
			"twitter":  twitterConfigured && d.TwitterProvider,
			"github":   githubConfigured && d.GithubProvider,
			"gitlab":   gitlabConfigured && d.GitlabProvider,
			"sso":      d.SsoProvider,
		},
	})
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package launch

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/common/plugins"
	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/endpointmanager"
	healthPkg "github.com/cilium/cilium/pkg/health/client"
	"github.com/cilium/cilium/pkg/health/defaults"
	"github.com/cilium/cilium/pkg/k8s"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/mtu"
	"github.com/cilium/cilium/pkg/node"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/pidfile"

	"github.com/vishvananda/netlink"
)

var (
	// vethName is the host-side link device name for cilium-health EP.
	vethName = "cilium_health"

	// vethPeerName is the endpoint-side link device name for cilium-health.
	vethPeerName = "cilium"

	// healthPidfile
	healthPidfile = "health-endpoint.pid"

	// client is used to ping the cilium-health endpoint as a health check.
	client *healthPkg.Client
)

func logFromCommand(cmd *exec.Cmd, netns string) error {
	scopedLog := log.WithField("netns", netns)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	go func() {
		in := bufio.NewScanner(stdout)
		for in.Scan() {
			scopedLog.Debugf("%s", in.Text())
		}
	}()
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	go func() {
		in := bufio.NewScanner(stderr)
		for in.Scan() {
			scopedLog.Infof("%s", in.Text())
		}
	}()

	return nil
}

func configureHealthRouting(netns, dev string, addressing *models.NodeAddressing) error {
	routes := []plugins.Route{}
	v4Routes, err := plugins.IPv4Routes(addressing, mtu.StandardMTU)
	if err == nil {
		routes = append(routes, v4Routes...)
	} else {
		log.Debugf("Couldn't get IPv4 routes for health routing")
	}
	v6Routes, err := plugins.IPv6Routes(addressing, mtu.StandardMTU)
	if err != nil {
		return fmt.Errorf("Failed to get IPv6 routes")
	}
	routes = append(routes, v6Routes...)

	prog := "ip"
	args := []string{"netns", "exec", netns, "bash", "-c"}
	routeCmds := []string{}
	for _, rt := range routes {
		cmd := strings.Join(rt.ToIPCommand(dev), " ")
		log.WithField("netns", netns).WithField("command", cmd).Info("Adding route")
		routeCmds = append(routeCmds, cmd)
	}
	cmd := strings.Join(routeCmds, " && ")
	args = append(args, cmd)

	log.Debugf("Running \"%s %+v\"", prog, args)
	out, err := exec.Command(prog, args...).CombinedOutput()
	if err == nil && len(out) > 0 {
		log.Warn(out)
	}

	return err
}

// PingEndpoint attempts to make an API ping request to the local cilium-health
// endpoint, and returns whether this was successful.
func PingEndpoint() error {
	if client == nil {
		return fmt.Errorf("cilium-health endpoint hasn't yet been initialized")
	}
	_, err := client.Restapi.GetHello(nil)
	return err
}

// CleanupEndpoint attempts to kill any existing cilium-health endpoint and
// clean up its devices and pidfiles.
func CleanupEndpoint(owner endpoint.Owner) {
	path := filepath.Join(option.Config.StateDir, healthPidfile)
	if err := pidfile.Kill(path); err != nil {
		scopedLog := log.WithField(logfields.Path, path).WithError(err)
		scopedLog.Info("Failed to kill previous cilium-health instance")
	}

	scopedLog := log.WithField(logfields.Veth, vethName)
	if link, err := netlink.LinkByName(vethName); err == nil {
		err = netlink.LinkDel(link)
		if err != nil {
			scopedLog.WithError(err).Info("Couldn't delete cilium-health device")
		}
	} else {
		scopedLog.WithError(err).Debug("Didn't find existing device")
	}
}

// LaunchAsEndpoint launches the cilium-health agent in a nested network
// namespace and attaches it to Cilium the same way as any other endpoint,
// but with special reserved labels.
//
// CleanupEndpoint() must be called before calling LaunchAsEndpoint() to ensure
// cleanup of prior cilium-health endpoint instances.
func LaunchAsEndpoint(owner endpoint.Owner, hostAddressing *models.NodeAddressing) error {

	ip4 := node.GetIPv4HealthIP()
	ip6 := node.GetIPv6HealthIP()

	// Prepare the endpoint change request
	id := int64(addressing.CiliumIPv6(ip6).EndpointID())
	info := &models.EndpointChangeRequest{
		ID:            id,
		ContainerID:   endpointid.NewCiliumID(id),
		ContainerName: "cilium-health",
		State:         models.EndpointStateWaitingForIdentity,
		Addressing: &models.AddressPair{
			IPV6: ip6.String(),
			IPV4: ip4.String(),
		},
	}

	if _, _, err := plugins.SetupVethWithNames(vethName, vethPeerName, mtu.StandardMTU, info); err != nil {
		return fmt.Errorf("Error while creating veth: %s", err)
	}

	pidfile := filepath.Join(option.Config.StateDir, healthPidfile)
	healthArgs := fmt.Sprintf("-d --admin=unix --passive --pidfile %s", pidfile)
	args := []string{info.ContainerName, info.InterfaceName, vethPeerName,
		ip6.String(), ip4.String(), "cilium-health", healthArgs}
	prog := filepath.Join(owner.GetBpfDir(), "spawn_netns.sh")

	cmd := exec.CommandContext(context.Background(), prog, args...)
	if err := logFromCommand(cmd, info.ContainerName); err != nil {
		return fmt.Errorf("Error while opening pipes to health endpoint: %s", err)
	}
	if err := cmd.Start(); err != nil {
		target := fmt.Sprintf("%s %s", prog, strings.Join(args, " "))
		return fmt.Errorf("Error spawning endpoint (%q): %s", target, err)
	}

	// Create the endpoint
	ep, err := endpoint.NewEndpointFromChangeModel(info)
	if err != nil {
		return fmt.Errorf("Error while creating endpoint model: %s", err)
	}
	ep.SetDefaultOpts(option.Config.Opts)

	// Give the endpoint a security identity
	lbls := labels.Labels{labels.IDNameHealth: labels.NewLabel(labels.IDNameHealth, "", labels.LabelSourceReserved)}
	ep.SetIdentityLabels(owner, lbls)

	// Wait until the cilium-health endpoint is running before setting up routes
	deadline := time.Now().Add(1 * time.Minute)
	for {
		if _, err := os.Stat(pidfile); err == nil {
			log.WithField("pidfile", pidfile).Debug("cilium-health agent running")
			break
		} else if time.Now().After(deadline) {
			return fmt.Errorf("Endpoint failed to run: %s", err)
		} else {
			time.Sleep(1 * time.Second)
		}
	}

	// Set up the endpoint routes
	if err = configureHealthRouting(info.ContainerName, vethPeerName, hostAddressing); err != nil {
		return fmt.Errorf("Error while configuring routes: %s", err)
	}

	// Add the endpoint
	if err := endpointmanager.AddEndpoint(owner, ep, "Create cilium-health endpoint"); err != nil {
		return fmt.Errorf("Error while adding endpoint: %s", err)
	}

	// Propagate health IPs to all other nodes
	if k8s.IsEnabled() {
		err := k8s.AnnotateNode(k8s.Client(), node.GetName(), nil, nil, ip4, ip6)
		if err != nil {
			return fmt.Errorf("Cannot annotate node CIDR range data: %s", err)
		}
	}

	client, err = healthPkg.NewClient(fmt.Sprintf("tcp://%s:%d", ip4, defaults.HTTPPathPort))
	if err != nil {
		return fmt.Errorf("Cannot establish connection to health endpoint: %s", err)
	}

	return nil
}
<commit_msg>health-ep: Document threadsafety<commit_after>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package launch

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/common/plugins"
	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/endpointmanager"
	healthPkg "github.com/cilium/cilium/pkg/health/client"
	"github.com/cilium/cilium/pkg/health/defaults"
	"github.com/cilium/cilium/pkg/k8s"
	"github.com/cilium/cilium/pkg/labels"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/mtu"
	"github.com/cilium/cilium/pkg/node"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/pidfile"

	"github.com/vishvananda/netlink"
)

var (
	// vethName is the host-side link device name for cilium-health EP.
	vethName = "cilium_health"

	// vethPeerName is the endpoint-side link device name for cilium-health.
	vethPeerName = "cilium"

	// healthPidfile
	healthPidfile = "health-endpoint.pid"

	// client is used to ping the cilium-health endpoint as a health check.
	client *healthPkg.Client
)

func logFromCommand(cmd *exec.Cmd, netns string) error {
	scopedLog := log.WithField("netns", netns)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	go func() {
		in := bufio.NewScanner(stdout)
		for in.Scan() {
			scopedLog.Debugf("%s", in.Text())
		}
	}()
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}
	go func() {
		in := bufio.NewScanner(stderr)
		for in.Scan() {
			scopedLog.Infof("%s", in.Text())
		}
	}()

	return nil
}

func configureHealthRouting(netns, dev string, addressing *models.NodeAddressing) error {
	routes := []plugins.Route{}
	v4Routes, err := plugins.IPv4Routes(addressing, mtu.StandardMTU)
	if err == nil {
		routes = append(routes, v4Routes...)
	} else {
		log.Debugf("Couldn't get IPv4 routes for health routing")
	}
	v6Routes, err := plugins.IPv6Routes(addressing, mtu.StandardMTU)
	if err != nil {
		return fmt.Errorf("Failed to get IPv6 routes")
	}
	routes = append(routes, v6Routes...)

	prog := "ip"
	args := []string{"netns", "exec", netns, "bash", "-c"}
	routeCmds := []string{}
	for _, rt := range routes {
		cmd := strings.Join(rt.ToIPCommand(dev), " ")
		log.WithField("netns", netns).WithField("command", cmd).Info("Adding route")
		routeCmds = append(routeCmds, cmd)
	}
	cmd := strings.Join(routeCmds, " && ")
	args = append(args, cmd)

	log.Debugf("Running \"%s %+v\"", prog, args)
	out, err := exec.Command(prog, args...).CombinedOutput()
	if err == nil && len(out) > 0 {
		log.Warn(out)
	}

	return err
}

// PingEndpoint attempts to make an API ping request to the local cilium-health
// endpoint, and returns whether this was successful.
//
// This function must only be used from the same goroutine as LaunchAsEndpoint().
// It is safe to call PingEndpoint() before LaunchAsEndpoint() so long as the
// goroutine is the same for both calls.
func PingEndpoint() error {
	// client is shared with LaunchAsEndpoint().
	if client == nil {
		return fmt.Errorf("cilium-health endpoint hasn't yet been initialized")
	}
	_, err := client.Restapi.GetHello(nil)
	return err
}

// CleanupEndpoint attempts to kill any existing cilium-health endpoint and
// clean up its devices and pidfiles. If any existing cilium-health endpoint
// exists in Cilium, it is removed from the endpoint manager.
//
// This is intended to be invoked in multiple situations:
// * The health endpoint has never been run before
// * The health endpoint was run during a previous run of the Cilium agent
// * The health endpoint crashed during the current run of the Cilium agent
//   and needs to be cleaned up before it is restarted.
func CleanupEndpoint(owner endpoint.Owner) {
	path := filepath.Join(option.Config.StateDir, healthPidfile)
	if err := pidfile.Kill(path); err != nil {
		scopedLog := log.WithField(logfields.Path, path).WithError(err)
		scopedLog.Info("Failed to kill previous cilium-health instance")
	}

	scopedLog := log.WithField(logfields.Veth, vethName)
	if link, err := netlink.LinkByName(vethName); err == nil {
		err = netlink.LinkDel(link)
		if err != nil {
			scopedLog.WithError(err).Info("Couldn't delete cilium-health device")
		}
	} else {
		scopedLog.WithError(err).Debug("Didn't find existing device")
	}
}

// LaunchAsEndpoint launches the cilium-health agent in a nested network
// namespace and attaches it to Cilium the same way as any other endpoint,
// but with special reserved labels.
//
// CleanupEndpoint() must be called before calling LaunchAsEndpoint() to ensure
// cleanup of prior cilium-health endpoint instances.
func LaunchAsEndpoint(owner endpoint.Owner, hostAddressing *models.NodeAddressing) error {

	ip4 := node.GetIPv4HealthIP()
	ip6 := node.GetIPv6HealthIP()

	// Prepare the endpoint change request
	id := int64(addressing.CiliumIPv6(ip6).EndpointID())
	info := &models.EndpointChangeRequest{
		ID:            id,
		ContainerID:   endpointid.NewCiliumID(id),
		ContainerName: "cilium-health",
		State:         models.EndpointStateWaitingForIdentity,
		Addressing: &models.AddressPair{
			IPV6: ip6.String(),
			IPV4: ip4.String(),
		},
	}

	if _, _, err := plugins.SetupVethWithNames(vethName, vethPeerName, mtu.StandardMTU, info); err != nil {
		return fmt.Errorf("Error while creating veth: %s", err)
	}

	pidfile := filepath.Join(option.Config.StateDir, healthPidfile)
	healthArgs := fmt.Sprintf("-d --admin=unix --passive --pidfile %s", pidfile)
	args := []string{info.ContainerName, info.InterfaceName, vethPeerName,
		ip6.String(), ip4.String(), "cilium-health", healthArgs}
	prog := filepath.Join(owner.GetBpfDir(), "spawn_netns.sh")

	cmd := exec.CommandContext(context.Background(), prog, args...)
	if err := logFromCommand(cmd, info.ContainerName); err != nil {
		return fmt.Errorf("Error while opening pipes to health endpoint: %s", err)
	}
	if err := cmd.Start(); err != nil {
		target := fmt.Sprintf("%s %s", prog, strings.Join(args, " "))
		return fmt.Errorf("Error spawning endpoint (%q): %s", target, err)
	}

	// Create the endpoint
	ep, err := endpoint.NewEndpointFromChangeModel(info)
	if err != nil {
		return fmt.Errorf("Error while creating endpoint model: %s", err)
	}
	ep.SetDefaultOpts(option.Config.Opts)

	// Give the endpoint a security identity
	lbls := labels.Labels{labels.IDNameHealth: labels.NewLabel(labels.IDNameHealth, "", labels.LabelSourceReserved)}
	ep.SetIdentityLabels(owner, lbls)

	// Wait until the cilium-health endpoint is running before setting up routes
	deadline := time.Now().Add(1 * time.Minute)
	for {
		if _, err := os.Stat(pidfile); err == nil {
			log.WithField("pidfile", pidfile).Debug("cilium-health agent running")
			break
		} else if time.Now().After(deadline) {
			return fmt.Errorf("Endpoint failed to run: %s", err)
		} else {
			time.Sleep(1 * time.Second)
		}
	}

	// Set up the endpoint routes
	if err = configureHealthRouting(info.ContainerName, vethPeerName, hostAddressing); err != nil {
		return fmt.Errorf("Error while configuring routes: %s", err)
	}

	// Add the endpoint
	if err := endpointmanager.AddEndpoint(owner, ep, "Create cilium-health endpoint"); err != nil {
		return fmt.Errorf("Error while adding endpoint: %s", err)
	}

	// Propagate health IPs to all other nodes
	if k8s.IsEnabled() {
		err := k8s.AnnotateNode(k8s.Client(), node.GetName(), nil, nil, ip4, ip6)
		if err != nil {
			return fmt.Errorf("Cannot annotate node CIDR range data: %s", err)
		}
	}

	// Initialize the health client to talk to this instance. This is why
	// the caller must limit usage of this package to a single goroutine.
	client, err = healthPkg.NewClient(fmt.Sprintf("tcp://%s:%d", ip4, defaults.HTTPPathPort))
	if err != nil {
		return fmt.Errorf("Cannot establish connection to health endpoint: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Paul Roy All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package azurestoragecache provides an implementation of httpcache.Cache that
// stores and retrieves data using Azure Storage.
package azurestoragecache // import "github.com/PaulARoy/azurestoragecache"

import (
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"bytes"

	vendorstorage "github.com/Azure/azure-sdk-for-go/storage"
)

// Cache objects store and retrieve data using Azure Storage
type Cache struct {
	// Our configuration for Azure Storage
	Config Config

	// The Azure Blob Storage Client
	Client vendorstorage.BlobStorageClient
}

type Config struct {
	// Account configuration for Azure Storage
	AccountName string
	AccountKey string

	// Container name to use to store blob
	ContainerName string
}

var noLogErrors, _ = strconv.ParseBool(os.Getenv("NO_LOG_AZUREBSCACHE_ERRORS"))

func (c *Cache) Get(key string) (resp []byte, ok bool) {
	rdr, err := c.Client.GetBlob(c.Config.ContainerName, key)
	if err != nil {
		return []byte{}, false
	}

	resp, err = ioutil.ReadAll(rdr)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Get failed: %s", err)
		}
	}

	rdr.Close()
	return resp, err == nil
}

func (c *Cache) Set(key string, block []byte) {
	err := c.Client.CreateBlockBlobFromReader(c.Config.ContainerName,
												key,
												uint64(len(block)),
												bytes.NewReader(block),
												nil)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Set failed: %s", err)
		}
		return
	}
}

func (c *Cache) Delete(key string) bool {
	res, err := c.Client.DeleteBlobIfExists(c.Config.ContainerName, key, nil)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Delete failed: %s", err)
		}
		return false
	}
	return res
}

// New returns a new Cache with underlying client for Azure Storage
//
// accountName is the Azure Storage Account Name (part of credentials)
// accountKey is the Azure Storage Account Key (part of credentials)
// containerName is the container name in which images will be stored (/!\ LOWER CASE)
//
// The environment variables AZURESTORAGE_ACCOUNT_NAME and AZURESTORAGE_ACCESS_KEY
// are used as credentials if nothing is provided.
func New(accountName string, accountKey string, containerName string) (*Cache, bool, error) {
	accName := accountName
	accKey := accountKey
	contName := containerName

	if (len(accName) <= 0) {
		accName = os.Getenv("AZURESTORAGE_ACCOUNT_NAME")
	}

	if (len(accKey) <= 0) {
		accKey = os.Getenv("AZURESTORAGE_ACCESS_KEY")
	}

	if (len(contName) <= 0) {
		contName = "cache"
	}

	cache := Cache{
		Config: Config{
			AccountName: accName,
			AccountKey: accKey,
			ContainerName: contName,
		},
	}

	api, err := vendorstorage.NewBasicClient(cache.Config.AccountName, cache.Config.AccountKey)
	if err != nil {
		return nil, false, err
	}

	cache.Client = api.GetBlobService()

	res, err := cache.Client.CreateContainerIfNotExists(cache.Config.ContainerName,
											vendorstorage.ContainerAccessTypeBlob)
	if err != nil {
		return nil, false, err
	}

	return &cache, res, nil
}<commit_msg>fixing spacing for github<commit_after>// Copyright 2017 Paul Roy All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package azurestoragecache provides an implementation of httpcache.Cache that
// stores and retrieves data using Azure Storage.
package azurestoragecache // import "github.com/PaulARoy/azurestoragecache"

import (
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"bytes"

	vendorstorage "github.com/Azure/azure-sdk-for-go/storage"
)

// Cache objects store and retrieve data using Azure Storage
type Cache struct {
	// Our configuration for Azure Storage
	Config Config

	// The Azure Blob Storage Client
	Client vendorstorage.BlobStorageClient
}

type Config struct {
	// Account configuration for Azure Storage
	AccountName string
	AccountKey string

	// Container name to use to store blob
	ContainerName string
}

var noLogErrors, _ = strconv.ParseBool(os.Getenv("NO_LOG_AZUREBSCACHE_ERRORS"))

func (c *Cache) Get(key string) (resp []byte, ok bool) {
	rdr, err := c.Client.GetBlob(c.Config.ContainerName, key)
	if err != nil {
		return []byte{}, false
	}

	resp, err = ioutil.ReadAll(rdr)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Get failed: %s", err)
		}
	}

	rdr.Close()
	return resp, err == nil
}

func (c *Cache) Set(key string, block []byte) {
	err := c.Client.CreateBlockBlobFromReader(c.Config.ContainerName,
									key,
									uint64(len(block)),
									bytes.NewReader(block),
									nil)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Set failed: %s", err)
		}
		return
	}
}

func (c *Cache) Delete(key string) bool {
	res, err := c.Client.DeleteBlobIfExists(c.Config.ContainerName, key, nil)
	if err != nil {
		if !noLogErrors {
			log.Printf("azurestoragecache.Delete failed: %s", err)
		}
		return false
	}
	return res
}

// New returns a new Cache with underlying client for Azure Storage
//
// accountName is the Azure Storage Account Name (part of credentials)
// accountKey is the Azure Storage Account Key (part of credentials)
// containerName is the container name in which images will be stored (/!\ LOWER CASE)
//
// The environment variables AZURESTORAGE_ACCOUNT_NAME and AZURESTORAGE_ACCESS_KEY
// are used as credentials if nothing is provided.
func New(accountName string, accountKey string, containerName string) (*Cache, bool, error) {
	accName := accountName
	accKey := accountKey
	contName := containerName

	if (len(accName) <= 0) {
		accName = os.Getenv("AZURESTORAGE_ACCOUNT_NAME")
	}

	if (len(accKey) <= 0) {
		accKey = os.Getenv("AZURESTORAGE_ACCESS_KEY")
	}

	if (len(contName) <= 0) {
		contName = "cache"
	}

	cache := Cache{
		Config: Config{
			AccountName: accName,
			AccountKey: accKey,
			ContainerName: contName,
		},
	}

	api, err := vendorstorage.NewBasicClient(cache.Config.AccountName, cache.Config.AccountKey)
	if err != nil {
		return nil, false, err
	}

	cache.Client = api.GetBlobService()

	res, err := cache.Client.CreateContainerIfNotExists(cache.Config.ContainerName, vendorstorage.ContainerAccessTypeBlob)
	if err != nil {
		return nil, false, err
	}

	return &cache, res, nil
}<|endoftext|>


<commit_before>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buffer

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"nvim-go/nvim/profile"

	"github.com/garyburd/neovim-go/vim"
	"github.com/juju/errors"
)

const (
	// Buffer options
	Bufhidden    = "bufhidden"  // string
	Buflisted    = "buflisted"  // bool
	Buftype      = "buftype"    // string
	Filetype     = "filetype"   // string
	OpModifiable = "modifiable" // bool
	OpModified   = "modified"   // bool
	Swapfile     = "swapfile"   // bool

	// Buffer var
	Colorcolumn = "colorcolumn" // string

	// Window options
	List           = "list"           // bool
	Number         = "number"         // bool
	Relativenumber = "relativenumber" // bool
	Winfixheight   = "winfixheight"   // bool
)

const (
	BufhiddenDelete  = "delete"   // delete the buffer from the buffer list, also when 'hidden' is set or using :hide, like using :bdelete.
	BufhiddenHide    = "hide"     // hide the buffer (don't unload it), also when 'hidden' is not set.
	BufhiddenUnload  = "unload"   // unload the buffer, also when 'hidden' is set or using :hide.
	BufhiddenWipe    = "wipe"     // wipe out the buffer from the buffer list, also when 'hidden' is set or using :hide, like using :bwipeout.
	BuftypeAcwrite   = "acwrite"  // buffer which will always be written with BufWriteCmd autocommands.
	BuftypeHelp      = "help"     // help buffer (you are not supposed to set this manually)
	BuftypeNofile    = "nofile"   // buffer which is not related to a file and will not be written.
	BuftypeNowrite   = "nowrite"  // buffer which will not be written.
	BuftypeQuickfix  = "quickfix" // quickfix buffer, contains list of errors :cwindow or list of locations :lwindow
	BuftypeTerminal  = "terminal" // terminal buffer, this is set automatically when a terminal is created. See nvim-terminal-emulator for more information.
	FiletypeAsm      = "asm"
	FiletypeC        = "c"
	FiletypeCpp      = "cpp"
	FiletypeDelve    = "delve"
	FiletypeGas      = "gas"
	FiletypeGo       = "go"
	FiletypeTerminal = "terminal"
)

type Buffer struct {
	Buffer  vim.Buffer
	Window  vim.Window
	Tabpage vim.Tabpage

	Name  string
	Bufnr interface{}
	Mode  string
	Size  int
}

func NewBuffer(name, mode string, size int) *Buffer {
	b := &Buffer{
		Name: name,
		Mode: mode,
		Size: size,
	}

	return b
}

func (b *Buffer) Open(v *vim.Vim) error {
	err := v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, "buffer.Open")
	}

	p := v.NewPipeline()
	p.CurrentBuffer(&b.Buffer)
	p.CurrentWindow(&b.Window)
	p.CurrentTabpage(&b.Tabpage)

	return p.Wait()
}

func (b *Buffer) Create(v *vim.Vim, bufOption, bufVar, winOption, winVar map[string]interface{}) error {
	defer profile.Start(time.Now(), "nvim/buffer.Create")

	err := v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, "buffer.Open")
	}

	p := v.NewPipeline()
	p.CurrentBuffer(&b.Buffer)
	p.CurrentWindow(&b.Window)
	p.CurrentTabpage(&b.Tabpage)
	p.Eval("bufnr('%')", &b.Bufnr)
	if err := p.Wait(); err != nil {
		return errors.Annotate(err, "nvim/buffer.Create")
	}

	if bufOption != nil {
		for k, op := range bufOption {
			p.SetBufferOption(b.Buffer, k, op)
		}
	}
	if bufVar != nil {
		for k, op := range bufVar {
			p.SetBufferVar(b.Buffer, k, op, nil)
		}
	}
	if winOption != nil {
		for k, op := range winOption {
			p.SetWindowOption(b.Window, k, op)
		}
	}
	p.Command(fmt.Sprintf("runtime! syntax/%s.vim", bufOption[Filetype]))
	if err := p.Wait(); err != nil {
		return errors.Annotate(err, "nvim/buffer.Create")
	}

	// TODO(zchee): Why can't set p.SetBufferOption?
	// p.Call("setbufvar", nil, b.Bufnr.(int64), "&colorcolumn", "")

	return p.Wait()
}

const (
	Map             = "map"
	MapNormal       = "nmap"
	MapVisualSelect = "vmap"
	MapSelect       = "smap"
	MapVisual       = "xmap"
	MapOperator     = "omap"
	MapInsert       = "imap"
	MapCommandLine  = "cmap"
	MapTerminal     = "tmap"

	Noremap             = "noremap"
	NoremapNormal       = "nnoremap"
	NoremapVisualSelect = "vnoremap"
	NoremapSelect       = "snoremap"
	NoremapVisual       = "xnoremap"
	NoremapOperator     = "onoremap"
	NoremapInsert       = "inoremap"
	NoremapCommandLine  = "cnoremap"
	NoremapTerminal     = "tnoremap"
)

// SetBufferMapping sets buffer local mapping.
// 'mapping' arg: [key]{destination}
func (b *Buffer) SetMapping(v *vim.Vim, mode string, mapping map[string]string) error {
	p := v.NewPipeline()

	if mapping != nil {
		cwin, err := v.CurrentWindow()
		if err != nil {
			return errors.Annotate(err, "nvim/buffer.SetMapping")
		}

		p.SetCurrentWindow(b.Window)
		defer v.SetCurrentWindow(cwin)

		for k, v := range mapping {
			p.Command(fmt.Sprintf("silent %s <buffer><silent>%s %s", mode, k, v))
		}
	}

	return p.Wait()
}

func ToBufferLines(v *vim.Vim, byt []byte) [][]byte {
	return bytes.Split(byt, []byte{'\n'})
}

// ByteOffset calculation of byte offset the current cursor position.
func ByteOffset(v *vim.Vim, b vim.Buffer, w vim.Window) (int, error) {
	var err error
	if b == 0 {
		b, err = v.CurrentBuffer()
		if err != nil {
			return 0, errors.Annotate(err, "nvim/buffer.ByteOffset")
		}
	}
	if w == 0 {
		w, err = v.CurrentWindow()
		if err != nil {
			return 0, errors.Annotate(err, "nvim/buffer.ByteOffset")
		}
	}

	// var cursor [2]int
	cursor, err := v.WindowCursor(w)

	// var byteBuf [][]byte
	byteBuf, err := v.BufferLines(b, 0, -1, true)

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

// ByteOffsetPipe calculation of byte offset the current cursor position use vim.Pipeline.
func ByteOffsetPipe(p *vim.Pipeline, b vim.Buffer, w vim.Window) (int, error) {
	if b == 0 {
		p.CurrentBuffer(&b)
		if err := p.Wait(); err != nil {
			return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
		}
	}
	if w == 0 {
		p.CurrentWindow(&w)
		if err := p.Wait(); err != nil {
			return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
		}
	}

	var cursor [2]int
	p.WindowCursor(w, &cursor)

	var byteBuf [][]byte
	p.BufferLines(b, 0, -1, true, &byteBuf)

	if err := p.Wait(); err != nil {
		return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
	}

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

func Contains(v *vim.Vim, b vim.Buffer) bool {
	bufs, _ := v.Buffers()
	for _, buf := range bufs {
		if buf == b {
			return true
		}
	}
	return false
}

func Modifiable(v *vim.Vim, b vim.Buffer) func() {
	v.SetBufferOption(b, OpModifiable, true)

	return func() {
		v.SetBufferOption(b, OpModifiable, false)
	}
}
<commit_msg>nvim/buffer: Split update syntax & Fix ByteOffset(Pipe)<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buffer

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"nvim-go/nvim/profile"

	"github.com/garyburd/neovim-go/vim"
	"github.com/juju/errors"
)

const (
	// Buffer options
	Bufhidden    = "bufhidden"  // string
	Buflisted    = "buflisted"  // bool
	Buftype      = "buftype"    // string
	Filetype     = "filetype"   // string
	OpModifiable = "modifiable" // bool
	OpModified   = "modified"   // bool
	Swapfile     = "swapfile"   // bool

	// Buffer var
	Colorcolumn = "colorcolumn" // string

	// Window options
	List           = "list"           // bool
	Number         = "number"         // bool
	Relativenumber = "relativenumber" // bool
	Winfixheight   = "winfixheight"   // bool
)

const (
	BufhiddenDelete  = "delete"   // delete the buffer from the buffer list, also when 'hidden' is set or using :hide, like using :bdelete.
	BufhiddenHide    = "hide"     // hide the buffer (don't unload it), also when 'hidden' is not set.
	BufhiddenUnload  = "unload"   // unload the buffer, also when 'hidden' is set or using :hide.
	BufhiddenWipe    = "wipe"     // wipe out the buffer from the buffer list, also when 'hidden' is set or using :hide, like using :bwipeout.
	BuftypeAcwrite   = "acwrite"  // buffer which will always be written with BufWriteCmd autocommands.
	BuftypeHelp      = "help"     // help buffer (you are not supposed to set this manually)
	BuftypeNofile    = "nofile"   // buffer which is not related to a file and will not be written.
	BuftypeNowrite   = "nowrite"  // buffer which will not be written.
	BuftypeQuickfix  = "quickfix" // quickfix buffer, contains list of errors :cwindow or list of locations :lwindow
	BuftypeTerminal  = "terminal" // terminal buffer, this is set automatically when a terminal is created. See nvim-terminal-emulator for more information.
	FiletypeAsm      = "asm"
	FiletypeC        = "c"
	FiletypeCpp      = "cpp"
	FiletypeDelve    = "delve"
	FiletypeGas      = "gas"
	FiletypeGo       = "go"
	FiletypeTerminal = "terminal"
)

type Buffer struct {
	Buffer  vim.Buffer
	Window  vim.Window
	Tabpage vim.Tabpage

	Name  string
	Bufnr interface{}
	Mode  string
	Size  int
}

func NewBuffer(name, mode string, size int) *Buffer {
	b := &Buffer{
		Name: name,
		Mode: mode,
		Size: size,
	}

	return b
}

func (b *Buffer) Open(v *vim.Vim) error {
	err := v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, "buffer.Open")
	}

	p := v.NewPipeline()
	p.CurrentBuffer(&b.Buffer)
	p.CurrentWindow(&b.Window)
	p.CurrentTabpage(&b.Tabpage)

	return p.Wait()
}

func (b *Buffer) Create(v *vim.Vim, bufOption, bufVar, winOption, winVar map[string]interface{}) error {
	defer profile.Start(time.Now(), "nvim/buffer.Create")

	err := v.Command(fmt.Sprintf("silent %s %s", b.Mode, b.Name))
	if err != nil {
		return errors.Annotate(err, "buffer.Open")
	}

	p := v.NewPipeline()
	p.CurrentBuffer(&b.Buffer)
	p.CurrentWindow(&b.Window)
	p.CurrentTabpage(&b.Tabpage)
	p.Eval("bufnr('%')", &b.Bufnr)
	if err := p.Wait(); err != nil {
		return errors.Annotate(err, "nvim/buffer.Create")
	}

	if bufOption != nil {
		for k, op := range bufOption {
			p.SetBufferOption(b.Buffer, k, op)
		}
	}
	if bufVar != nil {
		for k, op := range bufVar {
			p.SetBufferVar(b.Buffer, k, op, nil)
		}
	}
	if winOption != nil {
		for k, op := range winOption {
			p.SetWindowOption(b.Window, k, op)
		}
	}

	return p.Wait()
}

func (b *Buffer) UpdateSyntax(v *vim.Vim, syntax string) {
	if b.Name != "" {
		v.SetBufferName(b.Buffer, b.Name)
	}
	if syntax == "" {
		var filetype interface{}
		v.BufferOption(b.Buffer, "filetype", &filetype)
		syntax = fmt.Sprintf("%s", filetype)
	}
	v.Command(fmt.Sprintf("runtime! syntax/%s.vim", syntax))
}

const (
	Map             = "map"
	MapNormal       = "nmap"
	MapVisualSelect = "vmap"
	MapSelect       = "smap"
	MapVisual       = "xmap"
	MapOperator     = "omap"
	MapInsert       = "imap"
	MapCommandLine  = "cmap"
	MapTerminal     = "tmap"

	Noremap             = "noremap"
	NoremapNormal       = "nnoremap"
	NoremapVisualSelect = "vnoremap"
	NoremapSelect       = "snoremap"
	NoremapVisual       = "xnoremap"
	NoremapOperator     = "onoremap"
	NoremapInsert       = "inoremap"
	NoremapCommandLine  = "cnoremap"
	NoremapTerminal     = "tnoremap"
)

// SetBufferMapping sets buffer local mapping.
// 'mapping' arg: [key]{destination}
func (b *Buffer) SetMapping(v *vim.Vim, mode string, mapping map[string]string) error {
	p := v.NewPipeline()

	if mapping != nil {
		cwin, err := v.CurrentWindow()
		if err != nil {
			return errors.Annotate(err, "nvim/buffer.SetMapping")
		}

		p.SetCurrentWindow(b.Window)
		defer v.SetCurrentWindow(cwin)

		for k, v := range mapping {
			p.Command(fmt.Sprintf("silent %s <buffer><silent>%s %s", mode, k, v))
		}
	}

	return p.Wait()
}

func ToBufferLines(v *vim.Vim, byt []byte) [][]byte {
	return bytes.Split(byt, []byte{'\n'})
}

// ByteOffset calculation of byte offset the current cursor position.
func ByteOffset(v *vim.Vim, b vim.Buffer, w vim.Window) (int, error) {
	cursor, _ := v.WindowCursor(w)
	byteBuf, _ := v.BufferLines(b, 0, -1, true)

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

// ByteOffsetPipe calculation of byte offset the current cursor position use vim.Pipeline.
func ByteOffsetPipe(p *vim.Pipeline, b vim.Buffer, w vim.Window) (int, error) {
	var cursor [2]int
	p.WindowCursor(w, &cursor)

	var byteBuf [][]byte
	p.BufferLines(b, 0, -1, true, &byteBuf)

	if err := p.Wait(); err != nil {
		return 0, errors.Annotate(err, "nvim/buffer.ByteOffsetPipe")
	}

	if cursor[0] == 1 {
		return (1 + (cursor[1] - 1)), nil
	}

	offset := 0
	line := 1
	for _, buf := range byteBuf {
		if line == cursor[0] {
			offset++
			break
		}
		offset += (binary.Size(buf) + 1)
		line++
	}

	return (offset + (cursor[1] - 1)), nil
}

func Contains(v *vim.Vim, b vim.Buffer) bool {
	bufs, _ := v.Buffers()
	for _, buf := range bufs {
		if buf == b {
			return true
		}
	}
	return false
}

func Modifiable(v *vim.Vim, b vim.Buffer) func() {
	v.SetBufferOption(b, OpModifiable, true)

	return func() {
		v.SetBufferOption(b, OpModifiable, false)
	}
}
<|endoftext|>


<commit_before>package filters

import (
	"encoding/json"
	"fmt"
	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"regexp"
	"strings"
)

var ec2InstanceIdRegex = regexp.MustCompile("i-[0-9a-f]{8}")

type ec2InstanceIdParser interface {
	Parse(input string) string
}
type realEc2InstanceIdParser struct{}

func (p realEc2InstanceIdParser) Parse(input string) string {
	return ec2InstanceIdRegex.FindString(input)
}

type apiResponse struct {
	Reservations []struct {
		Instances []struct {
			PublicDnsName   string
			PublicIpAddress string
		}
	}
}

type ec2InstanceIdLookup struct {
	region        string
	commandRunner util.CommandRunner
	idParser      ec2InstanceIdParser
}

func (f *ec2InstanceIdLookup) Filter(targets []target.Target) []target.Target {
	if f.region == "" {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	// TODO: build map by parsed id, do a single query using --instance-ids
	for idx, t := range targets {
		instanceId := f.idParser.Parse(t.Host)
		if len(instanceId) > 0 {
			util.Logger.Infof("EC2 Instance lookup: %s in %s", instanceId, f.region)

			output, err := f.commandRunner.RunGetOutput("aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", f.region})
			util.Logger.Debugf("Response from AWS API: %s", output)
			if err != nil {
				util.Logger.Infof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", t.Host, instanceId, f.region, strings.TrimSpace(string(output)))
				continue
			}

			var data apiResponse
			if err = json.Unmarshal(output, &data); err != nil {
				panic(fmt.Sprintf("Invalid JSON returned by AWS API.\nError: %s\nJSON follows this line\n%s", err.Error(), output))
			}

			if data.Reservations == nil || len(data.Reservations) == 0 {
				util.Logger.Infof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", t.Host, instanceId, f.region)
				continue
			}
			targets[idx].Host = data.Reservations[0].Instances[0].PublicIpAddress
		} else {
			util.Logger.Debugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", t, f.region)
		}
	}
	return targets
}
func (f *ec2InstanceIdLookup) SetArgs(args []interface{}) {
	if len(args) != 1 {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	f.region = string(args[0].([]byte))
}
func (f *ec2InstanceIdLookup) String() string {
	return fmt.Sprintf("<%s %s>", nameEc2InstanceId, f.region)
}
<commit_msg>Make type name unique-er<commit_after>package filters

import (
	"encoding/json"
	"fmt"
	"github.com/abesto/easyssh/target"
	"github.com/abesto/easyssh/util"
	"regexp"
	"strings"
)

var ec2InstanceIdRegex = regexp.MustCompile("i-[0-9a-f]{8}")

type ec2InstanceIdParser interface {
	Parse(input string) string
}
type realEc2InstanceIdParser struct{}

func (p realEc2InstanceIdParser) Parse(input string) string {
	return ec2InstanceIdRegex.FindString(input)
}

type ec2DescribeInstanceApiResponse struct {
	Reservations []struct {
		Instances []struct {
			PublicDnsName   string
			PublicIpAddress string
		}
	}
}

type ec2InstanceIdLookup struct {
	region        string
	commandRunner util.CommandRunner
	idParser      ec2InstanceIdParser
}

func (f *ec2InstanceIdLookup) Filter(targets []target.Target) []target.Target {
	if f.region == "" {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	// TODO: build map by parsed id, do a single query using --instance-ids
	for idx, t := range targets {
		instanceId := f.idParser.Parse(t.Host)
		if len(instanceId) > 0 {
			util.Logger.Infof("EC2 Instance lookup: %s in %s", instanceId, f.region)

			output, err := f.commandRunner.RunGetOutput("aws", []string{"ec2", "describe-instances", "--instance-id", instanceId, "--region", f.region})
			util.Logger.Debugf("Response from AWS API: %s", output)
			if err != nil {
				util.Logger.Infof("EC2 Instance lookup failed for %s (%s) in region %s (aws command failed): %s", t.Host, instanceId, f.region, strings.TrimSpace(string(output)))
				continue
			}

			var data ec2DescribeInstanceApiResponse
			if err = json.Unmarshal(output, &data); err != nil {
				panic(fmt.Sprintf("Invalid JSON returned by AWS API.\nError: %s\nJSON follows this line\n%s", err.Error(), output))
			}

			if data.Reservations == nil || len(data.Reservations) == 0 {
				util.Logger.Infof("EC2 instance lookup failed for %s (%s) in region %s (Reservations is empty in the received JSON)", t.Host, instanceId, f.region)
				continue
			}
			targets[idx].Host = data.Reservations[0].Instances[0].PublicIpAddress
		} else {
			util.Logger.Debugf("Target %s looks like it doesn't have EC2 instance ID, skipping lookup for region %s", t, f.region)
		}
	}
	return targets
}
func (f *ec2InstanceIdLookup) SetArgs(args []interface{}) {
	if len(args) != 1 {
		panic(fmt.Sprintf("%s requires exactly one argument, the region name to use for looking up instances", nameEc2InstanceId))
	}
	f.region = string(args[0].([]byte))
}
func (f *ec2InstanceIdLookup) String() string {
	return fmt.Sprintf("<%s %s>", nameEc2InstanceId, f.region)
}
<|endoftext|>


<commit_before>package e2e

import (
	"os"
	"testing"

	"github.com/coreos/etcd/client"
	"github.com/pborman/uuid"
	"github.com/sttts/elastic-etcd/join"
	"golang.org/x/net/context"
)

func smokeTest(t *testing.T, epc *etcdProcessCluster) {
	cfg := client.Config{
		Endpoints: []string{},
	}
	for _, p := range epc.procs {
		cfg.Endpoints = append(cfg.Endpoints, p.cfg.clientURL)
	}
	c, err := client.New(cfg)
	if err != nil {
		t.Fatal(err)
	}

	kapi := client.NewKeysAPI(c)

	v := uuid.New()
	_, err = kapi.Set(context.Background(), "/foo", v, nil)
	if err != nil {
		t.Fatal(err)
	}

	resp, err := kapi.Get(context.Background(), "/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	if resp.Node == nil || resp.Node.Value != v {
		t.Fatalf("Invalid value %q for previously written key /foo", v)
	}
}

func launchCluster(t *testing.T, cfg *elasticEtcdClusterConfig) *etcdProcessCluster {
	epc, err := newElasticEtcdProcessCluster(cfg)
	if err != nil {
		t.Fatalf("could not start etcd process cluster (%v)", err)
	}

	smokeTest(t, epc)

	return epc
}

func testNewCluster(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()
}

func testRestartEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()

	// restart etcd with same flags
	proc, err := newEtcdProcess(epc.procs[2].cfg)
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2] = proc

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testRestartElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()

	// restart elastic-etcd and then etcd with the new flags
	newCfg, err := epc.procs[2].cfg.cfg.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2], err = newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testRestartCleanElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()

	// clean data dir
	if err := os.RemoveAll(epc.procs[2].cfg.dataDirPath); err != nil {
		t.Fatal(err)
	}

	// restart elastic-etcd and then etcd with the new flags
	newCfg, err := epc.procs[2].cfg.cfg.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2], err = newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testGrowElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// start a 4th etcd, increase cluster size
	eepc := *epc.procs[2].cfg.cfg
	eepc.num = 3
	eepc.clusterSize = 5

	newCfg, err := eepc.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	ep, err := newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}
	epc.procs = append(epc.procs, ep)

	// wait for join
	err = ep.waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

var (
	elasticReplaceConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.ReplaceStrategy),
	}

	elasticPruneConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.PruneStrategy),
	}

	elasticAddConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.AddStrategy),
	}

	elasticPreparedConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.PreparedStrategy),
	}
)

func TestElasticNewClusterPrepared(t *testing.T) { testNewCluster(t, &elasticPreparedConfig) }
func TestElasticNewClusterReplace(t *testing.T)  { testNewCluster(t, &elasticReplaceConfig) }
func TestElasticNewClusterPrune(t *testing.T)    { testNewCluster(t, &elasticPruneConfig) }
func TestElasticNewClusterAdd(t *testing.T)      { testNewCluster(t, &elasticAddConfig) }

func TestElasticRestartEtcdPrepared(t *testing.T) { testRestartEtcd(t, &elasticPreparedConfig) }
func TestElasticRestartEtcdReplace(t *testing.T)  { testRestartEtcd(t, &elasticReplaceConfig) }
func TestElasticRestartEtcdPrune(t *testing.T)    { testRestartEtcd(t, &elasticPruneConfig) }
func TestElasticRestartEtcdAdd(t *testing.T)      { testRestartEtcd(t, &elasticAddConfig) }

func TestElasticRestartElasticEtcdPrepared(t *testing.T) {
	testRestartElasticEtcd(t, &elasticPreparedConfig)
}
func TestElasticRestartElasticEtcdReplace(t *testing.T) {
	testRestartElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticRestartElasticEtcdPrune(t *testing.T) {
	testRestartElasticEtcd(t, &elasticPruneConfig)
}
func TestElasticRestartElasticEtcdAdd(t *testing.T) {
	testRestartElasticEtcd(t, &elasticAddConfig)
}

func TestElasticRestartCleanElasticEtcdReplace(t *testing.T) {
	testRestartCleanElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticRestartCleanElasticEtcdPrune(t *testing.T) {
	testRestartCleanElasticEtcd(t, &elasticPruneConfig)
}

func TestElasticGrowElasticEtcdReplace(t *testing.T) {
	testGrowElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticGrowElasticEtcdPrune(t *testing.T) {
	testGrowElasticEtcd(t, &elasticPruneConfig)
}
func TestElasticGrowElasticEtcdAdd(t *testing.T) {
	testGrowElasticEtcd(t, &elasticAddConfig)
}
<commit_msg>BAD BAD BAD: fix test race with sleep<commit_after>package e2e

import (
	"os"
	"testing"
	"time"

	"github.com/coreos/etcd/client"
	"github.com/pborman/uuid"
	"github.com/sttts/elastic-etcd/join"
	"golang.org/x/net/context"
)

func smokeTest(t *testing.T, epc *etcdProcessCluster) {
	cfg := client.Config{
		Endpoints: []string{},
	}
	for _, p := range epc.procs {
		cfg.Endpoints = append(cfg.Endpoints, p.cfg.clientURL)
	}
	c, err := client.New(cfg)
	if err != nil {
		t.Fatal(err)
	}

	kapi := client.NewKeysAPI(c)

	v := uuid.New()
	_, err = kapi.Set(context.Background(), "/foo", v, nil)
	if err != nil {
		t.Fatal(err)
	}

	resp, err := kapi.Get(context.Background(), "/foo", nil)
	if err != nil {
		t.Fatal(err)
	}
	if resp.Node == nil || resp.Node.Value != v {
		t.Fatalf("Invalid value %q for previously written key /foo", v)
	}
}

func launchCluster(t *testing.T, cfg *elasticEtcdClusterConfig) *etcdProcessCluster {
	epc, err := newElasticEtcdProcessCluster(cfg)
	if err != nil {
		t.Fatalf("could not start etcd process cluster (%v)", err)
	}

	smokeTest(t, epc)

	return epc
}

func testNewCluster(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()
}

func testRestartEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()
	time.Sleep(time.Second) // FIXME(sttts): do not fix races with sleeps :-(

	// restart etcd with same flags
	proc, err := newEtcdProcess(epc.procs[2].cfg)
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2] = proc

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testRestartElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()

	// restart elastic-etcd and then etcd with the new flags
	newCfg, err := epc.procs[2].cfg.cfg.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2], err = newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testRestartCleanElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// stop process
	_ = epc.procs[2].proc.Close()

	// clean data dir
	if err := os.RemoveAll(epc.procs[2].cfg.dataDirPath); err != nil {
		t.Fatal(err)
	}

	// restart elastic-etcd and then etcd with the new flags
	newCfg, err := epc.procs[2].cfg.cfg.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	epc.procs[2], err = newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}

	// wait for join
	err = epc.procs[2].waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

func testGrowElasticEtcd(t *testing.T, cfg *elasticEtcdClusterConfig) {
	epc := launchCluster(t, cfg)
	defer func() { _ = epc.Close() }()

	// start a 4th etcd, increase cluster size
	eepc := *epc.procs[2].cfg.cfg
	eepc.num = 3
	eepc.clusterSize = 5

	newCfg, err := eepc.etcdProcessConfig()
	if err != nil {
		t.Fatal(err)
	}
	ep, err := newEtcdProcess(newCfg)
	if err != nil {
		t.Fatal(err)
	}
	epc.procs = append(epc.procs, ep)

	// wait for join
	err = ep.waitForLaunch()
	if err != nil {
		t.Fatal(err)
	}

	smokeTest(t, epc)
}

var (
	elasticReplaceConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.ReplaceStrategy),
	}

	elasticPruneConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.PruneStrategy),
	}

	elasticAddConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.AddStrategy),
	}

	elasticPreparedConfig = elasticEtcdClusterConfig{
		initialClusterSize:   3,
		discoveryClusterSize: 3,
		joinStrategy:         string(join.PreparedStrategy),
	}
)

func TestElasticNewClusterPrepared(t *testing.T) { testNewCluster(t, &elasticPreparedConfig) }
func TestElasticNewClusterReplace(t *testing.T)  { testNewCluster(t, &elasticReplaceConfig) }
func TestElasticNewClusterPrune(t *testing.T)    { testNewCluster(t, &elasticPruneConfig) }
func TestElasticNewClusterAdd(t *testing.T)      { testNewCluster(t, &elasticAddConfig) }

func TestElasticRestartEtcdPrepared(t *testing.T) { testRestartEtcd(t, &elasticPreparedConfig) }
func TestElasticRestartEtcdReplace(t *testing.T)  { testRestartEtcd(t, &elasticReplaceConfig) }
func TestElasticRestartEtcdPrune(t *testing.T)    { testRestartEtcd(t, &elasticPruneConfig) }
func TestElasticRestartEtcdAdd(t *testing.T)      { testRestartEtcd(t, &elasticAddConfig) }

func TestElasticRestartElasticEtcdPrepared(t *testing.T) {
	testRestartElasticEtcd(t, &elasticPreparedConfig)
}
func TestElasticRestartElasticEtcdReplace(t *testing.T) {
	testRestartElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticRestartElasticEtcdPrune(t *testing.T) {
	testRestartElasticEtcd(t, &elasticPruneConfig)
}
func TestElasticRestartElasticEtcdAdd(t *testing.T) {
	testRestartElasticEtcd(t, &elasticAddConfig)
}

func TestElasticRestartCleanElasticEtcdReplace(t *testing.T) {
	testRestartCleanElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticRestartCleanElasticEtcdPrune(t *testing.T) {
	testRestartCleanElasticEtcd(t, &elasticPruneConfig)
}

func TestElasticGrowElasticEtcdReplace(t *testing.T) {
	testGrowElasticEtcd(t, &elasticReplaceConfig)
}
func TestElasticGrowElasticEtcdPrune(t *testing.T) {
	testGrowElasticEtcd(t, &elasticPruneConfig)
}
func TestElasticGrowElasticEtcdAdd(t *testing.T) {
	testGrowElasticEtcd(t, &elasticAddConfig)
}
<|endoftext|>