<commit_before>// Copyright 2013-2014 go-diameter authors.  All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package diam

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"time"

	"github.com/fiorix/go-diameter/diam/datatypes"
	"github.com/fiorix/go-diameter/diam/dict"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// Diameter message.
type Message struct {
	Header *Header
	AVP    []*AVP

	Dictionary *dict.Parser // Used to encode and decode AVPs.
}

// ReadMessage returns a Message. It uses the dictionary to parse the
// binary stream from the reader.
func ReadMessage(reader io.Reader, dictionary *dict.Parser) (*Message, error) {
	var err error
	hbytes := make([]byte, HeaderLength)
	if _, err = io.ReadFull(reader, hbytes); err != nil {
		return nil, err
	}
	m := &Message{}
	m.Header, err = decodeHeader(hbytes)
	if err != nil {
		return nil, err
	}
	pbytes := make([]byte, m.Header.MessageLength-HeaderLength)
	if _, err = io.ReadFull(reader, pbytes); err != nil {
		return nil, err
	}
	m.Dictionary = dictionary
	return decodeAVPs(m, pbytes)
}

// NewMessage creates and initializes Message.
func NewMessage(cmd uint32, flags uint8, appid, hopbyhop, endtoend uint32, dictionary *dict.Parser) *Message {
	if dictionary == nil {
		dictionary = dict.Default
	}
	if hopbyhop == 0 {
		hopbyhop = rand.Uint32()
	}
	if endtoend == 0 {
		endtoend = rand.Uint32()
	}
	return &Message{
		Header: &Header{
			Version:       1,
			MessageLength: HeaderLength,
			CommandFlags:  flags,
			CommandCode:   cmd,
			ApplicationId: appid,
			HopByHopId:    hopbyhop,
			EndToEndId:    endtoend,
		},
		Dictionary: dictionary,
	}
}

// NewAVP creates and initializes a new AVP and adds it to the Message.
// @code can be int, uint32 or string (e.g. 268 or Result-Code)
func (m *Message) NewAVP(code interface{}, flags uint8, vendor uint32, data datatypes.DataType) (*AVP, error) {
	var a *AVP
	switch code.(type) {
	case int:
		a = NewAVP(uint32(code.(int)), flags, vendor, data)
	case uint32:
		a = NewAVP(code.(uint32), flags, vendor, data)
	case string:
		dictAVP, err := m.Dictionary.FindAVP(
			m.Header.ApplicationId,
			code.(string),
		)
		if err != nil {
			return nil, err
		}
		a = NewAVP(dictAVP.Code, flags, vendor, data)
	}
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
	return a, nil
}

// AddAVP adds the AVP to the Message.
func (m *Message) AddAVP(a *AVP) {
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
}

func (m *Message) WriteTo(writer io.Writer) (int, error) {
	return writer.Write(m.Serialize())
}

func (m *Message) Serialize() []byte {
	b := make([]byte, m.Len())
	m.SerializeTo(b)
	return b
}

func (m *Message) SerializeTo(b []byte) {
	m.Header.SerializeTo(b[0:HeaderLength])
	offset := HeaderLength
	for _, avp := range m.AVP {
		avp.SerializeTo(b[offset:])
		offset += avp.Len()
	}
}

func (m *Message) Len() int {
	l := HeaderLength
	for _, avp := range m.AVP {
		l += avp.Len()
	}
	return HeaderLength + l
}

func decodeAVPs(m *Message, pbytes []byte) (*Message, error) {
	var avp *AVP
	var err error
	for n := 0; n < cap(pbytes); {
		avp, err = decodeAVP(
			pbytes[n:],
			m.Header.ApplicationId,
			m.Dictionary,
		)
		if err != nil {
			return nil, fmt.Errorf("Failed to decode AVP: %s", err)
		}
		m.AVP = append(m.AVP, avp)
		n += avp.Length + avp.Data.Padding()
		// TODO: Handle grouped AVPs.
	}
	return m, nil
}

// FindAVP searches the Message for a specific AVP.
// @code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avp, err := m.FindAVP(264)
//	avp, err := m.FindAVP("Origin-Host")
func (m *Message) FindAVP(code interface{}) (*AVP, error) {
	dictAVP, err := m.Dictionary.FindAVP(m.Header.ApplicationId, code)
	if err != nil {
		return nil, err
	}
	for _, a := range m.AVP {
		if dictAVP.Code == a.Code {
			return a, nil
		}
	}
	return nil, errors.New("Not found")
}

// Answer creates an answer for the current Message with an embedded
// Result-Code AVP.
func (m *Message) Answer(resultCode uint32) *Message {
	nm := NewMessage(
		m.Header.CommandCode,
		m.Header.CommandFlags&^0x80,
		m.Header.ApplicationId,
		m.Header.HopByHopId,
		m.Header.EndToEndId,
		m.Dictionary,
	)
	nm.NewAVP(268, 0x40, 0x00, datatypes.Unsigned32(resultCode))
	return nm
}

func (m *Message) String() string {
	var b bytes.Buffer
	var typ string
	if m.Header.CommandFlags&0x80 > 0 {
		typ = "Request"
	} else {
		typ = "Answer"
	}
	if dictCMD, err := m.Dictionary.FindCMD(
		m.Header.ApplicationId,
		m.Header.CommandCode,
	); err != nil {
		fmt.Fprintf(&b, "Unknown-%s\n%s\n", typ, m.Header)
	} else {
		fmt.Fprintf(&b,
			"%s-%s (%s%c)\n%s\n",
			dictCMD.Name,
			typ,
			dictCMD.Short,
			typ[0],
			m.Header)
	}
	for _, avp := range m.AVP {
		if dictAVP, err := m.Dictionary.FindAVP(
			m.Header.ApplicationId,
			avp.Code,
		); err != nil {
			fmt.Fprintf(&b, "\tUnknown %s\n", err)
		} else {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, avp)
		}
	}
	return b.String()
}
<commit_msg>Bugfix in Len(); Added NewRequest()<commit_after>// Copyright 2013-2014 go-diameter authors.  All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package diam

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"time"

	"github.com/fiorix/go-diameter/diam/datatypes"
	"github.com/fiorix/go-diameter/diam/dict"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

// Diameter message.
type Message struct {
	Header *Header
	AVP    []*AVP

	Dictionary *dict.Parser // Used to encode and decode AVPs.
}

// ReadMessage returns a Message. It uses the dictionary to parse the
// binary stream from the reader.
func ReadMessage(reader io.Reader, dictionary *dict.Parser) (*Message, error) {
	var err error
	hbytes := make([]byte, HeaderLength)
	if _, err = io.ReadFull(reader, hbytes); err != nil {
		return nil, err
	}
	m := &Message{}
	m.Header, err = decodeHeader(hbytes)
	if err != nil {
		return nil, err
	}
	pbytes := make([]byte, m.Header.MessageLength-HeaderLength)
	if _, err = io.ReadFull(reader, pbytes); err != nil {
		return nil, err
	}
	m.Dictionary = dictionary
	return decodeAVPs(m, pbytes)
}

// NewMessage creates and initializes Message.
func NewMessage(cmd uint32, flags uint8, appid, hopbyhop, endtoend uint32, dictionary *dict.Parser) *Message {
	if dictionary == nil {
		dictionary = dict.Default
	}
	if hopbyhop == 0 {
		hopbyhop = rand.Uint32()
	}
	if endtoend == 0 {
		endtoend = rand.Uint32()
	}
	return &Message{
		Header: &Header{
			Version:       1,
			MessageLength: HeaderLength,
			CommandFlags:  flags,
			CommandCode:   cmd,
			ApplicationId: appid,
			HopByHopId:    hopbyhop,
			EndToEndId:    endtoend,
		},
		Dictionary: dictionary,
	}
}

// NewRequest is an alias to NewMessage.
func NewRequest(cmd uint32, appid uint32, dictionary *dict.Parser) *Message {
	return NewMessage(cmd, 0x80, appid, 0, 0, dictionary)
}

// NewAVP creates and initializes a new AVP and adds it to the Message.
// @code can be int, uint32 or string (e.g. 268 or Result-Code)
func (m *Message) NewAVP(code interface{}, flags uint8, vendor uint32, data datatypes.DataType) (*AVP, error) {
	var a *AVP
	switch code.(type) {
	case int:
		a = NewAVP(uint32(code.(int)), flags, vendor, data)
	case uint32:
		a = NewAVP(code.(uint32), flags, vendor, data)
	case string:
		dictAVP, err := m.Dictionary.FindAVP(
			m.Header.ApplicationId,
			code.(string),
		)
		if err != nil {
			return nil, err
		}
		a = NewAVP(dictAVP.Code, flags, vendor, data)
	}
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
	return a, nil
}

// AddAVP adds the AVP to the Message.
func (m *Message) AddAVP(a *AVP) {
	m.AVP = append(m.AVP, a)
	m.Header.MessageLength += uint32(a.Len())
}

func (m *Message) WriteTo(writer io.Writer) (int, error) {
	return writer.Write(m.Serialize())
}

func (m *Message) Serialize() []byte {
	b := make([]byte, m.Len())
	m.SerializeTo(b)
	return b
}

func (m *Message) SerializeTo(b []byte) {
	m.Header.SerializeTo(b[0:HeaderLength])
	offset := HeaderLength
	for _, avp := range m.AVP {
		avp.SerializeTo(b[offset:])
		offset += avp.Len()
	}
}

func (m *Message) Len() int {
	l := HeaderLength
	for _, avp := range m.AVP {
		l += avp.Len()
	}
	return l
}

func decodeAVPs(m *Message, pbytes []byte) (*Message, error) {
	var avp *AVP
	var err error
	for n := 0; n < cap(pbytes); {
		avp, err = decodeAVP(
			pbytes[n:],
			m.Header.ApplicationId,
			m.Dictionary,
		)
		if err != nil {
			return nil, fmt.Errorf("Failed to decode AVP: %s", err)
		}
		m.AVP = append(m.AVP, avp)
		n += avp.Length + avp.Data.Padding()
		// TODO: Handle grouped AVPs.
	}
	return m, nil
}

// FindAVP searches the Message for a specific AVP.
// @code can be either the AVP code (int, uint32) or name (string).
//
// Example:
//
//	avp, err := m.FindAVP(264)
//	avp, err := m.FindAVP("Origin-Host")
func (m *Message) FindAVP(code interface{}) (*AVP, error) {
	dictAVP, err := m.Dictionary.FindAVP(m.Header.ApplicationId, code)
	if err != nil {
		return nil, err
	}
	for _, a := range m.AVP {
		if dictAVP.Code == a.Code {
			return a, nil
		}
	}
	return nil, errors.New("Not found")
}

// Answer creates an answer for the current Message with an embedded
// Result-Code AVP.
func (m *Message) Answer(resultCode uint32) *Message {
	nm := NewMessage(
		m.Header.CommandCode,
		m.Header.CommandFlags&^0x80,
		m.Header.ApplicationId,
		m.Header.HopByHopId,
		m.Header.EndToEndId,
		m.Dictionary,
	)
	nm.NewAVP(268, 0x40, 0x00, datatypes.Unsigned32(resultCode))
	return nm
}

