<commit_before>package cmap

import "testing"
import "strconv"

func BenchmarkItems(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.Items()
	}
}

func BenchmarkMarshalJson(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.MarshalJSON()
	}
}

func BenchmarkStrconv(b *testing.B) {
	for i := 0; i < b.N; i++ {
		strconv.Itoa(i)
	}
}

func BenchmarkSingleInsertAbsent(b *testing.B) {
	m := New()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Set(strconv.Itoa(i), "value")
	}
}

func BenchmarkSingleInsertPresent(b *testing.B) {
	m := New()
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Set("key", "value")
	}
}

func benchmarkMultiInsertDifferent(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	_, set := GetSet(m, finished)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		set(strconv.Itoa(i), "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiInsertDifferent_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 1)
}
func BenchmarkMultiInsertDifferent_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 16)
}
func BenchmarkMultiInsertDifferent_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 32)
}
func BenchmarkMultiInsertDifferent_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 256)
}

func BenchmarkMultiInsertSame(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	_, set := GetSet(m, finished)
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		set("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSame(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	get, _ := GetSet(m, finished)
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		get("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func benchmarkMultiGetSetDifferent(b *testing.B) {
	m := New()
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSet(m, finished)
	m.Set("-1", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		set(strconv.Itoa(i-1), "value")
		get(strconv.Itoa(i), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetDifferent_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 1)
}
func BenchmarkMultiGetSetDifferent_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 16)
}
func BenchmarkMultiGetSetDifferent_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 32)
}
func BenchmarkMultiGetSetDifferent_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 256)
}

func benchmarkMultiGetSetBlock(b *testing.B) {
	m := New()
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSet(m, finished)
	for i := 0; i < b.N; i++ {
		m.Set(strconv.Itoa(i%100), "value")
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		set(strconv.Itoa(i%100), "value")
		get(strconv.Itoa(i%100), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetBlock_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 1)
}
func BenchmarkMultiGetSetBlock_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 16)
}
func BenchmarkMultiGetSetBlock_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 32)
}
func BenchmarkMultiGetSetBlock_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 256)
}

func GetSet(m ConcurrentMap, finished chan struct{}) (set func(key, value string), get func(key, value string)) {
	return func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Get(key)
			}
			finished <- struct{}{}
		}, func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Set(key, value)
			}
			finished <- struct{}{}
		}
}

func runWithShards(bench func(b *testing.B), b *testing.B, shardsCount int) {
	oldShardsCount := SHARD_COUNT
	SHARD_COUNT = shardsCount
	bench(b)
	SHARD_COUNT = oldShardsCount
}

func BenchmarkKeys(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.Keys()
	}
}
<commit_msg>BenchmarkTest set/get run in goroutine<commit_after>package cmap

import (
	"strconv"
	"sync"
	"testing"
)

func BenchmarkItems(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.Items()
	}
}

func BenchmarkMarshalJson(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.MarshalJSON()
	}
}

func BenchmarkStrconv(b *testing.B) {
	for i := 0; i < b.N; i++ {
		strconv.Itoa(i)
	}
}

func BenchmarkSingleInsertAbsent(b *testing.B) {
	m := New()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Set(strconv.Itoa(i), "value")
	}
}

func BenchmarkSingleInsertAbsentSyncMap(b *testing.B) {
	var m sync.Map
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Store(strconv.Itoa(i), "value")
	}
}

func BenchmarkSingleInsertPresent(b *testing.B) {
	m := New()
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Set("key", "value")
	}
}

func BenchmarkSingleInsertPresentSyncMap(b *testing.B) {
	var m sync.Map
	m.Store("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m.Store("key", "value")
	}
}

func benchmarkMultiInsertDifferent(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	_, set := GetSet(m, finished)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i), "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiInsertDifferentSyncMap(b *testing.B) {
	var m sync.Map
	finished := make(chan struct{}, b.N)
	_, set := GetSetSyncMap(m, finished)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i), "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiInsertDifferent_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 1)
}
func BenchmarkMultiInsertDifferent_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 16)
}
func BenchmarkMultiInsertDifferent_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiInsertDifferent, b, 32)
}
func BenchmarkMultiInsertDifferent_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 256)
}

func BenchmarkMultiInsertSame(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	_, set := GetSet(m, finished)
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiInsertSameSyncMap(b *testing.B) {
	var m sync.Map
	finished := make(chan struct{}, b.N)
	_, set := GetSetSyncMap(m, finished)
	m.Store("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSame(b *testing.B) {
	m := New()
	finished := make(chan struct{}, b.N)
	get, _ := GetSet(m, finished)
	m.Set("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go get("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSameSyncMap(b *testing.B) {
	var m sync.Map
	finished := make(chan struct{}, b.N)
	get, _ := GetSetSyncMap(m, finished)
	m.Store("key", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go get("key", "value")
	}
	for i := 0; i < b.N; i++ {
		<-finished
	}
}

func benchmarkMultiGetSetDifferent(b *testing.B) {
	m := New()
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSet(m, finished)
	m.Set("-1", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i-1), "value")
		go get(strconv.Itoa(i), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetDifferentSyncMap(b *testing.B) {
	var m sync.Map
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSetSyncMap(m, finished)
	m.Store("-1", "value")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i-1), "value")
		go get(strconv.Itoa(i), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetDifferent_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 1)
}
func BenchmarkMultiGetSetDifferent_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 16)
}
func BenchmarkMultiGetSetDifferent_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 32)
}
func BenchmarkMultiGetSetDifferent_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetDifferent, b, 256)
}

func benchmarkMultiGetSetBlock(b *testing.B) {
	m := New()
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSet(m, finished)
	for i := 0; i < b.N; i++ {
		m.Set(strconv.Itoa(i%100), "value")
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i%100), "value")
		go get(strconv.Itoa(i%100), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetBlockSyncMap(b *testing.B) {
	var m sync.Map
	finished := make(chan struct{}, 2*b.N)
	get, set := GetSetSyncMap(m, finished)
	for i := 0; i < b.N; i++ {
		m.Store(strconv.Itoa(i%100), "value")
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		go set(strconv.Itoa(i%100), "value")
		go get(strconv.Itoa(i%100), "value")
	}
	for i := 0; i < 2*b.N; i++ {
		<-finished
	}
}

func BenchmarkMultiGetSetBlock_1_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 1)
}
func BenchmarkMultiGetSetBlock_16_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 16)
}
func BenchmarkMultiGetSetBlock_32_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 32)
}
func BenchmarkMultiGetSetBlock_256_Shard(b *testing.B) {
	runWithShards(benchmarkMultiGetSetBlock, b, 256)
}

func GetSet(m ConcurrentMap, finished chan struct{}) (set func(key, value string), get func(key, value string)) {
	return func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Get(key)
			}
			finished <- struct{}{}
		}, func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Set(key, value)
			}
			finished <- struct{}{}
		}
}

func GetSetSyncMap(m sync.Map, finished chan struct{}) (set func(key, value string), get func(key, value string)) {
	return func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Load(key)
			}
			finished <- struct{}{}
		}, func(key, value string) {
			for i := 0; i < 10; i++ {
				m.Store(key, value)
			}
			finished <- struct{}{}
		}
}

func runWithShards(bench func(b *testing.B), b *testing.B, shardsCount int) {
	oldShardsCount := SHARD_COUNT
	SHARD_COUNT = shardsCount
	bench(b)
	SHARD_COUNT = oldShardsCount
}

func BenchmarkKeys(b *testing.B) {
	m := New()

	// Insert 100 elements.
	for i := 0; i < 10000; i++ {
		m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)})
	}
	for i := 0; i < b.N; i++ {
		m.Keys()
	}
}
<|endoftext|>


<commit_before>package cloudns

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

// Api layer for CloDNS
type api struct {
	domainIndex      map[string]string
	nameserversNames []string
	creds            struct {
		id       string
		password string
	}
}

type requestParams map[string]string

type errorResponse struct {
	Status      string `json:"status"`
	Description string `json:"statusDescription"`
}

type nameserverRecord struct {
	Type string `json:"type"`
	Name string `json:"name"`
}

type nameserverResponse []nameserverRecord

type zoneRecord struct {
	Name   string `json:"name"`
	Type   string `json:"type"`
	Status string `json:"status"`
	Zone   string `json:"zone"`
}

type zoneResponse []zoneRecord

type domainRecord struct {
	ID               string `json:"id"`
	Type             string `json:"type"`
	Host             string `json:"host"`
	Target           string `json:"record"`
	Priority         string `json:"priority"`
	Weight           string `json:"weight"`
	Port             string `json:"port"`
	Service          string `json:"service"`
	Protocol         string `json:"protocol"`
	TTL              string `json:"ttl"`
	Status           int8   `json:"status"`
	CaaFlag          string `json:"caa_flag,omitempty"`
	CaaTag           string `json:"caa_type,omitempty"`
	CaaValue         string `json:"caa_value,omitempty"`
	TlsaUsage        string `json:"tlsa_usage,omitempty"`
	TlsaSelector     string `json:"tlsa_selector,omitempty"`
	TlsaMatchingType string `json:"tlsa_matching_type,omitempty"`
	SshfpAlgorithm   string `json:"algorithm,omitempty"`
	SshfpFingerprint string `json:"fp_type,omitempty"`
}

type recordResponse map[string]domainRecord

var allowedTTLValues = []uint32{
	60,      // 1 minute
	300,     // 5 minutes
	900,     // 15 minutes
	1800,    // 30 minutes
	3600,    // 1 hour
	21600,   // 6 hours
	43200,   // 12 hours
	86400,   // 1 day
	172800,  // 2 days
	259200,  // 3 days
	604800,  // 1 week
	1209600, // 2 weeks
	2419200, // 4 weeks
}

func (c *api) fetchAvailableNameservers() error {
	c.nameserversNames = nil

	var bodyString, err = c.get("/dns/available-name-servers.json", requestParams{})
	if err != nil {
		return fmt.Errorf("failed fetching available nameservers list from ClouDNS: %s", err)
	}

	var nr nameserverResponse
	json.Unmarshal(bodyString, &nr)

	for _, nameserver := range nr {
		if nameserver.Type == "premium" {
			c.nameserversNames = append(c.nameserversNames, nameserver.Name)
		}

	}
	return nil
}

func (c *api) fetchDomainList() error {
	c.domainIndex = map[string]string{}
	rowsPerPage := 100
	page := 1
	for {
		var dr zoneResponse
		params := requestParams{
			"page":          strconv.Itoa(page),
			"rows-per-page": strconv.Itoa(rowsPerPage),
		}
		endpoint := "/dns/list-zones.json"
		var bodyString, err = c.get(endpoint, params)
		if err != nil {
			return fmt.Errorf("failed fetching domain list from ClouDNS: %s", err)
		}
		json.Unmarshal(bodyString, &dr)

		for _, domain := range dr {
			c.domainIndex[domain.Name] = domain.Name
		}
		if len(dr) < rowsPerPage {
			break
		}
		page++
	}
	return nil
}

