<commit_before>package gorma

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// NewRelationalFieldDefinition returns an initialized
// RelationalFieldDefinition.
func NewRelationalFieldDefinition() *RelationalFieldDefinition {
	m := &RelationalFieldDefinition{
		Mappings: make(map[string]*MapDefinition),
	}
	return m
}

// Context returns the generic definition name used in error messages.
func (f *RelationalFieldDefinition) Context() string {
	if f.FieldName != "" {
		return fmt.Sprintf("RelationalField %#v", f.FieldName)
	}
	return "unnamed RelationalField"
}

// DSL returns this object's DSL.
func (f *RelationalFieldDefinition) DSL() func() {
	return f.DefinitionDSL
}

// Children returns a slice of this objects children.
func (f RelationalFieldDefinition) Children() []dslengine.Definition {
	// no children yet
	return []dslengine.Definition{}
}

// Attribute implements the Container interface of the goa Attribute
// model.
func (f *RelationalFieldDefinition) Attribute() *design.AttributeDefinition {
	return f.a
}

// FieldDefinition returns the field's struct definition.
func (f *RelationalFieldDefinition) FieldDefinition() string {
	var comment string
	if f.Description != "" {
		comment = "// " + f.Description
	}
	def := fmt.Sprintf("%s\t%s %s %s\n", f.FieldName, goDatatype(f, true), tags(f), comment)
	return def
}

// Tags returns the sql and gorm struct tags for the Definition.
func (f *RelationalFieldDefinition) Tags() string {
	return tags(f)
}

// LowerName returns the field name as a lowercase string.
func (f *RelationalFieldDefinition) LowerName() string {
	return strings.ToLower(f.FieldName)
}

// Underscore returns the field name as a lowercase string in snake case.
func (f *RelationalFieldDefinition) Underscore() string {
	runes := []rune(f.FieldName)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func goDatatype(f *RelationalFieldDefinition, includePtr bool) string {
	var ptr string
	if f.Nullable && includePtr {
		ptr = "*"
	}
	switch f.Datatype {
	case Boolean:
		return ptr + "bool"
	case Integer, BigInteger:
		return ptr + "int"
	case AutoInteger, AutoBigInteger:
		return ptr + "int " // sql/gorm tags later
	case Decimal:
		return ptr + "float32"
	case BigDecimal:
		return ptr + "float64"
	case String:
		return ptr + "string"
	case Text:
		return ptr + "string"
	case UUID:
		return ptr + "uuid.UUID"
	case Timestamp, NullableTimestamp:
		return ptr + "time.Time"
	case BelongsTo:
		return ptr + "int"
	case HasMany:
		return fmt.Sprintf("[]%s", f.HasMany)
	case HasManyKey, HasOneKey:
		return ptr + "int"
	case HasOne:
		return fmt.Sprintf("%s", f.HasOne)
	default:

		if f.Many2Many != "" {
			return fmt.Sprintf("[]%s", f.Many2Many)
		}
	}

	return "UNKNOWN TYPE"
}

func tags(f *RelationalFieldDefinition) string {
	var sqltags []string
	if f.SQLTag != "" {
		sqltags = append(sqltags, f.SQLTag)
	}

	var gormtags []string
	if f.DatabaseFieldName != "" && f.DatabaseFieldName != f.Underscore() {
		gormtags = append(gormtags, "column:"+f.DatabaseFieldName)
	}
	if f.PrimaryKey {
		gormtags = append(gormtags, "primary_key")
	}
	if f.Many2Many != "" {
		p := strings.ToLower(f.Parent.ModelName)
		j := strings.ToLower(f.Many2Many)
		gormtags = append(gormtags, "many2many:"+p+"_"+j)
	}

	var tags []string
	if len(sqltags) > 0 {
		sqltag := "sql:\"" + strings.Join(sqltags, ";") + "\""
		tags = append(tags, sqltag)
	}
	if len(gormtags) > 0 {
		gormtag := "gorm:\"" + strings.Join(gormtags, ";") + "\""
		tags = append(tags, gormtag)
	}

	if len(tags) > 0 {
		return "`" + strings.Join(tags, " ") + "`"
	}
	return ""
}
<commit_msg>Resolve target Model primary key type for BelongsTo|HasManyKey|HasOnKey<commit_after>package gorma

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// NewRelationalFieldDefinition returns an initialized
// RelationalFieldDefinition.
func NewRelationalFieldDefinition() *RelationalFieldDefinition {
	m := &RelationalFieldDefinition{
		Mappings: make(map[string]*MapDefinition),
	}
	return m
}

// Context returns the generic definition name used in error messages.
func (f *RelationalFieldDefinition) Context() string {
	if f.FieldName != "" {
		return fmt.Sprintf("RelationalField %#v", f.FieldName)
	}
	return "unnamed RelationalField"
}

// DSL returns this object's DSL.
func (f *RelationalFieldDefinition) DSL() func() {
	return f.DefinitionDSL
}

// Children returns a slice of this objects children.
func (f RelationalFieldDefinition) Children() []dslengine.Definition {
	// no children yet
	return []dslengine.Definition{}
}

// Attribute implements the Container interface of the goa Attribute
// model.
func (f *RelationalFieldDefinition) Attribute() *design.AttributeDefinition {
	return f.a
}

// FieldDefinition returns the field's struct definition.
func (f *RelationalFieldDefinition) FieldDefinition() string {
	var comment string
	if f.Description != "" {
		comment = "// " + f.Description
	}
	def := fmt.Sprintf("%s\t%s %s %s\n", f.FieldName, goDatatype(f, true), tags(f), comment)
	return def
}

// Tags returns the sql and gorm struct tags for the Definition.
func (f *RelationalFieldDefinition) Tags() string {
	return tags(f)
}

// LowerName returns the field name as a lowercase string.
func (f *RelationalFieldDefinition) LowerName() string {
	return strings.ToLower(f.FieldName)
}

// Underscore returns the field name as a lowercase string in snake case.
func (f *RelationalFieldDefinition) Underscore() string {
	runes := []rune(f.FieldName)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func goDatatype(f *RelationalFieldDefinition, includePtr bool) string {
	var ptr string
	if f.Nullable && includePtr {
		ptr = "*"
	}
	switch f.Datatype {
	case Boolean:
		return ptr + "bool"
	case Integer, BigInteger:
		return ptr + "int"
	case AutoInteger, AutoBigInteger:
		return ptr + "int " // sql/gorm tags later
	case Decimal:
		return ptr + "float32"
	case BigDecimal:
		return ptr + "float64"
	case String:
		return ptr + "string"
	case Text:
		return ptr + "string"
	case UUID:
		return ptr + "uuid.UUID"
	case Timestamp, NullableTimestamp:
		return ptr + "time.Time"
	case BelongsTo:
		return ptr + belongsToIDType(f, includePtr)
	case HasMany:
		return fmt.Sprintf("[]%s", f.HasMany)
	case HasManyKey:
		return ptr + hasManyIDType(f, includePtr)
	case HasOneKey:
		return ptr + hasOneIDType(f, includePtr)
	case HasOne:
		return fmt.Sprintf("%s", f.HasOne)
	default:

		if f.Many2Many != "" {
			return fmt.Sprintf("[]%s", f.Many2Many)
		}
	}

	return "UNKNOWN TYPE"
}

func belongsToIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.BelongsTo[modelName]
	return relatedIDType(model, includePtr)
}

func hasOneIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasOne[modelName]
	return relatedIDType(model, includePtr)
}

func hasManyIDType(f *RelationalFieldDefinition, includePtr bool) string {
	modelName := strings.Replace(f.FieldName, "ID", "", -1)
	model := f.Parent.HasMany[modelName]
	return relatedIDType(model, includePtr)
}

func relatedIDType(m *RelationalModelDefinition, includePtr bool) string {
	if m == nil {
		return "int"
	}
	if len(m.PrimaryKeys) > 1 {
		panic("Can't determine field Type when using multiple primary keys")
	}
	return goDatatype(m.PrimaryKeys[0], includePtr)
}

func tags(f *RelationalFieldDefinition) string {
	var sqltags []string
	if f.SQLTag != "" {
		sqltags = append(sqltags, f.SQLTag)
	}

	var gormtags []string
	if f.DatabaseFieldName != "" && f.DatabaseFieldName != f.Underscore() {
		gormtags = append(gormtags, "column:"+f.DatabaseFieldName)
	}
	if f.PrimaryKey {
		gormtags = append(gormtags, "primary_key")
	}
	if f.Many2Many != "" {
		p := strings.ToLower(f.Parent.ModelName)
		j := strings.ToLower(f.Many2Many)
		gormtags = append(gormtags, "many2many:"+p+"_"+j)
	}

	var tags []string
	if len(sqltags) > 0 {
		sqltag := "sql:\"" + strings.Join(sqltags, ";") + "\""
		tags = append(tags, sqltag)
	}
	if len(gormtags) > 0 {
		gormtag := "gorm:\"" + strings.Join(gormtags, ";") + "\""
		tags = append(tags, gormtag)
	}

	if len(tags) > 0 {
		return "`" + strings.Join(tags, " ") + "`"
	}
	return ""
}
<|endoftext|>


<commit_before>package census

import (
	"fmt"
)

type requestType string

const (
	REQUEST_CHARACTER        requestType = "character"
	REQUEST_CHARACTER_EVENTS requestType = "character_event"
)

type Request struct {
	*Census
	url string
}

func (c *Census) NewRequest(Type requestType, query string, resolves string, limit int, more ...string) *Request {
	req := new(Request)
	req.Census = c

	base := fmt.Sprintf("%v%v/get/%v/%v/",
		BaseURL,
		c.serviceID,
		c.namespace, Type)

	if query != "" {
		base = fmt.Sprintf("%v?%v", base, query)
	}

	if resolves != "" {
		base = fmt.Sprintf("%v&c:resolve=%v", base, resolves)
	}
	if limit != 0 {
		base = fmt.Sprintf("%v&c:limit=%v", base, limit)
	}

	for _, v := range more {
		base = fmt.Sprintf("%v&%v", base, v)
	}

	req.url = base
	fmt.Printf("Request builder made url: [%v]\n", req.url)
	return req
}

func (r *Request) Do(v interface{}) error {
	return decode(r.Census, r.url, v)
}
<commit_msg>Add request_world<commit_after>package census

import (
	"fmt"
)

type requestType string

const (
	REQUEST_CHARACTER        requestType = "character"
	REQUEST_CHARACTER_EVENTS requestType = "character_event"
	REQUEST_WORLD            requestType = "world"
)

type Request struct {
	*Census
	url string
}

func (c *Census) NewRequest(Type requestType, query string, resolves string, limit int, more ...string) *Request {
	req := new(Request)
	req.Census = c

	base := fmt.Sprintf("%v%v/get/%v/%v/",
		BaseURL,
		c.serviceID,
		c.namespace, Type)

	if query != "" {
		base = fmt.Sprintf("%v?%v", base, query)
	}

	if resolves != "" {
		base = fmt.Sprintf("%v&c:resolve=%v", base, resolves)
	}
	if limit != 0 {
		base = fmt.Sprintf("%v&c:limit=%v", base, limit)
	}

	for _, v := range more {
		base = fmt.Sprintf("%v&%v", base, v)
	}

	req.url = base
	fmt.Printf("Request builder made url: [%v]\n", req.url)
	return req
}

func (r *Request) Do(v interface{}) error {
	return decode(r.Census, r.url, v)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/takebayashi/go-dozens/dozens"
)

