<commit_before>package slack

import (
	"context"
	"net/url"
	"strconv"
)

const (
	DEFAULT_SEARCH_SORT      = "score"
	DEFAULT_SEARCH_SORT_DIR  = "desc"
	DEFAULT_SEARCH_HIGHLIGHT = false
	DEFAULT_SEARCH_COUNT     = 20
	DEFAULT_SEARCH_PAGE      = 1
)

type SearchParameters struct {
	Sort          string
	SortDirection string
	Highlight     bool
	Count         int
	Page          int
}

type CtxChannel struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type CtxMessage struct {
	User      string `json:"user"`
	Username  string `json:"username"`
	Text      string `json:"text"`
	Timestamp string `json:"ts"`
	Type      string `json:"type"`
}

type SearchMessage struct {
	Type        string       `json:"type"`
	Channel     CtxChannel   `json:"channel"`
	User        string       `json:"user"`
	Username    string       `json:"username"`
	Timestamp   string       `json:"ts"`
	Blocks      Blocks       `json:"blocks,omitempty"`
	Text        string       `json:"text"`
	Permalink   string       `json:"permalink"`
	Attachments []Attachment `json:"attachments"`
	Previous    CtxMessage   `json:"previous"`
	Previous2   CtxMessage   `json:"previous_2"`
	Next        CtxMessage   `json:"next"`
	Next2       CtxMessage   `json:"next_2"`
}

type SearchMessages struct {
	Matches    []SearchMessage `json:"matches"`
	Paging     `json:"paging"`
	Pagination `json:"pagination"`
	Total      int `json:"total"`
}

type SearchFiles struct {
	Matches    []File `json:"matches"`
	Paging     `json:"paging"`
	Pagination `json:"pagination"`
	Total      int `json:"total"`
}

type searchResponseFull struct {
	Query          string `json:"query"`
	SearchMessages `json:"messages"`
	SearchFiles    `json:"files"`
	SlackResponse
}

func NewSearchParameters() SearchParameters {
	return SearchParameters{
		Sort:          DEFAULT_SEARCH_SORT,
		SortDirection: DEFAULT_SEARCH_SORT_DIR,
		Highlight:     DEFAULT_SEARCH_HIGHLIGHT,
		Count:         DEFAULT_SEARCH_COUNT,
		Page:          DEFAULT_SEARCH_PAGE,
	}
}

func (api *Client) _search(ctx context.Context, path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
	values := url.Values{
		"token": {api.token},
		"query": {query},
	}
	if params.Sort != DEFAULT_SEARCH_SORT {
		values.Add("sort", params.Sort)
	}
	if params.SortDirection != DEFAULT_SEARCH_SORT_DIR {
		values.Add("sort_dir", params.SortDirection)
	}
	if params.Highlight != DEFAULT_SEARCH_HIGHLIGHT {
		values.Add("highlight", strconv.Itoa(1))
	}
	if params.Count != DEFAULT_SEARCH_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_SEARCH_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}

	response = &searchResponseFull{}
	err := api.postMethod(ctx, path, values, response)
	if err != nil {
		return nil, err
	}

	return response, response.Err()

}

func (api *Client) Search(query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
	return api.SearchContext(context.Background(), query, params)
}

func (api *Client) SearchContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
	response, err := api._search(ctx, "search.all", query, params, true, true)
	if err != nil {
		return nil, nil, err
	}
	return &response.SearchMessages, &response.SearchFiles, nil
}

func (api *Client) SearchFiles(query string, params SearchParameters) (*SearchFiles, error) {
	return api.SearchFilesContext(context.Background(), query, params)
}

func (api *Client) SearchFilesContext(ctx context.Context, query string, params SearchParameters) (*SearchFiles, error) {
	response, err := api._search(ctx, "search.files", query, params, true, false)
	if err != nil {
		return nil, err
	}
	return &response.SearchFiles, nil
}

func (api *Client) SearchMessages(query string, params SearchParameters) (*SearchMessages, error) {
	return api.SearchMessagesContext(context.Background(), query, params)
}

func (api *Client) SearchMessagesContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, error) {
	response, err := api._search(ctx, "search.messages", query, params, false, true)
	if err != nil {
		return nil, err
	}
	return &response.SearchMessages, nil
}
<commit_msg>Add fields to CtxChannel to get search API's response<commit_after>package slack

import (
	"context"
	"net/url"
	"strconv"
)

const (
	DEFAULT_SEARCH_SORT      = "score"
	DEFAULT_SEARCH_SORT_DIR  = "desc"
	DEFAULT_SEARCH_HIGHLIGHT = false
	DEFAULT_SEARCH_COUNT     = 20
	DEFAULT_SEARCH_PAGE      = 1
)

type SearchParameters struct {
	Sort          string
	SortDirection string
	Highlight     bool
	Count         int
	Page          int
}

type CtxChannel struct {
	ID                 string `json:"id"`
	Name               string `json:"name"`
	IsExtShared        bool   `json:"is_ext_shared"`
	IsMPIM             bool   `json:"is_mpim"`
	ISOrgShared        bool   `json:"is_org_shared"`
	IsPendingExtShared bool   `json:"is_pending_ext_shared"`
	IsPrivate          bool   `json:"is_private"`
	IsShared           bool   `json:"is_shared"`
}

type CtxMessage struct {
	User      string `json:"user"`
	Username  string `json:"username"`
	Text      string `json:"text"`
	Timestamp string `json:"ts"`
	Type      string `json:"type"`
}

type SearchMessage struct {
	Type        string       `json:"type"`
	Channel     CtxChannel   `json:"channel"`
	User        string       `json:"user"`
	Username    string       `json:"username"`
	Timestamp   string       `json:"ts"`
	Blocks      Blocks       `json:"blocks,omitempty"`
	Text        string       `json:"text"`
	Permalink   string       `json:"permalink"`
	Attachments []Attachment `json:"attachments"`
	Previous    CtxMessage   `json:"previous"`
	Previous2   CtxMessage   `json:"previous_2"`
	Next        CtxMessage   `json:"next"`
	Next2       CtxMessage   `json:"next_2"`
}

type SearchMessages struct {
	Matches    []SearchMessage `json:"matches"`
	Paging     `json:"paging"`
	Pagination `json:"pagination"`
	Total      int `json:"total"`
}

type SearchFiles struct {
	Matches    []File `json:"matches"`
	Paging     `json:"paging"`
	Pagination `json:"pagination"`
	Total      int `json:"total"`
}

type searchResponseFull struct {
	Query          string `json:"query"`
	SearchMessages `json:"messages"`
	SearchFiles    `json:"files"`
	SlackResponse
}

func NewSearchParameters() SearchParameters {
	return SearchParameters{
		Sort:          DEFAULT_SEARCH_SORT,
		SortDirection: DEFAULT_SEARCH_SORT_DIR,
		Highlight:     DEFAULT_SEARCH_HIGHLIGHT,
		Count:         DEFAULT_SEARCH_COUNT,
		Page:          DEFAULT_SEARCH_PAGE,
	}
}

func (api *Client) _search(ctx context.Context, path, query string, params SearchParameters, files, messages bool) (response *searchResponseFull, error error) {
	values := url.Values{
		"token": {api.token},
		"query": {query},
	}
	if params.Sort != DEFAULT_SEARCH_SORT {
		values.Add("sort", params.Sort)
	}
	if params.SortDirection != DEFAULT_SEARCH_SORT_DIR {
		values.Add("sort_dir", params.SortDirection)
	}
	if params.Highlight != DEFAULT_SEARCH_HIGHLIGHT {
		values.Add("highlight", strconv.Itoa(1))
	}
	if params.Count != DEFAULT_SEARCH_COUNT {
		values.Add("count", strconv.Itoa(params.Count))
	}
	if params.Page != DEFAULT_SEARCH_PAGE {
		values.Add("page", strconv.Itoa(params.Page))
	}

	response = &searchResponseFull{}
	err := api.postMethod(ctx, path, values, response)
	if err != nil {
		return nil, err
	}

	return response, response.Err()

}

func (api *Client) Search(query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
	return api.SearchContext(context.Background(), query, params)
}

func (api *Client) SearchContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, *SearchFiles, error) {
	response, err := api._search(ctx, "search.all", query, params, true, true)
	if err != nil {
		return nil, nil, err
	}
	return &response.SearchMessages, &response.SearchFiles, nil
}

func (api *Client) SearchFiles(query string, params SearchParameters) (*SearchFiles, error) {
	return api.SearchFilesContext(context.Background(), query, params)
}

func (api *Client) SearchFilesContext(ctx context.Context, query string, params SearchParameters) (*SearchFiles, error) {
	response, err := api._search(ctx, "search.files", query, params, true, false)
	if err != nil {
		return nil, err
	}
	return &response.SearchFiles, nil
}

func (api *Client) SearchMessages(query string, params SearchParameters) (*SearchMessages, error) {
	return api.SearchMessagesContext(context.Background(), query, params)
}

func (api *Client) SearchMessagesContext(ctx context.Context, query string, params SearchParameters) (*SearchMessages, error) {
	response, err := api._search(ctx, "search.messages", query, params, false, true)
	if err != nil {
		return nil, err
	}
	return &response.SearchMessages, nil
}
<|endoftext|>


<commit_before>package collector

import "encoding/json"

// nodeStatsResponse is a representation of a Elasticsearch Node Stats
type nodeStatsResponse struct {
	ClusterName string `json:"cluster_name"`
	Nodes       map[string]NodeStatsNodeResponse
}

type NodeStatsNodeResponse struct {
	Name             string                                     `json:"name"`
	Host             string                                     `json:"host"`
	Timestamp        int64                                      `json:"timestamp"`
	TransportAddress string                                     `json:"transport_address"`
	Hostname         string                                     `json:"hostname"`
	Roles            []string                                   `json:"roles"`
	Attributes       map[string]string                          `json:"attributes"`
	Indices          NodeStatsIndicesResponse                   `json:"indices"`
	OS               NodeStatsOSResponse                        `json:"os"`
	Network          NodeStatsNetworkResponse                   `json:"network"`
	FS               NodeStatsFSResponse                        `json:"fs"`
	ThreadPool       map[string]NodeStatsThreadPoolPoolResponse `json:"thread_pool"`
	JVM              NodeStatsJVMResponse                       `json:"jvm"`
	Breakers         map[string]NodeStatsBreakersResponse       `json:"breakers"`
	HTTP             map[string]int                             `json:"http"`
	Transport        NodeStatsTransportResponse                 `json:"transport"`
	Process          NodeStatsProcessResponse                   `json:"process"`
}