func (c *api) createDomain(domain string) error {
	params := requestParams{
		"domain-name": domain,
		"zone-type":   "master",
	}
	if _, err := c.get("/dns/register.json", params); err != nil {
		return fmt.Errorf("failed create domain (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) createRecord(domainID string, rec requestParams) error {
	rec["domain-name"] = domainID
	if _, err := c.get("/dns/add-record.json", rec); err != nil {
		return fmt.Errorf("failed create record (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) deleteRecord(domainID string, recordID string) error {
	params := requestParams{
		"domain-name": domainID,
		"record-id":   recordID,
	}
	if _, err := c.get("/dns/delete-record.json", params); err != nil {
		return fmt.Errorf("failed delete record (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) modifyRecord(domainID string, recordID string, rec requestParams) error {
	rec["domain-name"] = domainID
	rec["record-id"] = recordID
	if _, err := c.get("/dns/mod-record.json", rec); err != nil {
		return fmt.Errorf("failed update (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) getRecords(id string) ([]domainRecord, error) {
	params := requestParams{"domain-name": id}

	var bodyString, err = c.get("/dns/records.json", params)
	if err != nil {
		return nil, fmt.Errorf("failed fetching record list from ClouDNS: %s", err)
	}

	var dr recordResponse
	json.Unmarshal(bodyString, &dr)

	var records []domainRecord
	for _, rec := range dr {
		records = append(records, rec)
	}
	return records, nil
}

func (c *api) get(endpoint string, params requestParams) ([]byte, error) {
	client := &http.Client{}
	req, _ := http.NewRequest("GET", "https://api.cloudns.net"+endpoint, nil)
	q := req.URL.Query()

	//TODO: Support  sub-auth-id / sub-auth-user https://asia.cloudns.net/wiki/article/42/
	// Add auth params
	q.Add("auth-id", c.creds.id)
	q.Add("auth-password", c.creds.password)

	for pName, pValue := range params {
		q.Add(pName, pValue)
	}

	req.URL.RawQuery = q.Encode()

	resp, err := client.Do(req)
	if err != nil {
		return []byte{}, err
	}

	bodyString, _ := ioutil.ReadAll(resp.Body)

	// Got error from API ?
	var errResp errorResponse
	err = json.Unmarshal(bodyString, &errResp)
	if err != nil {
		return []byte{}, err
	}

	if errResp.Status == "Failed" {
		return bodyString, fmt.Errorf("ClouDNS API error: %s URL:%s%s ", errResp.Description, req.Host, req.URL.RequestURI())
	}

	return bodyString, nil
}

func fixTTL(ttl uint32) uint32 {
	// if the TTL is larger than the largest allowed value, return the largest allowed value
	if ttl > allowedTTLValues[len(allowedTTLValues)-1] {
		return allowedTTLValues[len(allowedTTLValues)-1]
	}

	for _, v := range allowedTTLValues {
		if v >= ttl {
			return v
		}
	}

	return allowedTTLValues[0]
}
<commit_msg>ClouDNS: Return API error only when body unmarshals to errorResponse (#875)<commit_after>package cloudns

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

// Api layer for CloDNS
type api struct {
	domainIndex      map[string]string
	nameserversNames []string
	creds            struct {
		id       string
		password string
	}
}

type requestParams map[string]string

type errorResponse struct {
	Status      string `json:"status"`
	Description string `json:"statusDescription"`
}

type nameserverRecord struct {
	Type string `json:"type"`
	Name string `json:"name"`
}

type nameserverResponse []nameserverRecord

type zoneRecord struct {
	Name   string `json:"name"`
	Type   string `json:"type"`
	Status string `json:"status"`
	Zone   string `json:"zone"`
}

type zoneResponse []zoneRecord

type domainRecord struct {
	ID               string `json:"id"`
	Type             string `json:"type"`
	Host             string `json:"host"`
	Target           string `json:"record"`
	Priority         string `json:"priority"`
	Weight           string `json:"weight"`
	Port             string `json:"port"`
	Service          string `json:"service"`
	Protocol         string `json:"protocol"`
	TTL              string `json:"ttl"`
	Status           int8   `json:"status"`
	CaaFlag          string `json:"caa_flag,omitempty"`
	CaaTag           string `json:"caa_type,omitempty"`
	CaaValue         string `json:"caa_value,omitempty"`
	TlsaUsage        string `json:"tlsa_usage,omitempty"`
	TlsaSelector     string `json:"tlsa_selector,omitempty"`
	TlsaMatchingType string `json:"tlsa_matching_type,omitempty"`
	SshfpAlgorithm   string `json:"algorithm,omitempty"`
	SshfpFingerprint string `json:"fp_type,omitempty"`
}

type recordResponse map[string]domainRecord

var allowedTTLValues = []uint32{
	60,      // 1 minute
	300,     // 5 minutes
	900,     // 15 minutes
	1800,    // 30 minutes
	3600,    // 1 hour
	21600,   // 6 hours
	43200,   // 12 hours
	86400,   // 1 day
	172800,  // 2 days
	259200,  // 3 days
	604800,  // 1 week
	1209600, // 2 weeks
	2419200, // 4 weeks
}

func (c *api) fetchAvailableNameservers() error {
	c.nameserversNames = nil

	var bodyString, err = c.get("/dns/available-name-servers.json", requestParams{})
	if err != nil {
		return fmt.Errorf("failed fetching available nameservers list from ClouDNS: %s", err)
	}

	var nr nameserverResponse
	json.Unmarshal(bodyString, &nr)

	for _, nameserver := range nr {
		if nameserver.Type == "premium" {
			c.nameserversNames = append(c.nameserversNames, nameserver.Name)
		}

	}
	return nil
}

func (c *api) fetchDomainList() error {
	c.domainIndex = map[string]string{}
	rowsPerPage := 100
	page := 1
	for {
		var dr zoneResponse
		params := requestParams{
			"page":          strconv.Itoa(page),
			"rows-per-page": strconv.Itoa(rowsPerPage),
		}
		endpoint := "/dns/list-zones.json"
		var bodyString, err = c.get(endpoint, params)
		if err != nil {
			return fmt.Errorf("failed fetching domain list from ClouDNS: %s", err)
		}
		json.Unmarshal(bodyString, &dr)

		for _, domain := range dr {
			c.domainIndex[domain.Name] = domain.Name
		}
		if len(dr) < rowsPerPage {
			break
		}
		page++
	}
	return nil
}

func (c *api) createDomain(domain string) error {
	params := requestParams{
		"domain-name": domain,
		"zone-type":   "master",
	}
	if _, err := c.get("/dns/register.json", params); err != nil {
		return fmt.Errorf("failed create domain (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) createRecord(domainID string, rec requestParams) error {
	rec["domain-name"] = domainID
	if _, err := c.get("/dns/add-record.json", rec); err != nil {
		return fmt.Errorf("failed create record (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) deleteRecord(domainID string, recordID string) error {
	params := requestParams{
		"domain-name": domainID,
		"record-id":   recordID,
	}
	if _, err := c.get("/dns/delete-record.json", params); err != nil {
		return fmt.Errorf("failed delete record (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) modifyRecord(domainID string, recordID string, rec requestParams) error {
	rec["domain-name"] = domainID
	rec["record-id"] = recordID
	if _, err := c.get("/dns/mod-record.json", rec); err != nil {
		return fmt.Errorf("failed update (ClouDNS): %s", err)
	}
	return nil
}

func (c *api) getRecords(id string) ([]domainRecord, error) {
	params := requestParams{"domain-name": id}

	var bodyString, err = c.get("/dns/records.json", params)
	if err != nil {
		return nil, fmt.Errorf("failed fetching record list from ClouDNS: %s", err)
	}

	var dr recordResponse
	json.Unmarshal(bodyString, &dr)

	var records []domainRecord
	for _, rec := range dr {
		records = append(records, rec)
	}
	return records, nil
}

func (c *api) get(endpoint string, params requestParams) ([]byte, error) {
	client := &http.Client{}
	req, _ := http.NewRequest("GET", "https://api.cloudns.net"+endpoint, nil)
	q := req.URL.Query()

	//TODO: Support  sub-auth-id / sub-auth-user https://asia.cloudns.net/wiki/article/42/
	// Add auth params
	q.Add("auth-id", c.creds.id)
	q.Add("auth-password", c.creds.password)

	for pName, pValue := range params {
		q.Add(pName, pValue)
	}

	req.URL.RawQuery = q.Encode()

	resp, err := client.Do(req)
	if err != nil {
		return []byte{}, err
	}

	bodyString, _ := ioutil.ReadAll(resp.Body)

	// Got error from API ?
	var errResp errorResponse
	err = json.Unmarshal(bodyString, &errResp)
	if err == nil {
		if errResp.Status == "Failed" {
			return bodyString, fmt.Errorf("ClouDNS API error: %s URL:%s%s ", errResp.Description, req.Host, req.URL.RequestURI())
		}
	}

	return bodyString, nil
}

func fixTTL(ttl uint32) uint32 {
	// if the TTL is larger than the largest allowed value, return the largest allowed value
	if ttl > allowedTTLValues[len(allowedTTLValues)-1] {
		return allowedTTLValues[len(allowedTTLValues)-1]
	}

	for _, v := range allowedTTLValues {
		if v >= ttl {
			return v
		}
	}

	return allowedTTLValues[0]
}
<|endoftext|>


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

package juju

import (
	"github.com/globocom/tsuru/heal"
	"github.com/globocom/tsuru/log"
	"os/exec"
	"strings"
)

func init() {
	heal.Register("bootstrap", &BootstrapMachineHealer{})
	heal.Register("bootstrap-provision", &BootstrapProvisionHealer{})
	heal.Register("zookeeper", &ZookeeperHealer{})
}

// ZookeeperHealer is an implementation for the Healer interface. For more
// detail on how a healer work, check the documentation of the heal package.
type ZookeeperHealer struct{}

func (h *ZookeeperHealer) NeedsHeal() bool {
	return false
}

// Heal restarts the zookeeper using upstart.
func (h *ZookeeperHealer) Heal() error {
	bootstrapMachine := getBootstrapMachine()
	log.Printf("Healing zookeeper")
	upStartCmd("stop", "zookeeper", bootstrapMachine.IpAddress)
	return upStartCmd("start", "zookeeper", bootstrapMachine.IpAddress)
}

// BootstrapProvisionHealer is an import for the Healer interface. For more
// details on how a healer work, check the documentation of the heal package.
type BootstrapProvisionHealer struct{}

// NeedsHeal always returns true.
func (h *BootstrapProvisionHealer) NeedsHeal() bool {
	return true
}

func (h *BootstrapProvisionHealer) Heal() error {
	bootstrapMachine := getBootstrapMachine()
	log.Printf("Healing bootstrap juju-provision-agent")
	return upStartCmd("start", "juju-provision-agent", bootstrapMachine.IpAddress)
}

// BootstrapMachineHealer is an implementation for the Healer interface. For more
// details on how a healer work, check the documentation of the heal package.
type BootstrapMachineHealer struct{}

// getBootstrapMachine returns the bootstrap machine.
func getBootstrapMachine() machine {
	p := JujuProvisioner{}
	output, _ := p.getOutput()
	// for juju bootstrap machine always is the machine 0.
	return output.Machines[0]
}

// NeedsHeal returns true if the AgentState of bootstrap machine is "not-started".
func (h *BootstrapMachineHealer) NeedsHeal() bool {
	bootstrapMachine := getBootstrapMachine()
	return bootstrapMachine.AgentState == "not-started"
}

func upStartCmd(cmd, daemon, machine string) error {
	args := []string{
		"-o",
		"StrictHostKeyChecking no",
		"-q",
		"-l",
		"ubuntu",
		machine,
		"sudo",
		cmd,
		daemon,
	}
	log.Printf(strings.Join(args, " "))
	c := exec.Command("ssh", args...)
	return c.Run()
}

// Heal executes the action for heal the bootstrap machine agent.
func (h *BootstrapMachineHealer) Heal() error {
	if h.NeedsHeal() {
		bootstrapMachine := getBootstrapMachine()
		log.Printf("Healing bootstrap juju-machine-agent")
		upStartCmd("stop", "juju-machine-agent", bootstrapMachine.IpAddress)
		return upStartCmd("start", "juju-machine-agent", bootstrapMachine.IpAddress)
	}
	log.Printf("Bootstrap juju-machine-agent needs no cure, skipping...")
	return nil
}
<commit_msg>Added docs for provision healer heal method.<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package juju

import (
	"github.com/globocom/tsuru/heal"
	"github.com/globocom/tsuru/log"
	"os/exec"
	"strings"
)

func init() {
	heal.Register("bootstrap", &BootstrapMachineHealer{})
	heal.Register("bootstrap-provision", &BootstrapProvisionHealer{})
	heal.Register("zookeeper", &ZookeeperHealer{})
}

// ZookeeperHealer is an implementation for the Healer interface. For more
// detail on how a healer work, check the documentation of the heal package.
type ZookeeperHealer struct{}

func (h *ZookeeperHealer) NeedsHeal() bool {
	return false
}

// Heal restarts the zookeeper using upstart.
func (h *ZookeeperHealer) Heal() error {
	bootstrapMachine := getBootstrapMachine()
	log.Printf("Healing zookeeper")
	upStartCmd("stop", "zookeeper", bootstrapMachine.IpAddress)
	return upStartCmd("start", "zookeeper", bootstrapMachine.IpAddress)
}

// BootstrapProvisionHealer is an import for the Healer interface. For more
// details on how a healer work, check the documentation of the heal package.
type BootstrapProvisionHealer struct{}

// NeedsHeal always returns true.
func (h *BootstrapProvisionHealer) NeedsHeal() bool {
	return true
}

// Heal starts the juju-provision-agent using upstart.
func (h *BootstrapProvisionHealer) Heal() error {
	bootstrapMachine := getBootstrapMachine()
	log.Printf("Healing bootstrap juju-provision-agent")
	return upStartCmd("start", "juju-provision-agent", bootstrapMachine.IpAddress)
}

// BootstrapMachineHealer is an implementation for the Healer interface. For more
// details on how a healer work, check the documentation of the heal package.
type BootstrapMachineHealer struct{}

// getBootstrapMachine returns the bootstrap machine.
func getBootstrapMachine() machine {
	p := JujuProvisioner{}
	output, _ := p.getOutput()
	// for juju bootstrap machine always is the machine 0.
	return output.Machines[0]
}

// NeedsHeal returns true if the AgentState of bootstrap machine is "not-started".
func (h *BootstrapMachineHealer) NeedsHeal() bool {
	bootstrapMachine := getBootstrapMachine()
	return bootstrapMachine.AgentState == "not-started"
}

func upStartCmd(cmd, daemon, machine string) error {
	args := []string{
		"-o",
		"StrictHostKeyChecking no",
		"-q",
		"-l",
		"ubuntu",
		machine,
		"sudo",
		cmd,
		daemon,
	}
	log.Printf(strings.Join(args, " "))
	c := exec.Command("ssh", args...)
	return c.Run()
}

// Heal executes the action for heal the bootstrap machine agent.
func (h *BootstrapMachineHealer) Heal() error {
	if h.NeedsHeal() {
		bootstrapMachine := getBootstrapMachine()
		log.Printf("Healing bootstrap juju-machine-agent")
		upStartCmd("stop", "juju-machine-agent", bootstrapMachine.IpAddress)
		return upStartCmd("start", "juju-machine-agent", bootstrapMachine.IpAddress)
	}
	log.Printf("Bootstrap juju-machine-agent needs no cure, skipping...")
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// Package crier reports finished prowjob status to git providers.
package crier

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	jsonpatch "github.com/evanphx/json-patch"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/apis/prowjobs/v1"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/retry"
	"k8s.io/client-go/util/workqueue"
	clientset "k8s.io/test-infra/prow/client/clientset/versioned"
	pjinformers "k8s.io/test-infra/prow/client/informers/externalversions/prowjobs/v1"
)

type reportClient interface {
	Report(pj *v1.ProwJob) ([]*v1.ProwJob, error)
	GetName() string
	ShouldReport(pj *v1.ProwJob) bool
}

// Controller struct defines how a controller should encapsulate
// logging, client connectivity, informing (list and watching)
// queueing, and handling of resource changes
type Controller struct {
	pjclientset clientset.Interface
	queue       workqueue.RateLimitingInterface
	informer    pjinformers.ProwJobInformer
	reporter    reportClient
	numWorkers  int
	wg          *sync.WaitGroup
}

// NewController constructs a new instance of the crier controller.
func NewController(
	pjclientset clientset.Interface,
	queue workqueue.RateLimitingInterface,
	informer pjinformers.ProwJobInformer,
	reporter reportClient,
	numWorkers int) *Controller {
	return &Controller{
		pjclientset: pjclientset,
		queue:       queue,
		informer:    informer,
		reporter:    reporter,
		numWorkers:  numWorkers,
		wg:          &sync.WaitGroup{},
	}
}

// Run is the main path of execution for the controller loop.
func (c *Controller) Run(ctx context.Context) {
	// handle a panic with logging and exiting
	defer utilruntime.HandleCrash()

	logrus.Info("Initiating controller")
	c.informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			logrus.WithField("prowjob", key).Trace("Add prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(newObj)
			logrus.WithField("prowjob", key).Trace("Update prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
	})

	// run the informer to start listing and watching resources
	go c.informer.Informer().Run(ctx.Done())

	// do the initial synchronization (one time) to populate resources
	if !cache.WaitForCacheSync(ctx.Done(), c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Error syncing cache"))
		return
	}
	logrus.Info("Controller.Run: cache sync complete")

	// run the runWorker method every second with a stop channel
	for i := 0; i < c.numWorkers; i++ {
		go wait.Until(c.runWorker, time.Second, ctx.Done())
	}

	logrus.Infof("Started %d workers", c.numWorkers)
	<-ctx.Done()
	logrus.Info("Shutting down workers")
	// ignore new items in the queue but when all goroutines
	// have completed existing items then shutdown
	c.queue.ShutDown()
	c.wg.Wait()
}

// HasSynced allows us to satisfy the Controller interface by wiring up the informer's HasSynced
// method to it.
func (c *Controller) HasSynced() bool {
	return c.informer.Informer().HasSynced()
}

// runWorker executes the loop to process new items added to the queue.
func (c *Controller) runWorker() {
	c.wg.Add(1)
	for c.processNextItem() {
	}
	c.wg.Done()
}

func (c *Controller) retry(key interface{}, log *logrus.Entry, err error) bool {
	if c.queue.NumRequeues(key) < 5 {
		log.WithError(err).Info("Failed processing item, retrying")
		c.queue.AddRateLimited(key)
	} else {
		log.WithError(err).Error("Failed processing item, no more retries")
		c.queue.Forget(key)
	}

	return true
}

func (c *Controller) updateReportState(pj *v1.ProwJob, log *logrus.Entry, reportedState v1.ProwJobState) error {
	pjData, err := json.Marshal(pj)
	if err != nil {
		return fmt.Errorf("error marshal pj: %v", err)
	}

	// update pj report status
	newpj := pj.DeepCopy()
	// we set omitempty on PrevReportStates, so here we need to init it if is nil
	if newpj.Status.PrevReportStates == nil {
		newpj.Status.PrevReportStates = map[string]v1.ProwJobState{}
	}
	newpj.Status.PrevReportStates[c.reporter.GetName()] = reportedState

	newpjData, err := json.Marshal(newpj)
	if err != nil {
		return fmt.Errorf("error marshal new pj: %v", err)
	}

	patch, err := jsonpatch.CreateMergePatch(pjData, newpjData)
	if err != nil {
		return fmt.Errorf("error CreateMergePatch: %v", err)
	}

	if len(patch) == 0 {
		logrus.Warnf("Empty merge patch: pjData: %s, newpjData: %s", string(pjData), string(newpjData))
	}

	log.Info("Created merge patch")
	_, err = c.pjclientset.ProwV1().ProwJobs(pj.Namespace).Patch(pj.Name, types.MergePatchType, patch)
	if err != nil {
		return err
	}

	// Block until the update is in the lister to make sure that events from another controller
	// that also does reporting dont trigger another report because our lister doesn't yet contain
	// the updated Status
	if err := wait.Poll(time.Second, 3*time.Second, func() (bool, error) {
		pj, err := c.informer.Lister().ProwJobs(newpj.Namespace).Get(newpj.Name)
		if err != nil {
			return false, err
		}
		if pj.Status.PrevReportStates != nil &&
			newpj.Status.PrevReportStates[c.reporter.GetName()] == reportedState {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("failed to wait for updated report status to be in lister: %v", err)
	}
	return nil
}

// processNextItem retrieves each queued item and takes the necessary handler action based off of if
// the item was created or deleted.
func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		logrus.Debug("Queue already shut down, exiting processNextItem")
		return false
	}

	defer c.queue.Done(key)

	// assert the string out of the key (format `namespace/name`)
	keyRaw := key.(string)
	log := logrus.WithField("reporter", c.reporter.GetName()).WithField("prowjob", keyRaw)
	log.Debug("processing next key")

	namespace, name, err := cache.SplitMetaNamespaceKey(keyRaw)
	if err != nil {
		log.WithError(err).Error("invalid resource key")
		c.queue.Forget(key)
		return true
	}

	// take the string key and get the object out of the indexer
	//
	// item will contain the complex object for the resource and
	// exists is a bool that'll indicate whether or not the
	// resource was created (true) or deleted (false)
	//
	// if there is an error in getting the key from the index
	// then we want to retry this particular queue key a certain
	// number of times (5 here) before we forget the queue key
	// and throw an error
	readOnlyPJ, err := c.informer.Lister().ProwJobs(namespace).Get(name)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Debug("object no longer exist")
			c.queue.Forget(key)
			return true
		}

		return c.retry(key, log, err)
	}
	pj := readOnlyPJ.DeepCopy()
	log = log.WithField("jobName", pj.Spec.Job)

	// not belong to the current reporter
	if !pj.Spec.Report || !c.reporter.ShouldReport(pj) {
		c.queue.Forget(key)
		return true
	}

	// we set omitempty on PrevReportStates, so here we need to init it if is nil
	if pj.Status.PrevReportStates == nil {
		pj.Status.PrevReportStates = map[string]v1.ProwJobState{}
	}

	// already reported current state
	if pj.Status.PrevReportStates[c.reporter.GetName()] == pj.Status.State {
		log.Trace("Already reported")
		c.queue.Forget(key)
		return true
	}

	log = log.WithField("jobStatus", pj.Status.State)
	log.Info("Will report state")
	pjs, err := c.reporter.Report(pj)
	if err != nil {
		log.WithError(err).Error("failed to report job")
		return c.retry(key, log, err)
	}

	log.Info("Reported job, now will update pj")
	reportedState := pj.Status.State
	for _, pjob := range pjs {
		// We have to retry here, if we return we lose the information that we already reported this job.
		if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			// Get it first, this is very cheap
			pj, err := c.pjclientset.ProwV1().ProwJobs(pjob.Namespace).Get(pjob.Name, metav1.GetOptions{})
			if err != nil {
				return err
			}
			// Must not wrap until we have kube 1.19, otherwise the RetryOnConflict won't recognize conflicts
			// correctly
			return c.updateReportState(pj, log, reportedState)
		}); err != nil {
			log.WithError(err).Error("Failed to update report state on prowjob")
			return c.retry(key, log, err)
		}

		log.Info("Successfully updated prowjob")
	}
	c.queue.Forget(key)
	return true
}
<commit_msg>Update each job to each jobs' reported state<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

// Package crier reports finished prowjob status to git providers.
package crier

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"
	"time"

	jsonpatch "github.com/evanphx/json-patch"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/apis/prowjobs/v1"

	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/retry"
	"k8s.io/client-go/util/workqueue"
	clientset "k8s.io/test-infra/prow/client/clientset/versioned"
	pjinformers "k8s.io/test-infra/prow/client/informers/externalversions/prowjobs/v1"
)

type reportClient interface {
	Report(pj *v1.ProwJob) ([]*v1.ProwJob, error)
	GetName() string
	ShouldReport(pj *v1.ProwJob) bool
}

// Controller struct defines how a controller should encapsulate
// logging, client connectivity, informing (list and watching)
// queueing, and handling of resource changes
type Controller struct {
	pjclientset clientset.Interface
	queue       workqueue.RateLimitingInterface
	informer    pjinformers.ProwJobInformer
	reporter    reportClient
	numWorkers  int
	wg          *sync.WaitGroup
}

// NewController constructs a new instance of the crier controller.
func NewController(
	pjclientset clientset.Interface,
	queue workqueue.RateLimitingInterface,
	informer pjinformers.ProwJobInformer,
	reporter reportClient,
	numWorkers int) *Controller {
	return &Controller{
		pjclientset: pjclientset,
		queue:       queue,
		informer:    informer,
		reporter:    reporter,
		numWorkers:  numWorkers,
		wg:          &sync.WaitGroup{},
	}
}

// Run is the main path of execution for the controller loop.
func (c *Controller) Run(ctx context.Context) {
	// handle a panic with logging and exiting
	defer utilruntime.HandleCrash()

	logrus.Info("Initiating controller")
	c.informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			logrus.WithField("prowjob", key).Trace("Add prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(newObj)
			logrus.WithField("prowjob", key).Trace("Update prowjob")
			if err != nil {
				logrus.WithError(err).Error("Cannot get key from object meta")
				return
			}
			c.queue.AddRateLimited(key)
		},
	})

	// run the informer to start listing and watching resources
	go c.informer.Informer().Run(ctx.Done())

	// do the initial synchronization (one time) to populate resources
	if !cache.WaitForCacheSync(ctx.Done(), c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Error syncing cache"))
		return
	}
	logrus.Info("Controller.Run: cache sync complete")

	// run the runWorker method every second with a stop channel
	for i := 0; i < c.numWorkers; i++ {
		go wait.Until(c.runWorker, time.Second, ctx.Done())
	}

	logrus.Infof("Started %d workers", c.numWorkers)
	<-ctx.Done()
	logrus.Info("Shutting down workers")
	// ignore new items in the queue but when all goroutines
	// have completed existing items then shutdown
	c.queue.ShutDown()
	c.wg.Wait()
}

// HasSynced allows us to satisfy the Controller interface by wiring up the informer's HasSynced
// method to it.
func (c *Controller) HasSynced() bool {
	return c.informer.Informer().HasSynced()
}

// runWorker executes the loop to process new items added to the queue.
func (c *Controller) runWorker() {
	c.wg.Add(1)
	for c.processNextItem() {
	}
	c.wg.Done()
}

func (c *Controller) retry(key interface{}, log *logrus.Entry, err error) bool {
	if c.queue.NumRequeues(key) < 5 {
		log.WithError(err).Info("Failed processing item, retrying")
		c.queue.AddRateLimited(key)
	} else {
		log.WithError(err).Error("Failed processing item, no more retries")
		c.queue.Forget(key)
	}

	return true
}

func (c *Controller) updateReportState(pj *v1.ProwJob, log *logrus.Entry, reportedState v1.ProwJobState) error {
	pjData, err := json.Marshal(pj)
	if err != nil {
		return fmt.Errorf("error marshal pj: %v", err)
	}

	// update pj report status
	newpj := pj.DeepCopy()
	// we set omitempty on PrevReportStates, so here we need to init it if is nil
	if newpj.Status.PrevReportStates == nil {
		newpj.Status.PrevReportStates = map[string]v1.ProwJobState{}
	}
	newpj.Status.PrevReportStates[c.reporter.GetName()] = reportedState

	newpjData, err := json.Marshal(newpj)
	if err != nil {
		return fmt.Errorf("error marshal new pj: %v", err)
	}

	patch, err := jsonpatch.CreateMergePatch(pjData, newpjData)
	if err != nil {
		return fmt.Errorf("error CreateMergePatch: %v", err)
	}

	if len(patch) == 0 {
		logrus.Warnf("Empty merge patch: pjData: %s, newpjData: %s", string(pjData), string(newpjData))
	}

	log.Info("Created merge patch")
	_, err = c.pjclientset.ProwV1().ProwJobs(pj.Namespace).Patch(pj.Name, types.MergePatchType, patch)
	if err != nil {
		return err
	}

	// Block until the update is in the lister to make sure that events from another controller
	// that also does reporting dont trigger another report because our lister doesn't yet contain
	// the updated Status
	if err := wait.Poll(time.Second, 3*time.Second, func() (bool, error) {
		pj, err := c.informer.Lister().ProwJobs(newpj.Namespace).Get(newpj.Name)
		if err != nil {
			return false, err
		}
		if pj.Status.PrevReportStates != nil &&
			newpj.Status.PrevReportStates[c.reporter.GetName()] == reportedState {
			return true, nil
		}
		return false, nil
	}); err != nil {
		return fmt.Errorf("failed to wait for updated report status to be in lister: %v", err)
	}
	return nil
}

// processNextItem retrieves each queued item and takes the necessary handler action based off of if
// the item was created or deleted.
func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		logrus.Debug("Queue already shut down, exiting processNextItem")
		return false
	}

	defer c.queue.Done(key)

	// assert the string out of the key (format `namespace/name`)
	keyRaw := key.(string)
	log := logrus.WithField("reporter", c.reporter.GetName()).WithField("prowjob", keyRaw)
	log.Debug("processing next key")

	namespace, name, err := cache.SplitMetaNamespaceKey(keyRaw)
	if err != nil {
		log.WithError(err).Error("invalid resource key")
		c.queue.Forget(key)
		return true
	}

	// take the string key and get the object out of the indexer
	//
	// item will contain the complex object for the resource and
	// exists is a bool that'll indicate whether or not the
	// resource was created (true) or deleted (false)
	//
	// if there is an error in getting the key from the index
	// then we want to retry this particular queue key a certain
	// number of times (5 here) before we forget the queue key
	// and throw an error
	readOnlyPJ, err := c.informer.Lister().ProwJobs(namespace).Get(name)
	if err != nil {
		if errors.IsNotFound(err) {
			log.Debug("object no longer exist")
			c.queue.Forget(key)
			return true
		}

		return c.retry(key, log, err)
	}
	pj := readOnlyPJ.DeepCopy()
	log = log.WithField("jobName", pj.Spec.Job)

	// not belong to the current reporter
	if !pj.Spec.Report || !c.reporter.ShouldReport(pj) {
		c.queue.Forget(key)
		return true
	}

	// we set omitempty on PrevReportStates, so here we need to init it if is nil
	if pj.Status.PrevReportStates == nil {
		pj.Status.PrevReportStates = map[string]v1.ProwJobState{}
	}

	// already reported current state
	if pj.Status.PrevReportStates[c.reporter.GetName()] == pj.Status.State {
		log.Trace("Already reported")
		c.queue.Forget(key)
		return true
	}

	log = log.WithField("jobStatus", pj.Status.State)
	log.Info("Will report state")
	pjs, err := c.reporter.Report(pj)
	if err != nil {
		log.WithError(err).Error("failed to report job")
		return c.retry(key, log, err)
	}

	log.Info("Reported job, now will update pj")
	for _, pjob := range pjs {
		reportedState := pjob.Status.State
		// We have to retry here, if we return we lose the information that we already reported this job.
		if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
			// Get it first, this is very cheap
			pj, err := c.pjclientset.ProwV1().ProwJobs(pjob.Namespace).Get(pjob.Name, metav1.GetOptions{})
			if err != nil {
				return err
			}
			// Must not wrap until we have kube 1.19, otherwise the RetryOnConflict won't recognize conflicts
			// correctly
			return c.updateReportState(pj, log, reportedState)
		}); err != nil {
			log.WithError(err).Error("Failed to update report state on prowjob")
			return c.retry(key, log, err)
		}

		log.Info("Successfully updated prowjob")
	}
	c.queue.Forget(key)
	return true
}
<|endoftext|>


<commit_before>package freedom

//go:generate errorgen

import (
	"context"
	"time"

	"v2ray.com/core"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/retry"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal"
	"v2ray.com/core/common/task"
	"v2ray.com/core/features/dns"
	"v2ray.com/core/features/policy"
	"v2ray.com/core/transport"
	"v2ray.com/core/transport/internet"
)

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		h := new(Handler)
		if err := core.RequireFeatures(ctx, func(pm policy.Manager, d dns.Client) error {
			return h.Init(config.(*Config), pm, d)
		}); err != nil {
			return nil, err
		}
		return h, nil
	}))
}

