<commit_before>package ss

import (
	"fmt"
	"io"
	"net"
	"os/user"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"github.com/olekukonko/tablewriter"
)

type TransportProtocol int

const (
	TCP TransportProtocol = iota
	TCP6
)

var (
	stringToProtocol = map[string]TransportProtocol{
		"tcp":  TCP,
		"tcp6": TCP6,
	}
	protocolToString = map[TransportProtocol]string{
		TCP:  "tcp",
		TCP6: "tcp6",
	}
)

// Process describes OS processes.
type Process struct {
	Protocol string

	Program string
	PID     int

	LocalIP   string
	LocalPort string

	RemoteIP   string
	RemotePort string

	State string

	User user.User
}

var processMembers = []string{
	"PROTOCOL",
	"PROGRAM",
	"PID",
	"LOCAL_ADDR",
	"REMOTE_ADDR",
	"USER",
}

// String describes Process type.
func (p Process) String() string {
	return fmt.Sprintf("Protocol: '%s' | Program: '%s' | PID: '%d' | LocalAddr: '%s%s' | RemoteAddr: '%s%s' | State: '%s' | User: '%+v'",
		p.Protocol,
		p.Program,
		p.PID,
		p.LocalIP,
		p.LocalPort,
		p.RemoteIP,
		p.RemotePort,
		p.State,
		p.User,
	)
}

// Kill kills all processes in arguments.
func Kill(w io.Writer, ps ...Process) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Fprintln(w, "Kill:", err)
		}
	}()

	pidToKill := make(map[int]string)
	pids := []int{}
	for _, p := range ps {
		if _, ok := pidToKill[p.PID]; !ok {
			pidToKill[p.PID] = p.Program
			pids = append(pids, p.PID)
		}
	}
	sort.Ints(pids)

	for _, pid := range pids {
		fmt.Fprintf(w, "syscall.Kill: %s [PID: %d]\n", pidToKill[pid], pid)
		if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
			fmt.Fprintln(w, "Kill:", err)
		}
	}
}

// parseLittleEndianIpv4 parses hexadecimal ipv4 IP addresses.
// For example, it converts '0101007F:0035' into string.
// It assumes that the system has little endian order.
func parseLittleEndianIpv4(s string) (string, string, error) {
	arr := strings.Split(s, ":")
	if len(arr) != 2 {
		return "", "", fmt.Errorf("cannot parse ipv4 %s", s)
	}
	if len(arr[0]) != 8 {
		return "", "", fmt.Errorf("cannot parse ipv4 ip %s", arr[0])
	}
	if len(arr[1]) != 4 {
		return "", "", fmt.Errorf("cannot parse ipv4 port %s", arr[1])
	}

	d0, err := strconv.ParseInt(arr[0][6:8], 16, 32)
	if err != nil {
		return "", "", err
	}
	d1, err := strconv.ParseInt(arr[0][4:6], 16, 32)
	if err != nil {
		return "", "", err
	}
	d2, err := strconv.ParseInt(arr[0][2:4], 16, 32)
	if err != nil {
		return "", "", err
	}
	d3, err := strconv.ParseInt(arr[0][0:2], 16, 32)
	if err != nil {
		return "", "", err
	}
	ip := fmt.Sprintf("%d.%d.%d.%d", d0, d1, d2, d3)

	p0, err := strconv.ParseInt(arr[1], 16, 32)
	if err != nil {
		return "", "", err
	}
	port := fmt.Sprintf("%d", p0)

	return ip, ":" + port, nil
}

// parseLittleEndianIpv6 parses hexadecimal ipv6 IP addresses.
// For example, it converts '4506012691A700C165EB1DE1F912918C:8BDA'
// into string.
// It assumes that the system has little endian order.
func parseLittleEndianIpv6(s string) (string, string, error) {
	arr := strings.Split(s, ":")
	if len(arr) != 2 {
		return "", "", fmt.Errorf("cannot parse ipv6 %s", s)
	}
	if len(arr[0]) != 32 {
		return "", "", fmt.Errorf("cannot parse ipv6 ip %s", arr[0])
	}
	if len(arr[1]) != 4 {
		return "", "", fmt.Errorf("cannot parse ipv6 port %s", arr[1])
	}

	// 32 characters, reverse by 2 characters
	ip := ""
	for i := 32; i > 0; i -= 2 {
		ip += fmt.Sprintf("%s", arr[0][i-2:i])
		if (len(ip)+1)%5 == 0 && len(ip) != 39 {
			ip += ":"
		}
	}

	p0, err := strconv.ParseInt(arr[1], 16, 32)
	if err != nil {
		return "", "", err
	}
	port := fmt.Sprintf("%d", p0)

	return ip, ":" + port, nil
}

// List lists all processes. filter is used to return Processes
// that matches the member values in filter struct.
func List(filter *Process, opts ...TransportProtocol) ([]Process, error) {
	rs := []Process{}
	for _, opt := range opts {

		ps, err := list(opt)
		if err != nil {
			return nil, err
		}

		if filter == nil {
			rs = append(rs, ps...)
			continue
		}

		pCh, done := make(chan Process), make(chan struct{})
		fv := *filter

		for _, p := range ps {

			go func(ft, process Process) {

				if !ft.Match(process) {
					done <- struct{}{}
					return
				}
				pCh <- process

			}(fv, p)

		}

		cn := 0
		ns := []Process{}
		for cn != len(ps) {
			select {
			case p := <-pCh:
				ns = append(ns, p)
				cn++
			case <-done:
				cn++
			}
		}

		close(pCh)
		close(done)

		rs = append(rs, ns...)
	}
	return rs, nil
}

// Match returns true if the Process matches the filter.
func (filter Process) Match(p Process) bool {
	if filter.Protocol != "" {
		if p.Protocol != filter.Protocol {
			return false
		}
	}
	// matches the suffix
	if filter.Program != "" {
		if !strings.HasSuffix(p.Program, filter.Program) {
			return false
		}
	}
	if filter.PID != 0 {
		if p.PID != filter.PID {
			return false
		}
	}
	if filter.LocalIP != "" {
		if p.LocalIP != filter.LocalIP {
			return false
		}
	}
	if filter.LocalPort != "" {
		if p.LocalPort != filter.LocalPort {
			return false
		}
	}
	if filter.RemoteIP != "" {
		if p.RemoteIP != filter.RemoteIP {
			return false
		}
	}
	if filter.RemotePort != "" {
		if p.RemotePort != filter.RemotePort {
			return false
		}
	}
	if filter.State != "" {
		if p.State != filter.State {
			return false
		}
	}
	// currently only support user name
	if filter.User.Username != "" {
		if p.User.Username != filter.User.Username {
			return false
		}
	}
	return true
}

// WriteToTable writes slice of Processes to ASCII table.
func WriteToTable(w io.Writer, ps ...Process) {
	table := tablewriter.NewWriter(w)
	table.SetHeader(processMembers)

	rows := make([][]string, len(ps))
	for i, p := range ps {
		sl := make([]string, len(processMembers))
		sl[0] = p.Protocol
		sl[1] = p.Program
		sl[2] = strconv.Itoa(p.PID)
		sl[3] = p.LocalIP + p.LocalPort
		sl[4] = p.RemoteIP + p.RemotePort
		sl[5] = p.User.Name
		rows[i] = sl
	}

	by(
		rows,
		makeAscendingFunc(0), // PROTOCOL
		makeAscendingFunc(1), // PROGRAM
		makeAscendingFunc(2), // PID
		makeAscendingFunc(3), // LOCAL_ADDR
		makeAscendingFunc(5), // USER
	).Sort(rows)

	for _, row := range rows {
		table.Append(row)
	}

	table.Render()
}

// ListPorts lists all ports that are being used.
func ListPorts(filter *Process, opts ...TransportProtocol) map[string]struct{} {
	rm := make(map[string]struct{})
	ps, _ := List(filter, opts...)
	for _, p := range ps {
		rm[p.LocalPort] = struct{}{}
		rm[p.RemotePort] = struct{}{}
	}
	return rm
}

type Ports struct {
	mu        sync.Mutex // guards the following
	beingUsed map[string]struct{}
}

func NewPorts() *Ports {
	p := &Ports{}
	p.beingUsed = make(map[string]struct{})
	return p
}

func (p *Ports) Exist(port string) bool {
	p.mu.Lock()
	defer p.mu.Unlock()
	_, ok := p.beingUsed[port]
	return ok
}

func (p *Ports) Add(ports ...string) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for _, port := range ports {
		p.beingUsed[port] = struct{}{}
	}
}

func (p *Ports) Free(ports ...string) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for _, port := range ports {
		delete(p.beingUsed, port)
	}
}

// Refresh refreshes ports that are being used by the OS.
// You would use as follows:
//
//	var (
//		globalPorts     = NewPorts()
//		refreshInterval = 10 * time.Second
//	)
//	func init() {
//		globalPorts.Refresh()
//		go func() {
//			for {
//				select {
//				case <-time.After(refreshInterval):
//					globalPorts.Refresh()
//				}
//			}
//		}()
//	}
//
func (p *Ports) Refresh() {
	p.mu.Lock()
	p.beingUsed = ListPorts(nil, TCP, TCP6)
	p.mu.Unlock()
}

func getFreePort(opts ...TransportProtocol) (string, error) {
	fp := ""
	var errMsg error
	for _, opt := range opts {
		protocolStr := ""
		switch opt {
		case TCP:
			protocolStr = "tcp"
		case TCP6:
			protocolStr = "tcp6"
		}
		addr, err := net.ResolveTCPAddr(protocolStr, "localhost:0")
		if err != nil {
			errMsg = err
			continue
		}
		l, err := net.ListenTCP(protocolStr, addr)
		if err != nil {
			errMsg = err
			continue
		}
		pd := l.Addr().(*net.TCPAddr).Port
		l.Close()
		fp = ":" + strconv.Itoa(pd)
		break
	}
	return fp, errMsg
}

// GetFreePorts returns multiple free ports from OS.
func GetFreePorts(num int, opts ...TransportProtocol) ([]string, error) {
	rm := make(map[string]struct{})
	for len(rm) != num {
		p, err := getFreePort(opts...)
		if err != nil {
			return nil, err
		}
		rm[p] = struct{}{}
	}
	rs := []string{}
	for p := range rm {
		rs = append(rs, p)
	}
	sort.Strings(rs)
	return rs, nil
}
<commit_msg>ss: return error for too many free port tries<commit_after>package ss

import (
	"fmt"
	"io"
	"net"
	"os/user"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"github.com/olekukonko/tablewriter"
)

type TransportProtocol int

const (
	TCP TransportProtocol = iota
	TCP6
)

var (
	stringToProtocol = map[string]TransportProtocol{
		"tcp":  TCP,
		"tcp6": TCP6,
	}
	protocolToString = map[TransportProtocol]string{
		TCP:  "tcp",
		TCP6: "tcp6",
	}
)

// Process describes OS processes.
type Process struct {
	Protocol string

	Program string
	PID     int

	LocalIP   string
	LocalPort string

	RemoteIP   string
	RemotePort string

	State string

	User user.User
}

var processMembers = []string{
	"PROTOCOL",
	"PROGRAM",
	"PID",
	"LOCAL_ADDR",
	"REMOTE_ADDR",
	"USER",
}

