<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"koding/db/models"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"sync"
	"time"
)

const IntegrationProxyURL = "/api/integration"

func sendAccount(account *models.Account, outputter *Outputter) {
	outputter.OnItem <- &Item{Name: "Account", Data: account}
}

func fetchSocial(userInfo *UserInfo, outputter *Outputter) {
	showExempt := "false"
	// show troll content if only user is admin or requester is marked as troll
	if userInfo.Account != nil &&
		(userInfo.Account.HasFlag(models.SUPER_ADMIN_FLAG) || userInfo.Account.IsExempt) {
		showExempt = "true"
	}

	var wg sync.WaitGroup
	urls := socialUrls(userInfo, "showExempt="+showExempt)

	onSocialItem := make(chan *Item, len(urls))

	for name, url := range urls {
		wg.Add(1)
		go func(name, url string) {
			defer wg.Done()

			item, err := fetchSocialItem(url, userInfo)
			if err != nil {
				Log.Error("Fetching prefetched socialdata item: %s, %v", name, err)

				onSocialItem <- &Item{Name: name, Data: nil}
				return
			}

			onSocialItem <- &Item{Name: name, Data: item}
		}(name, url)
	}

	wg.Wait()

	collectSocialItems(onSocialItem, outputter, len(urls))
}

func collectSocialItems(onItem <-chan *Item, outputter *Outputter, max int) {
	socialApiData := map[string]interface{}{}

	for i := 1; i <= max; i++ {
		socialItem := <-onItem

		if socialItem.Data != nil {
			socialApiData[socialItem.Name] = socialItem.Data
		}
	}

	outputter.OnItem <- &Item{Name: "SocialApiData", Data: socialApiData}
}

var timeout = time.Duration(1 * time.Second)

func dialTimeout(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, timeout)
}

func fetchSocialItem(fetchURL string, userInfo *UserInfo) (interface{}, error) {
	transport := http.Transport{Dial: dialTimeout}
	client := http.Client{Transport: &transport}
	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	u, err := url.Parse(conf.SocialApi.CustomDomain.Local)
	if err != nil {
		return nil, err
	}

	jar.SetCookies(u, userInfo.Cookies)
	client.Jar = jar

	resp, err := client.Get(fetchURL)
	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("Socialapi return non 200 status")
	}

	var data interface{}
	decoder := json.NewDecoder(resp.Body)

	err = decoder.Decode(&data)
	if err != nil {
		return nil, err
	}

	return data, nil
}

func socialUrls(userInfo *UserInfo, extras ...string) map[string]string {
	id := userInfo.SocialApiId
	groupQuery := fmt.Sprintf("groupName=%s", userInfo.Group.Slug)
	extras = append(extras, groupQuery)

	var urls = map[string]string{
		"followedChannels": buildUrl("%s/account/%[2]s/channels?accountId=%[2]s", id, extras...),
		"privateMessages":  buildUrl("%s/privatechannel/list?accountId=%s", id, extras...),
		"popularPosts":     buildUrl("%s/popular/posts/public?accountId=%s", id, extras...),
		"bot":              fmt.Sprintf("%s%s/botchannel", conf.SocialApi.CustomDomain.Local, IntegrationProxyURL),
		// "pinnedMessages":   buildUrl("%s/activity/pin/list?accountId=%s", id, extras...),
	}

	return urls
}

func buildUrl(path, socialApiId string, extras ...string) string {
	return fmt.Sprintf(path, conf.SocialApi.ProxyUrl, socialApiId) + "&" + strings.Join(extras, "&")
}
<commit_msg>gowebserver: deprecate Outputter usage<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"koding/db/models"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"sync"
	"time"
)

const IntegrationProxyURL = "/api/integration"

func fetchSocial(userInfo *UserInfo, user *LoggedInUser, wg *sync.WaitGroup) {
	defer wg.Done()
	// on new register, there's a race condition where SocialApiId
	// isn't sometimes set; in that case don't prefetch socialdata
	// since it'll return empty
	if userInfo.SocialApiId == "" {
		return
	}

	showExempt := "false"
	// show troll content if only user is admin or requester is marked as troll
	if userInfo.Account != nil &&
		(userInfo.Account.HasFlag(models.SUPER_ADMIN_FLAG) || userInfo.Account.IsExempt) {
		showExempt = "true"
	}

	var socialWg sync.WaitGroup
	urls := socialUrls(userInfo, "showExempt="+showExempt)

	onSocialItem := make(chan *Item, len(urls))

	for name, url := range urls {
		socialWg.Add(1)
		go func(name, url string) {
			defer socialWg.Done()

			item, err := fetchSocialItem(url, userInfo)
			if err != nil {
				Log.Error("Fetching prefetched socialdata item: %s, %v", name, err)
			}

			onSocialItem <- &Item{Name: name, Data: item}
		}(name, url)
	}

	socialWg.Wait()

	socialApiData := collectSocialItems(onSocialItem, len(urls))
	user.Set("SocialApiData", socialApiData)
}

func collectSocialItems(onItem <-chan *Item, max int) map[string]interface{} {
	socialApiData := map[string]interface{}{}

	for i := 1; i <= max; i++ {
		socialItem := <-onItem

		if socialItem.Data != nil {
			socialApiData[socialItem.Name] = socialItem.Data
		}
	}

	return socialApiData
}

var timeout = time.Duration(1 * time.Second)

func dialTimeout(network, addr string) (net.Conn, error) {
	return net.DialTimeout(network, addr, timeout)
}

func fetchSocialItem(fetchURL string, userInfo *UserInfo) (interface{}, error) {
	transport := http.Transport{Dial: dialTimeout}
	client := http.Client{Transport: &transport}
	jar, err := cookiejar.New(nil)
	if err != nil {
		return nil, err
	}

	u, err := url.Parse(conf.SocialApi.CustomDomain.Local)
	if err != nil {
		return nil, err
	}

	jar.SetCookies(u, userInfo.Cookies)
	client.Jar = jar

	resp, err := client.Get(fetchURL)
	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("Socialapi return non 200 status")
	}

	var data interface{}
	decoder := json.NewDecoder(resp.Body)

	err = decoder.Decode(&data)
	if err != nil {
		return nil, err
	}

	return data, nil
}

func socialUrls(userInfo *UserInfo, extras ...string) map[string]string {
	id := userInfo.SocialApiId
	groupQuery := fmt.Sprintf("groupName=%s", userInfo.Group.Slug)
	extras = append(extras, groupQuery)

	var urls = map[string]string{
		"followedChannels": buildUrl("%s/account/%[2]s/channels?accountId=%[2]s", id, extras...),
		"privateMessages":  buildUrl("%s/privatechannel/list?accountId=%s", id, extras...),
		"popularPosts":     buildUrl("%s/popular/posts/public?accountId=%s", id, extras...),
		"bot":              fmt.Sprintf("%s%s/botchannel", conf.SocialApi.CustomDomain.Local, IntegrationProxyURL),
		// "pinnedMessages":   buildUrl("%s/activity/pin/list?accountId=%s", id, extras...),
	}

	return urls
}

func buildUrl(path, socialApiId string, extras ...string) string {
	return fmt.Sprintf(path, conf.SocialApi.ProxyUrl, socialApiId) + "&" + strings.Join(extras, "&")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	kodingmodels "koding/db/models"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"

	"labix.org/v2/mgo/bson"

	"github.com/koding/bongo"
	"github.com/koding/logging"
)

func setDefaults(log logging.Logger) {
	group, err := modelhelper.GetGroup(models.Channel_KODING_NAME)
	if err != nil {
		log.Error("err while fetching koding group: %s", err.Error())
		return
	}

	log.Debug("mongo group found")

	setPublicChannel(log, group)
	setSetChangeLogChannel(log, group)
	log.Info("socialApi defaults are created")
}

func setPublicChannel(log logging.Logger, group *kodingmodels.Group) {
	c := models.NewChannel()
	selector := map[string]interface{}{
		"type_constant": models.Channel_TYPE_GROUP,
		"group_name":    models.Channel_KODING_NAME,
	}

	err := c.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		log.Error("err while fetching koding channel:", err.Error())
		return
	}

	if err == bongo.RecordNotFound {
		log.Debug("postgres group couldn't found, creating it")

		acc, err := createChannelOwner(group)
		if err != nil {
			log.Error(err.Error())
			return
		}

		c.Name = "public"
		c.CreatorId = acc.Id
		c.GroupName = models.Channel_KODING_NAME
		c.TypeConstant = models.Channel_TYPE_GROUP
		c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
		if err := c.Create(); err != nil {
			log.Error("err while creating the koding channel: %s", err.Error())
			return
		}
	}

	socialApiId := strconv.FormatInt(c.Id, 10)
	if group.SocialApiChannelId == socialApiId {
		log.Error("mongo and postgres socialApiChannelId ids are same")
		return
	}

	log.Debug("mongo and postgres socialApiChannelId ids are different, fixing it")
	if err := updateGroupPartially(group.Id, "socialApiChannelId", socialApiId); err != nil {
		log.Error("err while udpating socialApiChannelId: %s", err.Error())
		return
	}
}

func setSetChangeLogChannel(log logging.Logger, group *kodingmodels.Group) {

	c := models.NewChannel()
	selector := map[string]interface{}{
		"type_constant": models.Channel_TYPE_ANNOUNCEMENT,
		"group_name":    models.Channel_KODING_NAME,
	}

	// if err is nil
	// it means we already have that channel
	err := c.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		log.Error("err while fetching changelog channel:", err.Error())
		return
	}

	if err == bongo.RecordNotFound {
		log.Error("postgres changelog couldn't found, creating it")

		acc, err := createChannelOwner(group)
		if err != nil {
			log.Error(err.Error())
			return
		}

		c.Name = "changelog"
		c.CreatorId = acc.Id
		c.GroupName = models.Channel_KODING_NAME
		c.TypeConstant = models.Channel_TYPE_ANNOUNCEMENT
		c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE
		if err := c.Create(); err != nil {
			log.Error("err while creating the koding channel:", err.Error())
			return
		}
	}

	socialApiAnnouncementChannelId := strconv.FormatInt(c.Id, 10)
	if group.SocialApiAnnouncementChannelId == socialApiAnnouncementChannelId {
		log.Error("mongo and postgres socialApiAnnouncementChannel ids are same")
		return
	}

	log.Debug("mongo and postgres socialApiAnnouncementChannel ids are different, fixing it")
	if err := updateGroupPartially(group.Id, "socialApiAnnouncementChannelId", strconv.FormatInt(c.Id, 10)); err != nil {
		log.Error("err while udpating socialApiAnnouncementChannelId:", err.Error())
		return
	}
}

func createChannelOwner(group *kodingmodels.Group) (*models.Account, error) {
	owner, err := modelhelper.GetGroupOwner(group)
	if err != nil {
		return nil, fmt.Errorf("err while fetching koding owner: %s", err.Error())
	}

	acc := models.NewAccount()
	acc.OldId = owner.Id.Hex()
	acc.Nick = owner.Profile.Nickname
	if err := acc.FetchOrCreate(); err != nil {
		return nil, fmt.Errorf("err while fetching owner from postgres: %s", err.Error())
	}

	return acc, nil
}

func updateGroupPartially(groupId bson.ObjectId, property string, value string) error {
	return modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": groupId},
		modelhelper.Selector{
			"$set": modelhelper.Selector{
				property: value,
			},
		},
	)
}
<commit_msg>Social: Error to Info<commit_after>package main

import (
	"fmt"
	kodingmodels "koding/db/models"
	"koding/db/mongodb/modelhelper"
	"socialapi/models"
	"strconv"

	"labix.org/v2/mgo/bson"

	"github.com/koding/bongo"
	"github.com/koding/logging"
)