// Handler handles Freedom connections.
type Handler struct {
	policyManager policy.Manager
	dns           dns.Client
	config        Config
}

// Init initializes the Handler with necessary parameters.
func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error {
	h.config = *config
	h.policyManager = pm
	h.dns = d

	return nil
}

func (h *Handler) policy() policy.Session {
	p := h.policyManager.ForLevel(h.config.UserLevel)
	if h.config.Timeout > 0 && h.config.UserLevel == 0 {
		p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second
	}
	return p
}

func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
	var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP

	if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
		if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok {
			lookupFunc = lookupIPv4.LookupIPv4
		}
	} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
		if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok {
			lookupFunc = lookupIPv6.LookupIPv6
		}
	}

	ips, err := lookupFunc(domain)
	if err != nil {
		newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
	}
	if len(ips) == 0 {
		return nil
	}
	return net.IPAddress(ips[dice.Roll(len(ips))])
}

func isValidAddress(addr *net.IPOrDomain) bool {
	if addr == nil {
		return false
	}

	a := addr.AsAddress()
	return a != net.AnyIP
}

// Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return newError("target not specified.")
	}
	destination := outbound.Target
	if h.config.DestinationOverride != nil {
		server := h.config.DestinationOverride.Server
		if isValidAddress(server.Address) {
			destination.Address = server.Address.AsAddress()
		}
		if server.Port != 0 {
			destination.Port = net.Port(server.Port)
		}
	}
	newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))

	input := link.Reader
	output := link.Writer

	var conn internet.Connection
	err := retry.ExponentialBackoff(5, 100).On(func() error {
		dialDest := destination
		if h.config.useIP() && dialDest.Address.Family().IsDomain() {
			ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address())
			if ip != nil {
				dialDest = net.Destination{
					Network: dialDest.Network,
					Address: ip,
					Port:    dialDest.Port,
				}
				newError("dialing to to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
			}
		}

		rawConn, err := dialer.Dial(ctx, dialDest)
		if err != nil {
			return err
		}
		conn = rawConn
		return nil
	})
	if err != nil {
		return newError("failed to open connection to ", destination).Base(err)
	}
	defer conn.Close() // nolint: errcheck

	plcy := h.policy()
	ctx, cancel := context.WithCancel(ctx)
	timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)

	requestDone := func() error {
		defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)

		var writer buf.Writer
		if destination.Network == net.Network_TCP {
			writer = buf.NewWriter(conn)
		} else {
			writer = &buf.SequentialWriter{Writer: conn}
		}
		if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
			return newError("failed to process request").Base(err)
		}

		return nil
	}

	responseDone := func() error {
		defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)

		if err := buf.Copy(buf.NewReader(conn), output, buf.UpdateActivity(timer)); err != nil {
			return newError("failed to process response").Base(err)
		}

		return nil
	}

	if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
		return newError("connection ends").Base(err)
	}

	return nil
}
<commit_msg>force packet reader in freedom on UDP<commit_after>package freedom