// NodeStatsBreakersResponse is a representation of a statistics about the field data circuit breaker
type NodeStatsBreakersResponse struct {
	EstimatedSize int64   `json:"estimated_size_in_bytes"`
	LimitSize     int64   `json:"limit_size_in_bytes"`
	Overhead      float64 `json:"overhead"`
	Tripped       int64   `json:"tripped"`
}

// NodeStatsJVMResponse is a representation of a JVM stats, memory pool information, garbage collection, buffer pools, number of loaded/unloaded classes
type NodeStatsJVMResponse struct {
	BufferPools map[string]NodeStatsJVMBufferPoolResponse `json:"buffer_pools"`
	GC          NodeStatsJVMGCResponse                    `json:"gc"`
	Mem         NodeStatsJVMMemResponse                   `json:"mem"`
}

type NodeStatsJVMGCResponse struct {
	Collectors map[string]NodeStatsJVMGCCollectorResponse `json:"collectors"`
}

type NodeStatsJVMGCCollectorResponse struct {
	CollectionCount int64 `json:"collection_count"`
	CollectionTime  int64 `json:"collection_time_in_millis"`
}

type NodeStatsJVMBufferPoolResponse struct {
	Count         int64 `json:"count"`
	TotalCapacity int64 `json:"total_capacity_in_bytes"`
	Used          int64 `json:"used_in_bytes"`
}

type NodeStatsJVMMemResponse struct {
	HeapCommitted    int64                                  `json:"heap_committed_in_bytes"`
	HeapUsed         int64                                  `json:"heap_used_in_bytes"`
	HeapMax          int64                                  `json:"heap_max_in_bytes"`
	NonHeapCommitted int64                                  `json:"non_heap_committed_in_bytes"`
	NonHeapUsed      int64                                  `json:"non_heap_used_in_bytes"`
	Pools            map[string]NodeStatsJVMMemPoolResponse `json:"pools"`
}

type NodeStatsJVMMemPoolResponse struct {
	Used     int64 `json:"used_in_bytes"`
	Max      int64 `json:"max_in_bytes"`
	PeakUsed int64 `json:"peak_used_in_bytes"`
	PeakMax  int64 `json:"peak_max_in_bytes"`
}

type NodeStatsNetworkResponse struct {
	TCP NodeStatsTCPResponse `json:"tcp"`
}

// NodeStatsTransportResponse is a representation of a transport statistics about sent and received bytes in cluster communication
type NodeStatsTransportResponse struct {
	ServerOpen int64 `json:"server_open"`
	RxCount    int64 `json:"rx_count"`
	RxSize     int64 `json:"rx_size_in_bytes"`
	TxCount    int64 `json:"tx_count"`
	TxSize     int64 `json:"tx_size_in_bytes"`
}

// NodeStatsThreadPoolPoolResponse is a representation of a statistics about each thread pool, including current size, queue and rejected tasks
type NodeStatsThreadPoolPoolResponse struct {
	Threads   int64 `json:"threads"`
	Queue     int64 `json:"queue"`
	Active    int64 `json:"active"`
	Rejected  int64 `json:"rejected"`
	Largest   int64 `json:"largest"`
	Completed int64 `json:"completed"`
}

type NodeStatsTCPResponse struct {
	ActiveOpens  int64 `json:"active_opens"`
	PassiveOpens int64 `json:"passive_opens"`
	CurrEstab    int64 `json:"curr_estab"`
	InSegs       int64 `json:"in_segs"`
	OutSegs      int64 `json:"out_segs"`
	RetransSegs  int64 `json:"retrans_segs"`
	EstabResets  int64 `json:"estab_resets"`
	AttemptFails int64 `json:"attempt_fails"`
	InErrs       int64 `json:"in_errs"`
	OutRsts      int64 `json:"out_rsts"`
}

// NodeStatsIndicesResponse is a representation of a indices stats (size, document count, indexing and deletion times, search times, field cache size, merges and flushes)
type NodeStatsIndicesResponse struct {
	Docs         NodeStatsIndicesDocsResponse
	Store        NodeStatsIndicesStoreResponse
	Indexing     NodeStatsIndicesIndexingResponse
	Merges       NodeStatsIndicesMergesResponse
	Get          NodeStatsIndicesGetResponse
	Search       NodeStatsIndicesSearchResponse
	FieldData    NodeStatsIndicesCacheResponse `json:"fielddata"`
	FilterCache  NodeStatsIndicesCacheResponse `json:"filter_cache"`
	QueryCache   NodeStatsIndicesCacheResponse `json:"query_cache"`
	RequestCache NodeStatsIndicesCacheResponse `json:"request_cache"`
	Flush        NodeStatsIndicesFlushResponse
	Segments     NodeStatsIndicesSegmentsResponse
	Refresh      NodeStatsIndicesRefreshResponse
	Translog     NodeStatsIndicesTranslogResponse
}

type NodeStatsIndicesDocsResponse struct {
	Count   int64 `json:"count"`
	Deleted int64 `json:"deleted"`
}

type NodeStatsIndicesRefreshResponse struct {
	Total     int64 `json:"total"`
	TotalTime int64 `json:"total_time_in_millis"`
}

type NodeStatsIndicesTranslogResponse struct {
	Operations int64 `json:"operations"`
	Size       int64 `json:"size_in_bytes"`
}

type NodeStatsIndicesSegmentsResponse struct {
	Count  int64 `json:"count"`
	Memory int64 `json:"memory_in_bytes"`
}

type NodeStatsIndicesStoreResponse struct {
	Size         int64 `json:"size_in_bytes"`
	ThrottleTime int64 `json:"throttle_time_in_millis"`
}

type NodeStatsIndicesIndexingResponse struct {
	IndexTotal    int64 `json:"index_total"`
	IndexTime     int64 `json:"index_time_in_millis"`
	IndexCurrent  int64 `json:"index_current"`
	DeleteTotal   int64 `json:"delete_total"`
	DeleteTime    int64 `json:"delete_time_in_millis"`
	DeleteCurrent int64 `json:"delete_current"`
}

type NodeStatsIndicesMergesResponse struct {
	Current     int64 `json:"current"`
	CurrentDocs int64 `json:"current_docs"`
	CurrentSize int64 `json:"current_size_in_bytes"`
	Total       int64 `json:"total"`
	TotalDocs   int64 `json:"total_docs"`
	TotalSize   int64 `json:"total_size_in_bytes"`
	TotalTime   int64 `json:"total_time_in_millis"`
}

type NodeStatsIndicesGetResponse struct {
	Total        int64 `json:"total"`
	Time         int64 `json:"time_in_millis"`
	ExistsTotal  int64 `json:"exists_total"`
	ExistsTime   int64 `json:"exists_time_in_millis"`
	MissingTotal int64 `json:"missing_total"`
	MissingTime  int64 `json:"missing_time_in_millis"`
	Current      int64 `json:"current"`
}

type NodeStatsIndicesSearchResponse struct {
	OpenContext  int64 `json:"open_contexts"`
	QueryTotal   int64 `json:"query_total"`
	QueryTime    int64 `json:"query_time_in_millis"`
	QueryCurrent int64 `json:"query_current"`
	FetchTotal   int64 `json:"fetch_total"`
	FetchTime    int64 `json:"fetch_time_in_millis"`
	FetchCurrent int64 `json:"fetch_current"`
}

type NodeStatsIndicesFlushResponse struct {
	Total int64 `json:"total"`
	Time  int64 `json:"total_time_in_millis"`
}

type NodeStatsIndicesCacheResponse struct {
	Evictions  int64 `json:"evictions"`
	MemorySize int64 `json:"memory_size_in_bytes"`
	CacheCount int64 `json:"cache_count"`
	CacheSize  int64 `json:"cache_size"`
	HitCount   int64 `json:"hit_count"`
	MissCount  int64 `json:"miss_count"`
	TotalCount int64 `json:"total_count"`
}

// NodeStatsOSResponse is a representation of a  operating system stats, load average, mem, swap
type NodeStatsOSResponse struct {
	Timestamp int64 `json:"timestamp"`
	Uptime    int64 `json:"uptime_in_millis"`
	// LoadAvg was an array of per-cpu values pre-2.0, and is a string in 2.0
	// Leaving this here in case we want to implement parsing logic later
	LoadAvg json.RawMessage         `json:"load_average"`
	CPU     NodeStatsOSCPUResponse  `json:"cpu"`
	Mem     NodeStatsOSMemResponse  `json:"mem"`
	Swap    NodeStatsOSSwapResponse `json:"swap"`
}

type NodeStatsOSMemResponse struct {
	Free       int64 `json:"free_in_bytes"`
	Used       int64 `json:"used_in_bytes"`
	ActualFree int64 `json:"actual_free_in_bytes"`
	ActualUsed int64 `json:"actual_used_in_bytes"`
}

type NodeStatsOSSwapResponse struct {
	Used int64 `json:"used_in_bytes"`
	Free int64 `json:"free_in_bytes"`
}

type NodeStatsOSCPUResponse struct {
	Sys     int64                      `json:"sys"`
	User    int64                      `json:"user"`
	Idle    int64                      `json:"idle"`
	Steal   int64                      `json:"stolen"`
	LoadAvg NodeStatsOSCPULoadResponse `json:"load_average"`
	Percent int64                      `json:"percent"`
}

type NodeStatsOSCPULoadResponse struct {
	Load1  float64 `json:"1m"`
	Load5  float64 `json:"5m"`
	Load15 float64 `json:"15m"`
}

// NodeStatsProcessResponse is a representation of a process statistics, memory consumption, cpu usage, open file descriptors
type NodeStatsProcessResponse struct {
	Timestamp int64                       `json:"timestamp"`
	OpenFD    int64                       `json:"open_file_descriptors"`
	MaxFD     int64                       `json:"max_file_descriptors"`
	CPU       NodeStatsProcessCPUResponse `json:"cpu"`
	Memory    NodeStatsProcessMemResponse `json:"mem"`
}

type NodeStatsProcessMemResponse struct {
	Resident     int64 `json:"resident_in_bytes"`
	Share        int64 `json:"share_in_bytes"`
	TotalVirtual int64 `json:"total_virtual_in_bytes"`
}

type NodeStatsProcessCPUResponse struct {
	Percent int64 `json:"percent"`
	Sys     int64 `json:"sys_in_millis"`
	User    int64 `json:"user_in_millis"`
	Total   int64 `json:"total_in_millis"`
}

