<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/HewlettPackard/oneview-golang/ov"
)

func main() {
	var (
		ClientOV *ov.OVClient
	)
	apiversion, _ := strconv.Atoi(os.Getenv("ONEVIEW_APIVERSION"))
	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		apiversion,
		"*")

	// Get all the connection template available
	connTemplate, err := ovc.GetConnectionTemplate("", "", "", "")
	if err != nil {
		panic(err)
	} else {
		fmt.Println("#-----Got Connection Templates-----#")
		for i := range connTemplate.Members {
			fmt.Println(connTemplate.Members[i])
		}
	}

	testName := connTemplate.Members[0].Name
	// Get connection template by name
	connTemplate2, err := ovc.GetConnectionTemplateByName(testName)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("#-----Got Connection Template by name-----#")
		fmt.Println(connTemplate2)
	}

	// Get the default connection template
	default_connection, err := ovc.GetDefaultConnectionTemplate()
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("#-----Got Default Connection Template-----#")
		fmt.Println(default_connection)
	}

	Bandwidthoptions := ov.BandwidthType{
		MaximumBandwidth: 8000,
		TypicalBandwidth: 2500,
	}

	templateoptions := ov.ConnectionTemplate{
		Bandwidth: Bandwidthoptions,
		Name:      testName,
		Type:      "connection-template",
	}
	fmt.Println(templateoptions)
	// updating Connection Template
	// specific id can be given for update.
	id := strings.Split(connTemplate2.URI.String(), "/")[3] // eg: id = 063f055c-2cda-4420-be9d-024d609bc534
	template, err := ovc.UpdateConnectionTemplate(id, templateoptions)
	if err != nil {
		panic(err)
	} else {
		fmt.Println("Connection Template has been updated with maximum bandwidth: " + strconv.Itoa(template.Bandwidth.MaximumBandwidth))
	}

	// revert back the changes
	Bandwidthoption := ov.BandwidthType{
		MaximumBandwidth: 10000,
		TypicalBandwidth: 2500,
	}

	templateoption := ov.ConnectionTemplate{
		Bandwidth: Bandwidthoption,
		Name:      testName,
		Type:      "connection-template",
	}
	fmt.Println(templateoption)
	template2, err := ovc.UpdateConnectionTemplate(id, templateoption)
	if err != nil {
		panic(err)
	} else {
		fmt.Println("Connection Template has been updated with maximum bandwidth: " + strconv.Itoa(template2.Bandwidth.MaximumBandwidth))
	}

}
<commit_msg>added example<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/HewlettPackard/oneview-golang/ov"
)

func main() {
	var (
		ClientOV *ov.OVClient
	)
	apiversion, _ := strconv.Atoi(os.Getenv("ONEVIEW_APIVERSION"))
	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		apiversion,
		"*")

	// Get all the connection template available
	connTemplate, err := ovc.GetConnectionTemplate("", "", "", "")
	if err != nil {
		panic(err)
	} else {
		fmt.Println("#-----Got Connection Templates-----#")
		for i := range connTemplate.Members {
			fmt.Println(connTemplate.Members[i])
		}
	}

	testName := connTemplate.Members[0].Name
	// Get connection template by name
	connTemplate2, err := ovc.GetConnectionTemplateByName(testName)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("#-----Got Connection Template by name-----#")
		fmt.Println(connTemplate2)
	}

	templatebyUri, err := ovc.GetConnectionTemplateByURI(connTemplate2.URI)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("#-----Got Connection Template by URI-----#")
		fmt.Println(templatebyUri)
	}

	// Get the default connection template
	default_connection, err := ovc.GetDefaultConnectionTemplate()
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("#-----Got Default Connection Template-----#")
		fmt.Println(default_connection)
	}

	Bandwidthoptions := ov.BandwidthType{
		MaximumBandwidth: 8000,
		TypicalBandwidth: 2500,
	}

	templateoptions := ov.ConnectionTemplate{
		Bandwidth: Bandwidthoptions,
		Name:      testName,
		Type:      "connection-template",
	}
	fmt.Println(templateoptions)
	// updating Connection Template
	// specific id can be given for update.
	id := strings.Split(connTemplate2.URI.String(), "/")[3] // eg: id = 063f055c-2cda-4420-be9d-024d609bc534
	template, err := ovc.UpdateConnectionTemplate(id, templateoptions)
	if err != nil {
		panic(err)
	} else {
		fmt.Println("Connection Template has been updated with maximum bandwidth: " + strconv.Itoa(template.Bandwidth.MaximumBandwidth))
	}

	// revert back the changes
	Bandwidthoption := ov.BandwidthType{
		MaximumBandwidth: 10000,
		TypicalBandwidth: 2500,
	}

	templateoption := ov.ConnectionTemplate{
		Bandwidth: Bandwidthoption,
		Name:      testName,
		Type:      "connection-template",
	}
	fmt.Println(templateoption)
	template2, err := ovc.UpdateConnectionTemplate(id, templateoption)
	if err != nil {
		panic(err)
	} else {
		fmt.Println("Connection Template has been updated with maximum bandwidth: " + strconv.Itoa(template2.Bandwidth.MaximumBandwidth))
	}

}
<|endoftext|>


<commit_before>package main

import (
	"testing"

	cpb "github.com/google/kne/proto/controller"
	"github.com/h-fam/errdiff"
)

func TestNewDeployment(t *testing.T) {
	tests := []struct {
		desc    string
		req     *cpb.CreateClusterRequest
		wantErr string
	}{{
		desc: "full request spec",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					IpCount: 100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{},
			},
		},
	}, {
		desc: "bad metallb manifest dir",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					ManifestDir: "/foo",
					IpCount:     100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{},
			},
		},
		wantErr: "failed to validate path \"/foo\"",
	}, {
		desc:    "empty kind spec",
		req:     &cpb.CreateClusterRequest{},
		wantErr: "cluster type not supported:",
	}, {
		desc: "empty ingress spec",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{
					ManifestDir: "../../bar",
				},
			},
		},
		wantErr: "ingress spec not supported:",
	}, {
		desc: "bad meshnet manifest dir",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					IpCount: 100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{
					ManifestDir: "/foo",
				},
			},
		},
		wantErr: "failed to validate path \"/foo\"",
	}}
	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			d, err := newDeployment(tt.req)
			if s := errdiff.Substring(err, tt.wantErr); s != "" {
				t.Fatalf("unexpected error: %s", s)
			}
			if err != nil {
				return
			}
			t.Log(d)
		})
	}

}
<commit_msg>add cni type not supported test<commit_after>package main

import (
	"testing"

	cpb "github.com/google/kne/proto/controller"
	"github.com/h-fam/errdiff"
)

func TestNewDeployment(t *testing.T) {
	tests := []struct {
		desc    string
		req     *cpb.CreateClusterRequest
		wantErr string
	}{{
		desc: "full request spec",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					IpCount: 100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{},
			},
		},
	}, {
		desc: "bad metallb manifest dir",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					ManifestDir: "/foo",
					IpCount:     100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{},
			},
		},
		wantErr: "failed to validate path \"/foo\"",
	}, {
		desc:    "empty kind spec",
		req:     &cpb.CreateClusterRequest{},
		wantErr: "cluster type not supported:",
	}, {
		desc: "empty ingress spec",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{
					ManifestDir: "../../bar",
				},
			},
		},
		wantErr: "ingress spec not supported:",
	}, {
		desc: "bad meshnet manifest dir",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					IpCount: 100,
				},
			},
			CniSpec: &cpb.CreateClusterRequest_Meshnet{
				&cpb.MeshnetSpec{
					ManifestDir: "/foo",
				},
			},
		},
		wantErr: "failed to validate path \"/foo\"",
	}, {
		desc: "cni spec empty",
		req: &cpb.CreateClusterRequest{
			ClusterSpec: &cpb.CreateClusterRequest_Kind{
				&cpb.KindSpec{
					Name:    "kne",
					Recycle: true,
					Version: "0.11.1",
					Image:   "kindest/node:v1.22.1",
				},
			},
			IngressSpec: &cpb.CreateClusterRequest_Metallb{
				&cpb.MetallbSpec{
					ManifestDir: "/usr/local",
					IpCount:     100,
				},
			},
		},
		wantErr: "cni type not supported:",
	}}
	for _, tt := range tests {
		t.Run(tt.desc, func(t *testing.T) {
			d, err := newDeployment(tt.req)
			if s := errdiff.Substring(err, tt.wantErr); s != "" {
				t.Fatalf("unexpected error: %s", s)
			}
			if err != nil {
				return
			}
			t.Log(d)
		})
	}

}
<|endoftext|>


<commit_before>package auth

import (
	"errors"
	"fmt"

	"github.com/chrisolsen/ae"
	"github.com/chrisolsen/ae/attachment"
	"golang.org/x/net/context"
	"google.golang.org/appengine/datastore"
)

const (
	AccountStateUnconfirmed = iota
	AccountStateConfirmed
	AccountStateSuspended
	AccountStateTerminated
)

// Account model
type Account struct {
	ae.Model

	FirstName string `json:"firstName" datastore:",noindex"`
	LastName  string `json:"lastName" datastore:",noindex"`
	Gender    string `json:"gender" datastore:",noindex"`
	Locale    string `json:"locale" datastore:",noindex"`
	Location  string `json:"location" datastore:",noindex"`
	Name      string `json:"name" datastore:",noindex"`
	Timezone  int    `json:"timezone" datastore:",noindex"`
	Email     string `json:"email"`
	State     int    `json:"-"`

	Photo attachment.File `json:"photo"`
}

// AccountStore .
type AccountStore struct {
	ae.Store
}

// NewAccountStore returns a setup AccountStore
func NewAccountStore() AccountStore {
	s := AccountStore{}
	s.TableName = "accounts"
	return s
}

// Create creates a new account
func (s *AccountStore) Create(c context.Context, creds *Credentials, account *Account) (*datastore.Key, error) {
	var err error
	var accountKey *datastore.Key
	var cStore = NewCredentialStore()
	err = datastore.RunInTransaction(c, func(tc context.Context) error {
		accountKey, err = s.Store.Create(tc, account, nil)
		if err != nil {
			return fmt.Errorf("failed to create account: %v", err)
		}

		_, err = cStore.Create(tc, creds, accountKey)
		if err != nil {
			return fmt.Errorf("failed to create credentials: %v", err)
		}

		return nil
	}, &datastore.TransactionOptions{XG: true})

	return accountKey, nil
}

// GetAccountKeyByCredentials fetches the account matching the auth provider credentials
func (s *AccountStore) GetAccountKeyByCredentials(c context.Context, creds *Credentials) (*datastore.Key, error) {
	var err error
	cstore := NewCredentialStore()
	// on initial signup the account key will exist within the credentials
	if creds.AccountKey != nil {
		var accountCreds []*Credentials
		_, err = cstore.GetByAccount(c, creds.AccountKey, &accountCreds)
		if err != nil {
			return nil, fmt.Errorf("failed to find credentials by parent account: %v", err)
		}
		// validate credentials
		for _, ac := range accountCreds {
			if ac.ProviderID == creds.ProviderID && ac.ProviderName == creds.ProviderName {
				return creds.AccountKey, nil
			}
		}
		return nil, errors.New("no matching credentials found for account")
	}

	// by provider
	if len(creds.ProviderID) > 0 {
		return cstore.GetAccountKeyByProvider(c, creds)
	}

	// by username
	var userNameCreds []*Credentials
	ckeys, err := cstore.GetByUsername(c, creds.Username, &userNameCreds)
	if err != nil {
		return nil, err
	}

	if len(userNameCreds) != 1 {
		return nil, errors.New("unable to find unique credentials")
	}

	err = checkCrypt(userNameCreds[0].Password, creds.Password)
	if err != nil {
		return nil, err
	}
	return ckeys[0].Parent(), nil
}
<commit_msg>validate account before save<commit_after>package auth

import (
	"errors"
	"fmt"

	"github.com/chrisolsen/ae"
	"github.com/chrisolsen/ae/attachment"
	"golang.org/x/net/context"
	"google.golang.org/appengine/datastore"
)

const (
	AccountStateUnconfirmed = iota
	AccountStateConfirmed
	AccountStateSuspended
	AccountStateTerminated
)

// Account model
type Account struct {
	ae.Model

	FirstName string `json:"firstName" datastore:",noindex"`
	LastName  string `json:"lastName" datastore:",noindex"`
	Gender    string `json:"gender" datastore:",noindex"`
	Locale    string `json:"locale" datastore:",noindex"`
	Location  string `json:"location" datastore:",noindex"`
	Name      string `json:"name" datastore:",noindex"`
	Timezone  int    `json:"timezone" datastore:",noindex"`
	Email     string `json:"email"`
	State     int    `json:"-"`

	Photo attachment.File `json:"photo"`
}