//go:generate errorgen

import (
	"context"
	"time"

	"v2ray.com/core"
	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/dice"
	"v2ray.com/core/common/net"
	"v2ray.com/core/common/retry"
	"v2ray.com/core/common/session"
	"v2ray.com/core/common/signal"
	"v2ray.com/core/common/task"
	"v2ray.com/core/features/dns"
	"v2ray.com/core/features/policy"
	"v2ray.com/core/transport"
	"v2ray.com/core/transport/internet"
)

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		h := new(Handler)
		if err := core.RequireFeatures(ctx, func(pm policy.Manager, d dns.Client) error {
			return h.Init(config.(*Config), pm, d)
		}); err != nil {
			return nil, err
		}
		return h, nil
	}))
}

// Handler handles Freedom connections.
type Handler struct {
	policyManager policy.Manager
	dns           dns.Client
	config        Config
}

// Init initializes the Handler with necessary parameters.
func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error {
	h.config = *config
	h.policyManager = pm
	h.dns = d

	return nil
}

func (h *Handler) policy() policy.Session {
	p := h.policyManager.ForLevel(h.config.UserLevel)
	if h.config.Timeout > 0 && h.config.UserLevel == 0 {
		p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second
	}
	return p
}

func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
	var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP

	if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
		if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok {
			lookupFunc = lookupIPv4.LookupIPv4
		}
	} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
		if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok {
			lookupFunc = lookupIPv6.LookupIPv6
		}
	}

	ips, err := lookupFunc(domain)
	if err != nil {
		newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
	}
	if len(ips) == 0 {
		return nil
	}
	return net.IPAddress(ips[dice.Roll(len(ips))])
}