func (m *Message) String() string {
	var b bytes.Buffer
	var typ string
	if m.Header.CommandFlags&0x80 > 0 {
		typ = "Request"
	} else {
		typ = "Answer"
	}
	if dictCMD, err := m.Dictionary.FindCMD(
		m.Header.ApplicationId,
		m.Header.CommandCode,
	); err != nil {
		fmt.Fprintf(&b, "Unknown-%s\n%s\n", typ, m.Header)
	} else {
		fmt.Fprintf(&b,
			"%s-%s (%s%c)\n%s\n",
			dictCMD.Name,
			typ,
			dictCMD.Short,
			typ[0],
			m.Header)
	}
	for _, avp := range m.AVP {
		if dictAVP, err := m.Dictionary.FindAVP(
			m.Header.ApplicationId,
			avp.Code,
		); err != nil {
			fmt.Fprintf(&b, "\tUnknown %s\n", err)
		} else {
			fmt.Fprintf(&b, "\t%s %s\n", dictAVP.Name, avp)
		}
	}
	return b.String()
}
<|endoftext|>


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

package quickfix

import (
	"bytes"
	"fmt"
	"go/build"
	"go/token"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"nvim-go/config"
	"nvim-go/context"
	"nvim-go/pathutil"

	"github.com/neovim-go/vim"
)

// SetLoclist set the error results data to current buffer's locationlist.
func SetLoclist(v *vim.Vim, loclist []*vim.QuickfixError) error {
	// setloclist({nr}, {list} [, {action}])
	// v.Call(fname string, result interface{}, args ...interface{})
	if len(loclist) > 0 {
		v.Call("setloclist", nil, 0, loclist)
	} else {
		v.Command("lexpr ''")
	}

	return nil
}

// SetErrorlist set the error results data to current buffer's locationlist.
func SetErrorlist(v *vim.Vim, listtype string, errlist []*vim.QuickfixError) error {
	var setlist, clearlist func() error

	switch listtype {
	case "quickfix":
		setlist = func() error { return v.Call("setqflist", nil, errlist) }
		clearlist = func() error { return v.Command("cgetexpr ''") }
	case "locationlist":
		setlist = func() error { return v.Call("setloclist", nil, 0, errlist) }
		clearlist = func() error { return v.Command("lgetexpr ''") }
	}

	if len(errlist) == 0 {
		return clearlist()
	}

	return setlist()
}

// ErrorList merges the errlist map items and open the locationlist window.
// TODO(zchee): This function will reports the errors with open the quickfix window, but will close
// the quickfix window if no errors.
// Do ErrorList function name is appropriate?
func ErrorList(v *vim.Vim, w vim.Window, errlist map[string][]*vim.QuickfixError, keep bool) error {
	var openlist, closelist func() error

	listtype := config.ErrorListType
	switch config.ErrorListType {
	case "quickfix":
		openlist = func() error { return v.Command("copen") }
		closelist = func() error { return v.Command("cclose") }
	case "locationlist":
		openlist = func() error { return v.Command("lopen") }
		closelist = func() error { return v.Command("lclose") }
	}

	if errlist == nil {
		return closelist()
	}

	var loclist []*vim.QuickfixError
	for _, err := range errlist {
		loclist = append(loclist, err...)
	}

	if err := SetErrorlist(v, listtype, loclist); err != nil {
		return err
	}
	if len(loclist) == 0 {
		return closelist()
	}

	if keep {
		defer v.SetCurrentWindow(w)
	}
	return openlist()
}

// OpenLoclist open or close the current buffer's locationlist window.
func OpenLoclist(v *vim.Vim, w vim.Window, loclist []*vim.QuickfixError, keep bool) error {
	if len(loclist) == 0 {
		return v.Command("lclose")
	}

	v.Command("lopen")
	if keep {
		return v.SetCurrentWindow(w)
	}
	return nil
}

// CloseLoclist close the current buffer's locationlist window.
func CloseLoclist(v *vim.Vim) error {
	return v.Command("lclose")
}

// SetQuickfix set the error results data to quickfix list.
func SetQuickfix(p *vim.Pipeline, qflist []*vim.QuickfixError) error {
	p.Call("setqflist", nil, qflist)

	return nil
}

// OpenOuickfix open the quickfix list window.
func OpenOuickfix(p *vim.Pipeline, w vim.Window, keep bool) error {
	p.Command("copen")
	if keep {
		p.SetCurrentWindow(w)
	}
	if err := p.Wait(); err != nil {
		return err
	}

	return nil
}

// CloseQuickfix close the quickfix list window.
func CloseQuickfix(v *vim.Vim) error {
	return v.Command("cclose")
}

func GotoPos(v *vim.Vim, w vim.Window, pos token.Position, cwd string) error {
	fname, line, col := SplitPos(pos.String(), cwd)

	v.Command(fmt.Sprintf("edit %s", pathutil.Expand(fname)))
	v.SetWindowCursor(w, [2]int{line, col - 1})
	defer v.Command(`lclose`)

	return v.Command(`normal zz`)
}

// SplitPos parses a string of form 'token.Pos', and return the relative
// filepath from the current working directory path.
func SplitPos(pos string, cwd string) (string, int, int) {
	position := strings.Split(pos, ":")

	fname := position[0]
	line, err := strconv.ParseInt(position[1], 10, 64)
	if err != nil {
		line = 0
	}
	col, err := strconv.ParseInt(position[2], 10, 64)
	if err != nil {
		col = 0
	}

	if strings.HasPrefix(fname, cwd) {
		frel := strings.TrimPrefix(fname, cwd+string(filepath.Separator))
		if fname != frel {
			return frel, int(line), int(col)
		}
	}

	return fname, int(line), int(col)
}

var errRe = regexp.MustCompile(`(?m)^([^:]+):(\d+)(?::(\d+))?:\s(.*)`)

// ParseError parses a typical error message of Go compile tools.
// TODO(zchee): More elegant way
func ParseError(errors []byte, cwd string, ctxt *context.BuildContext) ([]*vim.QuickfixError, error) {
	var (
		parentDir string
		fpath     string
		errlist   []*vim.QuickfixError
	)

	// m[1]: relative file path of error file
	// m[2]: line number of error point
	// m[3]: column number of error point
	// m[4]: error description text
	for _, m := range errRe.FindAllSubmatch(errors, -1) {
		fpath = filepath.Base(string(bytes.Replace(m[1], []byte{'\t'}, nil, -1)))
		if bytes.Contains(m[1], []byte{'#'}) {
			// m[1][2:] is trim '# ' from errors directory
			// '# nvim-go/nvim' -> 'nvim-go/nvim'
			path := bytes.Split(m[1][2:], []byte{'\n'})

			// save the parent directory path for the second and subsequent error description
			parentDir = string(path[0])
			fpath = filepath.Base(string(bytes.Replace(path[1], []byte{'\t'}, nil, -1)))
		}

		filename := filepath.Join(parentDir, fpath)

		switch ctxt.Tool {
		case "go":
			goSrcPath := filepath.Join(build.Default.GOPATH, "src")
			currentDir := strings.TrimPrefix(cwd, goSrcPath+string(filepath.Separator))
			filename = strings.TrimPrefix(filepath.Clean(filename), currentDir+string(filepath.Separator))

		case "gb":
			if !filepath.IsAbs(filename) {
				filename = filepath.Join(ctxt.GbProjectDir, "src", filename)
			}
			if frel, err := filepath.Rel(cwd, filename); err == nil {
				filename = frel
			}
		}

		line, err := strconv.Atoi(string(m[2]))
		if err != nil {
			return nil, err
		}

		col, err := strconv.Atoi(string(m[3]))
		// fallback if cannot convert col to type int
		// Basically, col == ""
		if err != nil {
			col = 0
		}

		errlist = append(errlist, &vim.QuickfixError{
			FileName: filename,
			LNum:     line,
			Col:      col,
			Text:     string(bytes.TrimSpace(m[4])),
		})
	}

	return errlist, nil
}
<commit_msg>nvim/quickfix: Fix initialize quickfix commands and small optimize<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package quickfix

import (
	"bytes"
	"fmt"
	"go/build"
	"go/token"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"nvim-go/config"
	"nvim-go/context"
	"nvim-go/pathutil"

	"github.com/neovim-go/vim"
)

// SetLoclist set the error results data to current buffer's locationlist.
func SetLoclist(v *vim.Vim, loclist []*vim.QuickfixError) error {
	// setloclist({nr}, {list} [, {action}])
	// v.Call(fname string, result interface{}, args ...interface{})
	if len(loclist) > 0 {
		v.Call("setloclist", nil, 0, loclist)
	} else {
		v.Command("lexpr ''")
	}

	return nil
}

var (
	listtype                  ErrorListType
	openlistCmd, closelistCmd func() error
	setlistCmd, clearlistCmd  func() error
)

type ErrorListType string

const (
	Quickfix     ErrorListType = "quickfix"
	LocationList ErrorListType = "locationlist"
)

// SetErrorlist set the error results data to current buffer's locationlist.
func SetErrorlist(v *vim.Vim, errlist []*vim.QuickfixError) error {
	if setlistCmd == nil || clearlistCmd == nil {
		switch listtype {
		case Quickfix:
			setlistCmd = func() error { return v.Call("setqflist", nil, errlist) }
			clearlistCmd = func() error { return v.Command("cgetexpr ''") }
		case LocationList:
			setlistCmd = func() error { return v.Call("setloclist", nil, 0, errlist) }
			clearlistCmd = func() error { return v.Command("lgetexpr ''") }
		}
	}

	if len(errlist) == 0 {
		return clearlistCmd()
	}

	return setlistCmd()
}

// ErrorList merges the errlist map items and open the locationlist window.
// TODO(zchee): This function will reports the errors with open the quickfix window, but will close
// the quickfix window if no errors.
// Do ErrorList function name is appropriate?
func ErrorList(v *vim.Vim, w vim.Window, errors map[string][]*vim.QuickfixError, keep bool) error {
	if listtype == "" {
		listtype = ErrorListType(config.ErrorListType)
		switch listtype {
		case Quickfix:
			openlistCmd = func() error { return v.Command("copen") }
			closelistCmd = func() error { return v.Command("cclose") }
		case LocationList:
			openlistCmd = func() error { return v.Command("lopen") }
			closelistCmd = func() error { return v.Command("lclose") }
		}
	}

	if errors == nil || len(errors) == 0 {
		return closelistCmd()
	}

	var errlist []*vim.QuickfixError
	for _, err := range errors {
		errlist = append(errlist, err...)
	}

	if err := SetErrorlist(v, errlist); err != nil {
		return err
	}

	if keep {
		defer v.SetCurrentWindow(w)
	}
	return openlistCmd()
}