func resourceRecord() *schema.Resource {
	return &schema.Resource{
		Create: createRecord,
		Read:   readRecord,
		Update: updateRecord,
		Delete: deleteRecord,
		Schema: map[string]*schema.Schema{
			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"address": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"ttl": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
		},
	}
}

func createRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	record := &dozens.Record{SName: name, Type: d.Get("type").(string), Prio: d.Get("priority").(string), Content: d.Get("address").(string), Ttl: d.Get("ttl").(string)}
	record, err = client.AddRecord(domain, record)
	if err != nil {
		return err
	}
	applyRecord(record, d)
	return nil
}

func readRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	list, err := client.ListRecords(domain)
	if err != nil {
		return err
	}
	for _, record := range list {
		if record.FQName == (name + "." + domain.Name) {
			applyRecord(record, d)
			return nil
		}
	}
	d.SetId("")
	return nil
}

func updateRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	record := &dozens.Record{Id: d.Id(), Prio: d.Get("priority").(string), Content: d.Get("address").(string), Ttl: d.Get("ttl").(string)}
	_, err := client.EditRecord(record)
	return err
}

func deleteRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	list, err := client.ListRecords(domain)
	if err != nil {
		return nil
	}
	var rdel *dozens.Record
	for _, record := range list {
		if record.FQName == (name + "." + domain.Name) {
			rdel = record
		}
	}
	err = m.(*dozens.Client).DeleteRecord(rdel)
	d.SetId("")
	return err
}

func applyRecord(r *dozens.Record, d *schema.ResourceData) {
	d.SetId(r.Id)
	d.Set("type", r.Type)
	d.Set("priority", r.Prio)
	d.Set("address", r.Content)
	d.Set("ttl", r.Ttl)
}
<commit_msg>Allow read the record whose `name` is empty<commit_after>package main

import (
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/takebayashi/go-dozens/dozens"
)

func resourceRecord() *schema.Resource {
	return &schema.Resource{
		Create: createRecord,
		Read:   readRecord,
		Update: updateRecord,
		Delete: deleteRecord,
		Schema: map[string]*schema.Schema{
			"domain": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"address": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"priority": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"ttl": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
		},
	}
}

func createRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	record := &dozens.Record{SName: name, Type: d.Get("type").(string), Prio: d.Get("priority").(string), Content: d.Get("address").(string), Ttl: d.Get("ttl").(string)}
	record, err = client.AddRecord(domain, record)
	if err != nil {
		return err
	}
	applyRecord(record, d)
	return nil
}

func readRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	fqname := name + "." + domain.Name
	if name == "" {
		fqname = domain.Name
	}

	list, err := client.ListRecords(domain)
	if err != nil {
		return err
	}

	for _, record := range list {
		if record.FQName == fqname {
			applyRecord(record, d)
			return nil
		}
	}
	d.SetId("")
	return nil
}

func updateRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	record := &dozens.Record{Id: d.Id(), Prio: d.Get("priority").(string), Content: d.Get("address").(string), Ttl: d.Get("ttl").(string)}
	_, err := client.EditRecord(record)
	return err
}

func deleteRecord(d *schema.ResourceData, m interface{}) error {
	client := m.(*dozens.Client)
	domain, err := client.GetDomain(d.Get("domain").(string))
	if err != nil {
		return err
	}
	name := d.Get("name").(string)
	list, err := client.ListRecords(domain)
	if err != nil {
		return nil
	}
	var rdel *dozens.Record
	for _, record := range list {
		if record.FQName == (name + "." + domain.Name) {
			rdel = record
		}
	}
	err = m.(*dozens.Client).DeleteRecord(rdel)
	d.SetId("")
	return err
}

func applyRecord(r *dozens.Record, d *schema.ResourceData) {
	d.SetId(r.Id)
	d.Set("type", r.Type)
	d.Set("priority", r.Prio)
	d.Set("address", r.Content)
	d.Set("ttl", r.Ttl)
}
<|endoftext|>


<commit_before>package vmware

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"strings"
)

// Workstation9LinuxDriver is a driver that can run VMware Workstation 9
// on Linux.
type Workstation9LinuxDriver struct {
	AppPath          string
	VdiskManagerPath string
	VmrunPath        string
}

func (d *Workstation9LinuxDriver) CompactDisk(diskPath string) error {
	defragCmd := exec.Command(d.VdiskManagerPath, "-d", diskPath)
	if _, _, err := d.runAndLog(defragCmd); err != nil {
		return err
	}

	shrinkCmd := exec.Command(d.VdiskManagerPath, "-k", diskPath)
	if _, _, err := d.runAndLog(shrinkCmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) CreateDisk(output string, size string) error {
	cmd := exec.Command(d.VdiskManagerPath, "-c", "-s", size, "-a", "lsilogic", "-t", "1", output)
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) IsRunning(vmxPath string) (bool, error) {
	vmxPath, err := filepath.Abs(vmxPath)
	if err != nil {
		return false, err
	}

	cmd := exec.Command(d.VmrunPath, "-T", "ws", "list")
	stdout, _, err := d.runAndLog(cmd)
	if err != nil {
		return false, err
	}

	for _, line := range strings.Split(stdout, "\n") {
		if line == vmxPath {
			return true, nil
		}
	}

	return false, nil
}

func (d *Workstation9LinuxDriver) Start(vmxPath string, headless bool) error {
	guiArgument := "gui"
	if headless {
		guiArgument = "nogui"
	}

	cmd := exec.Command(d.VmrunPath, "-T", "ws", "start", vmxPath, guiArgument)
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) Stop(vmxPath string) error {
	cmd := exec.Command(d.VmrunPath, "-T", "ws", "stop", vmxPath, "hard")
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) Verify() error {
	if err := d.findApp(); err != nil {
		return fmt.Errorf("VMware Workstation application ('vmware') not found in path.")
	}

	if err := d.findVmrun(); err != nil {
		return fmt.Errorf("Required application 'vmrun' not found in path.")
	}

	if err := d.findVdiskManager(); err != nil {
		return fmt.Errorf("Required application 'vmware-vdiskmanager' not found in path.")
	}

	return nil
}

func (d *Workstation9LinuxDriver) findApp() error {
	path, err := exec.LookPath("vmware")
	if err != nil {
		return err
	}
	d.AppPath = path
	return nil
}

func (d *Workstation9LinuxDriver) findVdiskManager() error {
	path, err := exec.LookPath("vmware-vdiskmanager")
	if err != nil {
		return err
	}
	d.VdiskManagerPath = path
	return nil
}

func (d *Workstation9LinuxDriver) findVmrun() error {
	path, err := exec.LookPath("vmrun")
	if err != nil {
		return err
	}
	d.VmrunPath = path
	return nil
}

func (d *Workstation9LinuxDriver) ToolsIsoPath(flavor string) string {
	return "/usr/lib/vmware/isoimages/" + flavor + ".iso"
}

func (d *Workstation9LinuxDriver) DhcpLeasesPath(device string) string {
	return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases"
}

func (d *Workstation9LinuxDriver) runAndLog(cmd *exec.Cmd) (string, string, error) {
	var stdout, stderr bytes.Buffer

	log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:])
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()

	stdoutString := strings.TrimSpace(stdout.String())
	stderrString := strings.TrimSpace(stderr.String())

	if _, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf("VMware error: %s", stderrString)
	}

	log.Printf("stdout: %s", stdoutString)
	log.Printf("stderr: %s", stderrString)

	return stdout.String(), stderr.String(), err
}
<commit_msg>builder/vmware: look for license for WS9<commit_after>package vmware

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os/exec"
	"path/filepath"
	"strings"
)

// Workstation9LinuxDriver is a driver that can run VMware Workstation 9
// on Linux.
type Workstation9LinuxDriver struct {
	AppPath          string
	VdiskManagerPath string
	VmrunPath        string
}

func (d *Workstation9LinuxDriver) CompactDisk(diskPath string) error {
	defragCmd := exec.Command(d.VdiskManagerPath, "-d", diskPath)
	if _, _, err := d.runAndLog(defragCmd); err != nil {
		return err
	}

	shrinkCmd := exec.Command(d.VdiskManagerPath, "-k", diskPath)
	if _, _, err := d.runAndLog(shrinkCmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) CreateDisk(output string, size string) error {
	cmd := exec.Command(d.VdiskManagerPath, "-c", "-s", size, "-a", "lsilogic", "-t", "1", output)
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) IsRunning(vmxPath string) (bool, error) {
	vmxPath, err := filepath.Abs(vmxPath)
	if err != nil {
		return false, err
	}

	cmd := exec.Command(d.VmrunPath, "-T", "ws", "list")
	stdout, _, err := d.runAndLog(cmd)
	if err != nil {
		return false, err
	}

	for _, line := range strings.Split(stdout, "\n") {
		if line == vmxPath {
			return true, nil
		}
	}

	return false, nil
}

func (d *Workstation9LinuxDriver) Start(vmxPath string, headless bool) error {
	guiArgument := "gui"
	if headless {
		guiArgument = "nogui"
	}

	cmd := exec.Command(d.VmrunPath, "-T", "ws", "start", vmxPath, guiArgument)
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) Stop(vmxPath string) error {
	cmd := exec.Command(d.VmrunPath, "-T", "ws", "stop", vmxPath, "hard")
	if _, _, err := d.runAndLog(cmd); err != nil {
		return err
	}

	return nil
}

func (d *Workstation9LinuxDriver) Verify() error {
	if err := d.findApp(); err != nil {
		return fmt.Errorf("VMware Workstation application ('vmware') not found in path.")
	}

	if err := d.findVmrun(); err != nil {
		return fmt.Errorf("Required application 'vmrun' not found in path.")
	}

	if err := d.findVdiskManager(); err != nil {
		return fmt.Errorf("Required application 'vmware-vdiskmanager' not found in path.")
	}

	// Check to see if it APPEARS to be licensed.
	matches, err := filepath.Glob("/etc/vmware/license-*")
	if err != nil {
		return fmt.Errorf("Error looking for VMware license: %s", err)
	}

	if len(matches) == 0 {
		return errors.New("Workstation does not appear to be licensed. Please license it.")
	}

	return nil
}

func (d *Workstation9LinuxDriver) findApp() error {
	path, err := exec.LookPath("vmware")
	if err != nil {
		return err
	}
	d.AppPath = path
	return nil
}

func (d *Workstation9LinuxDriver) findVdiskManager() error {
	path, err := exec.LookPath("vmware-vdiskmanager")
	if err != nil {
		return err
	}
	d.VdiskManagerPath = path
	return nil
}

func (d *Workstation9LinuxDriver) findVmrun() error {
	path, err := exec.LookPath("vmrun")
	if err != nil {
		return err
	}
	d.VmrunPath = path
	return nil
}

func (d *Workstation9LinuxDriver) ToolsIsoPath(flavor string) string {
	return "/usr/lib/vmware/isoimages/" + flavor + ".iso"
}

func (d *Workstation9LinuxDriver) DhcpLeasesPath(device string) string {
	return "/etc/vmware/" + device + "/dhcpd/dhcpd.leases"
}

func (d *Workstation9LinuxDriver) runAndLog(cmd *exec.Cmd) (string, string, error) {
	var stdout, stderr bytes.Buffer

	log.Printf("Executing: %s %v", cmd.Path, cmd.Args[1:])
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()

	stdoutString := strings.TrimSpace(stdout.String())
	stderrString := strings.TrimSpace(stderr.String())

	if _, ok := err.(*exec.ExitError); ok {
		err = fmt.Errorf("VMware error: %s", stderrString)
	}

	log.Printf("stdout: %s", stdoutString)
	log.Printf("stderr: %s", stderrString)

	return stdout.String(), stderr.String(), err
}
<|endoftext|>


<commit_before>package nat

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/huin/goupnp"
	"github.com/huin/goupnp/dcps/internetgateway1"
	"github.com/huin/goupnp/dcps/internetgateway2"
)