func isValidAddress(addr *net.IPOrDomain) bool {
	if addr == nil {
		return false
	}

	a := addr.AsAddress()
	return a != net.AnyIP
}

// Process implements proxy.Outbound.
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
	outbound := session.OutboundFromContext(ctx)
	if outbound == nil || !outbound.Target.IsValid() {
		return newError("target not specified.")
	}
	destination := outbound.Target
	if h.config.DestinationOverride != nil {
		server := h.config.DestinationOverride.Server
		if isValidAddress(server.Address) {
			destination.Address = server.Address.AsAddress()
		}
		if server.Port != 0 {
			destination.Port = net.Port(server.Port)
		}
	}
	newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))

	input := link.Reader
	output := link.Writer

	var conn internet.Connection
	err := retry.ExponentialBackoff(5, 100).On(func() error {
		dialDest := destination
		if h.config.useIP() && dialDest.Address.Family().IsDomain() {
			ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address())
			if ip != nil {
				dialDest = net.Destination{
					Network: dialDest.Network,
					Address: ip,
					Port:    dialDest.Port,
				}
				newError("dialing to to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
			}
		}

		rawConn, err := dialer.Dial(ctx, dialDest)
		if err != nil {
			return err
		}
		conn = rawConn
		return nil
	})
	if err != nil {
		return newError("failed to open connection to ", destination).Base(err)
	}
	defer conn.Close() // nolint: errcheck

	plcy := h.policy()
	ctx, cancel := context.WithCancel(ctx)
	timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)

	requestDone := func() error {
		defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)

		var writer buf.Writer
		if destination.Network == net.Network_TCP {
			writer = buf.NewWriter(conn)
		} else {
			writer = &buf.SequentialWriter{Writer: conn}
		}
		if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
			return newError("failed to process request").Base(err)
		}

		return nil
	}

	responseDone := func() error {
		defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)

		var reader buf.Reader
		if destination.Network == net.Network_TCP {
			reader = buf.NewReader(conn)
		} else {
			reader = &buf.PacketReader{Reader: conn}
		}
		if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
			return newError("failed to process response").Base(err)
		}

		return nil
	}

	if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
		return newError("connection ends").Base(err)
	}

	return nil
}
<|endoftext|>


<commit_before>package mpflume

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

const (
	// Channel is Flume Channel Type
	Channel = "CHANNEL"
	// Sink is Flume Sink Type
	Sink    = "SINK"
	// Source is Flume Source Type
	Source  = "SOURCE"
)

// FlumePlugin mackerel plugin
type FlumePlugin struct {
	URI    string
	Prefix string
}

var graphdef = map[string]mp.Graphs{}

// MetricKeyPrefix interface for PluginWithPrefix
func (p *FlumePlugin) MetricKeyPrefix() string {
	if p.Prefix == "" {
		p.Prefix = "flume"
	}
	return p.Prefix
}

// GraphDefinition interface for mackerelplugin
func (p *FlumePlugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

// FetchMetrics interface for mackerelplugin
func (p *FlumePlugin) FetchMetrics() (map[string]float64, error) {
	m, err := p.getMetrics()
	if err != nil {
		return nil, err
	}
	return p.parseMetrics(m), nil
}

func (p *FlumePlugin) getMetrics() (map[string]interface{}, error) {
	res, err := http.Get(p.URI)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	var m map[string]interface{}
	dec := json.NewDecoder(res.Body)
	if err := dec.Decode(&m); err != nil {
		return nil, err
	}
	return m, nil
}

func (p *FlumePlugin) parseMetrics(metrics map[string]interface{}) map[string]float64 {
	ret := make(map[string]float64)
	for k, v := range metrics {
		array := strings.Split(k, ".")
		typeName := array[0]
		componentName := array[1]
		switch typeName {
		case Channel:
			p.parseChannel(ret, componentName, v.(map[string]interface{}))
			p.addGraphdefChannel(componentName)
		case Sink:
			p.parseSink(ret, componentName, v.(map[string]interface{}))
			p.addGraphdefSink(componentName)
		case Source:
			p.parseSource(ret, componentName, v.(map[string]interface{}))
			p.addGraphdefSource(componentName)
		}
	}

	return ret
}

func (p *FlumePlugin) convertFloat64(value string) float64 {
	f, _ := strconv.ParseFloat(value, 64)
	return f
}

func (p *FlumePlugin) parseChannel(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret[componentName + ".ChannelCapacity"]       = p.convertFloat64(value["ChannelCapacity"].(string))
	ret[componentName + ".ChannelSize"]           = p.convertFloat64(value["ChannelSize"].(string))
	ret[componentName + ".ChannelFillPercentage"] = p.convertFloat64(value["ChannelFillPercentage"].(string))
	ret[componentName + ".EventPutAttemptCount"]  = p.convertFloat64(value["EventPutAttemptCount"].(string))
	ret[componentName + ".EventPutSuccessCount"]  = p.convertFloat64(value["EventPutSuccessCount"].(string))
	ret[componentName + ".EventTakeAttemptCount"] = p.convertFloat64(value["EventTakeAttemptCount"].(string))
	ret[componentName + ".EventTakeSuccessCount"] = p.convertFloat64(value["EventTakeSuccessCount"].(string))
}

func (p *FlumePlugin) addGraphdefChannel(componentName string) {
	labelPrefix := strings.Title(p.Prefix + " " + componentName)
	graphdefPrefix := "channel."

	graphdef[graphdefPrefix + "capacity"] = mp.Graphs{
		Label: labelPrefix + " Channel Capacity",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".ChannelCapacity", Label: "Channel Capacity"},
			{Name: componentName + ".ChannelSize", Label: "Channel Size"},
		},
	}
	graphdef[graphdefPrefix + "use_rate"] = mp.Graphs{
		Label: labelPrefix + " Channel Use Rate",
		Unit:  "percentage",
		Metrics: []mp.Metrics{
			{Name: componentName + ".ChannelFillPercentage", Label: "Channel Fill Percentage"},
		},
	}
	graphdef[graphdefPrefix + "event_put_num"] = mp.Graphs{
		Label: labelPrefix + " Event Put Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".EventPutAttemptCount", Label: "Attempt Count", Diff: true},
			{Name: componentName + ".EventPutSuccessCount", Label: "Success Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "event_take_num"] = mp.Graphs{
		Label: labelPrefix + " Event Take Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".EventTakeAttemptCount", Label: "Attempt Count", Diff: true},
			{Name: componentName + ".EventTakeSuccessCount", Label: "Success Count", Diff: true},
		},
	}
}

func (p *FlumePlugin) parseSink(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret[componentName + ".BatchCompleteCount"]     = p.convertFloat64(value["BatchCompleteCount"].(string))
	ret[componentName + ".BatchEmptyCount"]        = p.convertFloat64(value["BatchEmptyCount"].(string))
	ret[componentName + ".BatchUnderflowCount"]    = p.convertFloat64(value["BatchUnderflowCount"].(string))
	ret[componentName + ".ConnectionCreatedCount"] = p.convertFloat64(value["ConnectionCreatedCount"].(string))
	ret[componentName + ".ConnectionClosedCount"]  = p.convertFloat64(value["ConnectionClosedCount"].(string))
	ret[componentName + ".ConnectionFailedCount"]  = p.convertFloat64(value["ConnectionFailedCount"].(string))
	ret[componentName + ".EventDrainAttemptCount"] = p.convertFloat64(value["EventDrainAttemptCount"].(string))
	ret[componentName + ".EventDrainSuccessCount"] = p.convertFloat64(value["EventDrainSuccessCount"].(string))
}

func (p *FlumePlugin) addGraphdefSink(componentName string) {
	labelPrefix := strings.Title(p.Prefix + " " + componentName)
	graphdefPrefix := "sink."

	graphdef[graphdefPrefix + "batch_num"] = mp.Graphs{
		Label: labelPrefix + " Batch Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".BatchCompleteCount", Label: "Complete Count", Diff: true},
			{Name: componentName + ".BatchEmptyCount", Label: "Empty Count", Diff: true},
			{Name: componentName + ".BatchUnderflowCount", Label: "Underflow Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "connection"] = mp.Graphs{
		Label: labelPrefix + " Connection",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".ConnectionCreatedCount", Label: "Created Count", Diff: true},
			{Name: componentName + ".ConnectionClosedCount", Label: "Closed Count", Diff: true},
			{Name: componentName + ".ConnectionFailedCount", Label: "Failed Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "event_drain_num"] = mp.Graphs{
		Label: labelPrefix + " Event Drain Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".EventDrainAttemptCount", Label: "Attempt Count", Diff: true},
			{Name: componentName + ".EventDrainSuccessCount", Label: "Success Count", Diff: true},
		},
	}
}

func (p *FlumePlugin) parseSource(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret[componentName + ".AppendAcceptedCount"]      = p.convertFloat64(value["AppendAcceptedCount"].(string))
	ret[componentName + ".AppendReceivedCount"]      = p.convertFloat64(value["AppendReceivedCount"].(string))
	ret[componentName + ".AppendBatchAcceptedCount"] = p.convertFloat64(value["AppendBatchAcceptedCount"].(string))
	ret[componentName + ".AppendBatchReceivedCount"] = p.convertFloat64(value["AppendBatchReceivedCount"].(string))
	ret[componentName + ".EventAcceptedCount"]       = p.convertFloat64(value["EventAcceptedCount"].(string))
	ret[componentName + ".EventReceivedCount"]       = p.convertFloat64(value["EventReceivedCount"].(string))
	ret[componentName + ".OpenConnectionCount"]      = p.convertFloat64(value["OpenConnectionCount"].(string))
}

func (p *FlumePlugin) addGraphdefSource(componentName string) {
	labelPrefix := strings.Title(p.Prefix + " " + componentName)
	graphdefPrefix := "source."

	graphdef[graphdefPrefix + "append_num"] = mp.Graphs{
		Label: labelPrefix + " Append Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".AppendAcceptedCount", Label: "Accepted Count", Diff: true},
			{Name: componentName + ".AppendReceivedCount", Label: "Received Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "append_batch_num"] = mp.Graphs{
		Label: labelPrefix + " Append Batch Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".AppendBatchAcceptedCount", Label: "Accepted Count", Diff: true},
			{Name: componentName + ".AppendBatchReceivedCount", Label: "Received Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "event_num"] = mp.Graphs{
		Label: labelPrefix + " Event Num",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".EventAcceptedCount", Label: "Accepted Count", Diff: true},
			{Name: componentName + ".EventReceivedCount", Label: "Received Count", Diff: true},
		},
	}
	graphdef[graphdefPrefix + "connection"] = mp.Graphs{
		Label: labelPrefix + " Connection",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: componentName + ".OpenConnectionCount", Label: "Open Count"},
		},
	}
}