// OpenLoclist open or close the current buffer's locationlist window.
func OpenLoclist(v *vim.Vim, w vim.Window, loclist []*vim.QuickfixError, keep bool) error {
	if len(loclist) == 0 {
		return v.Command("lclose")
	}

	v.Command("lopen")
	if keep {
		return v.SetCurrentWindow(w)
	}
	return nil
}

// CloseLoclist close the current buffer's locationlist window.
func CloseLoclist(v *vim.Vim) error {
	return v.Command("lclose")
}

// SetQuickfix set the error results data to quickfix list.
func SetQuickfix(p *vim.Pipeline, qflist []*vim.QuickfixError) error {
	p.Call("setqflist", nil, qflist)

	return nil
}

// OpenOuickfix open the quickfix list window.
func OpenOuickfix(p *vim.Pipeline, w vim.Window, keep bool) error {
	p.Command("copen")
	if keep {
		p.SetCurrentWindow(w)
	}
	if err := p.Wait(); err != nil {
		return err
	}

	return nil
}

// CloseQuickfix close the quickfix list window.
func CloseQuickfix(v *vim.Vim) error {
	return v.Command("cclose")
}

func GotoPos(v *vim.Vim, w vim.Window, pos token.Position, cwd string) error {
	fname, line, col := SplitPos(pos.String(), cwd)

	v.Command(fmt.Sprintf("edit %s", pathutil.Expand(fname)))
	v.SetWindowCursor(w, [2]int{line, col - 1})
	defer v.Command(`lclose`)

	return v.Command(`normal zz`)
}

// SplitPos parses a string of form 'token.Pos', and return the relative
// filepath from the current working directory path.
func SplitPos(pos string, cwd string) (string, int, int) {
	position := strings.Split(pos, ":")

	fname := position[0]
	line, err := strconv.ParseInt(position[1], 10, 64)
	if err != nil {
		line = 0
	}
	col, err := strconv.ParseInt(position[2], 10, 64)
	if err != nil {
		col = 0
	}

	if strings.HasPrefix(fname, cwd) {
		frel := strings.TrimPrefix(fname, cwd+string(filepath.Separator))
		if fname != frel {
			return frel, int(line), int(col)
		}
	}

	return fname, int(line), int(col)
}

var errRe = regexp.MustCompile(`(?m)^([^:]+):(\d+)(?::(\d+))?:\s(.*)`)

// ParseError parses a typical error message of Go compile tools.
// TODO(zchee): More elegant way
func ParseError(errors []byte, cwd string, ctxt *context.BuildContext) ([]*vim.QuickfixError, error) {
	var (
		parentDir string
		fpath     string
		errlist   []*vim.QuickfixError
	)

	// m[1]: relative file path of error file
	// m[2]: line number of error point
	// m[3]: column number of error point
	// m[4]: error description text
	for _, m := range errRe.FindAllSubmatch(errors, -1) {
		fpath = filepath.Base(string(bytes.Replace(m[1], []byte{'\t'}, nil, -1)))
		if bytes.Contains(m[1], []byte{'#'}) {
			// m[1][2:] is trim '# ' from errors directory
			// '# nvim-go/nvim' -> 'nvim-go/nvim'
			path := bytes.Split(m[1][2:], []byte{'\n'})

			// save the parent directory path for the second and subsequent error description
			parentDir = string(path[0])
			fpath = filepath.Base(string(bytes.Replace(path[1], []byte{'\t'}, nil, -1)))
		}

		filename := filepath.Join(parentDir, fpath)

		switch ctxt.Tool {
		case "go":
			goSrcPath := filepath.Join(build.Default.GOPATH, "src")
			currentDir := strings.TrimPrefix(cwd, goSrcPath+string(filepath.Separator))
			filename = strings.TrimPrefix(filepath.Clean(filename), currentDir+string(filepath.Separator))

		case "gb":
			if !filepath.IsAbs(filename) {
				filename = filepath.Join(ctxt.GbProjectDir, "src", filename)
			}
			if frel, err := filepath.Rel(cwd, filename); err == nil {
				filename = frel
			}
		}

		line, err := strconv.Atoi(string(m[2]))
		if err != nil {
			return nil, err
		}

		col, err := strconv.Atoi(string(m[3]))
		// fallback if cannot convert col to type int
		// Basically, col == ""
		if err != nil {
			col = 0
		}

		errlist = append(errlist, &vim.QuickfixError{
			FileName: filename,
			LNum:     line,
			Col:      col,
			Text:     string(bytes.TrimSpace(m[4])),
		})
	}

	return errlist, nil
}
<|endoftext|>


<commit_before>package aws

import (
	"bytes"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAutoscalingPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingPolicyCreate,
		Read:   resourceAwsAutoscalingPolicyRead,
		Update: resourceAwsAutoscalingPolicyUpdate,
		Delete: resourceAwsAutoscalingPolicyDelete,

		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"adjustment_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"autoscaling_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policy_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "SimpleScaling", // preserve AWS's default to make validation easier.
			},
			"cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"estimated_instance_warmup": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"metric_aggregation_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
			"min_adjustment_magnitude": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"min_adjustment_step": &schema.Schema{
				Type:          schema.TypeInt,
				Optional:      true,
				Deprecated:    "Use min_adjustment_magnitude instead, otherwise you may see a perpetual diff on this resource.",
				ConflictsWith: []string{"min_adjustment_magnitude"},
			},
			"scaling_adjustment": &schema.Schema{
				Type:          schema.TypeInt,
				Optional:      true,
				ConflictsWith: []string{"step_adjustment"},
			},
			"step_adjustment": &schema.Schema{
				Type:          schema.TypeSet,
				Optional:      true,
				ConflictsWith: []string{"scaling_adjustment"},
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"metric_interval_lower_bound": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"metric_interval_upper_bound": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"scaling_adjustment": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
				Set: resourceAwsAutoscalingScalingAdjustmentHash,
			},
		},
	}
}

func resourceAwsAutoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params, err := getAwsAutoscalingPutScalingPolicyInput(d)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] AutoScaling PutScalingPolicy: %#v", params)
	resp, err := autoscalingconn.PutScalingPolicy(&params)
	if err != nil {
		return fmt.Errorf("Error putting scaling policy: %s", err)
	}

	d.Set("arn", resp.PolicyARN)
	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Scaling PolicyARN: %s", d.Get("arn").(string))

	return resourceAwsAutoscalingPolicyRead(d, meta)
}

func resourceAwsAutoscalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
	p, err := getAwsAutoscalingPolicy(d, meta)
	if err != nil {
		return err
	}
	if p == nil {
		d.SetId("")
		return nil
	}

	log.Printf("[DEBUG] Read Scaling Policy: ASG: %s, SP: %s, Obj: %s", d.Get("autoscaling_group_name"), d.Get("name"), p)

	d.Set("adjustment_type", p.AdjustmentType)
	d.Set("autoscaling_group_name", p.AutoScalingGroupName)
	d.Set("cooldown", p.Cooldown)
	d.Set("estimated_instance_warmup", p.EstimatedInstanceWarmup)
	d.Set("metric_aggregation_type", p.MetricAggregationType)
	d.Set("policy_type", p.PolicyType)
	if p.MinAdjustmentMagnitude != nil {
		d.Set("min_adjustment_magnitude", p.MinAdjustmentMagnitude)
		d.Set("min_adjustment_step", 0)
	} else {
		d.Set("min_adjustment_step", p.MinAdjustmentStep)
	}
	d.Set("arn", p.PolicyARN)
	d.Set("name", p.PolicyName)
	d.Set("scaling_adjustment", p.ScalingAdjustment)
	d.Set("step_adjustment", flattenStepAdjustments(p.StepAdjustments))

	return nil
}

func resourceAwsAutoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params, inputErr := getAwsAutoscalingPutScalingPolicyInput(d)
	if inputErr != nil {
		return inputErr
	}

	log.Printf("[DEBUG] Autoscaling Update Scaling Policy: %#v", params)
	_, err := autoscalingconn.PutScalingPolicy(&params)
	if err != nil {
		return err
	}

	return resourceAwsAutoscalingPolicyRead(d, meta)
}

func resourceAwsAutoscalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn
	p, err := getAwsAutoscalingPolicy(d, meta)
	if err != nil {
		return err
	}
	if p == nil {
		return nil
	}

	params := autoscaling.DeletePolicyInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyName:           aws.String(d.Get("name").(string)),
	}
	log.Printf("[DEBUG] Deleting Autoscaling Policy opts: %s", params)
	if _, err := autoscalingconn.DeletePolicy(&params); err != nil {
		return fmt.Errorf("Autoscaling Scaling Policy: %s ", err)
	}

	d.SetId("")
	return nil
}

// PutScalingPolicy can safely resend all parameters without destroying the
// resource, so create and update can share this common function. It will error
// if certain mutually exclusive values are set.
func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) (autoscaling.PutScalingPolicyInput, error) {
	var params = autoscaling.PutScalingPolicyInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyName:           aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("adjustment_type"); ok {
		params.AdjustmentType = aws.String(v.(string))
	}

	if v, ok := d.GetOk("cooldown"); ok {
		params.Cooldown = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("estimated_instance_warmup"); ok {
		params.EstimatedInstanceWarmup = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("metric_aggregation_type"); ok {
		params.MetricAggregationType = aws.String(v.(string))
	}

	if v, ok := d.GetOk("policy_type"); ok {
		params.PolicyType = aws.String(v.(string))
	}

	//if policy_type=="SimpleScaling" then scaling_adjustment is required and 0 is allowed
	if v, ok := d.GetOk("scaling_adjustment"); ok || *params.PolicyType == "SimpleScaling" {
		params.ScalingAdjustment = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("step_adjustment"); ok {
		steps, err := expandStepAdjustments(v.(*schema.Set).List())
		if err != nil {
			return params, fmt.Errorf("metric_interval_lower_bound and metric_interval_upper_bound must be strings!")
		}
		params.StepAdjustments = steps
	}

	if v, ok := d.GetOk("min_adjustment_magnitude"); ok {
		// params.MinAdjustmentMagnitude = aws.Int64(int64(d.Get("min_adjustment_magnitude").(int)))
		params.MinAdjustmentMagnitude = aws.Int64(int64(v.(int)))
	} else if v, ok := d.GetOk("min_adjustment_step"); ok {
		// params.MinAdjustmentStep = aws.Int64(int64(d.Get("min_adjustment_step").(int)))
		params.MinAdjustmentStep = aws.Int64(int64(v.(int)))
	}

	// Validate our final input to confirm it won't error when sent to AWS.
	// First, SimpleScaling policy types...
	if *params.PolicyType == "SimpleScaling" && params.StepAdjustments != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use step_adjustments!")
	}
	if *params.PolicyType == "SimpleScaling" && params.MetricAggregationType != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use metric_aggregation_type!")
	}
	if *params.PolicyType == "SimpleScaling" && params.EstimatedInstanceWarmup != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use estimated_instance_warmup!")
	}

	// Second, StepScaling policy types...
	if *params.PolicyType == "StepScaling" && params.ScalingAdjustment != nil {
		return params, fmt.Errorf("StepScaling policy types cannot use scaling_adjustment!")
	}
	if *params.PolicyType == "StepScaling" && params.Cooldown != nil {
		return params, fmt.Errorf("StepScaling policy types cannot use cooldown!")
	}

	return params, nil
}