// String describes Process type.
func (p Process) String() string {
	return fmt.Sprintf("Protocol: '%s' | Program: '%s' | PID: '%d' | LocalAddr: '%s%s' | RemoteAddr: '%s%s' | State: '%s' | User: '%+v'",
		p.Protocol,
		p.Program,
		p.PID,
		p.LocalIP,
		p.LocalPort,
		p.RemoteIP,
		p.RemotePort,
		p.State,
		p.User,
	)
}

// Kill kills all processes in arguments.
func Kill(w io.Writer, ps ...Process) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Fprintln(w, "Kill:", err)
		}
	}()

	pidToKill := make(map[int]string)
	pids := []int{}
	for _, p := range ps {
		if _, ok := pidToKill[p.PID]; !ok {
			pidToKill[p.PID] = p.Program
			pids = append(pids, p.PID)
		}
	}
	sort.Ints(pids)

	for _, pid := range pids {
		fmt.Fprintf(w, "syscall.Kill: %s [PID: %d]\n", pidToKill[pid], pid)
		if err := syscall.Kill(pid, syscall.SIGKILL); err != nil {
			fmt.Fprintln(w, "Kill:", err)
		}
	}
}

// parseLittleEndianIpv4 parses hexadecimal ipv4 IP addresses.
// For example, it converts '0101007F:0035' into string.
// It assumes that the system has little endian order.
func parseLittleEndianIpv4(s string) (string, string, error) {
	arr := strings.Split(s, ":")
	if len(arr) != 2 {
		return "", "", fmt.Errorf("cannot parse ipv4 %s", s)
	}
	if len(arr[0]) != 8 {
		return "", "", fmt.Errorf("cannot parse ipv4 ip %s", arr[0])
	}
	if len(arr[1]) != 4 {
		return "", "", fmt.Errorf("cannot parse ipv4 port %s", arr[1])
	}

	d0, err := strconv.ParseInt(arr[0][6:8], 16, 32)
	if err != nil {
		return "", "", err
	}
	d1, err := strconv.ParseInt(arr[0][4:6], 16, 32)
	if err != nil {
		return "", "", err
	}
	d2, err := strconv.ParseInt(arr[0][2:4], 16, 32)
	if err != nil {
		return "", "", err
	}
	d3, err := strconv.ParseInt(arr[0][0:2], 16, 32)
	if err != nil {
		return "", "", err
	}
	ip := fmt.Sprintf("%d.%d.%d.%d", d0, d1, d2, d3)

	p0, err := strconv.ParseInt(arr[1], 16, 32)
	if err != nil {
		return "", "", err
	}
	port := fmt.Sprintf("%d", p0)

	return ip, ":" + port, nil
}

// parseLittleEndianIpv6 parses hexadecimal ipv6 IP addresses.
// For example, it converts '4506012691A700C165EB1DE1F912918C:8BDA'
// into string.
// It assumes that the system has little endian order.
func parseLittleEndianIpv6(s string) (string, string, error) {
	arr := strings.Split(s, ":")
	if len(arr) != 2 {
		return "", "", fmt.Errorf("cannot parse ipv6 %s", s)
	}
	if len(arr[0]) != 32 {
		return "", "", fmt.Errorf("cannot parse ipv6 ip %s", arr[0])
	}
	if len(arr[1]) != 4 {
		return "", "", fmt.Errorf("cannot parse ipv6 port %s", arr[1])
	}

	// 32 characters, reverse by 2 characters
	ip := ""
	for i := 32; i > 0; i -= 2 {
		ip += fmt.Sprintf("%s", arr[0][i-2:i])
		if (len(ip)+1)%5 == 0 && len(ip) != 39 {
			ip += ":"
		}
	}

	p0, err := strconv.ParseInt(arr[1], 16, 32)
	if err != nil {
		return "", "", err
	}
	port := fmt.Sprintf("%d", p0)

	return ip, ":" + port, nil
}

// List lists all processes. filter is used to return Processes
// that matches the member values in filter struct.
func List(filter *Process, opts ...TransportProtocol) ([]Process, error) {
	rs := []Process{}
	for _, opt := range opts {

		ps, err := list(opt)
		if err != nil {
			return nil, err
		}

		if filter == nil {
			rs = append(rs, ps...)
			continue
		}

		pCh, done := make(chan Process), make(chan struct{})
		fv := *filter

		for _, p := range ps {

			go func(ft, process Process) {

				if !ft.Match(process) {
					done <- struct{}{}
					return
				}
				pCh <- process

			}(fv, p)

		}

		cn := 0
		ns := []Process{}
		for cn != len(ps) {
			select {
			case p := <-pCh:
				ns = append(ns, p)
				cn++
			case <-done:
				cn++
			}
		}

		close(pCh)
		close(done)

		rs = append(rs, ns...)
	}
	return rs, nil
}

// Match returns true if the Process matches the filter.
func (filter Process) Match(p Process) bool {
	if filter.Protocol != "" {
		if p.Protocol != filter.Protocol {
			return false
		}
	}
	// matches the suffix
	if filter.Program != "" {
		if !strings.HasSuffix(p.Program, filter.Program) {
			return false
		}
	}
	if filter.PID != 0 {
		if p.PID != filter.PID {
			return false
		}
	}
	if filter.LocalIP != "" {
		if p.LocalIP != filter.LocalIP {
			return false
		}
	}
	if filter.LocalPort != "" {
		if p.LocalPort != filter.LocalPort {
			return false
		}
	}
	if filter.RemoteIP != "" {
		if p.RemoteIP != filter.RemoteIP {
			return false
		}
	}
	if filter.RemotePort != "" {
		if p.RemotePort != filter.RemotePort {
			return false
		}
	}
	if filter.State != "" {
		if p.State != filter.State {
			return false
		}
	}
	// currently only support user name
	if filter.User.Username != "" {
		if p.User.Username != filter.User.Username {
			return false
		}
	}
	return true
}

// WriteToTable writes slice of Processes to ASCII table.
func WriteToTable(w io.Writer, ps ...Process) {
	table := tablewriter.NewWriter(w)
	table.SetHeader(processMembers)

	rows := make([][]string, len(ps))
	for i, p := range ps {
		sl := make([]string, len(processMembers))
		sl[0] = p.Protocol
		sl[1] = p.Program
		sl[2] = strconv.Itoa(p.PID)
		sl[3] = p.LocalIP + p.LocalPort
		sl[4] = p.RemoteIP + p.RemotePort
		sl[5] = p.User.Name
		rows[i] = sl
	}

	by(
		rows,
		makeAscendingFunc(0), // PROTOCOL
		makeAscendingFunc(1), // PROGRAM
		makeAscendingFunc(2), // PID
		makeAscendingFunc(3), // LOCAL_ADDR
		makeAscendingFunc(5), // USER
	).Sort(rows)

	for _, row := range rows {
		table.Append(row)
	}

	table.Render()
}

// ListPorts lists all ports that are being used.
func ListPorts(filter *Process, opts ...TransportProtocol) map[string]struct{} {
	rm := make(map[string]struct{})
	ps, _ := List(filter, opts...)
	for _, p := range ps {
		rm[p.LocalPort] = struct{}{}
		rm[p.RemotePort] = struct{}{}
	}
	return rm
}

type Ports struct {
	mu        sync.Mutex // guards the following
	beingUsed map[string]struct{}
}

func NewPorts() *Ports {
	p := &Ports{}
	p.beingUsed = make(map[string]struct{})
	return p
}

func (p *Ports) Exist(port string) bool {
	p.mu.Lock()
	defer p.mu.Unlock()
	_, ok := p.beingUsed[port]
	return ok
}

func (p *Ports) Add(ports ...string) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for _, port := range ports {
		p.beingUsed[port] = struct{}{}
	}
}

func (p *Ports) Free(ports ...string) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for _, port := range ports {
		delete(p.beingUsed, port)
	}
}

// Refresh refreshes ports that are being used by the OS.
// You would use as follows:
//
//	var (
//		globalPorts     = NewPorts()
//		refreshInterval = 10 * time.Second
//	)
//	func init() {
//		globalPorts.Refresh()
//		go func() {
//			for {
//				select {
//				case <-time.After(refreshInterval):
//					globalPorts.Refresh()
//				}
//			}
//		}()
//	}
//
func (p *Ports) Refresh() {
	p.mu.Lock()
	p.beingUsed = ListPorts(nil, TCP, TCP6)
	p.mu.Unlock()
}

func getFreePort(opts ...TransportProtocol) (string, error) {
	fp := ""
	var errMsg error
	for _, opt := range opts {
		protocolStr := ""
		switch opt {
		case TCP:
			protocolStr = "tcp"
		case TCP6:
			protocolStr = "tcp6"
		}
		addr, err := net.ResolveTCPAddr(protocolStr, "localhost:0")
		if err != nil {
			errMsg = err
			continue
		}
		l, err := net.ListenTCP(protocolStr, addr)
		if err != nil {
			errMsg = err
			continue
		}
		pd := l.Addr().(*net.TCPAddr).Port
		l.Close()
		fp = ":" + strconv.Itoa(pd)
		break
	}
	return fp, errMsg
}

// GetFreePorts returns multiple free ports from OS.
func GetFreePorts(num int, opts ...TransportProtocol) ([]string, error) {
	try := 0
	rm := make(map[string]struct{})
	for len(rm) != num {
		if try > 150 {
			return nil, fmt.Errorf("too many tries to find free ports")
		}
		try++
		p, err := getFreePort(opts...)
		if err != nil {
			return nil, err
		}
		rm[p] = struct{}{}
	}
	rs := []string{}
	for p := range rm {
		rs = append(rs, p)
	}
	sort.Strings(rs)
	return rs, nil
}
<|endoftext|>


<commit_before>package ss

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	baseURL      = "http://www.screenscraper.fr/"
	gameInfoPath = "api/jeuInfos.php"
)

// ErrNotFound is the error returned when a ROM isn't found.
var ErrNotFound = errors.New("not found")

// DevInfo is the information about the developer and used across APIs.
type DevInfo struct {
	ID       string
	Password string
	Name     string
}

// UserInfo is information about the user making the call.
type UserInfo struct {
	ID       string
	Password string
}

// GameInfoReq is the information we use in the GameInfo command.
type GameInfoReq struct {
	Name    string
	SHA1    string
	RomType string
}

// GameNames is the name in many languages.
type GameNames struct {
	Original string `xml:"nom_ss"`
	EN       string `xml:"nom_us"`
	FR       string `xml:"nom_fr"`
	DE       string `xml:"nom_de"`
	ES       string `xml:"nom_es"`
	PT       string `xml:"nom_pt"`
}

// GameDesc is the desc in many languages.
type GameDesc struct {
	EN string `xml:"synopsis_en"`
	FR string `xml:"synopsis_fr"`
	DE string `xml:"synopsis_de"`
	ES string `xml:"synopsis_es"`
	PT string `xml:"synopsis_pt"`
}

// GameGenre is the genre in many languages.
type GameGenre struct {
	EN []string `xml:"genres_en>genre_en"`
	FR []string `xml:"genres_fr>genre_fr"`
	DE []string `xml:"genres_de>genre_de"`
	ES []string `xml:"genres_es>genre_es"`
	PT []string `xml:"genres_pt>genre_pt"`
}