// AccountStore .
type AccountStore struct {
	ae.Store
}

// NewAccountStore returns a setup AccountStore
func NewAccountStore() AccountStore {
	s := AccountStore{}
	s.TableName = "accounts"
	return s
}

func (s *AccountStore) Valid(a *Account) error {
	if a.FirstName == "" {
		return ae.NewValidationError("First name is required")
	}
	if a.LastName == "" {
		return ae.NewValidationError("Last name is required")
	}
	if a.LastName == "" {
		return ae.NewValidationError("Last name is required")
	}
	return nil
}

// Create creates a new account
func (s *AccountStore) Create(c context.Context, creds *Credentials, account *Account) (*datastore.Key, error) {
	var err error
	var accountKey *datastore.Key
	var cStore = NewCredentialStore()

	if err = s.Valid(account); err != nil {
		return nil, err
	}
	if err = creds.Valid(); err != nil {
		return nil, err
	}

	accountKey, err = s.Store.Create(c, account, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create account: %v", err)
	}
	_, err = cStore.Create(c, creds, accountKey)
	if err != nil {
		return nil, fmt.Errorf("failed to create credentials: %v", err)
	}

	return accountKey, nil
}

// GetAccountKeyByCredentials fetches the account matching the auth provider credentials
func (s *AccountStore) GetAccountKeyByCredentials(c context.Context, creds *Credentials) (*datastore.Key, error) {
	var err error
	cstore := NewCredentialStore()
	// on initial signup the account key will exist within the credentials
	if creds.AccountKey != nil {
		var accountCreds []*Credentials
		_, err = cstore.GetByAccount(c, creds.AccountKey, &accountCreds)
		if err != nil {
			return nil, fmt.Errorf("failed to find credentials by parent account: %v", err)
		}
		// validate credentials
		for _, ac := range accountCreds {
			if ac.ProviderID == creds.ProviderID && ac.ProviderName == creds.ProviderName {
				return creds.AccountKey, nil
			}
		}
		return nil, errors.New("no matching credentials found for account")
	}

	// by provider
	if len(creds.ProviderID) > 0 {
		return cstore.GetAccountKeyByProvider(c, creds)
	}

	// by username
	var userNameCreds []*Credentials
	ckeys, err := cstore.GetByUsername(c, creds.Username, &userNameCreds)
	if err != nil {
		return nil, err
	}

	if len(userNameCreds) != 1 {
		return nil, errors.New("unable to find unique credentials")
	}

	err = checkCrypt(userNameCreds[0].Password, creds.Password)
	if err != nil {
		return nil, err
	}
	return ckeys[0].Parent(), nil
}
<|endoftext|>


<commit_before>package betting

import "time"

type Competition struct {
	ID   string `json:"id,omitempty"`
	Name string `json:"name,omitempty"`
}

type CompetitionResult struct {
	Competition       Competition `json:"competition,omitempty"`
	MarketCount       int         `json:"marketCount,omitempty"`
	CompetitionRegion string      `json:"competitionRegion,omitempty"`
}

// ListCompetitions to get competitions associated with the markets selected by the filter
func (b *Betting) ListCompetitions(filter Filter) (competitionResults []CompetitionResult, err error) {
	err = b.Request(&competitionResults, BettingURL, "listCompetitions", &filter)
	if err != nil {
		return
	}

	return
}

