<commit_before>package pwnWatch

import (
	"net/http"
	"time"
	"io/ioutil"
	"encoding/json"
	"pwnWatch/api"
)

const APIurl = "https://haveibeenpwned.com/api/v2/"

func DoRequest(request *http.Request) ([]byte, error) {
	var httpClient = &http.Client{Timeout: 10 * time.Second}
	var response []byte

	request.Header.Set("User-Agent", "pwnWatch")

	res, getErr := httpClient.Do(request)

	if getErr != nil {
		return response,getErr
	}

	body, readErr := ioutil.ReadAll(res.Body)
	if readErr != nil{
		return response,readErr
	}
	return body, nil
}

func GetAccountBreaches(account string) ([]api.Breach, error) {
	var breaches []api.Breach

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breachedaccount/"+account,nil)

	if reqErr != nil{
		return breaches,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breaches, err
	}

	jsonErr := json.Unmarshal(body,&breaches)
	if jsonErr != nil {
		return breaches,jsonErr
	}

	return breaches,nil
}

func GetBreach(breachName string) (api.Breach, error) {
	var breach api.Breach
	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breach/"+breachName,nil)

	if reqErr != nil{
		return breach,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breach, err
	}

	jsonErr := json.Unmarshal(body,&breach)
	if jsonErr != nil {
		return breach,jsonErr
	}

	return breach,nil
}

func GetBreaches()  ([]api.Breach, error) {
	var breaches []api.Breach

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breaches",nil)

	if reqErr != nil{
		return breaches,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breaches, err
	}

	jsonErr := json.Unmarshal(body,&breaches)
	if jsonErr != nil {
		return breaches,jsonErr
	}

	return breaches,nil
}

func GetAccountPastes(account string) ([]api.Paste, error) {
	var pastes []api.Paste

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"pasteaccount/"+account,nil)

	if reqErr != nil{
		return pastes,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return pastes, err
	}

	jsonErr := json.Unmarshal(body,&pastes)
	if jsonErr != nil {
		return pastes,jsonErr
	}

	return pastes,nil
}

/*
var commandMap = make(map[string]Command,0)

type ExecFunc func(client *http.Client, flagSet *flag.FlagSet)

type Command struct {
	flagSet *flag.FlagSet
	exec ExecFunc
}

func RegisterCliCommand(name string, flagSet *flag.FlagSet, exec ExecFunc)  {
	commandMap[name] = Command{flagSet, exec}
}

func InitFlags()  {

}

*/<commit_msg>Created models + basic api call functions, WIP<commit_after>package haveibeenpwned

import (
	"net/http"
	"time"
	"io/ioutil"
	"encoding/json"
	"pwnWatch/api"
)

const APIurl = "https://haveibeenpwned.com/api/v2/"

func DoRequest(request *http.Request) ([]byte, error) {
	var httpClient = &http.Client{Timeout: 10 * time.Second}
	var response []byte

	request.Header.Set("User-Agent", "pwnWatch")

	res, getErr := httpClient.Do(request)

	if getErr != nil {
		return response,getErr
	}

	body, readErr := ioutil.ReadAll(res.Body)
	if readErr != nil{
		return response,readErr
	}
	return body, nil
}

func GetAccountBreaches(account string) ([]api.Breach, error) {
	var breaches []api.Breach

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breachedaccount/"+account,nil)

	if reqErr != nil{
		return breaches,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breaches, err
	}

	jsonErr := json.Unmarshal(body,&breaches)
	if jsonErr != nil {
		return breaches,jsonErr
	}

	return breaches,nil
}

func GetBreach(breachName string) (api.Breach, error) {
	var breach api.Breach
	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breach/"+breachName,nil)

	if reqErr != nil{
		return breach,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breach, err
	}

	jsonErr := json.Unmarshal(body,&breach)
	if jsonErr != nil {
		return breach,jsonErr
	}

	return breach,nil
}

func GetBreaches()  ([]api.Breach, error) {
	var breaches []api.Breach

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"breaches",nil)

	if reqErr != nil{
		return breaches,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return breaches, err
	}

	jsonErr := json.Unmarshal(body,&breaches)
	if jsonErr != nil {
		return breaches,jsonErr
	}

	return breaches,nil
}

func GetAccountPastes(account string) ([]api.Paste, error) {
	var pastes []api.Paste

	req, reqErr := http.NewRequest(http.MethodGet,APIurl+"pasteaccount/"+account,nil)

	if reqErr != nil{
		return pastes,reqErr
	}

	body, err := DoRequest(req)

	if err != nil{
		return pastes, err
	}

	jsonErr := json.Unmarshal(body,&pastes)
	if jsonErr != nil {
		return pastes,jsonErr
	}

	return pastes,nil
}

/*
var commandMap = make(map[string]Command,0)

type ExecFunc func(client *http.Client, flagSet *flag.FlagSet)

type Command struct {
	flagSet *flag.FlagSet
	exec ExecFunc
}

func RegisterCliCommand(name string, flagSet *flag.FlagSet, exec ExecFunc)  {
	commandMap[name] = Command{flagSet, exec}
}

func InitFlags()  {

}

*/<|endoftext|>


<commit_before>package core

import (
	serr "github.com/tapglue/snaas/error"
	"github.com/tapglue/snaas/platform/sns"
	"github.com/tapglue/snaas/service/app"
	"github.com/tapglue/snaas/service/device"
)

var defaultDeleted = false

// DeviceDeleteFunc removes the device of a user.
type DeviceDeleteFunc func(*app.App, Origin, string) error

// DeviceDelete removes the device of a user.
func DeviceDelete(devices device.Service) DeviceDeleteFunc {
	return func(
		currentApp *app.App,
		origin Origin,
		deviceID string,
	) error {
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			DeviceIDs: []string{
				deviceID,
			},
			UserIDs: []uint64{
				origin.UserID,
			},
		})
		if err != nil {
			return err
		}

		if len(ds) == 0 {
			return nil
		}

		d := ds[0]
		d.Deleted = true

		_, err = devices.Put(currentApp.Namespace(), d)

		return err
	}
}

// DeviceDisableFunc disables the device for the given endpointARN.
type DeviceDisableFunc func(currentApp *app.App, endpointARN string) error

// DeviceDisable disables the device for the given endpointARN.
func DeviceDisable(devices device.Service) DeviceDisableFunc {
	return func(currentApp *app.App, endpointARN string) error {
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			EndpointARNs: []string{
				endpointARN,
			},
		})
		if err != nil {
			return err
		}

		if len(ds) == 0 {
			return nil
		}

		d := ds[0]
		d.Disabled = true

		_, err = devices.Put(currentApp.Namespace(), d)

		return err
	}
}

// DeviceListUserFunc returns all devices for origin.
type DeviceListUserFunc func(
	currentApp *app.App,
	origin uint64,
) (device.List, error)

// DeviceListUser returns all devices for origin.
func DeviceListUser(devices device.Service) DeviceListUserFunc {
	return func(currentApp *app.App, origin uint64) (device.List, error) {
		return devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted:  &defaultDeleted,
			Disabled: &defaultDeleted,
			Platforms: []sns.Platform{
				device.PlatformIOSSandbox,
				device.PlatformIOS,
				device.PlatformAndroid,
			},
			UserIDs: []uint64{
				origin,
			},
		})
	}
}

// DeviceSyncEndpointFunc assures symmetry between the representation of the
// Device in the device.Service and SNS.
// * create endpoint if not present
// * sync tokens if different
type DeviceSyncEndpointFunc func(
	currentApp *app.App,
	platformARN string,
	input *device.Device,
) (*device.Device, error)

// DeviceSyncEndpoint assures symmetry between the representation of the Device
// in the device.Service and SNS Platform Application.
// * create endpoint if not present
// * sync tokens if different
func DeviceSyncEndpoint(
	devices device.Service,
	endpointCreate sns.EndpointCreateFunc,
	endpointRetrieve sns.EndpointRetrieveFunc,
	endpointUpdate sns.EndpointUpdateFunc,
) DeviceSyncEndpointFunc {
	return func(
		currentApp *app.App,
		platformARN string,
		input *device.Device,
	) (*device.Device, error) {
		// Create a new Endpoint for the device if none was created before.
		if input.EndpointARN == "" {
			e, err := endpointCreate(platformARN, input.Token)
			if err != nil {
				return nil, err
			}

			input.EndpointARN = e.ARN

			return devices.Put(currentApp.Namespace(), input)
		}

		e, err := endpointRetrieve(input.EndpointARN)
		if err != nil && !sns.IsEndpointDisabled(err) &&
			!sns.IsEndpointNotFound(err) {
			return nil, err
		}

		// If the Endpoint is disabled we disable the device and return an
		// appropriate error.
		if sns.IsEndpointDisabled(err) {
			input.Disabled = true

			_, err := devices.Put(currentApp.Namespace(), input)
			if err != nil {
				return nil, err
			}

			return nil, serr.Wrap(serr.ErrDeviceDisabled, "%d", input.ID)
		}

		// If the Endpoint is gone we create a new one.
		if sns.IsEndpointNotFound(err) {
			e, err := endpointCreate(platformARN, input.Token)
			if err != nil {
				return nil, err
			}

			input.EndpointARN = e.ARN

			return devices.Put(currentApp.Namespace(), input)
		}

		// Check if Tokens match.
		if input.Token != e.Token {
			_, err := endpointUpdate(input.EndpointARN, input.Token)
			if err != nil {
				return nil, err
			}
		}

		return input, nil
	}
}

// DeviceUpdateFunc stores the device data and updates the endpoint.
type DeviceUpdateFunc func(
	currentApp *app.App,
	origin Origin,
	deviceID string,
	platform sns.Platform,
	token string,
	language string,
) error

// DeviceUpdate stores the device info in the given device service.
func DeviceUpdate(devices device.Service) DeviceUpdateFunc {
	return func(
		currentApp *app.App,
		origin Origin,
		deviceID string,
		platform sns.Platform,
		token string,
		language string,
	) error {
		// Get user devices.
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			Platforms: []sns.Platform{
				platform,
			},
			UserIDs: []uint64{
				origin.UserID,
			},
		})
		if err != nil {
			return err
		}

		// Check if there is a device with that token.
		ts, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			Tokens: []string{
				token,
			},
		})
		if err != nil {
			return err
		}

		ds = append(ds, ts...)

		var old *device.Device

		for _, dev := range ds {
			if dev.DeviceID == deviceID &&
				dev.Token == token &&
				dev.Language == language &&
				dev.Platform == platform &&
				dev.UserID == origin.UserID {
				return nil
			}

			if dev.Token == token {
				old = dev
			}
		}

		d := &device.Device{
			DeviceID: deviceID,
			Disabled: false,
			Language: language,
			Platform: platform,
			Token:    token,
			UserID:   origin.UserID,
		}

		if old != nil {
			d.EndpointARN = ""
			d.ID = old.ID
		}

		_, err = devices.Put(currentApp.Namespace(), d)
		if err != nil {
			if device.IsInvalidDevice(err) {
				return wrapError(ErrInvalidEntity, "%s", err)
			}
		}

		return err
	}
}
<commit_msg>Hit correct device index<commit_after>package core

import (
	serr "github.com/tapglue/snaas/error"
	"github.com/tapglue/snaas/platform/sns"
	"github.com/tapglue/snaas/service/app"
	"github.com/tapglue/snaas/service/device"
)

var defaultDeleted = false

// DeviceDeleteFunc removes the device of a user.
type DeviceDeleteFunc func(*app.App, Origin, string) error

// DeviceDelete removes the device of a user.
func DeviceDelete(devices device.Service) DeviceDeleteFunc {
	return func(
		currentApp *app.App,
		origin Origin,
		deviceID string,
	) error {
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			DeviceIDs: []string{
				deviceID,
			},
			UserIDs: []uint64{
				origin.UserID,
			},
		})
		if err != nil {
			return err
		}

		if len(ds) == 0 {
			return nil
		}

		d := ds[0]
		d.Deleted = true

		_, err = devices.Put(currentApp.Namespace(), d)

		return err
	}
}