type NodeStatsHTTPResponse struct {
	CurrentOpen int64 `json:"current_open"`
	TotalOpen   int64 `json:"total_open"`
}

// NodeStatsFSResponse is a representation of a file system information, data path, free disk space, read/write stats
type NodeStatsFSResponse struct {
	Timestamp int64                     `json:"timestamp"`
	Data      []NodeStatsFSDataResponse `json:"data"`
}

type NodeStatsFSDataResponse struct {
	Path          string `json:"path"`
	Mount         string `json:"mount"`
	Device        string `json:"dev"`
	Total         int64  `json:"total_in_bytes"`
	Free          int64  `json:"free_in_bytes"`
	Available     int64  `json:"available_in_bytes"`
	DiskReads     int64  `json:"disk_reads"`
	DiskWrites    int64  `json:"disk_writes"`
	DiskReadSize  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize int64  `json:"disk_write_size_in_bytes"`
}

// ClusterHealthResponse is a representation of a Elasticsearch Cluster Health
type ClusterHealthResponse struct {
	ActivePrimaryShards     int64  `json:"active_primary_shards"`
	ActiveShards            int64  `json:"active_shards"`
	ClusterName             string `json:"cluster_name"`
	DelayedUnassignedShards int64  `json:"delayed_unassigned_shards"`
	InitializingShards      int64  `json:"initializing_shards"`
	NumberOfDataNodes       int64  `json:"number_of_data_nodes"`
	NumberOfInFlightFetch   int64  `json:"number_of_in_flight_fetch"`
	NumberOfNodes           int64  `json:"number_of_nodes"`
	NumberOfPendingTasks    int64  `json:"number_of_pending_tasks"`
	RelocatingShards        int64  `json:"relocating_shards"`
	Status                  string `json:"status"`
	TimedOut                bool   `json:"timed_out"`
	UnassignedShards        int64  `json:"unassigned_shards"`
}
<commit_msg>collector/nodes_response.go: Fix many golint issues.<commit_after>package collector

import "encoding/json"

// nodeStatsResponse is a representation of a Elasticsearch Node Stats
type nodeStatsResponse struct {
	ClusterName string `json:"cluster_name"`
	Nodes       map[string]NodeStatsNodeResponse
}

// NodeStatsNodeResponse defines node stats information structure for nodes
type NodeStatsNodeResponse struct {
	Name             string                                     `json:"name"`
	Host             string                                     `json:"host"`
	Timestamp        int64                                      `json:"timestamp"`
	TransportAddress string                                     `json:"transport_address"`
	Hostname         string                                     `json:"hostname"`
	Roles            []string                                   `json:"roles"`
	Attributes       map[string]string                          `json:"attributes"`
	Indices          NodeStatsIndicesResponse                   `json:"indices"`
	OS               NodeStatsOSResponse                        `json:"os"`
	Network          NodeStatsNetworkResponse                   `json:"network"`
	FS               NodeStatsFSResponse                        `json:"fs"`
	ThreadPool       map[string]NodeStatsThreadPoolPoolResponse `json:"thread_pool"`
	JVM              NodeStatsJVMResponse                       `json:"jvm"`
	Breakers         map[string]NodeStatsBreakersResponse       `json:"breakers"`
	HTTP             map[string]int                             `json:"http"`
	Transport        NodeStatsTransportResponse                 `json:"transport"`
	Process          NodeStatsProcessResponse                   `json:"process"`
}

// NodeStatsBreakersResponse is a representation of a statistics about the field data circuit breaker
type NodeStatsBreakersResponse struct {
	EstimatedSize int64   `json:"estimated_size_in_bytes"`
	LimitSize     int64   `json:"limit_size_in_bytes"`
	Overhead      float64 `json:"overhead"`
	Tripped       int64   `json:"tripped"`
}

// NodeStatsJVMResponse is a representation of a JVM stats, memory pool information, garbage collection, buffer pools, number of loaded/unloaded classes
type NodeStatsJVMResponse struct {
	BufferPools map[string]NodeStatsJVMBufferPoolResponse `json:"buffer_pools"`
	GC          NodeStatsJVMGCResponse                    `json:"gc"`
	Mem         NodeStatsJVMMemResponse                   `json:"mem"`
}

// NodeStatsJVMGCResponse defines node stats JVM garbage collector information structure
type NodeStatsJVMGCResponse struct {
	Collectors map[string]NodeStatsJVMGCCollectorResponse `json:"collectors"`
}

// NodeStatsJVMGCCollectorResponse defines node stats JVM garbage collector collection information structure
type NodeStatsJVMGCCollectorResponse struct {
	CollectionCount int64 `json:"collection_count"`
	CollectionTime  int64 `json:"collection_time_in_millis"`
}

// NodeStatsJVMBufferPoolResponse defines node stats JVM buffer pool information structure
type NodeStatsJVMBufferPoolResponse struct {
	Count         int64 `json:"count"`
	TotalCapacity int64 `json:"total_capacity_in_bytes"`
	Used          int64 `json:"used_in_bytes"`
}

// NodeStatsJVMMemResponse defines node stats JVM memory information structure
type NodeStatsJVMMemResponse struct {
	HeapCommitted    int64                                  `json:"heap_committed_in_bytes"`
	HeapUsed         int64                                  `json:"heap_used_in_bytes"`
	HeapMax          int64                                  `json:"heap_max_in_bytes"`
	NonHeapCommitted int64                                  `json:"non_heap_committed_in_bytes"`
	NonHeapUsed      int64                                  `json:"non_heap_used_in_bytes"`
	Pools            map[string]NodeStatsJVMMemPoolResponse `json:"pools"`
}

// NodeStatsJVMMemPoolResponse defines node stats JVM memory pool information structure
type NodeStatsJVMMemPoolResponse struct {
	Used     int64 `json:"used_in_bytes"`
	Max      int64 `json:"max_in_bytes"`
	PeakUsed int64 `json:"peak_used_in_bytes"`
	PeakMax  int64 `json:"peak_max_in_bytes"`
}

// NodeStatsNetworkResponse defines node stats network information structure
type NodeStatsNetworkResponse struct {
	TCP NodeStatsTCPResponse `json:"tcp"`
}

// NodeStatsTransportResponse is a representation of a transport statistics about sent and received bytes in cluster communication
type NodeStatsTransportResponse struct {
	ServerOpen int64 `json:"server_open"`
	RxCount    int64 `json:"rx_count"`
	RxSize     int64 `json:"rx_size_in_bytes"`
	TxCount    int64 `json:"tx_count"`
	TxSize     int64 `json:"tx_size_in_bytes"`
}

// NodeStatsThreadPoolPoolResponse is a representation of a statistics about each thread pool, including current size, queue and rejected tasks
type NodeStatsThreadPoolPoolResponse struct {
	Threads   int64 `json:"threads"`
	Queue     int64 `json:"queue"`
	Active    int64 `json:"active"`
	Rejected  int64 `json:"rejected"`
	Largest   int64 `json:"largest"`
	Completed int64 `json:"completed"`
}

// NodeStatsTCPResponse defines node stats TCP information structure
type NodeStatsTCPResponse struct {
	ActiveOpens  int64 `json:"active_opens"`
	PassiveOpens int64 `json:"passive_opens"`
	CurrEstab    int64 `json:"curr_estab"`
	InSegs       int64 `json:"in_segs"`
	OutSegs      int64 `json:"out_segs"`
	RetransSegs  int64 `json:"retrans_segs"`
	EstabResets  int64 `json:"estab_resets"`
	AttemptFails int64 `json:"attempt_fails"`
	InErrs       int64 `json:"in_errs"`
	OutRsts      int64 `json:"out_rsts"`
}

// NodeStatsIndicesResponse is a representation of a indices stats (size, document count, indexing and deletion times, search times, field cache size, merges and flushes)
type NodeStatsIndicesResponse struct {
	Docs         NodeStatsIndicesDocsResponse
	Store        NodeStatsIndicesStoreResponse
	Indexing     NodeStatsIndicesIndexingResponse
	Merges       NodeStatsIndicesMergesResponse
	Get          NodeStatsIndicesGetResponse
	Search       NodeStatsIndicesSearchResponse
	FieldData    NodeStatsIndicesCacheResponse `json:"fielddata"`
	FilterCache  NodeStatsIndicesCacheResponse `json:"filter_cache"`
	QueryCache   NodeStatsIndicesCacheResponse `json:"query_cache"`
	RequestCache NodeStatsIndicesCacheResponse `json:"request_cache"`
	Flush        NodeStatsIndicesFlushResponse
	Segments     NodeStatsIndicesSegmentsResponse
	Refresh      NodeStatsIndicesRefreshResponse
	Translog     NodeStatsIndicesTranslogResponse
}

// NodeStatsIndicesDocsResponse defines node stats docs information structure for indices
type NodeStatsIndicesDocsResponse struct {
	Count   int64 `json:"count"`
	Deleted int64 `json:"deleted"`
}

// NodeStatsIndicesRefreshResponse defines node stats refresh information structure for indices
type NodeStatsIndicesRefreshResponse struct {
	Total     int64 `json:"total"`
	TotalTime int64 `json:"total_time_in_millis"`
}

// NodeStatsIndicesTranslogResponse defines node stats translog information structure for indices
type NodeStatsIndicesTranslogResponse struct {
	Operations int64 `json:"operations"`
	Size       int64 `json:"size_in_bytes"`
}

// NodeStatsIndicesSegmentsResponse defines node stats segments information structure for indices
type NodeStatsIndicesSegmentsResponse struct {
	Count  int64 `json:"count"`
	Memory int64 `json:"memory_in_bytes"`
}

// NodeStatsIndicesStoreResponse defines node stats store information structure for indices
type NodeStatsIndicesStoreResponse struct {
	Size         int64 `json:"size_in_bytes"`
	ThrottleTime int64 `json:"throttle_time_in_millis"`
}

// NodeStatsIndicesIndexingResponse defines node stats indexing information structure for indices
type NodeStatsIndicesIndexingResponse struct {
	IndexTotal    int64 `json:"index_total"`
	IndexTime     int64 `json:"index_time_in_millis"`
	IndexCurrent  int64 `json:"index_current"`
	DeleteTotal   int64 `json:"delete_total"`
	DeleteTime    int64 `json:"delete_time_in_millis"`
	DeleteCurrent int64 `json:"delete_current"`
}