type upnp struct {
	dev     *goupnp.RootDevice
	service string
	client  upnpClient
}

type upnpClient interface {
	GetExternalIPAddress() (string, error)
	AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
	DeletePortMapping(string, uint16, string) error
	GetNATRSIPStatus() (sip bool, nat bool, err error)
}

func (n *upnp) ExternalIP() (addr net.IP, err error) {
	ipString, err := n.client.GetExternalIPAddress()
	if err != nil {
		return nil, err
	}
	ip := net.ParseIP(ipString)
	if ip == nil {
		return nil, errors.New("bad IP in response")
	}
	return ip, nil
}

func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
	ip, err := n.internalAddress()
	if err != nil {
		return nil
	}
	protocol = strings.ToUpper(protocol)
	lifetimeS := uint32(lifetime / time.Second)
	return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
}

func (n *upnp) internalAddress() (net.IP, error) {
	devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
	if err != nil {
		return nil, err
	}
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, iface := range ifaces {
		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			switch x := addr.(type) {
			case *net.IPNet:
				if x.Contains(devaddr.IP) {
					return x.IP, nil
				}
			}
		}
	}
	return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
}

func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
	return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
}

func (n *upnp) String() string {
	return "UPNP " + n.service
}

// discoverUPnP searches for Internet Gateway Devices
// and returns the first one it can find on the local network.
func discoverUPnP() Interface {
	found := make(chan *upnp, 2)
	// IGDv1
	go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
		switch sc.Service.ServiceType {
		case internetgateway1.URN_WANIPConnection_1:
			return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{sc}}
		case internetgateway1.URN_WANPPPConnection_1:
			return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{sc}}
		}
		return nil
	})
	// IGDv2
	go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
		switch sc.Service.ServiceType {
		case internetgateway2.URN_WANIPConnection_1:
			return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{sc}}
		case internetgateway2.URN_WANIPConnection_2:
			return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{sc}}
		case internetgateway2.URN_WANPPPConnection_1:
			return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{sc}}
		}
		return nil
	})
	for i := 0; i < cap(found); i++ {
		if c := <-found; c != nil {
			return c
		}
	}
	return nil
}

func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
	devs, err := goupnp.DiscoverDevices(target)
	if err != nil {
		return
	}
	found := false
	for i := 0; i < len(devs) && !found; i++ {
		if devs[i].Root == nil {
			continue
		}
		devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
			if found {
				return
			}
			// check for a matching IGD service
			sc := goupnp.ServiceClient{service.NewSOAPClient(), devs[i].Root, service}
			upnp := matcher(devs[i].Root, sc)
			if upnp == nil {
				return
			}
			// check whether port mapping is enabled
			if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
				return
			}
			out <- upnp
			found = true
		})
	}
	if !found {
		out <- nil
	}
}
<commit_msg>p2p/nat: add timeout for UPnP SOAP requests<commit_after>package nat

import (
	"errors"
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/huin/goupnp"
	"github.com/huin/goupnp/dcps/internetgateway1"
	"github.com/huin/goupnp/dcps/internetgateway2"
)

const soapRequestTimeout = 3 * time.Second

type upnp struct {
	dev     *goupnp.RootDevice
	service string
	client  upnpClient
}

type upnpClient interface {
	GetExternalIPAddress() (string, error)
	AddPortMapping(string, uint16, string, uint16, string, bool, string, uint32) error
	DeletePortMapping(string, uint16, string) error
	GetNATRSIPStatus() (sip bool, nat bool, err error)
}

func (n *upnp) ExternalIP() (addr net.IP, err error) {
	ipString, err := n.client.GetExternalIPAddress()
	if err != nil {
		return nil, err
	}
	ip := net.ParseIP(ipString)
	if ip == nil {
		return nil, errors.New("bad IP in response")
	}
	return ip, nil
}

func (n *upnp) AddMapping(protocol string, extport, intport int, desc string, lifetime time.Duration) error {
	ip, err := n.internalAddress()
	if err != nil {
		return nil
	}
	protocol = strings.ToUpper(protocol)
	lifetimeS := uint32(lifetime / time.Second)
	return n.client.AddPortMapping("", uint16(extport), protocol, uint16(intport), ip.String(), true, desc, lifetimeS)
}

func (n *upnp) internalAddress() (net.IP, error) {
	devaddr, err := net.ResolveUDPAddr("udp4", n.dev.URLBase.Host)
	if err != nil {
		return nil, err
	}
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	for _, iface := range ifaces {
		addrs, err := iface.Addrs()
		if err != nil {
			return nil, err
		}
		for _, addr := range addrs {
			switch x := addr.(type) {
			case *net.IPNet:
				if x.Contains(devaddr.IP) {
					return x.IP, nil
				}
			}
		}
	}
	return nil, fmt.Errorf("could not find local address in same net as %v", devaddr)
}

func (n *upnp) DeleteMapping(protocol string, extport, intport int) error {
	return n.client.DeletePortMapping("", uint16(extport), strings.ToUpper(protocol))
}

func (n *upnp) String() string {
	return "UPNP " + n.service
}

// discoverUPnP searches for Internet Gateway Devices
// and returns the first one it can find on the local network.
func discoverUPnP() Interface {
	found := make(chan *upnp, 2)
	// IGDv1
	go discover(found, internetgateway1.URN_WANConnectionDevice_1, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
		switch sc.Service.ServiceType {
		case internetgateway1.URN_WANIPConnection_1:
			return &upnp{dev, "IGDv1-IP1", &internetgateway1.WANIPConnection1{sc}}
		case internetgateway1.URN_WANPPPConnection_1:
			return &upnp{dev, "IGDv1-PPP1", &internetgateway1.WANPPPConnection1{sc}}
		}
		return nil
	})
	// IGDv2
	go discover(found, internetgateway2.URN_WANConnectionDevice_2, func(dev *goupnp.RootDevice, sc goupnp.ServiceClient) *upnp {
		switch sc.Service.ServiceType {
		case internetgateway2.URN_WANIPConnection_1:
			return &upnp{dev, "IGDv2-IP1", &internetgateway2.WANIPConnection1{sc}}
		case internetgateway2.URN_WANIPConnection_2:
			return &upnp{dev, "IGDv2-IP2", &internetgateway2.WANIPConnection2{sc}}
		case internetgateway2.URN_WANPPPConnection_1:
			return &upnp{dev, "IGDv2-PPP1", &internetgateway2.WANPPPConnection1{sc}}
		}
		return nil
	})
	for i := 0; i < cap(found); i++ {
		if c := <-found; c != nil {
			return c
		}
	}
	return nil
}

func discover(out chan<- *upnp, target string, matcher func(*goupnp.RootDevice, goupnp.ServiceClient) *upnp) {
	devs, err := goupnp.DiscoverDevices(target)
	if err != nil {
		return
	}
	found := false
	for i := 0; i < len(devs) && !found; i++ {
		if devs[i].Root == nil {
			continue
		}
		devs[i].Root.Device.VisitServices(func(service *goupnp.Service) {
			if found {
				return
			}
			// check for a matching IGD service
			sc := goupnp.ServiceClient{service.NewSOAPClient(), devs[i].Root, service}
			sc.SOAPClient.HTTPClient.Timeout = soapRequestTimeout
			upnp := matcher(devs[i].Root, sc)
			if upnp == nil {
				return
			}
			// check whether port mapping is enabled
			if _, nat, err := upnp.client.GetNATRSIPStatus(); err != nil || !nat {
				return
			}
			out <- upnp
			found = true
		})
	}
	if !found {
		out <- nil
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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 webhook implements the authenticator.Token interface using HTTP webhooks.
package webhook

import (
	"context"
	"time"

	authentication "k8s.io/api/authentication/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/authentication/authenticator"
	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/util/webhook"
	"k8s.io/client-go/kubernetes/scheme"
	authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
	"k8s.io/klog"
)

var (
	groupVersions = []schema.GroupVersion{authentication.SchemeGroupVersion}
)

const retryBackoff = 500 * time.Millisecond

// Ensure WebhookTokenAuthenticator implements the authenticator.Token interface.
var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)

type WebhookTokenAuthenticator struct {
	tokenReview    authenticationclient.TokenReviewInterface
	initialBackoff time.Duration
	implicitAuds   authenticator.Audiences
}

// NewFromInterface creates a webhook authenticator using the given tokenReview
// client. It is recommend to wrap this authenticator with the token cache
// authenticator implemented in
// k8s.io/apiserver/pkg/authentication/token/cache.
func NewFromInterface(tokenReview authenticationclient.TokenReviewInterface, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds)
}

// New creates a new WebhookTokenAuthenticator from the provided kubeconfig
// file. It is recommend to wrap this authenticator with the token cache
// authenticator implemented in
// k8s.io/apiserver/pkg/authentication/token/cache.
func New(kubeConfigFile string, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile)
	if err != nil {
		return nil, err
	}
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds)
}

// newWithBackoff allows tests to skip the sleep.
func newWithBackoff(tokenReview authenticationclient.TokenReviewInterface, initialBackoff time.Duration, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	return &WebhookTokenAuthenticator{tokenReview, initialBackoff, implicitAuds}, nil
}

// AuthenticateToken implements the authenticator.Token interface.
func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
	// We take implicit audiences of the API server at WebhookTokenAuthenticator
	// construction time. The outline of how we validate audience here is:
	//
	// * if the ctx is not audience limited, don't do any audience validation.
	// * if ctx is audience-limited, add the audiences to the tokenreview spec
	//   * if the tokenreview returns with audiences in the status that intersect
	//     with the audiences in the ctx, copy into the response and return success
	//   * if the tokenreview returns without an audience in the status, ensure
	//     the ctx audiences intersect with the implicit audiences, and set the
	//     intersection in the response.
	//   * otherwise return unauthenticated.
	wantAuds, checkAuds := authenticator.AudiencesFrom(ctx)
	r := &authentication.TokenReview{
		Spec: authentication.TokenReviewSpec{
			Token:     token,
			Audiences: wantAuds,
		},
	}
	var (
		result *authentication.TokenReview
		err    error
		auds   authenticator.Audiences
	)
	webhook.WithExponentialBackoff(w.initialBackoff, func() error {
		result, err = w.tokenReview.Create(r)
		return err
	})
	if err != nil {
		// An error here indicates bad configuration or an outage. Log for debugging.
		klog.Errorf("Failed to make webhook authenticator request: %v", err)
		return nil, false, err
	}

	if checkAuds {
		gotAuds := w.implicitAuds
		if len(result.Status.Audiences) > 0 {
			gotAuds = result.Status.Audiences
		}
		auds = wantAuds.Intersect(gotAuds)
		if len(auds) == 0 {
			return nil, false, nil
		}
	}

	r.Status = result.Status
	if !r.Status.Authenticated {
		return nil, false, nil
	}

	var extra map[string][]string
	if r.Status.User.Extra != nil {
		extra = map[string][]string{}
		for k, v := range r.Status.User.Extra {
			extra[k] = v
		}
	}

	return &authenticator.Response{
		User: &user.DefaultInfo{
			Name:   r.Status.User.Username,
			UID:    r.Status.User.UID,
			Groups: r.Status.User.Groups,
			Extra:  extra,
		},
		Audiences: auds,
	}, true, nil
}

// tokenReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
// and returns a TokenReviewInterface that uses that client. Note that the client submits TokenReview
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string) (authenticationclient.TokenReviewInterface, error) {
	localScheme := runtime.NewScheme()
	if err := scheme.AddToScheme(localScheme); err != nil {
		return nil, err
	}
	if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
		return nil, err
	}

	gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
	if err != nil {
		return nil, err
	}
	return &tokenReviewClient{gw}, nil
}

type tokenReviewClient struct {
	w *webhook.GenericWebhook
}

func (t *tokenReviewClient) Create(tokenReview *authentication.TokenReview) (*authentication.TokenReview, error) {
	result := &authentication.TokenReview{}
	err := t.w.RestClient.Post().Body(tokenReview).Do().Into(result)
	return result, err
}
<commit_msg>Return authentication webhook error message<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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 webhook implements the authenticator.Token interface using HTTP webhooks.
package webhook

import (
	"context"
	"errors"
	"time"

	authentication "k8s.io/api/authentication/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/authentication/authenticator"
	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/util/webhook"
	"k8s.io/client-go/kubernetes/scheme"
	authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
	"k8s.io/klog"
)

var (
	groupVersions = []schema.GroupVersion{authentication.SchemeGroupVersion}
)

const retryBackoff = 500 * time.Millisecond

// Ensure WebhookTokenAuthenticator implements the authenticator.Token interface.
var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)

type WebhookTokenAuthenticator struct {
	tokenReview    authenticationclient.TokenReviewInterface
	initialBackoff time.Duration
	implicitAuds   authenticator.Audiences
}

// NewFromInterface creates a webhook authenticator using the given tokenReview
// client. It is recommend to wrap this authenticator with the token cache
// authenticator implemented in
// k8s.io/apiserver/pkg/authentication/token/cache.
func NewFromInterface(tokenReview authenticationclient.TokenReviewInterface, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds)
}

// New creates a new WebhookTokenAuthenticator from the provided kubeconfig
// file. It is recommend to wrap this authenticator with the token cache
// authenticator implemented in
// k8s.io/apiserver/pkg/authentication/token/cache.
func New(kubeConfigFile string, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile)
	if err != nil {
		return nil, err
	}
	return newWithBackoff(tokenReview, retryBackoff, implicitAuds)
}

// newWithBackoff allows tests to skip the sleep.
func newWithBackoff(tokenReview authenticationclient.TokenReviewInterface, initialBackoff time.Duration, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
	return &WebhookTokenAuthenticator{tokenReview, initialBackoff, implicitAuds}, nil
}

// AuthenticateToken implements the authenticator.Token interface.
func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
	// We take implicit audiences of the API server at WebhookTokenAuthenticator
	// construction time. The outline of how we validate audience here is:
	//
	// * if the ctx is not audience limited, don't do any audience validation.
	// * if ctx is audience-limited, add the audiences to the tokenreview spec
	//   * if the tokenreview returns with audiences in the status that intersect
	//     with the audiences in the ctx, copy into the response and return success
	//   * if the tokenreview returns without an audience in the status, ensure
	//     the ctx audiences intersect with the implicit audiences, and set the
	//     intersection in the response.
	//   * otherwise return unauthenticated.
	wantAuds, checkAuds := authenticator.AudiencesFrom(ctx)
	r := &authentication.TokenReview{
		Spec: authentication.TokenReviewSpec{
			Token:     token,
			Audiences: wantAuds,
		},
	}
	var (
		result *authentication.TokenReview
		err    error
		auds   authenticator.Audiences
	)
	webhook.WithExponentialBackoff(w.initialBackoff, func() error {
		result, err = w.tokenReview.Create(r)
		return err
	})
	if err != nil {
		// An error here indicates bad configuration or an outage. Log for debugging.
		klog.Errorf("Failed to make webhook authenticator request: %v", err)
		return nil, false, err
	}

	if checkAuds {
		gotAuds := w.implicitAuds
		if len(result.Status.Audiences) > 0 {
			gotAuds = result.Status.Audiences
		}
		auds = wantAuds.Intersect(gotAuds)
		if len(auds) == 0 {
			return nil, false, nil
		}
	}

	r.Status = result.Status
	if !r.Status.Authenticated {
		var err error
		if len(r.Status.Error) != 0 {
			err = errors.New(r.Status.Error)
		}
		return nil, false, err
	}

	var extra map[string][]string
	if r.Status.User.Extra != nil {
		extra = map[string][]string{}
		for k, v := range r.Status.User.Extra {
			extra[k] = v
		}
	}

	return &authenticator.Response{
		User: &user.DefaultInfo{
			Name:   r.Status.User.Username,
			UID:    r.Status.User.UID,
			Groups: r.Status.User.Groups,
			Extra:  extra,
		},
		Audiences: auds,
	}, true, nil
}

// tokenReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
// and returns a TokenReviewInterface that uses that client. Note that the client submits TokenReview
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string) (authenticationclient.TokenReviewInterface, error) {
	localScheme := runtime.NewScheme()
	if err := scheme.AddToScheme(localScheme); err != nil {
		return nil, err
	}
	if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
		return nil, err
	}

	gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
	if err != nil {
		return nil, err
	}
	return &tokenReviewClient{gw}, nil
}

type tokenReviewClient struct {
	w *webhook.GenericWebhook
}

func (t *tokenReviewClient) Create(tokenReview *authentication.TokenReview) (*authentication.TokenReview, error) {
	result := &authentication.TokenReview{}
	err := t.w.RestClient.Post().Body(tokenReview).Do().Into(result)
	return result, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/parrot-translate/parrot/parrot-api/api"
	"github.com/parrot-translate/parrot/parrot-api/auth"
	"github.com/parrot-translate/parrot/parrot-api/config"
	"github.com/parrot-translate/parrot/parrot-api/datastore"
	"github.com/parrot-translate/parrot/parrot-api/logger"
	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
)

const ConfigFileLocation = "./parrot_api.yaml"

func init() {
	// Config log
	logrus.SetOutput(os.Stdout)
	logrus.SetFormatter(&logrus.TextFormatter{})
	logrus.SetLevel(logrus.InfoLevel)
}

// TODO: refactor this into cli to start server
func main() {
	conf := mustLoadConf()

	ds, err := datastore.NewDatastore(conf.DBName, conf.DBConn)
	if err != nil {
		logrus.Fatal(err)
	}
	defer ds.Close()

	// Ping DB until service is up, block meanwhile
	blockAndRetry(5*time.Second, func() bool {
		if err = ds.Ping(); err != nil {
			logrus.Error(fmt.Sprintf("failed to ping datastore.\nerr: %s", err))
			return false
		}
		return true
	})

	router := chi.NewRouter()
	router.Use(
		api.Cors,
		middleware.Recoverer,
		middleware.RequestID,
		middleware.RealIP,
		logger.Request,
		middleware.StripSlashes,
	)

	tp := auth.TokenProvider{Name: conf.AuthIssuer, SigningKey: []byte(conf.AuthSigningKey)}
	router.Mount("/api/v1/auth", auth.NewRouter(ds, tp))
	router.Mount("/api/v1", api.NewRouter(ds, tp))

	// config and init server
	bindInterface := ":" + conf.Port
	s := &http.Server{
		Addr:           bindInterface,
		Handler:        router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	logrus.Info(fmt.Sprintf("server listening on %s", bindInterface))

	logrus.Fatal(s.ListenAndServe())
}

func blockAndRetry(d time.Duration, fn func() bool) {
	for !fn() {
		logrus.Infof("retrying in %s...\n", d.String())
		time.Sleep(d)
	}
}

func mustLoadConf() *config.AppConfig {
	var conf *config.AppConfig

	// Check if config file exists
	_, err := os.Stat(ConfigFileLocation)
	// If not exists, load from environment
	if os.IsNotExist(err) {
		conf, err = config.FromEnv()
		if err != nil {
			logrus.Fatal(err)
		}
	} else {
		// If exists, load from file
		data, err := ioutil.ReadFile(ConfigFileLocation)
		if err != nil {
			logrus.Fatal(err)
		}

		conf, err = config.FromYaml(data)
		if err != nil {
			logrus.Fatal(err)
		}
	}

	// Set defaults if no value set
	config.SetOrDefault(conf)

	return conf
}
<commit_msg>Remove ping db and retry, should only try once<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/parrot-translate/parrot/parrot-api/api"
	"github.com/parrot-translate/parrot/parrot-api/auth"
	"github.com/parrot-translate/parrot/parrot-api/config"
	"github.com/parrot-translate/parrot/parrot-api/datastore"
	"github.com/parrot-translate/parrot/parrot-api/logger"
	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
)

const ConfigFileLocation = "./parrot_api.yaml"

func init() {
	// Config log
	logrus.SetOutput(os.Stdout)
	logrus.SetFormatter(&logrus.TextFormatter{})
	logrus.SetLevel(logrus.InfoLevel)
}