func setDefaults(log logging.Logger) {
	group, err := modelhelper.GetGroup(models.Channel_KODING_NAME)
	if err != nil {
		log.Error("err while fetching koding group: %s", err.Error())
		return
	}

	log.Debug("mongo group found")

	setPublicChannel(log, group)
	setSetChangeLogChannel(log, group)
	log.Info("socialApi defaults are created")
}

func setPublicChannel(log logging.Logger, group *kodingmodels.Group) {
	c := models.NewChannel()
	selector := map[string]interface{}{
		"type_constant": models.Channel_TYPE_GROUP,
		"group_name":    models.Channel_KODING_NAME,
	}

	err := c.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		log.Error("err while fetching koding channel:", err.Error())
		return
	}

	if err == bongo.RecordNotFound {
		log.Debug("postgres group couldn't found, creating it")

		acc, err := createChannelOwner(group)
		if err != nil {
			log.Error(err.Error())
			return
		}

		c.Name = "public"
		c.CreatorId = acc.Id
		c.GroupName = models.Channel_KODING_NAME
		c.TypeConstant = models.Channel_TYPE_GROUP
		c.PrivacyConstant = models.Channel_PRIVACY_PUBLIC
		if err := c.Create(); err != nil {
			log.Error("err while creating the koding channel: %s", err.Error())
			return
		}
	}

	socialApiId := strconv.FormatInt(c.Id, 10)
	if group.SocialApiChannelId == socialApiId {
		log.Error("mongo and postgres socialApiChannelId ids are same")
		return
	}

	log.Debug("mongo and postgres socialApiChannelId ids are different, fixing it")
	if err := updateGroupPartially(group.Id, "socialApiChannelId", socialApiId); err != nil {
		log.Error("err while udpating socialApiChannelId: %s", err.Error())
		return
	}
}

func setSetChangeLogChannel(log logging.Logger, group *kodingmodels.Group) {

	c := models.NewChannel()
	selector := map[string]interface{}{
		"type_constant": models.Channel_TYPE_ANNOUNCEMENT,
		"group_name":    models.Channel_KODING_NAME,
	}

	// if err is nil
	// it means we already have that channel
	err := c.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		log.Error("err while fetching changelog channel:", err.Error())
		return
	}

	if err == bongo.RecordNotFound {
		log.Error("postgres changelog couldn't found, creating it")

		acc, err := createChannelOwner(group)
		if err != nil {
			log.Error(err.Error())
			return
		}

		c.Name = "changelog"
		c.CreatorId = acc.Id
		c.GroupName = models.Channel_KODING_NAME
		c.TypeConstant = models.Channel_TYPE_ANNOUNCEMENT
		c.PrivacyConstant = models.Channel_PRIVACY_PRIVATE
		if err := c.Create(); err != nil {
			log.Error("err while creating the koding channel:", err.Error())
			return
		}
	}

	socialApiAnnouncementChannelId := strconv.FormatInt(c.Id, 10)
	if group.SocialApiAnnouncementChannelId == socialApiAnnouncementChannelId {
		log.Info("mongo and postgres socialApiAnnouncementChannel ids are same")
		return
	}

	log.Debug("mongo and postgres socialApiAnnouncementChannel ids are different, fixing it")
	if err := updateGroupPartially(group.Id, "socialApiAnnouncementChannelId", strconv.FormatInt(c.Id, 10)); err != nil {
		log.Error("err while udpating socialApiAnnouncementChannelId:", err.Error())
		return
	}
}

func createChannelOwner(group *kodingmodels.Group) (*models.Account, error) {
	owner, err := modelhelper.GetGroupOwner(group)
	if err != nil {
		return nil, fmt.Errorf("err while fetching koding owner: %s", err.Error())
	}

	acc := models.NewAccount()
	acc.OldId = owner.Id.Hex()
	acc.Nick = owner.Profile.Nickname
	if err := acc.FetchOrCreate(); err != nil {
		return nil, fmt.Errorf("err while fetching owner from postgres: %s", err.Error())
	}

	return acc, nil
}

func updateGroupPartially(groupId bson.ObjectId, property string, value string) error {
	return modelhelper.UpdateGroupPartial(
		modelhelper.Selector{"_id": groupId},
		modelhelper.Selector{
			"$set": modelhelper.Selector{
				property: value,
			},
		},
	)
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/agext/levenshtein"
	"reflect"
	"sort"
	"strings"
	"unicode"
)

// UnexpectedKey represents result of validation
type UnexpectedKey struct {
	Name        string
	SuggestName string
}

func normalizeKeyname(f reflect.StructField) string {
	name := string(unicode.ToLower([]rune(f.Name)[0])) + f.Name[1:]
	if s := f.Tag.Get("toml"); s != "" {
		name = s
	}
	return name
}

func addKeynametoCandidates(f reflect.StructField, candidates *[]string) *[]string {
	name := normalizeKeyname(f)
	*candidates = append(*candidates, name)
	return candidates
}

func makeCandidates(t reflect.Type) []string {
	if t.Kind() == reflect.Map || t.Kind() == reflect.Ptr {
		return makeCandidates(t.Elem())
	}
	var candidates []string
	fields := reflect.VisibleFields(t)
	for _, f := range fields {
		if s := f.Tag.Get("conf"); s == "ignore" {
			continue
		}
		if s := f.Tag.Get("conf"); s == "parent" {
			addKeynametoCandidates(f, &candidates)
			childCandidates := makeCandidates(f.Type)
			candidates = append(candidates, childCandidates...)
			continue
		}
		addKeynametoCandidates(f, &candidates)
	}

	return candidates
}

func nameSuggestion(given string, candidates []string) string {
	for _, candidate := range candidates {
		dist := levenshtein.Distance(given, candidate, nil)
		if dist < 3 {
			return candidate
		}
	}
	return ""
}

// ValidateConfigFile detect unexpected key in configfile
func ValidateConfigFile(file string) ([]UnexpectedKey, error) {
	config := &Config{}
	md, err := toml.DecodeFile(file, config)
	if err != nil {
		return nil, fmt.Errorf("failed to test config: %s", err)
	}

	var c Config
	candidates := makeCandidates(reflect.TypeOf(c))

	var parentKeys []string
	configFields := reflect.VisibleFields(reflect.TypeOf(c))
	for _, f := range configFields {
		if s := f.Tag.Get("conf"); s == "parent" {
			parentKeys = append(parentKeys, normalizeKeyname(f))
		}
	}

	var unexpectedKeys []UnexpectedKey
	for _, v := range md.Undecoded() {
		splitedKey := strings.Split(v.String(), ".")
		key := splitedKey[0]
		if containKeyName(parentKeys, key) {
			/*
					if conffile is following, UnexpectedKey.SuggestName should be `filesystems.use_mountpoint`, not `filesystems`
					```
					[filesystems]
				  use_mntpoint = true
					```
			*/
			suggestName := nameSuggestion(splitedKey[len(splitedKey)-1], candidates)
			if suggestName == "" {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					v.String(),
					"",
				})
			} else {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					v.String(),
					strings.Join(splitedKey[:len(splitedKey)-1], ".") + "." + suggestName,
				})
			}
		} else {
			// don't accept duplicate unexpectedKey
			if !containKey(unexpectedKeys, key) {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					key,
					nameSuggestion(key, candidates),
				})
			}
		}
	}

	for k1, v := range config.Plugin {
		/*
			detect [plugin.<unexpected>.<???>]
			default suggestion of [plugin.<unexpected>.<???>] is plugin.metrics.<???>
			don't have to detect critical syntax error about plugin here because error should have occured while loading config
			```
			[plugin.metrics.correct]
			```
			-> A configuration value of `command` should be string or string slice, but <nil>
			```
			[plugin.metrics]
			command = "test command"
			```
			-> type mismatch for config.PluginConfig: expected table but found string
		*/
		if k1 != "metrics" && k1 != "checks" && k1 != "metadata" {
			suggestName := nameSuggestion(k1, []string{"metrics", "checks", "metadata"})
			for k2 := range v {
				if suggestName == "" {
					unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
						fmt.Sprintf("plugin.%s.%s", k1, k2),
						fmt.Sprintf("plugin.metrics.%s", k2),
					})
				} else {
					unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
						fmt.Sprintf("plugin.%s.%s", k1, k2),
						fmt.Sprintf("plugin.%s.%s", suggestName, k2),
					})
				}
			}
		}
	}

	sort.Slice(unexpectedKeys, func(i, j int) bool {
		return unexpectedKeys[i].Name < unexpectedKeys[j].Name
	})

	return unexpectedKeys, nil
}

func containKey(target []UnexpectedKey, want string) bool {
	for _, v := range target {
		if v.Name == want {
			return true
		}
	}
	return false
}

func containKeyName(target []string, want string) bool {
	for _, v := range target {
		if v == want {
			return true
		}
	}
	return false
}
<commit_msg>addKeynametoCandidates -> addKeynameToCandidates<commit_after>package config

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/agext/levenshtein"
	"reflect"
	"sort"
	"strings"
	"unicode"
)

// UnexpectedKey represents result of validation
type UnexpectedKey struct {
	Name        string
	SuggestName string
}

func normalizeKeyname(f reflect.StructField) string {
	name := string(unicode.ToLower([]rune(f.Name)[0])) + f.Name[1:]
	if s := f.Tag.Get("toml"); s != "" {
		name = s
	}
	return name
}

func addKeynameToCandidates(f reflect.StructField, candidates *[]string) *[]string {
	name := normalizeKeyname(f)
	*candidates = append(*candidates, name)
	return candidates
}

func makeCandidates(t reflect.Type) []string {
	if t.Kind() == reflect.Map || t.Kind() == reflect.Ptr {
		return makeCandidates(t.Elem())
	}
	var candidates []string
	fields := reflect.VisibleFields(t)
	for _, f := range fields {
		if s := f.Tag.Get("conf"); s == "ignore" {
			continue
		}
		if s := f.Tag.Get("conf"); s == "parent" {
			addKeynameToCandidates(f, &candidates)
			childCandidates := makeCandidates(f.Type)
			candidates = append(candidates, childCandidates...)
			continue
		}
		addKeynameToCandidates(f, &candidates)
	}

	return candidates
}

func nameSuggestion(given string, candidates []string) string {
	for _, candidate := range candidates {
		dist := levenshtein.Distance(given, candidate, nil)
		if dist < 3 {
			return candidate
		}
	}
	return ""
}