// GameMedia is the media for many regions.
type GameMedia struct {
	ScreenShot    string `xml:"media_screenshot"`
	ScreenMarquee string `xml:"media_screenmarquee"`
	Marquee       string `xml:"media_marquee"`
	BoxUS         string `xml:"media_boxs>media_boxs2d>media_box2d_us"`
	BoxFR         string `xml:"media_boxs>media_boxs2d>media_box2d_fr"`
	BoxEU         string `xml:"media_boxs>media_boxs2d>media_box2d_eu"`
	BoxJP         string `xml:"media_boxs>media_boxs2d>media_box2d_jp"`
	BoxXX         string `xml:"media_boxs>media_boxs2d>media_box2d_xx"`
	Box3DUS       string `xml:"media_boxs>media_boxs3d>media_box3d_us"`
	Box3DFR       string `xml:"media_boxs>media_boxs3d>media_box3d_fr"`
	Box3DEU       string `xml:"media_boxs>media_boxs3d>media_box3d_eu"`
	Box3DJP       string `xml:"media_boxs>media_boxs3d>media_box3d_jp"`
	Box3DXX       string `xml:"media_boxs>media_boxs3d>media_box3d_xx"`
}

// GameDates is the date for many regions.
type GameDates struct {
	US string `xml:"date_us"`
	FR string `xml:"date_fr"`
	EU string `xml:"date_eu"`
	JP string `xml:"date_jp"`
	XX string `xml:"date_xx"`
}

// Game represents a game in SS.
type Game struct {
	ID        int       `xml:"id"`
	Name      string    `xml:"nom"`
	Names     GameNames `xml:"noms"`
	Region    string    `xml:"region"`
	Publisher string    `xml:"editeur"`
	Developer string    `xml:"developpeur"`
	Players   string    `xml:"joueurs"`
	Rating    string    `xml:"note"`
	Desc      GameDesc  `xml:"synopsis"`
	Genre     GameGenre `xml:"genres"`
	Media     GameMedia `xml:"medias"`
	Dates     GameDates `xml:"dates"`
}

// GameInfoResp is the response from GameInfo.
type GameInfoResp struct {
	Game Game `xml:"jeu"`
}

// GameInfo is the call to get game info.
func GameInfo(dev DevInfo, user UserInfo, req GameInfoReq) (*GameInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = gameInfoPath
	q := url.Values{}
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	if user.ID != "" {
		q.Set("ssid", user.ID)
	}
	if user.Password != "" {
		q.Set("sspassword", user.Password)
	}
	if req.SHA1 != "" {
		q.Set("sha1", req.SHA1)
	}
	if req.RomType == "" {
		q.Set("romtype", "rom")
	} else {
		q.Set("romtype", req.RomType)
	}
	if req.Name != "" {
		q.Set("romnom", req.Name)
	}
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		return nil, fmt.Errorf("getting game url:%s, error:%s", u, err)
	}
	defer resp.Body.Close()
	r := &GameInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if bytes.HasPrefix(b, []byte("Erreur : Rom/Iso/Dossier non trouv")) {
		return nil, ErrNotFound
	}
	if err := xml.Unmarshal(b, r); err != nil {
		return nil, err
	}
	return r, nil
}
<commit_msg>mask devid/pass when throwing error<commit_after>package ss

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
)

const (
	baseURL      = "http://www.screenscraper.fr/"
	gameInfoPath = "api/jeuInfos.php"
)

// ErrNotFound is the error returned when a ROM isn't found.
var ErrNotFound = errors.New("not found")

// DevInfo is the information about the developer and used across APIs.
type DevInfo struct {
	ID       string
	Password string
	Name     string
}

// UserInfo is information about the user making the call.
type UserInfo struct {
	ID       string
	Password string
}

// GameInfoReq is the information we use in the GameInfo command.
type GameInfoReq struct {
	Name    string
	SHA1    string
	RomType string
}

// GameNames is the name in many languages.
type GameNames struct {
	Original string `xml:"nom_ss"`
	EN       string `xml:"nom_us"`
	FR       string `xml:"nom_fr"`
	DE       string `xml:"nom_de"`
	ES       string `xml:"nom_es"`
	PT       string `xml:"nom_pt"`
}

// GameDesc is the desc in many languages.
type GameDesc struct {
	EN string `xml:"synopsis_en"`
	FR string `xml:"synopsis_fr"`
	DE string `xml:"synopsis_de"`
	ES string `xml:"synopsis_es"`
	PT string `xml:"synopsis_pt"`
}

// GameGenre is the genre in many languages.
type GameGenre struct {
	EN []string `xml:"genres_en>genre_en"`
	FR []string `xml:"genres_fr>genre_fr"`
	DE []string `xml:"genres_de>genre_de"`
	ES []string `xml:"genres_es>genre_es"`
	PT []string `xml:"genres_pt>genre_pt"`
}

// GameMedia is the media for many regions.
type GameMedia struct {
	ScreenShot    string `xml:"media_screenshot"`
	ScreenMarquee string `xml:"media_screenmarquee"`
	Marquee       string `xml:"media_marquee"`
	BoxUS         string `xml:"media_boxs>media_boxs2d>media_box2d_us"`
	BoxFR         string `xml:"media_boxs>media_boxs2d>media_box2d_fr"`
	BoxEU         string `xml:"media_boxs>media_boxs2d>media_box2d_eu"`
	BoxJP         string `xml:"media_boxs>media_boxs2d>media_box2d_jp"`
	BoxXX         string `xml:"media_boxs>media_boxs2d>media_box2d_xx"`
	Box3DUS       string `xml:"media_boxs>media_boxs3d>media_box3d_us"`
	Box3DFR       string `xml:"media_boxs>media_boxs3d>media_box3d_fr"`
	Box3DEU       string `xml:"media_boxs>media_boxs3d>media_box3d_eu"`
	Box3DJP       string `xml:"media_boxs>media_boxs3d>media_box3d_jp"`
	Box3DXX       string `xml:"media_boxs>media_boxs3d>media_box3d_xx"`
}

// GameDates is the date for many regions.
type GameDates struct {
	US string `xml:"date_us"`
	FR string `xml:"date_fr"`
	EU string `xml:"date_eu"`
	JP string `xml:"date_jp"`
	XX string `xml:"date_xx"`
}

// Game represents a game in SS.
type Game struct {
	ID        int       `xml:"id"`
	Name      string    `xml:"nom"`
	Names     GameNames `xml:"noms"`
	Region    string    `xml:"region"`
	Publisher string    `xml:"editeur"`
	Developer string    `xml:"developpeur"`
	Players   string    `xml:"joueurs"`
	Rating    string    `xml:"note"`
	Desc      GameDesc  `xml:"synopsis"`
	Genre     GameGenre `xml:"genres"`
	Media     GameMedia `xml:"medias"`
	Dates     GameDates `xml:"dates"`
}

// GameInfoResp is the response from GameInfo.
type GameInfoResp struct {
	Game Game `xml:"jeu"`
}