// TODO: refactor this into cli to start server
func main() {
	conf := mustLoadConf()

	ds, err := datastore.NewDatastore(conf.DBName, conf.DBConn)
	if err != nil {
		logrus.Fatal(err)
	}
	defer ds.Close()

	if err = ds.Ping(); err != nil {
		logrus.Fatal(fmt.Sprintf("failed to ping datastore.\nerr: %s", err))
	}

	router := chi.NewRouter()
	router.Use(
		api.Cors,
		middleware.Recoverer,
		middleware.RequestID,
		middleware.RealIP,
		logger.Request,
		middleware.StripSlashes,
	)

	tp := auth.TokenProvider{Name: conf.AuthIssuer, SigningKey: []byte(conf.AuthSigningKey)}
	router.Mount("/api/v1/auth", auth.NewRouter(ds, tp))
	router.Mount("/api/v1", api.NewRouter(ds, tp))

	// config and init server
	bindInterface := ":" + conf.Port
	s := &http.Server{
		Addr:           bindInterface,
		Handler:        router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	logrus.Info(fmt.Sprintf("server listening on %s", bindInterface))

	logrus.Fatal(s.ListenAndServe())
}

func mustLoadConf() *config.AppConfig {
	var conf *config.AppConfig

	// Check if config file exists
	_, err := os.Stat(ConfigFileLocation)
	// If not exists, load from environment
	if os.IsNotExist(err) {
		conf, err = config.FromEnv()
		if err != nil {
			logrus.Fatal(err)
		}
	} else {
		// If exists, load from file
		data, err := ioutil.ReadFile(ConfigFileLocation)
		if err != nil {
			logrus.Fatal(err)
		}

		conf, err = config.FromYaml(data)
		if err != nil {
			logrus.Fatal(err)
		}
	}

	// Set defaults if no value set
	config.SetOrDefault(conf)

	return conf
}
<|endoftext|>


<commit_before>package response

const Status_ignore int = -1 // -1 忽略
const (
	Status_fail          int = iota // 0 失败
	Status_success                  // 1 成功
	Status_invalid_user             // 2 无效用户
	Status_inactive_user            // 3 未激活用户
	Status_invalid_token            // 4 无效token
	Status_token_timeout            // 5 token超时
	Status_no_permission            // 6 无权限
	Status_no_data                  // 7 无数据
	Status_disuse                   // 8 无用
	Status_duplication              // 9 重复
	Status_fail_server              // 10 服务器错误
	Status_fail_sql                 // 11 数据错误
	Status_fail_captcha             // 12 验证码错误
	Status_fail_request             // 13 请求错误
	Status_fail_meta                // 14 meta错误
	Status_fail_query               // 15 query错误
	Status_fail_order               // 16 order错误
	Status_fail_limit               // 17 limit错误
	Status_fail_illegal             // 18 非用户拥有
	status_fail_none5               // 19 (预留)
	Status_fail_arg0                // 20 第1个参数错误
	Status_fail_arg1                // 21 第2个参数错误
	Status_fail_arg2                // 22 第3个参数错误
	Status_fail_arg3                // 23 第4个参数错误
	Status_fail_arg4                // 24 第5个参数错误
	Status_fail_arg5                // 25 第6个参数错误
	Status_fail_arg6                // 26 第7个参数错误
	Status_fail_arg7                // 27 第8个参数错误
	Status_fail_arg8                // 28 第9个参数错误
	Status_fail_arg9                // 29 第10个参数错误
	Status_fail_arg                 // 30 参数错误
)

const (
	Status_forbidden = 403 // 403 服务拒绝
)

const (
	Status_service_close int = 500 // 500 服务器关闭
)

const (
	Status_out_workday int = 601 // 601 非工作时间
)
<commit_msg>feat: new status<commit_after>package response

const Status_ignore int = -1 // -1 忽略
const (
	Status_fail          int = iota // 0 失败
	Status_success                  // 1 成功
	Status_invalid_user             // 2 无效用户
	Status_inactive_user            // 3 未激活用户
	Status_invalid_token            // 4 无效token
	Status_token_timeout            // 5 token超时
	Status_no_permission            // 6 无权限
	Status_no_data                  // 7 无数据
	Status_disuse                   // 8 无用
	Status_duplication              // 9 重复
	Status_fail_server              // 10 服务器错误
	Status_fail_sql                 // 11 数据错误
	Status_fail_captcha             // 12 验证码错误
	Status_fail_request             // 13 请求错误
	Status_fail_meta                // 14 meta错误
	Status_fail_query               // 15 query错误
	Status_fail_order               // 16 order错误
	Status_fail_limit               // 17 limit错误
	Status_fail_illegal             // 18 非用户拥有
	status_fail_none5               // 19 (预留)
	Status_fail_arg0                // 20 第1个参数错误
	Status_fail_arg1                // 21 第2个参数错误
	Status_fail_arg2                // 22 第3个参数错误
	Status_fail_arg3                // 23 第4个参数错误
	Status_fail_arg4                // 24 第5个参数错误
	Status_fail_arg5                // 25 第6个参数错误
	Status_fail_arg6                // 26 第7个参数错误
	Status_fail_arg7                // 27 第8个参数错误
	Status_fail_arg8                // 28 第9个参数错误
	Status_fail_arg9                // 29 第10个参数错误
	Status_fail_arg                 // 30 参数错误
	Status_fail_arg11               // 31 第11个参数错误
	Status_fail_arg12               // 32 第12个参数错误
	Status_fail_arg13               // 33 第13个参数错误
	Status_fail_arg14               // 34 第14个参数错误
	Status_fail_arg15               // 35 第15个参数错误
	Status_fail_arg16               // 36 第16个参数错误
	Status_fail_arg17               // 37 第17个参数错误
	Status_fail_arg18               // 38 第18个参数错误
	Status_fail_arg19               // 39 第19个参数错误
)

const (
	Status_forbidden = 403 // 403 服务拒绝
)

const (
	Status_service_close int = 500 // 500 服务器关闭
)

const (
	Status_out_workday int = 601 // 601 非工作时间
)
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 rest

import (
	"strconv"
	"strings"
	"time"

	"github.com/namsral/flag"

	"go.ligato.io/cn-infra/v2/config"
	"go.ligato.io/cn-infra/v2/infra"
	access "go.ligato.io/cn-infra/v2/rpc/rest/security/model/access-security"
)

const (
	// DefaultHost is a host used by default
	DefaultHost = "0.0.0.0"
	// DefaultHTTPPort is a port used by default
	DefaultHTTPPort = "9191"
	// DefaultEndpoint 0.0.0.0:9191
	DefaultEndpoint = DefaultHost + ":" + DefaultHTTPPort
)

// Config is a configuration for HTTP server
// It is meant to be extended with security (TLS...)
type Config struct {
	// Disabled disables HTTP server.
	Disabled bool

	// Endpoint is an address of HTTP server
	Endpoint string

	// ReadTimeout is the maximum duration for reading the entire
	// request, including the body.
	//
	// Because ReadTimeout does not let Handlers make per-request
	// decisions on each request body's acceptable deadline or
	// upload rate, most users will prefer to use
	// ReadHeaderTimeout. It is valid to use them both.
	ReadTimeout time.Duration

	// ReadHeaderTimeout is the amount of time allowed to read
	// request headers. The connection's read deadline is reset
	// after reading the headers and the Handler can decide what
	// is considered too slow for the body.
	ReadHeaderTimeout time.Duration

	// WriteTimeout is the maximum duration before timing out
	// writes of the response. It is reset whenever a new
	// request's header is read. Like ReadTimeout, it does not
	// let Handlers make decisions on a per-request basis.
	WriteTimeout time.Duration

	// IdleTimeout is the maximum amount of time to wait for the
	// next request when keep-alives are enabled. If IdleTimeout
	// is zero, the value of ReadTimeout is used. If both are
	// zero, there is no timeout.
	IdleTimeout time.Duration

	// MaxHeaderBytes controls the maximum number of bytes the
	// server will read parsing the request header's keys and
	// values, including the request line. It does not limit the
	// size of the request body.
	// If zero, DefaultMaxHeaderBytes is used.
	MaxHeaderBytes int

	// ServerCertfile is path to the server certificate. If the certificate and corresponding
	// key (see config item below) is defined server uses HTTPS instead of HTTP.
	ServerCertfile string `json:"server-cert-file"`

	// ServerKeyfile is path to the server key file.
	ServerKeyfile string `json:"server-key-file"`

	// ClientBasicAuth is a slice of credentials in form "username:password"
	// used for basic HTTP authentication. If defined only authenticated users are allowed
	// to access the server.
	ClientBasicAuth []string `json:"client-basic-auth"`

	// ClientCerts is a slice of the root certificate authorities
	// that servers uses to verify a client certificate
	ClientCerts []string `json:"client-cert-files"`

	// EnableTokenAuth enables token authorization for HTTP requests
	EnableTokenAuth bool `json:"enable-token-auth"`

	// TokenExpiration set globaly for all user tokens
	TokenExpiration time.Duration `json:"token-expiration"`

	// Users laoded from config file
	Users []access.User `json:"users"`

	// Hash cost for password. High values take a lot of time to process.
	PasswordHashCost int `json:"password-hash-cost"`

	// SignKey is used to sign a token. Default value is used if not set.
	SignKey string `json:"sign-key"`

	RateLimiter *struct {
		// Limit defines rate limit for number of requests per second.
		Limit float64 `json:"limit"`

		// MaxBurst defines max number of requests in single burst.
		MaxBurst int `json:"burst"`
	} `json:"rate-limiter"`
}

// DefaultConfig returns new instance of config with default endpoint
func DefaultConfig() *Config {
	return &Config{
		Endpoint: DefaultEndpoint,
	}
}

// PluginConfig tries :
// - to load flag <plugin-name>-port and then FixConfig() just in case
// - alternatively <plugin-name>-config and then FixConfig() just in case
// - alternatively DefaultConfig()
func PluginConfig(pluginCfg config.PluginConfig, cfg *Config, pluginName infra.PluginName) error {
	portFlag := flag.Lookup(httpPortFlag(pluginName))

	if portFlag != nil && portFlag.Value != nil && portFlag.Value.String() != "" && cfg != nil {
		cfg.Endpoint = DefaultHost + ":" + portFlag.Value.String()
	}

	if pluginCfg != nil {
		_, err := pluginCfg.LoadValue(cfg)
		if err != nil {
			return err
		}
	}

	FixConfig(cfg)

	return nil
}

// FixConfig fill default values for empty fields
func FixConfig(cfg *Config) {
	if cfg == nil {
		return
	}
	if cfg.Endpoint == "" {
		cfg.Endpoint = DefaultEndpoint
	}
}

// GetPort parses suffix from endpoint & returns integer after last ":" (otherwise it returns 0)
func (cfg *Config) GetPort() int {
	if cfg.Endpoint != "" && cfg.Endpoint != ":" {
		index := strings.LastIndex(cfg.Endpoint, ":")
		if index >= 0 {
			port, err := strconv.Atoi(cfg.Endpoint[index+1:])
			if err == nil {
				return port
			}
		}
	}

	return 0
}

// UseHTTPS returns true if server certificate and key is defined.
func (cfg *Config) UseHTTPS() bool {
	return cfg.ServerCertfile != "" && cfg.ServerKeyfile != ""
}

// DeclareHTTPPortFlag declares http port (with usage & default value) a flag for a particular plugin name
func DeclareHTTPPortFlag(pluginName infra.PluginName, defaultPortOpts ...uint) {
	var defaultPort string
	if len(defaultPortOpts) > 0 {
		defaultPort = string(defaultPortOpts[0])
	} else {
		defaultPort = DefaultHTTPPort
	}

	plugNameUpper := strings.ToUpper(string(pluginName))

	usage := "Configure Agent' " + plugNameUpper + " server (port & timeouts); also set via '" +
		plugNameUpper + config.EnvSuffix + "' env variable."
	flag.String(httpPortFlag(pluginName), defaultPort, usage)
}

func httpPortFlag(pluginName infra.PluginName) string {
	return strings.ToLower(string(pluginName)) + "-port"
}
<commit_msg>Update config.go (#430)<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 rest

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/namsral/flag"

	"go.ligato.io/cn-infra/v2/config"
	"go.ligato.io/cn-infra/v2/infra"
	access "go.ligato.io/cn-infra/v2/rpc/rest/security/model/access-security"
)

const (
	// DefaultHost is a host used by default
	DefaultHost = "0.0.0.0"
	// DefaultHTTPPort is a port used by default
	DefaultHTTPPort = "9191"
	// DefaultEndpoint 0.0.0.0:9191
	DefaultEndpoint = DefaultHost + ":" + DefaultHTTPPort
)

// Config is a configuration for HTTP server
// It is meant to be extended with security (TLS...)
type Config struct {
	// Disabled disables HTTP server.
	Disabled bool

	// Endpoint is an address of HTTP server
	Endpoint string

	// ReadTimeout is the maximum duration for reading the entire
	// request, including the body.
	//
	// Because ReadTimeout does not let Handlers make per-request
	// decisions on each request body's acceptable deadline or
	// upload rate, most users will prefer to use
	// ReadHeaderTimeout. It is valid to use them both.
	ReadTimeout time.Duration

	// ReadHeaderTimeout is the amount of time allowed to read
	// request headers. The connection's read deadline is reset
	// after reading the headers and the Handler can decide what
	// is considered too slow for the body.
	ReadHeaderTimeout time.Duration

	// WriteTimeout is the maximum duration before timing out
	// writes of the response. It is reset whenever a new
	// request's header is read. Like ReadTimeout, it does not
	// let Handlers make decisions on a per-request basis.
	WriteTimeout time.Duration

	// IdleTimeout is the maximum amount of time to wait for the
	// next request when keep-alives are enabled. If IdleTimeout
	// is zero, the value of ReadTimeout is used. If both are
	// zero, there is no timeout.
	IdleTimeout time.Duration

	// MaxHeaderBytes controls the maximum number of bytes the
	// server will read parsing the request header's keys and
	// values, including the request line. It does not limit the
	// size of the request body.
	// If zero, DefaultMaxHeaderBytes is used.
	MaxHeaderBytes int

	// ServerCertfile is path to the server certificate. If the certificate and corresponding
	// key (see config item below) is defined server uses HTTPS instead of HTTP.
	ServerCertfile string `json:"server-cert-file"`

	// ServerKeyfile is path to the server key file.
	ServerKeyfile string `json:"server-key-file"`

	// ClientBasicAuth is a slice of credentials in form "username:password"
	// used for basic HTTP authentication. If defined only authenticated users are allowed
	// to access the server.
	ClientBasicAuth []string `json:"client-basic-auth"`

	// ClientCerts is a slice of the root certificate authorities
	// that servers uses to verify a client certificate
	ClientCerts []string `json:"client-cert-files"`

	// EnableTokenAuth enables token authorization for HTTP requests
	EnableTokenAuth bool `json:"enable-token-auth"`

	// TokenExpiration set globaly for all user tokens
	TokenExpiration time.Duration `json:"token-expiration"`

	// Users laoded from config file
	Users []access.User `json:"users"`

	// Hash cost for password. High values take a lot of time to process.
	PasswordHashCost int `json:"password-hash-cost"`

	// SignKey is used to sign a token. Default value is used if not set.
	SignKey string `json:"sign-key"`

	RateLimiter *struct {
		// Limit defines rate limit for number of requests per second.
		Limit float64 `json:"limit"`

		// MaxBurst defines max number of requests in single burst.
		MaxBurst int `json:"burst"`
	} `json:"rate-limiter"`
}

// DefaultConfig returns new instance of config with default endpoint
func DefaultConfig() *Config {
	return &Config{
		Endpoint: DefaultEndpoint,
	}
}

// PluginConfig tries :
// - to load flag <plugin-name>-port and then FixConfig() just in case
// - alternatively <plugin-name>-config and then FixConfig() just in case
// - alternatively DefaultConfig()
func PluginConfig(pluginCfg config.PluginConfig, cfg *Config, pluginName infra.PluginName) error {
	portFlag := flag.Lookup(httpPortFlag(pluginName))

	if portFlag != nil && portFlag.Value != nil && portFlag.Value.String() != "" && cfg != nil {
		cfg.Endpoint = DefaultHost + ":" + portFlag.Value.String()
	}

	if pluginCfg != nil {
		_, err := pluginCfg.LoadValue(cfg)
		if err != nil {
			return err
		}
	}

	FixConfig(cfg)

	return nil
}

// FixConfig fill default values for empty fields
func FixConfig(cfg *Config) {
	if cfg == nil {
		return
	}
	if cfg.Endpoint == "" {
		cfg.Endpoint = DefaultEndpoint
	}
}

// GetPort parses suffix from endpoint & returns integer after last ":" (otherwise it returns 0)
func (cfg *Config) GetPort() int {
	if cfg.Endpoint != "" && cfg.Endpoint != ":" {
		index := strings.LastIndex(cfg.Endpoint, ":")
		if index >= 0 {
			port, err := strconv.Atoi(cfg.Endpoint[index+1:])
			if err == nil {
				return port
			}
		}
	}

	return 0
}

// UseHTTPS returns true if server certificate and key is defined.
func (cfg *Config) UseHTTPS() bool {
	return cfg.ServerCertfile != "" && cfg.ServerKeyfile != ""
}

// DeclareHTTPPortFlag declares http port (with usage & default value) a flag for a particular plugin name
func DeclareHTTPPortFlag(pluginName infra.PluginName, defaultPortOpts ...uint) {
	var defaultPort string
	if len(defaultPortOpts) > 0 {
		defaultPort = fmt.Sprint(defaultPortOpts[0])
	} else {
		defaultPort = DefaultHTTPPort
	}

	plugNameUpper := strings.ToUpper(string(pluginName))

	usage := "Configure Agent' " + plugNameUpper + " server (port & timeouts); also set via '" +
		plugNameUpper + config.EnvSuffix + "' env variable."
	flag.String(httpPortFlag(pluginName), defaultPort, usage)
}

func httpPortFlag(pluginName infra.PluginName) string {
	return strings.ToLower(string(pluginName)) + "-port"
}
<|endoftext|>


<commit_before>// Package runtime is the micro runtime
package runtime

import (
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"text/tabwriter"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/config/cmd"
	"github.com/micro/go-micro/v2/runtime"
	rs "github.com/micro/go-micro/v2/runtime/service"
	"github.com/micro/micro/v2/runtime/scheduler"
)

const (
	// RunUsage message for the run command
	RunUsage = "Required usage: micro run [service] [version] [--source github.com/micro/services]"
	// KillUsage message for the kill command
	KillUsage = "Require usage: micro kill [service] [version]"
	// GetUsage message for micro get command
	GetUsage = "Require usage: micro ps [service] [version]"
	// CannotWatch message for the run command
	CannotWatch = "Cannot watch filesystem on this runtime"
)

func defaultEnv() []string {
	var env []string
	for _, evar := range os.Environ() {
		if strings.HasPrefix(evar, "MICRO_") {
			env = append(env, evar)
		}
	}

	return env
}

func runService(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// we need some args to run
	if ctx.Args().Len() == 0 {
		fmt.Println(RunUsage)
		return
	}

	// set and validate the name (arg 1)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		fmt.Println(RunUsage)
		return
	}

	// set the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	// load the runtime. The default runtime is ignored if running on the platform
	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
		// TODO @BEN: Proxy to platform
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	source := ctx.String("source")
	exec := []string{"go", "run", filepath.Join(source, name)}

	// Determine the filepath
	fp := filepath.Join(os.Getenv("GOPATH"), "src", source, name)

	// Find the filepath or `go run` will pull from git by default
	if r.String() == "local" && os.Chdir(fp) == nil {
		exec = []string{"go", "run", "."}

		// watch the filesystem for changes
		sched := scheduler.New(name, version, fp)
		if err := r.Init(runtime.WithScheduler(sched)); err != nil {
			fmt.Printf("Could not start scheduler: %v", err)
			return
		}
	}

	// start the runtimes
	if err := r.Start(); err != nil {
		fmt.Printf("Could not start: %v", err)
		return
	}

	// add environment variable passed in via cli
	environment := defaultEnv()
	for _, evar := range ctx.StringSlice("env") {
		for _, e := range strings.Split(evar, ",") {
			if len(e) > 0 {
				environment = append(environment, strings.TrimSpace(e))
			}
		}
	}

	// specify the options
	opts := []runtime.CreateOption{
		runtime.WithCommand(exec...),
		runtime.WithOutput(os.Stdout),
		runtime.WithEnv(environment),
	}

	// run the service
	service := &runtime.Service{
		Name:     name,
		Source:   source,
		Version:  version,
		Metadata: make(map[string]string),
	}
	if err := r.Create(service, opts...); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Started service %v: %v\n", name, source)

	// if local	 then register signal handlers
	if r.String() == "local" {
		shutdown := make(chan os.Signal, 1)
		signal.Notify(shutdown, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

		// wait for shutdown
		<-shutdown

		// delete service from runtime
		if err := r.Delete(service); err != nil {
			fmt.Println(err)
			return
		}

		if err := r.Stop(); err != nil {
			fmt.Println(err)
			return
		}
	}
}

func killService(ctx *cli.Context, srvOpts ...micro.Option) {
	// we need some args to run
	if ctx.Args().Len() == 0 {
		fmt.Println(RunUsage)
		return
	}

	// set and validate the name (arg 1)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		fmt.Println(RunUsage)
		return
	}

	// set the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
		// TODO @BEN: Proxy to platform
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	service := &runtime.Service{
		Name:    name,
		Version: version,
	}

	if err := r.Delete(service); err != nil {
		fmt.Println(err)
		return
	}
}