// NodeStatsIndicesMergesResponse defines node stats merges information structure for indices
type NodeStatsIndicesMergesResponse struct {
	Current     int64 `json:"current"`
	CurrentDocs int64 `json:"current_docs"`
	CurrentSize int64 `json:"current_size_in_bytes"`
	Total       int64 `json:"total"`
	TotalDocs   int64 `json:"total_docs"`
	TotalSize   int64 `json:"total_size_in_bytes"`
	TotalTime   int64 `json:"total_time_in_millis"`
}

// NodeStatsIndicesGetResponse defines node stats get information structure for indices
type NodeStatsIndicesGetResponse struct {
	Total        int64 `json:"total"`
	Time         int64 `json:"time_in_millis"`
	ExistsTotal  int64 `json:"exists_total"`
	ExistsTime   int64 `json:"exists_time_in_millis"`
	MissingTotal int64 `json:"missing_total"`
	MissingTime  int64 `json:"missing_time_in_millis"`
	Current      int64 `json:"current"`
}

// NodeStatsIndicesSearchResponse defines node stats search information structure for indices
type NodeStatsIndicesSearchResponse struct {
	OpenContext  int64 `json:"open_contexts"`
	QueryTotal   int64 `json:"query_total"`
	QueryTime    int64 `json:"query_time_in_millis"`
	QueryCurrent int64 `json:"query_current"`
	FetchTotal   int64 `json:"fetch_total"`
	FetchTime    int64 `json:"fetch_time_in_millis"`
	FetchCurrent int64 `json:"fetch_current"`
}

// NodeStatsIndicesFlushResponse defines node stats flush information structure for indices
type NodeStatsIndicesFlushResponse struct {
	Total int64 `json:"total"`
	Time  int64 `json:"total_time_in_millis"`
}

// NodeStatsIndicesCacheResponse defines node stats cache information structure for indices
type NodeStatsIndicesCacheResponse struct {
	Evictions  int64 `json:"evictions"`
	MemorySize int64 `json:"memory_size_in_bytes"`
	CacheCount int64 `json:"cache_count"`
	CacheSize  int64 `json:"cache_size"`
	HitCount   int64 `json:"hit_count"`
	MissCount  int64 `json:"miss_count"`
	TotalCount int64 `json:"total_count"`
}

// NodeStatsOSResponse is a representation of a  operating system stats, load average, mem, swap
type NodeStatsOSResponse struct {
	Timestamp int64 `json:"timestamp"`
	Uptime    int64 `json:"uptime_in_millis"`
	// LoadAvg was an array of per-cpu values pre-2.0, and is a string in 2.0
	// Leaving this here in case we want to implement parsing logic later
	LoadAvg json.RawMessage         `json:"load_average"`
	CPU     NodeStatsOSCPUResponse  `json:"cpu"`
	Mem     NodeStatsOSMemResponse  `json:"mem"`
	Swap    NodeStatsOSSwapResponse `json:"swap"`
}

// NodeStatsOSMemResponse defines node stats operating system memory usage structure
type NodeStatsOSMemResponse struct {
	Free       int64 `json:"free_in_bytes"`
	Used       int64 `json:"used_in_bytes"`
	ActualFree int64 `json:"actual_free_in_bytes"`
	ActualUsed int64 `json:"actual_used_in_bytes"`
}

// NodeStatsOSSwapResponse defines node stats operating system swap usage structure
type NodeStatsOSSwapResponse struct {
	Used int64 `json:"used_in_bytes"`
	Free int64 `json:"free_in_bytes"`
}

// NodeStatsOSCPUResponse defines node stats operating system CPU usage structure
type NodeStatsOSCPUResponse struct {
	Sys     int64                      `json:"sys"`
	User    int64                      `json:"user"`
	Idle    int64                      `json:"idle"`
	Steal   int64                      `json:"stolen"`
	LoadAvg NodeStatsOSCPULoadResponse `json:"load_average"`
	Percent int64                      `json:"percent"`
}

// NodeStatsOSCPULoadResponse defines node stats operating system CPU load structure
type NodeStatsOSCPULoadResponse struct {
	Load1  float64 `json:"1m"`
	Load5  float64 `json:"5m"`
	Load15 float64 `json:"15m"`
}

// NodeStatsProcessResponse is a representation of a process statistics, memory consumption, cpu usage, open file descriptors
type NodeStatsProcessResponse struct {
	Timestamp int64                       `json:"timestamp"`
	OpenFD    int64                       `json:"open_file_descriptors"`
	MaxFD     int64                       `json:"max_file_descriptors"`
	CPU       NodeStatsProcessCPUResponse `json:"cpu"`
	Memory    NodeStatsProcessMemResponse `json:"mem"`
}

// NodeStatsProcessMemResponse defines node stats process memory usage structure
type NodeStatsProcessMemResponse struct {
	Resident     int64 `json:"resident_in_bytes"`
	Share        int64 `json:"share_in_bytes"`
	TotalVirtual int64 `json:"total_virtual_in_bytes"`
}

// NodeStatsProcessCPUResponse defines node stats process CPU usage structure
type NodeStatsProcessCPUResponse struct {
	Percent int64 `json:"percent"`
	Sys     int64 `json:"sys_in_millis"`
	User    int64 `json:"user_in_millis"`
	Total   int64 `json:"total_in_millis"`
}

// NodeStatsHTTPResponse defines node stats HTTP connections structure
type NodeStatsHTTPResponse struct {
	CurrentOpen int64 `json:"current_open"`
	TotalOpen   int64 `json:"total_open"`
}

// NodeStatsFSResponse is a representation of a file system information, data path, free disk space, read/write stats
type NodeStatsFSResponse struct {
	Timestamp int64                     `json:"timestamp"`
	Data      []NodeStatsFSDataResponse `json:"data"`
}

// NodeStatsFSDataResponse defines node stats filesystem data structure
type NodeStatsFSDataResponse struct {
	Path          string `json:"path"`
	Mount         string `json:"mount"`
	Device        string `json:"dev"`
	Total         int64  `json:"total_in_bytes"`
	Free          int64  `json:"free_in_bytes"`
	Available     int64  `json:"available_in_bytes"`
	DiskReads     int64  `json:"disk_reads"`
	DiskWrites    int64  `json:"disk_writes"`
	DiskReadSize  int64  `json:"disk_read_size_in_bytes"`
	DiskWriteSize int64  `json:"disk_write_size_in_bytes"`
}

// ClusterHealthResponse is a representation of a Elasticsearch Cluster Health
type ClusterHealthResponse struct {
	ActivePrimaryShards     int64  `json:"active_primary_shards"`
	ActiveShards            int64  `json:"active_shards"`
	ClusterName             string `json:"cluster_name"`
	DelayedUnassignedShards int64  `json:"delayed_unassigned_shards"`
	InitializingShards      int64  `json:"initializing_shards"`
	NumberOfDataNodes       int64  `json:"number_of_data_nodes"`
	NumberOfInFlightFetch   int64  `json:"number_of_in_flight_fetch"`
	NumberOfNodes           int64  `json:"number_of_nodes"`
	NumberOfPendingTasks    int64  `json:"number_of_pending_tasks"`
	RelocatingShards        int64  `json:"relocating_shards"`
	Status                  string `json:"status"`
	TimedOut                bool   `json:"timed_out"`
	UnassignedShards        int64  `json:"unassigned_shards"`
}
<|endoftext|>


<commit_before>package console

func NewHelpCommand() *Command {
	cmd := NewCommand("help")

	var opts struct {
		Command string `short:"c" long:"command" description:"Command name" required:"true"`
	}

	var HelpHandler = func(app *Application) {
		commands := app.GetCommands()
		commands[opts.Command].PrintHelp(app.GetOutput())

		app.GetOutput().Write("\n@{y}Available commands:\n", 10)
		for name, command := range commands {
			app.GetOutput().Write("  @{g}%-20s@{!}\t%s\n", 10, name, command.GetDescription())
		}
	}

	cmd.SetDescription("Displays help for a given command")
	cmd.SetHelp("The help command displays help for a given command")
	cmd.SetHandler(HelpHandler)
	cmd.SetOptions(&opts)
	return cmd
}
<commit_msg>pretty help<commit_after>package console

func NewHelpCommand() *Command {
	cmd := NewCommand("help")
	cmd.SetDescription("Displays help for a given command")
	cmd.SetHelp("The help command displays help for a given command")

	var options struct {
		Command string `short:"c" long:"command" description:"Command name" required:"true"`
	}
	cmd.SetOptions(&options)

	var handler = func(app *Application) {
		commands := app.GetCommands()
		commands[options.Command].PrintHelp(app.GetOutput())

		app.GetOutput().Write("\n@{y}Available commands:\n", 10)
		for name, command := range commands {
			app.GetOutput().Write("  @{g}%-20s@{!}\t%s\n", 10, name, command.GetDescription())
		}
	}
	cmd.SetHandler(handler)

	return cmd
}
<|endoftext|>


<commit_before>package gocode

import (
	"go/ast"
	"go/parser"
	"go/token"
	"gosubli.me/mg"
	"gosubli.me/something-borrowed/gocode"
	"io/ioutil"
	"path/filepath"
	"strings"
)

type mGocode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int

	calltip bool
}

type Res struct {
	Candidates  []gocode.MargoCandidate
	Suggestions []string
}

type calltipVisitor struct {
	offset int
	fset   *token.FileSet
	x      *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch x := node.(type) {
	case *ast.CallExpr:
		a := v.fset.Position(x.Pos())
		b := v.fset.Position(x.End())
		if (a.IsValid() && v.offset >= a.Offset) && (!b.IsValid() || v.offset <= b.Offset) {
			v.x = x
		}
	case *ast.AssignStmt:
		check := func(l ast.Node, r ast.Node) bool {
			a := v.fset.Position(l.Pos())
			b := v.fset.Position(l.End())
			if v.offset >= a.Offset && v.offset <= b.Offset {
				if c, ok := r.(*ast.CallExpr); ok {
					v.x = c
				}
				return true
			}
			return false
		}

		if len(x.Rhs) == 1 {
			check(x, x.Rhs[0])
		} else {
			for i, n := range x.Lhs {
				if i >= len(x.Rhs) || check(n, x.Rhs[i]) {
					break
				}
			}
		}
	}
	return v
}