func getAwsAutoscalingPolicy(d *schema.ResourceData, meta interface{}) (*autoscaling.ScalingPolicy, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params := autoscaling.DescribePoliciesInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyNames:          []*string{aws.String(d.Get("name").(string))},
	}

	log.Printf("[DEBUG] AutoScaling Scaling Policy Describe Params: %#v", params)
	resp, err := autoscalingconn.DescribePolicies(&params)
	if err != nil {
		//A ValidationError here can mean that either the Policy is missing OR the Autoscaling Group is missing
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ValidationError" {
			log.Printf("[WARNING] %s not found, removing from state", d.Id())
			d.SetId("")

			return nil, nil
		}
		return nil, fmt.Errorf("Error retrieving scaling policies: %s", err)
	}

	// find scaling policy
	name := d.Get("name")
	for idx, sp := range resp.ScalingPolicies {
		if *sp.PolicyName == name {
			return resp.ScalingPolicies[idx], nil
		}
	}

	// policy not found
	return nil, nil
}

func resourceAwsAutoscalingScalingAdjustmentHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	if v, ok := m["metric_interval_lower_bound"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v))
	}
	if v, ok := m["metric_interval_upper_bound"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v))
	}
	buf.WriteString(fmt.Sprintf("%d-", m["scaling_adjustment"].(int)))

	return hashcode.String(buf.String())
}
<commit_msg>provider/aws: Set aws_autoscaling_policy metric_aggregation_type to be (#10904)<commit_after>package aws

import (
	"bytes"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAutoscalingPolicy() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAutoscalingPolicyCreate,
		Read:   resourceAwsAutoscalingPolicyRead,
		Update: resourceAwsAutoscalingPolicyUpdate,
		Delete: resourceAwsAutoscalingPolicyDelete,

		Schema: map[string]*schema.Schema{
			"arn": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"adjustment_type": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"autoscaling_group_name": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"policy_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Default:  "SimpleScaling", // preserve AWS's default to make validation easier.
			},
			"cooldown": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"estimated_instance_warmup": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"metric_aggregation_type": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"min_adjustment_magnitude": &schema.Schema{
				Type:     schema.TypeInt,
				Optional: true,
			},
			"min_adjustment_step": &schema.Schema{
				Type:          schema.TypeInt,
				Optional:      true,
				Deprecated:    "Use min_adjustment_magnitude instead, otherwise you may see a perpetual diff on this resource.",
				ConflictsWith: []string{"min_adjustment_magnitude"},
			},
			"scaling_adjustment": &schema.Schema{
				Type:          schema.TypeInt,
				Optional:      true,
				ConflictsWith: []string{"step_adjustment"},
			},
			"step_adjustment": &schema.Schema{
				Type:          schema.TypeSet,
				Optional:      true,
				ConflictsWith: []string{"scaling_adjustment"},
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"metric_interval_lower_bound": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"metric_interval_upper_bound": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
						},
						"scaling_adjustment": &schema.Schema{
							Type:     schema.TypeInt,
							Required: true,
						},
					},
				},
				Set: resourceAwsAutoscalingScalingAdjustmentHash,
			},
		},
	}
}

func resourceAwsAutoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params, err := getAwsAutoscalingPutScalingPolicyInput(d)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] AutoScaling PutScalingPolicy: %#v", params)
	resp, err := autoscalingconn.PutScalingPolicy(&params)
	if err != nil {
		return fmt.Errorf("Error putting scaling policy: %s", err)
	}

	d.Set("arn", resp.PolicyARN)
	d.SetId(d.Get("name").(string))
	log.Printf("[INFO] AutoScaling Scaling PolicyARN: %s", d.Get("arn").(string))

	return resourceAwsAutoscalingPolicyRead(d, meta)
}

func resourceAwsAutoscalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
	p, err := getAwsAutoscalingPolicy(d, meta)
	if err != nil {
		return err
	}
	if p == nil {
		d.SetId("")
		return nil
	}

	log.Printf("[DEBUG] Read Scaling Policy: ASG: %s, SP: %s, Obj: %s", d.Get("autoscaling_group_name"), d.Get("name"), p)

	d.Set("adjustment_type", p.AdjustmentType)
	d.Set("autoscaling_group_name", p.AutoScalingGroupName)
	d.Set("cooldown", p.Cooldown)
	d.Set("estimated_instance_warmup", p.EstimatedInstanceWarmup)
	d.Set("metric_aggregation_type", p.MetricAggregationType)
	d.Set("policy_type", p.PolicyType)
	if p.MinAdjustmentMagnitude != nil {
		d.Set("min_adjustment_magnitude", p.MinAdjustmentMagnitude)
		d.Set("min_adjustment_step", 0)
	} else {
		d.Set("min_adjustment_step", p.MinAdjustmentStep)
	}
	d.Set("arn", p.PolicyARN)
	d.Set("name", p.PolicyName)
	d.Set("scaling_adjustment", p.ScalingAdjustment)
	d.Set("step_adjustment", flattenStepAdjustments(p.StepAdjustments))

	return nil
}

func resourceAwsAutoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params, inputErr := getAwsAutoscalingPutScalingPolicyInput(d)
	if inputErr != nil {
		return inputErr
	}

	log.Printf("[DEBUG] Autoscaling Update Scaling Policy: %#v", params)
	_, err := autoscalingconn.PutScalingPolicy(&params)
	if err != nil {
		return err
	}

	return resourceAwsAutoscalingPolicyRead(d, meta)
}

func resourceAwsAutoscalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
	autoscalingconn := meta.(*AWSClient).autoscalingconn
	p, err := getAwsAutoscalingPolicy(d, meta)
	if err != nil {
		return err
	}
	if p == nil {
		return nil
	}

	params := autoscaling.DeletePolicyInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyName:           aws.String(d.Get("name").(string)),
	}
	log.Printf("[DEBUG] Deleting Autoscaling Policy opts: %s", params)
	if _, err := autoscalingconn.DeletePolicy(&params); err != nil {
		return fmt.Errorf("Autoscaling Scaling Policy: %s ", err)
	}

	d.SetId("")
	return nil
}

// PutScalingPolicy can safely resend all parameters without destroying the
// resource, so create and update can share this common function. It will error
// if certain mutually exclusive values are set.
func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) (autoscaling.PutScalingPolicyInput, error) {
	var params = autoscaling.PutScalingPolicyInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyName:           aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("adjustment_type"); ok {
		params.AdjustmentType = aws.String(v.(string))
	}

	if v, ok := d.GetOk("cooldown"); ok {
		params.Cooldown = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("estimated_instance_warmup"); ok {
		params.EstimatedInstanceWarmup = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("metric_aggregation_type"); ok {
		params.MetricAggregationType = aws.String(v.(string))
	}

	if v, ok := d.GetOk("policy_type"); ok {
		params.PolicyType = aws.String(v.(string))
	}

	//if policy_type=="SimpleScaling" then scaling_adjustment is required and 0 is allowed
	if v, ok := d.GetOk("scaling_adjustment"); ok || *params.PolicyType == "SimpleScaling" {
		params.ScalingAdjustment = aws.Int64(int64(v.(int)))
	}

	if v, ok := d.GetOk("step_adjustment"); ok {
		steps, err := expandStepAdjustments(v.(*schema.Set).List())
		if err != nil {
			return params, fmt.Errorf("metric_interval_lower_bound and metric_interval_upper_bound must be strings!")
		}
		params.StepAdjustments = steps
	}

	if v, ok := d.GetOk("min_adjustment_magnitude"); ok {
		// params.MinAdjustmentMagnitude = aws.Int64(int64(d.Get("min_adjustment_magnitude").(int)))
		params.MinAdjustmentMagnitude = aws.Int64(int64(v.(int)))
	} else if v, ok := d.GetOk("min_adjustment_step"); ok {
		// params.MinAdjustmentStep = aws.Int64(int64(d.Get("min_adjustment_step").(int)))
		params.MinAdjustmentStep = aws.Int64(int64(v.(int)))
	}

	// Validate our final input to confirm it won't error when sent to AWS.
	// First, SimpleScaling policy types...
	if *params.PolicyType == "SimpleScaling" && params.StepAdjustments != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use step_adjustments!")
	}
	if *params.PolicyType == "SimpleScaling" && params.MetricAggregationType != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use metric_aggregation_type!")
	}
	if *params.PolicyType == "SimpleScaling" && params.EstimatedInstanceWarmup != nil {
		return params, fmt.Errorf("SimpleScaling policy types cannot use estimated_instance_warmup!")
	}

	// Second, StepScaling policy types...
	if *params.PolicyType == "StepScaling" && params.ScalingAdjustment != nil {
		return params, fmt.Errorf("StepScaling policy types cannot use scaling_adjustment!")
	}
	if *params.PolicyType == "StepScaling" && params.Cooldown != nil {
		return params, fmt.Errorf("StepScaling policy types cannot use cooldown!")
	}

	return params, nil
}

func getAwsAutoscalingPolicy(d *schema.ResourceData, meta interface{}) (*autoscaling.ScalingPolicy, error) {
	autoscalingconn := meta.(*AWSClient).autoscalingconn

	params := autoscaling.DescribePoliciesInput{
		AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
		PolicyNames:          []*string{aws.String(d.Get("name").(string))},
	}

	log.Printf("[DEBUG] AutoScaling Scaling Policy Describe Params: %#v", params)
	resp, err := autoscalingconn.DescribePolicies(&params)
	if err != nil {
		//A ValidationError here can mean that either the Policy is missing OR the Autoscaling Group is missing
		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "ValidationError" {
			log.Printf("[WARNING] %s not found, removing from state", d.Id())
			d.SetId("")

			return nil, nil
		}
		return nil, fmt.Errorf("Error retrieving scaling policies: %s", err)
	}

	// find scaling policy
	name := d.Get("name")
	for idx, sp := range resp.ScalingPolicies {
		if *sp.PolicyName == name {
			return resp.ScalingPolicies[idx], nil
		}
	}

	// policy not found
	return nil, nil
}

func resourceAwsAutoscalingScalingAdjustmentHash(v interface{}) int {
	var buf bytes.Buffer
	m := v.(map[string]interface{})
	if v, ok := m["metric_interval_lower_bound"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v))
	}
	if v, ok := m["metric_interval_upper_bound"]; ok {
		buf.WriteString(fmt.Sprintf("%f-", v))
	}
	buf.WriteString(fmt.Sprintf("%d-", m["scaling_adjustment"].(int)))

	return hashcode.String(buf.String())
}
<|endoftext|>


<commit_before>package api_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/ello/ello-go/streams/api"
	"github.com/ello/ello-go/streams/model"
	"github.com/julienschmidt/httprouter"
	"github.com/m4rw3r/uuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var StreamID uuid.UUID