func getService(ctx *cli.Context, srvOpts ...micro.Option) {
	runType := ctx.Bool("runtime")

	// get and validate the name (arg 1, optional)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		return
	}

	// get the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
		// TODO @BEN: Proxy to platform
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	var list bool

	// zero args so list all
	if ctx.Args().Len() == 0 {
		list = true
	} else {
		// set name as first arg
		name = ctx.Args().Get(0)
		// set version as second arg
		if ctx.Args().Len() > 1 {
			version = ctx.Args().Get(1)
		}
	}

	var services []*runtime.Service
	var err error

	// return a list of services
	switch list {
	case true:
		// return the runtiem services
		if runType {
			services, err = r.Read(runtime.ReadType("runtime"))
		} else {
			// list all running services
			services, err = r.List()
		}
	// return one service
	default:
		// check if service name was passed in
		if len(name) == 0 {
			fmt.Println(GetUsage)
			return
		}

		// get service with name and version
		opts := []runtime.ReadOption{
			runtime.ReadService(name),
			runtime.ReadVersion(version),
		}

		// return the runtime services
		if runType {
			opts = append(opts, runtime.ReadType("runtime"))
		}

		// read the service
		services, err = r.Read(opts...)
	}

	// check the error
	if err != nil {
		fmt.Println(err)
		return
	}

	// make sure we return UNKNOWN when empty string is supplied
	parse := func(m string) string {
		if len(m) == 0 {
			return "n/a"
		}
		return m
	}

	// don't do anything if there's no services
	if len(services) == 0 {
		return
	}

	sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })

	writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight)
	fmt.Fprintln(writer, "NAME\tVERSION\tSOURCE\tSTATUS\tBUILD\tMETADATA")
	for _, service := range services {
		status := parse(service.Metadata["status"])
		if status == "error" {
			status = service.Metadata["error"]
		}

		fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\t%s\n",
			service.Name,
			parse(service.Version),
			parse(service.Source),
			status,
			parse(service.Metadata["build"]),
			fmt.Sprintf("owner=%s,group=%s", parse(service.Metadata["owner"]), parse(service.Metadata["group"])))
	}
	writer.Flush()
}
<commit_msg>Remove Comments<commit_after>// Package runtime is the micro runtime
package runtime

import (
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"sort"
	"strings"
	"syscall"
	"text/tabwriter"

	"github.com/micro/cli/v2"
	"github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/config/cmd"
	"github.com/micro/go-micro/v2/runtime"
	rs "github.com/micro/go-micro/v2/runtime/service"
	"github.com/micro/micro/v2/runtime/scheduler"
)

const (
	// RunUsage message for the run command
	RunUsage = "Required usage: micro run [service] [version] [--source github.com/micro/services]"
	// KillUsage message for the kill command
	KillUsage = "Require usage: micro kill [service] [version]"
	// GetUsage message for micro get command
	GetUsage = "Require usage: micro ps [service] [version]"
	// CannotWatch message for the run command
	CannotWatch = "Cannot watch filesystem on this runtime"
)

func defaultEnv() []string {
	var env []string
	for _, evar := range os.Environ() {
		if strings.HasPrefix(evar, "MICRO_") {
			env = append(env, evar)
		}
	}

	return env
}