func (m *mGocode) Call() (interface{}, string) {
	if m.Src == "" {
		// this is here for testing, the client should always send the src
		s, _ := ioutil.ReadFile(m.Fn)
		m.Src = string(s)
	}

	if m.Src == "" {
		return nil, "No source"
	}

	pos := mg.BytePos(m.Src, m.Pos)
	if m.Pos < 0 {
		return nil, "Invalid offset"
	}

	src := []byte(m.Src)
	fn := m.Fn
	if !filepath.IsAbs(fn) {
		fn = filepath.Join(mg.OrString(m.Dir, m.Home), mg.OrString(fn, "_.go"))
	}

	res := Res{}
	if m.calltip {
		res.Candidates = m.calltips(src, fn, pos)
	} else {
		res.Candidates = m.completions(src, fn, pos)
	}

	if m.Autoinst && len(res.Candidates) == 0 {
		mg.AutoInstall(mg.AutoInstOptions{
			Src:           m.Src,
			Env:           m.Env,
			InstallSuffix: m.InstallSuffix,
		})
	}

	return res, ""
}

func (g *mGocode) completions(src []byte, fn string, pos int) []gocode.MargoCandidate {
	c := gocode.MargoConfig{}
	c.InstallSuffix = g.InstallSuffix
	c.Builtins = g.Builtins
	c.GOROOT, c.GOPATHS = mg.RootPaths(g.Env)
	return gocode.Margo.Complete(c, src, fn, pos)
}

func (m *mGocode) calltips(src []byte, fn string, offset int) []gocode.MargoCandidate {
	id, fset, af := identAtOffset(src, offset)
	if id != nil {
		cp := fset.Position(id.End())
		if cp.IsValid() {
			line := offsetLine(fset, af, offset)
			cr := cp.Offset
			cl := m.completions(src, fn, cr)

			if (cp.Line == line || line == 0) && len(cl) > 0 {
				for i, c := range cl {
					if strings.EqualFold(id.Name, c.Name) {
						return cl[i : i+1]
					}
				}
			}
		}
	}

	return []gocode.MargoCandidate{}
}

func identAtOffset(src []byte, offset int) (id *ast.Ident, fset *token.FileSet, af *ast.File) {
	fset = token.NewFileSet()
	af, _ = parser.ParseFile(fset, "<stdin>", src, 0)

	if af == nil {
		return
	}

	vis := &calltipVisitor{
		offset: offset,
		fset:   fset,
	}
	ast.Walk(vis, af)

	if vis.x != nil && vis.x.Fun != nil {
		switch v := vis.x.Fun.(type) {
		case *ast.Ident:
			id = v
		case *ast.SelectorExpr:
			id = v.Sel
		}
	}
	return
}

func offsetLine(fset *token.FileSet, af *ast.File, offset int) (line int) {
	defer func() {
		if err := recover(); err != nil {
			line = 0
		}
	}()
	return fset.File(af.Pos()).Position(token.Pos(offset)).Line
}

func init() {
	mg.Register("gocode_complete", func(b *mg.Broker) mg.Caller {
		return &mGocode{}
	})

	mg.Register("gocode_calltip", func(b *mg.Broker) mg.Caller {
		return &mGocode{calltip: true}
	})
}
<commit_msg>* move initialiation code out of mGocode.Call<commit_after>package gocode

import (
	"go/ast"
	"go/parser"
	"go/token"
	"gosubli.me/mg"
	"gosubli.me/something-borrowed/gocode"
	"io/ioutil"
	"path/filepath"
	"strings"
)

type mGocode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int

	calltip bool
}

type Res struct {
	Candidates  []gocode.MargoCandidate
	Suggestions []string
}

type calltipVisitor struct {
	offset int
	fset   *token.FileSet
	x      *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch x := node.(type) {
	case *ast.CallExpr:
		a := v.fset.Position(x.Pos())
		b := v.fset.Position(x.End())
		if (a.IsValid() && v.offset >= a.Offset) && (!b.IsValid() || v.offset <= b.Offset) {
			v.x = x
		}
	case *ast.AssignStmt:
		check := func(l ast.Node, r ast.Node) bool {
			a := v.fset.Position(l.Pos())
			b := v.fset.Position(l.End())
			if v.offset >= a.Offset && v.offset <= b.Offset {
				if c, ok := r.(*ast.CallExpr); ok {
					v.x = c
				}
				return true
			}
			return false
		}

		if len(x.Rhs) == 1 {
			check(x, x.Rhs[0])
		} else {
			for i, n := range x.Lhs {
				if i >= len(x.Rhs) || check(n, x.Rhs[i]) {
					break
				}
			}
		}
	}
	return v
}

func (m *mGocode) init() string {
	if !filepath.IsAbs(m.Fn) {
		m.Fn = filepath.Join(mg.OrString(m.Dir, m.Home), mg.OrString(m.Fn, "_.go"))
	}

	if m.Src == "" {
		// this is here for testing, the client should always send the src
		s, _ := ioutil.ReadFile(m.Fn)
		m.Src = string(s)
	}

	if m.Src == "" {
		return "No source"
	}

	m.Pos = mg.BytePos(m.Src, m.Pos)
	if m.Pos < 0 {
		return "Invalid offset"
	}

	return ""
}

func (m *mGocode) Call() (interface{}, string) {
	if e := m.init(); e != "" {
		return nil, e
	}

	res := Res{}
	if m.calltip {
		res.Candidates = m.calltips([]byte(m.Src), m.Fn, m.Pos)
	} else {
		res.Candidates = m.completions([]byte(m.Src), m.Fn, m.Pos)
	}

	if m.Autoinst && len(res.Candidates) == 0 {
		mg.AutoInstall(mg.AutoInstOptions{
			Src:           m.Src,
			Env:           m.Env,
			InstallSuffix: m.InstallSuffix,
		})
	}

	return res, ""
}

func (g *mGocode) completions(src []byte, fn string, pos int) []gocode.MargoCandidate {
	c := gocode.MargoConfig{}
	c.InstallSuffix = g.InstallSuffix
	c.Builtins = g.Builtins
	c.GOROOT, c.GOPATHS = mg.RootPaths(g.Env)
	return gocode.Margo.Complete(c, src, fn, pos)
}

func (m *mGocode) calltips(src []byte, fn string, offset int) []gocode.MargoCandidate {
	id, fset, af := identAtOffset(src, offset)
	if id != nil {
		cp := fset.Position(id.End())
		if cp.IsValid() {
			line := offsetLine(fset, af, offset)
			cr := cp.Offset
			cl := m.completions(src, fn, cr)

			if (cp.Line == line || line == 0) && len(cl) > 0 {
				for i, c := range cl {
					if strings.EqualFold(id.Name, c.Name) {
						return cl[i : i+1]
					}
				}
			}
		}
	}

	return []gocode.MargoCandidate{}
}

func identAtOffset(src []byte, offset int) (id *ast.Ident, fset *token.FileSet, af *ast.File) {
	fset = token.NewFileSet()
	af, _ = parser.ParseFile(fset, "<stdin>", src, 0)

	if af == nil {
		return
	}

	vis := &calltipVisitor{
		offset: offset,
		fset:   fset,
	}
	ast.Walk(vis, af)

	if vis.x != nil && vis.x.Fun != nil {
		switch v := vis.x.Fun.(type) {
		case *ast.Ident:
			id = v
		case *ast.SelectorExpr:
			id = v.Sel
		}
	}
	return
}

func offsetLine(fset *token.FileSet, af *ast.File, offset int) (line int) {
	defer func() {
		if err := recover(); err != nil {
			line = 0
		}
	}()
	return fset.File(af.Pos()).Position(token.Pos(offset)).Line
}

func init() {
	mg.Register("gocode_complete", func(b *mg.Broker) mg.Caller {
		return &mGocode{}
	})

	mg.Register("gocode_calltip", func(b *mg.Broker) mg.Caller {
		return &mGocode{calltip: true}
	})
}
<|endoftext|>


<commit_before>package kafka_consumer

import (
	"fmt"
	"testing"
	"time"

	"github.com/Shopify/sarama"
	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/influxdata/telegraf/plugins/parsers"
)

func TestReadsMetricsFromKafka(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	brokerPeers := []string{testutil.GetLocalHost() + ":9092"}
	testTopic := fmt.Sprintf("telegraf_test_topic_%d", time.Now().Unix())

	// Send a Kafka message to the kafka host
	msg := "cpu_load_short,direction=in,host=server01,region=us-west value=23422.0 1422568543702900257\n"
	producer, err := sarama.NewSyncProducer(brokerPeers, nil)
	require.NoError(t, err)
	_, _, err = producer.SendMessage(
		&sarama.ProducerMessage{
			Topic: testTopic,
			Value: sarama.StringEncoder(msg),
		})
	require.NoError(t, err)
	defer producer.Close()

	// Start the Kafka Consumer
	k := &Kafka{
		ConsumerGroup: "telegraf_test_consumers",
		Topics:        []string{testTopic},
		Brokers:       brokerPeers,
		PointBuffer:   100000,
		Offset:        "oldest",
	}
	p, _ := parsers.NewInfluxParser()
	k.SetParser(p)

	// Verify that we can now gather the sent message
	var acc testutil.Accumulator

	// Sanity check
	assert.Equal(t, 0, len(acc.Metrics), "There should not be any points")
	if err := k.Start(&acc); err != nil {
		t.Fatal(err.Error())
	} else {
		defer k.Stop()
	}

	waitForPoint(&acc, t)

	// Gather points
	err = acc.GatherError(k.Gather)
	require.NoError(t, err)
	if len(acc.Metrics) == 1 {
		point := acc.Metrics[0]
		assert.Equal(t, "cpu_load_short", point.Measurement)
		assert.Equal(t, map[string]interface{}{"value": 23422.0}, point.Fields)
		assert.Equal(t, map[string]string{
			"host":      "server01",
			"direction": "in",
			"region":    "us-west",
		}, point.Tags)
		assert.Equal(t, time.Unix(0, 1422568543702900257).Unix(), point.Time.Unix())
	} else {
		t.Errorf("No points found in accumulator, expected 1")
	}
}

// Waits for the metric that was sent to the kafka broker to arrive at the kafka
// consumer
func waitForPoint(acc *testutil.Accumulator, t *testing.T) {
	// Give the kafka container up to 2 seconds to get the point to the consumer
	ticker := time.NewTicker(5 * time.Millisecond)
	counter := 0
	for {
		select {
		case <-ticker.C:
			counter++
			if counter > 1000 {
				t.Fatal("Waited for 5s, point never arrived to consumer")
			} else if acc.NFields() == 1 {
				return
			}
		}
	}
}
<commit_msg>Skip kafka_consumer_integration_test due to issue on CircleCI<commit_after>package kafka_consumer

import (
	"fmt"
	"testing"
	"time"

	"github.com/Shopify/sarama"
	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/influxdata/telegraf/plugins/parsers"
)