// GameInfo is the call to get game info.
func GameInfo(dev DevInfo, user UserInfo, req GameInfoReq) (*GameInfoResp, error) {
	u, err := url.Parse(baseURL)
	u.Path = gameInfoPath
	q := url.Values{}
	q.Set("devid", dev.ID)
	q.Set("devpassword", dev.Password)
	if dev.Name != "" {
		q.Set("softname", dev.Name)
	}
	if user.ID != "" {
		q.Set("ssid", user.ID)
	}
	if user.Password != "" {
		q.Set("sspassword", user.Password)
	}
	if req.SHA1 != "" {
		q.Set("sha1", req.SHA1)
	}
	if req.RomType == "" {
		q.Set("romtype", "rom")
	} else {
		q.Set("romtype", req.RomType)
	}
	if req.Name != "" {
		q.Set("romnom", req.Name)
	}
	u.RawQuery = q.Encode()
	resp, err := http.Get(u.String())
	if err != nil {
		v := u.Query()
		v.Set("devid", "xxx")
		v.Set("devpassword", "yyy")
		v.Set("softname", "zzz")
		u.RawQuery = v.Encode()
		return nil, fmt.Errorf("getting game url:%s, error:%s", u, err)
	}
	defer resp.Body.Close()
	r := &GameInfoResp{}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if bytes.HasPrefix(b, []byte("Erreur : Rom/Iso/Dossier non trouv")) {
		return nil, ErrNotFound
	}
	if err := xml.Unmarshal(b, r); err != nil {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>package stack

import "errors"

type Stack struct {
  data []int
  top int
}

func (s *Stack) IsEmpty() bool {
  if s.top == 0 {
    return true
  }
  return false
}

func (s *Stack) IsFull() bool {
  if (s.top == len(s.data)-1) {
    return true
  }
  return false
}

func (s *Stack) Push(num int) (err error) {
  err = nil
  if s.IsFull() {
    err = errors.New("Stack is aleady full.")
    return
  }
  s.data[s.top] = num
  s.top++
  return
}

func (s *Stack) Pop() (value int, err error) {
  err = nil
  value = 0
  if s.IsEmpty() {
    err = errors.New("Stack is aleady empty.")
  } else {
    s.top--
    value = s.data[s.top]
  }
  return
}

func NewStack(size int) *Stack {
  s := new(Stack)
  s.data = make([]int, size)
  return s
}
<commit_msg>Added documentation and print function.<commit_after>package stack

import (
  "errors"
  "fmt"
)

type Stack struct {
  data []int
  top int
}

// Checks if the stack is empty.
func (s *Stack) IsEmpty() bool {
  if s.top == 0 {
    return true
  }
  return false
}

// Checks if the stack is full.
func (s *Stack) IsFull() bool {
  if (s.top == len(s.data)-1) {
    return true
  }
  return false
}

// Pushes and item onto stack.
func (s *Stack) Push(num int) (err error) {
  err = nil
  if s.IsFull() {
    err = errors.New("Stack is aleady full.")
    return
  }
  s.data[s.top] = num
  s.top++
  return
}

// Pops and an item from the stack.
func (s *Stack) Pop() (value int, err error) {
  err = nil
  value = 0
  if s.IsEmpty() {
    err = errors.New("Stack is aleady empty.")
  } else {
    s.top--
    value = s.data[s.top]
  }
  return
}

// Print stack.
func (s *Stack) Print() {
  if s.IsEmpty() {
    fmt.Println("Empty stack.")
  } else {
    fmt.Println(s.data[:s.top])
  }
}

// Creates a new empty stack of given size.
func NewStack(size int) *Stack {
  s := new(Stack)
  s.data = make([]int, size)
  return s
}
<|endoftext|>


<commit_before>/*
   Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied. See the License for the
   specific language governing permissions and limitations under the License.
*/

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strings"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudtrail"
    "github.com/aws/aws-sdk-go/service/s3"
    "github.com/aws/aws-sdk-go/service/sts"
)

// ConfigureBucket configures a bucket to send event activity to CloudTrail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     bucketName is the name of the bucket
// Output:
//     If success, nil
//     Otherwise, an error from the call to sts.GetCallerIdentity, json.Marshal, or s3.PutBucketPolicy
// snippet-start:[cloudtrail.go.configure_trail]
func ConfigureBucket(sess *session.Session, bucketName string) error {
    stsSvc := sts.New(sess)
    // snippet-start:[cloudtrail.go.configure_trail.policy]
    input := &sts.GetCallerIdentityInput{}

    result, err := stsSvc.GetCallerIdentity(input)
    if err != nil {
        return err
    }

    accountID := aws.StringValue(result.Account)

    // Create a policy so the bucket sends events to Amazon CloudTrail
    s3Policy := map[string]interface{}{
        "Version": "2012-10-17",
        "Statement": []map[string]interface{}{
            {
                "Sid":    "AWSCloudTrailAclCheck20150319",
                "Effect": "Allow",
                "Principal": map[string]interface{}{
                    "Service": "cloudtrail.amazonaws.com",
                },
                "Action":   "s3:GetBucketAcl",
                "Resource": "arn:aws:s3:::" + bucketName,
            },
            {
                "Sid":    "AWSCloudTrailWrite20150319",
                "Effect": "Allow",
                "Principal": map[string]interface{}{
                    "Service": "cloudtrail.amazonaws.com",
                },
                "Action":   "s3:PutObject",
                "Resource": "arn:aws:s3:::" + bucketName + "/AWSLogs/" + accountID + "/*",
                "Condition": map[string]interface{}{
                    "StringEquals": map[string]interface{}{
                        "s3:x-amz-acl": "bucket-owner-full-control",
                    },
                },
            },
        },
    }

    policy, err := json.Marshal(s3Policy)
    if err != nil {
        return err
    }

    // Create an S3 service
    s3Svc := s3.New(sess)

    // Set the policy on the bucket
    _, err = s3Svc.PutBucketPolicy(&s3.PutBucketPolicyInput{
        Bucket: aws.String(bucketName),
        Policy: aws.String(string(policy)),
    })
    if err != nil {
        return err
    }
    // snippet-end:[cloudtrail.go.configure_trail.policy]

    return nil
}
// snippet-end:[cloudtrail.go.configure_trail]

// CreateTrail creates a trail to get event activity from a bucket
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     bucketName is the name of the bucket
// Output:
//     If success, nil
//     Otherwise, an error from the call to sts.GetCallerIdentity, json.Marshal, or s3.PutBucketPolicy
// snippet-start:[cloudtrail.go.create_trail]
func CreateTrail(sess *session.Session, trailName string, bucketName string) error {
    // snippet-start:[cloudtrail.go.create_trail.create]
    svc := cloudtrail.New(sess)

    ctInput := &cloudtrail.CreateTrailInput{
        Name:         aws.String(trailName),
        S3BucketName: aws.String(bucketName),
    }

    _, err := svc.CreateTrail(ctInput)
    if err != nil {
        return err
    }

    return nil
    // snippet-end:[cloudtrail.go.create_trail.create]
}
// snippet-end:[cloudtrail.go.create_trail]

// GetTrails gets a list of trails
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, a list of Trail objects and nil
//     Otherwise, a nil object and an error from a call to DescribeTrails
func GetTrails(sess *session.Session) ([]*cloudtrail.Trail, error) {
    var trails []*cloudtrail.Trail
    svc := cloudtrail.New(sess)

    resp, err := svc.DescribeTrails(nil)
    if err != nil {
        return trails, err
    }

    return resp.TrailList, nil
}

// listTrails displays a list of your trails
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, nil
//     Otherwise, an error returned by a call to DescribeTrails
// snippet-start:[cloudtrail.go.list_trails]
func listTrails(sess *session.Session) error {
    svc := cloudtrail.New(sess)

    resp, err := svc.DescribeTrails(nil)
    if err != nil {
        return err
    }

    fmt.Println("Found", len(resp.TrailList), "trail(s):")
    fmt.Println("")

    for _, trail := range resp.TrailList {
        fmt.Println("Trail name:  " + *trail.Name)
        fmt.Println("Bucket name: " + *trail.S3BucketName)
        fmt.Println("")
    }

    return nil
}
// snippet-end:[cloudtrail.go.list_trails]

// DeleteTrail deletes a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
// Output:
//     If success, nil
//     Otherwise, an error from a call to DeleteTrail
// snippet-start:[cloudtrail.go.delete_trail]
func DeleteTrail(sess *session.Session, trailName string) error {
    svc := cloudtrail.New(sess)

    input := &cloudtrail.DeleteTrailInput{
        Name: aws.String(trailName),
    }

    _, err := svc.DeleteTrail(input)

    return err
}
// snippet-end:[cloudtrail.go.delete_trail]

// GetUser retrieves the name of the logged on user
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, the user name and nil
//     Otherwise, an empty string and an error from a call to GetCallerIdentity
func GetUser(sess *session.Session) (string, error) {
    svc := sts.New(sess)
    id, err := svc.GetCallerIdentity(nil)
    if err != nil {
        return "", err
    }

    // arn:aws:iam::ACCOUNT-ID:user/USER
    parts := strings.Split(*id.Arn, "/")
    userName := parts[1]

    return userName, nil
}

// GetTrailEvents gets the events for a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
// Output:
//     If success, a list of Event objects and nil
//     Otherwise, a nil object and an error from a call to LookupEvents
func GetTrailEvents(sess *session.Session, trailName string) ([]*cloudtrail.Event, error) {
    var events []*cloudtrail.Event
    svc := cloudtrail.New(sess)
    input := &cloudtrail.LookupEventsInput{EndTime: aws.Time(time.Now())}

    resp, err := svc.LookupEvents(input)
    if err != nil {
        return events, err
    }

    return resp.Events, nil
}

// listTrailEvents lists the events for the user in a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     userName is the name of the user
// Output:
//     If success, nil
//     Otherwise, an error from a call to LookupEvents
// snippet-start:[cloudtrail.go.list_trail_events]
func listTrailEvents(sess *session.Session, trailName string, userName string) error {
    svc := cloudtrail.New(sess)
    input := &cloudtrail.LookupEventsInput{EndTime: aws.Time(time.Now())}

    resp, err := svc.LookupEvents(input)
    if err != nil {
        return err
    }

    fmt.Println("Events for the user " + userName + " in trail " + trailName + ":")

    for _, event := range resp.Events {
        if userName == *event.Username {
            fmt.Println("")
            fmt.Println("Event:")
            fmt.Println("")
            fmt.Println("Name    ", aws.StringValue(event.EventName))
            fmt.Println("ID:     ", aws.StringValue(event.EventId))
            fmt.Println("Time:   ", aws.TimeValue(event.EventTime))

            fmt.Println("Resourcs:")

            for _, resource := range event.Resources {
                fmt.Println("  Name:", aws.StringValue(resource.ResourceName))
                fmt.Println("  Type:", aws.StringValue(resource.ResourceType))
            }
        }
    }

    return nil
}
// snippet-end:[cloudtrail.go.list_trail_events]

// usage displays information on using this app
// Inputs:
//     none
// Output:
//     none
func usage() {
    fmt.Println("Usage:")
    fmt.Println("")
    fmt.Println("    go run sqsQueueOps.go OPERATION")
    fmt.Println("")
    fmt.Println("where OPERATION is one of:")
    fmt.Println("    -l")
    fmt.Println("        list your trails")
    fmt.Println("    -c  TRAIL-NAME BUCKET-NAME")
    fmt.Println("        create trail TRAIL-NAME that gets events from bucket BUCKET-NAME")
    fmt.Println("    -d  TRAIL-NAME")
    fmt.Println("        delete trail TRAIL-NAME")
    fmt.Println("    -e  USER-NAME TRAIL-NAME")
    fmt.Println("        show events from the user USER-NAME for trail TRAIL-NAME")
    fmt.Println("    -h")
    fmt.Println("        print this message and quit")
    fmt.Println("")
}

func main() {
    // Parse args ourselves so the user can request only one operation
    op := ""
    numOps := 0 // So we know when the user has requested more than one operation
    length := len(os.Args)
    i := 1
    trailName := ""
    bucketName := ""
    userName := ""

    for i < length {
        switch os.Args[i] {
        case "-h":
            usage()
            return
        case "-c":
            // -c TRAIL-NAME BUCKET-NAME
            if length != 3 {
                fmt.Println("You must supply a trail name and bucket name")
                usage()
                return
            }
            op = "create"
            i++
            trailName = os.Args[i]
            i++
            bucketName = os.Args[i]
            numOps++

        case "-d":
            // -d TRAIL-NAME
            op = "delete"
            i++
            trailName = os.Args[i]
            numOps++

        case "-e":
            // -e TRAIL-NAME
            op = "events"
            i++
            userName = os.Args[i]
            i++
            trailName = os.Args[i]
            numOps++

        case "-l":
            // -l
            op = "list"
            numOps++

        default:
            fmt.Println("Unrecognized option: " + os.Args[i])
            usage()
            return
        }

        i++
    }

    if numOps > 1 {
        fmt.Println("You cannot request more than one operation")
        usage()
        return
    }

    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    switch op {
    case "create":
        if trailName == "" || bucketName == "" {
            fmt.Println("You must supply a trail name and a bucket name")
            usage()
            return
        }

        err := CreateTrail(sess, trailName, bucketName)
        if err != nil {
            fmt.Println("Got an error creating trail:")
            fmt.Println(err)
            return
        }

    case "delete":
        if trailName == "" {
            fmt.Println("You must supply a trail name")
            usage()
            return
        }

        err := DeleteTrail(sess, trailName)
        if err != nil {
            fmt.Println("Got an error deleting trail:")
            fmt.Println(err)
            return
        }

    case "events":
        if userName == "" || trailName == "" {
            fmt.Println("You must supply a user name and trail name")
            usage()
            return
        }

        err := listTrailEvents(sess, trailName, userName)
        if err != nil {
            fmt.Println("Got an error listing trail events:")
            fmt.Println(err)
            return
        }

    case "list":
        err := listTrails(sess)
        if err != nil {
            fmt.Println("Got an error listing trails:")
            fmt.Println(err)
            return
        }

    default:
        // This should never happen
        fmt.Println("Unrecognized operation (typo?): " + op)
        return
    }
}
<commit_msg>Found some 'Amazon CloudTrail' or just 'CloudTrail' without 'AWS'<commit_after>/*
   Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied. See the License for the
   specific language governing permissions and limitations under the License.
*/

package main

import (
    "encoding/json"
    "fmt"
    "os"
    "strings"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudtrail"
    "github.com/aws/aws-sdk-go/service/s3"
    "github.com/aws/aws-sdk-go/service/sts"
)

// ConfigureBucket configures a bucket to send event activity to AWS CloudTrail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     bucketName is the name of the bucket
// Output:
//     If success, nil
//     Otherwise, an error from the call to sts.GetCallerIdentity, json.Marshal, or s3.PutBucketPolicy
// snippet-start:[cloudtrail.go.configure_trail]
func ConfigureBucket(sess *session.Session, bucketName string) error {
    stsSvc := sts.New(sess)
    // snippet-start:[cloudtrail.go.configure_trail.policy]
    input := &sts.GetCallerIdentityInput{}

    result, err := stsSvc.GetCallerIdentity(input)
    if err != nil {
        return err
    }

    accountID := aws.StringValue(result.Account)

    // Create a policy so the bucket sends events to AWS CloudTrail
    s3Policy := map[string]interface{}{
        "Version": "2012-10-17",
        "Statement": []map[string]interface{}{
            {
                "Sid":    "AWSCloudTrailAclCheck20150319",
                "Effect": "Allow",
                "Principal": map[string]interface{}{
                    "Service": "cloudtrail.amazonaws.com",
                },
                "Action":   "s3:GetBucketAcl",
                "Resource": "arn:aws:s3:::" + bucketName,
            },
            {
                "Sid":    "AWSCloudTrailWrite20150319",
                "Effect": "Allow",
                "Principal": map[string]interface{}{
                    "Service": "cloudtrail.amazonaws.com",
                },
                "Action":   "s3:PutObject",
                "Resource": "arn:aws:s3:::" + bucketName + "/AWSLogs/" + accountID + "/*",
                "Condition": map[string]interface{}{
                    "StringEquals": map[string]interface{}{
                        "s3:x-amz-acl": "bucket-owner-full-control",
                    },
                },
            },
        },
    }

    policy, err := json.Marshal(s3Policy)
    if err != nil {
        return err
    }

    // Create an S3 service
    s3Svc := s3.New(sess)

    // Set the policy on the bucket
    _, err = s3Svc.PutBucketPolicy(&s3.PutBucketPolicyInput{
        Bucket: aws.String(bucketName),
        Policy: aws.String(string(policy)),
    })
    if err != nil {
        return err
    }
    // snippet-end:[cloudtrail.go.configure_trail.policy]

    return nil
}
// snippet-end:[cloudtrail.go.configure_trail]

// CreateTrail creates a trail to get event activity from a bucket
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     bucketName is the name of the bucket
// Output:
//     If success, nil
//     Otherwise, an error from the call to sts.GetCallerIdentity, json.Marshal, or s3.PutBucketPolicy
// snippet-start:[cloudtrail.go.create_trail]
func CreateTrail(sess *session.Session, trailName string, bucketName string) error {
    // snippet-start:[cloudtrail.go.create_trail.create]
    svc := cloudtrail.New(sess)

    ctInput := &cloudtrail.CreateTrailInput{
        Name:         aws.String(trailName),
        S3BucketName: aws.String(bucketName),
    }

    _, err := svc.CreateTrail(ctInput)
    if err != nil {
        return err
    }

    return nil
    // snippet-end:[cloudtrail.go.create_trail.create]
}
// snippet-end:[cloudtrail.go.create_trail]

// GetTrails gets a list of trails
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, a list of Trail objects and nil
//     Otherwise, a nil object and an error from a call to DescribeTrails
func GetTrails(sess *session.Session) ([]*cloudtrail.Trail, error) {
    var trails []*cloudtrail.Trail
    svc := cloudtrail.New(sess)

    resp, err := svc.DescribeTrails(nil)
    if err != nil {
        return trails, err
    }

    return resp.TrailList, nil
}

// listTrails displays a list of your trails
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, nil
//     Otherwise, an error returned by a call to DescribeTrails
// snippet-start:[cloudtrail.go.list_trails]
func listTrails(sess *session.Session) error {
    svc := cloudtrail.New(sess)

    resp, err := svc.DescribeTrails(nil)
    if err != nil {
        return err
    }

    fmt.Println("Found", len(resp.TrailList), "trail(s):")
    fmt.Println("")

    for _, trail := range resp.TrailList {
        fmt.Println("Trail name:  " + *trail.Name)
        fmt.Println("Bucket name: " + *trail.S3BucketName)
        fmt.Println("")
    }

    return nil
}
// snippet-end:[cloudtrail.go.list_trails]

// DeleteTrail deletes a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
// Output:
//     If success, nil
//     Otherwise, an error from a call to DeleteTrail
// snippet-start:[cloudtrail.go.delete_trail]
func DeleteTrail(sess *session.Session, trailName string) error {
    svc := cloudtrail.New(sess)

    input := &cloudtrail.DeleteTrailInput{
        Name: aws.String(trailName),
    }

    _, err := svc.DeleteTrail(input)

    return err
}
// snippet-end:[cloudtrail.go.delete_trail]

// GetUser retrieves the name of the logged on user
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
// Output:
//     If success, the user name and nil
//     Otherwise, an empty string and an error from a call to GetCallerIdentity
func GetUser(sess *session.Session) (string, error) {
    svc := sts.New(sess)
    id, err := svc.GetCallerIdentity(nil)
    if err != nil {
        return "", err
    }

    // arn:aws:iam::ACCOUNT-ID:user/USER
    parts := strings.Split(*id.Arn, "/")
    userName := parts[1]

    return userName, nil
}

// GetTrailEvents gets the events for a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
// Output:
//     If success, a list of Event objects and nil
//     Otherwise, a nil object and an error from a call to LookupEvents
func GetTrailEvents(sess *session.Session, trailName string) ([]*cloudtrail.Event, error) {
    var events []*cloudtrail.Event
    svc := cloudtrail.New(sess)
    input := &cloudtrail.LookupEventsInput{EndTime: aws.Time(time.Now())}

    resp, err := svc.LookupEvents(input)
    if err != nil {
        return events, err
    }

    return resp.Events, nil
}

// listTrailEvents lists the events for the user in a trail
// Inputs:
//     sess is the current session, which provides configuration for the SDK's service clients
//     trailName is the name of the trail
//     userName is the name of the user
// Output:
//     If success, nil
//     Otherwise, an error from a call to LookupEvents
// snippet-start:[cloudtrail.go.list_trail_events]
func listTrailEvents(sess *session.Session, trailName string, userName string) error {
    svc := cloudtrail.New(sess)
    input := &cloudtrail.LookupEventsInput{EndTime: aws.Time(time.Now())}

    resp, err := svc.LookupEvents(input)
    if err != nil {
        return err
    }

    fmt.Println("Events for the user " + userName + " in trail " + trailName + ":")

    for _, event := range resp.Events {
        if userName == *event.Username {
            fmt.Println("")
            fmt.Println("Event:")
            fmt.Println("")
            fmt.Println("Name    ", aws.StringValue(event.EventName))
            fmt.Println("ID:     ", aws.StringValue(event.EventId))
            fmt.Println("Time:   ", aws.TimeValue(event.EventTime))

            fmt.Println("Resourcs:")

            for _, resource := range event.Resources {
                fmt.Println("  Name:", aws.StringValue(resource.ResourceName))
                fmt.Println("  Type:", aws.StringValue(resource.ResourceType))
            }
        }
    }

    return nil
}
// snippet-end:[cloudtrail.go.list_trail_events]

// usage displays information on using this app
// Inputs:
//     none
// Output:
//     none
func usage() {
    fmt.Println("Usage:")
    fmt.Println("")
    fmt.Println("    go run sqsQueueOps.go OPERATION")
    fmt.Println("")
    fmt.Println("where OPERATION is one of:")
    fmt.Println("    -l")
    fmt.Println("        list your trails")
    fmt.Println("    -c  TRAIL-NAME BUCKET-NAME")
    fmt.Println("        create trail TRAIL-NAME that gets events from bucket BUCKET-NAME")
    fmt.Println("    -d  TRAIL-NAME")
    fmt.Println("        delete trail TRAIL-NAME")
    fmt.Println("    -e  USER-NAME TRAIL-NAME")
    fmt.Println("        show events from the user USER-NAME for trail TRAIL-NAME")
    fmt.Println("    -h")
    fmt.Println("        print this message and quit")
    fmt.Println("")
}

func main() {
    // Parse args ourselves so the user can request only one operation
    op := ""
    numOps := 0 // So we know when the user has requested more than one operation
    length := len(os.Args)
    i := 1
    trailName := ""
    bucketName := ""
    userName := ""

    for i < length {
        switch os.Args[i] {
        case "-h":
            usage()
            return
        case "-c":
            // -c TRAIL-NAME BUCKET-NAME
            if length != 3 {
                fmt.Println("You must supply a trail name and bucket name")
                usage()
                return
            }
            op = "create"
            i++
            trailName = os.Args[i]
            i++
            bucketName = os.Args[i]
            numOps++

        case "-d":
            // -d TRAIL-NAME
            op = "delete"
            i++
            trailName = os.Args[i]
            numOps++

        case "-e":
            // -e TRAIL-NAME
            op = "events"
            i++
            userName = os.Args[i]
            i++
            trailName = os.Args[i]
            numOps++

        case "-l":
            // -l
            op = "list"
            numOps++

        default:
            fmt.Println("Unrecognized option: " + os.Args[i])
            usage()
            return
        }

        i++
    }

    if numOps > 1 {
        fmt.Println("You cannot request more than one operation")
        usage()
        return
    }

    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    switch op {
    case "create":
        if trailName == "" || bucketName == "" {
            fmt.Println("You must supply a trail name and a bucket name")
            usage()
            return
        }

        err := CreateTrail(sess, trailName, bucketName)
        if err != nil {
            fmt.Println("Got an error creating trail:")
            fmt.Println(err)
            return
        }

    case "delete":
        if trailName == "" {
            fmt.Println("You must supply a trail name")
            usage()
            return
        }

        err := DeleteTrail(sess, trailName)
        if err != nil {
            fmt.Println("Got an error deleting trail:")
            fmt.Println(err)
            return
        }

    case "events":
        if userName == "" || trailName == "" {
            fmt.Println("You must supply a user name and trail name")
            usage()
            return
        }

        err := listTrailEvents(sess, trailName, userName)
        if err != nil {
            fmt.Println("Got an error listing trail events:")
            fmt.Println(err)
            return
        }

    case "list":
        err := listTrails(sess)
        if err != nil {
            fmt.Println("Got an error listing trails:")
            fmt.Println(err)
            return
        }

    default:
        // This should never happen
        fmt.Println("Unrecognized operation (typo?): " + op)
        return
    }
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"koding/broker/storage"
	"koding/tools/sockjs"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

type Client struct {
	Session        *sockjs.Session
	ControlChannel *amqp.Channel
	SocketId       string
	Broker         *Broker
	LastPayload    string
	Subscriptions  storage.Subscriptionable
}

// NewClient retuns a new client that is defined on a given session.
func NewClient(session *sockjs.Session, broker *Broker) *Client {
	socketId := randomString()
	session.Tag = socketId

	var err error
	controlChannel, err := broker.PublishConn.Channel()
	if err != nil {
		panic(err)
	}

	var subscriptions storage.Subscriptionable

	subscriptions, err = storage.NewStorage(conf, storage.REDIS, socketId)
	if err != nil {
		log.Critical("Couldnt access to redis/create a key for client %v", session.Tag)
		subscriptions, err = storage.NewStorage(conf, storage.SET, socketId)
		if err != nil {
			// this will never fail to here
			panic(err)
		}
	}

	globalMapMutex.Lock()
	sessionsMap[socketId] = session
	globalMapMutex.Unlock()

	return &Client{
		Session:        session,
		SocketId:       socketId,
		ControlChannel: controlChannel,
		Broker:         broker,
		Subscriptions:  subscriptions,
	}
}

// Close should be called whenever a client disconnects.
func (c *Client) Close() {
	log.Debug("Client Close Request for socketID: %v", c.SocketId)
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.RemoveFromRoute(routingKeyPrefix.(string))
		return true
	})

	c.Subscriptions.ClearWithTimeout(time.Minute * 5)

	for {
		err := c.ControlChannel.Publish(c.Broker.Config.AuthAllExchange, "broker.clientDisconnected", false, false, amqp.Publishing{Body: []byte(c.SocketId)})
		if err == nil {
			break
		}
		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			panic(err)
		}
		c.resetControlChannel()
	}

	log.Debug("Closing control channel for socketID: %v", c.SocketId)
	c.ControlChannel.Close()

	globalMapMutex.Lock()
	defer globalMapMutex.Unlock()
	delete(sessionsMap, c.SocketId)
}