func runService(ctx *cli.Context, srvOpts ...micro.Option) {
	// Init plugins
	for _, p := range Plugins() {
		p.Init(ctx)
	}

	// we need some args to run
	if ctx.Args().Len() == 0 {
		fmt.Println(RunUsage)
		return
	}

	// set and validate the name (arg 1)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		fmt.Println(RunUsage)
		return
	}

	// set the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	// load the runtime. The default runtime is ignored if running on the platform
	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	source := ctx.String("source")
	exec := []string{"go", "run", filepath.Join(source, name)}

	// Determine the filepath
	fp := filepath.Join(os.Getenv("GOPATH"), "src", source, name)

	// Find the filepath or `go run` will pull from git by default
	if r.String() == "local" && os.Chdir(fp) == nil {
		exec = []string{"go", "run", "."}

		// watch the filesystem for changes
		sched := scheduler.New(name, version, fp)
		if err := r.Init(runtime.WithScheduler(sched)); err != nil {
			fmt.Printf("Could not start scheduler: %v", err)
			return
		}
	}

	// start the runtimes
	if err := r.Start(); err != nil {
		fmt.Printf("Could not start: %v", err)
		return
	}

	// add environment variable passed in via cli
	environment := defaultEnv()
	for _, evar := range ctx.StringSlice("env") {
		for _, e := range strings.Split(evar, ",") {
			if len(e) > 0 {
				environment = append(environment, strings.TrimSpace(e))
			}
		}
	}

	// specify the options
	opts := []runtime.CreateOption{
		runtime.WithCommand(exec...),
		runtime.WithOutput(os.Stdout),
		runtime.WithEnv(environment),
	}

	// run the service
	service := &runtime.Service{
		Name:     name,
		Source:   source,
		Version:  version,
		Metadata: make(map[string]string),
	}
	if err := r.Create(service, opts...); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("Started service %v: %v\n", name, source)

	// if local	 then register signal handlers
	if r.String() == "local" {
		shutdown := make(chan os.Signal, 1)
		signal.Notify(shutdown, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

		// wait for shutdown
		<-shutdown

		// delete service from runtime
		if err := r.Delete(service); err != nil {
			fmt.Println(err)
			return
		}

		if err := r.Stop(); err != nil {
			fmt.Println(err)
			return
		}
	}
}

func killService(ctx *cli.Context, srvOpts ...micro.Option) {
	// we need some args to run
	if ctx.Args().Len() == 0 {
		fmt.Println(RunUsage)
		return
	}

	// set and validate the name (arg 1)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		fmt.Println(RunUsage)
		return
	}

	// set the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	service := &runtime.Service{
		Name:    name,
		Version: version,
	}

	if err := r.Delete(service); err != nil {
		fmt.Println(err)
		return
	}
}

func getService(ctx *cli.Context, srvOpts ...micro.Option) {
	runType := ctx.Bool("runtime")

	// get and validate the name (arg 1, optional)
	name := ctx.Args().Get(0)
	if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "/") {
		return
	}

	// get the version (arg 2, optional)
	version := "latest"
	if ctx.Args().Len() > 1 {
		version = ctx.Args().Get(1)
	}

	var r runtime.Runtime
	if ctx.Bool("platform") {
		r = rs.NewRuntime()
	} else {
		r = *cmd.DefaultCmd.Options().Runtime
	}

	var list bool

	// zero args so list all
	if ctx.Args().Len() == 0 {
		list = true
	} else {
		// set name as first arg
		name = ctx.Args().Get(0)
		// set version as second arg
		if ctx.Args().Len() > 1 {
			version = ctx.Args().Get(1)
		}
	}

	var services []*runtime.Service
	var err error

	// return a list of services
	switch list {
	case true:
		// return the runtiem services
		if runType {
			services, err = r.Read(runtime.ReadType("runtime"))
		} else {
			// list all running services
			services, err = r.List()
		}
	// return one service
	default:
		// check if service name was passed in
		if len(name) == 0 {
			fmt.Println(GetUsage)
			return
		}

		// get service with name and version
		opts := []runtime.ReadOption{
			runtime.ReadService(name),
			runtime.ReadVersion(version),
		}

		// return the runtime services
		if runType {
			opts = append(opts, runtime.ReadType("runtime"))
		}

		// read the service
		services, err = r.Read(opts...)
	}

	// check the error
	if err != nil {
		fmt.Println(err)
		return
	}

	// make sure we return UNKNOWN when empty string is supplied
	parse := func(m string) string {
		if len(m) == 0 {
			return "n/a"
		}
		return m
	}

	// don't do anything if there's no services
	if len(services) == 0 {
		return
	}

	sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })

	writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight)
	fmt.Fprintln(writer, "NAME\tVERSION\tSOURCE\tSTATUS\tBUILD\tMETADATA")
	for _, service := range services {
		status := parse(service.Metadata["status"])
		if status == "error" {
			status = service.Metadata["error"]
		}

		fmt.Fprintf(writer, "%s\t%s\t%s\t%s\t%s\t%s\n",
			service.Name,
			parse(service.Version),
			parse(service.Source),
			status,
			parse(service.Metadata["build"]),
			fmt.Sprintf("owner=%s,group=%s", parse(service.Metadata["owner"]), parse(service.Metadata["group"])))
	}
	writer.Flush()
}
<|endoftext|>


<commit_before>// Copyright (C) 2015  The GoHBase Authors.  All rights reserved.
// This file is part of GoHBase.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package region contains data structures to represent HBase regions.
package region

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/tsuna/gohbase/hrpc"
	"github.com/tsuna/gohbase/pb"
)

// Info describes a region.
type Info struct {
	// Table name.
	Table []byte

	// Name.
	Name []byte

	// StartKey
	StartKey []byte

	// StopKey.
	StopKey []byte

	// The attributes before this mutex are supposed to be immutable.
	// The attributes defined below can be changed and accesses must
	// be protected with this mutex.
	m sync.Mutex

	// Client.
	Client hrpc.RegionClient

	// Once a region becomes unreachable, this channel is created, and any
	// functions that wish to be notified when the region becomes available
	// again can read from this channel, which will be closed when the region
	// is available again
	available chan struct{}
}

// infoFromCell parses a KeyValue from the meta table and creates the
// corresponding Info object.
func infoFromCell(cell *pb.Cell) (*Info, error) {
	value := cell.Value
	if len(value) == 0 {
		return nil, fmt.Errorf("empty value in %q", cell)
	} else if value[0] != 'P' {
		return nil, fmt.Errorf("unsupported region info version %d in %q",
			value[0], cell)
	}
	const pbufMagic = 1346524486 // 4 bytes: "PBUF"
	magic := binary.BigEndian.Uint32(value)
	if magic != pbufMagic {
		return nil, fmt.Errorf("invalid magic number in %q", cell)
	}
	regInfo := &pb.RegionInfo{}
	err := proto.UnmarshalMerge(value[4:len(value)-4], regInfo)
	if err != nil {
		return nil, fmt.Errorf("failed to decode %q: %s", cell, err)
	}
	return &Info{
		Table:    regInfo.TableName.Qualifier,
		Name:     cell.Row,
		StartKey: regInfo.StartKey,
		StopKey:  regInfo.EndKey,
	}, nil
}

// ParseRegionInfo parses the contents of a row from the meta table.
// It's guaranteed to return a region info and a host/port OR return an error.
func ParseRegionInfo(metaRow *pb.GetResponse) (
	*Info, string, uint16, error) {

	var reg *Info
	var host string
	var port uint16

	for _, cell := range metaRow.Result.Cell {
		switch string(cell.Qualifier) {
		case "regioninfo":
			var err error
			reg, err = infoFromCell(cell)
			if err != nil {
				return nil, "", 0, err
			}
		case "server":
			value := cell.Value
			if len(value) == 0 {
				continue // Empty during NSRE.
			}
			colon := bytes.IndexByte(value, ':')
			if colon < 1 { // Colon can't be at the beginning.
				return nil, "", 0,
					fmt.Errorf("broken meta: no colon found in info:server %q", cell)
			}
			host = string(value[:colon])
			portU64, err := strconv.ParseUint(string(value[colon+1:]), 10, 16)
			if err != nil {
				return nil, "", 0, err
			}
			port = uint16(portU64)
		default:
			// Other kinds of qualifiers: ignore them.
			// TODO: If this is the parent of a split region, there are two other
			// KVs that could be useful: `info:splitA' and `info:splitB'.
			// Need to investigate whether we can use those as a hint to update our
			// regions_cache with the daughter regions of the split.
		}
	}

	if reg == nil {
		// There was no region in the row in meta, this is really not
		// expected.
		err := fmt.Errorf("Meta seems to be broken, there was no region in %s",
			metaRow)
		return nil, "", 0, err
	} else if port == 0 { // Either both `host' and `port' are set, or both aren't.
		return nil, "", 0, fmt.Errorf("Meta doesn't have a server location in %s",
			metaRow)
	}

	return reg, host, port, nil
}

// IsUnavailable returns true if this region has been marked as unavailable.
func (i *Info) IsUnavailable() bool {
	i.m.Lock()
	res := i.available != nil
	i.m.Unlock()
	return res
}

// GetAvailabilityChan returns a channel that can be used to wait on for
// notification that a connection to this region has been reestablished.
// If this region is not marked as unavailable, nil will be returned.
func (i *Info) GetAvailabilityChan() <-chan struct{} {
	i.m.Lock()
	ch := i.available
	i.m.Unlock()
	return ch
}

// MarkUnavailable will mark this region as unavailable, by creating the struct
// returned by GetAvailabilityChan. If this region was marked as available
// before this, true will be returned.
func (i *Info) MarkUnavailable() bool {
	created := false
	i.m.Lock()
	if i.available == nil {
		i.available = make(chan struct{})
		created = true
	}
	i.m.Unlock()
	return created
}

// MarkAvailable will mark this region as available again, by closing the struct
// returned by GetAvailabilityChan
func (i *Info) MarkAvailable() {
	i.m.Lock()
	ch := i.available
	i.available = nil
	close(ch)
	i.m.Unlock()
}

func (i *Info) String() string {
	return fmt.Sprintf("*region.Info{Table: %q, Name: %q, StopKey: %q}",
		i.Table, i.Name, i.StopKey)
}

// GetName returns region name
func (i *Info) GetName() []byte {
	return i.Name
}

// GetStopKey return region stop key
func (i *Info) GetStopKey() []byte {
	return i.StopKey
}

// GetStartKey return region start key
func (i *Info) GetStartKey() []byte {
	return i.StartKey
}

// GetTable returns region table
func (i *Info) GetTable() []byte {
	return i.Table
}

// GetClient returns region client
func (i *Info) GetClient() hrpc.RegionClient {
	return i.Client
}

// SetClient sets region client
func (i *Info) SetClient(c hrpc.RegionClient) {
	i.Client = c
}

// CompareGeneric is the same thing as Compare but for interface{}.
func CompareGeneric(a, b interface{}) int {
	return Compare(a.([]byte), b.([]byte))
}