func TestReadsMetricsFromKafka(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}
	t.Skip("Skipping test due to circleci issue; ref #2487")

	brokerPeers := []string{testutil.GetLocalHost() + ":9092"}
	testTopic := fmt.Sprintf("telegraf_test_topic_%d", time.Now().Unix())

	// Send a Kafka message to the kafka host
	msg := "cpu_load_short,direction=in,host=server01,region=us-west value=23422.0 1422568543702900257\n"
	producer, err := sarama.NewSyncProducer(brokerPeers, nil)
	require.NoError(t, err)
	_, _, err = producer.SendMessage(
		&sarama.ProducerMessage{
			Topic: testTopic,
			Value: sarama.StringEncoder(msg),
		})
	require.NoError(t, err)
	defer producer.Close()

	// Start the Kafka Consumer
	k := &Kafka{
		ConsumerGroup: "telegraf_test_consumers",
		Topics:        []string{testTopic},
		Brokers:       brokerPeers,
		PointBuffer:   100000,
		Offset:        "oldest",
	}
	p, _ := parsers.NewInfluxParser()
	k.SetParser(p)

	// Verify that we can now gather the sent message
	var acc testutil.Accumulator

	// Sanity check
	assert.Equal(t, 0, len(acc.Metrics), "There should not be any points")
	if err := k.Start(&acc); err != nil {
		t.Fatal(err.Error())
	} else {
		defer k.Stop()
	}

	waitForPoint(&acc, t)

	// Gather points
	err = acc.GatherError(k.Gather)
	require.NoError(t, err)
	if len(acc.Metrics) == 1 {
		point := acc.Metrics[0]
		assert.Equal(t, "cpu_load_short", point.Measurement)
		assert.Equal(t, map[string]interface{}{"value": 23422.0}, point.Fields)
		assert.Equal(t, map[string]string{
			"host":      "server01",
			"direction": "in",
			"region":    "us-west",
		}, point.Tags)
		assert.Equal(t, time.Unix(0, 1422568543702900257).Unix(), point.Time.Unix())
	} else {
		t.Errorf("No points found in accumulator, expected 1")
	}
}

// Waits for the metric that was sent to the kafka broker to arrive at the kafka
// consumer
func waitForPoint(acc *testutil.Accumulator, t *testing.T) {
	// Give the kafka container up to 2 seconds to get the point to the consumer
	ticker := time.NewTicker(5 * time.Millisecond)
	counter := 0
	for {
		select {
		case <-ticker.C:
			counter++
			if counter > 1000 {
				t.Fatal("Waited for 5s, point never arrived to consumer")
			} else if acc.NFields() == 1 {
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now().UTC()
	fmt.Println("Location:", t.Location(), ":Time:", t.Format(time.RFC3339Nano))
}
<commit_msg>sync Mon May  7 09:56:04 CEST 2018<commit_after>package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now().UTC()
	fmt.Println("Location:", t.Location(), ":Time:", t.Format(time.RFC3339Nano))
	fmt.Println("UnixNano:", t.UnixNano())
}
<|endoftext|>


<commit_before>package mlog

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestTime(t *testing.T) {
	b := &sliceBuffer{make([]byte, 0, 1024)}
	loc, _ := time.LoadLocation("America/Los_Angeles")

	// test nanos
	tm := time.Date(2016, time.November, 1, 2, 3, 4, 5, loc)
	writeTime(b, &tm, Lnanoseconds)
	assert.Equal(t, `2016-11-01T02:03:04.000000005-07:00`, b.String(),
		"time written incorrectly")

	b.Truncate(0)
	tm = time.Date(2016, time.January, 11, 12, 13, 14, 15, time.UTC)
	writeTime(b, &tm, Lnanoseconds)
	assert.Equal(t, `2016-01-11T12:13:14.000000015Z`, b.String(),
		"time written incorrectly")

	// test micros
	b.Truncate(0)
	tm = time.Date(2016, time.November, 1, 2, 3, 4, 5000, loc)
	writeTime(b, &tm, Lmicroseconds)
	assert.Equal(t, `2016-11-01T02:03:04.000005-07:00`, b.String(),
		"time written incorrectly")

	b.Truncate(0)
	tm = time.Date(2016, time.January, 11, 12, 13, 14, 15000, time.UTC)
	writeTime(b, &tm, Lmicroseconds)
	assert.Equal(t, `2016-01-11T12:13:14.000015Z`, b.String(),
		"time written incorrectly")

}
<commit_msg>cleanup<commit_after>package mlog

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestTime(t *testing.T) {
	loc := time.FixedZone("PDT", -25200)
	cases := []struct {
		F FlagSet
		T time.Time
		R string
	}{
		{
			Lnanoseconds,
			time.Date(2016, time.November, 1, 2, 3, 4, 5, loc),
			`2016-11-01T02:03:04.000000005-07:00`,
		},
		{
			Lnanoseconds,
			time.Date(2016, time.January, 11, 12, 13, 14, 15, time.UTC),
			`2016-01-11T12:13:14.000000015Z`,
		},
		{
			Lmicroseconds,
			time.Date(2016, time.November, 1, 2, 3, 4, 5000, loc),
			`2016-11-01T02:03:04.000005-07:00`,
		},
		{
			Lmicroseconds,
			time.Date(2016, time.January, 11, 12, 13, 14, 15000, time.UTC),
			`2016-01-11T12:13:14.000015Z`,
		},
	}

	b := &sliceBuffer{make([]byte, 0, 1024)}
	for _, tc := range cases {
		b.Truncate(0)
		writeTime(b, &(tc.T), tc.F)
		assert.Equal(t, tc.R, b.String(), "time written incorrectly")
	}
}
<|endoftext|>


<commit_before>package streams

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

	"github.com/FederationOfFathers/dashboard/clients/mixer"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/FederationOfFathers/dashboard/messaging"
	"go.uber.org/zap"
)

var bplog *zap.Logger

type Mixer mixer.Mixer

type mixerChannelResponse struct {
	Name          string `json:"name"`
	Token         string `json:"token"`
	ChannelOnline bool   `json:"online"`
	ChannelID     int64
	User          struct {
		AvatarUrl string `json:"avatarUrl"`
	} `json:"user"`
	Type struct {
		Name string `json:"name"`
	} `json:"type"`
}

type mixerManifestResponse struct {
	StartedAt string `json:"startedAt"`
}

func (b *Mixer) Update() error {
	b.Online = false
	b.Game = ""
	b.StartedAt = ""
	var c = mixerChannelResponse{}
	var cURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%s", b.BeamUsername)
	bplog.Debug("fetching channel", zap.String("url", cURL))
	chResponse, err := http.Get(cURL)
	if err != nil {
		return err
	}
	defer chResponse.Body.Close()
	if chResponse.StatusCode == 404 {
		bplog.Info(fmt.Sprintf("channel %s is not valid (404)", cURL))
		return nil
	} else if chResponse.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, cURL)
	}

	bodyContent, err := ioutil.ReadAll(chResponse.Body)
	if err != nil {
		bplog.Error("Unable to read body bytes", zap.Error(err))
	}

	if err := json.Unmarshal(bodyContent, &c); err != nil {
		return fmt.Errorf("Unable to decode JSON - %s", err.Error())
	}
	if !c.ChannelOnline {
		return nil
	}
	b.Online = c.ChannelOnline
	b.ChannelID = c.ChannelID
	b.Game = c.Type.Name
	b.Title = c.Name
	b.BeamUsername = c.Token
	b.AvatarUrl = c.User.AvatarUrl
	var m = mixerManifestResponse{}
	var mURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%d/manifest.light2", b.ChannelID)
	bplog.Debug("fetching manifest", zap.String("url", mURL))
	rsp, err := http.Get(mURL)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode == 404 {
		// Stream went offline
		return nil
	}
	if rsp.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, mURL)
	}
	if err := json.NewDecoder(rsp.Body).Decode(&m); err != nil {
		return err
	}
	b.StartedAt = m.StartedAt
	b.StartedTime, err = time.Parse(time.RFC3339Nano, m.StartedAt)
	if err != nil {
		return err
	}
	return nil
}

func mindMixer() {
	bplog = Logger.With(zap.String("service", "mixer"))
	bplog.Debug("begin minding")
	for _, stream := range Streams {
		if stream.Beam == "" {
			bplog.Debug("not a mixer.com stream", zap.Int("id", stream.ID), zap.Int("member_id", stream.MemberID))
			continue
		}
		bplog.Debug("minding mixer.com stream", zap.String("mixer id", stream.Beam))
		updateMixer(stream)
	}
	bplog.Debug("end minding")
}

func updateMixer(s *db.Stream) {
	m := Mixer{
		BeamUsername: s.Beam,
	}
	err := m.Update()
	if err != nil {
		bplog.Error("Error updating mixer stream details", zap.Error(err))
		return
	}

	if !m.Online {
		var save bool
		if s.BeamStop < s.BeamStart {
			s.BeamStop = time.Now().Unix()
			save = true
		}
		if s.BeamStop < s.BeamStart {
			s.BeamStop = s.BeamStart + 1
			save = true
		}
		if save {
			stopError := s.Save()
			if stopError != nil {
				bplog.Error(fmt.Sprintf("Unable to save stop data: %v", stopError))
			}
		}
		return
	}

	var startedAt = m.StartedTime.Unix()
	if startedAt <= s.BeamStart && s.BeamGame == m.Game {
		// Continuation of known stream
		return
	}

	s.BeamStart = startedAt
	s.BeamGame = m.Game
	if s.BeamStop > s.BeamStart {
		s.BeamStop = s.BeamStart - 1
	}
	updateErr := s.Save()
	if updateErr != nil {
		bplog.Error(fmt.Sprintf("Unable to save stream data: %v", updateErr))
		return
	}

	messaging.SendMixerStreamMessage(mixer.Mixer(m))
}
<commit_msg>Mapping ChannelID in struct to json 'id'<commit_after>package streams

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

	"github.com/FederationOfFathers/dashboard/clients/mixer"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/FederationOfFathers/dashboard/messaging"
	"go.uber.org/zap"
)

var bplog *zap.Logger

type Mixer mixer.Mixer

type mixerChannelResponse struct {
	Name          string `json:"name"`
	Token         string `json:"token"`
	ChannelOnline bool   `json:"online"`
	ChannelID     int64  `json:"id"`
	User          struct {
		AvatarUrl string `json:"avatarUrl"`
	} `json:"user"`
	Type struct {
		Name string `json:"name"`
	} `json:"type"`
}