// ValidateConfigFile detect unexpected key in configfile
func ValidateConfigFile(file string) ([]UnexpectedKey, error) {
	config := &Config{}
	md, err := toml.DecodeFile(file, config)
	if err != nil {
		return nil, fmt.Errorf("failed to test config: %s", err)
	}

	var c Config
	candidates := makeCandidates(reflect.TypeOf(c))

	var parentKeys []string
	configFields := reflect.VisibleFields(reflect.TypeOf(c))
	for _, f := range configFields {
		if s := f.Tag.Get("conf"); s == "parent" {
			parentKeys = append(parentKeys, normalizeKeyname(f))
		}
	}

	var unexpectedKeys []UnexpectedKey
	for _, v := range md.Undecoded() {
		splitedKey := strings.Split(v.String(), ".")
		key := splitedKey[0]
		if containKeyName(parentKeys, key) {
			/*
					if conffile is following, UnexpectedKey.SuggestName should be `filesystems.use_mountpoint`, not `filesystems`
					```
					[filesystems]
				  use_mntpoint = true
					```
			*/
			suggestName := nameSuggestion(splitedKey[len(splitedKey)-1], candidates)
			if suggestName == "" {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					v.String(),
					"",
				})
			} else {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					v.String(),
					strings.Join(splitedKey[:len(splitedKey)-1], ".") + "." + suggestName,
				})
			}
		} else {
			// don't accept duplicate unexpectedKey
			if !containKey(unexpectedKeys, key) {
				unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
					key,
					nameSuggestion(key, candidates),
				})
			}
		}
	}

	for k1, v := range config.Plugin {
		/*
			detect [plugin.<unexpected>.<???>]
			default suggestion of [plugin.<unexpected>.<???>] is plugin.metrics.<???>
			don't have to detect critical syntax error about plugin here because error should have occured while loading config
			```
			[plugin.metrics.correct]
			```
			-> A configuration value of `command` should be string or string slice, but <nil>
			```
			[plugin.metrics]
			command = "test command"
			```
			-> type mismatch for config.PluginConfig: expected table but found string
		*/
		if k1 != "metrics" && k1 != "checks" && k1 != "metadata" {
			suggestName := nameSuggestion(k1, []string{"metrics", "checks", "metadata"})
			for k2 := range v {
				if suggestName == "" {
					unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
						fmt.Sprintf("plugin.%s.%s", k1, k2),
						fmt.Sprintf("plugin.metrics.%s", k2),
					})
				} else {
					unexpectedKeys = append(unexpectedKeys, UnexpectedKey{
						fmt.Sprintf("plugin.%s.%s", k1, k2),
						fmt.Sprintf("plugin.%s.%s", suggestName, k2),
					})
				}
			}
		}
	}

	sort.Slice(unexpectedKeys, func(i, j int) bool {
		return unexpectedKeys[i].Name < unexpectedKeys[j].Name
	})

	return unexpectedKeys, nil
}

func containKey(target []UnexpectedKey, want string) bool {
	for _, v := range target {
		if v.Name == want {
			return true
		}
	}
	return false
}

func containKeyName(target []string, want string) bool {
	for _, v := range target {
		if v == want {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sources

import (
	"fmt"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/heapster/sources/api"
	"github.com/GoogleCloudPlatform/heapster/sources/datasource"
	"github.com/GoogleCloudPlatform/heapster/sources/nodes"
	"github.com/golang/glog"
)

type kubeNodeMetrics struct {
	kubeletApi  datasource.Kubelet
	kubeletPort int
	nodesApi    nodes.NodesApi
}

func NewKubeNodeMetrics(kubeletPort int, kubeletApi datasource.Kubelet, nodesApi nodes.NodesApi) api.Source {
	return &kubeNodeMetrics{
		kubeletApi:  kubeletApi,
		kubeletPort: kubeletPort,
		nodesApi:    nodesApi,
	}
}

func (self *kubeNodeMetrics) updateStats(host nodes.Host, info nodes.Info, start, end time.Time, resolution time.Duration) (api.Container, error) {
	container, err := self.kubeletApi.GetContainer(datasource.Host{IP: info.InternalIP, Port: self.kubeletPort, Resource: "stats/"}, start, end, resolution)
	if err != nil {
		glog.V(1).Infof("Failed to get machine stats from kubelet for node %s", host)
		return api.Container{}, err
	}
	if container == nil {
		// no stats found.
		glog.V(1).Infof("no machine stats from kubelet on node %s", host)
		return api.Container{}, fmt.Errorf("no machine stats from kubelet on node %s", host)
	}
	container.Hostname = string(host)
	return *container, nil
}

func (self *kubeNodeMetrics) getNodesInfo(nodeList *nodes.NodeList, start, end time.Time, resolution time.Duration) ([]api.Container, error) {
	var (
		lock sync.Mutex
		wg   sync.WaitGroup
	)
	nodesInfo := []api.Container{}
	for host, info := range nodeList.Items {
		wg.Add(1)
		go func(host nodes.Host, info nodes.Info) {
			defer wg.Done()
			if container, err := self.updateStats(host, info, start, end, resolution); err == nil {
				lock.Lock()
				defer lock.Unlock()
				nodesInfo = append(nodesInfo, container)
			}
		}(host, info)
	}
	wg.Wait()

	return nodesInfo, nil
}

func (self *kubeNodeMetrics) GetInfo(start, end time.Time, resolution time.Duration) (api.AggregateData, error) {
	kubeNodes, err := self.nodesApi.List()
	if err != nil || len(kubeNodes.Items) == 0 {
		return api.AggregateData{}, err
	}
	glog.V(3).Info("Fetched list of nodes from the master")
	nodesInfo, err := self.getNodesInfo(kubeNodes, start, end, resolution)
	if err != nil {
		return api.AggregateData{}, err
	}

	return api.AggregateData{Machine: nodesInfo}, nil
}

func (self *kubeNodeMetrics) DebugInfo() string {
	desc := "Source type: Kube Node Metrics\n"
	desc += self.nodesApi.DebugInfo() + "\n"

	return desc
}
<commit_msg>Export non-Kubernetes container metrics.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sources

import (
	"fmt"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/heapster/sources/api"
	"github.com/GoogleCloudPlatform/heapster/sources/datasource"
	"github.com/GoogleCloudPlatform/heapster/sources/nodes"
	"github.com/golang/glog"
)

type kubeNodeMetrics struct {
	kubeletApi  datasource.Kubelet
	kubeletPort int
	nodesApi    nodes.NodesApi
}

func NewKubeNodeMetrics(kubeletPort int, kubeletApi datasource.Kubelet, nodesApi nodes.NodesApi) api.Source {
	return &kubeNodeMetrics{
		kubeletApi:  kubeletApi,
		kubeletPort: kubeletPort,
		nodesApi:    nodesApi,
	}
}

// Returns the host container, non-Kubernetes containers, and an error (if any).
func (self *kubeNodeMetrics) updateStats(host nodes.Host, info nodes.Info, start, end time.Time, resolution time.Duration) (*api.Container, []api.Container, error) {
	// Get information for all containers.
	containers, err := self.kubeletApi.GetAllRawContainers(datasource.Host{IP: info.InternalIP, Port: self.kubeletPort}, start, end, resolution)
	if err != nil {
		glog.V(1).Infof("Failed to get container stats from kubelet for node %q", host)
		return nil, []api.Container{}, fmt.Errorf("failed to get container stats from Kubeler node %q: %v", host, err)
	}
	if len(containers) == 0 {
		// no stats found.
		glog.V(1).Infof("No container stats from Kubelet on node %q", host)
		return nil, []api.Container{}, fmt.Errorf("no container stats from Kubelet on node %q", host)
	}

	// Find host container.
	hostIndex := -1
	hostString := string(host)
	for i := range containers {
		if containers[i].Name == "/" {
			hostIndex = i
		}
		containers[i].Hostname = hostString
	}
	var hostContainer *api.Container = nil
	if hostIndex >= 0 {
		hostCopy := containers[hostIndex]
		hostContainer = &hostCopy
		containers = append(containers[:hostIndex], containers[hostIndex+1:]...)
	}
	return hostContainer, containers, nil
}

// Returns the host containers, non-Kubernetes containers, and an error (if any).
func (self *kubeNodeMetrics) getNodesInfo(nodeList *nodes.NodeList, start, end time.Time, resolution time.Duration) ([]api.Container, []api.Container, error) {
	var (
		lock sync.Mutex
		wg   sync.WaitGroup
	)
	hostContainers := make([]api.Container, 0, len(nodeList.Items))
	rawContainers := make([]api.Container, 0, len(nodeList.Items))
	for host, info := range nodeList.Items {
		wg.Add(1)
		go func(host nodes.Host, info nodes.Info) {
			defer wg.Done()
			if hostContainer, containers, err := self.updateStats(host, info, start, end, resolution); err == nil {
				lock.Lock()
				defer lock.Unlock()
				if hostContainers != nil {
					hostContainers = append(hostContainers, *hostContainer)
				}
				rawContainers = append(rawContainers, containers...)
			}
		}(host, info)
	}
	wg.Wait()

	return hostContainers, rawContainers, nil
}

func (self *kubeNodeMetrics) GetInfo(start, end time.Time, resolution time.Duration) (api.AggregateData, error) {
	kubeNodes, err := self.nodesApi.List()
	if err != nil || len(kubeNodes.Items) == 0 {
		return api.AggregateData{}, err
	}
	glog.V(3).Info("Fetched list of nodes from the master")
	hostContainers, rawContainers, err := self.getNodesInfo(kubeNodes, start, end, resolution)
	if err != nil {
		return api.AggregateData{}, err
	}

	return api.AggregateData{
		Machine:    hostContainers,
		Containers: rawContainers,
	}, nil
}

func (self *kubeNodeMetrics) DebugInfo() string {
	desc := "Source type: Kube Node Metrics\n"
	desc += self.nodesApi.DebugInfo() + "\n"

	return desc
}
<|endoftext|>


<commit_before>package retry

import (
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestDefaultNew(t *testing.T) {
	t.Parallel()

	r := New()
	if r.Tries != MaxRetries {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, MaxRetries)
	}

	if r.Tries != MaxRetries {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, MaxRetries)
	}

	err := r.Do(happy)
	if err != nil {
		t.Errorf("should have succeeded without an error, got %v. Retryer state %#v", err, r)
	}

	err = r.Do(sad)
	if err == nil {
		t.Errorf("should have failed with an error. Retryer state %#v", r)
	}
}

func TestInfNumberOfTries(t *testing.T) {
	t.Parallel()

	r := New(Tries(0))
	if r.Tries != 0 {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, 0)
	}

	for i := 0; i <= 1000; i++ {
		err := r.Do(sad)
		if err == nil {
			t.Errorf("should have failed with an error")
		}
	}
}

func TestSleep(t *testing.T) {
	t.Parallel()

	// using a ever failing function
	tcs := []struct {
		sleep int
		tries int
		wait  int
	}{
		{
			sleep: 50,
			tries: 1,
			wait:  100,
		},
		{
			sleep: 50,
			tries: 5,
			wait:  100,
		},
	}

	for i, tc := range tcs {
		r := New(Sleep(tc.sleep), Tries(tc.tries))
		ch := make(chan error)

		start := time.Now()
		go func() {
			ch <- r.Do(sad)
		}()

		select {
		case <-time.After(time.Duration((tc.sleep*tc.tries)+tc.wait) * time.Millisecond):
			t.Errorf("tc %d: should have slept only for %v took too long", i, time.Duration((tc.sleep*tc.tries)+tc.wait)*time.Millisecond)
		case err := <-ch:
			// have we finished sooner?
			if d := time.Since(start); d < time.Duration(tc.sleep*tc.tries)*time.Millisecond {
				t.Errorf("tc %d: retryer didn't sleep for the desired time, ended after %v", i, d)
			}
			if err == nil {
				t.Errorf("tc %d: should have failed with an error, Retryer state %#v", i, r)
			}
		}
	}
}

func TestSleepFn(t *testing.T) {
	t.Parallel()

	// we want to sleep for 100+200+300 ms = 600 ms -> linearly growing backoff
	sleepFn := func(attempts int) {
		sleep := time.Duration(100*attempts) * time.Millisecond
		time.Sleep(sleep)
	}

	r := New(SleepFn(sleepFn), Tries(3))
	ch := make(chan error)
	start := time.Now()
	go func() {
		ch <- r.Do(sad)
	}()

	expectedSleepDur := time.Duration(600 * time.Millisecond)
	select {
	case <-time.After(expectedSleepDur + 100*time.Millisecond):
		t.Errorf("should have slept only for %v took too long", expectedSleepDur)
	case err := <-ch:
		// have we finished sooner?
		if d := time.Since(start); d < expectedSleepDur {
			t.Errorf("retryer didn't sleep for the desired time, ended after %v", d)
		}
		if err == nil {
			t.Errorf("should have failed with an error, Retryer state %#v", r)
		}
	}
}

func TestPanicRecoveryEnabled(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("retryer with default panic recovery option - Recover - shouldn't have panicked: %v", r)
		}
	}()

	// testing retryer with panic recovery
	err := New(Recover()).Do(panicked)
	if err == nil {
		t.Error("expected an error containing panic stacktracke")
	}
	if !strings.HasPrefix(err.Error(), "retryer has recovered panic: explicit trigger of panic goroutine") ||
		!strings.Contains(err.Error(), "stack") {
		t.Errorf("unexpected error returned from panic recovery %v", err)
	}
}