// handleSessionMessage handles the received message from the client. It
// passes a response back to the client or publish the received message to a
// rabbitmq exchange for further process.
func (c *Client) handleSessionMessage(data interface{}) {
	defer log.RecoverAndLog()

	message := data.(map[string]interface{})
	log.Debug("Received message: %v", message)

	action := message["action"]
	switch action {
	case "subscribe":
		routingKeyPrefixes := strings.Split(message["routingKeyPrefix"].(string), " ")
		if err := c.Subscribe(routingKeyPrefixes...); err != nil {
			log.Error(err.Error())
		}

		sendToClient(c.Session, "broker.subscribed", message["routingKeyPrefix"])

	case "resubscribe":
		clientId := message["socketId"].(string)
		log.Debug("Resubscribe event for clientId: %v SocketId: %v", clientId, c.SocketId)
		found, err := c.Resubscribe(clientId)
		if err != nil {
			log.Error(err.Error())
		}
		log.Debug("Resubscribe found for socketID: %v, %v", clientId, found)
		sendToClient(c.Session, "broker.resubscribed", found)

	case "unsubscribe":
		routingKeyPrefix := message["routingKeyPrefix"].(string)
		log.Debug("Unsubscribe event for socketID: %v, and prefixes", c.SocketId, routingKeyPrefix)
		routingKeyPrefixes := strings.Split(routingKeyPrefix, " ")
		c.Unsubscribe(routingKeyPrefixes...)

	case "publish":
		exchange := message["exchange"].(string)
		routingKey := message["routingKey"].(string)
		payload := message["payload"].(string)

		log.Debug("Publish Event: Exchange: %v, RoutingKey %v, Payload %v",
			exchange,
			routingKey,
			payload,
		)

		err := c.Publish(exchange, routingKey, payload)
		if err != nil {
			log.Error(err.Error())
		}

	case "ping":
		sendToClient(c.Session, "broker.pong", nil)
		// TOOD - may be we need to revisit this part later about duration and request count
		go c.Subscriptions.ClearWithTimeout(time.Minute * 30)
	default:
		log.Warning("Invalid action. message: %v socketId: %v", message, c.SocketId)

	}
}