type mixerManifestResponse struct {
	StartedAt string `json:"startedAt"`
}

func (b *Mixer) Update() error {
	b.Online = false
	b.Game = ""
	b.StartedAt = ""
	var c = mixerChannelResponse{}
	var cURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%s", b.BeamUsername)
	bplog.Debug("fetching channel", zap.String("url", cURL))
	chResponse, err := http.Get(cURL)
	if err != nil {
		return err
	}
	defer chResponse.Body.Close()
	if chResponse.StatusCode == 404 {
		bplog.Info(fmt.Sprintf("channel %s is not valid (404)", cURL))
		return nil
	} else if chResponse.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d - '%s' for '%s'", chResponse.StatusCode, chResponse.Status, cURL)
	}

	bodyContent, err := ioutil.ReadAll(chResponse.Body)
	if err != nil {
		bplog.Error("Unable to read body bytes", zap.Error(err))
	}

	if err := json.Unmarshal(bodyContent, &c); err != nil {
		return fmt.Errorf("Unable to decode JSON - %s", err.Error())
	}
	if !c.ChannelOnline {
		return nil
	}
	b.Online = c.ChannelOnline
	b.ChannelID = c.ChannelID
	b.Game = c.Type.Name
	b.Title = c.Name
	b.BeamUsername = c.Token
	b.AvatarUrl = c.User.AvatarUrl
	var m = mixerManifestResponse{}
	var mURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%d/manifest.light2", b.ChannelID)
	bplog.Debug("fetching manifest", zap.String("url", mURL))
	rsp, err := http.Get(mURL)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode == 404 {
		// Stream went offline
		return nil
	}
	if rsp.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, mURL)
	}
	if err := json.NewDecoder(rsp.Body).Decode(&m); err != nil {
		return err
	}
	b.StartedAt = m.StartedAt
	b.StartedTime, err = time.Parse(time.RFC3339Nano, m.StartedAt)
	if err != nil {
		return err
	}
	return nil
}

func mindMixer() {
	bplog = Logger.With(zap.String("service", "mixer"))
	bplog.Debug("begin minding")
	for _, stream := range Streams {
		if stream.Beam == "" {
			bplog.Debug("not a mixer.com stream", zap.Int("id", stream.ID), zap.Int("member_id", stream.MemberID))
			continue
		}
		bplog.Debug("minding mixer.com stream", zap.String("mixer id", stream.Beam))
		updateMixer(stream)
	}
	bplog.Debug("end minding")
}

func updateMixer(s *db.Stream) {
	m := Mixer{
		BeamUsername: s.Beam,
	}
	err := m.Update()
	if err != nil {
		bplog.Error("Error updating mixer stream details", zap.Error(err))
		return
	}

	if !m.Online {
		var save bool
		if s.BeamStop < s.BeamStart {
			s.BeamStop = time.Now().Unix()
			save = true
		}
		if s.BeamStop < s.BeamStart {
			s.BeamStop = s.BeamStart + 1
			save = true
		}
		if save {
			stopError := s.Save()
			if stopError != nil {
				bplog.Error(fmt.Sprintf("Unable to save stop data: %v", stopError))
			}
		}
		return
	}

	var startedAt = m.StartedTime.Unix()
	if startedAt <= s.BeamStart && s.BeamGame == m.Game {
		// Continuation of known stream
		return
	}

	s.BeamStart = startedAt
	s.BeamGame = m.Game
	if s.BeamStop > s.BeamStart {
		s.BeamStop = s.BeamStart - 1
	}
	updateErr := s.Save()
	if updateErr != nil {
		bplog.Error(fmt.Sprintf("Unable to save stream data: %v", updateErr))
		return
	}

	messaging.SendMixerStreamMessage(mixer.Mixer(m))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/WhiteHatCP/seclab-listener/backend"
	"github.com/WhiteHatCP/seclab-listener/server"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
)

const (
	maxPacketAge = 10
)

func main() {
	if len(os.Args)%3 != 2 {
		fmt.Fprintf(os.Stderr, "usage: seclab key [dest open closed [..]]\n")
		return
	}
	keypath := os.Args[1]
	s := server.New(keypath, maxPacketAge)

	for i := 2; i+2 < len(os.Args); i += 3 {
		dest := os.Args[i+2]
		openfile := os.Args[i+3]
		closedfile := os.Args[i+4]
		s.AddBackend(backend.New(dest, openfile, closedfile))
	}

	syscall.Umask(0007)

	socket := "seclab.sock"
	ln, err := net.Listen("unix", socket)
	syscall.Chmod(socket, 0770)
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()

	// Make sure the socket closes
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for _ = range c {
			ln.Close()
		}
	}()

	s.Serve(ln)
}
<commit_msg>Loop through the args correctly<commit_after>package main

import (
	"fmt"
	"github.com/WhiteHatCP/seclab-listener/backend"
	"github.com/WhiteHatCP/seclab-listener/server"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
)

const (
	maxPacketAge = 10
)

func main() {
	if len(os.Args)%3 != 2 {
		fmt.Fprintf(os.Stderr, "usage: seclab key [dest open closed [..]]\n")
		return
	}
	keypath := os.Args[1]
	s := server.New(keypath, maxPacketAge)

	for i := 2; i+2 < len(os.Args); i += 3 {
		dest := os.Args[i]
		openfile := os.Args[i+1]
		closedfile := os.Args[i+2]
		s.AddBackend(backend.New(dest, openfile, closedfile))
	}

	syscall.Umask(0007)

	socket := "seclab.sock"
	ln, err := net.Listen("unix", socket)
	syscall.Chmod(socket, 0770)
	if err != nil {
		log.Fatal(err)
	}
	defer ln.Close()

	// Make sure the socket closes
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for _ = range c {
			ln.Close()
		}
	}()

	s.Serve(ln)
}
<|endoftext|>


<commit_before>// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay (ms) before first retry, without jitter.
	backoffInitialDelay = 1000
	// Maximum delay (ms) before a retry.
	maxBackoffDelay = 1024000
	// Percentage jitter to use when retrying.  Jitter is a random variation in
	// timing to prevent many retries from hitting the server simulatenously.
	// Recommended values are between 0 and 50
	jitterPercentage = 50
)

// Declared as a mutable variable for testing purposes.
var gcmSendEndpoint = GcmSendEndpoint

// GCM response types
const (
	ResponseErrorMissingRegistration = "MissingRegistration"
	ResponseErrorInvalidRegistration = "InvalidRegistration"
	ResponseErrorMismatchSenderID    = "MismatchSenderId"
	ResponseErrorNotRegistered       = "NotRegistered"
	ResponseErrorMessageTooBig       = "MessageTooBig"
	ResponseErrorInvalidDataKey      = "InvalidDataKey"
	ResponseErrorInvalidTTL          = "InvalidTtl"
	ResponseErrorUnavailable         = "Unavailable"
	ResponseErrorInternalServerError = "InternalServerError"
	ResponseErrorInvalidPackageName  = "InvalidPackageName"
)

// // Errors
// type JSONParseError struct{ error }
// type UnauthorizedError struct{ error }
// type UnknownError struct{ error }

const ()

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the HTTP field is nil, a zeroed http.Client will be allocated and used
// to send messages. If your application server runs on Google AppEngine,
// you must use the "appengine/urlfetch" package to create the *http.Client
// as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{APIKey: key, HTTP: client}
//
//		/* ... */
//	}
type Sender struct {
	APIKey string
	HTTP   *http.Client
}

// HTTPError is a custom error type to handle returning errors with GCM
// http response codes
type HTTPError struct {
	StatusCode int
	Err        error
	RetryAfter int
}