func TestPanicRecoveryDisabled(t *testing.T) {
	t.Parallel()

	defer func() {
		if r := recover(); r == nil {
			t.Errorf("retryer should have panicked with default settings")
		}
	}()

	New().Do(panicked)
}
func TestEnsureFn(t *testing.T) {
	t.Parallel()

	touched := false
	toggler := func(error) { touched = true }

	r := New(Ensure(toggler))
	err := r.Do(sad)
	if err == nil {
		t.Errorf("should have succeeded without an error, got %v. Retryer state %#v", err, r)
	}
	if !touched {
		t.Error("ensure function wasn't called")
	}
}

func TestErrorFnOn(t *testing.T) {
	t.Parallel()

	// errorTypeC is not amongst the ones to retry on, on 5th attempt we expect to succeed
	fn := func() error { return &errorTypeC{S: "error c triggered"} }
	ab := attemptsBased{
		succeedOnNth: 5,
		fn:           fn,
	}

	r := New(Tries(6), On([]error{&errorTypeA{}, &errorTypeB{}}))
	err := r.Do(ab.run)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if r.attempts != 6 {
		t.Errorf("incorrect attempts count, got %d want 5", r.attempts)

	}

	// errorTypeC is not amongst the ones to retry on, we try only once
	fn = func() error { return &errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), On([]error{&errorTypeA{}, &errorTypeC{}})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}

	// errorTypeC is not amongst the ones to retry on, as we supply empty slice of errors, we try only once
	fn = func() error { return &errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), On([]error{})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}
}

func TestErrorFnNot(t *testing.T) {
	t.Parallel()

	// errorTypeC is amongst the ones to ignore in the not slice on, we expect a success after 1st run
	errMsg := "error c triggered"
	fn := func() error { return &errorTypeC{S: errMsg} }

	r := New(Tries(3), Not([]error{&errorTypeA{}, &errorTypeC{}}))
	err := r.Do(fn)
	if err == nil {
		t.Errorf("unexpected error: %v", err)
	}
	if r.attempts != 1 {
		t.Errorf("incorrect attempts count, got %d want 1", r.attempts)
	}

	// errorTypeC is not amongst the ones to retry on, we try only once
	fn = func() error { return &errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), Not([]error{&errorTypeA{}, &errorTypeB{}})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}
	if !strings.Contains(err.Error(), errMsg) {
		t.Errorf("unexpected error returned, got: type:%v msg:'%v',  want to contain: type:%v msg:'%v'", reflect.TypeOf(err), err.Error(), "errorTypeC", errMsg)
	}
}

func TestAfterEachFail(t *testing.T) {
	t.Parallel()

	ch := make(chan error)
	fn := func(err error) { ch <- err }

	bodyFn := func() error { return errors.New("generic error") }
	ab := attemptsBased{
		succeedOnNth: 5,
		fn:           bodyFn,
	}

	go func() {
		New(AfterEachFail(fn), Tries(5)).Do(ab.run)
	}()

	for i := 0; i < 5; i++ {
		select {
		case <-ch:
			continue
		case <-time.After(50 * time.Millisecond):
			t.Fatal("didn't receive an error from the fail callback function")
		}
	}
}

func TestCombinedOptions(t *testing.T) {
	t.Parallel()

	ab := attemptsBased{
		succeedOnNth: 3,
		fn:           sad,
	}
	touched := false
	toggler := func(error) { touched = true }

	r := New(Tries(5), Sleep(10), Ensure(toggler))
	err := r.Do(ab.run)
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if !touched {
		t.Error("ensure function wasn't called")
	}
}

func TestSleepFnPriorityOverSleep(t *testing.T) {
	t.Parallel()

	// we want to sleep for 100+200+300 ms = 600 ms -> linearly growing backoff
	sleepFn := func(attempts int) {
		sleep := time.Duration(100*attempts) * time.Millisecond
		time.Sleep(sleep)
	}

	// the Sleep(1000) won't be used, if yes, it will be caught by the timers below
	r := New(SleepFn(sleepFn), Sleep(1000), Tries(3))
	ch := make(chan error)
	start := time.Now()
	go func() {
		ch <- r.Do(sad)
	}()

	expectedSleepDur := time.Duration(600 * time.Millisecond)
	select {
	case <-time.After(expectedSleepDur + 100*time.Millisecond):
		t.Errorf("should have slept only for %v took too long", expectedSleepDur)
	case err := <-ch:
		// have we finished sooner?
		if d := time.Since(start); d < expectedSleepDur {
			t.Errorf("retryer didn't sleep for the desired time, ended after %v", d)
		}
		if err == nil {
			t.Errorf("should have failed with an error, Retryer state %#v", r)
		}
	}
}

type errorTypeA struct {
	s string
}

func (e *errorTypeA) Error() string {
	return e.s
}

type errorTypeB struct {
	s string
}

func (e *errorTypeB) Error() string {
	return e.s
}

type errorTypeC struct {
	S string
}

func (e *errorTypeC) Error() string {
	return e.S
}

func happy() error {
	_ = 2 + 3
	return nil
}

func sad() error {
	_ = 2 + 3
	return errors.New("error on primitive addition")
}

func panicked() error {
	panic("explicit trigger of panic")
}

type attemptsBased struct {
	attempts     int
	succeedOnNth int
	fn           func() error
}

func (ab *attemptsBased) run() error {
	if ab.attempts == ab.succeedOnNth {
		return nil
	}

	ab.attempts++
	return ab.fn()
}
<commit_msg>Simplify tests by removing pointer based error values<commit_after>package retry

import (
	"errors"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestDefaultNew(t *testing.T) {
	t.Parallel()

	r := New()
	if r.Tries != MaxRetries {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, MaxRetries)
	}

	if r.Tries != MaxRetries {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, MaxRetries)
	}

	err := r.Do(happy)
	if err != nil {
		t.Errorf("should have succeeded without an error, got %v. Retryer state %#v", err, r)
	}

	err = r.Do(sad)
	if err == nil {
		t.Errorf("should have failed with an error. Retryer state %#v", r)
	}
}

func TestInfNumberOfTries(t *testing.T) {
	t.Parallel()

	r := New(Tries(0))
	if r.Tries != 0 {
		t.Fatalf("bad tries config, got %d want %d", r.Tries, 0)
	}

	for i := 0; i <= 1000; i++ {
		err := r.Do(sad)
		if err == nil {
			t.Errorf("should have failed with an error")
		}
	}
}

func TestSleep(t *testing.T) {
	t.Parallel()

	// using a ever failing function
	tcs := []struct {
		sleep int
		tries int
		wait  int
	}{
		{
			sleep: 50,
			tries: 1,
			wait:  100,
		},
		{
			sleep: 50,
			tries: 5,
			wait:  100,
		},
	}

	for i, tc := range tcs {
		r := New(Sleep(tc.sleep), Tries(tc.tries))
		ch := make(chan error)

		start := time.Now()
		go func() {
			ch <- r.Do(sad)
		}()

		select {
		case <-time.After(time.Duration((tc.sleep*tc.tries)+tc.wait) * time.Millisecond):
			t.Errorf("tc %d: should have slept only for %v took too long", i, time.Duration((tc.sleep*tc.tries)+tc.wait)*time.Millisecond)
		case err := <-ch:
			// have we finished sooner?
			if d := time.Since(start); d < time.Duration(tc.sleep*tc.tries)*time.Millisecond {
				t.Errorf("tc %d: retryer didn't sleep for the desired time, ended after %v", i, d)
			}
			if err == nil {
				t.Errorf("tc %d: should have failed with an error, Retryer state %#v", i, r)
			}
		}
	}
}

func TestSleepFn(t *testing.T) {
	t.Parallel()

	// we want to sleep for 100+200+300 ms = 600 ms -> linearly growing backoff
	sleepFn := func(attempts int) {
		sleep := time.Duration(100*attempts) * time.Millisecond
		time.Sleep(sleep)
	}

	r := New(SleepFn(sleepFn), Tries(3))
	ch := make(chan error)
	start := time.Now()
	go func() {
		ch <- r.Do(sad)
	}()

	expectedSleepDur := time.Duration(600 * time.Millisecond)
	select {
	case <-time.After(expectedSleepDur + 100*time.Millisecond):
		t.Errorf("should have slept only for %v took too long", expectedSleepDur)
	case err := <-ch:
		// have we finished sooner?
		if d := time.Since(start); d < expectedSleepDur {
			t.Errorf("retryer didn't sleep for the desired time, ended after %v", d)
		}
		if err == nil {
			t.Errorf("should have failed with an error, Retryer state %#v", r)
		}
	}
}

func TestPanicRecoveryEnabled(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Errorf("retryer with default panic recovery option - Recover - shouldn't have panicked: %v", r)
		}
	}()

	// testing retryer with panic recovery
	err := New(Recover()).Do(panicked)
	if err == nil {
		t.Error("expected an error containing panic stacktracke")
	}
	if !strings.HasPrefix(err.Error(), "retryer has recovered panic: explicit trigger of panic goroutine") ||
		!strings.Contains(err.Error(), "stack") {
		t.Errorf("unexpected error returned from panic recovery %v", err)
	}
}

func TestPanicRecoveryDisabled(t *testing.T) {
	t.Parallel()

	defer func() {
		if r := recover(); r == nil {
			t.Errorf("retryer should have panicked with default settings")
		}
	}()

	New().Do(panicked)
}
func TestEnsureFn(t *testing.T) {
	t.Parallel()

	touched := false
	toggler := func(error) { touched = true }

	r := New(Ensure(toggler))
	err := r.Do(sad)
	if err == nil {
		t.Errorf("should have succeeded without an error, got %v. Retryer state %#v", err, r)
	}
	if !touched {
		t.Error("ensure function wasn't called")
	}
}

func TestErrorFnOn(t *testing.T) {
	t.Parallel()

	// errorTypeC is not amongst the ones to retry on, on 5th attempt we expect to succeed
	fn := func() error { return errorTypeC{S: "error c triggered"} }
	ab := attemptsBased{
		succeedOnNth: 5,
		fn:           fn,
	}

	r := New(Tries(6), On([]error{errorTypeA{}, errorTypeB{}}))
	err := r.Do(ab.run)
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if r.attempts != 6 {
		t.Errorf("incorrect attempts count, got %d want 5", r.attempts)

	}

	// errorTypeC is not amongst the ones to retry on, we try only once
	fn = func() error { return errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), On([]error{errorTypeA{}, errorTypeC{}})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}

	// errorTypeC is not amongst the ones to retry on, as we supply empty slice of errors, we try only once
	fn = func() error { return errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), On([]error{})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}
}