type mockStreamService struct {
	internal       []model.StreamItem
	lastItemsOnAdd []model.StreamItem
	lastLimit      int
	lastFromSlug   string
}

func (s mockStreamService) Add(items []model.StreamItem) error {
	s.lastItemsOnAdd = items
	s.internal = append(s.internal, items...)
	return nil
}

func (s mockStreamService) Load(query model.StreamQuery, limit int, fromSlug string) ([]model.StreamItem, error) {
	s.lastLimit = limit
	s.lastFromSlug = fromSlug
	return s.internal, nil
}

var (
	router        *httprouter.Router
	response      *httptest.ResponseRecorder
	streamService mockStreamService
)

func Request(method string, route string, body string) {
	r, err := http.NewRequest(method, route, strings.NewReader(body))
	response = httptest.NewRecorder()

	log.WithFields(log.Fields{
		"url":     r.URL,
		"method":  r.Method,
		"headers": r.Header,
		"body":    body,
		"errors":  err,
	}).Debug("About to issue request")

	router.ServeHTTP(response, r)
}

var _ = BeforeSuite(func() {
	log.SetLevel(log.DebugLevel)

	router = httprouter.New()

	StreamID, _ := uuid.V4()

	streamService = mockStreamService{
		internal: generateFakeResponse(StreamID),
	}
	streamController := api.NewStreamController(streamService)

	streamController.Register(router)
})

func TestControllers(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Controllers Suite")
}

func generateFakeResponse(streamID uuid.UUID) []model.StreamItem {
	//fake data
	uuid1, _ := uuid.V4()
	uuid2, _ := uuid.V4()

	return []model.StreamItem{
		{
			ID:        uuid1,
			Timestamp: time.Now(),
			Type:      model.TypePost,
			StreamID:  streamID,
		},
		{
			ID:        uuid2,
			Timestamp: time.Now(),
			Type:      model.TypeRepost,
			StreamID:  streamID,
		},
	}
}

func logResponse(r *httptest.ResponseRecorder) {
	log.WithFields(log.Fields{
		"status":  r.Code,
		"headers": r.HeaderMap,
		"body":    r.Body.String(),
	}).Debug("Got Response")
}

func checkStreamItems(c model.StreamItem, c1 model.StreamItem) {
	Expect(c).NotTo(BeNil())
	Expect(c1).NotTo(BeNil())
	Expect(c1.StreamID).To(Equal(c.StreamID))
	Expect(c1.ID).To(Equal(c.ID))
	Expect(c1.Type).To(Equal(c.Type))
	Expect(c1.Timestamp).To(BeTemporally("~", c.Timestamp, time.Millisecond))
}

func checkAll(c []model.StreamItem, c1 []model.StreamItem) {
	for i := 0; i < len(c); i++ {
		checkStreamItems(c[i], c1[i])
	}
}
<commit_msg>Fixed tests<commit_after>package api_test

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/ello/ello-go/streams/api"
	"github.com/ello/ello-go/streams/model"
	"github.com/julienschmidt/httprouter"
	"github.com/m4rw3r/uuid"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var StreamID uuid.UUID

type mockStreamService struct {
	internal       []model.StreamItem
	lastItemsOnAdd []model.StreamItem
	lastLimit      int
	lastFromSlug   string
}

func (s mockStreamService) Add(items []model.StreamItem) error {
	s.lastItemsOnAdd = items
	s.internal = append(s.internal, items...)
	return nil
}

func (s mockStreamService) Load(query model.StreamQuery, limit int, fromSlug string) (*model.StreamQueryResponse, error) {
	s.lastLimit = limit
	s.lastFromSlug = fromSlug
	return &model.StreamQueryResponse{Items: s.internal}, nil
}

var (
	router        *httprouter.Router
	response      *httptest.ResponseRecorder
	streamService mockStreamService
)

func Request(method string, route string, body string) {
	r, err := http.NewRequest(method, route, strings.NewReader(body))
	response = httptest.NewRecorder()

	log.WithFields(log.Fields{
		"url":     r.URL,
		"method":  r.Method,
		"headers": r.Header,
		"body":    body,
		"errors":  err,
	}).Debug("About to issue request")

	router.ServeHTTP(response, r)
}

var _ = BeforeSuite(func() {
	log.SetLevel(log.DebugLevel)

	router = httprouter.New()

	StreamID, _ := uuid.V4()

	streamService = mockStreamService{
		internal: generateFakeResponse(StreamID),
	}
	streamController := api.NewStreamController(streamService)

	streamController.Register(router)
})

func TestControllers(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Controllers Suite")
}

func generateFakeResponse(streamID uuid.UUID) []model.StreamItem {
	//fake data
	uuid1, _ := uuid.V4()
	uuid2, _ := uuid.V4()

	return []model.StreamItem{
		{
			ID:        uuid1,
			Timestamp: time.Now(),
			Type:      model.TypePost,
			StreamID:  streamID,
		},
		{
			ID:        uuid2,
			Timestamp: time.Now(),
			Type:      model.TypeRepost,
			StreamID:  streamID,
		},
	}
}

func logResponse(r *httptest.ResponseRecorder) {
	log.WithFields(log.Fields{
		"status":  r.Code,
		"headers": r.HeaderMap,
		"body":    r.Body.String(),
	}).Debug("Got Response")
}

func checkStreamItems(c model.StreamItem, c1 model.StreamItem) {
	Expect(c).NotTo(BeNil())
	Expect(c1).NotTo(BeNil())
	Expect(c1.StreamID).To(Equal(c.StreamID))
	Expect(c1.ID).To(Equal(c.ID))
	Expect(c1.Type).To(Equal(c.Type))
	Expect(c1.Timestamp).To(BeTemporally("~", c.Timestamp, time.Millisecond))
}

func checkAll(c []model.StreamItem, c1 []model.StreamItem) {
	for i := 0; i < len(c); i++ {
		checkStreamItems(c[i], c1[i])
	}
}
<|endoftext|>


<commit_before>// +build js

package http

import (
	"bufio"
	"bytes"
	"errors"
	"io/ioutil"
	"net/textproto"
	"strconv"

	"github.com/bpowers/browsix-gopherjs/js"
)

var DefaultTransport RoundTripper = &XHRTransport{}

type XHRTransport struct {
	inflight map[*Request]*js.Object
}

func (t *XHRTransport) RoundTrip(req *Request) (*Response, error) {
	xhrConstructor := js.Global.Get("XMLHttpRequest")
	if xhrConstructor == js.Undefined {
		return nil, errors.New("net/http: XMLHttpRequest not available")
	}
	xhr := xhrConstructor.New()

	if t.inflight == nil {
		t.inflight = map[*Request]*js.Object{}
	}
	t.inflight[req] = xhr
	defer delete(t.inflight, req)

	respCh := make(chan *Response)
	errCh := make(chan error)

	xhr.Set("onload", func() {
		header, _ := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(xhr.Call("getAllResponseHeaders").String() + "\n")))).ReadMIMEHeader()
		body := js.Global.Get("Uint8Array").New(xhr.Get("response")).Interface().([]byte)

		contentLength := int64(-1)
		switch req.Method {
		case "HEAD":
			if l, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
				contentLength = l
			}
		default:
			contentLength = int64(len(body))
		}

		respCh <- &Response{
			Status:        xhr.Get("status").String() + " " + xhr.Get("statusText").String(),
			StatusCode:    xhr.Get("status").Int(),
			Header:        Header(header),
			ContentLength: contentLength,
			Body:          ioutil.NopCloser(bytes.NewReader(body)),
			Request:       req,
		}
	})

	xhr.Set("onerror", func(e *js.Object) {
		errCh <- errors.New("net/http: XMLHttpRequest failed")
	})

	xhr.Set("onabort", func(e *js.Object) {
		errCh <- errors.New("net/http: request canceled")
	})

	xhr.Call("open", req.Method, req.URL.String())
	xhr.Set("responseType", "arraybuffer") // has to be after "open" until https://bugzilla.mozilla.org/show_bug.cgi?id=1110761 is resolved
	for key, values := range req.Header {
		for _, value := range values {
			xhr.Call("setRequestHeader", key, value)
		}
	}
	var body []byte
	if req.Body != nil {
		var err error
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return nil, err
		}
	}
	xhr.Call("send", body)

	select {
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

func (t *XHRTransport) CancelRequest(req *Request) {
	if xhr, ok := t.inflight[req]; ok {
		xhr.Call("abort")
	}
}
<commit_msg>compiler/natives: use default roundtripper to talk to Go servers<commit_after>// +build js

package http

import (
	"bufio"
	"bytes"
	"errors"
	"io/ioutil"
	"net/textproto"
	"strconv"

	"github.com/bpowers/browsix-gopherjs/js"
)

//var DefaultTransport RoundTripper = &XHRTransport{}

type XHRTransport struct {
	inflight map[*Request]*js.Object
}

func (t *XHRTransport) RoundTrip(req *Request) (*Response, error) {
	xhrConstructor := js.Global.Get("XMLHttpRequest")
	if xhrConstructor == js.Undefined {
		return nil, errors.New("net/http: XMLHttpRequest not available")
	}
	xhr := xhrConstructor.New()

	if t.inflight == nil {
		t.inflight = map[*Request]*js.Object{}
	}
	t.inflight[req] = xhr
	defer delete(t.inflight, req)

	respCh := make(chan *Response)
	errCh := make(chan error)

	xhr.Set("onload", func() {
		header, _ := textproto.NewReader(bufio.NewReader(bytes.NewReader([]byte(xhr.Call("getAllResponseHeaders").String() + "\n")))).ReadMIMEHeader()
		body := js.Global.Get("Uint8Array").New(xhr.Get("response")).Interface().([]byte)

		contentLength := int64(-1)
		switch req.Method {
		case "HEAD":
			if l, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64); err == nil {
				contentLength = l
			}
		default:
			contentLength = int64(len(body))
		}

		respCh <- &Response{
			Status:        xhr.Get("status").String() + " " + xhr.Get("statusText").String(),
			StatusCode:    xhr.Get("status").Int(),
			Header:        Header(header),
			ContentLength: contentLength,
			Body:          ioutil.NopCloser(bytes.NewReader(body)),
			Request:       req,
		}
	})

	xhr.Set("onerror", func(e *js.Object) {
		errCh <- errors.New("net/http: XMLHttpRequest failed")
	})

	xhr.Set("onabort", func(e *js.Object) {
		errCh <- errors.New("net/http: request canceled")
	})

	xhr.Call("open", req.Method, req.URL.String())
	xhr.Set("responseType", "arraybuffer") // has to be after "open" until https://bugzilla.mozilla.org/show_bug.cgi?id=1110761 is resolved
	for key, values := range req.Header {
		for _, value := range values {
			xhr.Call("setRequestHeader", key, value)
		}
	}
	var body []byte
	if req.Body != nil {
		var err error
		body, err = ioutil.ReadAll(req.Body)
		if err != nil {
			return nil, err
		}
	}
	xhr.Call("send", body)

	select {
	case resp := <-respCh:
		return resp, nil
	case err := <-errCh:
		return nil, err
	}
}