// Do the plugin
func Do() {
	optHost   := flag.String("host", "localhost", "Host Name")
	optPort   := flag.String("port", "41414", "Port")
	optPrefix := flag.String("metric-key-prefix", "", "Metric key prefix")
	flag.Parse()
	mp.NewMackerelPlugin(&FlumePlugin{
		URI:    fmt.Sprintf("http://%s:%s/metrics", *optHost, *optPort),
		Prefix: *optPrefix,
	}).Run()
}
<commit_msg>fixed graph definition<commit_after>package mpflume

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	mp "github.com/mackerelio/go-mackerel-plugin"
)

const (
	// Channel is Flume Channel Type
	Channel = "CHANNEL"
	// Sink is Flume Sink Type
	Sink    = "SINK"
	// Source is Flume Source Type
	Source  = "SOURCE"
)

// FlumePlugin mackerel plugin
type FlumePlugin struct {
	URI    string
	Prefix string
}

// MetricKeyPrefix interface for PluginWithPrefix
func (p *FlumePlugin) MetricKeyPrefix() string {
	if p.Prefix == "" {
		p.Prefix = "flume"
	}
	return p.Prefix
}

// GraphDefinition interface for mackerelplugin
func (p *FlumePlugin) GraphDefinition() map[string]mp.Graphs {
	labelPrefix := strings.Title(p.Prefix)
	return map[string]mp.Graphs{
		"channel.capacity.#": {
			Label: labelPrefix + " Channel Capacity",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "ChannelCapacity", Label: "Channel Capacity"},
				{Name: "ChannelSize", Label: "Channel Size"},
			},
		},
		"channel.use_rate.#": {
			Label: labelPrefix + " Channel Use Rate",
			Unit:  "percentage",
			Metrics: []mp.Metrics{
				{Name: "ChannelFillPercentage", Label: "Channel Fill Percentage"},
			},
		},
		"channel.event_put_num.#": {
			Label: labelPrefix + " Channel Event Put Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "EventPutAttemptCount", Label: "Attempt Count", Diff: true},
				{Name: "EventPutSuccessCount", Label: "Success Count", Diff: true},
			},
		},
		"channel.event_take_num.#": {
			Label: labelPrefix + " Channel Event Take Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "EventTakeAttemptCount", Label: "Attempt Count", Diff: true},
				{Name: "EventTakeSuccessCount", Label: "Success Count", Diff: true},
			},
		},
		"sink.batch_num.#": {
			Label: labelPrefix + " Sink Batch Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "BatchCompleteCount", Label: "Complete Count", Diff: true},
				{Name: "BatchEmptyCount", Label: "Empty Count", Diff: true},
				{Name: "BatchUnderflowCount", Label: "Underflow Count", Diff: true},
			},
		},
		"sink.connection.#": {
			Label: labelPrefix + " Sink Connection",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "ConnectionCreatedCount", Label: "Created Count", Diff: true},
				{Name: "ConnectionClosedCount", Label: "Closed Count", Diff: true},
				{Name: "ConnectionFailedCount", Label: "Failed Count", Diff: true},
			},
		},
		"sink.event_drain_num.#": {
			Label: labelPrefix + " Sink Event Drain Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "EventDrainAttemptCount", Label: "Attempt Count", Diff: true},
				{Name: "EventDrainSuccessCount", Label: "Success Count", Diff: true},
			},
		},
		"source.append_num.#": {
			Label: labelPrefix + " Source Append Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "AppendAcceptedCount", Label: "Accepted Count", Diff: true},
				{Name: "AppendReceivedCount", Label: "Received Count", Diff: true},
			},
		},
		"source.append_batch_num.#": {
			Label: labelPrefix + " Source Append Batch Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "AppendBatchAcceptedCount", Label: "Accepted Count", Diff: true},
				{Name: "AppendBatchReceivedCount", Label: "Received Count", Diff: true},
			},
		},
		"source.event_num.#": {
			Label: labelPrefix + " Source Event Num",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "EventAcceptedCount", Label: "Accepted Count", Diff: true},
				{Name: "EventReceivedCount", Label: "Received Count", Diff: true},
			},
		},
		"source.connection.#": {
			Label: labelPrefix + " Source Connection",
			Unit:  "integer",
			Metrics: []mp.Metrics{
				{Name: "OpenConnectionCount", Label: "Open Count"},
			},
		},
	}
}

// FetchMetrics interface for mackerelplugin
func (p *FlumePlugin) FetchMetrics() (map[string]float64, error) {
	m, err := p.getMetrics()
	if err != nil {
		return nil, err
	}
	return p.parseMetrics(m), nil
}

func (p *FlumePlugin) getMetrics() (map[string]interface{}, error) {
	res, err := http.Get(p.URI)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	var m map[string]interface{}
	dec := json.NewDecoder(res.Body)
	if err := dec.Decode(&m); err != nil {
		return nil, err
	}
	return m, nil
}

func (p *FlumePlugin) parseMetrics(metrics map[string]interface{}) map[string]float64 {
	ret := make(map[string]float64)
	for k, v := range metrics {
		array := strings.Split(k, ".")
		typeName := array[0]
		componentName := array[1]
		switch typeName {
		case Channel:
			p.parseChannel(ret, componentName, v.(map[string]interface{}))
		case Sink:
			p.parseSink(ret, componentName, v.(map[string]interface{}))
		case Source:
			p.parseSource(ret, componentName, v.(map[string]interface{}))
		}
	}

	return ret
}

func (p *FlumePlugin) convertFloat64(value string) float64 {
	f, _ := strconv.ParseFloat(value, 64)
	return f
}

func (p *FlumePlugin) parseChannel(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret["channel.capacity." + componentName + ".ChannelCapacity"]             = p.convertFloat64(value["ChannelCapacity"].(string))
	ret["channel.capacity." + componentName + ".ChannelSize"]                 = p.convertFloat64(value["ChannelSize"].(string))
	ret["channel.use_rate." + componentName + ".ChannelFillPercentage"]       = p.convertFloat64(value["ChannelFillPercentage"].(string))
	ret["channel.event_put_num." + componentName + ".EventPutAttemptCount"]   = p.convertFloat64(value["EventPutAttemptCount"].(string))
	ret["channel.event_put_num." + componentName + ".EventPutSuccessCount"]   = p.convertFloat64(value["EventPutSuccessCount"].(string))
	ret["channel.event_take_num." + componentName + ".EventTakeAttemptCount"] = p.convertFloat64(value["EventTakeAttemptCount"].(string))
	ret["channel.event_take_num." + componentName + ".EventTakeSuccessCount"] = p.convertFloat64(value["EventTakeSuccessCount"].(string))
}

func (p *FlumePlugin) parseSink(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret["sink.batch_num." + componentName + ".BatchCompleteCount"]           = p.convertFloat64(value["BatchCompleteCount"].(string))
	ret["sink.batch_num." + componentName + ".BatchEmptyCount"]              = p.convertFloat64(value["BatchEmptyCount"].(string))
	ret["sink.batch_num." + componentName + ".BatchUnderflowCount"]          = p.convertFloat64(value["BatchUnderflowCount"].(string))
	ret["sink.connection." + componentName + ".ConnectionCreatedCount"]      = p.convertFloat64(value["ConnectionCreatedCount"].(string))
	ret["sink.connection." + componentName + ".ConnectionClosedCount"]       = p.convertFloat64(value["ConnectionClosedCount"].(string))
	ret["sink.connection." + componentName + ".ConnectionFailedCount"]       = p.convertFloat64(value["ConnectionFailedCount"].(string))
	ret["sink.event_drain_num." + componentName + ".EventDrainAttemptCount"] = p.convertFloat64(value["EventDrainAttemptCount"].(string))
	ret["sink.event_drain_num." + componentName + ".EventDrainSuccessCount"] = p.convertFloat64(value["EventDrainSuccessCount"].(string))
}

func (p *FlumePlugin) parseSource(ret map[string]float64, componentName string, value map[string]interface{}) {
	ret["source.append_num." + componentName + ".AppendAcceptedCount"]            = p.convertFloat64(value["AppendAcceptedCount"].(string))
	ret["source.append_num." + componentName + ".AppendReceivedCount"]            = p.convertFloat64(value["AppendReceivedCount"].(string))
	ret["source.append_batch_num." + componentName + ".AppendBatchAcceptedCount"] = p.convertFloat64(value["AppendBatchAcceptedCount"].(string))
	ret["source.append_batch_num." + componentName + ".AppendBatchReceivedCount"] = p.convertFloat64(value["AppendBatchReceivedCount"].(string))
	ret["source.event_num." + componentName + ".EventAcceptedCount"]              = p.convertFloat64(value["EventAcceptedCount"].(string))
	ret["source.event_num." + componentName + ".EventReceivedCount"]              = p.convertFloat64(value["EventReceivedCount"].(string))
	ret["source.connection." + componentName + ".OpenConnectionCount"]            = p.convertFloat64(value["OpenConnectionCount"].(string))
}

// Do the plugin
func Do() {
	optHost   := flag.String("host", "localhost", "Host Name")
	optPort   := flag.String("port", "41414", "Port")
	optPrefix := flag.String("metric-key-prefix", "", "Metric key prefix")
	flag.Parse()
	mp.NewMackerelPlugin(&FlumePlugin{
		URI:    fmt.Sprintf("http://%s:%s/metrics", *optHost, *optPort),
		Prefix: *optPrefix,
	}).Run()
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
)

// CmdSimpleFSMove is the 'fs list' command.
type CmdSimpleFSMove struct {
	libkb.Contextified
	src         []keybase1.Path
	dest        keybase1.Path
	interactive bool
	force       bool
}

// NewCmdSimpleFSMove creates a new cli.Command.
func NewCmdSimpleFSMove(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "mv",
		ArgumentHelp: "<source> [source] <dest>",
		Usage:        "move one or more directory elements to dest",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdSimpleFSMove{Contextified: libkb.NewContextified(g)}, "mv", c)
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "i, interactive",
				Usage: "Prompt before overwrite",
			},
			cli.BoolFlag{
				Name:  "f, force",
				Usage: "force overwrite",
			},
		},
	}
}

// Run runs the command in client/server mode.
func (c *CmdSimpleFSMove) Run() error {
	cli, err := GetSimpleFSClient(c.G())
	if err != nil {
		return err
	}

	ctx := context.TODO()

	destPaths, err := doSimpleFSGlob(c.G(), ctx, cli, c.src)
	if err != nil {
		return err
	}

	// Eat the error because it's ok here if the dest doesn't exist
	isDestDir, destPathString, _ := checkPathIsDir(ctx, cli, c.dest)

	for _, src := range destPaths {
		c.G().Log.Debug("SimpleFSMove %s -> %s, %v", pathToString(src), destPathString, isDestDir)

		dest, err := makeDestPath(c.G(), ctx, cli, src, c.dest, isDestDir, destPathString)

		if err == ErrTargetFileExists {
			if c.interactive == true {
				err = doOverwritePrompt(c.G(), pathToString(dest))
			} else if c.force == true {
				err = nil
			}
		}

		if err != nil {
			return err
		}
		c.G().Log.Debug("SimpleFSMove %s -> %s", pathToString(src), pathToString(dest))

		opid, err := cli.SimpleFSMakeOpid(ctx)
		if err != nil {
			return err
		}
		defer cli.SimpleFSClose(ctx, opid)

		err = cli.SimpleFSMove(ctx, keybase1.SimpleFSMoveArg{
			OpID: opid,
			Src:  src,
			Dest: dest,
		})
		if err != nil {
			break
		}
		err = cli.SimpleFSWait(ctx, opid)
		if err != nil {
			break
		}
	}
	return err
}