// DeviceDisableFunc disables the device for the given endpointARN.
type DeviceDisableFunc func(currentApp *app.App, endpointARN string) error

// DeviceDisable disables the device for the given endpointARN.
func DeviceDisable(devices device.Service) DeviceDisableFunc {
	return func(currentApp *app.App, endpointARN string) error {
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			EndpointARNs: []string{
				endpointARN,
			},
		})
		if err != nil {
			return err
		}

		if len(ds) == 0 {
			return nil
		}

		d := ds[0]
		d.Disabled = true

		_, err = devices.Put(currentApp.Namespace(), d)

		return err
	}
}

// DeviceListUserFunc returns all devices for origin.
type DeviceListUserFunc func(
	currentApp *app.App,
	origin uint64,
) (device.List, error)

// DeviceListUser returns all devices for origin.
func DeviceListUser(devices device.Service) DeviceListUserFunc {
	return func(currentApp *app.App, origin uint64) (device.List, error) {
		return devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted:  &defaultDeleted,
			Disabled: &defaultDeleted,
			Platforms: []sns.Platform{
				device.PlatformIOSSandbox,
				device.PlatformIOS,
				device.PlatformAndroid,
			},
			UserIDs: []uint64{
				origin,
			},
		})
	}
}

// DeviceSyncEndpointFunc assures symmetry between the representation of the
// Device in the device.Service and SNS.
// * create endpoint if not present
// * sync tokens if different
type DeviceSyncEndpointFunc func(
	currentApp *app.App,
	platformARN string,
	input *device.Device,
) (*device.Device, error)

// DeviceSyncEndpoint assures symmetry between the representation of the Device
// in the device.Service and SNS Platform Application.
// * create endpoint if not present
// * sync tokens if different
func DeviceSyncEndpoint(
	devices device.Service,
	endpointCreate sns.EndpointCreateFunc,
	endpointRetrieve sns.EndpointRetrieveFunc,
	endpointUpdate sns.EndpointUpdateFunc,
) DeviceSyncEndpointFunc {
	return func(
		currentApp *app.App,
		platformARN string,
		input *device.Device,
	) (*device.Device, error) {
		// Create a new Endpoint for the device if none was created before.
		if input.EndpointARN == "" {
			e, err := endpointCreate(platformARN, input.Token)
			if err != nil {
				return nil, err
			}

			input.EndpointARN = e.ARN

			return devices.Put(currentApp.Namespace(), input)
		}

		e, err := endpointRetrieve(input.EndpointARN)
		if err != nil && !sns.IsEndpointDisabled(err) &&
			!sns.IsEndpointNotFound(err) {
			return nil, err
		}

		// If the Endpoint is disabled we disable the device and return an
		// appropriate error.
		if sns.IsEndpointDisabled(err) {
			input.Disabled = true

			_, err := devices.Put(currentApp.Namespace(), input)
			if err != nil {
				return nil, err
			}

			return nil, serr.Wrap(serr.ErrDeviceDisabled, "%d", input.ID)
		}

		// If the Endpoint is gone we create a new one.
		if sns.IsEndpointNotFound(err) {
			e, err := endpointCreate(platformARN, input.Token)
			if err != nil {
				return nil, err
			}

			input.EndpointARN = e.ARN

			return devices.Put(currentApp.Namespace(), input)
		}

		// Check if Tokens match.
		if input.Token != e.Token {
			_, err := endpointUpdate(input.EndpointARN, input.Token)
			if err != nil {
				return nil, err
			}
		}

		return input, nil
	}
}

// DeviceUpdateFunc stores the device data and updates the endpoint.
type DeviceUpdateFunc func(
	currentApp *app.App,
	origin Origin,
	deviceID string,
	platform sns.Platform,
	token string,
	language string,
) error

// DeviceUpdate stores the device info in the given device service.
func DeviceUpdate(devices device.Service) DeviceUpdateFunc {
	return func(
		currentApp *app.App,
		origin Origin,
		deviceID string,
		platform sns.Platform,
		token string,
		language string,
	) error {
		// Get user devices.
		ds, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted:  &defaultDeleted,
			Disabled: &defaultDeleted,
			Platforms: []sns.Platform{
				platform,
			},
			UserIDs: []uint64{
				origin.UserID,
			},
		})
		if err != nil {
			return err
		}

		// Check if there is a device with that token.
		ts, err := devices.Query(currentApp.Namespace(), device.QueryOptions{
			Deleted: &defaultDeleted,
			Tokens: []string{
				token,
			},
		})
		if err != nil {
			return err
		}

		ds = append(ds, ts...)

		var old *device.Device

		for _, dev := range ds {
			if dev.DeviceID == deviceID &&
				dev.Token == token &&
				dev.Language == language &&
				dev.Platform == platform &&
				dev.UserID == origin.UserID {
				return nil
			}

			if dev.Token == token {
				old = dev
			}
		}

		d := &device.Device{
			DeviceID: deviceID,
			Disabled: false,
			Language: language,
			Platform: platform,
			Token:    token,
			UserID:   origin.UserID,
		}

		if old != nil {
			d.EndpointARN = ""
			d.ID = old.ID
		}

		_, err = devices.Put(currentApp.Namespace(), d)
		if err != nil {
			if device.IsInvalidDevice(err) {
				return wrapError(ErrInvalidEntity, "%s", err)
			}
		}

		return err
	}
}
<|endoftext|>


<commit_before>package parser

import (
	"bufio"
	"bytes"
	"io"
	"unicode"
)

// eof represents a marker rune for the end of the reader.
const eof = rune(0)

// Lexer defines a lexical scanner
type Lexer struct {
	src *bufio.Reader // input
	ch  rune          // current character
}

// NewLexer returns a new instance of Lexer.
func NewLexer(src io.Reader) *Lexer {
	return &Lexer{
		src: bufio.NewReader(src),
	}
}

// next reads the next rune from the bufferred reader. Returns the rune(0) if
// an error occurs (or io.EOF is returned).
func (l *Lexer) next() rune {
	var err error
	l.ch, _, err = l.src.ReadRune()
	if err != nil {
		return eof
	}

	return l.ch
}

// unread places the previously read rune back on the reader.
func (l *Lexer) unread() { _ = l.src.UnreadRune() }

// Scan scans the next token and returns the token and it's literal string.
func (l *Lexer) Scan() (tok Token, lit string) {
	ch := l.next()

	// skip white space
	for isWhitespace(ch) {
		ch = l.next()
	}

	// identifier
	if isLetter(ch) {
		return l.scanIdentifier()
	}

	switch ch {
	case eof:
		return EOF, ""
	}

	return 0, ""
}

func (l *Lexer) scanIdentifier() (Token, string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer

	// write current character before we move to the next
	buf.WriteRune(l.ch)

	return 0, ""
}

// Pos returns the position of the character immediately after the character or
// token returned by the last call to Next or Scan.
func (l *Lexer) Pos() Position {
	return Position{}
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

func isLetter(ch rune) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}

func isDigit(ch rune) bool {
	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}

// isWhitespace returns true if the rune is a space, tab, newline or carriage return
func isWhitespace(ch rune) bool {
	return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}
<commit_msg>lexer: add peek() method<commit_after>package parser

import (
	"bufio"
	"bytes"
	"io"
	"unicode"
)

// eof represents a marker rune for the end of the reader.
const eof = rune(0)

// Lexer defines a lexical scanner
type Lexer struct {
	src *bufio.Reader // input
	ch  rune          // current character
}

// NewLexer returns a new instance of Lexer.
func NewLexer(src io.Reader) *Lexer {
	return &Lexer{
		src: bufio.NewReader(src),
	}
}

// next reads the next rune from the bufferred reader. Returns the rune(0) if
// an error occurs (or io.EOF is returned).
func (l *Lexer) next() rune {
	var err error
	l.ch, _, err = l.src.ReadRune()
	if err != nil {
		return eof
	}

	return l.ch
}

// unread places the previously read rune back on the reader.
func (l *Lexer) unread() { _ = l.src.UnreadRune() }

func (l *Lexer) peek() rune {
	prev := l.ch
	peekCh := l.next()
	l.unread()
	l.ch = prev
	return peekCh
}

// Scan scans the next token and returns the token and it's literal string.
func (l *Lexer) Scan() (tok Token, lit string) {
	ch := l.next()

	// skip white space
	for isWhitespace(ch) {
		ch = l.next()
	}

	// identifier
	if isLetter(ch) {
		return l.scanIdentifier()
	}

	switch ch {
	case eof:
		return EOF, ""
	}

	return 0, ""
}

func (l *Lexer) scanIdentifier() (Token, string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer

	// write current character before we move to the next
	buf.WriteRune(l.ch)

	return 0, ""
}

// Pos returns the position of the character immediately after the character or
// token returned by the last call to Next or Scan.
func (l *Lexer) Pos() Position {
	return Position{}
}

// isSpace reports whether r is a space character.
func isSpace(r rune) bool {
	return r == ' ' || r == '\t'
}

// isEndOfLine reports whether r is an end-of-line character.
func isEndOfLine(r rune) bool {
	return r == '\r' || r == '\n'
}

func isLetter(ch rune) bool {
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch)
}

func isDigit(ch rune) bool {
	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
}

// isWhitespace returns true if the rune is a space, tab, newline or carriage return
func isWhitespace(ch rune) bool {
	return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
}
<|endoftext|>


<commit_before>package parser

import (
  "fmt"
  "regexp"
  "strings"
  "unicode/utf8"
  "bitbucket.org/yyuu/bs/strscan"
)

type lex struct {
  scanner strscan.StringScanner
  Filename string
  LineNumber int
  LineOffset int
  ignoreSpaces bool
  ignoreComments bool
}

func (self lex) String() string {
  source := fmt.Sprintf("%s%s...", self.scanner.Matched(), self.scanner.Peek(32))
  return fmt.Sprintf("%s:%d: %q", self.Filename, self.LineNumber, source)
}

type token struct {
  Id int
  Literal string
  Filename string
  LineNumber int
  LineOffset int
}

func (self token) String() string {
  return fmt.Sprintf("#<token:%d %s:%d,%d %q>", self.Id, self.Filename, self.LineNumber, self.LineOffset, self.Literal)
}

func lexer(filename string, source string) *lex {
  return &lex {
    scanner: strscan.New(source),
    Filename: filename,
    LineNumber: 0,
    LineOffset: 0,
    ignoreSpaces: true,
    ignoreComments: true,
  }
}

var keywords map[string]int = map[string]int {
  regexp.QuoteMeta("void"):     VOID,
  regexp.QuoteMeta("char"):     CHAR,
  regexp.QuoteMeta("short"):    SHORT,
  regexp.QuoteMeta("int"):      INT,
  regexp.QuoteMeta("long"):     LONG,
  regexp.QuoteMeta("struct"):   STRUCT,
  regexp.QuoteMeta("union"):    UNION,
  regexp.QuoteMeta("enum"):     ENUM,
  regexp.QuoteMeta("static"):   STATIC,
  regexp.QuoteMeta("extern"):   EXTERN,
  regexp.QuoteMeta("const"):    CONST,
  regexp.QuoteMeta("signed"):   SIGNED,
  regexp.QuoteMeta("unsigned"): UNSIGNED,
  regexp.QuoteMeta("if"):       IF,
  regexp.QuoteMeta("else"):     ELSE,
  regexp.QuoteMeta("switch"):   SWITCH,
  regexp.QuoteMeta("case"):     CASE,
  regexp.QuoteMeta("default"):  DEFAULT,
  regexp.QuoteMeta("while"):    WHILE,
  regexp.QuoteMeta("do"):       DO,
  regexp.QuoteMeta("for"):      FOR,
  regexp.QuoteMeta("return"):   RETURN,
  regexp.QuoteMeta("break"):    BREAK,
  regexp.QuoteMeta("continue"): CONTINUE,
  regexp.QuoteMeta("goto"):     GOTO,
  regexp.QuoteMeta("typedef"):  TYPEDEF,
  regexp.QuoteMeta("import"):   IMPORT,
  regexp.QuoteMeta("sizeof"):   SIZEOF,
}