// Publish publish the given payload for to the given exchange and routingkey.
func (c *Client) Publish(exchange, routingKey, payload string) error {
	if !strings.HasPrefix(routingKey, "client.") {
		return fmt.Errorf("Invalid routing key: %v socketId: %v", routingKey, c.SocketId)
	}

	for {
		c.LastPayload = ""
		err := c.ControlChannel.Publish(exchange, routingKey, false, false, amqp.Publishing{CorrelationId: c.SocketId, Body: []byte(payload)})
		if err == nil {
			c.LastPayload = payload
			break
		}

		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			log.Warning("payload: %v routing key: %v exchange: %v err: %v",
				payload, routingKey, exchange, err)
		}

		time.Sleep(time.Second / 4) // penalty for crashing the AMQP channel
		c.resetControlChannel()
	}

	return nil
}

// gaugeStart starts the gauge for a given session. It returns a new
// function which ends the gauge for the given session. Usually one invokes
// gaugeStart and calls the returned function in a defer statement.
func (c *Client) gaugeStart() (gaugeEnd func()) {
	log.Debug("Client connected: %v", c.Session.Tag)
	changeClientsGauge(1)
	changeNewClientsGauge(1)
	if c.Session.IsWebsocket {
		changeWebsocketClientsGauge(1)
	}

	return func() {
		log.Debug("Client disconnected: %v", c.Session.Tag)
		changeClientsGauge(-1)
		if c.Session.IsWebsocket {
			changeWebsocketClientsGauge(-1)
		}
	}
}

// resetControlChannel closes the current client's control channel and creates
// a new channel. It also listens to any server side error and publish back
// the error to the client.
func (c *Client) resetControlChannel() {
	if c.ControlChannel != nil {
		c.ControlChannel.Close()
	}

	var err error
	c.ControlChannel, err = c.Broker.PublishConn.Channel()
	if err != nil {
		panic(err)
	}

	go func() {
		defer log.RecoverAndLog()

		for amqpErr := range c.ControlChannel.NotifyClose(make(chan *amqp.Error)) {
			if !(strings.Contains(amqpErr.Error(), "NOT_FOUND") && (strings.Contains(amqpErr.Error(), "koding-social-") || strings.Contains(amqpErr.Error(), "auth-"))) {
				log.Warning("AMQP channel: %v Last publish payload: %v", amqpErr.Error(), c.LastPayload)
			}

			sendToClient(c.Session, "broker.error", map[string]interface{}{
				"code":    amqpErr.Code,
				"reason":  amqpErr.Reason,
				"server":  amqpErr.Server,
				"recover": amqpErr.Recover,
			})
		}
	}()
}

// RemoveFromRoute removes the sessions for the given routingKeyPrefix.
func (c *Client) RemoveFromRoute(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			continue
		}
		routeMap[routingKeyPrefix].Remove(c.SocketId)

		if routeMap[routingKeyPrefix].Size() == 0 {
			delete(routeMap, routingKeyPrefix)
		}
	}
}

// Add to route
// todo ~ check for multiple subscriptions
func (c *Client) AddToRoute() {
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.AddToRouteMapNOTS(routingKeyPrefix.(string))
		return true
	})
}

func (c *Client) AddToRouteMapNOTS(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			routeMap[routingKeyPrefix] = set.New()
		}
		routeMap[routingKeyPrefix].Add(c.SocketId)
	}
}

// Subscribe add the given routingKeyPrefix to the list of subscriptions
// associated with this client.
func (c *Client) Subscribe(routingKeyPrefixes ...string) error {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		res, err := c.Subscriptions.Has(routingKeyPrefix)
		if err != nil {
			return err
		}

		if res {
			return fmt.Errorf("Duplicate subscription to same routing key. %v %v", c.Session.Tag, routingKeyPrefix)
		}
	}

	if err := c.Subscriptions.Subscribe(routingKeyPrefixes...); err != nil {
		return err
	}

	globalMapMutex.Lock()
	c.AddToRouteMapNOTS(routingKeyPrefixes...)
	globalMapMutex.Unlock()

	// Log some information about the Client
	go func() {
		length, err := c.Subscriptions.Len()
		if err != nil {
			log.Warning("Error while trying to get Subscriptions.Len() for: %v Error: %v", c.Session.Tag, err)
		}

		if length > 0 && length%2000 == 0 {
			log.Warning("Client with more than %v subscriptions %v", strconv.Itoa(length), c.Session.Tag)
		}
	}()

	return nil
}

func (c *Client) Resubscribe(sessionId string) (bool, error) {
	found, err := c.Subscriptions.Resubscribe(sessionId)
	if err != nil {
		return false, err
	}

	if !found {
		return false, nil
	}

	c.AddToRoute()
	return true, nil
}

// Unsubscribe deletes the given routingKey prefix from the subscription list
// and removes it from the global route map
func (c *Client) Unsubscribe(routingKeyPrefixes ...string) {
	c.RemoveFromRoute(routingKeyPrefixes...)
	if err := c.Subscriptions.Unsubscribe(routingKeyPrefixes...); err != nil {
		fmt.Errorf("%v", err)
	}
}

// randomString() returns a new 16 char length random string
func randomString() string {
	r := make([]byte, 128/8)
	rand.Read(r)
	return base64.StdEncoding.EncodeToString(r)
}
<commit_msg>Broker: remove checking for multiple subscriptions<commit_after>package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"koding/broker/storage"
	"koding/tools/sockjs"
	"strconv"
	"strings"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

type Client struct {
	Session        *sockjs.Session
	ControlChannel *amqp.Channel
	SocketId       string
	Broker         *Broker
	LastPayload    string
	Subscriptions  storage.Subscriptionable
}

// NewClient retuns a new client that is defined on a given session.
func NewClient(session *sockjs.Session, broker *Broker) *Client {
	socketId := randomString()
	session.Tag = socketId

	var err error
	controlChannel, err := broker.PublishConn.Channel()
	if err != nil {
		panic(err)
	}

	var subscriptions storage.Subscriptionable

	subscriptions, err = storage.NewStorage(conf, storage.REDIS, socketId)
	if err != nil {
		log.Critical("Couldnt access to redis/create a key for client %v", session.Tag)
		subscriptions, err = storage.NewStorage(conf, storage.SET, socketId)
		if err != nil {
			// this will never fail to here
			panic(err)
		}
	}

	globalMapMutex.Lock()
	sessionsMap[socketId] = session
	globalMapMutex.Unlock()

	return &Client{
		Session:        session,
		SocketId:       socketId,
		ControlChannel: controlChannel,
		Broker:         broker,
		Subscriptions:  subscriptions,
	}
}

// Close should be called whenever a client disconnects.
func (c *Client) Close() {
	log.Debug("Client Close Request for socketID: %v", c.SocketId)
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.RemoveFromRoute(routingKeyPrefix.(string))
		return true
	})

	c.Subscriptions.ClearWithTimeout(time.Minute * 5)

	for {
		err := c.ControlChannel.Publish(c.Broker.Config.AuthAllExchange, "broker.clientDisconnected", false, false, amqp.Publishing{Body: []byte(c.SocketId)})
		if err == nil {
			break
		}
		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			panic(err)
		}
		c.resetControlChannel()
	}

	log.Debug("Closing control channel for socketID: %v", c.SocketId)
	c.ControlChannel.Close()

	globalMapMutex.Lock()
	defer globalMapMutex.Unlock()
	delete(sessionsMap, c.SocketId)
}