// ParseArgv does nothing for this command.
func (c *CmdSimpleFSMove) ParseArgv(ctx *cli.Context) error {
	var err error
	c.interactive = ctx.Bool("interactive")
	c.force = ctx.Bool("force")

	if c.force && c.interactive {
		return errors.New("force and interactive are incompatible")
	}

	c.src, c.dest, err = parseSrcDestArgs(c.G(), ctx, "mv")
	return err
}

// GetUsage says what this command needs to operate.
func (c *CmdSimpleFSMove) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		KbKeyring: true,
		API:       true,
	}
}
<commit_msg>add OpCanceler to SimpleFSMove<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"

	"golang.org/x/net/context"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
)

// CmdSimpleFSMove is the 'fs list' command.
type CmdSimpleFSMove struct {
	libkb.Contextified
	src         []keybase1.Path
	dest        keybase1.Path
	interactive bool
	force       bool
	opCanceler  *OpCanceler
}

var _ Canceler = (*CmdSimpleFSMove)(nil)

// NewCmdSimpleFSMove creates a new cli.Command.
func NewCmdSimpleFSMove(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "mv",
		ArgumentHelp: "<source> [source] <dest>",
		Usage:        "move one or more directory elements to dest",
		Action: func(c *cli.Context) {
			cl.ChooseCommand(&CmdSimpleFSMove{
				Contextified: libkb.NewContextified(g),
				opCanceler:   NewOpCanceler(g),
			}, "mv", c)
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:  "i, interactive",
				Usage: "Prompt before overwrite",
			},
			cli.BoolFlag{
				Name:  "f, force",
				Usage: "force overwrite",
			},
		},
	}
}

// Run runs the command in client/server mode.
func (c *CmdSimpleFSMove) Run() error {
	cli, err := GetSimpleFSClient(c.G())
	if err != nil {
		return err
	}

	ctx := context.TODO()

	destPaths, err := doSimpleFSGlob(c.G(), ctx, cli, c.src)
	if err != nil {
		return err
	}

	// Eat the error because it's ok here if the dest doesn't exist
	isDestDir, destPathString, _ := checkPathIsDir(ctx, cli, c.dest)

	for _, src := range destPaths {
		c.G().Log.Debug("SimpleFSMove %s -> %s, %v", pathToString(src), destPathString, isDestDir)

		dest, err := makeDestPath(c.G(), ctx, cli, src, c.dest, isDestDir, destPathString)

		if err == ErrTargetFileExists {
			if c.interactive == true {
				err = doOverwritePrompt(c.G(), pathToString(dest))
			} else if c.force == true {
				err = nil
			}
		}

		if err != nil {
			return err
		}
		c.G().Log.Debug("SimpleFSMove %s -> %s", pathToString(src), pathToString(dest))

		// Don't spawn new jobs if we've been cancelled.
		// TODO: This is still a race condition, if we get cancelled immediately after.
		if c.opCanceler.IsCancelled() {
			break
		}

		opid, err := cli.SimpleFSMakeOpid(ctx)
		if err != nil {
			return err
		}
		c.opCanceler.AddOp(opid)
		defer cli.SimpleFSClose(ctx, opid)

		err = cli.SimpleFSMove(ctx, keybase1.SimpleFSMoveArg{
			OpID: opid,
			Src:  src,
			Dest: dest,
		})
		if err != nil {
			break
		}
		err = cli.SimpleFSWait(ctx, opid)
		if err != nil {
			break
		}
	}
	return err
}

// ParseArgv does nothing for this command.
func (c *CmdSimpleFSMove) ParseArgv(ctx *cli.Context) error {
	var err error
	c.interactive = ctx.Bool("interactive")
	c.force = ctx.Bool("force")

	if c.force && c.interactive {
		return errors.New("force and interactive are incompatible")
	}

	c.src, c.dest, err = parseSrcDestArgs(c.G(), ctx, "mv")
	return err
}

// GetUsage says what this command needs to operate.
func (c *CmdSimpleFSMove) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		KbKeyring: true,
		API:       true,
	}
}

func (c *CmdSimpleFSMove) Cancel() error {
	return c.opCanceler.Cancel()
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdTeamSettings struct {
	libkb.Contextified

	Team keybase1.TeamName

	// These fields are non-zero valued when their action is requested
	Description         *string
	JoinAsRole          *keybase1.TeamRole
	ProfilePromote      *bool
	AllowProfilePromote *bool
	Showcase            *bool
}

func newCmdTeamSettings(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "settings",
		ArgumentHelp: "<team name>",
		Usage:        "Edit team settings.",
		Description:  teamSettingsDoc,
		Action: func(c *cli.Context) {
			cmd := NewCmdTeamSettingsRunner(g)
			cl.ChooseCommand(cmd, "settings", c)
		},
		Flags: []cli.Flag{
			// Many of these are StringFlag instead of BoolFlag because BoolFlag is displeasing.
			// For example `keybase team settings teamname --bool-flag false` sets the flag to true.

			cli.BoolFlag{
				Name:  "p, print",
				Usage: "Print all your team settings",
			},
			cli.StringFlag{
				Name:  "description",
				Usage: "Set the team description",
			},
			cli.StringFlag{
				Name:  "open-team",
				Usage: "[reader|writer|off] Set whether anyone can join without being invited and the role they become",
			},
			cli.StringFlag{
				Name:  "profile-promote",
				Usage: "[yes|no] Set whether your own profile should promote this team and its description",
			},
			cli.StringFlag{
				Name:  "allow-profile-promote",
				Usage: "[yes|no] Set whether non-admins are allowed to promote this team and its description on their profiles",
			},
			cli.StringFlag{
				Name:  "showcase",
				Usage: "[yes|no] Set whether to promote this team and its description on keybase.io/popular-teams",
			},
		},
	}
}

func NewCmdTeamSettingsRunner(g *libkb.GlobalContext) *CmdTeamSettings {
	return &CmdTeamSettings{Contextified: libkb.NewContextified(g)}
}

func (c *CmdTeamSettings) ParseArgv(ctx *cli.Context) (err error) {
	c.Team, err = ParseOneTeamNameK1(ctx)
	if err != nil {
		return err
	}

	var exclusiveActions []string

	if ctx.IsSet("description") {
		exclusiveActions = append(exclusiveActions, "description")
		desc := ctx.String("description")
		c.Description = &desc
	}

	if ctx.IsSet("open-team") {
		exclusiveActions = append(exclusiveActions, "open-team")

		role := keybase1.TeamRole_NONE
		val := ctx.String("open-team")
		switch val {
		case "reader":
			role = keybase1.TeamRole_READER
		case "writer":
			role = keybase1.TeamRole_WRITER
		default:
			open, err := cli.ParseBoolFriendly(val)
			if err != nil || open {
				return fmt.Errorf("open-team must be one of [reader|writer|off]")
			}
		}
		c.JoinAsRole = &role
	}

	if ctx.IsSet("profile-promote") {
		exclusiveActions = append(exclusiveActions, "profile-promote")
		val, err := ctx.BoolStrict("profile-promote")
		if err != nil {
			return err
		}
		c.ProfilePromote = &val
	}

	if ctx.IsSet("allow-profile-promote") {
		exclusiveActions = append(exclusiveActions, "allow-profile-promote")
		val, err := ctx.BoolStrict("allow-profile-promote")
		if err != nil {
			return err
		}
		c.AllowProfilePromote = &val
	}

	if ctx.IsSet("showcase") {
		exclusiveActions = append(exclusiveActions, "showcase")
		val, err := ctx.BoolStrict("showcase")
		if err != nil {
			return err
		}
		c.Showcase = &val
	}

	if len(exclusiveActions) > 1 {
		return fmt.Errorf("only one of these actions a time: %v", strings.Join(exclusiveActions, ", "))
	}

	return nil
}

func (c *CmdTeamSettings) Run() error {
	ctx, ctxCancel := context.WithCancel(context.TODO())
	defer ctxCancel()
	ctx = libkb.WithLogTag(ctx, "CTS")

	cli, err := GetTeamsClient(c.G())
	if err != nil {
		return err
	}

	if c.Description != nil {
		err = c.setDescription(ctx, cli, *c.Description)
		if err != nil {
			return err
		}
	}

	if c.JoinAsRole != nil {
		err = c.setOpenness(ctx, cli, *c.JoinAsRole)
		if err != nil {
			return err
		}
	}

	if c.ProfilePromote != nil {
		err = c.setProfilePromote(ctx, cli, *c.ProfilePromote)
		if err != nil {
			return err
		}
	}

	if c.AllowProfilePromote != nil {
		err = c.setAllowProfilePromote(ctx, cli, *c.AllowProfilePromote)
		if err != nil {
			return err
		}
	}

	if c.Showcase != nil {
		err = c.setShowcase(ctx, cli, *c.Showcase)
		if err != nil {
			return err
		}
	}

	err = c.printCurrentSettings(ctx, cli)
	if err != nil {
		return err
	}

	return nil
}

func (c *CmdTeamSettings) setDescription(ctx context.Context, cli keybase1.TeamsClient, desc string) error {
	return cli.SetTeamShowcase(ctx, keybase1.SetTeamShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: nil,
		Description: &desc,
	})
}

func (c *CmdTeamSettings) setOpenness(ctx context.Context, cli keybase1.TeamsClient, joinAsRole keybase1.TeamRole) error {
	dui := c.G().UI.GetTerminalUI()

	open := joinAsRole != keybase1.TeamRole_NONE

	arg := keybase1.TeamSetSettingsArg{
		Name: c.Team.String(),
		Settings: keybase1.TeamSettings{
			Open:   open,
			JoinAs: joinAsRole,
		},
	}

	err := cli.TeamSetSettings(ctx, arg)
	if err != nil {
		if e, ok := err.(libkb.NoOpError); ok {
			dui.Printf("%s\n", e.Desc)
			return nil
		}

		return err
	}

	if open {
		dui.Printf("Team set to open.\n")
	} else {
		dui.Printf("Team set to closed.\n")
	}
	return nil
}

func (c *CmdTeamSettings) setProfilePromote(ctx context.Context, cli keybase1.TeamsClient, promote bool) error {
	return cli.SetTeamMemberShowcase(ctx, keybase1.SetTeamMemberShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: promote,
	})
}

func (c *CmdTeamSettings) setAllowProfilePromote(ctx context.Context, cli keybase1.TeamsClient, allow bool) error {
	// awaiting CORE-6550
	return fmt.Errorf("The ability to allow non-admins to promote your team is coming soon!")
}

func (c *CmdTeamSettings) setShowcase(ctx context.Context, cli keybase1.TeamsClient, showcase bool) error {
	return cli.SetTeamShowcase(ctx, keybase1.SetTeamShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: &showcase,
		Description: nil,
	})
}