var operators map[string]int = map[string]int {
  regexp.QuoteMeta("..."):      DOTDOTDOT,
  regexp.QuoteMeta("<<="):      LSHIFTEQ,
  regexp.QuoteMeta(">>="):      RSHIFTEQ,
  regexp.QuoteMeta("!="):       NEQ,
  regexp.QuoteMeta("%="):       MODEQ,
  regexp.QuoteMeta("&&"):       ANDAND,
  regexp.QuoteMeta("&="):       ANDEQ,
  regexp.QuoteMeta("*="):       MULEQ,
  regexp.QuoteMeta("++"):       PLUSPLUS,
  regexp.QuoteMeta("+="):       PLUSEQ,
  regexp.QuoteMeta("--"):       MINUSMINUS,
  regexp.QuoteMeta("-="):       MINUSEQ,
  regexp.QuoteMeta("->"):       ARROW,
  regexp.QuoteMeta("/="):       DIVEQ,
  regexp.QuoteMeta("<<"):       LSHIFT,
  regexp.QuoteMeta("<="):       LTEQ,
  regexp.QuoteMeta("=="):       EQEQ,
  regexp.QuoteMeta(">="):       GTEQ,
  regexp.QuoteMeta(">>"):       RSHIFT,
  regexp.QuoteMeta("^="):       XOREQ,
  regexp.QuoteMeta("|="):       OREQ,
  regexp.QuoteMeta("||"):       OROR,
}

func (self *lex) GetToken() (t *token) {
  if self.scanner.IsEOS() {
    return nil
  }

  t = self.scanSpaces()
  if t != nil {
    if ! self.ignoreSpaces {
      return t
    } else {
      return self.GetToken()
    }
  }

  t = self.scanBlockComment()
  if t != nil {
    if ! self.ignoreComments {
      return t
    } else {
      return self.GetToken()
    }
  }
  t = self.scanLineComment()
  if t != nil {
    if ! self.ignoreComments {
      return t
    } else {
      return self.GetToken()
    }
  }

  t = self.scanKeyword()
  if t != nil {
    return t
  }

  t = self.scanIdentifier()
  if t != nil {
    return t
  }

  t = self.scanInteger()
  if t != nil {
    return t
  }

  t = self.scanCharacter()
  if t != nil {
    return t
  }

  t = self.scanString()
  if t != nil {
    return t
  }

  t = self.scanOperator()
  if t != nil {
    return t
  }

  panic(fmt.Errorf("lexer error: %s", self))
}

func (self *lex) consume(id int, literal string) (t *token) {
  t = &token {
    Id: id,
    Literal: literal,
    Filename: self.Filename,
    LineNumber: self.LineNumber,
    LineOffset: self.LineOffset,
  }

  self.LineNumber += strings.Count(literal, "\n")
  i := strings.LastIndex(literal, "\n")
  if i < 0 {
    self.LineOffset += len(literal)
  } else {
    self.LineOffset = len(literal[i:])
  }

  return t
}

func (self *lex) scanBlockComment() *token {
  s := self.scanner.Scan("/\\*")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("\\*/")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(BLOCK_COMMENT, s + more)
}

func (self *lex) scanLineComment() *token {
  s := self.scanner.Scan("//")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("(\n|\r\n|\r)")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(LINE_COMMENT, s + more)
}

func (self *lex) scanSpaces() *token {
  s := self.scanner.Scan("[ \t\n\r\f]+")
  if s == "" {
    return nil
  }
  return self.consume(SPACES, s)
}

func (self *lex) scanIdentifier() *token {
  s := self.scanner.Scan("[_A-Za-z][_0-9A-Za-z]*")
  if s == "" {
    return nil
  }
  return self.consume(IDENTIFIER, s)
}

func (self *lex) scanInteger() *token {
  s := self.scanner.Scan("([1-9][0-9]*U?L?|0[Xx][0-9A-Fa-f]+U?L?|0[0-7]*U?L?)")
  if s == "" {
    return nil
  }
  return self.consume(INTEGER, s)
}

func (self *lex) scanKeyword() *token {
  for r, id := range keywords {
    s := self.scanner.Scan(r)
    if s != "" {
      return self.consume(id, s)
    }
  }
  return nil
}

func (self *lex) scanCharacter() *token {
  s := self.scanner.Scan("'")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("'")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(CHARACTER, s + more)
}

func (self *lex) scanString() *token {
  s := self.scanner.Scan("\"")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("\"")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(STRING, s + more)
}

func (self *lex) scanOperator() *token {
  for r, id := range operators {
    s := self.scanner.Scan(r)
    if s != "" {
      return self.consume(id, s)
    }
  }

  // use next rune as an operator if available
  s := self.scanner.Scan(".")
  if s != "" {
    r, _ := utf8.DecodeRuneInString(s)
    return self.consume(int(r), s)
  }
  return nil
}
<commit_msg>Use slice to declare keywords/operators since they have its order<commit_after>package parser

import (
  "fmt"
  "regexp"
  "strings"
  "unicode/utf8"
  "bitbucket.org/yyuu/bs/strscan"
)

type lex struct {
  scanner strscan.StringScanner
  Filename string
  LineNumber int
  LineOffset int
  ignoreSpaces bool
  ignoreComments bool
}

func (self lex) String() string {
  source := fmt.Sprintf("%s%s...", self.scanner.Matched(), self.scanner.Peek(32))
  return fmt.Sprintf("%s:%d: %q", self.Filename, self.LineNumber, source)
}

type token struct {
  Id int
  Literal string
  Filename string
  LineNumber int
  LineOffset int
}

func (self token) String() string {
  return fmt.Sprintf("#<token:%d %s:%d,%d %q>", self.Id, self.Filename, self.LineNumber, self.LineOffset, self.Literal)
}

func lexer(filename string, source string) *lex {
  return &lex {
    scanner: strscan.New(source),
    Filename: filename,
    LineNumber: 0,
    LineOffset: 0,
    ignoreSpaces: true,
    ignoreComments: true,
  }
}

type key struct {
  re string
  id int
}

func fixed_key(s string, n int) key {
  return key { regexp.QuoteMeta(s), n }
}

var keywords []key = []key {
  fixed_key("void",     VOID),
  fixed_key("char",     CHAR),
  fixed_key("short",    SHORT),
  fixed_key("int",      INT),
  fixed_key("long",     LONG),
  fixed_key("struct",   STRUCT),
  fixed_key("union",    UNION),
  fixed_key("enum",     ENUM),
  fixed_key("static",   STATIC),
  fixed_key("extern",   EXTERN),
  fixed_key("const",    CONST),
  fixed_key("signed",   SIGNED),
  fixed_key("unsigned", UNSIGNED),
  fixed_key("if",       IF),
  fixed_key("else",     ELSE),
  fixed_key("switch",   SWITCH),
  fixed_key("case",     CASE),
  fixed_key("default",  DEFAULT),
  fixed_key("while",    WHILE),
  fixed_key("do",       DO),
  fixed_key("for",      FOR),
  fixed_key("return",   RETURN),
  fixed_key("break",    BREAK),
  fixed_key("continue", CONTINUE),
  fixed_key("goto",     GOTO),
  fixed_key("typedef",  TYPEDEF),
  fixed_key("import",   IMPORT),
  fixed_key("sizeof",   SIZEOF),
}

var operators []key = []key {
  fixed_key("...",      DOTDOTDOT),
  fixed_key("<<=",      LSHIFTEQ),
  fixed_key(">>=",      RSHIFTEQ),
  fixed_key("!=",       NEQ),
  fixed_key("%=",       MODEQ),
  fixed_key("&&",       ANDAND),
  fixed_key("&=",       ANDEQ),
  fixed_key("*=",       MULEQ),
  fixed_key("++",       PLUSPLUS),
  fixed_key("+=",       PLUSEQ),
  fixed_key("--",       MINUSMINUS),
  fixed_key("-=",       MINUSEQ),
  fixed_key("->",       ARROW),
  fixed_key("/=",       DIVEQ),
  fixed_key("<<",       LSHIFT),
  fixed_key("<=",       LTEQ),
  fixed_key("==",       EQEQ),
  fixed_key(">=",       GTEQ),
  fixed_key(">>",       RSHIFT),
  fixed_key("^=",       XOREQ),
  fixed_key("|=",       OREQ),
  fixed_key("||",       OROR),
}

func (self *lex) GetToken() (t *token) {
  if self.scanner.IsEOS() {
    return nil
  }

  t = self.scanSpaces()
  if t != nil {
    if ! self.ignoreSpaces {
      return t
    } else {
      return self.GetToken()
    }
  }

  t = self.scanBlockComment()
  if t != nil {
    if ! self.ignoreComments {
      return t
    } else {
      return self.GetToken()
    }
  }
  t = self.scanLineComment()
  if t != nil {
    if ! self.ignoreComments {
      return t
    } else {
      return self.GetToken()
    }
  }

  t = self.scanKeyword()
  if t != nil {
    return t
  }

  t = self.scanIdentifier()
  if t != nil {
    return t
  }

  t = self.scanInteger()
  if t != nil {
    return t
  }

  t = self.scanCharacter()
  if t != nil {
    return t
  }

  t = self.scanString()
  if t != nil {
    return t
  }

  t = self.scanOperator()
  if t != nil {
    return t
  }

  panic(fmt.Errorf("lexer error: %s", self))
}

func (self *lex) consume(id int, literal string) (t *token) {
  t = &token {
    Id: id,
    Literal: literal,
    Filename: self.Filename,
    LineNumber: self.LineNumber,
    LineOffset: self.LineOffset,
  }

  self.LineNumber += strings.Count(literal, "\n")
  i := strings.LastIndex(literal, "\n")
  if i < 0 {
    self.LineOffset += len(literal)
  } else {
    self.LineOffset = len(literal[i:])
  }

  return t
}

func (self *lex) scanBlockComment() *token {
  s := self.scanner.Scan("/\\*")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("\\*/")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(BLOCK_COMMENT, s + more)
}

func (self *lex) scanLineComment() *token {
  s := self.scanner.Scan("//")
  if s == "" {
    return nil
  }
  more := self.scanner.ScanUntil("(\n|\r\n|\r)")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(LINE_COMMENT, s + more)
}

func (self *lex) scanSpaces() *token {
  s := self.scanner.Scan("[ \t\n\r\f]+")
  if s == "" {
    return nil
  }
  return self.consume(SPACES, s)
}

func (self *lex) scanIdentifier() *token {
  s := self.scanner.Scan("[_A-Za-z][_0-9A-Za-z]*")
  if s == "" {
    return nil
  }
  return self.consume(IDENTIFIER, s)
}

func (self *lex) scanInteger() *token {
  s := self.scanner.Scan("([1-9][0-9]*U?L?|0[Xx][0-9A-Fa-f]+U?L?|0[0-7]*U?L?)")
  if s == "" {
    return nil
  }
  return self.consume(INTEGER, s)
}

func (self *lex) scanKeyword() *token {
  for i := range keywords {
    x := keywords[i]
    s := self.scanner.Scan(x.re)
    if s != "" {
      return self.consume(x.id, s)
    }
  }
  return nil
}

func (self *lex) scanCharacter() *token {
  s := self.scanner.Scan("'")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("'")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(CHARACTER, s + more)
}

func (self *lex) scanString() *token {
  s := self.scanner.Scan("\"")
  if s == "" {
    return nil
  }
  // TODO: handle escape character properly
  more := self.scanner.ScanUntil("\"")
  if more == "" {
    panic(fmt.Errorf("lexer error: %s", self))
  }
  return self.consume(STRING, s + more)
}

