<commit_before>package wakatime

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// Version is the library version
const Version = "0.1"

// APIBase is the root URL of the API
const APIBase = "https://wakatime.com/api/v1/"

// CurrentUser replaces the current user in the request
const CurrentUser = "current"

const dateFormat = "01/02/2006"

// Range is the stats report interval range
type Range string

// Stats report ranges
const (
	Last7Days   Range = "last_7_days"
	Last30Days        = "last_30_days"
	Last6Months       = "last_6_months"
	LastYear          = "last_year"
	AllTime           = "all_time"
)

// Time is time.Time alias, used for parsing response timestamps
type Time time.Time

// WakaTime is the main structure
type WakaTime struct {
	client *http.Client
}

// DurationsData is single duration segment
type DurationsData struct {
	Duration Time
	Project  string
	Time     Time
}

// Durations is the structure rerurned by the durations request
type Durations struct {
	Branches []string
	Data     []DurationsData
	End      Time
	Start    Time
	TimeZone string
}

// StatsItem is single item in the stats report
type StatsItem struct {
	CreatedAt    time.Time
	ID           string
	ModifiedAt   time.Time
	Name         string
	Percent      float32
	TotalSeconds int
}

// StatsEditor represents editor data in the stats report
type StatsEditor StatsItem

// StatsLanguage represents language data in the stats report
type StatsLanguage StatsItem

// StatsOperatingSystem represents operating system data in the stats report
type StatsOperatingSystem StatsItem

// StatsProject represents project data in the stats report
type StatsProject StatsItem

// StatsData is the main data body in the stats report
type StatsData struct {
	CreatedAt                 time.Time
	Editors                   []StatsEditor
	End                       Time
	HumanReadableDailyAverage string
	HumanReadableTotal        string
	ID                        string
	IsUpToDate                bool
	Languages                 []StatsLanguage
	ModifiedAt                time.Time
	OperatingSystems          []StatsOperatingSystem
	Project                   *string
	Projects                  []StatsProject
	Range                     Range
	Start                     Time
	Status                    string
	Timeout                   int
	Timezone                  string
	TotalSeconds              int
	UserID                    string
	Username                  string
	WritesOnly                bool
}

// Stats is the data returned by the stats report
type Stats struct {
	Data StatsData
}

type SummaryGrandTotal struct {
	Digital      string
	Hours        int
	Minutes      int
	Seconds      int
	Text         string
	TotalSeconds int
}

type SummaryItem struct {
	SummaryGrandTotal
	Name    string
	Percent float32
}

type SummaryEditor SummaryItem
type SummaryLanguage SummaryItem
type SummaryOperatingSystem SummaryItem
type SummaryProject SummaryItem

type SumaryRange struct {
	Date      string
	DateHuman string
	End       Time
	Start     Time
	Text      string
	Timezone  string
}

type SummariesData struct {
	Editors          []SummaryEditor
	GrandTotal       SummaryGrandTotal
	Languages        []SummaryLanguage
	OperatingSystems []SummaryOperatingSystem
	Projects         []SummaryProject
	Range            SumaryRange
}

type Summaries struct {
	Data  []SummariesData
	End   Time
	Start Time
}

// New initializes the library
func New(rt http.RoundTripper) *WakaTime {
	return &WakaTime{
		client: &http.Client{
			Transport: rt,
		},
	}
}