func (c *CmdTeamSettings) printCurrentSettings(ctx context.Context, cli keybase1.TeamsClient) error {
	details, err := cli.TeamGet(ctx, keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: true})
	if err != nil {
		return err
	}

	var showcaseInfo *keybase1.TeamAndMemberShowcase
	tmp, err := cli.GetTeamAndMemberShowcase(ctx, c.Team.String())
	if err != nil {
		c.G().Log.CDebugf(ctx, "failed to get team showcase info: %v", err)
	} else {
		showcaseInfo = &tmp
	}

	dui := c.G().UI.GetTerminalUI()
	dui.Printf("Current settings for team %q:\n", c.Team.String())
	if showcaseInfo != nil && showcaseInfo.TeamShowcase.Description != nil {
		dui.Printf("  Description:     %v\n", *showcaseInfo.TeamShowcase.Description)
	}
	dui.Printf("  Open:            %v\n", c.tfToYn(details.Settings.Open,
		fmt.Sprintf("default membership = %v", strings.ToLower(details.Settings.JoinAs.String()))))
	if showcaseInfo != nil {
		dui.Printf("  Showcased:       %v\n", c.tfToYn(showcaseInfo.TeamShowcase.IsShowcased, "on keybase.io/popular-teams"))
		dui.Printf("  Promoted:        %v\n", c.tfToYn(showcaseInfo.IsMemberShowcased, "on your profile"))
		// CORE-6550: show whether non-admins are allowed to promote
	}

	return nil
}

func (c *CmdTeamSettings) tfToYn(x bool, parenthetical string) string {
	if x {
		if len(parenthetical) > 0 {
			return fmt.Sprintf("yes (%v)", parenthetical)
		}
		return "yes"
	}
	return "no"
}

func (c *CmdTeamSettings) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}

const teamSettingsDoc = `"keybase team settings" lets you edit settings for a team

EXAMPLES:
Review team settings:
    keybase team settings acme
Open a team so anyone can join as a reader:
    keybase team settings acme --open-team=reader
Showcase a team publicly:
    keybase team settings acme --showcase=yes
Promote a team on your profile:
    keybase team settings acme --profile-promote=yes
`
<commit_msg>add more team settings examples<commit_after>package client

import (
	"fmt"
	"strings"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdTeamSettings struct {
	libkb.Contextified

	Team keybase1.TeamName

	// These fields are non-zero valued when their action is requested
	Description         *string
	JoinAsRole          *keybase1.TeamRole
	ProfilePromote      *bool
	AllowProfilePromote *bool
	Showcase            *bool
}

func newCmdTeamSettings(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "settings",
		ArgumentHelp: "<team name>",
		Usage:        "Edit team settings.",
		Description:  teamSettingsDoc,
		Action: func(c *cli.Context) {
			cmd := NewCmdTeamSettingsRunner(g)
			cl.ChooseCommand(cmd, "settings", c)
		},
		Flags: []cli.Flag{
			// Many of these are StringFlag instead of BoolFlag because BoolFlag is displeasing.
			// For example `keybase team settings teamname --bool-flag false` sets the flag to true.

			cli.BoolFlag{
				Name:  "p, print",
				Usage: "Print all your team settings",
			},
			cli.StringFlag{
				Name:  "description",
				Usage: "Set the team description",
			},
			cli.StringFlag{
				Name:  "open-team",
				Usage: "[reader|writer|off] Set whether anyone can join without being invited and the role they become",
			},
			cli.StringFlag{
				Name:  "profile-promote",
				Usage: "[yes|no] Set whether your own profile should promote this team and its description",
			},
			cli.StringFlag{
				Name:  "allow-profile-promote",
				Usage: "[yes|no] Set whether non-admins are allowed to promote this team and its description on their profiles",
			},
			cli.StringFlag{
				Name:  "showcase",
				Usage: "[yes|no] Set whether to promote this team and its description on keybase.io/popular-teams",
			},
		},
	}
}

func NewCmdTeamSettingsRunner(g *libkb.GlobalContext) *CmdTeamSettings {
	return &CmdTeamSettings{Contextified: libkb.NewContextified(g)}
}

func (c *CmdTeamSettings) ParseArgv(ctx *cli.Context) (err error) {
	c.Team, err = ParseOneTeamNameK1(ctx)
	if err != nil {
		return err
	}

	var exclusiveActions []string

	if ctx.IsSet("description") {
		exclusiveActions = append(exclusiveActions, "description")
		desc := ctx.String("description")
		c.Description = &desc
	}

	if ctx.IsSet("open-team") {
		exclusiveActions = append(exclusiveActions, "open-team")

		role := keybase1.TeamRole_NONE
		val := ctx.String("open-team")
		switch val {
		case "reader":
			role = keybase1.TeamRole_READER
		case "writer":
			role = keybase1.TeamRole_WRITER
		default:
			open, err := cli.ParseBoolFriendly(val)
			if err != nil || open {
				return fmt.Errorf("open-team must be one of [reader|writer|off]")
			}
		}
		c.JoinAsRole = &role
	}

	if ctx.IsSet("profile-promote") {
		exclusiveActions = append(exclusiveActions, "profile-promote")
		val, err := ctx.BoolStrict("profile-promote")
		if err != nil {
			return err
		}
		c.ProfilePromote = &val
	}

	if ctx.IsSet("allow-profile-promote") {
		exclusiveActions = append(exclusiveActions, "allow-profile-promote")
		val, err := ctx.BoolStrict("allow-profile-promote")
		if err != nil {
			return err
		}
		c.AllowProfilePromote = &val
	}

	if ctx.IsSet("showcase") {
		exclusiveActions = append(exclusiveActions, "showcase")
		val, err := ctx.BoolStrict("showcase")
		if err != nil {
			return err
		}
		c.Showcase = &val
	}

	if len(exclusiveActions) > 1 {
		return fmt.Errorf("only one of these actions a time: %v", strings.Join(exclusiveActions, ", "))
	}

	return nil
}

func (c *CmdTeamSettings) Run() error {
	ctx, ctxCancel := context.WithCancel(context.TODO())
	defer ctxCancel()
	ctx = libkb.WithLogTag(ctx, "CTS")

	cli, err := GetTeamsClient(c.G())
	if err != nil {
		return err
	}

	if c.Description != nil {
		err = c.setDescription(ctx, cli, *c.Description)
		if err != nil {
			return err
		}
	}

	if c.JoinAsRole != nil {
		err = c.setOpenness(ctx, cli, *c.JoinAsRole)
		if err != nil {
			return err
		}
	}

	if c.ProfilePromote != nil {
		err = c.setProfilePromote(ctx, cli, *c.ProfilePromote)
		if err != nil {
			return err
		}
	}

	if c.AllowProfilePromote != nil {
		err = c.setAllowProfilePromote(ctx, cli, *c.AllowProfilePromote)
		if err != nil {
			return err
		}
	}

	if c.Showcase != nil {
		err = c.setShowcase(ctx, cli, *c.Showcase)
		if err != nil {
			return err
		}
	}

	err = c.printCurrentSettings(ctx, cli)
	if err != nil {
		return err
	}

	return nil
}

func (c *CmdTeamSettings) setDescription(ctx context.Context, cli keybase1.TeamsClient, desc string) error {
	return cli.SetTeamShowcase(ctx, keybase1.SetTeamShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: nil,
		Description: &desc,
	})
}

func (c *CmdTeamSettings) setOpenness(ctx context.Context, cli keybase1.TeamsClient, joinAsRole keybase1.TeamRole) error {
	dui := c.G().UI.GetTerminalUI()

	open := joinAsRole != keybase1.TeamRole_NONE

	arg := keybase1.TeamSetSettingsArg{
		Name: c.Team.String(),
		Settings: keybase1.TeamSettings{
			Open:   open,
			JoinAs: joinAsRole,
		},
	}

	err := cli.TeamSetSettings(ctx, arg)
	if err != nil {
		if e, ok := err.(libkb.NoOpError); ok {
			dui.Printf("%s\n", e.Desc)
			return nil
		}

		return err
	}

	if open {
		dui.Printf("Team set to open.\n")
	} else {
		dui.Printf("Team set to closed.\n")
	}
	return nil
}

func (c *CmdTeamSettings) setProfilePromote(ctx context.Context, cli keybase1.TeamsClient, promote bool) error {
	return cli.SetTeamMemberShowcase(ctx, keybase1.SetTeamMemberShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: promote,
	})
}

func (c *CmdTeamSettings) setAllowProfilePromote(ctx context.Context, cli keybase1.TeamsClient, allow bool) error {
	// awaiting CORE-6550
	return fmt.Errorf("The ability to allow non-admins to promote your team is coming soon!")
}

func (c *CmdTeamSettings) setShowcase(ctx context.Context, cli keybase1.TeamsClient, showcase bool) error {
	return cli.SetTeamShowcase(ctx, keybase1.SetTeamShowcaseArg{
		Name:        c.Team.String(),
		IsShowcased: &showcase,
		Description: nil,
	})
}

func (c *CmdTeamSettings) printCurrentSettings(ctx context.Context, cli keybase1.TeamsClient) error {
	details, err := cli.TeamGet(ctx, keybase1.TeamGetArg{Name: c.Team.String(), ForceRepoll: true})
	if err != nil {
		return err
	}

	var showcaseInfo *keybase1.TeamAndMemberShowcase
	tmp, err := cli.GetTeamAndMemberShowcase(ctx, c.Team.String())
	if err != nil {
		c.G().Log.CDebugf(ctx, "failed to get team showcase info: %v", err)
	} else {
		showcaseInfo = &tmp
	}

	dui := c.G().UI.GetTerminalUI()
	dui.Printf("Current settings for team %q:\n", c.Team.String())
	if showcaseInfo != nil && showcaseInfo.TeamShowcase.Description != nil {
		dui.Printf("  Description:     %v\n", *showcaseInfo.TeamShowcase.Description)
	}
	dui.Printf("  Open:            %v\n", c.tfToYn(details.Settings.Open,
		fmt.Sprintf("default membership = %v", strings.ToLower(details.Settings.JoinAs.String()))))
	if showcaseInfo != nil {
		dui.Printf("  Showcased:       %v\n", c.tfToYn(showcaseInfo.TeamShowcase.IsShowcased, "on keybase.io/popular-teams"))
		dui.Printf("  Promoted:        %v\n", c.tfToYn(showcaseInfo.IsMemberShowcased, "on your profile"))
		// CORE-6550: show whether non-admins are allowed to promote
	}

	return nil
}

func (c *CmdTeamSettings) tfToYn(x bool, parenthetical string) string {
	if x {
		if len(parenthetical) > 0 {
			return fmt.Sprintf("yes (%v)", parenthetical)
		}
		return "yes"
	}
	return "no"
}

func (c *CmdTeamSettings) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}

const teamSettingsDoc = `"keybase team settings" lets you edit settings for a team

EXAMPLES:
Review team settings:
    keybase team settings acme
Open a team so anyone can join as a reader:
    keybase team settings acme --open-team=reader
Showcase a team publicly:
    keybase team settings acme --showcase=yes
Promote a team on your profile:
    keybase team settings acme --profile-promote=yes
Set a description for the team to show if promoted:
    keybase team settings acme --description="Rocket-Powered Products"
Clear the team description:
    keybase team settings acme --description=""
`
<|endoftext|>


<commit_before>package weather

var (
	CurrentCondition string
	CurrentLocation  string
)

func Log(city, condition string) string {
	CurrentLocation, CurrentCondition = city, condition
	return CurrentLocation + " - current weather condition: " + CurrentCondition
}
<commit_msg>Solve weather<commit_after>// Package weather contains utilities for logging weath conditions
package weather

var (
	// CurrentCondition describes the current weather condition (e.g. "rainy")
	CurrentCondition string
	// CurrentLocation describes the current city (e.g. "New York")
	CurrentLocation string
)

// Log returns a string that describes the weath conditions in a particular city
func Log(city, condition string) string {
	CurrentLocation, CurrentCondition = city, condition
	return CurrentLocation + " - current weather condition: " + CurrentCondition
}
<|endoftext|>