func TestErrorFnNot(t *testing.T) {
	t.Parallel()

	// errorTypeC is amongst the ones to ignore in the not slice on, we expect a success after 1st run
	errMsg := "error c triggered"
	fn := func() error { return errorTypeC{S: errMsg} }

	r := New(Tries(3), Not([]error{errorTypeA{}, errorTypeC{}}))
	err := r.Do(fn)
	if err == nil {
		t.Errorf("unexpected error: %v", err)
	}
	if r.attempts != 1 {
		t.Errorf("incorrect attempts count, got %d want 1", r.attempts)
	}

	// errorTypeC is not amongst the ones to retry on, we try only once
	fn = func() error { return errorTypeC{S: "error c triggered"} }
	err = New(Tries(1), Not([]error{errorTypeA{}, errorTypeB{}})).Do(fn)
	if err == nil {
		t.Errorf("expected errorType error")
	}
	if !strings.Contains(err.Error(), errMsg) {
		t.Errorf("unexpected error returned, got: type:%v msg:'%v',  want to contain: type:%v msg:'%v'", reflect.TypeOf(err), err.Error(), "errorTypeC", errMsg)
	}
}

func TestAfterEachFail(t *testing.T) {
	t.Parallel()

	ch := make(chan error)
	fn := func(err error) { ch <- err }

	bodyFn := func() error { return errors.New("generic error") }
	ab := attemptsBased{
		succeedOnNth: 5,
		fn:           bodyFn,
	}

	go func() {
		New(AfterEachFail(fn), Tries(5)).Do(ab.run)
	}()

	for i := 0; i < 5; i++ {
		select {
		case <-ch:
			continue
		case <-time.After(50 * time.Millisecond):
			t.Fatal("didn't receive an error from the fail callback function")
		}
	}
}

func TestCombinedOptions(t *testing.T) {
	t.Parallel()

	ab := attemptsBased{
		succeedOnNth: 3,
		fn:           sad,
	}
	touched := false
	toggler := func(error) { touched = true }

	r := New(Tries(5), Sleep(10), Ensure(toggler))
	err := r.Do(ab.run)
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if !touched {
		t.Error("ensure function wasn't called")
	}
}

func TestSleepFnPriorityOverSleep(t *testing.T) {
	t.Parallel()

	// we want to sleep for 100+200+300 ms = 600 ms -> linearly growing backoff
	sleepFn := func(attempts int) {
		sleep := time.Duration(100*attempts) * time.Millisecond
		time.Sleep(sleep)
	}

	// the Sleep(1000) won't be used, if yes, it will be caught by the timers below
	r := New(SleepFn(sleepFn), Sleep(1000), Tries(3))
	ch := make(chan error)
	start := time.Now()
	go func() {
		ch <- r.Do(sad)
	}()

	expectedSleepDur := time.Duration(600 * time.Millisecond)
	select {
	case <-time.After(expectedSleepDur + 100*time.Millisecond):
		t.Errorf("should have slept only for %v took too long", expectedSleepDur)
	case err := <-ch:
		// have we finished sooner?
		if d := time.Since(start); d < expectedSleepDur {
			t.Errorf("retryer didn't sleep for the desired time, ended after %v", d)
		}
		if err == nil {
			t.Errorf("should have failed with an error, Retryer state %#v", r)
		}
	}
}

type errorTypeA struct {
	s string
}

func (e errorTypeA) Error() string {
	return e.s
}

type errorTypeB struct {
	s string
}

func (e errorTypeB) Error() string {
	return e.s
}

type errorTypeC struct {
	S string
}

func (e errorTypeC) Error() string {
	return e.S
}

func happy() error {
	_ = 2 + 3
	return nil
}

func sad() error {
	_ = 2 + 3
	return errors.New("error on primitive addition")
}

func panicked() error {
	panic("explicit trigger of panic")
}

type attemptsBased struct {
	attempts     int
	succeedOnNth int
	fn           func() error
}

func (ab *attemptsBased) run() error {
	if ab.attempts == ab.succeedOnNth {
		return nil
	}

	ab.attempts++
	return ab.fn()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"google.golang.org/grpc"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/cyberdelia/go-metrics-graphite"
	"github.com/docker/containerd"
	"github.com/docker/containerd/api/grpc/server"
	"github.com/docker/containerd/api/grpc/types"
	"github.com/docker/containerd/api/http/pprof"
	"github.com/docker/containerd/supervisor"
	"github.com/docker/docker/pkg/listeners"
	"github.com/rcrowley/go-metrics"
)

const (
	usage               = `High performance container daemon`
	minRlimit           = 1024
	defaultStateDir     = "/run/containerd"
	defaultGRPCEndpoint = "unix:///run/containerd/containerd.sock"
)

var daemonFlags = []cli.Flag{
	cli.BoolFlag{
		Name:  "debug",
		Usage: "enable debug output in the logs",
	},
	cli.StringFlag{
		Name:  "state-dir",
		Value: defaultStateDir,
		Usage: "runtime state directory",
	},
	cli.DurationFlag{
		Name:  "metrics-interval",
		Value: 5 * time.Minute,
		Usage: "interval for flushing metrics to the store",
	},
	cli.StringFlag{
		Name:  "listen,l",
		Value: defaultGRPCEndpoint,
		Usage: "proto://address on which the GRPC API will listen",
	},
	cli.StringFlag{
		Name:  "runtime,r",
		Value: "runc",
		Usage: "name or path of the OCI compliant runtime to use when executing containers",
	},
	cli.StringSliceFlag{
		Name:  "runtime-args",
		Value: &cli.StringSlice{},
		Usage: "specify additional runtime args",
	},
	cli.StringFlag{
		Name:  "shim",
		Value: "containerd-shim",
		Usage: "Name or path of shim",
	},
	cli.StringFlag{
		Name:  "pprof-address",
		Usage: "http address to listen for pprof events",
	},
	cli.DurationFlag{
		Name:  "start-timeout",
		Value: 15 * time.Second,
		Usage: "timeout duration for waiting on a container to start before it is killed",
	},
	cli.IntFlag{
		Name:  "retain-count",
		Value: 500,
		Usage: "number of past events to keep in the event log",
	},
	cli.StringFlag{
		Name:  "graphite-address",
		Usage: "Address of graphite server",
	},
}

func main() {
	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.RFC3339Nano})
	app := cli.NewApp()
	app.Name = "containerd"
	if containerd.GitCommit != "" {
		app.Version = fmt.Sprintf("%s commit: %s", containerd.Version, containerd.GitCommit)
	} else {
		app.Version = containerd.Version
	}
	app.Usage = usage
	app.Flags = daemonFlags
	app.Before = func(context *cli.Context) error {
		if context.GlobalBool("debug") {
			logrus.SetLevel(logrus.DebugLevel)
			if context.GlobalDuration("metrics-interval") > 0 {
				if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
					return err
				}
			}

		}
		if p := context.GlobalString("pprof-address"); len(p) > 0 {
			pprof.Enable(p)
		}
		if err := checkLimits(); err != nil {
			return err
		}
		return nil
	}

	app.Action = func(context *cli.Context) {
		if err := daemon(context); err != nil {
			logrus.Fatal(err)
		}
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}

func daemon(context *cli.Context) error {
	// setup a standard reaper so that we don't leave any zombies if we are still alive
	// this is just good practice because we are spawning new processes
	s := make(chan os.Signal, 2048)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	sv, err := supervisor.New(
		context.String("state-dir"),
		context.String("runtime"),
		context.String("shim"),
		context.StringSlice("runtime-args"),
		context.Duration("start-timeout"),
		context.Int("retain-count"))
	if err != nil {
		return err
	}
	wg := &sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		w := supervisor.NewWorker(sv, wg)
		go w.Start()
	}
	if err := sv.Start(); err != nil {
		return err
	}
	// Split the listen string of the form proto://addr
	listenSpec := context.String("listen")
	listenParts := strings.SplitN(listenSpec, "://", 2)
	if len(listenParts) != 2 {
		return fmt.Errorf("bad listen address format %s, expected proto://address", listenSpec)
	}
	server, err := startServer(listenParts[0], listenParts[1], sv)
	if err != nil {
		return err
	}
	for ss := range s {
		switch ss {
		default:
			logrus.Infof("stopping containerd after receiving %s", ss)
			server.Stop()
			os.Exit(0)
		}
	}
	return nil
}

func startServer(protocol, address string, sv *supervisor.Supervisor) (*grpc.Server, error) {
	// TODO: We should use TLS.
	// TODO: Add an option for the SocketGroup.
	sockets, err := listeners.Init(protocol, address, "", nil)
	if err != nil {
		return nil, err
	}
	if len(sockets) != 1 {
		return nil, fmt.Errorf("incorrect number of listeners")
	}
	l := sockets[0]
	s := grpc.NewServer()
	types.RegisterAPIServer(s, server.NewServer(sv))
	go func() {
		logrus.Debugf("containerd: grpc api on %s", address)
		if err := s.Serve(l); err != nil {
			logrus.WithField("error", err).Fatal("containerd: serve grpc")
		}
	}()
	return s, nil
}

// getDefaultID returns the hostname for the instance host
func getDefaultID() string {
	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}
	return hostname
}

func checkLimits() error {
	var l syscall.Rlimit
	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l); err != nil {
		return err
	}
	if l.Cur <= minRlimit {
		logrus.WithFields(logrus.Fields{
			"current": l.Cur,
			"max":     l.Max,
		}).Warn("containerd: low RLIMIT_NOFILE changing to max")
		l.Cur = l.Max
		return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
	}
	return nil
}

func debugMetrics(interval time.Duration, graphiteAddr string) error {
	for name, m := range supervisor.Metrics() {
		if err := metrics.DefaultRegistry.Register(name, m); err != nil {
			return err
		}
	}
	processMetrics()
	if graphiteAddr != "" {
		addr, err := net.ResolveTCPAddr("tcp", graphiteAddr)
		if err != nil {
			return err
		}
		go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
	} else {
		l := log.New(os.Stdout, "[containerd] ", log.LstdFlags)
		go metrics.Log(metrics.DefaultRegistry, interval, l)
	}
	return nil
}
<commit_msg>Remove obsolete comment about reaper in containerd/main.go (#297)<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"google.golang.org/grpc"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/cyberdelia/go-metrics-graphite"
	"github.com/docker/containerd"
	"github.com/docker/containerd/api/grpc/server"
	"github.com/docker/containerd/api/grpc/types"
	"github.com/docker/containerd/api/http/pprof"
	"github.com/docker/containerd/supervisor"
	"github.com/docker/docker/pkg/listeners"
	"github.com/rcrowley/go-metrics"
)

const (
	usage               = `High performance container daemon`
	minRlimit           = 1024
	defaultStateDir     = "/run/containerd"
	defaultGRPCEndpoint = "unix:///run/containerd/containerd.sock"
)

var daemonFlags = []cli.Flag{
	cli.BoolFlag{
		Name:  "debug",
		Usage: "enable debug output in the logs",
	},
	cli.StringFlag{
		Name:  "state-dir",
		Value: defaultStateDir,
		Usage: "runtime state directory",
	},
	cli.DurationFlag{
		Name:  "metrics-interval",
		Value: 5 * time.Minute,
		Usage: "interval for flushing metrics to the store",
	},
	cli.StringFlag{
		Name:  "listen,l",
		Value: defaultGRPCEndpoint,
		Usage: "proto://address on which the GRPC API will listen",
	},
	cli.StringFlag{
		Name:  "runtime,r",
		Value: "runc",
		Usage: "name or path of the OCI compliant runtime to use when executing containers",
	},
	cli.StringSliceFlag{
		Name:  "runtime-args",
		Value: &cli.StringSlice{},
		Usage: "specify additional runtime args",
	},
	cli.StringFlag{
		Name:  "shim",
		Value: "containerd-shim",
		Usage: "Name or path of shim",
	},
	cli.StringFlag{
		Name:  "pprof-address",
		Usage: "http address to listen for pprof events",
	},
	cli.DurationFlag{
		Name:  "start-timeout",
		Value: 15 * time.Second,
		Usage: "timeout duration for waiting on a container to start before it is killed",
	},
	cli.IntFlag{
		Name:  "retain-count",
		Value: 500,
		Usage: "number of past events to keep in the event log",
	},
	cli.StringFlag{
		Name:  "graphite-address",
		Usage: "Address of graphite server",
	},
}