func (t *XHRTransport) CancelRequest(req *Request) {
	if xhr, ok := t.inflight[req]; ok {
		xhr.Call("abort")
	}
}
<|endoftext|>


<commit_before>package ring

// "fmt"
import "testing"
import "fmt"

type measure struct {
	f float64
}

func (m measure) Value() float64 {
	return m.f
}
func TestSetsSize(t *testing.T) {
	r := &Ring{}
	r.SetCapacity(10)
	if r.Capacity() != 10 {
		t.Fatal("Size of ring was not 10", r.Capacity())
	}
}

func TestSavesSomeData(t *testing.T) {
	r := Ring{}
	r.SetCapacity(7)
	for i := 0; i <= 14; i++ {
		r.Enqueue(measure{float64(i)})
	}
	fmt.Println(r.head)
	fmt.Println(r.tail)

	for i := 0; i < 7; i++ {
		fmt.Println(r.Dequeue().Value())

	}
}
<commit_msg>fix test<commit_after>package ring

// "fmt"
import (
	"fmt"
	"testing"
	"time"
)

type measure struct {
	f float64
}

func (m measure) Value() float64 {
	return m.f
}

func (m measure) TimeStamp() time.Time {
	return time.Now()
}
func TestSetsSize(t *testing.T) {
	r := &Ring{}
	r.SetCapacity(10)
	if r.Capacity() != 10 {
		t.Fatal("Size of ring was not 10", r.Capacity())
	}
}

func TestSavesSomeData(t *testing.T) {
	r := Ring{}
	r.SetCapacity(7)
	for i := 0; i <= 14; i++ {
		r.Enqueue(measure{float64(i)})
	}
	fmt.Println(r.head)
	fmt.Println(r.tail)

	for i := 0; i < 7; i++ {
		fmt.Println(r.Dequeue().Value())

	}
}
<|endoftext|>


<commit_before>package server

import (
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/facette/facette/pkg/library"
	"github.com/facette/facette/pkg/utils"
	"github.com/facette/facette/thirdparty/github.com/fatih/set"
)

func (server *Server) handleCatalog(writer http.ResponseWriter, request *http.Request) {
	setHTTPCacheHeaders(writer)

	if strings.HasPrefix(request.URL.Path, urlCatalogPath+"origins/") {
		server.handleOrigin(writer, request)
	} else if strings.HasPrefix(request.URL.Path, urlCatalogPath+"sources/") {
		server.handleSource(writer, request)
	} else if strings.HasPrefix(request.URL.Path, urlCatalogPath+"metrics/") {
		server.handleMetric(writer, request)
	} else {
		server.handleResponse(writer, nil, http.StatusNotFound)
	}
}

func (server *Server) handleOrigin(writer http.ResponseWriter, request *http.Request) {
	originName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"origins/")

	if originName == "" {
		server.handleOriginList(writer, request)
		return
	}

	if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	} else if _, ok := server.Catalog.Origins[originName]; !ok {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	response := OriginResponse{
		Name:      originName,
		Connector: server.Config.Origins[originName].ConnectorSettings["type"],
		Updated:   server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleOriginList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), origin.Name) {
			continue
		}

		originSet.Add(origin.Name)
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(originSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}

func (server *Server) handleSource(writer http.ResponseWriter, request *http.Request) {
	sourceName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"sources/")

	if sourceName == "" {
		server.handleSourceList(writer, request)
		return
	} else if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if _, ok := origin.Sources[sourceName]; ok {
			originSet.Add(origin.Name)
		}
	}

	if originSet.Size() == 0 {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	origins := set.StringSlice(originSet)
	sort.Strings(origins)

	response := SourceResponse{
		Name:    sourceName,
		Origins: origins,
		Updated: server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleSourceList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originName := request.FormValue("origin")

	sourceSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if originName != "" && origin.Name != originName {
			continue
		}

		for key := range origin.Sources {
			if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), key) {
				continue
			}

			sourceSet.Add(key)
		}
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(sourceSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}

func (server *Server) handleMetric(writer http.ResponseWriter, request *http.Request) {
	metricName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"metrics/")

	if metricName == "" {
		server.handleMetricList(writer, request)
		return
	} else if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)
	sourceSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		for _, source := range origin.Sources {
			if _, ok := source.Metrics[metricName]; ok {
				originSet.Add(origin.Name)
				sourceSet.Add(source.Name)
			}
		}
	}

	if originSet.Size() == 0 {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	origins := set.StringSlice(originSet)
	sort.Strings(origins)

	sources := set.StringSlice(sourceSet)
	sort.Strings(sources)

	response := MetricResponse{
		Name:    metricName,
		Origins: origins,
		Sources: sources,
		Updated: server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleMetricList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originName := request.FormValue("origin")
	sourceName := request.FormValue("source")

	sourceSet := set.New(set.ThreadSafe)

	if strings.HasPrefix(sourceName, library.LibraryGroupPrefix) {
		for _, entryName := range server.Library.ExpandGroup(
			strings.TrimPrefix(sourceName, library.LibraryGroupPrefix),
			library.LibraryItemSourceGroup,
		) {
			sourceSet.Add(entryName)
		}
	} else if sourceName != "" {
		sourceSet.Add(sourceName)
	}

	metricSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if originName != "" && origin.Name != originName {
			continue
		}

		for _, source := range origin.Sources {
			if sourceName != "" && sourceSet.IsEmpty() || !sourceSet.IsEmpty() && !sourceSet.Has(source.Name) {
				continue
			}

			for key := range source.Metrics {
				if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), key) {
					continue
				}

				metricSet.Add(key)
			}
		}
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(metricSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}
<commit_msg>Fix forgotten `ConnectorSettings` rename<commit_after>package server

import (
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/facette/facette/pkg/library"
	"github.com/facette/facette/pkg/utils"
	"github.com/facette/facette/thirdparty/github.com/fatih/set"
)

func (server *Server) handleCatalog(writer http.ResponseWriter, request *http.Request) {
	setHTTPCacheHeaders(writer)

	if strings.HasPrefix(request.URL.Path, urlCatalogPath+"origins/") {
		server.handleOrigin(writer, request)
	} else if strings.HasPrefix(request.URL.Path, urlCatalogPath+"sources/") {
		server.handleSource(writer, request)
	} else if strings.HasPrefix(request.URL.Path, urlCatalogPath+"metrics/") {
		server.handleMetric(writer, request)
	} else {
		server.handleResponse(writer, nil, http.StatusNotFound)
	}
}

func (server *Server) handleOrigin(writer http.ResponseWriter, request *http.Request) {
	originName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"origins/")

	if originName == "" {
		server.handleOriginList(writer, request)
		return
	}

	if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	} else if _, ok := server.Catalog.Origins[originName]; !ok {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	response := OriginResponse{
		Name:      originName,
		Connector: server.Config.Origins[originName].Connector["type"],
		Updated:   server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleOriginList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), origin.Name) {
			continue
		}

		originSet.Add(origin.Name)
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(originSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}

func (server *Server) handleSource(writer http.ResponseWriter, request *http.Request) {
	sourceName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"sources/")

	if sourceName == "" {
		server.handleSourceList(writer, request)
		return
	} else if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if _, ok := origin.Sources[sourceName]; ok {
			originSet.Add(origin.Name)
		}
	}

	if originSet.Size() == 0 {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	origins := set.StringSlice(originSet)
	sort.Strings(origins)

	response := SourceResponse{
		Name:    sourceName,
		Origins: origins,
		Updated: server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleSourceList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originName := request.FormValue("origin")

	sourceSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if originName != "" && origin.Name != originName {
			continue
		}

		for key := range origin.Sources {
			if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), key) {
				continue
			}

			sourceSet.Add(key)
		}
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(sourceSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}

func (server *Server) handleMetric(writer http.ResponseWriter, request *http.Request) {
	metricName := strings.TrimPrefix(request.URL.Path, urlCatalogPath+"metrics/")

	if metricName == "" {
		server.handleMetricList(writer, request)
		return
	} else if response, status := server.parseShowRequest(writer, request); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originSet := set.New(set.ThreadSafe)
	sourceSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		for _, source := range origin.Sources {
			if _, ok := source.Metrics[metricName]; ok {
				originSet.Add(origin.Name)
				sourceSet.Add(source.Name)
			}
		}
	}

	if originSet.Size() == 0 {
		server.handleResponse(writer, serverResponse{mesgResourceNotFound}, http.StatusNotFound)
		return
	}

	origins := set.StringSlice(originSet)
	sort.Strings(origins)

	sources := set.StringSlice(sourceSet)
	sort.Strings(sources)

	response := MetricResponse{
		Name:    metricName,
		Origins: origins,
		Sources: sources,
		Updated: server.Catalog.Updated.Format(time.RFC3339),
	}

	server.handleResponse(writer, response, http.StatusOK)
}

func (server *Server) handleMetricList(writer http.ResponseWriter, request *http.Request) {
	var offset, limit int

	if response, status := server.parseListRequest(writer, request, &offset, &limit); status != http.StatusOK {
		server.handleResponse(writer, response, status)
		return
	}

	originName := request.FormValue("origin")
	sourceName := request.FormValue("source")

	sourceSet := set.New(set.ThreadSafe)

	if strings.HasPrefix(sourceName, library.LibraryGroupPrefix) {
		for _, entryName := range server.Library.ExpandGroup(
			strings.TrimPrefix(sourceName, library.LibraryGroupPrefix),
			library.LibraryItemSourceGroup,
		) {
			sourceSet.Add(entryName)
		}
	} else if sourceName != "" {
		sourceSet.Add(sourceName)
	}

	metricSet := set.New(set.ThreadSafe)

	for _, origin := range server.Catalog.Origins {
		if originName != "" && origin.Name != originName {
			continue
		}

		for _, source := range origin.Sources {
			if sourceName != "" && sourceSet.IsEmpty() || !sourceSet.IsEmpty() && !sourceSet.Has(source.Name) {
				continue
			}

			for key := range source.Metrics {
				if request.FormValue("filter") != "" && !utils.FilterMatch(request.FormValue("filter"), key) {
					continue
				}

				metricSet.Add(key)
			}
		}
	}

	response := &listResponse{
		list:   StringListResponse(set.StringSlice(metricSet)),
		offset: offset,
		limit:  limit,
	}

	server.applyResponseLimit(writer, request, response)

	server.handleResponse(writer, response.list, http.StatusOK)
}
<|endoftext|>


<commit_before>package ligno

// Log creates record and queues it for processing.
// Required parameters are level for record and event that occurred. Any
// additional parameters will be transformed to key-value pairs for record
// in order in which they were provided. There should be even number of them,
// but in case that there is on number of parameters, empty string is
// appended. Example:
//   defaultLog.Log(INFO, "User logged in", "user_id", user_id, "platform", PLATFORM_NAME)
// will be translated into log record with following keys:
//  {LEVEL: INFO", EVENT: "User logged in", "user_id": user_id, "platform": PLATFORM_NAME}
func Log(level Level, event string, pairs ...interface{}) {
	rootLogger.Log(2, level, event, pairs...)
}