// Compare compares two region names.
// We can't just use bytes.Compare() because it doesn't play nicely
// with the way META keys are built as the first region has an empty start
// key.  Let's assume we know about those 2 regions in our cache:
//   .META.,,1
//   tableA,,1273018455182
// We're given an RPC to execute on "tableA", row "\x00" (1 byte row key
// containing a 0).  If we use Compare() to sort the entries in the cache,
// when we search for the entry right before "tableA,\000,:"
// we'll erroneously find ".META.,,1" instead of the entry for first
// region of "tableA".
//
// Since this scheme breaks natural ordering, we need this comparator to
// implement a special version of comparison to handle this scenario.
func Compare(a, b []byte) int {
	var length int
	if la, lb := len(a), len(b); la < lb {
		length = la
	} else {
		length = lb
	}
	// Reminder: region names are of the form:
	//   table_name,start_key,timestamp[.MD5.]
	// First compare the table names.
	var i int
	for i = 0; i < length; i++ {
		ai := a[i]    // Saves one pointer deference every iteration.
		bi := b[i]    // Saves one pointer deference every iteration.
		if ai != bi { // The name of the tables differ.
			if ai == ',' {
				return -1001 // `a' has a smaller table name.  a < b
			} else if bi == ',' {
				return 1001 // `b' has a smaller table name.  a > b
			}
			return int(ai) - int(bi)
		}
		if ai == ',' { // Remember: at this point ai == bi.
			break // We're done comparing the table names.  They're equal.
		}
	}

	// Now find the last comma in both `a' and `b'.  We need to start the
	// search from the end as the row key could have an arbitrary number of
	// commas and we don't know its length.
	aComma := findCommaFromEnd(a, i)
	bComma := findCommaFromEnd(b, i)
	// If either `a' or `b' is followed immediately by another comma, then
	// they are the first region (it's the empty start key).
	i++ // No need to check against `length', there MUST be more bytes.

	// Compare keys.
	var firstComma int
	if aComma < bComma {
		firstComma = aComma
	} else {
		firstComma = bComma
	}
	for ; i < firstComma; i++ {
		ai := a[i]
		bi := b[i]
		if ai != bi { // The keys differ.
			return int(ai) - int(bi)
		}
	}
	if aComma < bComma {
		return -1002 // `a' has a shorter key.  a < b
	} else if bComma < aComma {
		return 1002 // `b' has a shorter key.  a > b
	}

	// Keys have the same length and have compared identical.  Compare the
	// rest, which essentially means: use start code as a tie breaker.
	for ; /*nothing*/ i < length; i++ {
		ai := a[i]
		bi := b[i]
		if ai != bi { // The start codes differ.
			return int(ai) - int(bi)
		}
	}

	return len(a) - len(b)
}

// Because there is no `LastIndexByte()' in the standard `bytes' package.
func findCommaFromEnd(b []byte, offset int) int {
	for i := len(b) - 1; i > offset; i-- {
		if b[i] == ',' {
			return i
		}
	}
	panic(fmt.Errorf("No comma found in %q after offset %d", b, offset))
}
<commit_msg>region: Make `GetClient()`/`SetClient()` thread-safe.<commit_after>// Copyright (C) 2015  The GoHBase Authors.  All rights reserved.
// This file is part of GoHBase.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package region contains data structures to represent HBase regions.
package region

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strconv"
	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/tsuna/gohbase/hrpc"
	"github.com/tsuna/gohbase/pb"
)

// Info describes a region.
type Info struct {
	// Table name.
	Table []byte

	// Name.
	Name []byte

	// StartKey
	StartKey []byte

	// StopKey.
	StopKey []byte

	// The attributes before this mutex are supposed to be immutable.
	// The attributes defined below can be changed and accesses must
	// be protected with this mutex.
	m sync.Mutex

	// Client.
	Client hrpc.RegionClient

	// Once a region becomes unreachable, this channel is created, and any
	// functions that wish to be notified when the region becomes available
	// again can read from this channel, which will be closed when the region
	// is available again
	available chan struct{}
}

// infoFromCell parses a KeyValue from the meta table and creates the
// corresponding Info object.
func infoFromCell(cell *pb.Cell) (*Info, error) {
	value := cell.Value
	if len(value) == 0 {
		return nil, fmt.Errorf("empty value in %q", cell)
	} else if value[0] != 'P' {
		return nil, fmt.Errorf("unsupported region info version %d in %q",
			value[0], cell)
	}
	const pbufMagic = 1346524486 // 4 bytes: "PBUF"
	magic := binary.BigEndian.Uint32(value)
	if magic != pbufMagic {
		return nil, fmt.Errorf("invalid magic number in %q", cell)
	}
	regInfo := &pb.RegionInfo{}
	err := proto.UnmarshalMerge(value[4:len(value)-4], regInfo)
	if err != nil {
		return nil, fmt.Errorf("failed to decode %q: %s", cell, err)
	}
	return &Info{
		Table:    regInfo.TableName.Qualifier,
		Name:     cell.Row,
		StartKey: regInfo.StartKey,
		StopKey:  regInfo.EndKey,
	}, nil
}

// ParseRegionInfo parses the contents of a row from the meta table.
// It's guaranteed to return a region info and a host/port OR return an error.
func ParseRegionInfo(metaRow *pb.GetResponse) (
	*Info, string, uint16, error) {

	var reg *Info
	var host string
	var port uint16

	for _, cell := range metaRow.Result.Cell {
		switch string(cell.Qualifier) {
		case "regioninfo":
			var err error
			reg, err = infoFromCell(cell)
			if err != nil {
				return nil, "", 0, err
			}
		case "server":
			value := cell.Value
			if len(value) == 0 {
				continue // Empty during NSRE.
			}
			colon := bytes.IndexByte(value, ':')
			if colon < 1 { // Colon can't be at the beginning.
				return nil, "", 0,
					fmt.Errorf("broken meta: no colon found in info:server %q", cell)
			}
			host = string(value[:colon])
			portU64, err := strconv.ParseUint(string(value[colon+1:]), 10, 16)
			if err != nil {
				return nil, "", 0, err
			}
			port = uint16(portU64)
		default:
			// Other kinds of qualifiers: ignore them.
			// TODO: If this is the parent of a split region, there are two other
			// KVs that could be useful: `info:splitA' and `info:splitB'.
			// Need to investigate whether we can use those as a hint to update our
			// regions_cache with the daughter regions of the split.
		}
	}

	if reg == nil {
		// There was no region in the row in meta, this is really not
		// expected.
		err := fmt.Errorf("Meta seems to be broken, there was no region in %s",
			metaRow)
		return nil, "", 0, err
	} else if port == 0 { // Either both `host' and `port' are set, or both aren't.
		return nil, "", 0, fmt.Errorf("Meta doesn't have a server location in %s",
			metaRow)
	}

	return reg, host, port, nil
}

// IsUnavailable returns true if this region has been marked as unavailable.
func (i *Info) IsUnavailable() bool {
	i.m.Lock()
	res := i.available != nil
	i.m.Unlock()
	return res
}

// GetAvailabilityChan returns a channel that can be used to wait on for
// notification that a connection to this region has been reestablished.
// If this region is not marked as unavailable, nil will be returned.
func (i *Info) GetAvailabilityChan() <-chan struct{} {
	i.m.Lock()
	ch := i.available
	i.m.Unlock()
	return ch
}

// MarkUnavailable will mark this region as unavailable, by creating the struct
// returned by GetAvailabilityChan. If this region was marked as available
// before this, true will be returned.
func (i *Info) MarkUnavailable() bool {
	created := false
	i.m.Lock()
	if i.available == nil {
		i.available = make(chan struct{})
		created = true
	}
	i.m.Unlock()
	return created
}

// MarkAvailable will mark this region as available again, by closing the struct
// returned by GetAvailabilityChan
func (i *Info) MarkAvailable() {
	i.m.Lock()
	ch := i.available
	i.available = nil
	close(ch)
	i.m.Unlock()
}

func (i *Info) String() string {
	return fmt.Sprintf("*region.Info{Table: %q, Name: %q, StopKey: %q}",
		i.Table, i.Name, i.StopKey)
}

// GetName returns region name
func (i *Info) GetName() []byte {
	return i.Name
}

// GetStopKey return region stop key
func (i *Info) GetStopKey() []byte {
	return i.StopKey
}

// GetStartKey return region start key
func (i *Info) GetStartKey() []byte {
	return i.StartKey
}

// GetTable returns region table
func (i *Info) GetTable() []byte {
	return i.Table
}

// GetClient returns region client
func (i *Info) GetClient() hrpc.RegionClient {
	i.m.Lock()
	c := i.Client
	i.m.Unlock()
	return c
}

// SetClient sets region client
func (i *Info) SetClient(c hrpc.RegionClient) {
	i.m.Lock()
	i.Client = c
	i.m.Unlock()
}

// CompareGeneric is the same thing as Compare but for interface{}.
func CompareGeneric(a, b interface{}) int {
	return Compare(a.([]byte), b.([]byte))
}

// Compare compares two region names.
// We can't just use bytes.Compare() because it doesn't play nicely
// with the way META keys are built as the first region has an empty start
// key.  Let's assume we know about those 2 regions in our cache:
//   .META.,,1
//   tableA,,1273018455182
// We're given an RPC to execute on "tableA", row "\x00" (1 byte row key
// containing a 0).  If we use Compare() to sort the entries in the cache,
// when we search for the entry right before "tableA,\000,:"
// we'll erroneously find ".META.,,1" instead of the entry for first
// region of "tableA".
//
// Since this scheme breaks natural ordering, we need this comparator to
// implement a special version of comparison to handle this scenario.
func Compare(a, b []byte) int {
	var length int
	if la, lb := len(a), len(b); la < lb {
		length = la
	} else {
		length = lb
	}
	// Reminder: region names are of the form:
	//   table_name,start_key,timestamp[.MD5.]
	// First compare the table names.
	var i int
	for i = 0; i < length; i++ {
		ai := a[i]    // Saves one pointer deference every iteration.
		bi := b[i]    // Saves one pointer deference every iteration.
		if ai != bi { // The name of the tables differ.
			if ai == ',' {
				return -1001 // `a' has a smaller table name.  a < b
			} else if bi == ',' {
				return 1001 // `b' has a smaller table name.  a > b
			}
			return int(ai) - int(bi)
		}
		if ai == ',' { // Remember: at this point ai == bi.
			break // We're done comparing the table names.  They're equal.
		}
	}

	// Now find the last comma in both `a' and `b'.  We need to start the
	// search from the end as the row key could have an arbitrary number of
	// commas and we don't know its length.
	aComma := findCommaFromEnd(a, i)
	bComma := findCommaFromEnd(b, i)
	// If either `a' or `b' is followed immediately by another comma, then
	// they are the first region (it's the empty start key).
	i++ // No need to check against `length', there MUST be more bytes.

	// Compare keys.
	var firstComma int
	if aComma < bComma {
		firstComma = aComma
	} else {
		firstComma = bComma
	}
	for ; i < firstComma; i++ {
		ai := a[i]
		bi := b[i]
		if ai != bi { // The keys differ.
			return int(ai) - int(bi)
		}
	}
	if aComma < bComma {
		return -1002 // `a' has a shorter key.  a < b
	} else if bComma < aComma {
		return 1002 // `b' has a shorter key.  a > b
	}

	// Keys have the same length and have compared identical.  Compare the
	// rest, which essentially means: use start code as a tie breaker.
	for ; /*nothing*/ i < length; i++ {
		ai := a[i]
		bi := b[i]
		if ai != bi { // The start codes differ.
			return int(ai) - int(bi)
		}
	}

	return len(a) - len(b)
}

// Because there is no `LastIndexByte()' in the standard `bytes' package.
func findCommaFromEnd(b []byte, offset int) int {
	for i := len(b) - 1; i > offset; i-- {
		if b[i] == ',' {
			return i
		}
	}
	panic(fmt.Errorf("No comma found in %q after offset %d", b, offset))
}
<|endoftext|>