func (r *HTTPError) Error() string {
	return fmt.Sprintf("%d error: %s\nretry-after: %d", r.StatusCode, r.Err, r.RetryAfter)
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, *HTTPError) {
	if err := checkSender(s); err != nil {
		return nil, &HTTPError{Err: err}
	} else if err := checkMessage(msg); err != nil {
		return nil, &HTTPError{Err: err}
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	req, err := http.NewRequest("POST", gcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, &HTTPError{Err: err}
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.APIKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.HTTP.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if the status is not StatusOK, return with the error
	if resp.StatusCode != http.StatusOK {
		delta, err := parseRetryAfter(resp.Header.Get("Retry-After"))
		if err != nil {
			return nil, &HTTPError{
				StatusCode: resp.StatusCode,
				Err:        errors.New(resp.Status),
			}
		}
		return nil, &HTTPError{
			StatusCode: resp.StatusCode,
			Err:        errors.New(resp.Status),
			RetryAfter: delta,
		}
	}

	// get the body and decode
	response := new(Response)
	err = json.NewDecoder(resp.Body).Decode(response)
	if err != nil {
		return response, &HTTPError{Err: err}
	}
	return response, nil
}

// parseRetryAfter attempts to parse the contents of the Retry-After http
// header.  It returns the time in seconds if successful, and an error for
// either an unparseable value or a nil value.
func parseRetryAfter(r string) (int, error) {
	// if not set
	if r == "" {
		return 0, fmt.Errorf("Empty Retry-After header")
	}

	// if set as an integer delta (assumed to be in seconds)
	if delta, err := strconv.Atoi(r); err == nil {
		return max(delta, 0), err
	}

	// if set as a date, convert to a time in seconds
	if ra, err := time.Parse(time.RFC1123, r); err == nil {
		delta := (ra.UnixNano() - time.Now().UnixNano()) / 1e9
		return int(delta), err
	}
	fmt.Printf("#936r Unparseable 'Retry-After' header: %s", r)
	return 0, fmt.Errorf("Unparseable 'Retry-After' header: %s", r)
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, *HTTPError) {
	if retries < 0 {
		return nil, &HTTPError{Err: fmt.Errorf("'retries' must be positive")}
	}

	// Send the message for the first time.
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if there were no errors, or retries = 0, return the result
	if resp.Failure == 0 || retries == 0 {
		return resp, err // err should always be nil here
	}

	// One or more messages failed to send.
	regIDs := msg.RegistrationIDs // store the original RegistrationIDs
	allResults := make(map[string]Result, len(regIDs))
	backoff := backoffInitialDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 && i < retries; i++ {
		sleepTime := calculateSleep(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			msg.RegistrationIDs = regIDs
			return nil, &HTTPError{Err: err}
		}
	}

	// Bring the message back to its original state.
	msg.RegistrationIDs = regIDs

	// Create a Response containing the overall results.
	finalResults := make([]Result, len(regIDs))
	var success, failure, canonicalIDs int
	for i := 0; i < len(regIDs); i++ {
		result, _ := allResults[regIDs[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIDs++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// Return the most recent multicast id.
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIDs,
		Results:      finalResults,
	}, nil
}

func calculateSleep(backoff int) int {
	return (backoff*(100-jitterPercentage))/100 + rand.Intn((2*backoff*jitterPercentage)/100)
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	unsentRegIDs := make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regID := msg.RegistrationIDs[i]
		allResults[regID] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIDs = append(unsentRegIDs, regID)
		}
	}
	msg.RegistrationIDs = unsentRegIDs
	return len(unsentRegIDs)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

// max returns the larger of two integers.
func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.APIKey == "" {
		return errors.New("the sender's API key must not be empty")
	}
	if sender.HTTP == nil {
		sender.HTTP = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("the message must not be nil")
	} else if msg.RegistrationIDs == nil {
		return errors.New("the message's RegistrationIDs field must not be nil")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("the message must specify at least one registration ID")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("the message may specify at most 1000 registration IDs")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("the message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks)")
	}
	return nil
}
<commit_msg>use constant for initial backoff delay<commit_after>// Google Cloud Messaging for application servers implemented using the
// Go programming language.
package gcm

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

const (
	// GcmSendEndpoint is the endpoint for sending messages to the GCM server.
	GcmSendEndpoint = "https://android.googleapis.com/gcm/send"
	// Initial delay (ms) before first retry, without jitter.
	initialBackoffDelay = 1000
	// Maximum delay (ms) before a retry.
	maxBackoffDelay = 1024000
	// Percentage jitter to use when retrying.  Jitter is a random variation in
	// timing to prevent many retries from hitting the server simulatenously.
	// Recommended values are between 0 and 50
	jitterPercentage = 50
)

// Declared as a mutable variable for testing purposes.
var gcmSendEndpoint = GcmSendEndpoint

// GCM response types
const (
	ResponseErrorMissingRegistration = "MissingRegistration"
	ResponseErrorInvalidRegistration = "InvalidRegistration"
	ResponseErrorMismatchSenderID    = "MismatchSenderId"
	ResponseErrorNotRegistered       = "NotRegistered"
	ResponseErrorMessageTooBig       = "MessageTooBig"
	ResponseErrorInvalidDataKey      = "InvalidDataKey"
	ResponseErrorInvalidTTL          = "InvalidTtl"
	ResponseErrorUnavailable         = "Unavailable"
	ResponseErrorInternalServerError = "InternalServerError"
	ResponseErrorInvalidPackageName  = "InvalidPackageName"
)

// // Errors
// type JSONParseError struct{ error }
// type UnauthorizedError struct{ error }
// type UnknownError struct{ error }

const ()

// Sender abstracts the interaction between the application server and the
// GCM server. The developer must obtain an API key from the Google APIs
// Console page and pass it to the Sender so that it can perform authorized
// requests on the application server's behalf. To send a message to one or
// more devices use the Sender's Send or SendNoRetry methods.
//
// If the HTTP field is nil, a zeroed http.Client will be allocated and used
// to send messages. If your application server runs on Google AppEngine,
// you must use the "appengine/urlfetch" package to create the *http.Client
// as follows:
//
//	func handler(w http.ResponseWriter, r *http.Request) {
//		c := appengine.NewContext(r)
//		client := urlfetch.Client(c)
//		sender := &gcm.Sender{APIKey: key, HTTP: client}
//
//		/* ... */
//	}
type Sender struct {
	APIKey string
	HTTP   *http.Client
}

// HTTPError is a custom error type to handle returning errors with GCM
// http response codes
type HTTPError struct {
	StatusCode int
	Err        error
	RetryAfter int
}

func (r *HTTPError) Error() string {
	return fmt.Sprintf("%d error: %s\nretry-after: %d", r.StatusCode, r.Err, r.RetryAfter)
}

// SendNoRetry sends a message to the GCM server without retrying in case of
// service unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
func (s *Sender) SendNoRetry(msg *Message) (*Response, *HTTPError) {
	if err := checkSender(s); err != nil {
		return nil, &HTTPError{Err: err}
	} else if err := checkMessage(msg); err != nil {
		return nil, &HTTPError{Err: err}
	}

	data, err := json.Marshal(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	req, err := http.NewRequest("POST", gcmSendEndpoint, bytes.NewBuffer(data))
	if err != nil {
		return nil, &HTTPError{Err: err}
	}
	req.Header.Add("Authorization", fmt.Sprintf("key=%s", s.APIKey))
	req.Header.Add("Content-Type", "application/json")

	resp, err := s.HTTP.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if the status is not StatusOK, return with the error
	if resp.StatusCode != http.StatusOK {
		delta, err := parseRetryAfter(resp.Header.Get("Retry-After"))
		if err != nil {
			return nil, &HTTPError{
				StatusCode: resp.StatusCode,
				Err:        errors.New(resp.Status),
			}
		}
		return nil, &HTTPError{
			StatusCode: resp.StatusCode,
			Err:        errors.New(resp.Status),
			RetryAfter: delta,
		}
	}

	// get the body and decode
	response := new(Response)
	err = json.NewDecoder(resp.Body).Decode(response)
	if err != nil {
		return response, &HTTPError{Err: err}
	}
	return response, nil
}

// parseRetryAfter attempts to parse the contents of the Retry-After http
// header.  It returns the time in seconds if successful, and an error for
// either an unparseable value or a nil value.
func parseRetryAfter(r string) (int, error) {
	// if not set
	if r == "" {
		return 0, fmt.Errorf("Empty Retry-After header")
	}

	// if set as an integer delta (assumed to be in seconds)
	if delta, err := strconv.Atoi(r); err == nil {
		return max(delta, 0), err
	}

	// if set as a date, convert to a time in seconds
	if ra, err := time.Parse(time.RFC1123, r); err == nil {
		delta := (ra.UnixNano() - time.Now().UnixNano()) / 1e9
		return int(delta), err
	}
	fmt.Printf("#936r Unparseable 'Retry-After' header: %s", r)
	return 0, fmt.Errorf("Unparseable 'Retry-After' header: %s", r)
}

// Send sends a message to the GCM server, retrying in case of service
// unavailability. A non-nil error is returned if a non-recoverable
// error occurs (i.e. if the response status is not "200 OK").
//
// Note that messages are retried using exponential backoff, and as a
// result, this method may block for several seconds.
func (s *Sender) Send(msg *Message, retries int) (*Response, *HTTPError) {
	if retries < 0 {
		return nil, &HTTPError{Err: fmt.Errorf("'retries' must be positive")}
	}

	// Send the message for the first time.
	resp, err := s.SendNoRetry(msg)
	if err != nil {
		return nil, &HTTPError{Err: err}
	}

	// if there were no errors, or retries = 0, return the result
	if resp.Failure == 0 || retries == 0 {
		return resp, err // err should always be nil here
	}

	// One or more messages failed to send.
	regIDs := msg.RegistrationIDs // store the original RegistrationIDs
	// TODO: Don't modify the msg object via the pointer, as we may have
	// multiple goroutines acting on it at once
	allResults := make(map[string]Result, len(regIDs))
	backoff := initialBackoffDelay
	for i := 0; updateStatus(msg, resp, allResults) > 0 && i < retries; i++ {
		sleepTime := calculateSleep(backoff)
		time.Sleep(time.Duration(sleepTime) * time.Millisecond)
		backoff = min(2*backoff, maxBackoffDelay)
		if resp, err = s.SendNoRetry(msg); err != nil {
			msg.RegistrationIDs = regIDs
			return nil, &HTTPError{Err: err}
		}
	}

	// Bring the message back to its original state.
	msg.RegistrationIDs = regIDs

	// Create a Response containing the overall results.
	finalResults := make([]Result, len(regIDs))
	var success, failure, canonicalIDs int
	for i := 0; i < len(regIDs); i++ {
		result, _ := allResults[regIDs[i]]
		finalResults[i] = result
		if result.MessageID != "" {
			if result.RegistrationID != "" {
				canonicalIDs++
			}
			success++
		} else {
			failure++
		}
	}

	return &Response{
		// Return the most recent multicast id.
		MulticastID:  resp.MulticastID,
		Success:      success,
		Failure:      failure,
		CanonicalIDs: canonicalIDs,
		Results:      finalResults,
	}, nil
}

func calculateSleep(backoff int) int {
	return (backoff*(100-jitterPercentage))/100 + rand.Intn((2*backoff*jitterPercentage)/100)
}

// updateStatus updates the status of the messages sent to devices and
// returns the number of recoverable errors that could be retried.
func updateStatus(msg *Message, resp *Response, allResults map[string]Result) int {
	unsentRegIDs := make([]string, 0, resp.Failure)
	for i := 0; i < len(resp.Results); i++ {
		regID := msg.RegistrationIDs[i]
		allResults[regID] = resp.Results[i]
		if resp.Results[i].Error == "Unavailable" {
			unsentRegIDs = append(unsentRegIDs, regID)
		}
	}
	msg.RegistrationIDs = unsentRegIDs
	return len(unsentRegIDs)
}

// min returns the smaller of two integers. For exciting religious wars
// about why this wasn't included in the "math" package, see this thread:
// https://groups.google.com/d/topic/golang-nuts/dbyqx_LGUxM/discussion
func min(a, b int) int {
	if a <= b {
		return a
	}
	return b
}

// max returns the larger of two integers.
func max(a, b int) int {
	if a >= b {
		return a
	}
	return b
}

// checkSender returns an error if the sender is not well-formed and
// initializes a zeroed http.Client if one has not been provided.
func checkSender(sender *Sender) error {
	if sender.APIKey == "" {
		return errors.New("the sender's API key must not be empty")
	}
	if sender.HTTP == nil {
		sender.HTTP = new(http.Client)
	}
	return nil
}

// checkMessage returns an error if the message is not well-formed.
func checkMessage(msg *Message) error {
	if msg == nil {
		return errors.New("the message must not be nil")
	} else if msg.RegistrationIDs == nil {
		return errors.New("the message's RegistrationIDs field must not be nil")
	} else if len(msg.RegistrationIDs) == 0 {
		return errors.New("the message must specify at least one registration ID")
	} else if len(msg.RegistrationIDs) > 1000 {
		return errors.New("the message may specify at most 1000 registration IDs")
	} else if msg.TimeToLive < 0 || 2419200 < msg.TimeToLive {
		return errors.New("the message's TimeToLive field must be an integer " +
			"between 0 and 2419200 (4 weeks)")
	}
	return nil
}
<|endoftext|>