// LogCtx adds provided message in specified level.
func LogCtx(level Level, message string, ctx Ctx) {
	rootLogger.LogCtx(2, level, message, ctx)
}

// Debug creates log record and queues it for processing with DEBUG level.
// Additional parameters have same semantics as in Log method.
func Debug(event string, pairs ...interface{}) {
	rootLogger.Log(2, DEBUG, event, pairs...)
}

// DebugCtx logs message in DEBUG level with provided context.
func DebugCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, DEBUG, message, ctx)
}

// Info creates log record and queues it for processing with INFO level.
// Additional parameters have same semantics as in Log method.
func Info(event string, pairs ...interface{}) {
	rootLogger.Log(2, INFO, event, pairs...)
}

// InfoCtx logs message in INFO level with provided context.
func InfoCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, INFO, message, ctx)
}

// Warning creates log record and queues it for processing with WARNING level.
// Additional parameters have same semantics as in Log method.
func Warning(event string, pairs ...interface{}) {
	rootLogger.Log(2, WARNING, event, pairs...)
}

// WarningCtx logs message in WARNING level with provided context.
func WarningCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, WARNING, message, ctx)
}

// Error creates log record and queues it for processing with ERROR level.
// Additional parameters have same semantics as in Log method.
func Error(event string, pairs ...interface{}) {
	rootLogger.Log(2, ERROR, event, pairs...)
}

// ErrorCtx logs message in ERROR level with provided context.
func ErrorCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, ERROR, message, ctx)
}

// Critical creates log record and queues it for processing with CRITICAL level.
// Additional parameters have same semantics as in Log method.
func Critical(event string, pairs ...interface{}) {
	rootLogger.Log(2, CRITICAL, event, pairs...)
}

// CriticalCtx logs message in CRITICAL level with provided context.
func CriticalCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, CRITICAL, message, ctx)
}

// SetHandler sets new handler for default logger.
func SetHandler(handler Handler) {
	rootLogger.SetHandler(handler)
}
<commit_msg>Implement stdlib interface for default logger<commit_after>package ligno

import (
	"fmt"
	"os"
)

// Log creates record and queues it for processing.
// Required parameters are level for record and event that occurred. Any
// additional parameters will be transformed to key-value pairs for record
// in order in which they were provided. There should be even number of them,
// but in case that there is on number of parameters, empty string is
// appended. Example:
//   defaultLog.Log(INFO, "User logged in", "user_id", user_id, "platform", PLATFORM_NAME)
// will be translated into log record with following keys:
//  {LEVEL: INFO", EVENT: "User logged in", "user_id": user_id, "platform": PLATFORM_NAME}
func Log(level Level, event string, pairs ...interface{}) {
	rootLogger.Log(2, level, event, pairs...)
}

// LogCtx adds provided message in specified level.
func LogCtx(level Level, message string, ctx Ctx) {
	rootLogger.LogCtx(2, level, message, ctx)
}

// Debug creates log record and queues it for processing with DEBUG level.
// Additional parameters have same semantics as in Log method.
func Debug(event string, pairs ...interface{}) {
	rootLogger.Log(2, DEBUG, event, pairs...)
}

// DebugCtx logs message in DEBUG level with provided context.
func DebugCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, DEBUG, message, ctx)
}

// Info creates log record and queues it for processing with INFO level.
// Additional parameters have same semantics as in Log method.
func Info(event string, pairs ...interface{}) {
	rootLogger.Log(2, INFO, event, pairs...)
}

// InfoCtx logs message in INFO level with provided context.
func InfoCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, INFO, message, ctx)
}

// Warning creates log record and queues it for processing with WARNING level.
// Additional parameters have same semantics as in Log method.
func Warning(event string, pairs ...interface{}) {
	rootLogger.Log(2, WARNING, event, pairs...)
}

// WarningCtx logs message in WARNING level with provided context.
func WarningCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, WARNING, message, ctx)
}

// Error creates log record and queues it for processing with ERROR level.
// Additional parameters have same semantics as in Log method.
func Error(event string, pairs ...interface{}) {
	rootLogger.Log(2, ERROR, event, pairs...)
}

// ErrorCtx logs message in ERROR level with provided context.
func ErrorCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, ERROR, message, ctx)
}

// Critical creates log record and queues it for processing with CRITICAL level.
// Additional parameters have same semantics as in Log method.
func Critical(event string, pairs ...interface{}) {
	rootLogger.Log(2, CRITICAL, event, pairs...)
}

// CriticalCtx logs message in CRITICAL level with provided context.
func CriticalCtx(message string, ctx Ctx) {
	rootLogger.LogCtx(2, CRITICAL, message, ctx)
}

// Printf formats message according to stdlib rules and logs it in INFO level.
func Printf(format string, v ...interface{}) {
	rootLogger.Log(2, INFO, fmt.Sprintf(format, v...))
}

// Print formats message according to stdlib rules and logs it in INFO level.
func Print(v ...interface{}) {
	rootLogger.Log(2, INFO, fmt.Sprint(v...))
}

// Printfln formats message according to stdlib rules and logs it in INFO level.
func Println(v ...interface{}) {
	rootLogger.Log(2, INFO, fmt.Sprintln(v...))
}

// Fatal formats message according to stdlib rules, logs it in CRITICAL level
// and exists application.
func Fatal(v ...interface{}) {
	rootLogger.Log(2, CRITICAL, fmt.Sprint(v...))
	os.Exit(1)
}

// Fatalf formats message according to stdlib rules, logs it in CRITICAL level
// and exists application.
func Fatalf(format string, v ...interface{}) {
	rootLogger.Log(2, CRITICAL, fmt.Sprintf(format, v...))
	os.Exit(1)
}

// Fatalln formats message according to stdlib rules, logs it in CRITICAL level
// and exists application.
func Fatalln(v ...interface{}) {
	rootLogger.Log(2, CRITICAL, fmt.Sprintln(v...))
	os.Exit(1)
}

// Panic formats message according to stdlib rules, logs it in CRITICAL level
// and panics.
func Panic(v ...interface{}) {
	s := fmt.Sprint(v...)
	rootLogger.Log(2, CRITICAL, s)
	panic(s)
}

// Panicf formats message according to stdlib rules, logs it in CRITICAL level
// and panics.
func Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	rootLogger.Log(2, CRITICAL, s)
	panic(s)
}

// Panicln formats message according to stdlib rules, logs it in CRITICAL level
// and panics.
func Panicln(v ...interface{}) {
	s := fmt.Sprintln(v...)
	rootLogger.Log(2, CRITICAL, s)
	panic(s)
}
// SetHandler sets new handler for default logger.
func SetHandler(handler Handler) {
	rootLogger.SetHandler(handler)
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package instances

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/golang/glog"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/apimachinery/pkg/util/sets"

	"k8s.io/ingress/controllers/gce/storage"
	"k8s.io/ingress/controllers/gce/utils"
)

const (
	// State string required by gce library to list all instances.
	allInstances = "ALL"
)

// Instances implements NodePool.
type Instances struct {
	cloud InstanceGroups
	// zones is a list of zones seeded by Kubernetes node zones.
	// TODO: we can figure this out.
	snapshotter storage.Snapshotter
	zoneLister
}

// NewNodePool creates a new node pool.
// - cloud: implements InstanceGroups, used to sync Kubernetes nodes with
//   members of the cloud InstanceGroup.
func NewNodePool(cloud InstanceGroups) NodePool {
	return &Instances{cloud, storage.NewInMemoryPool(), nil}
}

// Init initializes the instance pool. The given zoneLister is used to list
// all zones that require an instance group, and to lookup which zone a
// given Kubernetes node is in so we can add it to the right instance group.
func (i *Instances) Init(zl zoneLister) {
	i.zoneLister = zl
}

// AddInstanceGroup creates or gets an instance group if it doesn't exist
// and adds the given port to it. Returns a list of one instance group per zone,
// all of which have the exact same named port.
func (i *Instances) AddInstanceGroup(name string, port int64) ([]*compute.InstanceGroup, *compute.NamedPort, error) {
	igs := []*compute.InstanceGroup{}
	// TODO: move port naming to namer
	namedPort := &compute.NamedPort{Name: fmt.Sprintf("port%v", port), Port: port}

	zones, err := i.ListZones()
	if err != nil {
		return igs, namedPort, err
	}

	defer i.snapshotter.Add(name, struct{}{})
	for _, zone := range zones {
		ig, _ := i.Get(name, zone)
		var err error
		if ig == nil {
			glog.Infof("Creating instance group %v in zone %v", name, zone)
			if err = i.cloud.CreateInstanceGroup(&compute.InstanceGroup{Name: name}, zone); err != nil {
				return nil, nil, err
			}
			ig, err = i.cloud.GetInstanceGroup(name, zone)
			if err != nil {
				return nil, nil, err
			}
		} else {
			glog.V(3).Infof("Instance group %v already exists in zone %v, adding port %d to it", name, zone, port)
		}

		found := false
		for _, np := range ig.NamedPorts {
			if np.Port == port {
				glog.V(3).Infof("Instance group %v already has named port %+v", ig.Name, np)
				found = true
				break
			}
		}
		if !found {
			if err := i.cloud.SetNamedPortsOfInstanceGroup(ig.Name, zone, append(ig.NamedPorts, namedPort)); err != nil {
				return nil, nil, err
			}
		}
		igs = append(igs, ig)
	}
	return igs, namedPort, nil
}