// Durations fetches the durations report
func (wt *WakaTime) Durations(user string, date time.Time, project, branches *string) (*Durations, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/durations"
	q := u.Query()
	q.Set("date", date.Format(dateFormat))
	if project != nil {
		q.Set("project", *project)
	}
	if branches != nil {
		q.Set("branches", *branches)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var dr Durations
	if err = json.Unmarshal(content, &dr); err != nil {
		return nil, err
	}
	return &dr, nil
}

// Stats fetches the stats report
func (wt *WakaTime) Stats(user string, rng Range, timeout *int, writesOnly *bool, project *string) (*Stats, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/stats/" + rng.String()
	q := u.Query()
	if timeout != nil {
		q.Set("timeout", strconv.Itoa(*timeout))
	}
	if writesOnly != nil {
		q.Set("writes_only", strconv.FormatBool(*writesOnly))
	}
	if project != nil {
		q.Set("project", *project)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var st Stats
	if err = json.Unmarshal(content, &st); err != nil {
		return nil, err
	}
	return &st, nil
}

// Summaries fetches the summaries report
func (wt *WakaTime) Summaries(user string, start, end time.Time, project, branches *string) (*Summaries, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/summaries"
	q := u.Query()
	q.Set("start", start.Format(dateFormat))
	q.Set("end", start.Format(dateFormat))
	if project != nil {
		q.Set("project", *project)
	}
	if branches != nil {
		q.Set("branches", *branches)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var sm Summaries
	if err = json.Unmarshal(content, &sm); err != nil {
		return nil, err
	}
	return &sm, nil
}

// Users fetches the users report
func (wt *WakaTime) Users() {}

// UnmarshalJSON unmarshals the Time type
func (ut *Time) UnmarshalJSON(data []byte) error {
	ts, err := strconv.ParseFloat(string(data), 32)
	if err != nil {
		return err
	}
	sec := int64(ts)
	ns := int64((ts - float64(sec)) * float64(time.Second))
	*ut = Time(time.Unix(int64(sec), ns))
	return nil
}

// String returns the string representation of Range
func (r Range) String() string {
	return string(r)
}

func (wt *WakaTime) fetchURL(url string) ([]byte, error) {
	var err error
	var resp *http.Response
	if resp, err = wt.client.Get(url); err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP Error: %d", resp.StatusCode)
	}
	return ioutil.ReadAll(resp.Body)
}
<commit_msg>Users<commit_after>package wakatime

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// Version is the library version
const Version = "0.1"

// APIBase is the root URL of the API
const APIBase = "https://wakatime.com/api/v1/"

// CurrentUser replaces the current user in the request
const CurrentUser = "current"

const dateFormat = "01/02/2006"

// Range is the stats report interval range
type Range string

// Stats report ranges
const (
	Last7Days   Range = "last_7_days"
	Last30Days        = "last_30_days"
	Last6Months       = "last_6_months"
	LastYear          = "last_year"
	AllTime           = "all_time"
)

// Time is time.Time alias, used for parsing response timestamps
type Time time.Time

// WakaTime is the main structure
type WakaTime struct {
	client *http.Client
}

// DurationsData is single duration segment
type DurationsData struct {
	Duration Time
	Project  string
	Time     Time
}

// Durations is the structure rerurned by the durations request
type Durations struct {
	Branches []string
	Data     []DurationsData
	End      Time
	Start    Time
	TimeZone string
}

// StatsItem is single item in the stats report
type StatsItem struct {
	CreatedAt    time.Time
	ID           string
	ModifiedAt   time.Time
	Name         string
	Percent      float32
	TotalSeconds int
}

// StatsEditor represents editor data in the stats report
type StatsEditor StatsItem

// StatsLanguage represents language data in the stats report
type StatsLanguage StatsItem

// StatsOperatingSystem represents operating system data in the stats report
type StatsOperatingSystem StatsItem

// StatsProject represents project data in the stats report
type StatsProject StatsItem

// StatsData is the main data body in the stats report
type StatsData struct {
	CreatedAt                 time.Time
	Editors                   []StatsEditor
	End                       Time
	HumanReadableDailyAverage string
	HumanReadableTotal        string
	ID                        string
	IsUpToDate                bool
	Languages                 []StatsLanguage
	ModifiedAt                time.Time
	OperatingSystems          []StatsOperatingSystem
	Project                   *string
	Projects                  []StatsProject
	Range                     Range
	Start                     Time
	Status                    string
	Timeout                   int
	Timezone                  string
	TotalSeconds              int
	UserID                    string
	Username                  string
	WritesOnly                bool
}

// Stats is the data returned by the stats report
type Stats struct {
	Data StatsData
}

type SummaryGrandTotal struct {
	Digital      string
	Hours        int
	Minutes      int
	Seconds      int
	Text         string
	TotalSeconds int
}

type SummaryItem struct {
	SummaryGrandTotal
	Name    string
	Percent float32
}

type SummaryEditor SummaryItem
type SummaryLanguage SummaryItem
type SummaryOperatingSystem SummaryItem
type SummaryProject SummaryItem

type SumaryRange struct {
	Date      string
	DateHuman string
	End       Time
	Start     Time
	Text      string
	Timezone  string
}

type SummariesData struct {
	Editors          []SummaryEditor
	GrandTotal       SummaryGrandTotal
	Languages        []SummaryLanguage
	OperatingSystems []SummaryOperatingSystem
	Projects         []SummaryProject
	Range            SumaryRange
}

type Summaries struct {
	Data  []SummariesData
	End   Time
	Start Time
}

type UserData struct {
	Created              time.Time
	Email                string
	EmailPublic          bool
	FullName             string
	HumanReadableWebsite string
	ID                   string
	LastHeartbeat        time.Time
	LastPlugin           string
	LastPluginName       string
	LastProject          string
	Location             string
	LoggedTimePublic     bool
	Modified             time.Time
	Photo                string
	PhotoPublic          string
	Plan                 string
	Timezone             string
	Username             string
	Website              string
}

type Users struct {
	Data UserData
}

// New initializes the library
func New(rt http.RoundTripper) *WakaTime {
	return &WakaTime{
		client: &http.Client{
			Transport: rt,
		},
	}
}

// Durations fetches the durations report
func (wt *WakaTime) Durations(user string, date time.Time, project, branches *string) (*Durations, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/durations"
	q := u.Query()
	q.Set("date", date.Format(dateFormat))
	if project != nil {
		q.Set("project", *project)
	}
	if branches != nil {
		q.Set("branches", *branches)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var dr Durations
	if err = json.Unmarshal(content, &dr); err != nil {
		return nil, err
	}
	return &dr, nil
}

// Stats fetches the stats report
func (wt *WakaTime) Stats(user string, rng Range, timeout *int, writesOnly *bool, project *string) (*Stats, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/stats/" + rng.String()
	q := u.Query()
	if timeout != nil {
		q.Set("timeout", strconv.Itoa(*timeout))
	}
	if writesOnly != nil {
		q.Set("writes_only", strconv.FormatBool(*writesOnly))
	}
	if project != nil {
		q.Set("project", *project)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var st Stats
	if err = json.Unmarshal(content, &st); err != nil {
		return nil, err
	}
	return &st, nil
}

// Summaries fetches the summaries report
func (wt *WakaTime) Summaries(user string, start, end time.Time, project, branches *string) (*Summaries, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user + "/summaries"
	q := u.Query()
	q.Set("start", start.Format(dateFormat))
	q.Set("end", start.Format(dateFormat))
	if project != nil {
		q.Set("project", *project)
	}
	if branches != nil {
		q.Set("branches", *branches)
	}
	u.RawQuery = q.Encode()
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var sm Summaries
	if err = json.Unmarshal(content, &sm); err != nil {
		return nil, err
	}
	return &sm, nil
}

// Users fetches the users report
func (wt *WakaTime) Users(user string) (*Users, error) {
	var err error
	var u *url.URL
	if u, err = url.Parse(APIBase); err != nil {
		return nil, err
	}
	u.Path += "users/" + user
	var content []byte
	if content, err = wt.fetchURL(u.String()); err != nil {
		return nil, err
	}
	var us Users
	if err = json.Unmarshal(content, &us); err != nil {
		return nil, err
	}
	return &us, nil
}

// UnmarshalJSON unmarshals the Time type
func (ut *Time) UnmarshalJSON(data []byte) error {
	ts, err := strconv.ParseFloat(string(data), 32)
	if err != nil {
		return err
	}
	sec := int64(ts)
	ns := int64((ts - float64(sec)) * float64(time.Second))
	*ut = Time(time.Unix(int64(sec), ns))
	return nil
}

// String returns the string representation of Range
func (r Range) String() string {
	return string(r)
}

func (wt *WakaTime) fetchURL(url string) ([]byte, error) {
	var err error
	var resp *http.Response
	if resp, err = wt.client.Get(url); err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP Error: %d", resp.StatusCode)
	}
	return ioutil.ReadAll(resp.Body)
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

const domain = "cozy.example.net"

func TestParseHost(t *testing.T) {
	apis := echo.New()

	apis.GET("/", func(c echo.Context) error {
		instance := middlewares.GetInstance(c)
		assert.NotNil(t, instance, "the instance should have been set in the echo context")
		return c.String(http.StatusOK, "OK")
	}, middlewares.NeedInstance)

	router, err := Create(apis, func(c echo.Context, domain, slug string) error {
		return c.String(200, "OK:"+slug)
	})

	if !assert.NoError(t, err) {
		return
	}

	urls := map[string]string{
		"https://" + domain + "/":        "OK",
		"https://foo." + domain + "/app": "OK:foo",
		"https://bar." + domain + "/app": "OK:bar",
	}

	for u, k := range urls {
		w := httptest.NewRecorder()
		req := httptest.NewRequest("GET", u, nil)
		router.ServeHTTP(w, req)
		assert.Equal(t, http.StatusOK, w.Code)
		assert.Equal(t, k, w.Body.String())
	}
}

func TestMain(m *testing.M) {
	config.UseTestFile()
	instance.Destroy(domain)
	instance.Create(domain, "en", nil)
	res := m.Run()
	instance.Destroy(domain)
	os.Exit(res)
}
<commit_msg>Fix tests<commit_after>package web

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/web/middlewares"
	"github.com/labstack/echo"
	"github.com/stretchr/testify/assert"
)

const domain = "cozy.example.net"

func TestParseHost(t *testing.T) {
	apis := echo.New()

	apis.GET("/", func(c echo.Context) error {
		instance := middlewares.GetInstance(c)
		assert.NotNil(t, instance, "the instance should have been set in the echo context")
		return c.String(http.StatusOK, "OK")
	}, middlewares.NeedInstance)

	router, err := Create(apis, func(c echo.Context) error {
		slug := c.Get("slug").(string)
		return c.String(200, "OK:"+slug)
	})

	if !assert.NoError(t, err) {
		return
	}

	urls := map[string]string{
		"https://" + domain + "/":        "OK",
		"https://foo." + domain + "/app": "OK:foo",
		"https://bar." + domain + "/app": "OK:bar",
	}

	for u, k := range urls {
		w := httptest.NewRecorder()
		req := httptest.NewRequest("GET", u, nil)
		router.ServeHTTP(w, req)
		assert.Equal(t, http.StatusOK, w.Code)
		assert.Equal(t, k, w.Body.String())
	}
}

func TestMain(m *testing.M) {
	config.UseTestFile()
	instance.Destroy(domain)
	instance.Create(domain, "en", nil)
	res := m.Run()
	instance.Destroy(domain)
	os.Exit(res)
}
<|endoftext|>


<commit_before>package trie

type keyTranslator func(key string) []int
type indexTranslator func(b byte) int

func indexResolver(f indexTranslator) keyTranslator {
	cache := map[string][]int{}
	return func(key string) []int {
		retr, ok := cache[key]
		if ok {
			return retr
		}

		retr = make([]int, len(key))
		for i, ch := range key {
			retr[i] = f(byte(ch))
		}
		cache[key] = retr
		return retr
	}
}

type TrieNode struct {
	Data interface{}
	// Tag should help to annotate a specific feature e.g color, discovered, isDir etc
	Tag      interface{}
	Children *[]*TrieNode
	// Eos(End-Of-Sequence) is designated to terminate a sequence.
	Eos bool
}

type alphabet struct {
	max          int
	min          int
	alphabetizer keyTranslator
}

func asciiAlphabetizer(ch byte) int {
	return int(ch)
}

var AsciiAlphabet = &alphabet{
	min:          0,
	max:          255, // TODO: Perform a sizeof(byte) op
	alphabetizer: indexResolver(asciiAlphabetizer),
}

var (
	NumericStart = 0
	NumericEnd   = 9
	NumericOther = (NumericEnd - NumericStart) + 1
)

func numericAlphabetizer(b byte) int {
	if b >= '0' && b <= '9' {
		return int(b - '0')
	}
	return NumericOther
}

var NumericAlphabet = &alphabet{
	min:          NumericStart,
	max:          NumericOther + 1,
	alphabetizer: indexResolver(numericAlphabetizer),
}

type Trie struct {
	root       *TrieNode
	translator *alphabet
}

func newTrieNode(data interface{}) *TrieNode {
	return &TrieNode{
		Data:     data,
		Children: nil,
	}
}

func (tn *TrieNode) findNode(alphaIndices []int) (cur *TrieNode, ok bool) {
	cur = tn
	i, max := 0, len(alphaIndices)
	for {
		if cur == nil || i >= max {
			break
		}
		next := cur.Children
		if next == nil {
			return nil, false
		}
		children := *next
		first := alphaIndices[i] % len(children)
		cur = children[first]
		i += 1
	}
	if cur == nil || !cur.Eos {
		return nil, false
	}

	return cur, true
}

func (tn *TrieNode) pop(alphaIndices []int) (popd interface{}, ok bool) {
	location, ok := tn.findNode(alphaIndices)
	if !ok || location == nil {
		return nil, false
	}

	popd = location.Data
	location.Data = nil
	location.Eos = false
	// TODO: Perform a check on whether all the Children are set to
	// nil, if so, clean up the array memory, free and set it to nil.

	return popd, true
}

func (tn *TrieNode) tagOn(pass func(*TrieNode) bool, tag interface{}) (count int) {
	count = 0
	defer func() {
		if pass(tn) {
			tn.Tag = tag
			count += 1
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		count += child.tagOn(pass, tag)
	}

	return count
}

func (tn *TrieNode) EOS() bool {
	return tn.Eos
}

func (tn *TrieNode) Match(pass func(*TrieNode) bool) (matches chan *TrieNode) {
	matches = make(chan *TrieNode)
	go func() {
		tn.match(pass, &matches)
		close(matches)
	}()
	return matches
}

func (tn *TrieNode) match(pass func(*TrieNode) bool, matches *chan *TrieNode) {
	defer func() {
		if pass(tn) {
			*matches <- tn
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		child.match(pass, &*matches)
	}
	return
}

func (tn *TrieNode) applyOnEos(f func(*TrieNode)) {
	defer func() {
		if tn.Eos {
			f(tn)
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		child.applyOnEos(f)
	}
	return
}

func (tn *TrieNode) walk() chan interface{} {
	results := make(chan interface{})
	go func() {
		defer func() {
			close(results)
		}()

		if tn.Children == nil {
			return
		}

		Children := *tn.Children
		for _, child := range Children {
			if child == nil {
				continue
			}
			if child.Eos {
				results <- child.Data
			}
			childChan := child.walk()
			for res := range childChan {
				results <- res
			}
		}
	}()
	return results
}

func (tn *TrieNode) get(alphaIndices []int) (value interface{}, ok bool) {
	location, ok := tn.findNode(alphaIndices)
	if !ok || location == nil {
		return nil, false
	}
	return location.Data, true
}

func (tn *TrieNode) set(alphaIndices []int, data interface{}, maxLen int) (prev interface{}, inserted *TrieNode) {
	indicesLen := len(alphaIndices)
	if indicesLen < 1 {
		prev = tn.Data
		tn.Data = data
		tn.Eos = true
		return prev, tn
	}

	var children []*TrieNode
	if tn.Children == nil {
		children = make([]*TrieNode, maxLen)
		tn.Children = &children
	}

	children = *tn.Children
	first := alphaIndices[0] % maxLen
	if children[first] == nil {
		children[first] = newTrieNode(nil)
	}

	child := children[first]
	return child.set(alphaIndices[1:], data, maxLen)
}

func (t *Trie) Set(key string, value interface{}) (prev interface{}) {
	indices := t.translator.alphabetizer(key)
	prev, _ = t.root.set(indices, value, t.translator.max)
	return prev
}

func (t *Trie) Get(key string) (value interface{}, ok bool) {
	indices := t.translator.alphabetizer(key)
	return t.root.get(indices)
}

func (t *Trie) Pop(key string) (popd interface{}, ok bool) {
	indices := t.translator.alphabetizer(key)
	return t.root.pop(indices)
}

func (t *Trie) Walk() chan interface{} {
	return t.root.walk()
}

func (t *Trie) Apply(f func(*TrieNode)) {
	t.root.applyOnEos(f)
}

func (t *Trie) Tag(pass func(*TrieNode) bool, tag interface{}) int {
	return t.root.tagOn(pass, tag)
}

func (t *Trie) Match(pass func(*TrieNode) bool) (matches chan *TrieNode) {
	matches = make(chan *TrieNode)
	go func() {
		t.root.match(pass, &matches)
		close(matches)
	}()
	return matches
}

func potentialDir(t *TrieNode, onTerminal bool) bool {
	if t.Children == nil || len(*t.Children) < 1 {
		return false
	}
	nonNilCount := 0
	for _, child := range *t.Children {
		if child != nil {
			nonNilCount += 1
		}
	}
	return nonNilCount >= 2

}

func PotentialTerminalDir(t *TrieNode) bool {
	return potentialDir(t, false)
}

func PotentialDir(t *TrieNode) bool {
	return potentialDir(t, false)
}

func New(alphabetizer *alphabet) *Trie {
	t := &Trie{
		root:       newTrieNode(""),
		translator: alphabetizer,
	}
	return t
}
<commit_msg>trie: HasEOS + potentialDir<commit_after>package trie

type keyTranslator func(key string) []int
type indexTranslator func(b byte) int

func indexResolver(f indexTranslator) keyTranslator {
	cache := map[string][]int{}
	return func(key string) []int {
		retr, ok := cache[key]
		if ok {
			return retr
		}

		retr = make([]int, len(key))
		for i, ch := range key {
			retr[i] = f(byte(ch))
		}
		cache[key] = retr
		return retr
	}
}

type TrieNode struct {
	Data interface{}
	// Tag should help to annotate a specific feature e.g color, discovered, isDir etc
	Tag      interface{}
	Children *[]*TrieNode
	// Eos(End-Of-Sequence) is designated to terminate a sequence.
	Eos bool
}

type alphabet struct {
	max          int
	min          int
	alphabetizer keyTranslator
}

func asciiAlphabetizer(ch byte) int {
	return int(ch)
}

var AsciiAlphabet = &alphabet{
	min:          0,
	max:          255, // TODO: Perform a sizeof(byte) op
	alphabetizer: indexResolver(asciiAlphabetizer),
}

var (
	NumericStart = 0
	NumericEnd   = 9
	NumericOther = (NumericEnd - NumericStart) + 1
)

func numericAlphabetizer(b byte) int {
	if b >= '0' && b <= '9' {
		return int(b - '0')
	}
	return NumericOther
}

var NumericAlphabet = &alphabet{
	min:          NumericStart,
	max:          NumericOther + 1,
	alphabetizer: indexResolver(numericAlphabetizer),
}

type Trie struct {
	root       *TrieNode
	translator *alphabet
}

func newTrieNode(data interface{}) *TrieNode {
	return &TrieNode{
		Data:     data,
		Children: nil,
	}
}

func (tn *TrieNode) findNode(alphaIndices []int) (cur *TrieNode, ok bool) {
	cur = tn
	i, max := 0, len(alphaIndices)
	for {
		if cur == nil || i >= max {
			break
		}
		next := cur.Children
		if next == nil {
			return nil, false
		}
		children := *next
		first := alphaIndices[i] % len(children)
		cur = children[first]
		i += 1
	}
	if cur == nil || !cur.Eos {
		return nil, false
	}

	return cur, true
}

func (tn *TrieNode) pop(alphaIndices []int) (popd interface{}, ok bool) {
	location, ok := tn.findNode(alphaIndices)
	if !ok || location == nil {
		return nil, false
	}

	popd = location.Data
	location.Data = nil
	location.Eos = false
	// TODO: Perform a check on whether all the Children are set to
	// nil, if so, clean up the array memory, free and set it to nil.

	return popd, true
}

func (tn *TrieNode) tagOn(pass func(*TrieNode) bool, tag interface{}) (count int) {
	count = 0
	defer func() {
		if pass(tn) {
			tn.Tag = tag
			count += 1
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		count += child.tagOn(pass, tag)
	}

	return count
}

func (tn *TrieNode) EOS() bool {
	return tn.Eos
}

func (tn *TrieNode) Match(pass func(*TrieNode) bool) (matches chan *TrieNode) {
	matches = make(chan *TrieNode)
	go func() {
		tn.match(pass, &matches)
		close(matches)
	}()
	return matches
}

func (tn *TrieNode) match(pass func(*TrieNode) bool, matches *chan *TrieNode) {
	defer func() {
		if pass(tn) {
			*matches <- tn
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		child.match(pass, &*matches)
	}
	return
}

func (tn *TrieNode) applyOnEos(f func(*TrieNode)) {
	defer func() {
		if tn.Eos {
			f(tn)
		}
	}()

	if tn.Children == nil {
		return
	}

	Children := *tn.Children
	for _, child := range Children {
		if child == nil {
			continue
		}
		child.applyOnEos(f)
	}
	return
}

func (tn *TrieNode) walk() chan interface{} {
	results := make(chan interface{})
	go func() {
		defer func() {
			close(results)
		}()

		if tn.Children == nil {
			return
		}

		Children := *tn.Children
		for _, child := range Children {
			if child == nil {
				continue
			}
			if child.Eos {
				results <- child.Data
			}
			childChan := child.walk()
			for res := range childChan {
				results <- res
			}
		}
	}()
	return results
}

func (tn *TrieNode) get(alphaIndices []int) (value interface{}, ok bool) {
	location, ok := tn.findNode(alphaIndices)
	if !ok || location == nil {
		return nil, false
	}
	return location.Data, true
}

func (tn *TrieNode) set(alphaIndices []int, data interface{}, maxLen int) (prev interface{}, inserted *TrieNode) {
	indicesLen := len(alphaIndices)
	if indicesLen < 1 {
		prev = tn.Data
		tn.Data = data
		tn.Eos = true
		return prev, tn
	}

	var children []*TrieNode
	if tn.Children == nil {
		children = make([]*TrieNode, maxLen)
		tn.Children = &children
	}

	children = *tn.Children
	first := alphaIndices[0] % maxLen
	if children[first] == nil {
		children[first] = newTrieNode(nil)
	}

	child := children[first]
	return child.set(alphaIndices[1:], data, maxLen)
}

func (t *Trie) Set(key string, value interface{}) (prev interface{}) {
	indices := t.translator.alphabetizer(key)
	prev, _ = t.root.set(indices, value, t.translator.max)
	return prev
}

func (t *Trie) Get(key string) (value interface{}, ok bool) {
	indices := t.translator.alphabetizer(key)
	return t.root.get(indices)
}

func (t *Trie) Pop(key string) (popd interface{}, ok bool) {
	indices := t.translator.alphabetizer(key)
	return t.root.pop(indices)
}

func (t *Trie) Walk() chan interface{} {
	return t.root.walk()
}

func (t *Trie) Apply(f func(*TrieNode)) {
	t.root.applyOnEos(f)
}

func (t *Trie) Tag(pass func(*TrieNode) bool, tag interface{}) int {
	return t.root.tagOn(pass, tag)
}

func (t *Trie) Match(pass func(*TrieNode) bool) (matches chan *TrieNode) {
	matches = make(chan *TrieNode)
	go func() {
		t.root.match(pass, &matches)
		close(matches)
	}()
	return matches
}

func potentialDir(t *TrieNode, onTerminal bool) bool {
	if t.Children == nil || len(*t.Children) < 1 {
		return false
	}
	if onTerminal && !t.Eos {
		return false
	}
	nonNilCount := 0
	for _, child := range *t.Children {
		if child != nil {
			nonNilCount += 1
		}
	}
	return nonNilCount >= 2

}

func PotentialTerminalDir(t *TrieNode) bool {
	return potentialDir(t, true)
}

func PotentialDir(t *TrieNode) bool {
	return potentialDir(t, false)
}

func HasEOS(t *TrieNode) bool {
	return t != nil && t.Eos
}

func New(alphabetizer *alphabet) *Trie {
	t := &Trie{
		root:       newTrieNode(""),
		translator: alphabetizer,
	}
	return t
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"
)

var (
	// ErrInvalidConnectionState indicates that the connection is not in a valid state.
	ErrInvalidConnectionState = errors.New("connection is in an invalid state")

	// ErrNoPeers indicates that there are no peers.
	ErrNoPeers = errors.New("no peers available")

	peerRng = NewRand(time.Now().UnixNano())
)

// Connectable is the interface used by peers to create connections.
type Connectable interface {
	// Connect tries to connect to the given hostPort.
	Connect(ctx context.Context, hostPort string) (*Connection, error)
	// Logger returns the logger to use.
	Logger() Logger
}

// PeerList maintains a list of Peers.
type PeerList struct {
	sync.RWMutex

	parent          *RootPeerList
	peersByHostPort map[string]*Peer
	peers           []*Peer
	scoreCalculator scoreCalculator
}

func newPeerList(root *RootPeerList) *PeerList {
	return &PeerList{
		parent:          root,
		peersByHostPort: make(map[string]*Peer),
		scoreCalculator: newPreferIncoming(),
	}
}

// Siblings don't share peer lists (though they take care not to double-connect
// to the same hosts).
func (l *PeerList) newSibling() *PeerList {
	sib := newPeerList(l.parent)
	return sib
}

// Add adds a peer to the list if it does not exist, or returns any existing peer.
func (l *PeerList) Add(hostPort string) *Peer {
	l.RLock()

	if p, ok := l.peersByHostPort[hostPort]; ok {
		l.RUnlock()
		return p
	}

	l.RUnlock()
	l.Lock()
	defer l.Unlock()

	if p, ok := l.peersByHostPort[hostPort]; ok {
		return p
	}

	p := l.parent.Add(hostPort)
	atomic.AddUint32(&p.scCount, 1)
	l.peersByHostPort[hostPort] = p
	l.peers = append(l.peers, p)
	return p
}

// Get returns a peer from the peer list, or nil if none can be found.
// Get will avoid peers in prevSelected unless there are no other peers left.
func (l *PeerList) Get(prevSelected map[string]struct{}) (*Peer, error) {
	l.RLock()

	if len(l.peers) == 0 {
		l.RUnlock()
		return nil, ErrNoPeers
	}

	// Select a peer, avoiding previously selected peers. If all peers have been previously
	// selected, then it's OK to repick them.
	peer := l.choosePeer(l.peers, prevSelected)
	if peer == nil {
		peer = l.choosePeer(l.peers, nil)
	}
	l.RUnlock()

	return peer, nil
}

func (l *PeerList) choosePeer(peers []*Peer, prevSelected map[string]struct{}) *Peer {
	var maxScore uint64
	var bestPeer *Peer

	// pick peer with highest score that is not in prevSelected.
	for _, peer := range peers {
		if _, ok := prevSelected[peer.HostPort()]; ok {
			continue
		}

		score := l.scoreCalculator.GetScore(peer)
		if score > maxScore {
			maxScore = score
			bestPeer = peer
		}
	}

	if bestPeer != nil {
		atomic.AddUint64(&bestPeer.chosenCount, 1)
	}
	return bestPeer
}

// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist.
func (l *PeerList) GetOrAdd(hostPort string) *Peer {
	l.RLock()
	if p, ok := l.peersByHostPort[hostPort]; ok {
		l.RUnlock()
		return p
	}

	l.RUnlock()
	return l.Add(hostPort)
}

// Copy returns a map of the peer list. This method should only be used for testing.
func (l *PeerList) Copy() map[string]*Peer {
	l.RLock()
	defer l.RUnlock()

	listCopy := make(map[string]*Peer)
	for k, v := range l.peersByHostPort {
		listCopy[k] = v
	}
	return listCopy
}

// Peer represents a single autobahn service or client with a unique host:port.
type Peer struct {
	channel      Connectable
	hostPort     string
	onConnChange func(*Peer)

	// scCount is the number of subchannels that this peer is added to.
	scCount uint32

	mut                 sync.RWMutex // mut protects connections.
	inboundConnections  []*Connection
	outboundConnections []*Connection
	chosenCount         uint64
}

func newPeer(channel Connectable, hostPort string, onConnChange func(*Peer)) *Peer {
	return &Peer{
		channel:      channel,
		hostPort:     hostPort,
		onConnChange: onConnChange,
	}
}

// HostPort returns the host:port used to connect to this peer.
func (p *Peer) HostPort() string {
	return p.hostPort
}

// getActive returns a list of active connections.
// TODO(prashant): Should we clear inactive connections?
func (p *Peer) getActive() []*Connection {
	var active []*Connection
	p.runWithConnections(func(c *Connection) {
		if c.IsActive() {
			active = append(active, c)
		}
	})
	return active
}

func randConn(conns []*Connection) *Connection {
	return conns[peerRng.Intn(len(conns))]
}

// GetConnection returns an active connection to this peer. If no active connections
// are found, it will create a new outbound connection and return it.
func (p *Peer) GetConnection(ctx context.Context) (*Connection, error) {
	// TODO(prashant): Use some sort of scoring to pick a connection.
	if activeConns := p.getActive(); len(activeConns) > 0 {
		return randConn(activeConns), nil
	}

	// No active connections, make a new outgoing connection.
	c, err := p.Connect(ctx)
	if err != nil {
		return nil, err
	}
	return c, nil
}

// AddInboundConnection adds an active inbound connection to the peer's connection list.
// If a connection is not active, ErrInvalidConnectionState will be returned.
func (p *Peer) AddInboundConnection(c *Connection) error {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		// TODO(prashantv): Block inbound connections when the connection is not active.
		break
	default:
		return ErrInvalidConnectionState
	}

	p.mut.Lock()
	p.inboundConnections = append(p.inboundConnections, c)
	p.mut.Unlock()

	p.connectionStateChanged(c)
	return nil
}

// canRemove returns whether this peer can be safely removed from the root peer list.
func (p *Peer) canRemove() bool {
	p.mut.RLock()
	count := len(p.inboundConnections) + len(p.outboundConnections) + int(p.scCount)
	p.mut.RUnlock()
	return count == 0
}

// AddOutboundConnection adds an active outbound connection to the peer's connection list.
// If a connection is not active, ErrInvalidConnectionState will be returned.
func (p *Peer) AddOutboundConnection(c *Connection) error {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		break
	default:
		return ErrInvalidConnectionState
	}

	p.mut.Lock()
	p.outboundConnections = append(p.outboundConnections, c)
	p.mut.Unlock()

	p.connectionStateChanged(c)
	return nil
}

// checkInboundConnection will check whether the changed connection is an inbound
// connection, and will remove any closed connections.
func (p *Peer) checkInboundConnection(changed *Connection) (updated bool, isInbound bool) {
	newConns := p.inboundConnections[:0]
	for _, c := range p.inboundConnections {
		if c == changed {
			isInbound = true
		}

		if c.readState() != connectionClosed {
			newConns = append(newConns, c)
		} else {
			updated = true
		}
	}
	if updated {
		p.inboundConnections = newConns
	}

	return updated, isInbound
}

// connectionStateChanged is called when one of the peers' connections states changes.
func (p *Peer) connectionStateChanged(changed *Connection) {
	p.mut.Lock()
	updated, _ := p.checkInboundConnection(changed)
	p.mut.Unlock()

	if updated {
		p.onConnChange(p)
	}
}

// Connect adds a new outbound connection to the peer.
func (p *Peer) Connect(ctx context.Context) (*Connection, error) {
	c, err := p.channel.Connect(ctx, p.hostPort)
	if err != nil {
		return nil, err
	}

	if err := p.AddOutboundConnection(c); err != nil {
		return nil, err
	}

	return c, nil
}

// BeginCall starts a new call to this specific peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (p *Peer) BeginCall(ctx context.Context, serviceName string, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	if callOptions == nil {
		callOptions = defaultCallOptions
	}
	callOptions.RequestState.AddSelectedPeer(p.HostPort())

	conn, err := p.GetConnection(ctx)
	if err != nil {
		return nil, err
	}

	if callOptions == nil {
		callOptions = defaultCallOptions
	}
	call, err := conn.beginCall(ctx, serviceName, callOptions, operationName)
	if err != nil {
		return nil, err
	}

	return call, err
}

// NumInbound returns the number of inbound connections to this node.
func (p *Peer) NumInbound() int {
	p.mut.RLock()
	count := len(p.inboundConnections)
	p.mut.RUnlock()
	return count
}

func (p *Peer) runWithConnections(f func(*Connection)) {
	p.mut.RLock()
	inboundConns := p.inboundConnections
	outboundConns := p.outboundConnections
	p.mut.RUnlock()

	for _, c := range inboundConns {
		f(c)
	}

	for _, c := range outboundConns {
		f(c)
	}
}
<commit_msg>Never create peers with blank host:port<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"
)

var (
	// ErrInvalidConnectionState indicates that the connection is not in a valid state.
	ErrInvalidConnectionState = errors.New("connection is in an invalid state")

	// ErrNoPeers indicates that there are no peers.
	ErrNoPeers = errors.New("no peers available")

	peerRng = NewRand(time.Now().UnixNano())
)

// Connectable is the interface used by peers to create connections.
type Connectable interface {
	// Connect tries to connect to the given hostPort.
	Connect(ctx context.Context, hostPort string) (*Connection, error)
	// Logger returns the logger to use.
	Logger() Logger
}

// PeerList maintains a list of Peers.
type PeerList struct {
	sync.RWMutex

	parent          *RootPeerList
	peersByHostPort map[string]*Peer
	peers           []*Peer
	scoreCalculator scoreCalculator
}

func newPeerList(root *RootPeerList) *PeerList {
	return &PeerList{
		parent:          root,
		peersByHostPort: make(map[string]*Peer),
		scoreCalculator: newPreferIncoming(),
	}
}

// Siblings don't share peer lists (though they take care not to double-connect
// to the same hosts).
func (l *PeerList) newSibling() *PeerList {
	sib := newPeerList(l.parent)
	return sib
}

// Add adds a peer to the list if it does not exist, or returns any existing peer.
func (l *PeerList) Add(hostPort string) *Peer {
	l.RLock()

	if p, ok := l.peersByHostPort[hostPort]; ok {
		l.RUnlock()
		return p
	}

	l.RUnlock()
	l.Lock()
	defer l.Unlock()

	if p, ok := l.peersByHostPort[hostPort]; ok {
		return p
	}

	p := l.parent.Add(hostPort)
	atomic.AddUint32(&p.scCount, 1)
	l.peersByHostPort[hostPort] = p
	l.peers = append(l.peers, p)
	return p
}

// Get returns a peer from the peer list, or nil if none can be found.
// Get will avoid peers in prevSelected unless there are no other peers left.
func (l *PeerList) Get(prevSelected map[string]struct{}) (*Peer, error) {
	l.RLock()

	if len(l.peers) == 0 {
		l.RUnlock()
		return nil, ErrNoPeers
	}

	// Select a peer, avoiding previously selected peers. If all peers have been previously
	// selected, then it's OK to repick them.
	peer := l.choosePeer(l.peers, prevSelected)
	if peer == nil {
		peer = l.choosePeer(l.peers, nil)
	}
	l.RUnlock()

	return peer, nil
}

func (l *PeerList) choosePeer(peers []*Peer, prevSelected map[string]struct{}) *Peer {
	var maxScore uint64
	var bestPeer *Peer

	// pick peer with highest score that is not in prevSelected.
	for _, peer := range peers {
		if _, ok := prevSelected[peer.HostPort()]; ok {
			continue
		}

		score := l.scoreCalculator.GetScore(peer)
		if score > maxScore {
			maxScore = score
			bestPeer = peer
		}
	}

	if bestPeer != nil {
		atomic.AddUint64(&bestPeer.chosenCount, 1)
	}
	return bestPeer
}

// GetOrAdd returns a peer for the given hostPort, creating one if it doesn't yet exist.
func (l *PeerList) GetOrAdd(hostPort string) *Peer {
	l.RLock()
	if p, ok := l.peersByHostPort[hostPort]; ok {
		l.RUnlock()
		return p
	}

	l.RUnlock()
	return l.Add(hostPort)
}

// Copy returns a map of the peer list. This method should only be used for testing.
func (l *PeerList) Copy() map[string]*Peer {
	l.RLock()
	defer l.RUnlock()

	listCopy := make(map[string]*Peer)
	for k, v := range l.peersByHostPort {
		listCopy[k] = v
	}
	return listCopy
}

// Peer represents a single autobahn service or client with a unique host:port.
type Peer struct {
	channel      Connectable
	hostPort     string
	onConnChange func(*Peer)

	// scCount is the number of subchannels that this peer is added to.
	scCount uint32

	mut                 sync.RWMutex // mut protects connections.
	inboundConnections  []*Connection
	outboundConnections []*Connection
	chosenCount         uint64
}

func newPeer(channel Connectable, hostPort string, onConnChange func(*Peer)) *Peer {
	if hostPort == "" {
		panic("Cannot create peer with blank hostPort")
	}
	return &Peer{
		channel:      channel,
		hostPort:     hostPort,
		onConnChange: onConnChange,
	}
}

// HostPort returns the host:port used to connect to this peer.
func (p *Peer) HostPort() string {
	return p.hostPort
}

// getActive returns a list of active connections.
// TODO(prashant): Should we clear inactive connections?
func (p *Peer) getActive() []*Connection {
	var active []*Connection
	p.runWithConnections(func(c *Connection) {
		if c.IsActive() {
			active = append(active, c)
		}
	})
	return active
}

func randConn(conns []*Connection) *Connection {
	return conns[peerRng.Intn(len(conns))]
}

// GetConnection returns an active connection to this peer. If no active connections
// are found, it will create a new outbound connection and return it.
func (p *Peer) GetConnection(ctx context.Context) (*Connection, error) {
	// TODO(prashant): Use some sort of scoring to pick a connection.
	if activeConns := p.getActive(); len(activeConns) > 0 {
		return randConn(activeConns), nil
	}

	// No active connections, make a new outgoing connection.
	c, err := p.Connect(ctx)
	if err != nil {
		return nil, err
	}
	return c, nil
}

// AddInboundConnection adds an active inbound connection to the peer's connection list.
// If a connection is not active, ErrInvalidConnectionState will be returned.
func (p *Peer) AddInboundConnection(c *Connection) error {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		// TODO(prashantv): Block inbound connections when the connection is not active.
		break
	default:
		return ErrInvalidConnectionState
	}

	p.mut.Lock()
	p.inboundConnections = append(p.inboundConnections, c)
	p.mut.Unlock()

	p.connectionStateChanged(c)
	return nil
}

// canRemove returns whether this peer can be safely removed from the root peer list.
func (p *Peer) canRemove() bool {
	p.mut.RLock()
	count := len(p.inboundConnections) + len(p.outboundConnections) + int(p.scCount)
	p.mut.RUnlock()
	return count == 0
}

// AddOutboundConnection adds an active outbound connection to the peer's connection list.
// If a connection is not active, ErrInvalidConnectionState will be returned.
func (p *Peer) AddOutboundConnection(c *Connection) error {
	switch c.readState() {
	case connectionActive, connectionStartClose:
		break
	default:
		return ErrInvalidConnectionState
	}

	p.mut.Lock()
	p.outboundConnections = append(p.outboundConnections, c)
	p.mut.Unlock()

	p.connectionStateChanged(c)
	return nil
}

// checkInboundConnection will check whether the changed connection is an inbound
// connection, and will remove any closed connections.
func (p *Peer) checkInboundConnection(changed *Connection) (updated bool, isInbound bool) {
	newConns := p.inboundConnections[:0]
	for _, c := range p.inboundConnections {
		if c == changed {
			isInbound = true
		}

		if c.readState() != connectionClosed {
			newConns = append(newConns, c)
		} else {
			updated = true
		}
	}
	if updated {
		p.inboundConnections = newConns
	}

	return updated, isInbound
}

// connectionStateChanged is called when one of the peers' connections states changes.
func (p *Peer) connectionStateChanged(changed *Connection) {
	p.mut.Lock()
	updated, _ := p.checkInboundConnection(changed)
	p.mut.Unlock()

	if updated {
		p.onConnChange(p)
	}
}

// Connect adds a new outbound connection to the peer.
func (p *Peer) Connect(ctx context.Context) (*Connection, error) {
	c, err := p.channel.Connect(ctx, p.hostPort)
	if err != nil {
		return nil, err
	}

	if err := p.AddOutboundConnection(c); err != nil {
		return nil, err
	}

	return c, nil
}

// BeginCall starts a new call to this specific peer, returning an OutboundCall that can
// be used to write the arguments of the call.
func (p *Peer) BeginCall(ctx context.Context, serviceName string, operationName string, callOptions *CallOptions) (*OutboundCall, error) {
	if callOptions == nil {
		callOptions = defaultCallOptions
	}
	callOptions.RequestState.AddSelectedPeer(p.HostPort())

	conn, err := p.GetConnection(ctx)
	if err != nil {
		return nil, err
	}

	if callOptions == nil {
		callOptions = defaultCallOptions
	}
	call, err := conn.beginCall(ctx, serviceName, callOptions, operationName)
	if err != nil {
		return nil, err
	}

	return call, err
}

// NumInbound returns the number of inbound connections to this node.
func (p *Peer) NumInbound() int {
	p.mut.RLock()
	count := len(p.inboundConnections)
	p.mut.RUnlock()
	return count
}

func (p *Peer) runWithConnections(f func(*Connection)) {
	p.mut.RLock()
	inboundConns := p.inboundConnections
	outboundConns := p.outboundConnections
	p.mut.RUnlock()

	for _, c := range inboundConns {
		f(c)
	}

	for _, c := range outboundConns {
		f(c)
	}
}
<|endoftext|>


<commit_before>package vm

import (
	"github.com/nitrogen-lang/nitrogen/src/object"
	"github.com/nitrogen-lang/nitrogen/src/vm/opcode"
)

func (vm *VirtualMachine) compareObjects(left, right object.Object, op byte) object.Object {
	switch {
	case left.Type() != right.Type():
		return object.FalseConst
	case object.ObjectsAre(object.IntergerObj, left, right):
		return vm.evalIntegerInfixExpression(op, left, right)
	case object.ObjectsAre(object.FloatObj, left, right):
		return vm.evalFloatInfixExpression(op, left, right)
	case object.ObjectsAre(object.StringObj, left, right):
		return vm.evalStringInfixExpression(op, left, right)
	case object.ObjectsAre(object.BooleanObj, left, right):
		return vm.evalBoolInfixExpression(op, left, right)
	}

	return object.NewException("comparison not implemented for type %s", left.Type())
}

func (vm *VirtualMachine) evalIntegerInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	switch op {
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalFloatInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Float).Value
	rightVal := right.(*object.Float).Value

	switch op {
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalStringInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.String).String()
	rightVal := right.(*object.String).String()

	switch op {
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalBoolInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Boolean).Value
	rightVal := right.(*object.Boolean).Value

	switch op {
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}
<commit_msg>Made nil objects comparable to each other<commit_after>package vm

import (
	"github.com/nitrogen-lang/nitrogen/src/object"
	"github.com/nitrogen-lang/nitrogen/src/vm/opcode"
)

func (vm *VirtualMachine) compareObjects(left, right object.Object, op byte) object.Object {
	switch {
	case left.Type() != right.Type():
		return object.FalseConst
	case object.ObjectsAre(object.IntergerObj, left, right):
		return vm.evalIntegerInfixExpression(op, left, right)
	case object.ObjectsAre(object.FloatObj, left, right):
		return vm.evalFloatInfixExpression(op, left, right)
	case object.ObjectsAre(object.StringObj, left, right):
		return vm.evalStringInfixExpression(op, left, right)
	case object.ObjectsAre(object.BooleanObj, left, right):
		return vm.evalBoolInfixExpression(op, left, right)
	case object.ObjectsAre(object.NullObj, left, right):
		return vm.evalNullInfixExpression(op)
	}

	return object.NewException("comparison not implemented for type %s", left.Type())
}

func (vm *VirtualMachine) evalIntegerInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	switch op {
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalFloatInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Float).Value
	rightVal := right.(*object.Float).Value

	switch op {
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalStringInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.String).String()
	rightVal := right.(*object.String).String()

	switch op {
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	case opcode.CmpGT:
		return object.NativeBoolToBooleanObj(leftVal > rightVal)
	case opcode.CmpGTEq:
		return object.NativeBoolToBooleanObj(leftVal >= rightVal)
	case opcode.CmpLT:
		return object.NativeBoolToBooleanObj(leftVal < rightVal)
	case opcode.CmpLTEq:
		return object.NativeBoolToBooleanObj(leftVal <= rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalBoolInfixExpression(op byte, left, right object.Object) object.Object {
	leftVal := left.(*object.Boolean).Value
	rightVal := right.(*object.Boolean).Value

	switch op {
	case opcode.CmpEq:
		return object.NativeBoolToBooleanObj(leftVal == rightVal)
	case opcode.CmpNotEq:
		return object.NativeBoolToBooleanObj(leftVal != rightVal)
	}

	return object.NewException("unknown operator: %s %s %s", left.Type(), op, right.Type())
}

func (vm *VirtualMachine) evalNullInfixExpression(op byte) object.Object {
	switch op {
	case opcode.CmpEq:
		return object.TrueConst
	case opcode.CmpNotEq:
		return object.FalseConst
	}

	return object.NewException("unknown operator: nil %s nil", op)
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"

	"github.com/alecthomas/kingpin"
	_ "github.com/go-sql-driver/mysql"
)

var (
	DB  *sql.DB
	err error

	mysqlHost = kingpin.Flag("host", "Connect to host.").Default("localhost").Short('h').String()
	mysqlUser = kingpin.Flag("user", "User for login.").Default("root").Short('u').String()
	mysqlPass = kingpin.Flag("password", "Password to use when connecting to server.").Short('h').String()
)

func main() {
	kingpin.Parse()
	DB, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:3306)/mysql", *mysqlUser, *mysqlPass, *mysqlHost),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", statusHandler)
	http.ListenAndServe(":9199", nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	err = DB.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = DB.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch value {
	case 4, 2:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<commit_msg>make http host and port configurable<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"

	"github.com/alecthomas/kingpin"
	_ "github.com/go-sql-driver/mysql"
)

var (
	DB  *sql.DB
	err error

	mysqlHost = kingpin.Flag("host", "Connect to host.").Default("localhost").Short('h').String()
	mysqlUser = kingpin.Flag("user", "User for login.").Default("root").Short('u').String()
	mysqlPass = kingpin.Flag("password", "Password to use when connecting to server.").Short('h').String()
	httpHost = kingpin.Flag("http-host", "Connect to host").Default("localhost").Short('H').String()
	httpPort = kingpin.Flag("http-port", "Connect to host").Default("9199").Short('P').String()
)

func main() {
	kingpin.Parse()
	DB, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:3306)/mysql", *mysqlUser, *mysqlPass, *mysqlHost),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", statusHandler)
	http.ListenAndServe(*httpHost+":"+*httpPort, nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	err = DB.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = DB.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch value {
	case 4, 2:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<|endoftext|>


<commit_before>package easyss

import (
	"context"
	_ "embed"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nange/easyss/util"
	log "github.com/sirupsen/logrus"
	"github.com/xjasonlyu/tun2socks/v2/engine"
)

var (
	//go:embed scripts/create_tun_dev.sh
	createTunDevSh []byte
	//go:embed scripts/create_tun_dev_windows.bat
	createTunDevShWindows []byte
	//go:embed scripts/close_tun_dev.sh
	closeTunDevSh []byte
	//go:embed scripts/close_tun_dev_windows.bat
	closeTunDevShWindows []byte

	//go:embed wintun/wintun_amd64.dll
	wintunAmd64 []byte
	//go:embed wintun/wintun_arm64.dll
	wintunArm64 []byte
	//go:embed wintun/wintun_x86.dll
	wintunX86 []byte
	//go:embed wintun/wintun_arm.dll
	wintunArm []byte
)

const (
	TunDevice = "tun-easyss"
	TunIP     = "198.18.0.1"
	TunGW     = "198.18.0.1"
	TunMask   = "255.255.0.0"
	TunIPSub  = "198.18.0.1/16"
)

func (ss *Easyss) CreateTun2socks() error {
	ss.mu.Lock()
	defer ss.mu.Unlock()

	if err := writeWinTunToDiskIfNeeded(); err != nil {
		log.Errorf("write wintun.dll to disk err:%s", err.Error())
		return err
	}

	key := &engine.Key{
		Proxy:                ss.Socks5ProxyAddr(),
		Device:               TunDevice,
		LogLevel:             "error",
		TCPSendBufferSize:    "1m",
		TCPReceiveBufferSize: "1m",
		UDPTimeout:           ss.Timeout(),
	}
	engine.Insert(key)
	engine.Start()

	if err := ss.createTunDevAndSetIpRoute(); err != nil {
		log.Errorf("add tun device and set ip-route err:%s", err.Error())
		return err
	}

	ss.tun2socksEnabled = true
	log.Infof("tun2socks server and tun device init success")
	return nil
}

func (ss *Easyss) createTunDevAndSetIpRoute() error {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	if ss.ServerIP() == "" {
		return errors.New("server ips is empty")
	}

	var shellFilename string
	var shellContent []byte
	switch runtime.GOOS {
	case "linux":
		shellFilename = "create_tun_dev.sh"
		shellContent = createTunDevSh
	case "windows":
		shellFilename = "create_tun_dev_windows.bat"
		shellContent = createTunDevShWindows
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	namePath, err := util.WriteToTemp(shellFilename, shellContent)
	if err != nil {
		log.Errorf("write close_tun_dev.sh to temp file err:%v", err.Error())
		return err
	}
	defer os.RemoveAll(namePath)

	switch runtime.GOOS {
	case "linux":
		if err := exec.CommandContext(ctx, "pkexec", "bash",
			namePath, TunDevice, TunIPSub, TunGW, ss.ServerIP(), ss.LocalGateway(), ss.LocalDevice()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	case "windows":
		dir := filepath.Dir(namePath)
		newNamePath := filepath.Join(dir, shellFilename)
		if err := os.Rename(namePath, newNamePath); err != nil {
			return err
		}
		namePath = newNamePath
		if err := exec.CommandContext(ctx, "cmd.exe", "/C",
			namePath, TunDevice, TunIP, TunGW, TunMask, ss.ServerIP(), ss.LocalGateway()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	return nil
}

func (ss *Easyss) CloseTun2socks() error {
	ss.mu.Lock()
	defer ss.mu.Unlock()

	return ss.closeTun2socks()
}

func (ss *Easyss) closeTun2socks() error {
	engine.Stop()
	if err := ss.closeTunDevAndDelIpRoute(); err != nil {
		return err
	}

	ss.tun2socksEnabled = false
	return nil
}

func (ss *Easyss) closeTunDevAndDelIpRoute() error {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	var shellFilename string
	var shellContent []byte
	switch runtime.GOOS {
	case "linux":
		shellFilename = "close_tun_dev.sh"
		shellContent = closeTunDevSh
	case "windows":
		shellFilename = "close_tun_dev_windows.bat"
		shellContent = closeTunDevShWindows
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	namePath, err := util.WriteToTemp(shellFilename, shellContent)
	if err != nil {
		log.Errorf("write close_tun_dev.sh to temp file err:%v", err.Error())
		return err
	}
	defer os.RemoveAll(namePath)

	switch runtime.GOOS {
	case "linux":
		if err := exec.CommandContext(ctx, "pkexec", "bash",
			namePath, TunDevice, ss.ServerIP(), ss.LocalGateway(), ss.LocalDevice()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	case "windows":
		dir := filepath.Dir(namePath)
		newNamePath := filepath.Join(dir, shellFilename)
		if err := os.Rename(namePath, newNamePath); err != nil {
			return err
		}
		namePath = newNamePath
		if err := exec.CommandContext(ctx, "cmd.exe", "/C",
			namePath, TunGW, ss.ServerIP(), ss.LocalGateway()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	return nil
}

func writeWinTunToDiskIfNeeded() error {
	if runtime.GOOS != "windows" {
		return nil
	}

	writeBytes := func(b []byte) error {
		path := filepath.Join(util.CurrentDir(), "wintun.dll")
		return os.WriteFile(path, b, 0666)
	}

	switch runtime.GOARCH {
	case "amd64":
		return writeBytes(wintunAmd64)
	case "arm64":
		return writeBytes(wintunArm64)
	case "386":
		return writeBytes(wintunX86)
	case "arm":
		return writeBytes(wintunArm)
	default:
		return fmt.Errorf("unsupported arch:%s", runtime.GOARCH)
	}
}
<commit_msg>tun2socks: use different up order for different os<commit_after>package easyss

import (
	"context"
	_ "embed"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nange/easyss/util"
	log "github.com/sirupsen/logrus"
	"github.com/xjasonlyu/tun2socks/v2/engine"
)

var (
	//go:embed scripts/create_tun_dev.sh
	createTunDevSh []byte
	//go:embed scripts/create_tun_dev_windows.bat
	createTunDevShWindows []byte
	//go:embed scripts/close_tun_dev.sh
	closeTunDevSh []byte
	//go:embed scripts/close_tun_dev_windows.bat
	closeTunDevShWindows []byte

	//go:embed wintun/wintun_amd64.dll
	wintunAmd64 []byte
	//go:embed wintun/wintun_arm64.dll
	wintunArm64 []byte
	//go:embed wintun/wintun_x86.dll
	wintunX86 []byte
	//go:embed wintun/wintun_arm.dll
	wintunArm []byte
)

const (
	TunDevice = "tun-easyss"
	TunIP     = "198.18.0.1"
	TunGW     = "198.18.0.1"
	TunMask   = "255.255.0.0"
	TunIPSub  = "198.18.0.1/16"
)

func (ss *Easyss) CreateTun2socks() error {
	ss.mu.Lock()
	defer ss.mu.Unlock()

	switch runtime.GOOS {
	case "linux":
		if err := ss.createTunDevAndSetIpRoute(); err != nil {
			log.Errorf("add tun device and set ip-route err:%s", err.Error())
			return err
		}
		ss.startTun2socksEngine()
	case "windows":
		if err := writeWinTunToDisk(); err != nil {
			log.Errorf("write wintun.dll to disk err:%s", err.Error())
			return err
		}
		ss.startTun2socksEngine()
		if err := ss.createTunDevAndSetIpRoute(); err != nil {
			log.Errorf("add tun device and set ip-route err:%s", err.Error())
			return err
		}
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	ss.tun2socksEnabled = true
	log.Infof("tun2socks server and tun device init success")
	return nil
}

func (ss *Easyss) startTun2socksEngine() {
	key := &engine.Key{
		Proxy:                ss.Socks5ProxyAddr(),
		Device:               TunDevice,
		LogLevel:             "error",
		TCPSendBufferSize:    "1m",
		TCPReceiveBufferSize: "1m",
		UDPTimeout:           ss.Timeout(),
	}
	engine.Insert(key)
	engine.Start()
}

func (ss *Easyss) createTunDevAndSetIpRoute() error {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	if ss.ServerIP() == "" {
		return errors.New("server ips is empty")
	}

	var shellFilename string
	var shellContent []byte
	switch runtime.GOOS {
	case "linux":
		shellFilename = "create_tun_dev.sh"
		shellContent = createTunDevSh
	case "windows":
		shellFilename = "create_tun_dev_windows.bat"
		shellContent = createTunDevShWindows
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	namePath, err := util.WriteToTemp(shellFilename, shellContent)
	if err != nil {
		log.Errorf("write close_tun_dev.sh to temp file err:%v", err.Error())
		return err
	}
	defer os.RemoveAll(namePath)

	switch runtime.GOOS {
	case "linux":
		if err := exec.CommandContext(ctx, "pkexec", "bash",
			namePath, TunDevice, TunIPSub, TunGW, ss.ServerIP(), ss.LocalGateway(), ss.LocalDevice()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	case "windows":
		dir := filepath.Dir(namePath)
		newNamePath := filepath.Join(dir, shellFilename)
		if err := os.Rename(namePath, newNamePath); err != nil {
			return err
		}
		namePath = newNamePath
		if err := exec.CommandContext(ctx, "cmd.exe", "/C",
			namePath, TunDevice, TunIP, TunGW, TunMask, ss.ServerIP(), ss.LocalGateway()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	return nil
}

func (ss *Easyss) CloseTun2socks() error {
	ss.mu.Lock()
	defer ss.mu.Unlock()

	return ss.closeTun2socks()
}

func (ss *Easyss) closeTun2socks() error {
	engine.Stop()
	if err := ss.closeTunDevAndDelIpRoute(); err != nil {
		return err
	}

	ss.tun2socksEnabled = false
	return nil
}

func (ss *Easyss) closeTunDevAndDelIpRoute() error {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	var shellFilename string
	var shellContent []byte
	switch runtime.GOOS {
	case "linux":
		shellFilename = "close_tun_dev.sh"
		shellContent = closeTunDevSh
	case "windows":
		shellFilename = "close_tun_dev_windows.bat"
		shellContent = closeTunDevShWindows
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	namePath, err := util.WriteToTemp(shellFilename, shellContent)
	if err != nil {
		log.Errorf("write close_tun_dev.sh to temp file err:%v", err.Error())
		return err
	}
	defer os.RemoveAll(namePath)

	switch runtime.GOOS {
	case "linux":
		if err := exec.CommandContext(ctx, "pkexec", "bash",
			namePath, TunDevice, ss.ServerIP(), ss.LocalGateway(), ss.LocalDevice()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	case "windows":
		dir := filepath.Dir(namePath)
		newNamePath := filepath.Join(dir, shellFilename)
		if err := os.Rename(namePath, newNamePath); err != nil {
			return err
		}
		namePath = newNamePath
		if err := exec.CommandContext(ctx, "cmd.exe", "/C",
			namePath, TunGW, ss.ServerIP(), ss.LocalGateway()).Run(); err != nil {
			log.Errorf("exec %s err:%s", shellFilename, err.Error())
			return err
		}
	default:
		return fmt.Errorf("unsupported os:%s", runtime.GOOS)
	}

	return nil
}

func writeWinTunToDisk() error {
	writeBytes := func(b []byte) error {
		path := filepath.Join(util.CurrentDir(), "wintun.dll")
		return os.WriteFile(path, b, 0666)
	}

	switch runtime.GOARCH {
	case "amd64":
		return writeBytes(wintunAmd64)
	case "arm64":
		return writeBytes(wintunArm64)
	case "386":
		return writeBytes(wintunX86)
	case "arm":
		return writeBytes(wintunArm)
	default:
		return fmt.Errorf("unsupported arch:%s", runtime.GOARCH)
	}
}
<|endoftext|>


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

package time

import (
	"os";
	"time"
)

// Seconds since January 1, 1970 00:00:00 GMT
export func Seconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec
}

// Nanoseconds since January 1, 1970 00:00:00 GMT
export func Nanoseconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec*1e9 + nsec
}

export const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Saturday;
)

export type Time struct {
	year int64;	// 2008 is 2008
	month, day int;	// Sep-17 is 9, 17
	hour, minute, second int;	// 10:43:12 is 10, 43, 12
	weekday int;		// Sunday = 0, Monday = 1, ...
	zoneoffset int;	// seconds west of UTC
	zone string;
}

var RegularMonths = []int{
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
var LeapMonths = []int{
	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}

func Months(year int64) *[]int {
	if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
		return &LeapMonths
	} else {
		return &RegularMonths
	}
	return nil	// not reached
}

const (
	SecondsPerDay = 24*60*60;

	DaysPer400Years = 365*400+97;
	DaysPer100Years = 365*100+24;
	DaysPer4Years = 365*4+1;

	Days1970To2001 = 31*365+8;
)

export func SecondsToUTC(sec int64) *Time {
	t := new(Time);

	// Split into time and day.
	day := sec/SecondsPerDay;
	sec -= day*SecondsPerDay;
	if sec < 0 {
		day--;
		sec += SecondsPerDay
	}

	// Time
	t.hour = int(sec/3600);
	t.minute = int((sec/60)%60);
	t.second = int(sec%60);

	// Day 0 = January 1, 1970 was a Thursday
	t.weekday = int((day + Thursday) % 7);
	if t.weekday < 0 {
		t.weekday += 7
	}

	// Change day from 0 = 1970 to 0 = 2001,
	// to make leap year calculations easier
	// (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
	day -= Days1970To2001;

	year := int64(2001);
	if day < 0 {
		// Go back enough 400 year cycles to make day positive.
		n := -day/DaysPer400Years + 1;
		year -= 400*n;
		day += DaysPer400Years*n;
	} else {
		// Cut off 400 year cycles.
		n := day/DaysPer400Years;
		year += 400*n;
		day -= DaysPer400Years*n;
	}

	// Cut off 100-year cycles
	n := day/DaysPer100Years;
	year += 100*n;
	day -= DaysPer100Years*n;

	// Cut off 4-year cycles
	n = day/DaysPer4Years;
	year += 4*n;
	day -= DaysPer4Years*n;

	// Cut off non-leap years.
	n = day/365;
	year += n;
	day -= 365*n;

	t.year = year;

	// If someone ever needs yearday,
	// tyearday = day (+1?)

	months := Months(year);
	var m int;
	yday := int(day);
	for m = 0; m < 12 && yday >= months[m]; m++ {
		yday -= months[m]
	}
	t.month = m+1;
	t.day = yday+1;
	t.zone = "GMT";

	return t;
}

export func UTC() (t *Time, err *os.Error) {
	return SecondsToUTC(Seconds()), nil
}

// TODO: Should this return an error?
export func SecondsToLocalTime(sec int64) *Time {
	zone, offset, err := time.LookupTimezone(sec);
	if err != nil {
		return SecondsToUTC(sec)
	}
	t := SecondsToUTC(sec+int64(offset));
	t.zone = zone;
	t.zoneoffset = offset;
	return t
}

export func LocalTime() (t *Time, err *os.Error) {
	return SecondsToLocalTime(Seconds()), nil
}

// Compute number of seconds since January 1, 1970.
func (t *Time) Seconds() int64 {
	// First, accumulate days since January 1, 2001.
	// Using 2001 instead of 1970 makes the leap-year
	// handling easier (see SecondsToUTC), because
	// it is at the beginning of the 4-, 100-, and 400-year cycles.
	day := int64(0);

	// Rewrite year to be >= 2001.
	year := t.year;
	if year < 2001 {
		n := (2001 - year)/400 + 1;
		year += 400*n;
		day -= DaysPer400Years*n;
	}

	// Add in days from 400-year cycles.
	n := (year - 2001) / 400;
	year -= 400*n;
	day += DaysPer400Years*n;

	// Add in 100-year cycles.
	n = (year - 2001) / 100;
	year -= 100*n;
	day += DaysPer100Years*n;

	// Add in 4-year cycles.
	n = (year - 2001) / 4;
	year -= 4*n;
	day += DaysPer4Years*n;

	// Add in non-leap years.
	n = year - 2001;
	day += 365*n;

	// Add in days this year.
	months := Months(t.year);
	for m := 0; m < t.month-1; m++ {
		day += int64(months[m])
	}
	day += int64(t.day - 1);

	// Convert days to seconds since January 1, 2001.
	sec := day * SecondsPerDay;

	// Add in time elapsed today.
	sec += int64(t.hour) * 3600;
	sec += int64(t.minute) * 60;
	sec += int64(t.second);

	// Convert from seconds since 2001 to seconds since 1970.
	sec += Days1970To2001 * SecondsPerDay;

	// Account for local time zone.
	sec -= int64(t.zoneoffset);
	return sec
}

var LongDayNames = []string{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
}

var ShortDayNames = []string{
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat"
}

var ShortMonthNames = []string{
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
}

func Copy(dst *[]byte, s string) {
	for i := 0; i < len(s); i++ {
		dst[i] = s[i]
	}
}

func Decimal(dst *[]byte, n int) {
	if n < 0 {
		n = 0
	}
	for i := len(dst)-1; i >= 0; i-- {
		dst[i] = byte(n%10 + '0');
		n /= 10
	}
}

func AddString(buf *[]byte, bp int, s string) int {
	n := len(s);
	Copy(buf[bp:bp+n], s);
	return bp+n
}

// Just enough of strftime to implement the date formats below.
// Not exported.
func Format(t *Time, fmt string) string {
	buf := new([]byte, 128);
	bp := 0;

	for i := 0; i < len(fmt); i++ {
		if fmt[i] == '%' {
			i++;
			switch fmt[i] {
			case 'A':	// %A full weekday name
				bp = AddString(buf, bp, LongDayNames[t.weekday]);
			case 'a':	// %a abbreviated weekday name
				bp = AddString(buf, bp, ShortDayNames[t.weekday]);
			case 'b':	// %b abbreviated month name
				bp = AddString(buf, bp, ShortMonthNames[t.month-1]);
			case 'd':	// %d day of month (01-31)
				Decimal(buf[bp:bp+2], t.day);
				bp += 2;
			case 'e':	// %e day of month ( 1-31)
				if t.day >= 10 {
					Decimal(buf[bp:bp+2], t.day)
				} else {
					buf[bp] = ' ';
					buf[bp+1] = byte(t.day + '0')
				}
				bp += 2;
			case 'H':	// %H hour 00-23
				Decimal(buf[bp:bp+2], t.hour);
				bp += 2;
			case 'M':	// %M minute 00-59
				Decimal(buf[bp:bp+2], t.minute);
				bp += 2;
			case 'S':	// %S second 00-59
				Decimal(buf[bp:bp+2], t.second);
				bp += 2;
			case 'Y':	// %Y year 2008
				Decimal(buf[bp:bp+4], int(t.year));
				bp += 4;
			case 'y':	// %y year 08
				Decimal(buf[bp:bp+2], int(t.year%100));
				bp += 2;
			case 'Z':
				bp = AddString(buf, bp, t.zone);
			default:
				buf[bp] = '%';
				buf[bp+1] = fmt[i];
				bp += 2
			}
		} else {
			buf[bp] = fmt[i];
			bp++;
		}
	}
	return string(buf[0:bp])
}

// ANSI C asctime: Sun Nov  6 08:49:37 1994
func (t *Time) Asctime() string {
	return Format(t, "%a %b %e %H:%M:%S %Y")
}

// RFC 850: Sunday, 06-Nov-94 08:49:37 GMT
func (t *Time) RFC850() string {
	return Format(t, "%A, %d-%b-%y %H:%M:%S %Z")
}

// RFC 1123: Sun, 06 Nov 1994 08:49:37 GMT
func (t *Time) RFC1123() string {
	return Format(t, "%a, %d %b %Y %H:%M:%S %Z")
}

// date(1) - Sun Nov  6 08:49:37 GMT 1994
func (t *Time) String() string {
	return Format(t, "%a %b %e %H:%M:%S %Z %Y")
}

<commit_msg>more impossible time errors<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

import (
	"os";
	"time"
)

// Seconds since January 1, 1970 00:00:00 GMT
export func Seconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec
}

// Nanoseconds since January 1, 1970 00:00:00 GMT
export func Nanoseconds() int64 {
	sec, nsec, err := os.Time();
	if err != nil {
		panic("time: os.Time: ", err.String());
	}
	return sec*1e9 + nsec
}

export const (
	Sunday = iota;
	Monday;
	Tuesday;
	Wednesday;
	Thursday;
	Friday;
	Saturday;
)

export type Time struct {
	year int64;	// 2008 is 2008
	month, day int;	// Sep-17 is 9, 17
	hour, minute, second int;	// 10:43:12 is 10, 43, 12
	weekday int;		// Sunday = 0, Monday = 1, ...
	zoneoffset int;	// seconds west of UTC
	zone string;
}

var RegularMonths = []int{
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
var LeapMonths = []int{
	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}

func Months(year int64) *[]int {
	if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
		return &LeapMonths
	} else {
		return &RegularMonths
	}
	return nil	// not reached
}

const (
	SecondsPerDay = 24*60*60;

	DaysPer400Years = 365*400+97;
	DaysPer100Years = 365*100+24;
	DaysPer4Years = 365*4+1;

	Days1970To2001 = 31*365+8;
)

export func SecondsToUTC(sec int64) *Time {
	t := new(Time);

	// Split into time and day.
	day := sec/SecondsPerDay;
	sec -= day*SecondsPerDay;
	if sec < 0 {
		day--;
		sec += SecondsPerDay
	}

	// Time
	t.hour = int(sec/3600);
	t.minute = int((sec/60)%60);
	t.second = int(sec%60);

	// Day 0 = January 1, 1970 was a Thursday
	t.weekday = int((day + Thursday) % 7);
	if t.weekday < 0 {
		t.weekday += 7
	}

	// Change day from 0 = 1970 to 0 = 2001,
	// to make leap year calculations easier
	// (2001 begins 4-, 100-, and 400-year cycles ending in a leap year.)
	day -= Days1970To2001;

	year := int64(2001);
	if day < 0 {
		// Go back enough 400 year cycles to make day positive.
		n := -day/DaysPer400Years + 1;
		year -= 400*n;
		day += DaysPer400Years*n;
	} else {
		// Cut off 400 year cycles.
		n := day/DaysPer400Years;
		year += 400*n;
		day -= DaysPer400Years*n;
	}

	// Cut off 100-year cycles
	n := day/DaysPer100Years;
	year += 100*n;
	day -= DaysPer100Years*n;

	// Cut off 4-year cycles
	n = day/DaysPer4Years;
	year += 4*n;
	day -= DaysPer4Years*n;

	// Cut off non-leap years.
	n = day/365;
	year += n;
	day -= 365*n;

	t.year = year;

	// If someone ever needs yearday,
	// tyearday = day (+1?)

	months := Months(year);
	var m int;
	yday := int(day);
	for m = 0; m < 12 && yday >= months[m]; m++ {
		yday -= months[m]
	}
	t.month = m+1;
	t.day = yday+1;
	t.zone = "GMT";

	return t;
}

export func UTC() *Time {
	return SecondsToUTC(Seconds())
}

// TODO: Should this return an error?
export func SecondsToLocalTime(sec int64) *Time {
	zone, offset, err := time.LookupTimezone(sec);
	if err != nil {
		return SecondsToUTC(sec)
	}
	t := SecondsToUTC(sec+int64(offset));
	t.zone = zone;
	t.zoneoffset = offset;
	return t
}

export func LocalTime() *Time {
	return SecondsToLocalTime(Seconds())
}

// Compute number of seconds since January 1, 1970.
func (t *Time) Seconds() int64 {
	// First, accumulate days since January 1, 2001.
	// Using 2001 instead of 1970 makes the leap-year
	// handling easier (see SecondsToUTC), because
	// it is at the beginning of the 4-, 100-, and 400-year cycles.
	day := int64(0);

	// Rewrite year to be >= 2001.
	year := t.year;
	if year < 2001 {
		n := (2001 - year)/400 + 1;
		year += 400*n;
		day -= DaysPer400Years*n;
	}

	// Add in days from 400-year cycles.
	n := (year - 2001) / 400;
	year -= 400*n;
	day += DaysPer400Years*n;

	// Add in 100-year cycles.
	n = (year - 2001) / 100;
	year -= 100*n;
	day += DaysPer100Years*n;

	// Add in 4-year cycles.
	n = (year - 2001) / 4;
	year -= 4*n;
	day += DaysPer4Years*n;

	// Add in non-leap years.
	n = year - 2001;
	day += 365*n;

	// Add in days this year.
	months := Months(t.year);
	for m := 0; m < t.month-1; m++ {
		day += int64(months[m])
	}
	day += int64(t.day - 1);

	// Convert days to seconds since January 1, 2001.
	sec := day * SecondsPerDay;

	// Add in time elapsed today.
	sec += int64(t.hour) * 3600;
	sec += int64(t.minute) * 60;
	sec += int64(t.second);

	// Convert from seconds since 2001 to seconds since 1970.
	sec += Days1970To2001 * SecondsPerDay;

	// Account for local time zone.
	sec -= int64(t.zoneoffset);
	return sec
}

var LongDayNames = []string{
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
	"Saturday"
}

var ShortDayNames = []string{
	"Sun",
	"Mon",
	"Tue",
	"Wed",
	"Thu",
	"Fri",
	"Sat"
}

var ShortMonthNames = []string{
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dec"
}

func Copy(dst *[]byte, s string) {
	for i := 0; i < len(s); i++ {
		dst[i] = s[i]
	}
}

func Decimal(dst *[]byte, n int) {
	if n < 0 {
		n = 0
	}
	for i := len(dst)-1; i >= 0; i-- {
		dst[i] = byte(n%10 + '0');
		n /= 10
	}
}

func AddString(buf *[]byte, bp int, s string) int {
	n := len(s);
	Copy(buf[bp:bp+n], s);
	return bp+n
}

// Just enough of strftime to implement the date formats below.
// Not exported.
func Format(t *Time, fmt string) string {
	buf := new([]byte, 128);
	bp := 0;

	for i := 0; i < len(fmt); i++ {
		if fmt[i] == '%' {
			i++;
			switch fmt[i] {
			case 'A':	// %A full weekday name
				bp = AddString(buf, bp, LongDayNames[t.weekday]);
			case 'a':	// %a abbreviated weekday name
				bp = AddString(buf, bp, ShortDayNames[t.weekday]);
			case 'b':	// %b abbreviated month name
				bp = AddString(buf, bp, ShortMonthNames[t.month-1]);
			case 'd':	// %d day of month (01-31)
				Decimal(buf[bp:bp+2], t.day);
				bp += 2;
			case 'e':	// %e day of month ( 1-31)
				if t.day >= 10 {
					Decimal(buf[bp:bp+2], t.day)
				} else {
					buf[bp] = ' ';
					buf[bp+1] = byte(t.day + '0')
				}
				bp += 2;
			case 'H':	// %H hour 00-23
				Decimal(buf[bp:bp+2], t.hour);
				bp += 2;
			case 'M':	// %M minute 00-59
				Decimal(buf[bp:bp+2], t.minute);
				bp += 2;
			case 'S':	// %S second 00-59
				Decimal(buf[bp:bp+2], t.second);
				bp += 2;
			case 'Y':	// %Y year 2008
				Decimal(buf[bp:bp+4], int(t.year));
				bp += 4;
			case 'y':	// %y year 08
				Decimal(buf[bp:bp+2], int(t.year%100));
				bp += 2;
			case 'Z':
				bp = AddString(buf, bp, t.zone);
			default:
				buf[bp] = '%';
				buf[bp+1] = fmt[i];
				bp += 2
			}
		} else {
			buf[bp] = fmt[i];
			bp++;
		}
	}
	return string(buf[0:bp])
}

// ANSI C asctime: Sun Nov  6 08:49:37 1994
func (t *Time) Asctime() string {
	return Format(t, "%a %b %e %H:%M:%S %Y")
}

// RFC 850: Sunday, 06-Nov-94 08:49:37 GMT
func (t *Time) RFC850() string {
	return Format(t, "%A, %d-%b-%y %H:%M:%S %Z")
}

// RFC 1123: Sun, 06 Nov 1994 08:49:37 GMT
func (t *Time) RFC1123() string {
	return Format(t, "%a, %d %b %Y %H:%M:%S %Z")
}

// date(1) - Sun Nov  6 08:49:37 GMT 1994
func (t *Time) String() string {
	return Format(t, "%a %b %e %H:%M:%S %Z %Y")
}

<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"flag"
	"os"
	"path"
	"time"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/rest"
)

const (
	resyncPeriod              = 15 * time.Second
	provisionerName           = "example.com/hostpath"
	exponentialBackOffOnError = false
	failedRetryThreshold      = 5
	leasePeriod               = controller.DefaultLeaseDuration
	retryPeriod               = controller.DefaultRetryPeriod
	renewDeadline             = controller.DefaultRenewDeadline
	termLimit                 = controller.DefaultTermLimit
)

type hostPathProvisioner struct {
	// The directory to create PV-backing directories in
	pvDir string

	// Identity of this hostPathProvisioner, set to node's name. Used to identify
	// "this" provisioner's PVs.
	identity string
}

func NewHostPathProvisioner() controller.Provisioner {
	nodeName := os.Getenv("NODE_NAME")
	if nodeName == "" {
		glog.Fatal("env variable NODE_NAME must be set so that this provisioner can identify itself")
	}
	return &hostPathProvisioner{
		pvDir:    "/tmp/hostpath-provisioner",
		identity: nodeName,
	}
}

var _ controller.Provisioner = &hostPathProvisioner{}

// Provision creates a storage asset and returns a PV object representing it.
func (p *hostPathProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	path := path.Join(p.pvDir, options.PVName)

	if err := os.MkdirAll(path, 0777); err != nil {
		return nil, err
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: options.PVName,
			Annotations: map[string]string{
				"hostPathProvisionerIdentity": p.identity,
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				HostPath: &v1.HostPathVolumeSource{
					Path: path,
				},
			},
		},
	}

	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *hostPathProvisioner) Delete(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations["hostPathProvisionerIdentity"]
	if !ok {
		return errors.New("identity annotation not found on PV")
	}
	if ann != p.identity {
		return &controller.IgnoredError{"identity annotation on PV does not match ours"}
	}

	path := path.Join(p.pvDir, volume.Name)
	if err := os.RemoveAll(path); err != nil {
		return err
	}

	return nil
}

func main() {
	flag.Parse()
	flag.Set("logtostderr", "true")

	// Create an InClusterConfig and use it to create a client for the controller
	// to use to communicate with Kubernetes
	config, err := rest.InClusterConfig()
	if err != nil {
		glog.Fatalf("Failed to create config: %v", err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Failed to create client: %v", err)
	}

	// The controller needs to know what the server version is because out-of-tree
	// provisioners aren't officially supported until 1.5
	serverVersion, err := clientset.Discovery().ServerVersion()
	if err != nil {
		glog.Fatalf("Error getting server version: %v", err)
	}

	// Create the provisioner: it implements the Provisioner interface expected by
	// the controller
	hostPathProvisioner := NewHostPathProvisioner()

	// Start the provision controller which will dynamically provision hostPath
	// PVs
	pc := controller.NewProvisionController(clientset, resyncPeriod, provisionerName, hostPathProvisioner, serverVersion.GitVersion, exponentialBackOffOnError, failedRetryThreshold, leasePeriod, renewDeadline, retryPeriod, termLimit)
	pc.Run(wait.NeverStop)
}
<commit_msg>Set umask to 0 to make sure the pvc directories have proper permissions<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"errors"
	"flag"
	"os"
	"path"
	"time"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/rest"
	"syscall"
)

const (
	resyncPeriod              = 15 * time.Second
	provisionerName           = "example.com/hostpath"
	exponentialBackOffOnError = false
	failedRetryThreshold      = 5
	leasePeriod               = controller.DefaultLeaseDuration
	retryPeriod               = controller.DefaultRetryPeriod
	renewDeadline             = controller.DefaultRenewDeadline
	termLimit                 = controller.DefaultTermLimit
)

type hostPathProvisioner struct {
	// The directory to create PV-backing directories in
	pvDir string

	// Identity of this hostPathProvisioner, set to node's name. Used to identify
	// "this" provisioner's PVs.
	identity string
}

func NewHostPathProvisioner() controller.Provisioner {
	nodeName := os.Getenv("NODE_NAME")
	if nodeName == "" {
		glog.Fatal("env variable NODE_NAME must be set so that this provisioner can identify itself")
	}
	return &hostPathProvisioner{
		pvDir:    "/tmp/hostpath-provisioner",
		identity: nodeName,
	}
}

var _ controller.Provisioner = &hostPathProvisioner{}

// Provision creates a storage asset and returns a PV object representing it.
func (p *hostPathProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	path := path.Join(p.pvDir, options.PVName)

	if err := os.MkdirAll(path, 0777); err != nil {
		return nil, err
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: options.PVName,
			Annotations: map[string]string{
				"hostPathProvisionerIdentity": p.identity,
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				HostPath: &v1.HostPathVolumeSource{
					Path: path,
				},
			},
		},
	}

	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *hostPathProvisioner) Delete(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations["hostPathProvisionerIdentity"]
	if !ok {
		return errors.New("identity annotation not found on PV")
	}
	if ann != p.identity {
		return &controller.IgnoredError{"identity annotation on PV does not match ours"}
	}

	path := path.Join(p.pvDir, volume.Name)
	if err := os.RemoveAll(path); err != nil {
		return err
	}

	return nil
}

func main() {
	syscall.Umask(0)

	flag.Parse()
	flag.Set("logtostderr", "true")

	// Create an InClusterConfig and use it to create a client for the controller
	// to use to communicate with Kubernetes
	config, err := rest.InClusterConfig()
	if err != nil {
		glog.Fatalf("Failed to create config: %v", err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Failed to create client: %v", err)
	}

	// The controller needs to know what the server version is because out-of-tree
	// provisioners aren't officially supported until 1.5
	serverVersion, err := clientset.Discovery().ServerVersion()
	if err != nil {
		glog.Fatalf("Error getting server version: %v", err)
	}

	// Create the provisioner: it implements the Provisioner interface expected by
	// the controller
	hostPathProvisioner := NewHostPathProvisioner()

	// Start the provision controller which will dynamically provision hostPath
	// PVs
	pc := controller.NewProvisionController(clientset, resyncPeriod, provisionerName, hostPathProvisioner, serverVersion.GitVersion, exponentialBackOffOnError, failedRetryThreshold, leasePeriod, renewDeadline, retryPeriod, termLimit)
	pc.Run(wait.NeverStop)
}
<|endoftext|>