// handleSessionMessage handles the received message from the client. It
// passes a response back to the client or publish the received message to a
// rabbitmq exchange for further process.
func (c *Client) handleSessionMessage(data interface{}) {
	defer log.RecoverAndLog()

	message := data.(map[string]interface{})
	log.Debug("Received message: %v", message)

	action := message["action"]
	switch action {
	case "subscribe":
		routingKeyPrefixes := strings.Split(message["routingKeyPrefix"].(string), " ")
		if err := c.Subscribe(routingKeyPrefixes...); err != nil {
			log.Error(err.Error())
		}

		sendToClient(c.Session, "broker.subscribed", message["routingKeyPrefix"])

	case "resubscribe":
		clientId := message["socketId"].(string)
		log.Debug("Resubscribe event for clientId: %v SocketId: %v", clientId, c.SocketId)
		found, err := c.Resubscribe(clientId)
		if err != nil {
			log.Error(err.Error())
		}
		log.Debug("Resubscribe found for socketID: %v, %v", clientId, found)
		sendToClient(c.Session, "broker.resubscribed", found)

	case "unsubscribe":
		routingKeyPrefix := message["routingKeyPrefix"].(string)
		log.Debug("Unsubscribe event for socketID: %v, and prefixes", c.SocketId, routingKeyPrefix)
		routingKeyPrefixes := strings.Split(routingKeyPrefix, " ")
		c.Unsubscribe(routingKeyPrefixes...)

	case "publish":
		exchange := message["exchange"].(string)
		routingKey := message["routingKey"].(string)
		payload := message["payload"].(string)

		log.Debug("Publish Event: Exchange: %v, RoutingKey %v, Payload %v",
			exchange,
			routingKey,
			payload,
		)

		err := c.Publish(exchange, routingKey, payload)
		if err != nil {
			log.Error(err.Error())
		}

	case "ping":
		sendToClient(c.Session, "broker.pong", nil)
		// TOOD - may be we need to revisit this part later about duration and request count
		go c.Subscriptions.ClearWithTimeout(time.Minute * 30)
	default:
		log.Warning("Invalid action. message: %v socketId: %v", message, c.SocketId)

	}
}

// Publish publish the given payload for to the given exchange and routingkey.
func (c *Client) Publish(exchange, routingKey, payload string) error {
	if !strings.HasPrefix(routingKey, "client.") {
		return fmt.Errorf("Invalid routing key: %v socketId: %v", routingKey, c.SocketId)
	}

	for {
		c.LastPayload = ""
		err := c.ControlChannel.Publish(exchange, routingKey, false, false, amqp.Publishing{CorrelationId: c.SocketId, Body: []byte(payload)})
		if err == nil {
			c.LastPayload = payload
			break
		}

		if amqpError, isAmqpError := err.(*amqp.Error); !isAmqpError || amqpError.Code != amqp.ChannelError {
			log.Warning("payload: %v routing key: %v exchange: %v err: %v",
				payload, routingKey, exchange, err)
		}

		time.Sleep(time.Second / 4) // penalty for crashing the AMQP channel
		c.resetControlChannel()
	}

	return nil
}

// gaugeStart starts the gauge for a given session. It returns a new
// function which ends the gauge for the given session. Usually one invokes
// gaugeStart and calls the returned function in a defer statement.
func (c *Client) gaugeStart() (gaugeEnd func()) {
	log.Debug("Client connected: %v", c.Session.Tag)
	changeClientsGauge(1)
	changeNewClientsGauge(1)
	if c.Session.IsWebsocket {
		changeWebsocketClientsGauge(1)
	}

	return func() {
		log.Debug("Client disconnected: %v", c.Session.Tag)
		changeClientsGauge(-1)
		if c.Session.IsWebsocket {
			changeWebsocketClientsGauge(-1)
		}
	}
}

// resetControlChannel closes the current client's control channel and creates
// a new channel. It also listens to any server side error and publish back
// the error to the client.
func (c *Client) resetControlChannel() {
	if c.ControlChannel != nil {
		c.ControlChannel.Close()
	}

	var err error
	c.ControlChannel, err = c.Broker.PublishConn.Channel()
	if err != nil {
		panic(err)
	}

	go func() {
		defer log.RecoverAndLog()

		for amqpErr := range c.ControlChannel.NotifyClose(make(chan *amqp.Error)) {
			if !(strings.Contains(amqpErr.Error(), "NOT_FOUND") && (strings.Contains(amqpErr.Error(), "koding-social-") || strings.Contains(amqpErr.Error(), "auth-"))) {
				log.Warning("AMQP channel: %v Last publish payload: %v", amqpErr.Error(), c.LastPayload)
			}

			sendToClient(c.Session, "broker.error", map[string]interface{}{
				"code":    amqpErr.Code,
				"reason":  amqpErr.Reason,
				"server":  amqpErr.Server,
				"recover": amqpErr.Recover,
			})
		}
	}()
}

// RemoveFromRoute removes the sessions for the given routingKeyPrefix.
func (c *Client) RemoveFromRoute(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			continue
		}
		routeMap[routingKeyPrefix].Remove(c.SocketId)

		if routeMap[routingKeyPrefix].Size() == 0 {
			delete(routeMap, routingKeyPrefix)
		}
	}
}

// Add to route
// todo ~ check for multiple subscriptions
func (c *Client) AddToRoute() {
	c.Subscriptions.Each(func(routingKeyPrefix interface{}) bool {
		c.AddToRouteMapNOTS(routingKeyPrefix.(string))
		return true
	})
}

func (c *Client) AddToRouteMapNOTS(routingKeyPrefixes ...string) {
	for _, routingKeyPrefix := range routingKeyPrefixes {
		if _, ok := routeMap[routingKeyPrefix]; !ok {
			routeMap[routingKeyPrefix] = set.New()
		}
		routeMap[routingKeyPrefix].Add(c.SocketId)
	}
}

// Subscribe add the given routingKeyPrefix to the list of subscriptions
// associated with this client.
func (c *Client) Subscribe(routingKeyPrefixes ...string) error {
	if err := c.Subscriptions.Subscribe(routingKeyPrefixes...); err != nil {
		return err
	}

	globalMapMutex.Lock()
	c.AddToRouteMapNOTS(routingKeyPrefixes...)
	globalMapMutex.Unlock()

	// Log some information about the Client
	go func() {
		length, err := c.Subscriptions.Len()
		if err != nil {
			log.Warning("Error while trying to get Subscriptions.Len() for: %v Error: %v", c.Session.Tag, err)
		}

		if length > 0 && length%2000 == 0 {
			log.Warning("Client with more than %v subscriptions %v", strconv.Itoa(length), c.Session.Tag)
		}
	}()

	return nil
}

func (c *Client) Resubscribe(sessionId string) (bool, error) {
	found, err := c.Subscriptions.Resubscribe(sessionId)
	if err != nil {
		return false, err
	}

	if !found {
		return false, nil
	}

	c.AddToRoute()
	return true, nil
}

// Unsubscribe deletes the given routingKey prefix from the subscription list
// and removes it from the global route map
func (c *Client) Unsubscribe(routingKeyPrefixes ...string) {
	c.RemoveFromRoute(routingKeyPrefixes...)
	if err := c.Subscriptions.Unsubscribe(routingKeyPrefixes...); err != nil {
		fmt.Errorf("%v", err)
	}
}