// DeleteInstanceGroup deletes the given IG by name, from all zones.
func (i *Instances) DeleteInstanceGroup(name string) error {
	defer i.snapshotter.Delete(name)
	errs := []error{}

	zones, err := i.ListZones()
	if err != nil {
		return err
	}
	for _, zone := range zones {
		if err := i.cloud.DeleteInstanceGroup(name, zone); err != nil {
			if !utils.IsHTTPErrorCode(err, http.StatusNotFound) {
				errs = append(errs, err)
			}
		} else {
			glog.Infof("Deleted instance group %v in zone %v", name, zone)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// list lists all instances in all zones.
func (i *Instances) list(name string) (sets.String, error) {
	nodeNames := sets.NewString()
	zones, err := i.ListZones()
	if err != nil {
		return nodeNames, err
	}

	for _, zone := range zones {
		instances, err := i.cloud.ListInstancesInInstanceGroup(
			name, zone, allInstances)
		if err != nil {
			return nodeNames, err
		}
		for _, ins := range instances.Items {
			// TODO: If round trips weren't so slow one would be inclided
			// to GetInstance using this url and get the name.
			parts := strings.Split(ins.Instance, "/")
			nodeNames.Insert(parts[len(parts)-1])
		}
	}
	return nodeNames, nil
}

// Get returns the Instance Group by name.
func (i *Instances) Get(name, zone string) (*compute.InstanceGroup, error) {
	ig, err := i.cloud.GetInstanceGroup(name, zone)
	if err != nil {
		return nil, err
	}
	i.snapshotter.Add(name, struct{}{})
	return ig, nil
}

// splitNodesByZones takes a list of node names and returns a map of zone:node names.
// It figures out the zones by asking the zoneLister.
func (i *Instances) splitNodesByZone(names []string) map[string][]string {
	nodesByZone := map[string][]string{}
	for _, name := range names {
		zone, err := i.GetZoneForNode(name)
		if err != nil {
			glog.Errorf("Failed to get zones for %v: %v, skipping", name, err)
			continue
		}
		if _, ok := nodesByZone[zone]; !ok {
			nodesByZone[zone] = []string{}
		}
		nodesByZone[zone] = append(nodesByZone[zone], name)
	}
	return nodesByZone
}

// Add adds the given instances to the appropriately zoned Instance Group.
func (i *Instances) Add(groupName string, names []string) error {
	errs := []error{}
	for zone, nodeNames := range i.splitNodesByZone(names) {
		glog.V(1).Infof("Adding nodes %v to %v in zone %v", nodeNames, groupName, zone)
		if err := i.cloud.AddInstancesToInstanceGroup(groupName, zone, i.cloud.ToInstanceReferences(zone, nodeNames)); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// Remove removes the given instances from the appropriately zoned Instance Group.
func (i *Instances) Remove(groupName string, names []string) error {
	errs := []error{}
	for zone, nodeNames := range i.splitNodesByZone(names) {
		glog.V(1).Infof("Removing nodes %v from %v in zone %v", nodeNames, groupName, zone)
		if err := i.cloud.RemoveInstancesFromInstanceGroup(groupName, zone, i.cloud.ToInstanceReferences(zone, nodeNames)); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// Sync syncs kubernetes instances with the instances in the instance group.
func (i *Instances) Sync(nodes []string) (err error) {
	glog.V(4).Infof("Syncing nodes %v", nodes)

	defer func() {
		// The node pool is only responsible for syncing nodes to instance
		// groups. It never creates/deletes, so if an instance groups is
		// not found there's nothing it can do about it anyway. Most cases
		// this will happen because the backend pool has deleted the instance
		// group, however if it happens because a user deletes the IG by mistake
		// we should just wait till the backend pool fixes it.
		if utils.IsHTTPErrorCode(err, http.StatusNotFound) {
			glog.Infof("Node pool encountered a 404, ignoring: %v", err)
			err = nil
		}
	}()

	pool := i.snapshotter.Snapshot()
	for igName := range pool {
		gceNodes := sets.NewString()
		gceNodes, err = i.list(igName)
		if err != nil {
			return err
		}
		kubeNodes := sets.NewString(nodes...)

		// A node deleted via kubernetes could still exist as a gce vm. We don't
		// want to route requests to it. Similarly, a node added to kubernetes
		// needs to get added to the instance group so we do route requests to it.

		removeNodes := gceNodes.Difference(kubeNodes).List()
		addNodes := kubeNodes.Difference(gceNodes).List()
		if len(removeNodes) != 0 {
			glog.V(4).Infof("Removing nodes from IG: %v", removeNodes)
			if err = i.Remove(igName, removeNodes); err != nil {
				return err
			}
		}

		if len(addNodes) != 0 {
			glog.V(4).Infof("Adding nodes to IG: %v", removeNodes)
			if err = i.Add(igName, addNodes); err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Log correct list of nodes<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package instances

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/golang/glog"

	compute "google.golang.org/api/compute/v1"
	"k8s.io/apimachinery/pkg/util/sets"

	"k8s.io/ingress/controllers/gce/storage"
	"k8s.io/ingress/controllers/gce/utils"
)

const (
	// State string required by gce library to list all instances.
	allInstances = "ALL"
)

// Instances implements NodePool.
type Instances struct {
	cloud InstanceGroups
	// zones is a list of zones seeded by Kubernetes node zones.
	// TODO: we can figure this out.
	snapshotter storage.Snapshotter
	zoneLister
}

// NewNodePool creates a new node pool.
// - cloud: implements InstanceGroups, used to sync Kubernetes nodes with
//   members of the cloud InstanceGroup.
func NewNodePool(cloud InstanceGroups) NodePool {
	return &Instances{cloud, storage.NewInMemoryPool(), nil}
}

// Init initializes the instance pool. The given zoneLister is used to list
// all zones that require an instance group, and to lookup which zone a
// given Kubernetes node is in so we can add it to the right instance group.
func (i *Instances) Init(zl zoneLister) {
	i.zoneLister = zl
}

// AddInstanceGroup creates or gets an instance group if it doesn't exist
// and adds the given port to it. Returns a list of one instance group per zone,
// all of which have the exact same named port.
func (i *Instances) AddInstanceGroup(name string, port int64) ([]*compute.InstanceGroup, *compute.NamedPort, error) {
	igs := []*compute.InstanceGroup{}
	// TODO: move port naming to namer
	namedPort := &compute.NamedPort{Name: fmt.Sprintf("port%v", port), Port: port}

	zones, err := i.ListZones()
	if err != nil {
		return igs, namedPort, err
	}

	defer i.snapshotter.Add(name, struct{}{})
	for _, zone := range zones {
		ig, _ := i.Get(name, zone)
		var err error
		if ig == nil {
			glog.Infof("Creating instance group %v in zone %v", name, zone)
			if err = i.cloud.CreateInstanceGroup(&compute.InstanceGroup{Name: name}, zone); err != nil {
				return nil, nil, err
			}
			ig, err = i.cloud.GetInstanceGroup(name, zone)
			if err != nil {
				return nil, nil, err
			}
		} else {
			glog.V(3).Infof("Instance group %v already exists in zone %v, adding port %d to it", name, zone, port)
		}

		found := false
		for _, np := range ig.NamedPorts {
			if np.Port == port {
				glog.V(3).Infof("Instance group %v already has named port %+v", ig.Name, np)
				found = true
				break
			}
		}
		if !found {
			if err := i.cloud.SetNamedPortsOfInstanceGroup(ig.Name, zone, append(ig.NamedPorts, namedPort)); err != nil {
				return nil, nil, err
			}
		}
		igs = append(igs, ig)
	}
	return igs, namedPort, nil
}

// DeleteInstanceGroup deletes the given IG by name, from all zones.
func (i *Instances) DeleteInstanceGroup(name string) error {
	defer i.snapshotter.Delete(name)
	errs := []error{}

	zones, err := i.ListZones()
	if err != nil {
		return err
	}
	for _, zone := range zones {
		if err := i.cloud.DeleteInstanceGroup(name, zone); err != nil {
			if !utils.IsHTTPErrorCode(err, http.StatusNotFound) {
				errs = append(errs, err)
			}
		} else {
			glog.Infof("Deleted instance group %v in zone %v", name, zone)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// list lists all instances in all zones.
func (i *Instances) list(name string) (sets.String, error) {
	nodeNames := sets.NewString()
	zones, err := i.ListZones()
	if err != nil {
		return nodeNames, err
	}

	for _, zone := range zones {
		instances, err := i.cloud.ListInstancesInInstanceGroup(
			name, zone, allInstances)
		if err != nil {
			return nodeNames, err
		}
		for _, ins := range instances.Items {
			// TODO: If round trips weren't so slow one would be inclided
			// to GetInstance using this url and get the name.
			parts := strings.Split(ins.Instance, "/")
			nodeNames.Insert(parts[len(parts)-1])
		}
	}
	return nodeNames, nil
}

// Get returns the Instance Group by name.
func (i *Instances) Get(name, zone string) (*compute.InstanceGroup, error) {
	ig, err := i.cloud.GetInstanceGroup(name, zone)
	if err != nil {
		return nil, err
	}
	i.snapshotter.Add(name, struct{}{})
	return ig, nil
}

// splitNodesByZones takes a list of node names and returns a map of zone:node names.
// It figures out the zones by asking the zoneLister.
func (i *Instances) splitNodesByZone(names []string) map[string][]string {
	nodesByZone := map[string][]string{}
	for _, name := range names {
		zone, err := i.GetZoneForNode(name)
		if err != nil {
			glog.Errorf("Failed to get zones for %v: %v, skipping", name, err)
			continue
		}
		if _, ok := nodesByZone[zone]; !ok {
			nodesByZone[zone] = []string{}
		}
		nodesByZone[zone] = append(nodesByZone[zone], name)
	}
	return nodesByZone
}

// Add adds the given instances to the appropriately zoned Instance Group.
func (i *Instances) Add(groupName string, names []string) error {
	errs := []error{}
	for zone, nodeNames := range i.splitNodesByZone(names) {
		glog.V(1).Infof("Adding nodes %v to %v in zone %v", nodeNames, groupName, zone)
		if err := i.cloud.AddInstancesToInstanceGroup(groupName, zone, i.cloud.ToInstanceReferences(zone, nodeNames)); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// Remove removes the given instances from the appropriately zoned Instance Group.
func (i *Instances) Remove(groupName string, names []string) error {
	errs := []error{}
	for zone, nodeNames := range i.splitNodesByZone(names) {
		glog.V(1).Infof("Removing nodes %v from %v in zone %v", nodeNames, groupName, zone)
		if err := i.cloud.RemoveInstancesFromInstanceGroup(groupName, zone, i.cloud.ToInstanceReferences(zone, nodeNames)); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) == 0 {
		return nil
	}
	return fmt.Errorf("%v", errs)
}

// Sync syncs kubernetes instances with the instances in the instance group.
func (i *Instances) Sync(nodes []string) (err error) {
	glog.V(4).Infof("Syncing nodes %v", nodes)

	defer func() {
		// The node pool is only responsible for syncing nodes to instance
		// groups. It never creates/deletes, so if an instance groups is
		// not found there's nothing it can do about it anyway. Most cases
		// this will happen because the backend pool has deleted the instance
		// group, however if it happens because a user deletes the IG by mistake
		// we should just wait till the backend pool fixes it.
		if utils.IsHTTPErrorCode(err, http.StatusNotFound) {
			glog.Infof("Node pool encountered a 404, ignoring: %v", err)
			err = nil
		}
	}()

	pool := i.snapshotter.Snapshot()
	for igName := range pool {
		gceNodes := sets.NewString()
		gceNodes, err = i.list(igName)
		if err != nil {
			return err
		}
		kubeNodes := sets.NewString(nodes...)

		// A node deleted via kubernetes could still exist as a gce vm. We don't
		// want to route requests to it. Similarly, a node added to kubernetes
		// needs to get added to the instance group so we do route requests to it.

		removeNodes := gceNodes.Difference(kubeNodes).List()
		addNodes := kubeNodes.Difference(gceNodes).List()
		if len(removeNodes) != 0 {
			glog.V(4).Infof("Removing nodes from IG: %v", removeNodes)
			if err = i.Remove(igName, removeNodes); err != nil {
				return err
			}
		}

		if len(addNodes) != 0 {
			glog.V(4).Infof("Adding nodes to IG: %v", addNodes)
			if err = i.Add(igName, addNodes); err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>