type CountryCodeResult struct {
	CountryCode string `json:"countryCode,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListCountries to get a list of countries associated with the markets selected by the filter
func (b *Betting) ListCountries(filter Filter) (countryCodeResults []CountryCodeResult, err error) {
	err = b.Request(&countryCodeResults, BettingURL, "listCountries", &filter)
	if err != nil {
		return
	}

	return
}

type PriceSize struct {
	Price float64
	Size  float64
}

type CurrentOrderSummary struct {
	BetID               string           `json:"betId"`
	MarketID            string           `json:"marketId"`
	SelectionID         int64            `json:"selectionId"`
	Handicap            float64          `json:"handicap"`
	PriceSize           PriceSize        `json:"priceSize"`
	BspLiability        float64          `json:"bspLiability"`
	Side                ESide            `json:"side"`
	Status              EOrderStatus     `json:"status"`
	PersistenceType     EPersistenceType `json:"persistenceType"`
	OrderType           EOrderType       `json:"orderType"`
	PlacedDate          time.Time        `json:"placedDate"`
	MatchedDate         time.Time        `json:"matchedDate"`
	AveragePriceMatched float64          `json:"averagePriceMatched,omitempty"`
	SizeMatched         float64          `json:"sizeMatched,omitempty"`
	SizeRemaining       float64          `json:"sizeRemaining,omitempty"`
	SizeLapsed          float64          `json:"sizeLapsed,omitempty"`
	SizeCancelled       float64          `json:"sizeCancelled,omitempty"`
	SizeVoided          float64          `json:"sizeVoided,omitempty"`
	RegulatorAuthCode   string           `json:"regulatorAuthCode,omitempty"`
	RegulatorCode       string           `json:"regulatorCode,omitempty"`
}

type CurrentOrderSummaryReport struct {
	CurrentOrders []CurrentOrderSummary `json:"currentOrders,omitempty"`
	MoreAvailable bool                  `json:"moreAvailable,omitempty"`
}

// ListCurrentOrders to get a list of your current orders
func (b *Betting) ListCurrentOrders(filter Filter) (currentOrderSummaryReport CurrentOrderSummaryReport, err error) {
	err = b.Request(&currentOrderSummaryReport, BettingURL, "listCurrentOrders", &filter)
	if err != nil {
		return
	}

	return
}

type ItemDescription struct {
	EventTypeDesc   string    `json:"eventTypeDesc,omitempty"`
	EventDesc       string    `json:"eventDesc,omitempty"`
	MarketDesc      string    `json:"marketDesc,omitempty"`
	MarketType      string    `json:"marketType,omitempty"`
	MarketStartTime time.Time `json:"marketStartTime,omitempty"`
	RunnerDesc      string    `json:"runnerDesc,omitempty"`
	NumberOfWinners int       `json:"numberOfWinners,omitempty"`
	EachWayDivisor  float64   `json:"eachWayDivisor,omitempty"`
}

type ClearedOrderSummary struct {
	EventTypeID     string           `json:"eventTypeId,omitempty"`
	EventID         string           `json:"eventId,omitempty"`
	MarketID        string           `json:"marketId,omitempty"`
	SelectionID     int64            `json:"selectionId,omitempty"`
	Handicap        float64          `json:"handicap,omitempty"`
	BetID           string           `json:"betId,omitempty"`
	PlacedDate      time.Time        `json:"placedDate,omitempty"`
	PersistenceType EPersistenceType `json:"persistenceType,omitempty"`
	OrderType       EOrderType       `json:"orderType,omitempty"`
	Side            ESide            `json:"side,omitempty"`
	ItemDescription ItemDescription  `json:"itemDescription,omitempty"`
	BetOutcome      string           `json:"betOutcome,omitempty"`
	PriceRequested  float64          `json:"priceRequested,omitempty"`
	SettledDate     time.Time        `json:"settledDate,omitempty"`
	LastMatchedDate time.Time        `json:"lastMatchedDate,omitempty"`
	BetCount        int              `json:"betCount,omitempty"`
	Commission      float64          `json:"commission,omitempty"`
	PriceMatched    float64          `json:"priceMatched,omitempty"`
	PriceReduced    bool             `json:"priceReduced,omitempty"`
	SizeSettled     float64          `json:"sizeSettled,omitempty"`
	Profit          float64          `json:"profit,omitempty"`
	SizeCancelled   float64          `json:"sizeCancelled,omitempty"`
}

type ClearedOrderSummaryReport struct {
	ClearedOrders []ClearedOrderSummary `json:"clearedOrders"`
	MoreAvailable bool                  `json:"moreAvailable"`
}

// ListClearedOrders to get a list of settled bets based on the bet status, ordered by settled date
func (b *Betting) ListClearedOrders(filter Filter) (clearedOrderSummaryReport ClearedOrderSummaryReport, err error) {
	err = b.Request(&clearedOrderSummaryReport, BettingURL, "listClearedOrders", &filter)
	if err != nil {
		return
	}

	return
}

type Event struct {
	ID          string    `json:"id,omitempty"`
	Name        string    `json:"name,omitempty"`
	CountryCode string    `json:"countryCode,omitempty"`
	Timezone    string    `json:"timezone,omitempty"`
	Venue       string    `json:"venue,omitempty"`
	OpenDate    time.Time `json:"openDate,omitempty"`
}

type EventResult struct {
	Event       Event `json:"event,omitempty"`
	MarketCount int   `json:"marketCount,omitempty"`
}

// ListEvents to get a list of all events
func (b *Betting) ListEvents(filter Filter) (eventResult []EventResult, err error) {
	err = b.Request(&eventResult, BettingURL, "listEvents", &filter)
	if err != nil {
		return
	}

	return
}

type EventType struct {
	ID   string `json:"id,omitempty"`
	Name string `json:"name,omitempty"`
}

type EventTypeResult struct {
	EventType   EventType `json:"eventType,omitempty"`
	MarketCount int       `json:"marketCount,omitempty"`
}

// ListEventTypes to get a list of all event types
func (b *Betting) ListEventTypes(filter Filter) (eventTypeResult []EventTypeResult, err error) {
	err = b.Request(&eventTypeResult, BettingURL, "listEventTypes", &filter)
	if err != nil {
		return
	}

	return
}

type MarketCatalogue struct {
	MarketID        string            `json:"marketId,omitempty"`
	MarketName      string            `json:"marketName,omitempty"`
	MarketStartTime time.Time         `json:"marketStartTime,omitempty"`
	Description     MarketDescription `json:"description,omitempty"`
	TotalMatched    float64           `json:"totalMatched,omitempty"`
	Runners         []RunnerCatalog   `json:"runners,omitempty"`
	EventType       EventType         `json:"eventType,omitempty"`
	Competition     Competition       `json:"competition,omitempty"`
	Event           Event             `json:"event,omitempty"`
}

type MarketDescription struct {
	PersistenceEnabled bool               `json:"persistenceEnabled,omitempty"`
	BspMarket          bool               `json:"bspMarket,omitempty"`
	MarketTime         time.Time          `json:"marketTime,omitempty"`
	SuspendTime        time.Time          `json:"suspendTime,omitempty"`
	SettleTime         time.Time          `json:"settleTime,omitempty"`
	BettingType        EMarketBettingType `json:"bettingType,omitempty"`
	TurnInPlayEnabled  bool               `json:"turnInPlayEnabled,omitempty"`
	MarketType         string             `json:"marketType,omitempty"`
	Regulator          string             `json:"marketId,omitempty"`
	MarketBaseRate     float64            `json:"marketBaseRate,omitempty"`
	DiscountAllowed    bool               `json:"discountAllowed,omitempty"`
	Wallet             string             `json:"wallet,omitempty"`
	Rules              string             `json:"rules,omitempty"`
	RulesHasDate       bool               `json:"rulesHasDate,omitempty"`
	EachWayDivisor     float64            `json:"eachWayDivisor,omitempty"`
	Clarifications     string             `json:"clarifications,omitempty"`
}

type RunnerCatalog struct {
	SelectionID  int64             `json:"selectionId,omitempty"`
	RunnerName   string            `json:"runnerName,omitempty"`
	Handicap     float64           `json:"handicap,omitempty"`
	SortPriority int               `json:"sortPriority,omitempty"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

// ListMarketCatalogue to get a list of information about published (ACTIVE/SUSPENDED) markets that does not change (or changes very rarely).
func (b *Betting) ListMarketCatalogue(filter Filter) (marketCatalogue []MarketCatalogue, err error) {
	err = b.Request(&marketCatalogue, BettingURL, "listMarketCatalogue", &filter)
	if err != nil {
		return
	}

	return
}

type MarketProfitAndLoss struct {
	MarketId          string                `json:"marketId,omitempty"`
	CommissionApplied float64               `json:"commissionApplied,omitempty"`
	ProfitAndLosses   []RunnerProfitAndLoss `json:"profitAndLosses,omitempty"`
}

type RunnerProfitAndLoss struct {
	SelectionId int64   `json:"selectionId,omitempty"`
	IfWin       float64 `json:"ifWin,omitempty"`
	IfLose      float64 `json:"ifLose,omitempty"`
	IfPlace     float64 `json:"ifPlace,omitempty"`
}

// ListMarketProfitAndLoss Retrieve profit and loss for a given list of OPEN markets.
func (b *Betting) ListMarketProfitAndLoss(filter Filter) (marketProfitAndLoss []MarketProfitAndLoss, err error) {
	err = b.Request(&marketProfitAndLoss, BettingURL, "listMarketProfitAndLoss", &filter)
	if err != nil {
		return
	}

	return
}

type MarketTypeResult struct {
	MarketType  string `json:"marketType,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListMarketTypes to get a list of market types (i.e. MATCH_ODDS, NEXT_GOAL) associated with the markets selected by the MarketFilter.
func (b *Betting) ListMarketTypes(filter Filter) (marketTypeResult []MarketTypeResult, err error) {
	err = b.Request(&marketTypeResult, BettingURL, "listMarketTypes", &filter)
	if err != nil {
		return
	}

	return
}

type TimeRangeResult struct {
	TimeRange   DateRange `json:"timeRange,omitempty"`
	MarketCount int       `json:"marketCount,omitempty"`
}

// ListTimeRangeResult to get  a list of time ranges in the granularity specified in the request (i.e. 3PM to 4PM, Aug 14th to Aug 15th) associated with the markets selected by the MarketFilter.
func (b *Betting) ListTimeRangeResult(filter Filter) (timeRangeResult []TimeRangeResult, err error) {
	err = b.Request(&timeRangeResult, BettingURL, "listTimeRanges", &filter)
	if err != nil {
		return
	}

	return
}

type VenueResult struct {
	Venue       string `json:"venue,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListVenueResult to get a list of Venues (i.e. Cheltenham, Ascot) associated with the markets selected by the MarketFilter. Currently, only Horse Racing markets are associated with a Venue.
func (b *Betting) ListVenueResult(filter Filter) (venueResult []VenueResult, err error) {
	err = b.Request(&venueResult, BettingURL, "listVenues", &filter)
	if err != nil {
		return
	}

	return
}
<commit_msg>Change EOL CRLF -> LF<commit_after>package betting

import "time"

type Competition struct {
	ID   string `json:"id,omitempty"`
	Name string `json:"name,omitempty"`
}

type CompetitionResult struct {
	Competition       Competition `json:"competition,omitempty"`
	MarketCount       int         `json:"marketCount,omitempty"`
	CompetitionRegion string      `json:"competitionRegion,omitempty"`
}

// ListCompetitions to get competitions associated with the markets selected by the filter
func (b *Betting) ListCompetitions(filter Filter) (competitionResults []CompetitionResult, err error) {
	err = b.Request(&competitionResults, BettingURL, "listCompetitions", &filter)
	if err != nil {
		return
	}

	return
}

type CountryCodeResult struct {
	CountryCode string `json:"countryCode,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListCountries to get a list of countries associated with the markets selected by the filter
func (b *Betting) ListCountries(filter Filter) (countryCodeResults []CountryCodeResult, err error) {
	err = b.Request(&countryCodeResults, BettingURL, "listCountries", &filter)
	if err != nil {
		return
	}

	return
}

type PriceSize struct {
	Price float64
	Size  float64
}

type CurrentOrderSummary struct {
	BetID               string           `json:"betId"`
	MarketID            string           `json:"marketId"`
	SelectionID         int64            `json:"selectionId"`
	Handicap            float64          `json:"handicap"`
	PriceSize           PriceSize        `json:"priceSize"`
	BspLiability        float64          `json:"bspLiability"`
	Side                ESide            `json:"side"`
	Status              EOrderStatus     `json:"status"`
	PersistenceType     EPersistenceType `json:"persistenceType"`
	OrderType           EOrderType       `json:"orderType"`
	PlacedDate          time.Time        `json:"placedDate"`
	MatchedDate         time.Time        `json:"matchedDate"`
	AveragePriceMatched float64          `json:"averagePriceMatched,omitempty"`
	SizeMatched         float64          `json:"sizeMatched,omitempty"`
	SizeRemaining       float64          `json:"sizeRemaining,omitempty"`
	SizeLapsed          float64          `json:"sizeLapsed,omitempty"`
	SizeCancelled       float64          `json:"sizeCancelled,omitempty"`
	SizeVoided          float64          `json:"sizeVoided,omitempty"`
	RegulatorAuthCode   string           `json:"regulatorAuthCode,omitempty"`
	RegulatorCode       string           `json:"regulatorCode,omitempty"`
}

type CurrentOrderSummaryReport struct {
	CurrentOrders []CurrentOrderSummary `json:"currentOrders,omitempty"`
	MoreAvailable bool                  `json:"moreAvailable,omitempty"`
}

// ListCurrentOrders to get a list of your current orders
func (b *Betting) ListCurrentOrders(filter Filter) (currentOrderSummaryReport CurrentOrderSummaryReport, err error) {
	err = b.Request(&currentOrderSummaryReport, BettingURL, "listCurrentOrders", &filter)
	if err != nil {
		return
	}

	return
}

type ItemDescription struct {
	EventTypeDesc   string    `json:"eventTypeDesc,omitempty"`
	EventDesc       string    `json:"eventDesc,omitempty"`
	MarketDesc      string    `json:"marketDesc,omitempty"`
	MarketType      string    `json:"marketType,omitempty"`
	MarketStartTime time.Time `json:"marketStartTime,omitempty"`
	RunnerDesc      string    `json:"runnerDesc,omitempty"`
	NumberOfWinners int       `json:"numberOfWinners,omitempty"`
	EachWayDivisor  float64   `json:"eachWayDivisor,omitempty"`
}

type ClearedOrderSummary struct {
	EventTypeID     string           `json:"eventTypeId,omitempty"`
	EventID         string           `json:"eventId,omitempty"`
	MarketID        string           `json:"marketId,omitempty"`
	SelectionID     int64            `json:"selectionId,omitempty"`
	Handicap        float64          `json:"handicap,omitempty"`
	BetID           string           `json:"betId,omitempty"`
	PlacedDate      time.Time        `json:"placedDate,omitempty"`
	PersistenceType EPersistenceType `json:"persistenceType,omitempty"`
	OrderType       EOrderType       `json:"orderType,omitempty"`
	Side            ESide            `json:"side,omitempty"`
	ItemDescription ItemDescription  `json:"itemDescription,omitempty"`
	BetOutcome      string           `json:"betOutcome,omitempty"`
	PriceRequested  float64          `json:"priceRequested,omitempty"`
	SettledDate     time.Time        `json:"settledDate,omitempty"`
	LastMatchedDate time.Time        `json:"lastMatchedDate,omitempty"`
	BetCount        int              `json:"betCount,omitempty"`
	Commission      float64          `json:"commission,omitempty"`
	PriceMatched    float64          `json:"priceMatched,omitempty"`
	PriceReduced    bool             `json:"priceReduced,omitempty"`
	SizeSettled     float64          `json:"sizeSettled,omitempty"`
	Profit          float64          `json:"profit,omitempty"`
	SizeCancelled   float64          `json:"sizeCancelled,omitempty"`
}

type ClearedOrderSummaryReport struct {
	ClearedOrders []ClearedOrderSummary `json:"clearedOrders"`
	MoreAvailable bool                  `json:"moreAvailable"`
}

// ListClearedOrders to get a list of settled bets based on the bet status, ordered by settled date
func (b *Betting) ListClearedOrders(filter Filter) (clearedOrderSummaryReport ClearedOrderSummaryReport, err error) {
	err = b.Request(&clearedOrderSummaryReport, BettingURL, "listClearedOrders", &filter)
	if err != nil {
		return
	}

	return
}

type Event struct {
	ID          string    `json:"id,omitempty"`
	Name        string    `json:"name,omitempty"`
	CountryCode string    `json:"countryCode,omitempty"`
	Timezone    string    `json:"timezone,omitempty"`
	Venue       string    `json:"venue,omitempty"`
	OpenDate    time.Time `json:"openDate,omitempty"`
}

type EventResult struct {
	Event       Event `json:"event,omitempty"`
	MarketCount int   `json:"marketCount,omitempty"`
}

// ListEvents to get a list of all events
func (b *Betting) ListEvents(filter Filter) (eventResult []EventResult, err error) {
	err = b.Request(&eventResult, BettingURL, "listEvents", &filter)
	if err != nil {
		return
	}

	return
}

type EventType struct {
	ID   string `json:"id,omitempty"`
	Name string `json:"name,omitempty"`
}

type EventTypeResult struct {
	EventType   EventType `json:"eventType,omitempty"`
	MarketCount int       `json:"marketCount,omitempty"`
}

// ListEventTypes to get a list of all event types
func (b *Betting) ListEventTypes(filter Filter) (eventTypeResult []EventTypeResult, err error) {
	err = b.Request(&eventTypeResult, BettingURL, "listEventTypes", &filter)
	if err != nil {
		return
	}

	return
}

type MarketCatalogue struct {
	MarketID        string            `json:"marketId,omitempty"`
	MarketName      string            `json:"marketName,omitempty"`
	MarketStartTime time.Time         `json:"marketStartTime,omitempty"`
	Description     MarketDescription `json:"description,omitempty"`
	TotalMatched    float64           `json:"totalMatched,omitempty"`
	Runners         []RunnerCatalog   `json:"runners,omitempty"`
	EventType       EventType         `json:"eventType,omitempty"`
	Competition     Competition       `json:"competition,omitempty"`
	Event           Event             `json:"event,omitempty"`
}

type MarketDescription struct {
	PersistenceEnabled bool               `json:"persistenceEnabled,omitempty"`
	BspMarket          bool               `json:"bspMarket,omitempty"`
	MarketTime         time.Time          `json:"marketTime,omitempty"`
	SuspendTime        time.Time          `json:"suspendTime,omitempty"`
	SettleTime         time.Time          `json:"settleTime,omitempty"`
	BettingType        EMarketBettingType `json:"bettingType,omitempty"`
	TurnInPlayEnabled  bool               `json:"turnInPlayEnabled,omitempty"`
	MarketType         string             `json:"marketType,omitempty"`
	Regulator          string             `json:"marketId,omitempty"`
	MarketBaseRate     float64            `json:"marketBaseRate,omitempty"`
	DiscountAllowed    bool               `json:"discountAllowed,omitempty"`
	Wallet             string             `json:"wallet,omitempty"`
	Rules              string             `json:"rules,omitempty"`
	RulesHasDate       bool               `json:"rulesHasDate,omitempty"`
	EachWayDivisor     float64            `json:"eachWayDivisor,omitempty"`
	Clarifications     string             `json:"clarifications,omitempty"`
}

type RunnerCatalog struct {
	SelectionID  int64             `json:"selectionId,omitempty"`
	RunnerName   string            `json:"runnerName,omitempty"`
	Handicap     float64           `json:"handicap,omitempty"`
	SortPriority int               `json:"sortPriority,omitempty"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

// ListMarketCatalogue to get a list of information about published (ACTIVE/SUSPENDED) markets that does not change (or changes very rarely).
func (b *Betting) ListMarketCatalogue(filter Filter) (marketCatalogue []MarketCatalogue, err error) {
	err = b.Request(&marketCatalogue, BettingURL, "listMarketCatalogue", &filter)
	if err != nil {
		return
	}

	return
}

type MarketProfitAndLoss struct {
	MarketId          string                `json:"marketId,omitempty"`
	CommissionApplied float64               `json:"commissionApplied,omitempty"`
	ProfitAndLosses   []RunnerProfitAndLoss `json:"profitAndLosses,omitempty"`
}

type RunnerProfitAndLoss struct {
	SelectionId int64   `json:"selectionId,omitempty"`
	IfWin       float64 `json:"ifWin,omitempty"`
	IfLose      float64 `json:"ifLose,omitempty"`
	IfPlace     float64 `json:"ifPlace,omitempty"`
}

// ListMarketProfitAndLoss Retrieve profit and loss for a given list of OPEN markets.
func (b *Betting) ListMarketProfitAndLoss(filter Filter) (marketProfitAndLoss []MarketProfitAndLoss, err error) {
	err = b.Request(&marketProfitAndLoss, BettingURL, "listMarketProfitAndLoss", &filter)
	if err != nil {
		return
	}

	return
}

type MarketTypeResult struct {
	MarketType  string `json:"marketType,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListMarketTypes to get a list of market types (i.e. MATCH_ODDS, NEXT_GOAL) associated with the markets selected by the MarketFilter.
func (b *Betting) ListMarketTypes(filter Filter) (marketTypeResult []MarketTypeResult, err error) {
	err = b.Request(&marketTypeResult, BettingURL, "listMarketTypes", &filter)
	if err != nil {
		return
	}

	return
}

type TimeRangeResult struct {
	TimeRange   DateRange `json:"timeRange,omitempty"`
	MarketCount int       `json:"marketCount,omitempty"`
}

// ListTimeRangeResult to get  a list of time ranges in the granularity specified in the request (i.e. 3PM to 4PM, Aug 14th to Aug 15th) associated with the markets selected by the MarketFilter.
func (b *Betting) ListTimeRangeResult(filter Filter) (timeRangeResult []TimeRangeResult, err error) {
	err = b.Request(&timeRangeResult, BettingURL, "listTimeRanges", &filter)
	if err != nil {
		return
	}

	return
}

type VenueResult struct {
	Venue       string `json:"venue,omitempty"`
	MarketCount int    `json:"marketCount,omitempty"`
}

// ListVenueResult to get a list of Venues (i.e. Cheltenham, Ascot) associated with the markets selected by the MarketFilter. Currently, only Horse Racing markets are associated with a Venue.
func (b *Betting) ListVenueResult(filter Filter) (venueResult []VenueResult, err error) {
	err = b.Request(&venueResult, BettingURL, "listVenues", &filter)
	if err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package tcp

import (
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/peer"
	"net"
	"sync"
	"time"
)

type tcpConnector struct {
	peer.SessionManager

	peer.CorePeerProperty
	peer.CoreContextSet
	peer.CoreRunningTag
	peer.CoreProcBundle
	peer.CoreTCPSocketOption

	defaultSes *tcpSession

	tryConnTimes int // 尝试连接次数

	sesEndSignal sync.WaitGroup

	reconDur time.Duration
}

func (self *tcpConnector) Start() cellnet.Peer {

	self.WaitStopFinished()

	if self.IsRunning() {
		return self
	}

	go self.connect(self.Address())

	return self
}

func (self *tcpConnector) Session() cellnet.Session {
	return self.defaultSes
}

func (self *tcpConnector) SetSessionManager(raw interface{}) {
	self.SessionManager = raw.(peer.SessionManager)
}

func (self *tcpConnector) Stop() {
	if !self.IsRunning() {
		return
	}

	if self.IsStopping() {
		return
	}

	self.StartStopping()

	// 通知发送关闭
	self.defaultSes.Close()

	// 等待线程结束
	self.WaitStopFinished()

}

func (self *tcpConnector) ReconnectDuration() time.Duration {

	return self.reconDur
}

func (self *tcpConnector) SetReconnectDuration(v time.Duration) {
	self.reconDur = v
}

func (self *tcpConnector) Port() int {
	if self.defaultSes.conn == nil {
		return 0
	}

	return self.defaultSes.conn.LocalAddr().(*net.TCPAddr).Port
}

const reportConnectFailedLimitTimes = 3

// 连接器，传入连接地址和发送封包次数
func (self *tcpConnector) connect(address string) {

	self.SetRunning(true)

	for {
		self.tryConnTimes++

		// 尝试用Socket连接地址
		conn, err := net.Dial("tcp", address)

		self.defaultSes.conn = conn

		// 发生错误时退出
		if err != nil {

			if self.tryConnTimes <= reportConnectFailedLimitTimes {
				log.Errorf("#tcp.connect failed(%s) %v", self.NameOrAddress(), err.Error())
			}

			if self.tryConnTimes == reportConnectFailedLimitTimes {
				log.Errorf("(%s) continue reconnecting, but mute log", self.NameOrAddress())
			}

			// 没重连就退出
			if self.ReconnectDuration() == 0 {

				log.Debugf("#tcp.connect failed(%s)@%d address: %s", self.Name(), self.defaultSes.ID(), self.Address())

				self.PostEvent(&cellnet.RecvMsgEvent{self.defaultSes, &cellnet.SessionConnectError{}})
				break
			}

			// 有重连就等待
			time.Sleep(self.ReconnectDuration())

			// 继续连接
			continue
		}

		self.sesEndSignal.Add(1)

		self.ApplySocketOption(conn)

		self.defaultSes.Start()

		self.tryConnTimes = 0

		self.PostEvent(&cellnet.RecvMsgEvent{self.defaultSes, &cellnet.SessionConnected{}})

		self.sesEndSignal.Wait()

		self.defaultSes.conn = nil

		// 没重连就退出/主动退出
		if self.IsStopping() || self.ReconnectDuration() == 0 {
			break
		}

		// 有重连就等待
		time.Sleep(self.ReconnectDuration())

		// 继续连接
		continue

	}

	self.SetRunning(false)

	self.EndStopping()
}

func (self *tcpConnector) IsReady() bool {

	return self.SessionCount() != 0
}

func (self *tcpConnector) TypeName() string {
	return "tcp.Connector"
}

func init() {

	peer.RegisterPeerCreator(func() cellnet.Peer {
		self := &tcpConnector{
			SessionManager: new(peer.CoreSessionManager),
		}

		self.defaultSes = newSession(nil, self, func() {
			self.sesEndSignal.Done()
		})

		self.CoreTCPSocketOption.Init()

		return self
	})
}
<commit_msg>处理关闭正在重连的连接<commit_after>package tcp

import (
	"github.com/davyxu/cellnet"
	"github.com/davyxu/cellnet/peer"
	"net"
	"sync"
	"time"
)

type tcpConnector struct {
	peer.SessionManager

	peer.CorePeerProperty
	peer.CoreContextSet
	peer.CoreRunningTag
	peer.CoreProcBundle
	peer.CoreTCPSocketOption

	defaultSes *tcpSession

	tryConnTimes int // 尝试连接次数

	sesEndSignal sync.WaitGroup

	reconDur time.Duration
}

func (self *tcpConnector) Start() cellnet.Peer {

	self.WaitStopFinished()

	if self.IsRunning() {
		return self
	}

	go self.connect(self.Address())

	return self
}

func (self *tcpConnector) Session() cellnet.Session {
	return self.defaultSes
}

func (self *tcpConnector) SetSessionManager(raw interface{}) {
	self.SessionManager = raw.(peer.SessionManager)
}

func (self *tcpConnector) Stop() {
	if !self.IsRunning() {
		return
	}

	if self.IsStopping() {
		return
	}

	self.StartStopping()

	// 通知发送关闭
	self.defaultSes.Close()

	// 等待线程结束
	self.WaitStopFinished()

}

func (self *tcpConnector) ReconnectDuration() time.Duration {

	return self.reconDur
}

func (self *tcpConnector) SetReconnectDuration(v time.Duration) {
	self.reconDur = v
}

func (self *tcpConnector) Port() int {
	if self.defaultSes.conn == nil {
		return 0
	}

	return self.defaultSes.conn.LocalAddr().(*net.TCPAddr).Port
}

const reportConnectFailedLimitTimes = 3

// 连接器，传入连接地址和发送封包次数
func (self *tcpConnector) connect(address string) {

	self.SetRunning(true)

	for {
		self.tryConnTimes++

		// 尝试用Socket连接地址
		conn, err := net.Dial("tcp", address)

		self.defaultSes.conn = conn

		// 发生错误时退出
		if err != nil {

			if self.tryConnTimes <= reportConnectFailedLimitTimes {
				log.Errorf("#tcp.connect failed(%s) %v", self.NameOrAddress(), err.Error())
			}

			if self.tryConnTimes == reportConnectFailedLimitTimes {
				log.Errorf("(%s) continue reconnecting, but mute log", self.NameOrAddress())
			}

			// 没重连就退出
			if self.ReconnectDuration() == 0 || self.IsStopping() {

				self.PostEvent(&cellnet.RecvMsgEvent{
					Ses: self.defaultSes,
					Msg: &cellnet.SessionConnectError{},
				})

				break
			}

			// 有重连就等待
			time.Sleep(self.ReconnectDuration())

			// 继续连接
			continue
		}

		self.sesEndSignal.Add(1)

		self.ApplySocketOption(conn)

		self.defaultSes.Start()

		self.tryConnTimes = 0

		self.PostEvent(&cellnet.RecvMsgEvent{Ses: self.defaultSes, Msg: &cellnet.SessionConnected{}})

		self.sesEndSignal.Wait()

		self.defaultSes.conn = nil

		// 没重连就退出/主动退出
		if self.IsStopping() || self.ReconnectDuration() == 0 {
			break
		}

		// 有重连就等待
		time.Sleep(self.ReconnectDuration())

		// 继续连接
		continue

	}

	self.SetRunning(false)

	self.EndStopping()
}

func (self *tcpConnector) IsReady() bool {

	return self.SessionCount() != 0
}

func (self *tcpConnector) TypeName() string {
	return "tcp.Connector"
}

func init() {

	peer.RegisterPeerCreator(func() cellnet.Peer {
		self := &tcpConnector{
			SessionManager: new(peer.CoreSessionManager),
		}

		self.defaultSes = newSession(nil, self, func() {
			self.sesEndSignal.Done()
		})

		self.CoreTCPSocketOption.Init()

		return self
	})
}
<|endoftext|>


<commit_before>package phrase

import "testing"

// phrase is a dummy Phrase for testing
type phrase string

func (p phrase) Phrase() string { return string(p) }

func TestClean(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{in: "a", want: "a"},
		{in: "  a \t ", want: "a"},
		{in: "☃s☃n☃o☃w☃m☃a☃n☃!☃", want: "snowman!"},
	}

	for _, tt := range cases {
		c := Clean(phrase(tt.in))
		have := c.Phrase()
		if have != tt.want {
			t.Errorf("clean %q want %q have %q", tt.in, tt.want, have)
		}
	}
}

func TestTruncate(t *testing.T) {
	cases := []struct {
		in     string
		length int
		want   string
	}{
		{in: "", length: 0, want: ""},
		{in: "", length: 1, want: ""},
		{in: "a", length: 0, want: ""},
		{in: "a", length: 1, want: "a"},
		{in: "a", length: 2, want: "a"},
		{in: "abcdef", length: 6, want: "abcdef"},
	}

	for _, tt := range cases {
		c := Truncate(phrase(tt.in), tt.length)
		have := c.Phrase()
		if have != tt.want {
			t.Errorf("truncate %q want %q have %q", tt.in, tt.want, have)
		}
	}
}
<commit_msg>Tighten up tests<commit_after>package phrase

import "testing"

// phrase is a dummy Phrase for testing
type phrase string

func (p phrase) Phrase() string { return string(p) }

func TestClean(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{in: "a", want: "a"},
		{in: "  a \t ", want: "a"},
		{in: "☃s☃n☃o☃w☃m☃a☃n☃!☃", want: "snowman!"},
	}

	for _, tt := range cases {
		c := Clean(phrase(tt.in))
		if have := c.Phrase(); have != tt.want {
			t.Errorf("clean %q: want %q have %q", tt.in, tt.want, have)
		}
	}
}

func TestTruncate(t *testing.T) {
	cases := []struct {
		in     string
		length int
		want   string
	}{
		{in: "", length: 0, want: ""},
		{in: "", length: 1, want: ""},
		{in: "a", length: 0, want: ""},
		{in: "a", length: 1, want: "a"},
		{in: "a", length: 2, want: "a"},
		{in: "abcdef", length: 6, want: "abcdef"},
	}

	for _, tt := range cases {
		c := Truncate(phrase(tt.in), tt.length)
		have := c.Phrase()
		if have != tt.want {
			t.Errorf("truncate %q: want %q have %q", tt.in, tt.want, have)
		}
	}
}
<|endoftext|>


<commit_before>package brew

import (
	"bytes"
	"context"
	"crypto/sha256"
	"fmt"
	"log"
	"strings"
	"text/template"

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/config"
	"github.com/goreleaser/releaser/split"
	"golang.org/x/oauth2"
)

const formulae = `class {{ .Name }} < Formula
  desc "{{ .Desc }}"
  homepage "{{ .Homepage }}"
  url "https://github.com/{{ .Repo }}/releases/download/{{ .Tag }}/{{ .BinaryName }}_#{%x(uname -s).gsub(/\n/, '')}_#{%x(uname -m).gsub(/\n/, '')}.tar.gz"
  head "https://github.com/{{ .Repo }}.git"
  version "{{ .Tag }}"

  def install
    bin.install "{{ .BinaryName }}"
  end

  {{- if .Caveats }}

  def caveats
    "{{ .Caveats }}"
  end
  {{- end }}
end
`

type templateData struct {
	Name, Desc, Homepage, Repo, Tag, BinaryName, Caveats string
}

// Pipe for brew deployment
type Pipe struct{}

// Name of the pipe
func (Pipe) Name() string {
	return "Homebrew"
}

// Run the pipe
func (Pipe) Run(config config.ProjectConfig) error {
	if config.Brew.Repo == "" {
		return nil
	}
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	owner, repo := split.OnSlash(config.Brew.Repo)
	name := config.BinaryName + ".rb"

	log.Println("Updating", name, "on", config.Brew.Repo, "...")
	out, err := buildFormulae(config, client)
	if err != nil {
		return err
	}
	sha, err := sha(client, owner, repo, name, out)
	if err != nil {
		return err
	}
	_, _, err = client.Repositories.UpdateFile(
		owner, repo, name, &github.RepositoryContentFileOptions{
			Committer: &github.CommitAuthor{
				Name:  github.String("goreleaserbot"),
				Email: github.String("bot@goreleaser"),
			},
			Content: out.Bytes(),
			Message: github.String(config.BinaryName + " version " + config.Git.CurrentTag),
			SHA:     sha,
		},
	)
	return err
}
func sha(client *github.Client, owner, repo, name string, out bytes.Buffer) (*string, error) {
	file, _, _, err := client.Repositories.GetContents(
		owner, repo, name, &github.RepositoryContentGetOptions{},
	)
	if err == nil {
		return file.SHA, err
	}
	return github.String(fmt.Sprintf("%s", sha256.Sum256(out.Bytes()))), err
}

func buildFormulae(config config.ProjectConfig, client *github.Client) (bytes.Buffer, error) {
	data, err := dataFor(config, client)
	if err != nil {
		return bytes.Buffer{}, err
	}
	return doBuildFormulae(data)
}

func doBuildFormulae(data templateData) (bytes.Buffer, error) {
	var out bytes.Buffer
	tmpl, err := template.New(data.BinaryName).Parse(formulae)
	if err != nil {
		return out, err
	}
	err = tmpl.Execute(&out, data)
	return out, err
}

func dataFor(config config.ProjectConfig, client *github.Client) (result templateData, err error) {
	var homepage string
	var description string
	owner, repo := split.OnSlash(config.Repo)
	rep, _, err := client.Repositories.Get(owner, repo)
	if err != nil {
		return result, err
	}
	if rep.Homepage != nil && *rep.Homepage != "" {
		homepage = *rep.Homepage
	} else {
		homepage = *rep.HTMLURL
	}
	if rep.Description == nil {
		description = "TODO"
	} else {
		description = *rep.Description
	}
	return templateData{
		Name:       formulaNameFor(config.BinaryName),
		Desc:       description,
		Homepage:   homepage,
		Repo:       config.Repo,
		Tag:        config.Git.CurrentTag,
		BinaryName: config.BinaryName,
		Caveats:    config.Brew.Caveats,
	}, err
}

func formulaNameFor(name string) string {
	name = strings.Replace(name, "-", " ", -1)
	name = strings.Replace(name, "_", " ", -1)
	return strings.Replace(strings.Title(name), " ", "", -1)
}
<commit_msg>fixed first release to homebrew repo<commit_after>package brew

import (
	"bytes"
	"context"
	"crypto/sha256"
	"fmt"
	"log"
	"strings"
	"text/template"

	"github.com/google/go-github/github"
	"github.com/goreleaser/releaser/config"
	"github.com/goreleaser/releaser/split"
	"golang.org/x/oauth2"
)

const formulae = `class {{ .Name }} < Formula
  desc "{{ .Desc }}"
  homepage "{{ .Homepage }}"
  url "https://github.com/{{ .Repo }}/releases/download/{{ .Tag }}/{{ .BinaryName }}_#{%x(uname -s).gsub(/\n/, '')}_#{%x(uname -m).gsub(/\n/, '')}.tar.gz"
  head "https://github.com/{{ .Repo }}.git"
  version "{{ .Tag }}"

  def install
    bin.install "{{ .BinaryName }}"
  end

  {{- if .Caveats }}

  def caveats
    "{{ .Caveats }}"
  end
  {{- end }}
end
`

type templateData struct {
	Name, Desc, Homepage, Repo, Tag, BinaryName, Caveats string
}

// Pipe for brew deployment
type Pipe struct{}

// Name of the pipe
func (Pipe) Name() string {
	return "Homebrew"
}

// Run the pipe
func (Pipe) Run(config config.ProjectConfig) error {
	if config.Brew.Repo == "" {
		return nil
	}
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Token},
	)
	tc := oauth2.NewClient(context.Background(), ts)
	client := github.NewClient(tc)

	owner, repo := split.OnSlash(config.Brew.Repo)
	name := config.BinaryName + ".rb"

	log.Println("Updating", name, "on", config.Brew.Repo, "...")
	out, err := buildFormulae(config, client)
	if err != nil {
		return err
	}
	sha, err := sha(client, owner, repo, name, out)
	if err != nil {
		return err
	}
	_, _, err = client.Repositories.UpdateFile(
		owner, repo, name, &github.RepositoryContentFileOptions{
			Committer: &github.CommitAuthor{
				Name:  github.String("goreleaserbot"),
				Email: github.String("bot@goreleaser"),
			},
			Content: out.Bytes(),
			Message: github.String(config.BinaryName + " version " + config.Git.CurrentTag),
			SHA:     sha,
		},
	)
	return err
}

func sha(client *github.Client, owner, repo, name string, out bytes.Buffer) (*string, error) {
	file, _, _, err := client.Repositories.GetContents(
		owner, repo, name, &github.RepositoryContentGetOptions{},
	)
	if err == nil {
		return file.SHA, err
	}
	return github.String(fmt.Sprintf("%s", sha256.Sum256(out.Bytes()))), nil
}

func buildFormulae(config config.ProjectConfig, client *github.Client) (bytes.Buffer, error) {
	data, err := dataFor(config, client)
	if err != nil {
		return bytes.Buffer{}, err
	}
	return doBuildFormulae(data)
}

func doBuildFormulae(data templateData) (bytes.Buffer, error) {
	var out bytes.Buffer
	tmpl, err := template.New(data.BinaryName).Parse(formulae)
	if err != nil {
		return out, err
	}
	err = tmpl.Execute(&out, data)
	return out, err
}

func dataFor(config config.ProjectConfig, client *github.Client) (result templateData, err error) {
	var homepage string
	var description string
	owner, repo := split.OnSlash(config.Repo)
	rep, _, err := client.Repositories.Get(owner, repo)
	if err != nil {
		return result, err
	}
	if rep.Homepage != nil && *rep.Homepage != "" {
		homepage = *rep.Homepage
	} else {
		homepage = *rep.HTMLURL
	}
	if rep.Description == nil {
		description = "TODO"
	} else {
		description = *rep.Description
	}
	return templateData{
		Name:       formulaNameFor(config.BinaryName),
		Desc:       description,
		Homepage:   homepage,
		Repo:       config.Repo,
		Tag:        config.Git.CurrentTag,
		BinaryName: config.BinaryName,
		Caveats:    config.Brew.Caveats,
	}, err
}

func formulaNameFor(name string) string {
	name = strings.Replace(name, "-", " ", -1)
	name = strings.Replace(name, "_", " ", -1)
	return strings.Replace(strings.Title(name), " ", "", -1)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 util

import (
	"encoding/json"
	"fmt"
	"hash/fnv"

	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	"gopkg.in/src-d/enry.v1/regex"
)

func ComputeCertificateRequestName(crt *cmapi.Certificate) (string, error) {
	crt = crt.DeepCopy()
	specBytes, err := json.Marshal(crt.Spec)
	if err != nil {
		return "", err
	}

	hashF := fnv.New32()
	_, err = hashF.Write(specBytes)
	if err != nil {
		return "", err
	}

	if len(crt.Name) >= 52 {
		// shorten the cert name to 52 chars to ensure the total length of the name
		// also shorten the 52 char string to the last non-symbol character
		// is less than or equal to 64 characters
		validCharIndexes := regex.MustCompile(`[a-zA-Z\d]`).FindAllStringIndex(fmt.Sprintf("%.52s", crt.Name), -1)
		crt.Name = crt.Name[:validCharIndexes[len(validCharIndexes)-1][1]]
	}

	return fmt.Sprintf("%s-%d", crt.Name, hashF.Sum32()), nil
}
<commit_msg>Use correct regexp package<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

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 util

import (
	"encoding/json"
	"fmt"
	"hash/fnv"

	"regexp"

	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
)

func ComputeCertificateRequestName(crt *cmapi.Certificate) (string, error) {
	crt = crt.DeepCopy()
	specBytes, err := json.Marshal(crt.Spec)
	if err != nil {
		return "", err
	}

	hashF := fnv.New32()
	_, err = hashF.Write(specBytes)
	if err != nil {
		return "", err
	}

	if len(crt.Name) >= 52 {
		// shorten the cert name to 52 chars to ensure the total length of the name
		// also shorten the 52 char string to the last non-symbol character
		// is less than or equal to 64 characters
		validCharIndexes := regexp.MustCompile(`[a-zA-Z\d]`).FindAllStringIndex(fmt.Sprintf("%.52s", crt.Name), -1)
		crt.Name = crt.Name[:validCharIndexes[len(validCharIndexes)-1][1]]
	}

	return fmt.Sprintf("%s-%d", crt.Name, hashF.Sum32()), nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"fmt"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

// ValidationErrorEnum is a type of validation error.
type ValidationErrorEnum string

// These are known errors of validation.
const (
	ErrTypeInvalid      ValidationErrorEnum = "invalid value"
	ErrTypeNotSupported ValidationErrorEnum = "unsupported value"
	ErrTypeDuplicate    ValidationErrorEnum = "duplicate value"
	ErrTypeNotFound     ValidationErrorEnum = "not found"
)

// ValidationError is an implementation of the 'error' interface, which represents an error of validation.
type ValidationError struct {
	ErrorType  ValidationErrorEnum
	ErrorField string
	BadValue   interface{}
}

func (v ValidationError) Error() string {
	return fmt.Sprintf("%s: %v '%v'", v.ErrorField, v.ErrorType, v.BadValue)
}

// Factory functions for errors.
func makeInvalidError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeInvalid, field, value}
}

func makeNotSupportedError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeNotSupported, field, value}
}

func makeDuplicateError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeDuplicate, field, value}
}

func makeNotFoundError(field string, value interface{}) ValidationError {
	return ValidationError{ErrTypeNotFound, field, value}
}

// A helper for accumulating errors.  This could be moved to util if anyone else needs it.
type errorList []error

func (list *errorList) Append(errs ...error) {
	*list = append(*list, errs...)
}

func validateVolumes(volumes []Volume) (util.StringSet, errorList) {
	allErrs := errorList{}

	allNames := util.StringSet{}
	for i := range volumes {
		vol := &volumes[i] // so we can set default values
		errs := errorList{}
		// TODO(thockin) enforce that a source is set once we deprecate the implied form.
		if vol.Source != nil {
			errs = validateSource(vol.Source)
		}
		if !util.IsDNSLabel(vol.Name) {
			errs.Append(makeInvalidError("Volume.Name", vol.Name))
		} else if allNames.Has(vol.Name) {
			errs.Append(makeDuplicateError("Volume.Name", vol.Name))
		}
		if len(errs) == 0 {
			allNames.Insert(vol.Name)
		} else {
			allErrs.Append(errs...)
		}
	}
	return allNames, allErrs
}

func validateSource(source *VolumeSource) errorList {
	numVolumes := 0
	allErrs := errorList{}
	if source.HostDirectory != nil {
		numVolumes++
		allErrs.Append(validateHostDir(source.HostDirectory)...)
	}
	if source.EmptyDirectory != nil {
		numVolumes++
		//EmptyDirs have nothing to validate
	}
	if numVolumes != 1 {
		allErrs.Append(makeInvalidError("Volume.Source", source))
	}
	return allErrs
}

func validateHostDir(hostDir *HostDirectory) errorList {
	allErrs := errorList{}
	if hostDir.Path == "" {
		allErrs.Append(makeNotFoundError("HostDir.Path", hostDir.Path))
	}
	return allErrs
}

var supportedPortProtocols = util.NewStringSet("TCP", "UDP")

func validatePorts(ports []Port) errorList {
	allErrs := errorList{}

	allNames := util.StringSet{}
	for i := range ports {
		port := &ports[i] // so we can set default values
		if len(port.Name) > 0 {
			if len(port.Name) > 63 || !util.IsDNSLabel(port.Name) {
				allErrs.Append(makeInvalidError("Port.Name", port.Name))
			} else if allNames.Has(port.Name) {
				allErrs.Append(makeDuplicateError("Port.name", port.Name))
			} else {
				allNames.Insert(port.Name)
			}
		}
		if !util.IsValidPortNum(port.ContainerPort) {
			allErrs.Append(makeInvalidError("Port.ContainerPort", port.ContainerPort))
		}
		if port.HostPort == 0 {
			port.HostPort = port.ContainerPort
		} else if !util.IsValidPortNum(port.HostPort) {
			allErrs.Append(makeInvalidError("Port.HostPort", port.HostPort))
		}
		if len(port.Protocol) == 0 {
			port.Protocol = "TCP"
		} else if !supportedPortProtocols.Has(strings.ToUpper(port.Protocol)) {
			allErrs.Append(makeNotSupportedError("Port.Protocol", port.Protocol))
		}
	}
	return allErrs
}

func validateEnv(vars []EnvVar) errorList {
	allErrs := errorList{}

	for i := range vars {
		ev := &vars[i] // so we can set default values
		if len(ev.Name) == 0 {
			allErrs.Append(makeInvalidError("EnvVar.Name", ev.Name))
		}
		if !util.IsCIdentifier(ev.Name) {
			allErrs.Append(makeInvalidError("EnvVar.Name", ev.Name))
		}
	}
	return allErrs
}

func validateVolumeMounts(mounts []VolumeMount, volumes util.StringSet) errorList {
	allErrs := errorList{}

	for i := range mounts {
		mnt := &mounts[i] // so we can set default values
		if len(mnt.Name) == 0 {
			allErrs.Append(makeInvalidError("VolumeMount.Name", mnt.Name))
		} else if !volumes.Has(mnt.Name) {
			allErrs.Append(makeNotFoundError("VolumeMount.Name", mnt.Name))
		}
		if len(mnt.MountPath) == 0 {
			// Backwards compat.
			if len(mnt.Path) == 0 {
				allErrs.Append(makeInvalidError("VolumeMount.MountPath", mnt.MountPath))
			} else {
				glog.Warning("DEPRECATED: VolumeMount.Path has been replaced by VolumeMount.MountPath")
				mnt.MountPath = mnt.Path
				mnt.Path = ""
			}
		}
		if len(mnt.MountType) != 0 {
			glog.Warning("DEPRECATED: VolumeMount.MountType will be removed. The Volume struct will handle types")
		}
	}
	return allErrs
}

// AccumulateUniquePorts runs an extraction function on each Port of each Container,
// accumulating the results and returning an error if any ports conflict.
func AccumulateUniquePorts(containers []Container, accumulator map[int]bool, extract func(*Port) int) errorList {
	allErrs := errorList{}

	for ci := range containers {
		ctr := &containers[ci]
		for pi := range ctr.Ports {
			port := extract(&ctr.Ports[pi])
			if accumulator[port] {
				allErrs.Append(makeDuplicateError("Port", port))
			} else {
				accumulator[port] = true
			}
		}
	}
	return allErrs
}

// Checks for colliding Port.HostPort values across a slice of containers.
func checkHostPortConflicts(containers []Container) errorList {
	allPorts := map[int]bool{}
	return AccumulateUniquePorts(containers, allPorts, func(p *Port) int { return p.HostPort })
}

func validateContainers(containers []Container, volumes util.StringSet) errorList {
	allErrs := errorList{}

	allNames := util.StringSet{}
	for i := range containers {
		ctr := &containers[i] // so we can set default values
		if !util.IsDNSLabel(ctr.Name) {
			allErrs.Append(makeInvalidError("Container.Name", ctr.Name))
		} else if allNames.Has(ctr.Name) {
			allErrs.Append(makeDuplicateError("Container.Name", ctr.Name))
		} else {
			allNames.Insert(ctr.Name)
		}
		if len(ctr.Image) == 0 {
			allErrs.Append(makeInvalidError("Container.Image", ctr.Name))
		}
		allErrs.Append(validatePorts(ctr.Ports)...)
		allErrs.Append(validateEnv(ctr.Env)...)
		allErrs.Append(validateVolumeMounts(ctr.VolumeMounts, volumes)...)
	}
	// Check for colliding ports across all containers.
	// TODO(thockin): This really is dependent on the network config of the host (IP per pod?)
	// and the config of the new manifest.  But we have not specced that out yet, so we'll just
	// make some assumptions for now.  As of now, pods share a network namespace, which means that
	// every Port.HostPort across the whole pod must be unique.
	allErrs.Append(checkHostPortConflicts(containers)...)

	return allErrs
}

var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2")

// ValidateManifest tests that the specified ContainerManifest has valid data.
// This includes checking formatting and uniqueness.  It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
func ValidateManifest(manifest *ContainerManifest) []error {
	allErrs := errorList{}

	if len(manifest.Version) == 0 {
		allErrs.Append(makeInvalidError("ContainerManifest.Version", manifest.Version))
	} else if !supportedManifestVersions.Has(strings.ToLower(manifest.Version)) {
		allErrs.Append(makeNotSupportedError("ContainerManifest.Version", manifest.Version))
	}
	allVolumes, errs := validateVolumes(manifest.Volumes)
	if len(errs) != 0 {
		allErrs.Append(errs...)
	}
	allErrs.Append(validateContainers(manifest.Containers, allVolumes)...)
	return []error(allErrs)
}

func ValidatePodState(podState *PodState) []error {
	allErrs := errorList(ValidateManifest(&podState.Manifest))
	if podState.RestartPolicy.Type == "" {
		podState.RestartPolicy.Type = RestartAlways
	} else if podState.RestartPolicy.Type != RestartAlways &&
		podState.RestartPolicy.Type != RestartOnFailure &&
		podState.RestartPolicy.Type != RestartNever {
		allErrs.Append(makeNotSupportedError("PodState.RestartPolicy.Type", podState.RestartPolicy.Type))
	}

	return []error(allErrs)
}

// Pod tests if required fields in the pod are set.
func ValidatePod(pod *Pod) []error {
	allErrs := errorList{}
	if pod.ID == "" {
		allErrs.Append(makeInvalidError("Pod.ID", pod.ID))
	}
	allErrs.Append(ValidatePodState(&pod.DesiredState)...)
	return []error(allErrs)
}

// ValidateService tests if required fields in the service are set.
func ValidateService(service *Service) []error {
	allErrs := errorList{}
	if service.ID == "" {
		allErrs.Append(makeInvalidError("Service.ID", service.ID))
	} else if !util.IsDNS952Label(service.ID) {
		allErrs.Append(makeInvalidError("Service.ID", service.ID))
	}
	if labels.Set(service.Selector).AsSelector().Empty() {
		allErrs.Append(makeInvalidError("Service.Selector", service.Selector))
	}
	return []error(allErrs)
}

// ValidateReplicationController tests if required fields in the replication controller are set.
func ValidateReplicationController(controller *ReplicationController) []error {
	errors := []error{}
	if controller.ID == "" {
		errors = append(errors, makeInvalidError("ReplicationController.ID", controller.ID))
	}
	if labels.Set(controller.DesiredState.ReplicaSelector).AsSelector().Empty() {
		errors = append(errors, makeInvalidError("ReplicationController.ReplicaSelector", controller.DesiredState.ReplicaSelector))
	}
	if controller.DesiredState.Replicas < 0 {
		errors = append(errors, makeInvalidError("ReplicationController.Replicas", controller.DesiredState.Replicas))
	}
	errors = append(errors, ValidateManifest(&controller.DesiredState.PodTemplate.DesiredState.Manifest)...)
	return errors
}
<commit_msg>Use new api/errors, get rid of api.* error code<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
	"strings"

	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
)

func validateVolumes(volumes []Volume) (util.StringSet, errs.ErrorList) {
	allErrs := errs.ErrorList{}

	allNames := util.StringSet{}
	for i := range volumes {
		vol := &volumes[i] // so we can set default values
		el := errs.ErrorList{}
		// TODO(thockin) enforce that a source is set once we deprecate the implied form.
		if vol.Source != nil {
			el = validateSource(vol.Source)
		}
		if !util.IsDNSLabel(vol.Name) {
			el.Append(errs.NewInvalid("Volume.Name", vol.Name))
		} else if allNames.Has(vol.Name) {
			el.Append(errs.NewDuplicate("Volume.Name", vol.Name))
		}
		if len(el) == 0 {
			allNames.Insert(vol.Name)
		} else {
			allErrs.Append(el...)
		}
	}
	return allNames, allErrs
}

func validateSource(source *VolumeSource) errs.ErrorList {
	numVolumes := 0
	allErrs := errs.ErrorList{}
	if source.HostDirectory != nil {
		numVolumes++
		allErrs.Append(validateHostDir(source.HostDirectory)...)
	}
	if source.EmptyDirectory != nil {
		numVolumes++
		//EmptyDirs have nothing to validate
	}
	if numVolumes != 1 {
		allErrs.Append(errs.NewInvalid("Volume.Source", source))
	}
	return allErrs
}

func validateHostDir(hostDir *HostDirectory) errs.ErrorList {
	allErrs := errs.ErrorList{}
	if hostDir.Path == "" {
		allErrs.Append(errs.NewNotFound("HostDir.Path", hostDir.Path))
	}
	return allErrs
}

var supportedPortProtocols = util.NewStringSet("TCP", "UDP")

func validatePorts(ports []Port) errs.ErrorList {
	allErrs := errs.ErrorList{}

	allNames := util.StringSet{}
	for i := range ports {
		port := &ports[i] // so we can set default values
		if len(port.Name) > 0 {
			if len(port.Name) > 63 || !util.IsDNSLabel(port.Name) {
				allErrs.Append(errs.NewInvalid("Port.Name", port.Name))
			} else if allNames.Has(port.Name) {
				allErrs.Append(errs.NewDuplicate("Port.name", port.Name))
			} else {
				allNames.Insert(port.Name)
			}
		}
		if !util.IsValidPortNum(port.ContainerPort) {
			allErrs.Append(errs.NewInvalid("Port.ContainerPort", port.ContainerPort))
		}
		if port.HostPort == 0 {
			port.HostPort = port.ContainerPort
		} else if !util.IsValidPortNum(port.HostPort) {
			allErrs.Append(errs.NewInvalid("Port.HostPort", port.HostPort))
		}
		if len(port.Protocol) == 0 {
			port.Protocol = "TCP"
		} else if !supportedPortProtocols.Has(strings.ToUpper(port.Protocol)) {
			allErrs.Append(errs.NewNotSupported("Port.Protocol", port.Protocol))
		}
	}
	return allErrs
}

func validateEnv(vars []EnvVar) errs.ErrorList {
	allErrs := errs.ErrorList{}

	for i := range vars {
		ev := &vars[i] // so we can set default values
		if len(ev.Name) == 0 {
			allErrs.Append(errs.NewInvalid("EnvVar.Name", ev.Name))
		}
		if !util.IsCIdentifier(ev.Name) {
			allErrs.Append(errs.NewInvalid("EnvVar.Name", ev.Name))
		}
	}
	return allErrs
}

func validateVolumeMounts(mounts []VolumeMount, volumes util.StringSet) errs.ErrorList {
	allErrs := errs.ErrorList{}

	for i := range mounts {
		mnt := &mounts[i] // so we can set default values
		if len(mnt.Name) == 0 {
			allErrs.Append(errs.NewInvalid("VolumeMount.Name", mnt.Name))
		} else if !volumes.Has(mnt.Name) {
			allErrs.Append(errs.NewNotFound("VolumeMount.Name", mnt.Name))
		}
		if len(mnt.MountPath) == 0 {
			// Backwards compat.
			if len(mnt.Path) == 0 {
				allErrs.Append(errs.NewInvalid("VolumeMount.MountPath", mnt.MountPath))
			} else {
				glog.Warning("DEPRECATED: VolumeMount.Path has been replaced by VolumeMount.MountPath")
				mnt.MountPath = mnt.Path
				mnt.Path = ""
			}
		}
		if len(mnt.MountType) != 0 {
			glog.Warning("DEPRECATED: VolumeMount.MountType will be removed. The Volume struct will handle types")
		}
	}
	return allErrs
}

// AccumulateUniquePorts runs an extraction function on each Port of each Container,
// accumulating the results and returning an error if any ports conflict.
func AccumulateUniquePorts(containers []Container, accumulator map[int]bool, extract func(*Port) int) errs.ErrorList {
	allErrs := errs.ErrorList{}

	for ci := range containers {
		ctr := &containers[ci]
		for pi := range ctr.Ports {
			port := extract(&ctr.Ports[pi])
			if accumulator[port] {
				allErrs.Append(errs.NewDuplicate("Port", port))
			} else {
				accumulator[port] = true
			}
		}
	}
	return allErrs
}

// Checks for colliding Port.HostPort values across a slice of containers.
func checkHostPortConflicts(containers []Container) errs.ErrorList {
	allPorts := map[int]bool{}
	return AccumulateUniquePorts(containers, allPorts, func(p *Port) int { return p.HostPort })
}

func validateContainers(containers []Container, volumes util.StringSet) errs.ErrorList {
	allErrs := errs.ErrorList{}

	allNames := util.StringSet{}
	for i := range containers {
		ctr := &containers[i] // so we can set default values
		if !util.IsDNSLabel(ctr.Name) {
			allErrs.Append(errs.NewInvalid("Container.Name", ctr.Name))
		} else if allNames.Has(ctr.Name) {
			allErrs.Append(errs.NewDuplicate("Container.Name", ctr.Name))
		} else {
			allNames.Insert(ctr.Name)
		}
		if len(ctr.Image) == 0 {
			allErrs.Append(errs.NewInvalid("Container.Image", ctr.Name))
		}
		allErrs.Append(validatePorts(ctr.Ports)...)
		allErrs.Append(validateEnv(ctr.Env)...)
		allErrs.Append(validateVolumeMounts(ctr.VolumeMounts, volumes)...)
	}
	// Check for colliding ports across all containers.
	// TODO(thockin): This really is dependent on the network config of the host (IP per pod?)
	// and the config of the new manifest.  But we have not specced that out yet, so we'll just
	// make some assumptions for now.  As of now, pods share a network namespace, which means that
	// every Port.HostPort across the whole pod must be unique.
	allErrs.Append(checkHostPortConflicts(containers)...)

	return allErrs
}

var supportedManifestVersions = util.NewStringSet("v1beta1", "v1beta2")

// ValidateManifest tests that the specified ContainerManifest has valid data.
// This includes checking formatting and uniqueness.  It also canonicalizes the
// structure by setting default values and implementing any backwards-compatibility
// tricks.
func ValidateManifest(manifest *ContainerManifest) []error {
	allErrs := errs.ErrorList{}

	if len(manifest.Version) == 0 {
		allErrs.Append(errs.NewInvalid("ContainerManifest.Version", manifest.Version))
	} else if !supportedManifestVersions.Has(strings.ToLower(manifest.Version)) {
		allErrs.Append(errs.NewNotSupported("ContainerManifest.Version", manifest.Version))
	}
	allVolumes, errs := validateVolumes(manifest.Volumes)
	if len(errs) != 0 {
		allErrs.Append(errs...)
	}
	allErrs.Append(validateContainers(manifest.Containers, allVolumes)...)
	return []error(allErrs)
}

func ValidatePodState(podState *PodState) []error {
	allErrs := errs.ErrorList(ValidateManifest(&podState.Manifest))
	if podState.RestartPolicy.Type == "" {
		podState.RestartPolicy.Type = RestartAlways
	} else if podState.RestartPolicy.Type != RestartAlways &&
		podState.RestartPolicy.Type != RestartOnFailure &&
		podState.RestartPolicy.Type != RestartNever {
		allErrs.Append(errs.NewNotSupported("PodState.RestartPolicy.Type", podState.RestartPolicy.Type))
	}

	return []error(allErrs)
}

// Pod tests if required fields in the pod are set.
func ValidatePod(pod *Pod) []error {
	allErrs := errs.ErrorList{}
	if pod.ID == "" {
		allErrs.Append(errs.NewInvalid("Pod.ID", pod.ID))
	}
	allErrs.Append(ValidatePodState(&pod.DesiredState)...)
	return []error(allErrs)
}

// ValidateService tests if required fields in the service are set.
func ValidateService(service *Service) []error {
	allErrs := errs.ErrorList{}
	if service.ID == "" {
		allErrs.Append(errs.NewInvalid("Service.ID", service.ID))
	} else if !util.IsDNS952Label(service.ID) {
		allErrs.Append(errs.NewInvalid("Service.ID", service.ID))
	}
	if labels.Set(service.Selector).AsSelector().Empty() {
		allErrs.Append(errs.NewInvalid("Service.Selector", service.Selector))
	}
	return []error(allErrs)
}

// ValidateReplicationController tests if required fields in the replication controller are set.
func ValidateReplicationController(controller *ReplicationController) []error {
	el := []error{}
	if controller.ID == "" {
		el = append(el, errs.NewInvalid("ReplicationController.ID", controller.ID))
	}
	if labels.Set(controller.DesiredState.ReplicaSelector).AsSelector().Empty() {
		el = append(el, errs.NewInvalid("ReplicationController.ReplicaSelector", controller.DesiredState.ReplicaSelector))
	}
	if controller.DesiredState.Replicas < 0 {
		el = append(el, errs.NewInvalid("ReplicationController.Replicas", controller.DesiredState.Replicas))
	}
	el = append(el, ValidateManifest(&controller.DesiredState.PodTemplate.DesiredState.Manifest)...)
	return el
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	_, err := exec.LookPath("npm")
	if err != nil {
		fmt.Println("FATAL ERROR: could not find NPM\n" +
			"SEE: https://nodejs.org/ for installation instructions...")
		return
	}
	_, err = exec.LookPath("polymer")
	if err != nil {
		fmt.Println("FATAL ERROR: could not find Polymer CLI\n" +
			"SEE: https://polymer-library.polymer-project.org/3.0/docs/tools/polymer-cli for installation instructions...")
		return
	}
	err = os.Chdir("./public")
	if err != nil {
		fmt.Printf("FATAL ERROR: could not descend into ./public folder: %v\n", err)
		return
	}
	fmt.Println("*** INSTALLING WEBCOMPONENTS ***")
	cmd := exec.Command("npm", "install")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Printf("FATAL ERROR: could not fetch webcomponents: %v", err)
		return
	}
	fmt.Println("*** SUCCESS ***")
	err = os.Chdir("../")
	if err != nil {
		fmt.Printf("FATAL ERROR: could not descend into main folder: %v\n", err)
		return
	}
	fmt.Println("*** BUILDING POLYMER SOURCES ***")
	cmd = exec.Command("polymer", "build")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Printf("FATAL ERROR: could not build Polymer sources: %v", err)
		return
	}
	fmt.Println("*** SUCCESS ***")
}
<commit_msg>better msg<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	_, err := exec.LookPath("npm")
	if err != nil {
		fmt.Println("FATAL ERROR: could not find NPM\n" +
			"SEE: https://nodejs.org/ for installation instructions...")
		return
	}
	_, err = exec.LookPath("polymer")
	if err != nil {
		fmt.Println("FATAL ERROR: could not find Polymer CLI\n" +
			"SEE: https://polymer-library.polymer-project.org/3.0/docs/tools/polymer-cli for installation instructions...")
		return
	}
	err = os.Chdir("./public")
	if err != nil {
		fmt.Printf("FATAL ERROR: could not descend into ./public folder: %v\n", err)
		return
	}
	fmt.Println("*** INSTALLING WEBCOMPONENTS ***")
	cmd := exec.Command("npm", "install")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Printf("FATAL ERROR: could not fetch webcomponents: %v", err)
		return
	}
	fmt.Println("*** SUCCESS ***")
	err = os.Chdir("../")
	if err != nil {
		fmt.Printf("FATAL ERROR: could not return to the main folder: %v\n", err)
		return
	}
	fmt.Println("*** BUILDING POLYMER SOURCES ***")
	cmd = exec.Command("polymer", "build")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	if err != nil {
		fmt.Printf("FATAL ERROR: could not build Polymer sources: %v", err)
		return
	}
	fmt.Println("*** SUCCESS ***")
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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.
//
// Author: Tamir Duberstein (tamird@gmail.com)

package rpc

import (
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials"

	"github.com/cockroachdb/cockroach/base"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/grpcutil"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
	"github.com/cockroachdb/cockroach/util/syncutil"
	"github.com/cockroachdb/cockroach/util/timeutil"
)

const (
	defaultHeartbeatInterval = 3 * time.Second
	// The coefficient by which the maximum offset is multiplied to determine the
	// maximum acceptable measurement latency.
	maximumPingDurationMult = 2
)

// NewServer is a thin wrapper around grpc.NewServer that registers a heartbeat
// service.
func NewServer(ctx *Context) *grpc.Server {
	var s *grpc.Server
	if ctx.Insecure {
		s = grpc.NewServer()
	} else {
		tlsConfig, err := ctx.GetServerTLSConfig()
		if err != nil {
			panic(err)
		}
		s = grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)))
	}
	RegisterHeartbeatServer(s, &HeartbeatService{
		clock:              ctx.localClock,
		remoteClockMonitor: ctx.RemoteClocks,
	})
	return s
}

type connMeta struct {
	conn    *grpc.ClientConn
	healthy bool
}

// Context contains the fields required by the rpc framework.
type Context struct {
	*base.Context

	localClock   *hlc.Clock
	Stopper      *stop.Stopper
	RemoteClocks *RemoteClockMonitor

	HeartbeatInterval time.Duration
	HeartbeatTimeout  time.Duration
	HeartbeatCB       func()

	localInternalServer roachpb.InternalServer

	conns struct {
		syncutil.Mutex
		cache map[string]connMeta
	}
}

// NewContext creates an rpc Context with the supplied values.
func NewContext(baseCtx *base.Context, clock *hlc.Clock, stopper *stop.Stopper) *Context {
	ctx := &Context{
		Context: baseCtx,
	}
	if clock != nil {
		ctx.localClock = clock
	} else {
		ctx.localClock = hlc.NewClock(hlc.UnixNano)
	}
	ctx.Stopper = stopper
	ctx.RemoteClocks = newRemoteClockMonitor(clock, 10*defaultHeartbeatInterval)
	ctx.HeartbeatInterval = defaultHeartbeatInterval
	ctx.HeartbeatTimeout = 2 * defaultHeartbeatInterval

	stopper.RunWorker(func() {
		<-stopper.ShouldQuiesce()

		ctx.conns.Lock()
		for key, meta := range ctx.conns.cache {
			ctx.removeConn(key, meta.conn)
		}
		ctx.conns.Unlock()
	})

	return ctx
}

// GetLocalInternalServerForAddr returns the context's internal batch server
// for addr, if it exists.
func (ctx *Context) GetLocalInternalServerForAddr(addr string) roachpb.InternalServer {
	if addr == ctx.Addr {
		return ctx.localInternalServer
	}
	return nil
}

// SetLocalInternalServer sets the context's local internal batch server.
func (ctx *Context) SetLocalInternalServer(internalServer roachpb.InternalServer) {
	ctx.localInternalServer = internalServer
}

func (ctx *Context) removeConn(key string, conn *grpc.ClientConn) {
	if err := conn.Close(); err != nil && !grpcutil.IsClosedConnection(err) {
		if log.V(1) {
			log.Errorf(context.TODO(), "failed to close client connection: %s", err)
		}
	}
	delete(ctx.conns.cache, key)
}

// GRPCDial calls grpc.Dial with the options appropriate for the context.
func (ctx *Context) GRPCDial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	if meta, ok := ctx.conns.cache[target]; ok {
		return meta.conn, nil
	}

	dialOpt, err := ctx.GRPCDialOption()
	if err != nil {
		return nil, err
	}

	dialOpts := make([]grpc.DialOption, 0, 1+len(opts))
	dialOpts = append(dialOpts, dialOpt)
	dialOpts = append(dialOpts, opts...)

	conn, err := grpc.Dial(target, dialOpts...)
	if err == nil {
		if ctx.conns.cache == nil {
			ctx.conns.cache = make(map[string]connMeta)
		}
		ctx.conns.cache[target] = connMeta{conn: conn}

		if ctx.Stopper.RunTask(func() {
			ctx.Stopper.RunWorker(func() {
				if err := ctx.runHeartbeat(conn, target); err != nil && !grpcutil.IsClosedConnection(err) {
					log.Error(context.TODO(), err)
				}
				ctx.conns.Lock()
				ctx.removeConn(target, conn)
				ctx.conns.Unlock()
			})
		}) != nil {
			ctx.removeConn(target, conn)
		}
	}
	return conn, err
}

// GRPCDialOption returns the GRPC dialing option appropriate for the context.
func (ctx *Context) GRPCDialOption() (grpc.DialOption, error) {
	var dialOpt grpc.DialOption
	if ctx.Insecure {
		dialOpt = grpc.WithInsecure()
	} else {
		tlsConfig, err := ctx.GetClientTLSConfig()
		if err != nil {
			return dialOpt, err
		}
		dialOpt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
	}
	return dialOpt, nil
}

// setConnHealthy sets the health status of the connection.
func (ctx *Context) setConnHealthy(remoteAddr string, healthy bool) {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	meta, ok := ctx.conns.cache[remoteAddr]
	if ok {
		meta.healthy = healthy
		ctx.conns.cache[remoteAddr] = meta
	}
}

// IsConnHealthy returns whether the most recent heartbeat succeeded or not.
// This should not be used as a definite status of a nodes health and just used
// to prioritized healthy nodes over unhealthy ones.
func (ctx *Context) IsConnHealthy(remoteAddr string) bool {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	return ctx.conns.cache[remoteAddr].healthy
}

func (ctx *Context) runHeartbeat(cc *grpc.ClientConn, remoteAddr string) error {
	request := PingRequest{Addr: ctx.Addr}
	heartbeatClient := NewHeartbeatClient(cc)

	var heartbeatTimer timeutil.Timer
	defer heartbeatTimer.Stop()
	for {
		// If we should stop, return immediately. Note that we check this
		// at the beginning and end of the loop because we may 'continue'
		// before reaching the end.
		select {
		case <-ctx.Stopper.ShouldStop():
			return nil
		default:
		}
		sendTime := ctx.localClock.PhysicalTime()
		response, err := ctx.heartbeat(heartbeatClient, request)
		ctx.setConnHealthy(remoteAddr, err == nil)
		if err != nil {
			if grpc.Code(err) == codes.DeadlineExceeded {
				continue
			}
			return err
		}
		receiveTime := ctx.localClock.PhysicalTime()

		// Only update the clock offset measurement if we actually got a
		// successful response from the server.
		if pingDuration := receiveTime.Sub(sendTime); pingDuration > maximumPingDurationMult*ctx.localClock.MaxOffset() {
			request.Offset.Reset()
		} else {
			// Offset and error are measured using the remote clock reading
			// technique described in
			// http://se.inf.tu-dresden.de/pubs/papers/SRDS1994.pdf, page 6.
			// However, we assume that drift and min message delay are 0, for
			// now.
			request.Offset.MeasuredAt = receiveTime.UnixNano()
			request.Offset.Uncertainty = (pingDuration / 2).Nanoseconds()
			remoteTimeNow := time.Unix(0, response.ServerTime).Add(pingDuration / 2)
			request.Offset.Offset = remoteTimeNow.Sub(receiveTime).Nanoseconds()
		}
		ctx.RemoteClocks.UpdateOffset(remoteAddr, request.Offset)

		if cb := ctx.HeartbeatCB; cb != nil {
			cb()
		}

		// Wait after the heartbeat so that the first iteration gets a wait-free
		// heartbeat attempt.
		heartbeatTimer.Reset(ctx.HeartbeatInterval)
		select {
		case <-ctx.Stopper.ShouldStop():
			return nil
		case <-heartbeatTimer.C:
			heartbeatTimer.Read = true
		}
	}
}

func (ctx *Context) heartbeat(heartbeatClient HeartbeatClient, request PingRequest) (*PingResponse, error) {
	goCtx, cancel := context.WithTimeout(context.Background(), ctx.HeartbeatTimeout)
	defer cancel()
	// NB: We want the request to fail-fast (the default), otherwise we won't be
	// notified of transport failures.
	return heartbeatClient.Ping(goCtx, &request)
}
<commit_msg>rpc: set the max-msg-size to math.MaxInt32 to match previous default<commit_after>// Copyright 2015 The Cockroach 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.
//
// Author: Tamir Duberstein (tamird@gmail.com)

package rpc

import (
	"math"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/credentials"

	"github.com/cockroachdb/cockroach/base"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/util/grpcutil"
	"github.com/cockroachdb/cockroach/util/hlc"
	"github.com/cockroachdb/cockroach/util/log"
	"github.com/cockroachdb/cockroach/util/stop"
	"github.com/cockroachdb/cockroach/util/syncutil"
	"github.com/cockroachdb/cockroach/util/timeutil"
)

const (
	defaultHeartbeatInterval = 3 * time.Second
	// The coefficient by which the maximum offset is multiplied to determine the
	// maximum acceptable measurement latency.
	maximumPingDurationMult = 2
)

// NewServer is a thin wrapper around grpc.NewServer that registers a heartbeat
// service.
func NewServer(ctx *Context) *grpc.Server {
	opts := []grpc.ServerOption{
		grpc.MaxMsgSize(math.MaxInt32), // TODO(peter,tamird): need tests before lowering
	}
	if !ctx.Insecure {
		tlsConfig, err := ctx.GetServerTLSConfig()
		if err != nil {
			panic(err)
		}
		opts = append(opts, grpc.Creds(credentials.NewTLS(tlsConfig)))
	}
	s := grpc.NewServer(opts...)
	RegisterHeartbeatServer(s, &HeartbeatService{
		clock:              ctx.localClock,
		remoteClockMonitor: ctx.RemoteClocks,
	})
	return s
}

type connMeta struct {
	conn    *grpc.ClientConn
	healthy bool
}

// Context contains the fields required by the rpc framework.
type Context struct {
	*base.Context

	localClock   *hlc.Clock
	Stopper      *stop.Stopper
	RemoteClocks *RemoteClockMonitor

	HeartbeatInterval time.Duration
	HeartbeatTimeout  time.Duration
	HeartbeatCB       func()

	localInternalServer roachpb.InternalServer

	conns struct {
		syncutil.Mutex
		cache map[string]connMeta
	}
}

// NewContext creates an rpc Context with the supplied values.
func NewContext(baseCtx *base.Context, clock *hlc.Clock, stopper *stop.Stopper) *Context {
	ctx := &Context{
		Context: baseCtx,
	}
	if clock != nil {
		ctx.localClock = clock
	} else {
		ctx.localClock = hlc.NewClock(hlc.UnixNano)
	}
	ctx.Stopper = stopper
	ctx.RemoteClocks = newRemoteClockMonitor(clock, 10*defaultHeartbeatInterval)
	ctx.HeartbeatInterval = defaultHeartbeatInterval
	ctx.HeartbeatTimeout = 2 * defaultHeartbeatInterval

	stopper.RunWorker(func() {
		<-stopper.ShouldQuiesce()

		ctx.conns.Lock()
		for key, meta := range ctx.conns.cache {
			ctx.removeConn(key, meta.conn)
		}
		ctx.conns.Unlock()
	})

	return ctx
}

// GetLocalInternalServerForAddr returns the context's internal batch server
// for addr, if it exists.
func (ctx *Context) GetLocalInternalServerForAddr(addr string) roachpb.InternalServer {
	if addr == ctx.Addr {
		return ctx.localInternalServer
	}
	return nil
}

// SetLocalInternalServer sets the context's local internal batch server.
func (ctx *Context) SetLocalInternalServer(internalServer roachpb.InternalServer) {
	ctx.localInternalServer = internalServer
}

func (ctx *Context) removeConn(key string, conn *grpc.ClientConn) {
	if err := conn.Close(); err != nil && !grpcutil.IsClosedConnection(err) {
		if log.V(1) {
			log.Errorf(context.TODO(), "failed to close client connection: %s", err)
		}
	}
	delete(ctx.conns.cache, key)
}

// GRPCDial calls grpc.Dial with the options appropriate for the context.
func (ctx *Context) GRPCDial(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	if meta, ok := ctx.conns.cache[target]; ok {
		return meta.conn, nil
	}

	dialOpt, err := ctx.GRPCDialOption()
	if err != nil {
		return nil, err
	}

	dialOpts := make([]grpc.DialOption, 0, 1+len(opts))
	dialOpts = append(dialOpts, dialOpt)
	dialOpts = append(dialOpts, opts...)

	conn, err := grpc.Dial(target, dialOpts...)
	if err == nil {
		if ctx.conns.cache == nil {
			ctx.conns.cache = make(map[string]connMeta)
		}
		ctx.conns.cache[target] = connMeta{conn: conn}

		if ctx.Stopper.RunTask(func() {
			ctx.Stopper.RunWorker(func() {
				if err := ctx.runHeartbeat(conn, target); err != nil && !grpcutil.IsClosedConnection(err) {
					log.Error(context.TODO(), err)
				}
				ctx.conns.Lock()
				ctx.removeConn(target, conn)
				ctx.conns.Unlock()
			})
		}) != nil {
			ctx.removeConn(target, conn)
		}
	}
	return conn, err
}

// GRPCDialOption returns the GRPC dialing option appropriate for the context.
func (ctx *Context) GRPCDialOption() (grpc.DialOption, error) {
	var dialOpt grpc.DialOption
	if ctx.Insecure {
		dialOpt = grpc.WithInsecure()
	} else {
		tlsConfig, err := ctx.GetClientTLSConfig()
		if err != nil {
			return dialOpt, err
		}
		dialOpt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
	}
	return dialOpt, nil
}

// setConnHealthy sets the health status of the connection.
func (ctx *Context) setConnHealthy(remoteAddr string, healthy bool) {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	meta, ok := ctx.conns.cache[remoteAddr]
	if ok {
		meta.healthy = healthy
		ctx.conns.cache[remoteAddr] = meta
	}
}

// IsConnHealthy returns whether the most recent heartbeat succeeded or not.
// This should not be used as a definite status of a nodes health and just used
// to prioritized healthy nodes over unhealthy ones.
func (ctx *Context) IsConnHealthy(remoteAddr string) bool {
	ctx.conns.Lock()
	defer ctx.conns.Unlock()

	return ctx.conns.cache[remoteAddr].healthy
}

func (ctx *Context) runHeartbeat(cc *grpc.ClientConn, remoteAddr string) error {
	request := PingRequest{Addr: ctx.Addr}
	heartbeatClient := NewHeartbeatClient(cc)

	var heartbeatTimer timeutil.Timer
	defer heartbeatTimer.Stop()
	for {
		// If we should stop, return immediately. Note that we check this
		// at the beginning and end of the loop because we may 'continue'
		// before reaching the end.
		select {
		case <-ctx.Stopper.ShouldStop():
			return nil
		default:
		}
		sendTime := ctx.localClock.PhysicalTime()
		response, err := ctx.heartbeat(heartbeatClient, request)
		ctx.setConnHealthy(remoteAddr, err == nil)
		if err != nil {
			if grpc.Code(err) == codes.DeadlineExceeded {
				continue
			}
			return err
		}
		receiveTime := ctx.localClock.PhysicalTime()

		// Only update the clock offset measurement if we actually got a
		// successful response from the server.
		if pingDuration := receiveTime.Sub(sendTime); pingDuration > maximumPingDurationMult*ctx.localClock.MaxOffset() {
			request.Offset.Reset()
		} else {
			// Offset and error are measured using the remote clock reading
			// technique described in
			// http://se.inf.tu-dresden.de/pubs/papers/SRDS1994.pdf, page 6.
			// However, we assume that drift and min message delay are 0, for
			// now.
			request.Offset.MeasuredAt = receiveTime.UnixNano()
			request.Offset.Uncertainty = (pingDuration / 2).Nanoseconds()
			remoteTimeNow := time.Unix(0, response.ServerTime).Add(pingDuration / 2)
			request.Offset.Offset = remoteTimeNow.Sub(receiveTime).Nanoseconds()
		}
		ctx.RemoteClocks.UpdateOffset(remoteAddr, request.Offset)

		if cb := ctx.HeartbeatCB; cb != nil {
			cb()
		}

		// Wait after the heartbeat so that the first iteration gets a wait-free
		// heartbeat attempt.
		heartbeatTimer.Reset(ctx.HeartbeatInterval)
		select {
		case <-ctx.Stopper.ShouldStop():
			return nil
		case <-heartbeatTimer.C:
			heartbeatTimer.Read = true
		}
	}
}

func (ctx *Context) heartbeat(heartbeatClient HeartbeatClient, request PingRequest) (*PingResponse, error) {
	goCtx, cancel := context.WithTimeout(context.Background(), ctx.HeartbeatTimeout)
	defer cancel()
	// NB: We want the request to fail-fast (the default), otherwise we won't be
	// notified of transport failures.
	return heartbeatClient.Ping(goCtx, &request)
}
<|endoftext|>