func main() {
	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.RFC3339Nano})
	app := cli.NewApp()
	app.Name = "containerd"
	if containerd.GitCommit != "" {
		app.Version = fmt.Sprintf("%s commit: %s", containerd.Version, containerd.GitCommit)
	} else {
		app.Version = containerd.Version
	}
	app.Usage = usage
	app.Flags = daemonFlags
	app.Before = func(context *cli.Context) error {
		if context.GlobalBool("debug") {
			logrus.SetLevel(logrus.DebugLevel)
			if context.GlobalDuration("metrics-interval") > 0 {
				if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
					return err
				}
			}

		}
		if p := context.GlobalString("pprof-address"); len(p) > 0 {
			pprof.Enable(p)
		}
		if err := checkLimits(); err != nil {
			return err
		}
		return nil
	}

	app.Action = func(context *cli.Context) {
		if err := daemon(context); err != nil {
			logrus.Fatal(err)
		}
	}
	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}

func daemon(context *cli.Context) error {
	s := make(chan os.Signal, 2048)
	signal.Notify(s, syscall.SIGTERM, syscall.SIGINT)
	sv, err := supervisor.New(
		context.String("state-dir"),
		context.String("runtime"),
		context.String("shim"),
		context.StringSlice("runtime-args"),
		context.Duration("start-timeout"),
		context.Int("retain-count"))
	if err != nil {
		return err
	}
	wg := &sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		w := supervisor.NewWorker(sv, wg)
		go w.Start()
	}
	if err := sv.Start(); err != nil {
		return err
	}
	// Split the listen string of the form proto://addr
	listenSpec := context.String("listen")
	listenParts := strings.SplitN(listenSpec, "://", 2)
	if len(listenParts) != 2 {
		return fmt.Errorf("bad listen address format %s, expected proto://address", listenSpec)
	}
	server, err := startServer(listenParts[0], listenParts[1], sv)
	if err != nil {
		return err
	}
	for ss := range s {
		switch ss {
		default:
			logrus.Infof("stopping containerd after receiving %s", ss)
			server.Stop()
			os.Exit(0)
		}
	}
	return nil
}

func startServer(protocol, address string, sv *supervisor.Supervisor) (*grpc.Server, error) {
	// TODO: We should use TLS.
	// TODO: Add an option for the SocketGroup.
	sockets, err := listeners.Init(protocol, address, "", nil)
	if err != nil {
		return nil, err
	}
	if len(sockets) != 1 {
		return nil, fmt.Errorf("incorrect number of listeners")
	}
	l := sockets[0]
	s := grpc.NewServer()
	types.RegisterAPIServer(s, server.NewServer(sv))
	go func() {
		logrus.Debugf("containerd: grpc api on %s", address)
		if err := s.Serve(l); err != nil {
			logrus.WithField("error", err).Fatal("containerd: serve grpc")
		}
	}()
	return s, nil
}

// getDefaultID returns the hostname for the instance host
func getDefaultID() string {
	hostname, err := os.Hostname()
	if err != nil {
		panic(err)
	}
	return hostname
}

func checkLimits() error {
	var l syscall.Rlimit
	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l); err != nil {
		return err
	}
	if l.Cur <= minRlimit {
		logrus.WithFields(logrus.Fields{
			"current": l.Cur,
			"max":     l.Max,
		}).Warn("containerd: low RLIMIT_NOFILE changing to max")
		l.Cur = l.Max
		return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &l)
	}
	return nil
}

func debugMetrics(interval time.Duration, graphiteAddr string) error {
	for name, m := range supervisor.Metrics() {
		if err := metrics.DefaultRegistry.Register(name, m); err != nil {
			return err
		}
	}
	processMetrics()
	if graphiteAddr != "" {
		addr, err := net.ResolveTCPAddr("tcp", graphiteAddr)
		if err != nil {
			return err
		}
		go graphite.Graphite(metrics.DefaultRegistry, 10e9, "metrics", addr)
	} else {
		l := log.New(os.Stdout, "[containerd] ", log.LstdFlags)
		go metrics.Log(metrics.DefaultRegistry, interval, l)
	}
	return nil
}
<|endoftext|>


<commit_before>package trackello

import "testing"

func TestTotalStatisticsCount(t *testing.T) {
	stats := &Statistics{
		comments:             10,
		checkListItemUpdates: 10,
		creates:              10,
		updates:              10,
	}

	if stats.Total() != 40 {
		t.Fatalf("Expected %d, got %d", 40, stats.Total())
	}
}
<commit_msg>add test for printing statistics<commit_after>package trackello

import "testing"

func TestTotalStatisticsCount(t *testing.T) {
	stats := &Statistics{
		comments:             10,
		checkListItemUpdates: 10,
		creates:              10,
		updates:              10,
	}

	if stats.Total() != 40 {
		t.Fatalf("Expected %d, got %d", 40, stats.Total())
	}
}

func TestPrintStatistics(t *testing.T) {
	stats := &Statistics{
		comments:             10,
		checkListItemUpdates: 10,
		creates:              10,
		updates:              10,
	}

	printed := stats.PrintStatistics()

	expected := "[\x1b[96m10 +\x1b[0m\x1b[91m 10 ≡\x1b[0m\x1b[92m 10 ✓\x1b[0m\x1b[95m 10 …\x1b[0m]"
	if printed != expected {
		t.Fatalf("Printed output doesn't match expected: got %s, wanted %s", printed, expected)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/mitchellh/colorstring"

	"github.com/yuuki1/gokc/log"
	"github.com/yuuki1/gokc/parser"
)

func setDebugOutputLevel() {
	for _, f := range os.Args {
		if f == "-D" || f == "--debug" || f == "-debug" {
			log.IsDebug = true
		}
	}

	debugEnv := os.Getenv("GOKC_DEBUG")
	if debugEnv != "" {
		showDebug, err := strconv.ParseBool(debugEnv)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error parsing boolean value from GRABENI_DEBUG: %s\n", err)
			os.Exit(1)
		}
		log.IsDebug = showDebug
	}
}

func init() {
	setDebugOutputLevel()
}

func main() {
	var filepath string
	flag.StringVar(&filepath, "f", "", "keepalived.conf file path")
	flag.Parse()

	if filepath == "" {
		fmt.Println("filepath required")
		os.Exit(1)
	}

	file, err := os.Open(filepath)
	if err != nil {
		fmt.Printf("%s\n", err)
		os.Exit(1)
	}

	p := parser.NewParser(file)
	if err := p.Parse(); err != nil {
		var msgs []string
		for _, e := range p.Errors() {
			msg := colorstring.Color(fmt.Sprintf("[white]%s:%d:%d: [red]%s[reset]", filepath, e.Line, e.Column, e.Message))
			msgs = append(msgs, msg)
		}
		log.Errorf("%s", strings.Join(msgs, "\n"))
	}

	log.Infof("gokc: the configuration file %s syntax is ok", filepath)
}
<commit_msg>Add file exist check<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/mitchellh/colorstring"

	"github.com/yuuki1/gokc/log"
	"github.com/yuuki1/gokc/parser"
)

func setDebugOutputLevel() {
	for _, f := range os.Args {
		if f == "-D" || f == "--debug" || f == "-debug" {
			log.IsDebug = true
		}
	}

	debugEnv := os.Getenv("GOKC_DEBUG")
	if debugEnv != "" {
		showDebug, err := strconv.ParseBool(debugEnv)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error parsing boolean value from GRABENI_DEBUG: %s\n", err)
			os.Exit(1)
		}
		log.IsDebug = showDebug
	}
}

func init() {
	setDebugOutputLevel()
}

func main() {
	var filepath string
	flag.StringVar(&filepath, "f", "", "keepalived.conf file path")
	flag.Parse()

	if filepath == "" {
		fmt.Println("filepath required")
		os.Exit(1)
	}

	if _, err := os.Stat(filepath); err != nil {
		log.Error(err)
	}

	file, err := os.Open(filepath)
	if err != nil {
		fmt.Printf("%s\n", err)
		os.Exit(1)
	}

	p := parser.NewParser(file)
	if err := p.Parse(); err != nil {
		var msgs []string
		for _, e := range p.Errors() {
			msg := colorstring.Color(fmt.Sprintf("[white]%s:%d:%d: [red]%s[reset]", filepath, e.Line, e.Column, e.Message))
			msgs = append(msgs, msg)
		}
		log.Errorf("%s", strings.Join(msgs, "\n"))
	}

	log.Infof("gokc: the configuration file %s syntax is ok", filepath)
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/gsamokovarov/jump/cli"
	"github.com/gsamokovarov/jump/config"
	"github.com/gsamokovarov/jump/scoring"
)

func hintCmd(args cli.Args, conf *config.Config) {
	if len(args) == 0 {
		return
	}

	entries, err := conf.ReadEntries()
	if err != nil {
		cli.Exitf(1, "%s\n", err)
	}

	fuzzyEntries := scoring.NewFuzzyEntries(entries, args.CommandName())
	fuzzyEntries.Sort()
	for _, entry := range fuzzyEntries.Entries {
		cli.Outf("%s\n", entry.Path)
	}
}

func init() {
	cli.RegisterCommand("hint", "Hints relevant paths for jumping.", hintCmd)
}
<commit_msg>Hint all the entries if no arguments given to jump hint<commit_after>package cmd

import (
	"sort"

	"github.com/gsamokovarov/jump/cli"
	"github.com/gsamokovarov/jump/config"
	"github.com/gsamokovarov/jump/scoring"
)

func hintCmd(args cli.Args, conf *config.Config) {
	var hints scoring.Entries

	entries, err := conf.ReadEntries()
	if err != nil {
		cli.Exitf(1, "%s\n", err)
	}

	if len(args) == 0 {
		// We usually keep them reversely sort to optimize the fuzzy search.
		sort.Sort(sort.Reverse(entries))

		hints = entries
	} else {
		fuzzyEntries := scoring.NewFuzzyEntries(entries, args.CommandName())
		fuzzyEntries.Sort()

		hints = fuzzyEntries.Entries
	}

	for _, entry := range hints {
		cli.Outf("%s\n", entry.Path)
	}
}

func init() {
	cli.RegisterCommand("hint", "Hints relevant paths for jumping.", hintCmd)
}
<|endoftext|>


<commit_before>// +build ignore

package main

func main() {

}
<commit_msg>all: gofmt -w .<commit_after>// +build ignore

package main

func main() {

}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google, Inc. All rights reserved.

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

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

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

package cmd