func (self *lex) scanOperator() *token {
  for i := range operators {
    x := operators[i]
    s := self.scanner.Scan(x.re)
    if s != "" {
      return self.consume(x.id, s)
    }
  }

  // use next rune as an operator if available
  s := self.scanner.Scan(".")
  if s != "" {
    r, _ := utf8.DecodeRuneInString(s)
    return self.consume(int(r), s)
  }
  return nil
}
<|endoftext|>


<commit_before>package tsacmd

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"sync"
	"time"

	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/atc"
	"github.com/concourse/tsa"
	"github.com/felixge/tcpkeepalive"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/rata"
	"golang.org/x/crypto/ssh"
)

func keepaliveDialerFactory(network string, address string) gconn.DialerFunc {
	return func(string, string) (net.Conn, error) {
		conn, err := net.DialTimeout(network, address, 5*time.Second)
		if err != nil {
			return nil, err
		}

		kac, err := tcpkeepalive.EnableKeepAlive(conn)
		if err != nil {
			println("failed to enable connection keepalive: " + err.Error())
		}

		err = kac.SetKeepAliveIdle(10 * time.Second)
		if err != nil {
			println("failed to set keepalive idle threshold: " + err.Error())
		}

		err = kac.SetKeepAliveCount(3)
		if err != nil {
			println("failed to set keepalive count: " + err.Error())
		}

		err = kac.SetKeepAliveInterval(5 * time.Second)
		if err != nil {
			println("failed to set keepalive interval: " + err.Error())
		}

		return conn, nil
	}
}

type registrarSSHServer struct {
	logger            lager.Logger
	atcEndpoint       *rata.RequestGenerator
	tokenGenerator    tsa.TokenGenerator
	heartbeatInterval time.Duration
	cprInterval       time.Duration
	forwardHost       string
	config            *ssh.ServerConfig
	httpClient        *http.Client
}

func (server *registrarSSHServer) Serve(listener net.Listener) {
	for {
		c, err := listener.Accept()
		if err != nil {
			server.logger.Error("failed-to-accept", err)
			return
		}

		logger := server.logger.Session("connection")

		conn, chans, reqs, err := ssh.NewServerConn(c, server.config)
		if err != nil {
			logger.Info("handshake-failed", lager.Data{"error": err.Error()})
			continue
		}

		go server.handleConn(logger, conn, chans, reqs)
	}
}

type forwardedTCPIP struct {
	bindAddr  string
	process   ifrit.Process
	boundPort uint32
}

func (server *registrarSSHServer) handleConn(logger lager.Logger, conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
	defer conn.Close()

	forwardedTCPIPs := make(chan forwardedTCPIP, 2)
	go server.handleForwardRequests(logger, conn, reqs, forwardedTCPIPs)

	var processes []ifrit.Process
	var process ifrit.Process

	// ensure processes get cleaned up
	defer func() {
		cleanupLog := logger.Session("cleanup")

		for _, p := range processes {
			cleanupLog.Debug("interrupting")

			p.Signal(os.Interrupt)
		}

		for _, p := range processes {
			err := <-p.Wait()
			if err != nil {
				cleanupLog.Error("process-exited-with-failure", err)
			} else {
				cleanupLog.Debug("process-exited-successfully")
			}
		}
	}()

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			logger.Info("rejecting-unknown-channel-type", lager.Data{
				"type": newChannel.ChannelType(),
			})

			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Error("failed-to-accept-channel", err)
			return
		}

		defer channel.Close()

		for req := range requests {
			logger.Info("channel-request", lager.Data{
				"type": req.Type,
			})

			if req.Type != "exec" {
				logger.Info("rejecting")
				req.Reply(false, nil)
				continue
			}

			var request execRequest
			err = ssh.Unmarshal(req.Payload, &request)
			if err != nil {
				logger.Error("malformed-exec-request", err)
				req.Reply(false, nil)
				return
			}

			workerRequest, err := parseRequest(request.Command)
			if err != nil {
				fmt.Fprintf(channel, "invalid command: %s", err)
				req.Reply(false, nil)
				continue
			}

			switch r := workerRequest.(type) {
			case registerWorkerRequest:
				logger := logger.Session("register-worker")

				req.Reply(true, nil)

				process, err = server.continuouslyRegisterWorkerDirectly(logger, channel)
				if err != nil {
					logger.Error("failed-to-register", err)
					return
				}

				processes = append(processes, process)

				err = conn.Wait()
				logger.Error("connection-closed", err)

			case forwardWorkerRequest:
				logger := logger.Session("forward-worker")

				forwards := map[string]forwardedTCPIP{}

				for i := 0; i < r.expectedForwards(); i++ {
					select {
					case forwarded := <-forwardedTCPIPs:
						logger.Info("forwarded-tcpip", lager.Data{
							"bound-port": forwarded.boundPort,
						})

						processes = append(processes, forwarded.process)

						forwards[forwarded.bindAddr] = forwarded

					case <-time.After(10 * time.Second): // todo better?
						logger.Info("never-forwarded-tcpip")
					}
				}

				switch len(forwards) {
				case 0:
					fmt.Fprintf(channel, "requested forwarding but no forwards given\n")
					return

				case 1:
					for _, gardenForward := range forwards {
						process, err = server.continuouslyRegisterForwardedWorker(
							logger,
							channel,
							gardenForward.boundPort,
							0,
						)
						if err != nil {
							logger.Error("failed-to-register", err)
							return
						}

						processes = append(processes, process)

						break
					}

				case 2:
					gardenForward, found := forwards[r.gardenAddr]
					if !found {
						fmt.Fprintf(channel, "garden address %s not found in forwards\n", r.gardenAddr)
						return
					}

					baggageclaimForward, found := forwards[r.baggageclaimAddr]
					if !found {
						fmt.Fprintf(channel, "baggageclaim address %s not found in forwards\n", r.gardenAddr)
						return
					}

					process, err = server.continuouslyRegisterForwardedWorker(
						logger,
						channel,
						gardenForward.boundPort,
						baggageclaimForward.boundPort,
					)
					if err != nil {
						logger.Error("failed-to-register", err)
						return
					}

					processes = append(processes, process)
				}

				err = conn.Wait()
				logger.Error("connection-closed", err)

			default:
				logger.Info("invalid-command", lager.Data{
					"command": request.Command,
				})

				req.Reply(false, nil)
			}
		}
	}
}

func (server *registrarSSHServer) continuouslyRegisterWorkerDirectly(
	logger lager.Logger,
	channel ssh.Channel,
) (ifrit.Process, error) {
	logger.Session("start")
	defer logger.Session("done")

	var worker atc.Worker
	err := json.NewDecoder(channel).Decode(&worker)
	if err != nil {
		return nil, err
	}

	return server.heartbeatWorker(logger, worker, channel), nil
}

func (server *registrarSSHServer) continuouslyRegisterForwardedWorker(
	logger lager.Logger,
	channel ssh.Channel,
	gardenPort uint32,
	baggageclaimPort uint32,
) (ifrit.Process, error) {
	logger.Session("start")
	defer logger.Session("done")

	var worker atc.Worker
	err := json.NewDecoder(channel).Decode(&worker)
	if err != nil {
		return nil, err
	}

	worker.GardenAddr = fmt.Sprintf("%s:%d", server.forwardHost, gardenPort)

	if baggageclaimPort != 0 {
		worker.BaggageclaimURL = fmt.Sprintf("http://%s:%d", server.forwardHost, baggageclaimPort)
	}

	return server.heartbeatWorker(logger, worker, channel), nil
}

func (server *registrarSSHServer) heartbeatWorker(logger lager.Logger, worker atc.Worker, channel ssh.Channel) ifrit.Process {
	return ifrit.Background(tsa.NewHeartbeater(
		logger,
		clock.NewClock(),
		server.heartbeatInterval,
		server.cprInterval,
		gclient.New(gconn.NewWithDialerAndLogger(keepaliveDialerFactory("tcp", worker.GardenAddr), logger.Session("garden-connection"))),
		server.atcEndpoint,
		server.tokenGenerator,
		worker,
		channel,
	))
}

func (server *registrarSSHServer) handleForwardRequests(
	logger lager.Logger,
	conn *ssh.ServerConn,
	reqs <-chan *ssh.Request,
	forwardedTCPIPs chan<- forwardedTCPIP,
) {
	var forwardedThings int

	for r := range reqs {
		switch r.Type {
		case "tcpip-forward":
			logger := logger.Session("tcpip-forward")

			forwardedThings++

			if forwardedThings > 2 {
				logger.Info("rejecting-extra-forward-request")
				r.Reply(false, nil)
				continue
			}

			var req tcpipForwardRequest
			err := ssh.Unmarshal(r.Payload, &req)
			if err != nil {
				logger.Error("malformed-tcpip-request", err)
				r.Reply(false, nil)
				continue
			}

			listener, err := net.Listen("tcp", "0.0.0.0:0")
			if err != nil {
				logger.Error("failed-to-listen", err)
				r.Reply(false, nil)
				continue
			}

			defer listener.Close()

			bindAddr := net.JoinHostPort(req.BindIP, fmt.Sprintf("%d", req.BindPort))

			logger.Info("forwarding-tcpip", lager.Data{
				"requested-bind-addr": bindAddr,
			})

			_, port, err := net.SplitHostPort(listener.Addr().String())
			if err != nil {
				r.Reply(false, nil)
				continue
			}

			var res tcpipForwardResponse
			_, err = fmt.Sscanf(port, "%d", &res.BoundPort)
			if err != nil {
				r.Reply(false, nil)
				continue
			}

			forPort := req.BindPort
			if forPort == 0 {
				forPort = res.BoundPort
			}

			process := server.forwardTCPIP(logger, conn, listener, req.BindIP, forPort)

			forwardedTCPIPs <- forwardedTCPIP{
				bindAddr:  fmt.Sprintf("%s:%d", req.BindIP, req.BindPort),
				boundPort: res.BoundPort,
				process:   process,
			}

			r.Reply(true, ssh.Marshal(res))

		default:
			logger.Info("ignoring-request", lager.Data{"type": r.Type})
			r.Reply(false, nil)
		}
	}
}

func (server *registrarSSHServer) forwardTCPIP(
	logger lager.Logger,
	conn *ssh.ServerConn,
	listener net.Listener,
	forwardIP string,
	forwardPort uint32,
) ifrit.Process {
	return ifrit.Background(ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		go func() {
			<-signals

			listener.Close()
		}()

		close(ready)

		for {
			localConn, err := listener.Accept()
			if err != nil {
				logger.Error("failed-to-accept", err)
				break
			}

			go forwardLocalConn(logger, localConn, conn, forwardIP, forwardPort)
		}

		return nil
	}))
}

func forwardLocalConn(logger lager.Logger, localConn net.Conn, conn *ssh.ServerConn, forwardIP string, forwardPort uint32) {
	defer localConn.Close()

	var req forwardTCPIPChannelRequest
	req.ForwardIP = forwardIP
	req.ForwardPort = forwardPort

	host, port, err := net.SplitHostPort(localConn.RemoteAddr().String())
	if err != nil {
		logger.Error("failed-to-split-host-port", err)
		return
	}

	req.OriginIP = host
	_, err = fmt.Sscanf(port, "%d", &req.OriginPort)
	if err != nil {
		logger.Error("failed-to-parse-port", err)
		return
	}

	channel, reqs, err := conn.OpenChannel("forwarded-tcpip", ssh.Marshal(req))
	if err != nil {
		logger.Error("failed-to-open-channel", err)
		return
	}

	defer channel.Close()

	go func() {
		for r := range reqs {
			logger.Info("ignoring-request", lager.Data{
				"type": r.Type,
			})

			r.Reply(false, nil)
		}
	}()

	wg := new(sync.WaitGroup)

	pipe := func(to io.WriteCloser, from io.ReadCloser) {
		// if either end breaks, close both ends to ensure they're both unblocked,
		// otherwise io.Copy can block forever if e.g. reading after write end has
		// gone away
		defer to.Close()
		defer from.Close()
		defer wg.Done()

		io.Copy(to, from)
	}

	wg.Add(1)
	go pipe(localConn, channel)

	wg.Add(1)
	go pipe(channel, localConn)

	wg.Wait()
}
<commit_msg>don't log harmless error when shutting down<commit_after>package tsacmd

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	gclient "github.com/cloudfoundry-incubator/garden/client"
	gconn "github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/concourse/atc"
	"github.com/concourse/tsa"
	"github.com/felixge/tcpkeepalive"
	"github.com/pivotal-golang/clock"
	"github.com/pivotal-golang/lager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/rata"
	"golang.org/x/crypto/ssh"
)