// randomString() returns a new 16 char length random string
func randomString() string {
	r := make([]byte, 128/8)
	rand.Read(r)
	return base64.StdEncoding.EncodeToString(r)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	DefaultFederationName = "federation"

	// TODO: Only suppoprts IPv4 addresses. Also add a regexp for IPv6 addresses.
	FederatedIPAddrRegexp  = `(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
	FederatedDNS1123Regexp = `([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)*([a-z0-9]([-a-z0-9]*[a-z0-9])?)`
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			federationName = DefaultFederationName
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	It("should be able to discover a federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		svcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})

	It("should be able to discover a non-local federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		// Delete a federated service shard in the default e2e Kubernetes cluster.
		err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)

		localSvcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
		}
		for _, name := range localSvcDNSNames {
			discoverService(f, name, false)
		}

		svcDNSNames := []string{
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *api.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]
		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_internalclientset.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By("Creating a federated service")
	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: api.ServiceSpec{
			Selector: labels,
			Ports: []api.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	_, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, service, 1)
	}
}

func discoverService(f *framework.Framework, name string, exists bool) {
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: []string{"sh", "-c", "nslookup", name},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
	if exists {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`Name:\s+` + FederatedDNS1123Regexp + `\nAddress \d+:\s+` + FederatedIPAddrRegexp + `\s+` + FederatedDNS1123Regexp,
		})
	} else {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`nslookup: can't resolve '` + name + `'`,
		})
	}
}
<commit_msg>One more place we need a secret.<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"os"
	"time"

	"k8s.io/kubernetes/federation/apis/federation"
	"k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_3"
	"k8s.io/kubernetes/pkg/client/restclient"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	"k8s.io/kubernetes/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	UserAgentName = "federation-e2e-service-controller"
	// TODO(madhusudancs): Using the same values as defined in the federated
	// service controller. Replace it with the values from the e2e framework.
	KubeAPIQPS   = 20.0
	KubeAPIBurst = 30

	FederatedServiceTimeout = 5 * time.Minute

	FederatedServiceName = "federated-service"
	FederatedServicePod  = "federated-service-test-pod"

	DefaultFederationName = "federation"

	// TODO: Only suppoprts IPv4 addresses. Also add a regexp for IPv6 addresses.
	FederatedIPAddrRegexp  = `(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
	FederatedDNS1123Regexp = `([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)*([a-z0-9]([-a-z0-9]*[a-z0-9])?)`
)

var _ = framework.KubeDescribe("Service [Feature:Federation]", func() {
	var clusterClientSets []*release_1_3.Clientset
	var federationName string
	f := framework.NewDefaultFederatedFramework("service")

	BeforeEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// TODO: Federation API server should be able to answer this.
		if federationName = os.Getenv("FEDERATION_NAME"); federationName == "" {
			federationName = DefaultFederationName
		}

		contexts := f.GetUnderlyingFederatedContexts()

		for _, context := range contexts {
			framework.Logf("Creating cluster object: %s (%s)", context.Name, context.Cluster.Cluster.Server)
			cluster := federation.Cluster{
				ObjectMeta: api.ObjectMeta{
					Name: context.Name,
				},
				Spec: federation.ClusterSpec{
					ServerAddressByClientCIDRs: []federation.ServerAddressByClientCIDR{
						{
							ClientCIDR:    "0.0.0.0/0",
							ServerAddress: context.Cluster.Cluster.Server,
						},
					},
					SecretRef: &api.LocalObjectReference{
						// Note: Name must correlate with federation build script secret name,
						//       which currently matches the cluster name.
						//       See federation/cluster/common.sh:132
						Name: context.Name,
					},
				},
			}
			_, err := f.FederationClientset.Federation().Clusters().Create(&cluster)
			framework.ExpectNoError(err, "Creating cluster")
		}

		var clusterList *federation.ClusterList
		By("Obtaining a list of all the clusters")
		if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
			var err error
			clusterList, err = f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
			if err != nil {
				return false, err
			}
			framework.Logf("%d clusters registered, waiting for %d", len(clusterList.Items), len(contexts))
			if len(clusterList.Items) == len(contexts) {
				return true, nil
			}
			return false, nil
		}); err != nil {
			framework.Failf("Failed to list registered clusters: %+v", err)
		}

		for _, cluster := range clusterList.Items {
			framework.Logf("Creating a clientset for the cluster %s", cluster.Name)

			Expect(framework.TestContext.KubeConfig).ToNot(Equal(""), "KubeConfig must be specified to load clusters' client config")
			kubecfg, err := clientcmd.LoadFromFile(framework.TestContext.KubeConfig)
			framework.ExpectNoError(err, "error loading KubeConfig: %v", err)

			cfgOverride := &clientcmd.ConfigOverrides{
				ClusterInfo: clientcmdapi.Cluster{
					Server: cluster.Spec.ServerAddressByClientCIDRs[0].ServerAddress,
				},
			}
			ccfg := clientcmd.NewNonInteractiveClientConfig(*kubecfg, cluster.Name, cfgOverride, clientcmd.NewDefaultClientConfigLoadingRules())
			cfg, err := ccfg.ClientConfig()
			Expect(err).NotTo(HaveOccurred())

			cfg.QPS = KubeAPIQPS
			cfg.Burst = KubeAPIBurst
			clset := release_1_3.NewForConfigOrDie(restclient.AddUserAgent(cfg, UserAgentName))
			clusterClientSets = append(clusterClientSets, clset)
		}
	})

	AfterEach(func() {
		framework.SkipUnlessFederated(f.Client)

		// Delete the registered clusters in the federation API server.
		clusterList, err := f.FederationClientset.Federation().Clusters().List(api.ListOptions{})
		Expect(err).NotTo(HaveOccurred())
		for _, cluster := range clusterList.Items {
			err := f.FederationClientset.Federation().Clusters().Delete(cluster.Name, &api.DeleteOptions{})
			Expect(err).NotTo(HaveOccurred())
		}
	})

	It("should be able to discover a federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		svcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})

	It("should be able to discover a non-local federated service", func() {
		framework.SkipUnlessFederated(f.Client)

		createService(f.FederationClientset, clusterClientSets, f.Namespace.Name)

		// Delete a federated service shard in the default e2e Kubernetes cluster.
		err := f.Clientset_1_3.Core().Services(f.Namespace.Name).Delete(FederatedServiceName, &api.DeleteOptions{})
		Expect(err).NotTo(HaveOccurred())
		waitForFederatedServiceShard(f.Clientset_1_3, f.Namespace.Name, nil, 0)

		localSvcDNSNames := []string{
			FederatedServiceName,
			fmt.Sprintf("%s.%s", FederatedServiceName, f.Namespace.Name),
			fmt.Sprintf("%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name),
		}
		for _, name := range localSvcDNSNames {
			discoverService(f, name, false)
		}

		svcDNSNames := []string{
			fmt.Sprintf("%s.%s.%s", FederatedServiceName, f.Namespace.Name, federationName),
			fmt.Sprintf("%s.%s.%s.svc.cluster.local.", FederatedServiceName, f.Namespace.Name, federationName),
		}
		for _, name := range svcDNSNames {
			discoverService(f, name, true)
		}
	})
})

// waitForFederatedServiceShard waits until the number of shards of a given federated
// service reaches the expected value, i.e. numSvcs in the given individual Kubernetes
// cluster. If the shard count, i.e. numSvcs is expected to be at least one, then
// it also checks if the first shard's name and spec matches that of the given service.
func waitForFederatedServiceShard(cs *release_1_3.Clientset, namespace string, service *api.Service, numSvcs int) {
	By("Fetching a federated service shard")
	var clSvcList *v1.ServiceList
	if err := wait.PollImmediate(framework.Poll, FederatedServiceTimeout, func() (bool, error) {
		var err error
		clSvcList, err = cs.Core().Services(namespace).List(api.ListOptions{})
		if err != nil {
			return false, err
		}
		n := len(clSvcList.Items)
		if n == numSvcs {
			return true, nil
		}
		framework.Logf("%d services found, waiting for %d, trying again in %s", n, numSvcs, framework.Poll)
		return false, nil
	}); err != nil {
		framework.Failf("Failed to list registered clusters: %+v", err)
	}

	if numSvcs > 0 && service != nil {
		// Renaming for clarity/readability
		clSvc := clSvcList.Items[0]
		Expect(clSvc.Name).To(Equal(service.Name))
		Expect(clSvc.Spec).To(Equal(service.Spec))
	}
}

func createService(fcs *federation_internalclientset.Clientset, clusterClientSets []*release_1_3.Clientset, namespace string) {
	By("Creating a federated service")
	labels := map[string]string{
		"foo": "bar",
	}

	svc1port := "svc1"
	svc2port := "svc2"

	service := &api.Service{
		ObjectMeta: api.ObjectMeta{
			Name: FederatedServiceName,
		},
		Spec: api.ServiceSpec{
			Selector: labels,
			Ports: []api.ServicePort{
				{
					Name:       "portname1",
					Port:       80,
					TargetPort: intstr.FromString(svc1port),
				},
				{
					Name:       "portname2",
					Port:       81,
					TargetPort: intstr.FromString(svc2port),
				},
			},
		},
	}
	_, err := fcs.Core().Services(namespace).Create(service)
	Expect(err).NotTo(HaveOccurred())
	for _, cs := range clusterClientSets {
		waitForFederatedServiceShard(cs, namespace, service, 1)
	}
}

func discoverService(f *framework.Framework, name string, exists bool) {
	pod := &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   FederatedServicePod,
			Labels: map[string]string{"name": FederatedServicePod},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:    "federated-service-discovery-container",
					Image:   "gcr.io/google_containers/busybox:1.24",
					Command: []string{"sh", "-c", "nslookup", name},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
	if exists {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`Name:\s+` + FederatedDNS1123Regexp + `\nAddress \d+:\s+` + FederatedIPAddrRegexp + `\s+` + FederatedDNS1123Regexp,
		})
	} else {
		f.TestContainerOutputRegexp("federated service discovery", pod, 0, []string{
			`nslookup: can't resolve '` + name + `'`,
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var (
	countDirs  int
	countFiles int
	opt        Options
)

var mutx sync.Mutex

func update(f os.FileInfo) {
	if f.IsDir() {
		countDirs++
	} else {
		countFiles++
	}
}

// readDir reads the files from the dir "s" recursively and checks if there are duplicated
func readDir(s string, depth int) error {
	depth++

	files, err := ioutil.ReadDir(s)
	if err != nil {
		return err
	}
	if len(files) == 0 {
		return nil
	}

	for _, f := range files {
		if f.Name() == ".godedupe_ignore" {
			return nil
		}
	}

	for _, f := range files {

		path := s + string(filepath.Separator) + f.Name()
		file := File{
			path,
			f,
		}

		update(file.info)

		if !opt.quiet {
			fmt.Printf("[+] Analyzed: %v directories and %v files\r", countDirs, countFiles)
		}

		if !file.info.IsDir() {
			// Only scan for files of a given extension
			if opt.fileExt != "" && !strings.HasSuffix(file.info.Name(), opt.fileExt) {
			} else if opt.excludeEmptyFiles && file.info.Size() == 0 {
			} else if opt.excludeHiddenFiles && strings.HasPrefix(file.info.Name(), ".") {
			} else if !opt.followSymlinks && file.info.Mode()&os.ModeSymlink != 0 {
			} else {
				mutx.Lock()
				AddFile(file)
				mutx.Unlock()
			}
		} else if opt.enableRecursion {
			if depth < opt.maxDepth || opt.maxDepth == -1 {
				readDir(path, depth)
			}
		}
	}
	return nil
}

// Start the program with the targetDirs options. Options param is read only
func Start(options Options) {
	// Set the global variable so readDir function can access to the options
	opt = options

	if len(opt.targetDirs) == 0 {
		fmt.Println("Errorr: No directory found")
		return
	}

	for _, dir := range opt.targetDirs {
		if info, err := os.Stat(dir); err == nil && !info.IsDir() && !opt.quiet {
			fmt.Printf("[-] %s is not a valid directory", info.Name())
			return
		}
	}

	for _, dir := range opt.targetDirs {
		if !opt.quiet {
			fmt.Println("[+] Reading directory:", dir)
		}
		err := readDir(dir, 0)
		if err != nil && !opt.quiet {
			fmt.Println("[-]", err)
		}
	}

	if !opt.quiet {
		fmt.Printf("\n[+] Stage 1 / 3 completed\n")
	}

	ValidateDuplicatedFiles()

	reportData := ObtainReportData()
	if opt.sameLine {
		reportData.ReportSameLine()
	} else {
		reportData.ReportDuplicated(opt.showSummary)
	}

	if opt.jsonFile != "" {
		reportData.ExportDuplicate(opt.jsonFile)
	}

	summary := fmt.Sprintf("%v duplicated files in (%v sets) occupying %v bytes\n",
		reportData.duplicates, reportData.sets, ConvertBytes(reportData.totalSize))
	if opt.showNotification {
		ShowNotification("godedupe finish", summary)
	}
}
<commit_msg>Show errors<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var (
	countDirs  int
	countFiles int
	opt        Options
)

var mutx sync.Mutex

func update(f os.FileInfo) {
	if f.IsDir() {
		countDirs++
	} else {
		countFiles++
	}
}

// readDir reads the files from the dir "s" recursively and checks if there are duplicated
func readDir(s string, depth int) {
	depth++

	files, err := ioutil.ReadDir(s)
	if err != nil {
		fmt.Printf("[-] Directory %s could not be read and will be ignored. Error %s\n", err)
		return
	}
	if len(files) == 0 {
		return
	}

	for _, f := range files {
		if f.Name() == ".godedupe_ignore" {
			return
		}
	}

	for _, f := range files {

		path := s + string(filepath.Separator) + f.Name()
		file := File{
			path,
			f,
		}

		update(file.info)

		if !opt.quiet {
			fmt.Printf("[+] Analyzed: %v directories and %v files\r", countDirs, countFiles)
		}

		if !file.info.IsDir() {
			// Only scan for files of a given extension
			if opt.fileExt != "" && !strings.HasSuffix(file.info.Name(), opt.fileExt) {
			} else if opt.excludeEmptyFiles && file.info.Size() == 0 {
			} else if opt.excludeHiddenFiles && strings.HasPrefix(file.info.Name(), ".") {
			} else if !opt.followSymlinks && file.info.Mode()&os.ModeSymlink != 0 {
			} else {
				mutx.Lock()
				AddFile(file)
				mutx.Unlock()
			}
		} else if opt.enableRecursion {
			if depth < opt.maxDepth || opt.maxDepth == -1 {
				readDir(path, depth)
			}
		}
	}
}

// Start the program with the targetDirs options. Options param is read only
func Start(options Options) {
	// Set the global variable so readDir function can access to the options
	opt = options

	if len(opt.targetDirs) == 0 {
		fmt.Println("Errorr: No directory found")
		return
	}

	for _, dir := range opt.targetDirs {
		if info, err := os.Stat(dir); err == nil && !info.IsDir() && !opt.quiet {
			fmt.Printf("[-] %s is not a valid directory", info.Name())
			return
		}
	}

	for _, dir := range opt.targetDirs {
		if !opt.quiet {
			fmt.Println("[+] Reading directory:", dir)
		}
		readDir(dir, 0)
	}

	if !opt.quiet {
		fmt.Printf("\n[+] Stage 1 / 3 completed\n")
	}

	ValidateDuplicatedFiles()

	reportData := ObtainReportData()
	if opt.sameLine {
		reportData.ReportSameLine()
	} else {
		reportData.ReportDuplicated(opt.showSummary)
	}

	if opt.jsonFile != "" {
		reportData.ExportDuplicate(opt.jsonFile)
	}

	summary := fmt.Sprintf("%v duplicated files in (%v sets) occupying %v bytes\n",
		reportData.duplicates, reportData.sets, ConvertBytes(reportData.totalSize))
	if opt.showNotification {
		ShowNotification("godedupe finish", summary)
	}
}
<|endoftext|>