import (
	goflag "flag"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/GoogleCloudPlatform/container-diff/differs"
	"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
	pkgutil "github.com/GoogleCloudPlatform/container-diff/pkg/util"
	"github.com/GoogleCloudPlatform/container-diff/util"
	"github.com/docker/docker/client"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

var json bool
var save bool
var types diffTypes
var noCache bool

var LogLevel string

type validatefxn func(args []string) error

const (
	DaemonPrefix = "daemon://"
	RemotePrefix = "remote://"
)

var RootCmd = &cobra.Command{
	Use:   "container-diff",
	Short: "container-diff is a tool for analyzing and comparing container images",
	Long: `container-diff is a CLI tool for analyzing and comparing container images.

Images can be specified from either a local Docker daemon, or from a remote registry.
To specify a local image, prefix the image ID with 'daemon://', e.g. 'daemon://gcr.io/foo/bar'.
To specify a remote image, prefix the image ID with 'remote://', e.g. 'remote://gcr.io/foo/bar'.
If no prefix is specified, the local daemon will be checked first.

Tarballs can also be specified by simply providing the path to the .tar, .tar.gz, or .tgz file.`,
	PersistentPreRun: func(c *cobra.Command, s []string) {
		ll, err := logrus.ParseLevel(LogLevel)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		logrus.SetLevel(ll)
	},
}

func outputResults(resultMap map[string]util.Result) {
	// Outputs diff/analysis results in alphabetical order by analyzer name
	sortedTypes := []string{}
	for analyzerType := range resultMap {
		sortedTypes = append(sortedTypes, analyzerType)
	}
	sort.Strings(sortedTypes)

	results := make([]interface{}, len(resultMap))
	for i, analyzerType := range sortedTypes {
		result := resultMap[analyzerType]
		if json {
			results[i] = result.OutputStruct()
		} else {
			err := result.OutputText(analyzerType)
			if err != nil {
				logrus.Error(err)
			}
		}
	}
	if json {
		err := util.JSONify(results)
		if err != nil {
			logrus.Error(err)
		}
	}
}

func validateArgs(args []string, validatefxns ...validatefxn) error {
	for _, validatefxn := range validatefxns {
		if err := validatefxn(args); err != nil {
			return err
		}
	}
	return nil
}

func checkIfValidAnalyzer(_ []string) error {
	if len(types) == 0 {
		types = []string{"apt"}
	}
	for _, name := range types {
		if _, exists := differs.Analyzers[name]; !exists {
			return fmt.Errorf("Argument %s is not a valid analyzer", name)
		}
	}
	return nil
}

func getPrepperForImage(image string) (pkgutil.Prepper, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}

	cacheDir, err := cacheDir()
	if err != nil {
		return nil, err
	}
	var fsCache cache.Cache
	if !noCache {
		fsCache, err = cache.NewFileCache(cacheDir)
		if err != nil {
			return nil, err
		}
	}

	if pkgutil.IsTar(image) {
		return pkgutil.TarPrepper{
			Source: image,
			Client: cli,
			Cache:  fsCache,
		}, nil

	} else if strings.HasPrefix(image, DaemonPrefix) {
		return pkgutil.DaemonPrepper{
			Source: strings.Replace(image, DaemonPrefix, "", -1),
			Client: cli,
			Cache:  fsCache,
		}, nil
	}
	// either has remote prefix or has no prefix, in which case we force remote
	return pkgutil.CloudPrepper{
		Source: strings.Replace(image, RemotePrefix, "", -1),
		Client: cli,
		Cache:  fsCache,
	}, nil
}

func cacheDir() (string, error) {
	user, err := user.Current()
	if err != nil {
		return "", err
	}
	rootDir := filepath.Join(user.HomeDir, ".container-diff")
	return filepath.Join(rootDir, "cache"), nil
}