func keepaliveDialerFactory(network string, address string) gconn.DialerFunc {
	return func(string, string) (net.Conn, error) {
		conn, err := net.DialTimeout(network, address, 5*time.Second)
		if err != nil {
			return nil, err
		}

		kac, err := tcpkeepalive.EnableKeepAlive(conn)
		if err != nil {
			println("failed to enable connection keepalive: " + err.Error())
		}

		err = kac.SetKeepAliveIdle(10 * time.Second)
		if err != nil {
			println("failed to set keepalive idle threshold: " + err.Error())
		}

		err = kac.SetKeepAliveCount(3)
		if err != nil {
			println("failed to set keepalive count: " + err.Error())
		}

		err = kac.SetKeepAliveInterval(5 * time.Second)
		if err != nil {
			println("failed to set keepalive interval: " + err.Error())
		}

		return conn, nil
	}
}

type registrarSSHServer struct {
	logger            lager.Logger
	atcEndpoint       *rata.RequestGenerator
	tokenGenerator    tsa.TokenGenerator
	heartbeatInterval time.Duration
	cprInterval       time.Duration
	forwardHost       string
	config            *ssh.ServerConfig
	httpClient        *http.Client
}

func (server *registrarSSHServer) Serve(listener net.Listener) {
	for {
		c, err := listener.Accept()
		if err != nil {
			if !strings.Contains(err.Error(), "use of closed network connection") {
				server.logger.Error("failed-to-accept", err)
			}

			return
		}

		logger := server.logger.Session("connection")

		conn, chans, reqs, err := ssh.NewServerConn(c, server.config)
		if err != nil {
			logger.Info("handshake-failed", lager.Data{"error": err.Error()})
			continue
		}

		go server.handleConn(logger, conn, chans, reqs)
	}
}

type forwardedTCPIP struct {
	bindAddr  string
	process   ifrit.Process
	boundPort uint32
}

func (server *registrarSSHServer) handleConn(logger lager.Logger, conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
	defer conn.Close()

	forwardedTCPIPs := make(chan forwardedTCPIP, 2)
	go server.handleForwardRequests(logger, conn, reqs, forwardedTCPIPs)

	var processes []ifrit.Process
	var process ifrit.Process

	// ensure processes get cleaned up
	defer func() {
		cleanupLog := logger.Session("cleanup")

		for _, p := range processes {
			cleanupLog.Debug("interrupting")

			p.Signal(os.Interrupt)
		}

		for _, p := range processes {
			err := <-p.Wait()
			if err != nil {
				cleanupLog.Error("process-exited-with-failure", err)
			} else {
				cleanupLog.Debug("process-exited-successfully")
			}
		}
	}()

	for newChannel := range chans {
		if newChannel.ChannelType() != "session" {
			logger.Info("rejecting-unknown-channel-type", lager.Data{
				"type": newChannel.ChannelType(),
			})

			newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := newChannel.Accept()
		if err != nil {
			logger.Error("failed-to-accept-channel", err)
			return
		}

		defer channel.Close()

		for req := range requests {
			logger.Info("channel-request", lager.Data{
				"type": req.Type,
			})

			if req.Type != "exec" {
				logger.Info("rejecting")
				req.Reply(false, nil)
				continue
			}

			var request execRequest
			err = ssh.Unmarshal(req.Payload, &request)
			if err != nil {
				logger.Error("malformed-exec-request", err)
				req.Reply(false, nil)
				return
			}

			workerRequest, err := parseRequest(request.Command)
			if err != nil {
				fmt.Fprintf(channel, "invalid command: %s", err)
				req.Reply(false, nil)
				continue
			}

			switch r := workerRequest.(type) {
			case registerWorkerRequest:
				logger := logger.Session("register-worker")

				req.Reply(true, nil)

				process, err = server.continuouslyRegisterWorkerDirectly(logger, channel)
				if err != nil {
					logger.Error("failed-to-register", err)
					return
				}

				processes = append(processes, process)

				err = conn.Wait()
				logger.Error("connection-closed", err)

			case forwardWorkerRequest:
				logger := logger.Session("forward-worker")

				forwards := map[string]forwardedTCPIP{}

				for i := 0; i < r.expectedForwards(); i++ {
					select {
					case forwarded := <-forwardedTCPIPs:
						logger.Info("forwarded-tcpip", lager.Data{
							"bound-port": forwarded.boundPort,
						})

						processes = append(processes, forwarded.process)

						forwards[forwarded.bindAddr] = forwarded

					case <-time.After(10 * time.Second): // todo better?
						logger.Info("never-forwarded-tcpip")
					}
				}

				switch len(forwards) {
				case 0:
					fmt.Fprintf(channel, "requested forwarding but no forwards given\n")
					return

				case 1:
					for _, gardenForward := range forwards {
						process, err = server.continuouslyRegisterForwardedWorker(
							logger,
							channel,
							gardenForward.boundPort,
							0,
						)
						if err != nil {
							logger.Error("failed-to-register", err)
							return
						}

						processes = append(processes, process)

						break
					}

				case 2:
					gardenForward, found := forwards[r.gardenAddr]
					if !found {
						fmt.Fprintf(channel, "garden address %s not found in forwards\n", r.gardenAddr)
						return
					}

					baggageclaimForward, found := forwards[r.baggageclaimAddr]
					if !found {
						fmt.Fprintf(channel, "baggageclaim address %s not found in forwards\n", r.gardenAddr)
						return
					}

					process, err = server.continuouslyRegisterForwardedWorker(
						logger,
						channel,
						gardenForward.boundPort,
						baggageclaimForward.boundPort,
					)
					if err != nil {
						logger.Error("failed-to-register", err)
						return
					}

					processes = append(processes, process)
				}

				err = conn.Wait()
				logger.Error("connection-closed", err)

			default:
				logger.Info("invalid-command", lager.Data{
					"command": request.Command,
				})

				req.Reply(false, nil)
			}
		}
	}
}

func (server *registrarSSHServer) continuouslyRegisterWorkerDirectly(
	logger lager.Logger,
	channel ssh.Channel,
) (ifrit.Process, error) {
	logger.Session("start")
	defer logger.Session("done")

	var worker atc.Worker
	err := json.NewDecoder(channel).Decode(&worker)
	if err != nil {
		return nil, err
	}

	return server.heartbeatWorker(logger, worker, channel), nil
}

func (server *registrarSSHServer) continuouslyRegisterForwardedWorker(
	logger lager.Logger,
	channel ssh.Channel,
	gardenPort uint32,
	baggageclaimPort uint32,
) (ifrit.Process, error) {
	logger.Session("start")
	defer logger.Session("done")

	var worker atc.Worker
	err := json.NewDecoder(channel).Decode(&worker)
	if err != nil {
		return nil, err
	}

	worker.GardenAddr = fmt.Sprintf("%s:%d", server.forwardHost, gardenPort)

	if baggageclaimPort != 0 {
		worker.BaggageclaimURL = fmt.Sprintf("http://%s:%d", server.forwardHost, baggageclaimPort)
	}

	return server.heartbeatWorker(logger, worker, channel), nil
}

func (server *registrarSSHServer) heartbeatWorker(logger lager.Logger, worker atc.Worker, channel ssh.Channel) ifrit.Process {
	return ifrit.Background(tsa.NewHeartbeater(
		logger,
		clock.NewClock(),
		server.heartbeatInterval,
		server.cprInterval,
		gclient.New(gconn.NewWithDialerAndLogger(keepaliveDialerFactory("tcp", worker.GardenAddr), logger.Session("garden-connection"))),
		server.atcEndpoint,
		server.tokenGenerator,
		worker,
		channel,
	))
}

func (server *registrarSSHServer) handleForwardRequests(
	logger lager.Logger,
	conn *ssh.ServerConn,
	reqs <-chan *ssh.Request,
	forwardedTCPIPs chan<- forwardedTCPIP,
) {
	var forwardedThings int

	for r := range reqs {
		switch r.Type {
		case "tcpip-forward":
			logger := logger.Session("tcpip-forward")

			forwardedThings++

			if forwardedThings > 2 {
				logger.Info("rejecting-extra-forward-request")
				r.Reply(false, nil)
				continue
			}

			var req tcpipForwardRequest
			err := ssh.Unmarshal(r.Payload, &req)
			if err != nil {
				logger.Error("malformed-tcpip-request", err)
				r.Reply(false, nil)
				continue
			}

			listener, err := net.Listen("tcp", "0.0.0.0:0")
			if err != nil {
				logger.Error("failed-to-listen", err)
				r.Reply(false, nil)
				continue
			}

			defer listener.Close()

			bindAddr := net.JoinHostPort(req.BindIP, fmt.Sprintf("%d", req.BindPort))

			logger.Info("forwarding-tcpip", lager.Data{
				"requested-bind-addr": bindAddr,
			})

			_, port, err := net.SplitHostPort(listener.Addr().String())
			if err != nil {
				r.Reply(false, nil)
				continue
			}

			var res tcpipForwardResponse
			_, err = fmt.Sscanf(port, "%d", &res.BoundPort)
			if err != nil {
				r.Reply(false, nil)
				continue
			}

			forPort := req.BindPort
			if forPort == 0 {
				forPort = res.BoundPort
			}

			process := server.forwardTCPIP(logger, conn, listener, req.BindIP, forPort)

			forwardedTCPIPs <- forwardedTCPIP{
				bindAddr:  fmt.Sprintf("%s:%d", req.BindIP, req.BindPort),
				boundPort: res.BoundPort,
				process:   process,
			}

			r.Reply(true, ssh.Marshal(res))

		default:
			logger.Info("ignoring-request", lager.Data{"type": r.Type})
			r.Reply(false, nil)
		}
	}
}

func (server *registrarSSHServer) forwardTCPIP(
	logger lager.Logger,
	conn *ssh.ServerConn,
	listener net.Listener,
	forwardIP string,
	forwardPort uint32,
) ifrit.Process {
	return ifrit.Background(ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error {
		go func() {
			<-signals

			listener.Close()
		}()

		close(ready)

		for {
			localConn, err := listener.Accept()
			if err != nil {
				logger.Error("failed-to-accept", err)
				break
			}

			go forwardLocalConn(logger, localConn, conn, forwardIP, forwardPort)
		}

		return nil
	}))
}

func forwardLocalConn(logger lager.Logger, localConn net.Conn, conn *ssh.ServerConn, forwardIP string, forwardPort uint32) {
	defer localConn.Close()

	var req forwardTCPIPChannelRequest
	req.ForwardIP = forwardIP
	req.ForwardPort = forwardPort

	host, port, err := net.SplitHostPort(localConn.RemoteAddr().String())
	if err != nil {
		logger.Error("failed-to-split-host-port", err)
		return
	}

	req.OriginIP = host
	_, err = fmt.Sscanf(port, "%d", &req.OriginPort)
	if err != nil {
		logger.Error("failed-to-parse-port", err)
		return
	}

	channel, reqs, err := conn.OpenChannel("forwarded-tcpip", ssh.Marshal(req))
	if err != nil {
		logger.Error("failed-to-open-channel", err)
		return
	}

	defer channel.Close()

	go func() {
		for r := range reqs {
			logger.Info("ignoring-request", lager.Data{
				"type": r.Type,
			})

			r.Reply(false, nil)
		}
	}()

	wg := new(sync.WaitGroup)

	pipe := func(to io.WriteCloser, from io.ReadCloser) {
		// if either end breaks, close both ends to ensure they're both unblocked,
		// otherwise io.Copy can block forever if e.g. reading after write end has
		// gone away
		defer to.Close()
		defer from.Close()
		defer wg.Done()

		io.Copy(to, from)
	}

	wg.Add(1)
	go pipe(localConn, channel)

	wg.Add(1)
	go pipe(channel, localConn)

	wg.Wait()
}
<|endoftext|>