func init() {
	RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.")
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

// Define a type named "diffSlice" as a slice of strings
type diffTypes []string

// Now, for our new type, implement the two methods of
// the flag.Value interface...
// The first method is String() string
func (d *diffTypes) String() string {
	return strings.Join(*d, ",")
}

// The second method is Set(value string) error
func (d *diffTypes) Set(value string) error {
	// Dedupe repeated elements.
	for _, t := range *d {
		if t == value {
			return nil
		}
	}
	*d = append(*d, value)
	return nil
}

func (d *diffTypes) Type() string {
	return "Diff Types"
}

func addSharedFlags(cmd *cobra.Command) {
	cmd.Flags().BoolVarP(&json, "json", "j", false, "JSON Output defines if the diff should be returned in a human readable format (false) or a JSON (true).")
	cmd.Flags().VarP(&types, "type", "t", "This flag sets the list of analyzer types to use. Set it repeatedly to use multiple analyzers.")
	cmd.Flags().BoolVarP(&save, "save", "s", false, "Set this flag to save rather than remove the final image filesystems on exit.")
	cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.")
	cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of layers on each run.")
}
<commit_msg>add image tag as latest if none given<commit_after>/*
Copyright 2017 Google, Inc. All rights reserved.

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

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

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

package cmd

import (
	goflag "flag"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"sort"
	"strings"

	"github.com/GoogleCloudPlatform/container-diff/differs"
	"github.com/GoogleCloudPlatform/container-diff/pkg/cache"
	pkgutil "github.com/GoogleCloudPlatform/container-diff/pkg/util"
	"github.com/GoogleCloudPlatform/container-diff/util"
	"github.com/docker/docker/client"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

var json bool
var save bool
var types diffTypes
var noCache bool

var LogLevel string

type validatefxn func(args []string) error

const (
	DaemonPrefix = "daemon://"
	RemotePrefix = "remote://"
)

var RootCmd = &cobra.Command{
	Use:   "container-diff",
	Short: "container-diff is a tool for analyzing and comparing container images",
	Long: `container-diff is a CLI tool for analyzing and comparing container images.

Images can be specified from either a local Docker daemon, or from a remote registry.
To specify a local image, prefix the image ID with 'daemon://', e.g. 'daemon://gcr.io/foo/bar'.
To specify a remote image, prefix the image ID with 'remote://', e.g. 'remote://gcr.io/foo/bar'.
If no prefix is specified, the local daemon will be checked first.

Tarballs can also be specified by simply providing the path to the .tar, .tar.gz, or .tgz file.`,
	PersistentPreRun: func(c *cobra.Command, s []string) {
		ll, err := logrus.ParseLevel(LogLevel)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		logrus.SetLevel(ll)
	},
}

func outputResults(resultMap map[string]util.Result) {
	// Outputs diff/analysis results in alphabetical order by analyzer name
	sortedTypes := []string{}
	for analyzerType := range resultMap {
		sortedTypes = append(sortedTypes, analyzerType)
	}
	sort.Strings(sortedTypes)

	results := make([]interface{}, len(resultMap))
	for i, analyzerType := range sortedTypes {
		result := resultMap[analyzerType]
		if json {
			results[i] = result.OutputStruct()
		} else {
			err := result.OutputText(analyzerType)
			if err != nil {
				logrus.Error(err)
			}
		}
	}
	if json {
		err := util.JSONify(results)
		if err != nil {
			logrus.Error(err)
		}
	}
}

func validateArgs(args []string, validatefxns ...validatefxn) error {
	for _, validatefxn := range validatefxns {
		if err := validatefxn(args); err != nil {
			return err
		}
	}
	return nil
}

func checkIfValidAnalyzer(_ []string) error {
	if len(types) == 0 {
		types = []string{"apt"}
	}
	for _, name := range types {
		if _, exists := differs.Analyzers[name]; !exists {
			return fmt.Errorf("Argument %s is not a valid analyzer", name)
		}
	}
	return nil
}

func getPrepperForImage(image string) (pkgutil.Prepper, error) {
	cli, err := client.NewEnvClient()
	if err != nil {
		return nil, err
	}

	cacheDir, err := cacheDir()
	if err != nil {
		return nil, err
	}
	var fsCache cache.Cache
	if !noCache {
		fsCache, err = cache.NewFileCache(cacheDir)
		if err != nil {
			return nil, err
		}
	}

	if pkgutil.IsTar(image) {
		return pkgutil.TarPrepper{
			Source: image,
			Client: cli,
			Cache:  fsCache,
		}, nil

	} else if strings.HasPrefix(image, DaemonPrefix) {

		// remove the DaemonPrefix
		image := strings.Replace(image, DaemonPrefix, "", -1)
		// see if the image name has tag provided, if not add latest as tag
		if !strings.Contains(image, ":") {
			image = image + ":latest"
		}

		return pkgutil.DaemonPrepper{
			Source: image,
			Client: cli,
			Cache:  fsCache,
		}, nil
	}
	// either has remote prefix or has no prefix, in which case we force remote
	return pkgutil.CloudPrepper{
		Source: strings.Replace(image, RemotePrefix, "", -1),
		Client: cli,
		Cache:  fsCache,
	}, nil
}

func cacheDir() (string, error) {
	user, err := user.Current()
	if err != nil {
		return "", err
	}
	rootDir := filepath.Join(user.HomeDir, ".container-diff")
	return filepath.Join(rootDir, "cache"), nil
}

func init() {
	RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.")
	pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}

// Define a type named "diffSlice" as a slice of strings
type diffTypes []string

// Now, for our new type, implement the two methods of
// the flag.Value interface...
// The first method is String() string
func (d *diffTypes) String() string {
	return strings.Join(*d, ",")
}

// The second method is Set(value string) error
func (d *diffTypes) Set(value string) error {
	// Dedupe repeated elements.
	for _, t := range *d {
		if t == value {
			return nil
		}
	}
	*d = append(*d, value)
	return nil
}

func (d *diffTypes) Type() string {
	return "Diff Types"
}

func addSharedFlags(cmd *cobra.Command) {
	cmd.Flags().BoolVarP(&json, "json", "j", false, "JSON Output defines if the diff should be returned in a human readable format (false) or a JSON (true).")
	cmd.Flags().VarP(&types, "type", "t", "This flag sets the list of analyzer types to use. Set it repeatedly to use multiple analyzers.")
	cmd.Flags().BoolVarP(&save, "save", "s", false, "Set this flag to save rather than remove the final image filesystems on exit.")
	cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.")
	cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of layers on each run.")
}
<|endoftext|>


<commit_before>// Copyright 2015 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 cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tsuru/tsuru/auth/saml"
)

const formPostPage = `<!DOCTYPE html>

	<form method="POST" name="frm" action="{{.url}}">
		<input type="hidden" name="SAMLRequest" value="{{.saml_request}}" />
		<input type="submit" value="Go to login" />
	</form>

`

const successSamlCallBackMarkup = `

Login Successful!

You can close this window now.

`

const errorSamlCallBackMarkup = `

Login Failed!

%s

`

func SamlCallbackSuccessMessage() string {
	return successSamlCallBackMarkup
}

func SamlCallbackFailureMessage() string {
	return errorSamlCallBackMarkup
}

func samlRequestId(schemeData map[string]string) string {
	return schemeData["request_id"]
}

//Return timeout in seconds
func samlRequestTimeout(schemeData map[string]string) int {
	p := schemeData["request_timeout"]
	timeout, _ := strconv.Atoi(p)
	return timeout
}

func samlPreLogin(schemeData map[string]string, finish chan bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			finish <- true
		}()
		t := template.New("saml")
		t, err := t.Parse(formPostPage)
		if err != nil {
			page := fmt.Sprintf(errorSamlCallBackMarkup, err.Error())
			w.Header().Add("Content-Type", "text/html")
			w.Write([]byte(page))
		} else {
			t.Execute(w, schemeData)
		}
	}
}

func requestToken(schemeData map[string]string) (string, error) {
	maxRetries := samlRequestTimeout(schemeData) - 7
	time.Sleep(5 * time.Second)
	id := samlRequestId(schemeData)
	params := map[string]string{"request_id": id}
	for count := 0; count <= maxRetries; count += 2 {
		u, err := GetURL("/auth/login")
		if err != nil {
			return "", fmt.Errorf("Error in GetURL: %s", err.Error())
		}
		var buf bytes.Buffer
		err = json.NewEncoder(&buf).Encode(params)
		if err != nil {
			return "", fmt.Errorf("Error encoding params %#v: %s", params, err.Error())
		}
		resp, err := http.Post(u, "application/json", &buf)
		if err != nil {
			return "", fmt.Errorf("Error during login post: %s", err.Error())
		}
		defer resp.Body.Close()
		result, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return "", fmt.Errorf("Error reading body: %s", err.Error())
		}
		if strings.TrimSpace(string(result)) == saml.ErrRequestWaitingForCredentials.Message {
			if count < maxRetries {
				time.Sleep(2 * time.Second)
			}
			continue
		}
		data := make(map[string]interface{})
		if err = json.Unmarshal(result, &data); err != nil {
			return "", fmt.Errorf("API response: %s", result)
		}
		return data["token"].(string), nil
	}
	// finish when timeout
	return "", saml.ErrRequestWaitingForCredentials
}

func (c *login) samlLogin(context *Context, client *Client) error {
	schemeData := c.getScheme().Data
	l, err := net.Listen("tcp", ":0")
	if err != nil {
		return err
	}
	_, port, err := net.SplitHostPort(l.Addr().String())
	if err != nil {
		return err
	}
	finish := make(chan bool)
	preLoginUrl := fmt.Sprintf("http://localhost:%s/", port)
	http.HandleFunc("/", samlPreLogin(schemeData, finish))
	server := &http.Server{}
	go server.Serve(l)
	if err := open(preLoginUrl); err != nil {
		fmt.Fprintln(context.Stdout, "Failed to start your browser.")
		fmt.Fprintf(context.Stdout, "Please open the following URL in your browser: %s\n", preLoginUrl)
	}
	<-finish
	token, err := requestToken(schemeData)
	switch err {
	case nil:
		writeToken(token)
		fmt.Fprintln(context.Stdout, "\nSuccessfully logged in!")
		break
	case saml.ErrRequestWaitingForCredentials:
		fmt.Fprintln(context.Stdout, "\nLogin failed! Timeout waiting for credentials from IDP, please try again.")
		break
	default:
		fmt.Fprintln(context.Stdout, "\nLogin failed for some reason, please try again: "+err.Error())
	}
	return nil
}
<commit_msg>cmd/saml: remove variable shadowing reported by vet<commit_after>// Copyright 2015 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 cmd

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tsuru/tsuru/auth/saml"
)

const formPostPage = `<!DOCTYPE html>

	<form method="POST" name="frm" action="{{.url}}">
		<input type="hidden" name="SAMLRequest" value="{{.saml_request}}" />
		<input type="submit" value="Go to login" />
	</form>

`

const successSamlCallBackMarkup = `

Login Successful!

You can close this window now.

`

const errorSamlCallBackMarkup = `

Login Failed!

%s

`

func SamlCallbackSuccessMessage() string {
	return successSamlCallBackMarkup
}

func SamlCallbackFailureMessage() string {
	return errorSamlCallBackMarkup
}

func samlRequestId(schemeData map[string]string) string {
	return schemeData["request_id"]
}

//Return timeout in seconds
func samlRequestTimeout(schemeData map[string]string) int {
	p := schemeData["request_timeout"]
	timeout, _ := strconv.Atoi(p)
	return timeout
}

func samlPreLogin(schemeData map[string]string, finish chan bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			finish <- true
		}()
		t := template.New("saml")
		t, err := t.Parse(formPostPage)
		if err != nil {
			page := fmt.Sprintf(errorSamlCallBackMarkup, err.Error())
			w.Header().Add("Content-Type", "text/html")
			w.Write([]byte(page))
		} else {
			t.Execute(w, schemeData)
		}
	}
}

func requestToken(schemeData map[string]string) (string, error) {
	maxRetries := samlRequestTimeout(schemeData) - 7
	time.Sleep(5 * time.Second)
	id := samlRequestId(schemeData)
	params := map[string]string{"request_id": id}
	for count := 0; count <= maxRetries; count += 2 {
		u, err := GetURL("/auth/login")
		if err != nil {
			return "", fmt.Errorf("Error in GetURL: %s", err.Error())
		}
		var buf bytes.Buffer
		err = json.NewEncoder(&buf).Encode(params)
		if err != nil {
			return "", fmt.Errorf("Error encoding params %#v: %s", params, err.Error())
		}
		resp, err := http.Post(u, "application/json", &buf)
		if err != nil {
			return "", fmt.Errorf("Error during login post: %s", err.Error())
		}
		defer resp.Body.Close()
		result, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return "", fmt.Errorf("Error reading body: %s", err.Error())
		}
		if strings.TrimSpace(string(result)) == saml.ErrRequestWaitingForCredentials.Message {
			if count < maxRetries {
				time.Sleep(2 * time.Second)
			}
			continue
		}
		data := make(map[string]interface{})
		if err = json.Unmarshal(result, &data); err != nil {
			return "", fmt.Errorf("API response: %s", result)
		}
		return data["token"].(string), nil
	}
	// finish when timeout
	return "", saml.ErrRequestWaitingForCredentials
}

func (c *login) samlLogin(context *Context, client *Client) error {
	schemeData := c.getScheme().Data
	l, err := net.Listen("tcp", ":0")
	if err != nil {
		return err
	}
	_, port, err := net.SplitHostPort(l.Addr().String())
	if err != nil {
		return err
	}
	finish := make(chan bool)
	preLoginUrl := fmt.Sprintf("http://localhost:%s/", port)
	http.HandleFunc("/", samlPreLogin(schemeData, finish))
	server := &http.Server{}
	go server.Serve(l)
	if err = open(preLoginUrl); err != nil {
		fmt.Fprintln(context.Stdout, "Failed to start your browser.")
		fmt.Fprintf(context.Stdout, "Please open the following URL in your browser: %s\n", preLoginUrl)
	}
	<-finish
	token, err := requestToken(schemeData)
	switch err {
	case nil:
		writeToken(token)
		fmt.Fprintln(context.Stdout, "\nSuccessfully logged in!")
		break
	case saml.ErrRequestWaitingForCredentials:
		fmt.Fprintln(context.Stdout, "\nLogin failed! Timeout waiting for credentials from IDP, please try again.")
		break
	default:
		fmt.Fprintln(context.Stdout, "\nLogin failed for some reason, please try again: "+err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"

	"github.com/BurntSushi/toml"
	"github.com/google/go-github/github"
	"github.com/knqyf263/pet/config"
	"github.com/knqyf263/pet/snippet"
	"github.com/spf13/cobra"
	"golang.org/x/oauth2"
)

// syncCmd represents the sync command
var syncCmd = &cobra.Command{
	Use:   "sync",
	Short: "Sync snippets",
	Long:  `Sync snippets with gist`,
	RunE:  sync,
}

func sync(cmd *cobra.Command, args []string) (err error) {
	if config.Conf.Gist.AccessToken == "" {
		return fmt.Errorf(`access_token is empty.
Go https://github.com/settings/tokens/new and create access_token (only need "gist" scope).
Write access_token in config file (pet configure).
		`)
	}

	if config.Flag.Upload {
		return upload()
	}
	return download()
}

func githubClient() *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Conf.Gist.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client := github.NewClient(tc)
	return client
}

func upload() (err error) {
	ctx := context.Background()

	var snippets snippet.Snippets
	if err := snippets.Load(); err != nil {
		return err
	}

	body, err := snippets.ToString()
	if err != nil {
		return err
	}

	client := githubClient()
	gist := github.Gist{
		Description: github.String("description"),
		Public:      github.Bool(config.Conf.Gist.Public),
		Files: map[github.GistFilename]github.GistFile{
			github.GistFilename(config.Conf.Gist.FileName): github.GistFile{
				Content: github.String(body),
			},
		},
	}

	gistID := config.Conf.Gist.GistID
	if gistID == "" {
		var retGist *github.Gist
		retGist, _, err = client.Gists.Create(ctx, &gist)
		if err != nil {
			return err
		}
		fmt.Printf("Gist ID: %s\n", retGist.GetID())
	} else {
		_, _, err = client.Gists.Edit(ctx, gistID, &gist)
		if err != nil {
			return err
		}
	}
	fmt.Println("Upload success")
	return nil
}

func download() error {
	if config.Conf.Gist.GistID == "" {
		return fmt.Errorf("Gist ID is empty")
	}
	ctx := context.Background()
	client := githubClient()
	resGist, _, err := client.Gists.Get(ctx, config.Conf.Gist.GistID)
	if err != nil {
		return fmt.Errorf("Failed to download gist: %v", err)
	}
	content := resGist.Files[github.GistFilename(config.Conf.Gist.FileName)].Content

	var snippets snippet.Snippets
	toml.Decode(*content, &snippets)
	if err := snippets.Save(); err != nil {
		return err
	}
	fmt.Println("Download success")
	return nil
}

func init() {
	RootCmd.AddCommand(syncCmd)
	syncCmd.Flags().BoolVarP(&config.Flag.Upload, "upload", "u", false,
		`Upload snippets to gist`)
}
<commit_msg>Fix dereference error when file name does not exist (#51)<commit_after>package cmd

import (
	"context"
	"fmt"

	"github.com/BurntSushi/toml"
	"github.com/google/go-github/github"
	"github.com/knqyf263/pet/config"
	"github.com/knqyf263/pet/snippet"
	"github.com/spf13/cobra"
	"golang.org/x/oauth2"
)

// syncCmd represents the sync command
var syncCmd = &cobra.Command{
	Use:   "sync",
	Short: "Sync snippets",
	Long:  `Sync snippets with gist`,
	RunE:  sync,
}

func sync(cmd *cobra.Command, args []string) (err error) {
	if config.Conf.Gist.AccessToken == "" {
		return fmt.Errorf(`access_token is empty.
Go https://github.com/settings/tokens/new and create access_token (only need "gist" scope).
Write access_token in config file (pet configure).
		`)
	}

	if config.Flag.Upload {
		return upload()
	}
	return download()
}

func githubClient() *github.Client {
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: config.Conf.Gist.AccessToken},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client := github.NewClient(tc)
	return client
}

func upload() (err error) {
	ctx := context.Background()

	var snippets snippet.Snippets
	if err := snippets.Load(); err != nil {
		return err
	}

	body, err := snippets.ToString()
	if err != nil {
		return err
	}

	client := githubClient()
	gist := github.Gist{
		Description: github.String("description"),
		Public:      github.Bool(config.Conf.Gist.Public),
		Files: map[github.GistFilename]github.GistFile{
			github.GistFilename(config.Conf.Gist.FileName): github.GistFile{
				Content: github.String(body),
			},
		},
	}

	gistID := config.Conf.Gist.GistID
	if gistID == "" {
		var retGist *github.Gist
		retGist, _, err = client.Gists.Create(ctx, &gist)
		if err != nil {
			return err
		}
		fmt.Printf("Gist ID: %s\n", retGist.GetID())
	} else {
		_, _, err = client.Gists.Edit(ctx, gistID, &gist)
		if err != nil {
			return err
		}
	}
	fmt.Println("Upload success")
	return nil
}

func download() error {
	if config.Conf.Gist.GistID == "" {
		return fmt.Errorf("Gist ID is empty")
	}
	ctx := context.Background()
	client := githubClient()
	resGist, _, err := client.Gists.Get(ctx, config.Conf.Gist.GistID)
	if err != nil {
		return fmt.Errorf("Failed to download gist: %v", err)
	}
	var content string
	for _, f := range resGist.Files {
		if *f.Filename == config.Conf.Gist.FileName {
			content = *f.Content
			break
		}
	}
	if content == "" {
		return fmt.Errorf("%s: no such file in gist %s",
			config.Conf.Gist.FileName, config.Conf.Gist.GistID)
	}

	var snippets snippet.Snippets
	toml.Decode(content, &snippets)
	if err := snippets.Save(); err != nil {
		return err
	}
	fmt.Println("Download success")
	return nil
}

func init() {
	RootCmd.AddCommand(syncCmd)
	syncCmd.Flags().BoolVarP(&config.Flag.Upload, "upload", "u", false,
		`Upload snippets to gist`)
}
<|endoftext|>