<commit_before>package tutum

import "encoding/json"

type SListResponse struct {
	Objects []Service `json: "objects"`
}

type Service struct {
	Autodestroy            string       `json:"autodestroy"`
	Autoredeploy           bool         `json:"autoredeploy"`
	Autorestart            string       `json:"autorestart"`
	Containers             []string     `json:"containers"`
	Container_ports        []SCPInfo    `json:"container_ports"`
	Container_size         string       `json:"container_size"`
	Current_num_containers int          `json:"current_num_containers"`
	Deployed_datetime      string       `json:"deployed_datetime"`
	Destroyed_datetime     string       `json:"destroyed_datetime"`
	Entrypoint             string       `json:"entrypoint"`
	Exit_code              int          `json:"exit_code"`
	Exit_code_message      string       `json:"exit_code_message"`
	Image_name             string       `json:"image_name"`
	Image_tag              string       `json:"image_tag"`
	Linked_to_service      []LinkToInfo `json:"linked_to_service"`
	Name                   string       `json:"name"`
	Public_dns             string       `json:"public_dns"`
	Resource_uri           string       `json:"resource_uri"`
	Run_command            string       `json:"run_command"`
	Started_datetime       string       `json:"started_datetime"`
	State                  string       `json:"state"`
	Stack                  string       `json:"stack"`
	Stopped_datetime       string       `json:"stopped_datetime"`
	Target_num_containers  int          `json:"target_num_containers"`
	Unique_name            string       `json:"unique_name"`
	Uuid                   string       `json:"uuid"`
}

type SCPInfo struct {
	Container  string `json:"container"`
	Inner_port int    `json:"inner_port"`
	Outer_port int    `json:"outer_port"`
	Protocol   string `json:"protocol"`
}

type LinkToInfo struct {
	From_service string `json:"from_service"`
	Name         string `json:"name"`
	To_service   string `json:"to_service"`
}

func ListServices() ([]Service, error) {
	url := "service/"
	request := "GET"
	body := []byte(`{}`)
	var response SListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}
	return response.Objects, nil

}

func GetService(uuid string) (Service, error) {

	url := "service/" + uuid + "/"
	request := "GET"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func CreateService(newService []byte) (Service, error) {

	url := "service/"
	request := "POST"

	var response Service

	data, err := TutumCall(url, request, newService)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func GetServiceLogs(uuid string) (string, error) {

	url := "service/" + uuid + "/logs/"
	request := "GET"
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		return "", err
	}

	s := string(data)

	return s, nil

}

func UpdateService(uuid string, update []byte) (Service, error) {

	url := "service/" + uuid + "/"
	request := "PATCH"
	var response Service

	data, err := TutumCall(url, request, update)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StartService(uuid string) (Service, error) {

	url := "service/" + uuid + "/start/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func StopService(uuid string) (Service, error) {

	url := "service/" + uuid + "/stop/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

func RedeployService(uuid string) (Service, error) {

	url := "service/" + uuid + "/redeploy/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

func TerminateService(uuid string) (Service, error) {
	url := "service/" + uuid + "/"
	request := "DELETE"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}
<commit_msg>comment on Services endpoints<commit_after>package tutum

import "encoding/json"

type SListResponse struct {
	Objects []Service `json: "objects"`
}

type Service struct {
	Autodestroy            string       `json:"autodestroy"`
	Autoredeploy           bool         `json:"autoredeploy"`
	Autorestart            string       `json:"autorestart"`
	Containers             []string     `json:"containers"`
	Container_ports        []SCPInfo    `json:"container_ports"`
	Container_size         string       `json:"container_size"`
	Current_num_containers int          `json:"current_num_containers"`
	Deployed_datetime      string       `json:"deployed_datetime"`
	Destroyed_datetime     string       `json:"destroyed_datetime"`
	Entrypoint             string       `json:"entrypoint"`
	Exit_code              int          `json:"exit_code"`
	Exit_code_message      string       `json:"exit_code_message"`
	Image_name             string       `json:"image_name"`
	Image_tag              string       `json:"image_tag"`
	Linked_to_service      []LinkToInfo `json:"linked_to_service"`
	Name                   string       `json:"name"`
	Public_dns             string       `json:"public_dns"`
	Resource_uri           string       `json:"resource_uri"`
	Run_command            string       `json:"run_command"`
	Started_datetime       string       `json:"started_datetime"`
	State                  string       `json:"state"`
	Stack                  string       `json:"stack"`
	Stopped_datetime       string       `json:"stopped_datetime"`
	Target_num_containers  int          `json:"target_num_containers"`
	Unique_name            string       `json:"unique_name"`
	Uuid                   string       `json:"uuid"`
}

type SCPInfo struct {
	Container  string `json:"container"`
	Inner_port int    `json:"inner_port"`
	Outer_port int    `json:"outer_port"`
	Protocol   string `json:"protocol"`
}

//Basic information from linked services
type LinkToInfo struct {
	From_service string `json:"from_service"`
	Name         string `json:"name"`
	To_service   string `json:"to_service"`
}

/*
func ListServices
Returns : Array of Service objects
*/
func ListServices() ([]Service, error) {
	url := "service/"
	request := "GET"
	body := []byte(`{}`)
	var response SListResponse

	data, err := TutumCall(url, request, body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(data, &response)
	if err != nil {
		return nil, err
	}
	return response.Objects, nil

}

/*
func GetService
Argument : uuid
Returns : Service JSON object
*/
func GetService(uuid string) (Service, error) {

	url := "service/" + uuid + "/"
	request := "GET"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

/*
func CreateService
Argument : newService JSON object
Returns : Service JSON object
*/
func CreateService(newService []byte) (Service, error) {

	url := "service/"
	request := "POST"

	var response Service

	data, err := TutumCall(url, request, newService)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

/*
func GetServiceLogs
Argument : uuid
Returns : A string containing the logs of the service
*/
func GetServiceLogs(uuid string) (string, error) {

	url := "service/" + uuid + "/logs/"
	request := "GET"
	body := []byte(`{}`)

	data, err := TutumCall(url, request, body)
	if err != nil {
		return "", err
	}

	s := string(data)

	return s, nil

}

/*
func UpdateService
Argument : uuid, updatedService JSON object
Returns : Service JSON object
*/
func UpdateService(uuid string, updatedService []byte) (Service, error) {

	url := "service/" + uuid + "/"
	request := "PATCH"
	var response Service

	data, err := TutumCall(url, request, updatedService)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

/*
func StartService
Argument : uuid
Returns : Service JSON object
*/
func StartService(uuid string) (Service, error) {

	url := "service/" + uuid + "/start/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

/*
func StopService
Argument : uuid
Returns : Service JSON object
*/
func StopService(uuid string) (Service, error) {

	url := "service/" + uuid + "/stop/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil
}

/*
func RedeployService
Argument : uuid
Returns : Service JSON object
*/
func RedeployService(uuid string) (Service, error) {

	url := "service/" + uuid + "/redeploy/"
	request := "POST"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}

/*
func TerminateService
Argument : uuid
Returns : Service JSON object
*/
func TerminateService(uuid string) (Service, error) {
	url := "service/" + uuid + "/"
	request := "DELETE"
	body := []byte(`{}`)
	var response Service

	data, err := TutumCall(url, request, body)
	if err != nil {
		panic(err)
	}

	err = json.Unmarshal(data, &response)
	if err != nil {
		panic(err)
	}

	return response, nil

}
<|endoftext|>


<commit_before>package pflaghelpers

import (
	"fmt"
	"net"
	"os"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

func EnsureRequired(cmd *cobra.Command) {
	problematicFlags := []string{}

	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
		if IsFlagRequired(flag) && !flag.Changed {
			problematicFlags = append(problematicFlags, flag.Name)
		}
	})

	if len(problematicFlags) > 0 {
		for i, flagName := range problematicFlags {
			problematicFlags[i] = fmt.Sprintf("`%s`", flagName)
		}

		var err error
		if len(problematicFlags) == 1 {
			_, err = fmt.Fprintf(cmd.Out(), "Usage error: flag %s is required", problematicFlags[0])
		} else {
			_, err = fmt.Fprintf(cmd.Out(),
				"Usage error: flags %s are required", strings.Join(problematicFlags, ", "))
		}
		if err != nil {
			panic(err)
		}

		os.Exit(1)
	}
}

func Bind(cmd *cobra.Command) {
	cobra.OnInitialize(func() {
		EnsureRequired(cmd)
	})
}

func IsFlagRequired(flag *pflag.Flag) bool {
	return strings.HasSuffix(flag.Usage, "(required)")
}

// A series of MustGet* functions, which now make some sense.

func MustGetString(fs *pflag.FlagSet, name string) string {
	if rv, err := fs.GetString(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetBool(fs *pflag.FlagSet, name string) bool {
	if rv, err := fs.GetBool(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetDuration(fs *pflag.FlagSet, name string) time.Duration {
	if rv, err := fs.GetDuration(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetFloat32(fs *pflag.FlagSet, name string) float32 {
	if rv, err := fs.GetFloat32(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetFloat64(fs *pflag.FlagSet, name string) float64 {
	if rv, err := fs.GetFloat64(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetIP(fs *pflag.FlagSet, name string) net.IP {
	if rv, err := fs.GetIP(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetIPv4Mask(fs *pflag.FlagSet, name string) net.IPMask {
	if rv, err := fs.GetIPv4Mask(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt(fs *pflag.FlagSet, name string) int {
	if rv, err := fs.GetInt(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt32(fs *pflag.FlagSet, name string) int32 {
	if rv, err := fs.GetInt32(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt64(fs *pflag.FlagSet, name string) int64 {
	if rv, err := fs.GetInt64(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt8(fs *pflag.FlagSet, name string) int8 {
	if rv, err := fs.GetInt8(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}
<commit_msg>[bugfix] Making it actually work<commit_after>package pflaghelpers

import (
	"fmt"
	"net"
	"os"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

func EnsureRequired(rootCmd *cobra.Command) {
	cmd, _, err := rootCmd.Find(os.Args[1:])
	if err != nil {
		panic(err)
	}

	problematicFlags := []string{}

	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
		if IsFlagRequired(flag) && !flag.Changed {
			problematicFlags = append(problematicFlags, flag.Name)
		}
	})

	if len(problematicFlags) > 0 {
		for i, flagName := range problematicFlags {
			problematicFlags[i] = fmt.Sprintf("`%s`", flagName)
		}

		var err error
		if len(problematicFlags) == 1 {
			_, err = fmt.Fprintf(cmd.Out(),
				"Usage error: flag %s is required\n", problematicFlags[0])
		} else {
			_, err = fmt.Fprintf(cmd.Out(),
				"Usage error: flags %s are required\n", strings.Join(problematicFlags, ", "))
		}
		if err != nil {
			panic(err)
		}

		os.Exit(1)
	}
}

func Bind(cmd *cobra.Command) {
	cobra.OnInitialize(func() {
		EnsureRequired(cmd)
	})
}

func IsFlagRequired(flag *pflag.Flag) bool {
	return strings.HasSuffix(flag.Usage, "(required)")
}

// A series of MustGet* functions, which now make some sense.

func MustGetString(fs *pflag.FlagSet, name string) string {
	if rv, err := fs.GetString(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetBool(fs *pflag.FlagSet, name string) bool {
	if rv, err := fs.GetBool(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetDuration(fs *pflag.FlagSet, name string) time.Duration {
	if rv, err := fs.GetDuration(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetFloat32(fs *pflag.FlagSet, name string) float32 {
	if rv, err := fs.GetFloat32(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetFloat64(fs *pflag.FlagSet, name string) float64 {
	if rv, err := fs.GetFloat64(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetIP(fs *pflag.FlagSet, name string) net.IP {
	if rv, err := fs.GetIP(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetIPv4Mask(fs *pflag.FlagSet, name string) net.IPMask {
	if rv, err := fs.GetIPv4Mask(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt(fs *pflag.FlagSet, name string) int {
	if rv, err := fs.GetInt(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt32(fs *pflag.FlagSet, name string) int32 {
	if rv, err := fs.GetInt32(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt64(fs *pflag.FlagSet, name string) int64 {
	if rv, err := fs.GetInt64(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}

func MustGetInt8(fs *pflag.FlagSet, name string) int8 {
	if rv, err := fs.GetInt8(name); err != nil {
		panic(err)
	} else {
		return rv
	}
}
<|endoftext|>


<commit_before>// Config client. Also talks to coordinator for watches and versions.
//Typical use case is to get a dynamic bucket and use it to read configuration.
//The dynamic bucket is auto-updated.
//
//Sample usage:
//
// Create client instance with 50 as the size of LRU cache
//      client := cfgsvc.NewConfigServiceClient("http://localhost:8080", 50)
//
//
// get key and check its value
//  if flag := client.GetDynamicBucket("mybucket").GetBool("myflag"); flag {
//      do stuff
//  }
//
//
// If you do not wish to remember the bucket name in runtime, you can
// use the bucket struct directly, it will be auto-updated by client.
//  bucket := client.GetDynamicBucket("mybucket");
//
//
//  endpoint := bucket.GetString("endpoint");
package cfgsvc

import (
	"encoding/json"
	"errors"
	"github.com/hashicorp/golang-lru"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
	"strings"
	"fmt"
)

// ConfigServiceClient provides API to interact with config service to
// read and watch for configuration changes
type ConfigServiceClient struct {
	httpClient         *HttpClient
	instanceMetadata   *InstanceMetadata
	dynamicBucketCache *lru.Cache
	staticBucketCache  *lru.Cache
	mutex              sync.Mutex
}

type InstanceMetadata struct {
	App           string `json:"app"`
	Zone          string `json:"zone"`
	InstanceGroup string `json:"instance_group"`
	Hostname      string `json:"hostname"`
	PrimaryIP     string `json:"primary_ip"`
	Id 			  string `json:"id"`
}

type CfgSvcApiOverrides struct {
	Endpoint      string
}

const InstanceMetadataFile = "/etc/default/megh/instance_metadata.json"
const DefaultZone = "in-mumbai-preprod"
const CfgSvcApiOverridesFile = "/etc/default/cfg-api"
const CloudCliEndpoint = "http://10.47.255.6:8080"

var instVpcToCfgSvc = map[string]string{
	"fk-helios": "http://10.47.0.179",
	"fk-prod-ch": "http://10.47.0.101",
}

var instZoneToCfgsvc = map[string]string{
	"in-mumbai-prod":    "http://10.85.50.3",
	"in-mumbai-preprod":    "http://10.85.42.8",
	"in-mumbai-preprod-b":    "http://10.85.42.8",
	"in-mumbai-gateway": "http://10.85.50.3",
	"in-chennai-1":      "http://10.47.0.101",
}

const LATEST_VERSION = -1

// NewConfigServiceClient creates a new instance of config service client and returns its pointer.
func NewConfigServiceClient(cacheSize int) (*ConfigServiceClient, error) {

	client := &ConfigServiceClient{}

	// get instance metadata
	meta := readInstMetadata()

	netHttpClient := &http.Client{Timeout: time.Duration(60 * time.Second)}

	// get url
	url := ""
	ok := false

	overrides, err := getOverrides(CfgSvcApiOverridesFile)
	if err == nil && len(overrides.Endpoint) > 0 {
		log.Println("Overriding endpoint")
		url = overrides.Endpoint
	} else {
		if !(meta.Zone == "in-mumbai-preprod" || meta.Zone == "in-mumbai-preprod-b" || meta.Zone == "#NULL#") {
			// get the vpc info
			log.Println("Attempting to get vpc name")
			vpcSubnetName, _ := getVpcSubnetName(netHttpClient, meta)
			if vpcSubnetName != "" {
				if url, ok = instVpcToCfgSvc[vpcSubnetName]; ok {
					log.Println("Vpc found: " + vpcSubnetName)
				}
			}
		}

		if url == "" {
			url, ok = instZoneToCfgsvc[meta.Zone]
			if !ok {
				log.Println("Instance zone not found, defaulting to " + DefaultZone)
				url = instZoneToCfgsvc[DefaultZone]
			}
		}
	}

	log.Println("Using endpoint: " + url)

	// create client
	httpClient, err := NewHttpClient(netHttpClient, url, meta)
	if err != nil {
		return nil, err
	}

	// dynamic cache
	client.dynamicBucketCache, err = lru.NewWithEvict(cacheSize, func(bucketName interface{}, value interface{}) {
		dynamicBucket := value.(*DynamicBucket)
		log.Println("Removing bucket from local cache: ", bucketName)
		dynamicBucket.Disconnected(errors.New("Bucket evicted from cache, please fetch it again"))
		dynamicBucket.shutdown()
	})

	// static cache
	client.staticBucketCache, err = lru.NewWithEvict(cacheSize, func(bucketName interface{}, value interface{}) {
		log.Println("Removing bucket from local cache: ", bucketName)
	})

	if err != nil {
		return nil, err
	} else {
		client.httpClient = httpClient
		return client, nil
	}
}

//Get a dynamic bucket which is auto-updated by a setting watch.
//Keeps a local reference of the static bucket for updating and caching.
func (this *ConfigServiceClient) GetDynamicBucket(name string) (*DynamicBucket, error) {
	if val, ok := this.dynamicBucketCache.Get(name); ok {
		dynamicBucket := val.(*DynamicBucket)
		return dynamicBucket, nil
	} else {
		//Use mutex to ensure the bucket will be fetched only once!
		this.mutex.Lock()
		defer this.mutex.Unlock()

		//Check cache again to see if the another thread has
		//already initialized the bucket
		if val, ok := this.dynamicBucketCache.Get(name); ok {
			dynamicBucket := val.(*DynamicBucket)
			return dynamicBucket, nil
		} else {
			// Initialize the bucket if this the first time
			return this.initDynamicBucket(name)
		}
	}
}

//Initialises a dynamic bucket given the bucket name
func (this *ConfigServiceClient) initDynamicBucket(name string) (*DynamicBucket, error) {
	log.Println("Initializing Config bucket: " + name)

	dynamicBucket := &DynamicBucket{httpClient: this.httpClient}

	err := ValidateBucketName(name)
	if err != nil {
		return nil, err
	}

	err = dynamicBucket.init(name)

	if err != nil {
		log.Println("Error fetching bucket: ", err)
		return nil, err
	} else {
		this.dynamicBucketCache.Add(name, dynamicBucket)
		go this.httpClient.WatchBucket(name, this.dynamicBucketCache, dynamicBucket)
		return dynamicBucket, nil
	}
}

//Get a bucket with given version. It does not set any watches.
func (this *ConfigServiceClient) GetBucket(name string, version int) (*Bucket, error) {
	if val, ok := this.staticBucketCache.Get(cacheKey(name, version)); ok {
		bucket := val.(*Bucket)
		return bucket, nil
	} else {
		//Use mutex to ensure the bucket will be fetched only once!
		this.mutex.Lock()
		defer this.mutex.Unlock()

		//Check cache again to see if the another thread has
		//already initialized the bucket
		if val, ok := this.staticBucketCache.Get(cacheKey(name, version)); ok {
			bucket := val.(*Bucket)
			return bucket, nil
		} else {
			// Initialize the bucket if this the first time
			return this.initStaticBucket(name, version)
		}
	}
}

//Initialises a bucket with given version. It does not set any watches.
func (this *ConfigServiceClient) initStaticBucket(name string, version int) (*Bucket, error) {
	log.Println("Initializing Config bucket: " + name)

	err := ValidateBucketName(name)
	if err != nil {
		return nil, err
	}
	bucket, err := this.httpClient.GetBucket(name, version)
	if err != nil {
		log.Println("Error fetching bucket: ", err)
		return nil, err
	} else {
		this.staticBucketCache.Add(cacheKey(name, version), bucket)
		return bucket, nil
	}
}

func cacheKey(name string, version int) string {
	return name + ":" + strconv.Itoa(version)
}

func getProperties(fileName string) (map[string]string, error) {
	bytes, err := ioutil.ReadFile(fileName)

	if err != nil {
		log.Println("Failed to read file: " + fileName + ". Ignoring overrides")
		return nil, err
	}

	lines := strings.Split(string(bytes[:]), "\n")

	properties := map[string]string{}
	for _, line := range lines {
		if len(line) > 0 {
			kv := strings.Split(line, "=")
			if len(kv) != 2 {
				return nil, fmt.Errorf("format error in line : \"%s\"", line)
			}

			key := strings.TrimSpace(kv[0])
			value := strings.TrimSpace(kv[1])

			if len(key) == 0 || len(value) == 0 {
				return nil, fmt.Errorf("format error in line : \"%s\"", line)
			}

			properties[key] = value
		}
	}

	return properties, nil
}

func getOverrides(fileName string) (CfgSvcApiOverrides, error) {
	overrides := CfgSvcApiOverrides{Endpoint : ""}

	properties, err := getProperties(fileName)

	if err != nil {
		return overrides, err
	}

	host, ok := properties["host"]
	if !ok {
		return overrides, fmt.Errorf("empty overrides")
	}

	port_str, ok := properties["port"]

	if !ok {
		port_str = "80"
	} else {
		_, err = strconv.Atoi(port_str)
		if err != nil {
			return overrides, fmt.Errorf("port is not a number")
		}
	}

	overrides.Endpoint = "http://" + host + ":" + port_str

	return overrides, nil
}

func doRequest(httpClient *http.Client, url string) ([]byte, error) {
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")

	resp, err := httpClient.Do(req)

	if err != nil {
		log.Println("Failed to do request. error: " + err.Error())
		return nil, err
	}

	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func getVpcSubnetName(httpClient *http.Client, meta *InstanceMetadata) (string, error) {

	url := CloudCliEndpoint + "/compute/v2/apps/" + meta.App + "/zones/" + meta.Zone + "/instances/" + meta.Id

	resp_body, err := doRequest(httpClient, url)

	if err != nil {
		return "", err
	}

	var jsonVal map[string]interface{}

	if err := json.Unmarshal(resp_body, &jsonVal); err != nil {
        log.Println("Error parsing cloud cli rsponse as json. error: " + err.Error())
        return "", err
    }

    return strings.ToLower(jsonVal["vpc_subnet_name"].(string)), nil
}

func readInstMetadata() *InstanceMetadata {

	// create instance
	meta := &InstanceMetadata{}

	// read from json
	jsn, err := os.Open(InstanceMetadataFile)
	if err != nil {
		log.Println("Error opening " + InstanceMetadataFile + ": " + err.Error())
	}

	// parse json
	jsonParser := json.NewDecoder(jsn)
	if err = jsonParser.Decode(&meta); err != nil {
		log.Println("Error parsing instance metadata: " + err.Error())
	}

	// get hostname
	if meta.Hostname, err = os.Hostname(); err != nil {
		log.Println("Error getting hostname, using from metadata (" + meta.Hostname + "): " + err.Error())
	}

	// get ipv4
	if meta.PrimaryIP == "" {
		interfaces, _ := net.Interfaces()
		for _, inter := range interfaces {
			if addrs, err := inter.Addrs(); err == nil {
				for _, addr := range addrs {
					switch ip := addr.(type) {
					case *net.IPNet:
						if ip.IP.DefaultMask() != nil && !ip.IP.IsLoopback() {
							meta.PrimaryIP = ip.IP.To4().String()
						}
					}
				}
			}
		}
	}

	// defaults
	if meta.Zone == "" {
		meta.InstanceGroup = "#NULL#"
	}
	if meta.App == "" {
		meta.App = "#NULL#"
	}
	if meta.InstanceGroup == "" {
		meta.InstanceGroup = "#NULL#"
	}
	return meta
}
<commit_msg>Updating vpc name<commit_after>// Config client. Also talks to coordinator for watches and versions.
//Typical use case is to get a dynamic bucket and use it to read configuration.
//The dynamic bucket is auto-updated.
//
//Sample usage:
//
// Create client instance with 50 as the size of LRU cache
//      client := cfgsvc.NewConfigServiceClient("http://localhost:8080", 50)
//
//
// get key and check its value
//  if flag := client.GetDynamicBucket("mybucket").GetBool("myflag"); flag {
//      do stuff
//  }
//
//
// If you do not wish to remember the bucket name in runtime, you can
// use the bucket struct directly, it will be auto-updated by client.
//  bucket := client.GetDynamicBucket("mybucket");
//
//
//  endpoint := bucket.GetString("endpoint");
package cfgsvc

import (
	"encoding/json"
	"errors"
	"github.com/hashicorp/golang-lru"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"
	"strings"
	"fmt"
)

// ConfigServiceClient provides API to interact with config service to
// read and watch for configuration changes
type ConfigServiceClient struct {
	httpClient         *HttpClient
	instanceMetadata   *InstanceMetadata
	dynamicBucketCache *lru.Cache
	staticBucketCache  *lru.Cache
	mutex              sync.Mutex
}

type InstanceMetadata struct {
	App           string `json:"app"`
	Zone          string `json:"zone"`
	InstanceGroup string `json:"instance_group"`
	Hostname      string `json:"hostname"`
	PrimaryIP     string `json:"primary_ip"`
	Id 			  string `json:"id"`
}

type CfgSvcApiOverrides struct {
	Endpoint      string
}

const InstanceMetadataFile = "/etc/default/megh/instance_metadata.json"
const DefaultZone = "in-mumbai-preprod"
const CfgSvcApiOverridesFile = "/etc/default/cfg-api"
const CloudCliEndpoint = "http://10.47.255.6:8080"

var instVpcToCfgSvc = map[string]string{
	"fk-helios-ch": "http://10.47.0.179",
	"fk-prod-ch": "http://10.47.0.101",
}

var instZoneToCfgsvc = map[string]string{
	"in-mumbai-prod":    "http://10.85.50.3",
	"in-mumbai-preprod":    "http://10.85.42.8",
	"in-mumbai-preprod-b":    "http://10.85.42.8",
	"in-mumbai-gateway": "http://10.85.50.3",
	"in-chennai-1":      "http://10.47.0.101",
}

const LATEST_VERSION = -1

// NewConfigServiceClient creates a new instance of config service client and returns its pointer.
func NewConfigServiceClient(cacheSize int) (*ConfigServiceClient, error) {

	client := &ConfigServiceClient{}

	// get instance metadata
	meta := readInstMetadata()

	netHttpClient := &http.Client{Timeout: time.Duration(60 * time.Second)}

	// get url
	url := ""
	ok := false

	overrides, err := getOverrides(CfgSvcApiOverridesFile)
	if err == nil && len(overrides.Endpoint) > 0 {
		log.Println("Overriding endpoint")
		url = overrides.Endpoint
	} else {
		if !(meta.Zone == "in-mumbai-preprod" || meta.Zone == "in-mumbai-preprod-b" || meta.Zone == "#NULL#") {
			// get the vpc info
			log.Println("Attempting to get vpc name")
			vpcSubnetName, _ := getVpcSubnetName(netHttpClient, meta)
			if vpcSubnetName != "" {
				if url, ok = instVpcToCfgSvc[vpcSubnetName]; ok {
					log.Println("Vpc found: " + vpcSubnetName)
				}
			}
		}

		if url == "" {
			url, ok = instZoneToCfgsvc[meta.Zone]
			if !ok {
				log.Println("Instance zone not found, defaulting to " + DefaultZone)
				url = instZoneToCfgsvc[DefaultZone]
			}
		}
	}

	log.Println("Using endpoint: " + url)

	// create client
	httpClient, err := NewHttpClient(netHttpClient, url, meta)
	if err != nil {
		return nil, err
	}

	// dynamic cache
	client.dynamicBucketCache, err = lru.NewWithEvict(cacheSize, func(bucketName interface{}, value interface{}) {
		dynamicBucket := value.(*DynamicBucket)
		log.Println("Removing bucket from local cache: ", bucketName)
		dynamicBucket.Disconnected(errors.New("Bucket evicted from cache, please fetch it again"))
		dynamicBucket.shutdown()
	})

	// static cache
	client.staticBucketCache, err = lru.NewWithEvict(cacheSize, func(bucketName interface{}, value interface{}) {
		log.Println("Removing bucket from local cache: ", bucketName)
	})

	if err != nil {
		return nil, err
	} else {
		client.httpClient = httpClient
		return client, nil
	}
}

//Get a dynamic bucket which is auto-updated by a setting watch.
//Keeps a local reference of the static bucket for updating and caching.
func (this *ConfigServiceClient) GetDynamicBucket(name string) (*DynamicBucket, error) {
	if val, ok := this.dynamicBucketCache.Get(name); ok {
		dynamicBucket := val.(*DynamicBucket)
		return dynamicBucket, nil
	} else {
		//Use mutex to ensure the bucket will be fetched only once!
		this.mutex.Lock()
		defer this.mutex.Unlock()

		//Check cache again to see if the another thread has
		//already initialized the bucket
		if val, ok := this.dynamicBucketCache.Get(name); ok {
			dynamicBucket := val.(*DynamicBucket)
			return dynamicBucket, nil
		} else {
			// Initialize the bucket if this the first time
			return this.initDynamicBucket(name)
		}
	}
}

//Initialises a dynamic bucket given the bucket name
func (this *ConfigServiceClient) initDynamicBucket(name string) (*DynamicBucket, error) {
	log.Println("Initializing Config bucket: " + name)

	dynamicBucket := &DynamicBucket{httpClient: this.httpClient}

	err := ValidateBucketName(name)
	if err != nil {
		return nil, err
	}

	err = dynamicBucket.init(name)

	if err != nil {
		log.Println("Error fetching bucket: ", err)
		return nil, err
	} else {
		this.dynamicBucketCache.Add(name, dynamicBucket)
		go this.httpClient.WatchBucket(name, this.dynamicBucketCache, dynamicBucket)
		return dynamicBucket, nil
	}
}

//Get a bucket with given version. It does not set any watches.
func (this *ConfigServiceClient) GetBucket(name string, version int) (*Bucket, error) {
	if val, ok := this.staticBucketCache.Get(cacheKey(name, version)); ok {
		bucket := val.(*Bucket)
		return bucket, nil
	} else {
		//Use mutex to ensure the bucket will be fetched only once!
		this.mutex.Lock()
		defer this.mutex.Unlock()

		//Check cache again to see if the another thread has
		//already initialized the bucket
		if val, ok := this.staticBucketCache.Get(cacheKey(name, version)); ok {
			bucket := val.(*Bucket)
			return bucket, nil
		} else {
			// Initialize the bucket if this the first time
			return this.initStaticBucket(name, version)
		}
	}
}

//Initialises a bucket with given version. It does not set any watches.
func (this *ConfigServiceClient) initStaticBucket(name string, version int) (*Bucket, error) {
	log.Println("Initializing Config bucket: " + name)

	err := ValidateBucketName(name)
	if err != nil {
		return nil, err
	}
	bucket, err := this.httpClient.GetBucket(name, version)
	if err != nil {
		log.Println("Error fetching bucket: ", err)
		return nil, err
	} else {
		this.staticBucketCache.Add(cacheKey(name, version), bucket)
		return bucket, nil
	}
}

func cacheKey(name string, version int) string {
	return name + ":" + strconv.Itoa(version)
}

func getProperties(fileName string) (map[string]string, error) {
	bytes, err := ioutil.ReadFile(fileName)

	if err != nil {
		log.Println("Failed to read file: " + fileName + ". Ignoring overrides")
		return nil, err
	}

	lines := strings.Split(string(bytes[:]), "\n")

	properties := map[string]string{}
	for _, line := range lines {
		if len(line) > 0 {
			kv := strings.Split(line, "=")
			if len(kv) != 2 {
				return nil, fmt.Errorf("format error in line : \"%s\"", line)
			}

			key := strings.TrimSpace(kv[0])
			value := strings.TrimSpace(kv[1])

			if len(key) == 0 || len(value) == 0 {
				return nil, fmt.Errorf("format error in line : \"%s\"", line)
			}

			properties[key] = value
		}
	}

	return properties, nil
}

func getOverrides(fileName string) (CfgSvcApiOverrides, error) {
	overrides := CfgSvcApiOverrides{Endpoint : ""}

	properties, err := getProperties(fileName)

	if err != nil {
		return overrides, err
	}

	host, ok := properties["host"]
	if !ok {
		return overrides, fmt.Errorf("empty overrides")
	}

	port_str, ok := properties["port"]

	if !ok {
		port_str = "80"
	} else {
		_, err = strconv.Atoi(port_str)
		if err != nil {
			return overrides, fmt.Errorf("port is not a number")
		}
	}

	overrides.Endpoint = "http://" + host + ":" + port_str

	return overrides, nil
}

func doRequest(httpClient *http.Client, url string) ([]byte, error) {
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")

	resp, err := httpClient.Do(req)

	if err != nil {
		log.Println("Failed to do request. error: " + err.Error())
		return nil, err
	}

	defer resp.Body.Close()

	return ioutil.ReadAll(resp.Body)
}

func getVpcSubnetName(httpClient *http.Client, meta *InstanceMetadata) (string, error) {

	url := CloudCliEndpoint + "/compute/v2/apps/" + meta.App + "/zones/" + meta.Zone + "/instances/" + meta.Id

	resp_body, err := doRequest(httpClient, url)

	if err != nil {
		return "", err
	}

	var jsonVal map[string]interface{}

	if err := json.Unmarshal(resp_body, &jsonVal); err != nil {
        log.Println("Error parsing cloud cli rsponse as json. error: " + err.Error())
        return "", err
    }

    return strings.ToLower(jsonVal["vpc_subnet_name"].(string)), nil
}

func readInstMetadata() *InstanceMetadata {

	// create instance
	meta := &InstanceMetadata{}

	// read from json
	jsn, err := os.Open(InstanceMetadataFile)
	if err != nil {
		log.Println("Error opening " + InstanceMetadataFile + ": " + err.Error())
	}

	// parse json
	jsonParser := json.NewDecoder(jsn)
	if err = jsonParser.Decode(&meta); err != nil {
		log.Println("Error parsing instance metadata: " + err.Error())
	}

	// get hostname
	if meta.Hostname, err = os.Hostname(); err != nil {
		log.Println("Error getting hostname, using from metadata (" + meta.Hostname + "): " + err.Error())
	}

	// get ipv4
	if meta.PrimaryIP == "" {
		interfaces, _ := net.Interfaces()
		for _, inter := range interfaces {
			if addrs, err := inter.Addrs(); err == nil {
				for _, addr := range addrs {
					switch ip := addr.(type) {
					case *net.IPNet:
						if ip.IP.DefaultMask() != nil && !ip.IP.IsLoopback() {
							meta.PrimaryIP = ip.IP.To4().String()
						}
					}
				}
			}
		}
	}

	// defaults
	if meta.Zone == "" {
		meta.InstanceGroup = "#NULL#"
	}
	if meta.App == "" {
		meta.App = "#NULL#"
	}
	if meta.InstanceGroup == "" {
		meta.InstanceGroup = "#NULL#"
	}
	return meta
}
<|endoftext|>