<commit_before>package stack

import (
	"context"
	"fmt"
	"os"

	"github.com/cozy/checkup"
	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/scheduler"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/google/gops/agent"
)

var (
	broker jobs.Broker
	schder scheduler.Scheduler
)

var log = logger.WithNamespace("stack")

type gopAgent struct{}

func (g gopAgent) Shutdown(ctx context.Context) error {
	fmt.Print("  shutting down gops...")
	agent.Close()
	fmt.Println("ok.")
	return nil
}

// Start is used to initialize all the
func Start() (utils.Shutdowner, error) {
	if config.IsDevRelease() {
		fmt.Println(`                           !! DEVELOPMENT RELEASE !!
You are running a development release which may deactivate some very important
security features. Please do not use this binary as your production server.
`)
	}

	err := agent.Listen(&agent.Options{NoShutdownCleanup: true})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on gops agent: %s\n", err)
	}

	// Check that we can properly reach CouchDB.
	db, err := checkup.HTTPChecker{
		URL:         config.CouchURL().String(),
		MustContain: `"version":"2`,
	}.Check()
	if err != nil {
		return nil, fmt.Errorf("Could not reach Couchdb 2.0 database: %s", err.Error())
	}
	if db.Status() == checkup.Down {
		return nil, fmt.Errorf("Could not reach Couchdb 2.0 database:\n%s", db.String())
	}
	if db.Status() != checkup.Healthy {
		log.Warnf("CouchDB does not seem to be in a healthy state, "+
			"the cozy-stack will be starting anyway:\n%s", db.String())
	}

	// Init the main global connection to the swift server
	fsURL := config.FsURL()
	if fsURL.Scheme == config.SchemeSwift {
		if err := config.InitSwiftConnection(fsURL); err != nil {
			return nil, err
		}
	}

	jobsConfig := config.GetConfig().Jobs
	nbWorkers := jobsConfig.Workers
	if cli := jobsConfig.Redis.Client(); cli != nil {
		broker = jobs.NewRedisBroker(nbWorkers, cli)
		schder = scheduler.NewRedisScheduler(cli)
	} else {
		broker = jobs.NewMemBroker(nbWorkers)
		schder = scheduler.NewMemScheduler()
	}
	if err := broker.Start(jobs.GetWorkersList()); err != nil {
		return nil, err
	}
	if err := schder.Start(broker); err != nil {
		return nil, err
	}

	return utils.NewGroupShutdown(broker, schder, gopAgent{}), nil
}

// GetBroker returns the global job broker.
func GetBroker() jobs.Broker {
	if broker == nil {
		panic("Job system not initialized")
	}
	return broker
}

// GetScheduler returns the global job scheduler.
func GetScheduler() scheduler.Scheduler {
	if schder == nil {
		panic("Job system not initialized")
	}
	return schder
}
<commit_msg>Fix gops (breaking change from https://github.com/google/gops/pull/49)<commit_after>package stack

import (
	"context"
	"fmt"
	"os"

	"github.com/cozy/checkup"
	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/scheduler"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/google/gops/agent"
)

var (
	broker jobs.Broker
	schder scheduler.Scheduler
)

var log = logger.WithNamespace("stack")

type gopAgent struct{}

func (g gopAgent) Shutdown(ctx context.Context) error {
	fmt.Print("  shutting down gops...")
	agent.Close()
	fmt.Println("ok.")
	return nil
}

// Start is used to initialize all the
func Start() (utils.Shutdowner, error) {
	if config.IsDevRelease() {
		fmt.Println(`                           !! DEVELOPMENT RELEASE !!
You are running a development release which may deactivate some very important
security features. Please do not use this binary as your production server.
`)
	}

	err := agent.Listen(agent.Options{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error on gops agent: %s\n", err)
	}

	// Check that we can properly reach CouchDB.
	db, err := checkup.HTTPChecker{
		URL:         config.CouchURL().String(),
		MustContain: `"version":"2`,
	}.Check()
	if err != nil {
		return nil, fmt.Errorf("Could not reach Couchdb 2.0 database: %s", err.Error())
	}
	if db.Status() == checkup.Down {
		return nil, fmt.Errorf("Could not reach Couchdb 2.0 database:\n%s", db.String())
	}
	if db.Status() != checkup.Healthy {
		log.Warnf("CouchDB does not seem to be in a healthy state, "+
			"the cozy-stack will be starting anyway:\n%s", db.String())
	}

	// Init the main global connection to the swift server
	fsURL := config.FsURL()
	if fsURL.Scheme == config.SchemeSwift {
		if err := config.InitSwiftConnection(fsURL); err != nil {
			return nil, err
		}
	}

	jobsConfig := config.GetConfig().Jobs
	nbWorkers := jobsConfig.Workers
	if cli := jobsConfig.Redis.Client(); cli != nil {
		broker = jobs.NewRedisBroker(nbWorkers, cli)
		schder = scheduler.NewRedisScheduler(cli)
	} else {
		broker = jobs.NewMemBroker(nbWorkers)
		schder = scheduler.NewMemScheduler()
	}
	if err := broker.Start(jobs.GetWorkersList()); err != nil {
		return nil, err
	}
	if err := schder.Start(broker); err != nil {
		return nil, err
	}

	return utils.NewGroupShutdown(broker, schder, gopAgent{}), nil
}

// GetBroker returns the global job broker.
func GetBroker() jobs.Broker {
	if broker == nil {
		panic("Job system not initialized")
	}
	return broker
}

// GetScheduler returns the global job scheduler.
func GetScheduler() scheduler.Scheduler {
	if schder == nil {
		panic("Job system not initialized")
	}
	return schder
}
<|endoftext|>


<commit_before>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type AssertionTestCase struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewAsssertionTestCase(actual interface{}, instance interface{}) (newInstance AssertionTestCase) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	assert := func(testCases []AssertionTestCase, test func(actual interface{}, instance interface{})) {

		for _, testCase := range testCases {
			actualValue := testCase.Actual
			instanceValue := testCase.Instance
			testName := fmt.Sprintf("When Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
			if testCase.ActualStringNumber {
				testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER in this case", testName)
			}
			if testCase.InstanceStringNumber {
				testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER in this case", testName)
			}
			It(testName, func() {
				test(actualValue, instanceValue)
			})
		}
	}

	/*
	   Using this test function and the test cases we get a nice readable output.  If you run ginkgo -v here is an example of the output

	   Assertions Greater Than Succeeds
	     When Actual is of type int and Instance is of type string. Instance value is a STRING NUMBER
	*/

	var nilValue interface{}

	FContext("Greater Than", func() {

		Context("Succeeds", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(float64(1.1), nilValue),
				NewAsssertionTestCase(int(1), nilValue),
				NewAsssertionTestCase("1", nilValue),
				NewAsssertionTestCase("a", nilValue),
				NewAsssertionTestCase(float64(5), float64(1)),
				NewAsssertionTestCase(int(5), float64(1)),
				NewAsssertionTestCase("2.2", float64(1)),
				NewAsssertionTestCase(int(5), int(1)),
				NewAsssertionTestCase("5", int(1)),
				NewAsssertionTestCase("abc", "a"),
				NewAsssertionTestCase(float64(1.3), "1.2"),
				NewAsssertionTestCase(int(3), "1"),
				NewAsssertionTestCase("3.1", "2"),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				key := "some:key"
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			})
		})

		FContext("Fails", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(nilValue, nilValue),
				NewAsssertionTestCase(nilValue, int(5)),
				NewAsssertionTestCase(nilValue, "5.1"),
				NewAsssertionTestCase(nilValue, "fubar"),
				NewAsssertionTestCase(nilValue, float64(6.1)),
				NewAsssertionTestCase(float64(5.1), float64(6.1)),
				NewAsssertionTestCase(int(5), float64(6.1)),
				NewAsssertionTestCase("5", float64(6.1)),
				NewAsssertionTestCase(float64(3.1), int(6)),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(false))
				Expect(result["message"]).To(Equal(fmt.Sprintf("FAIL: %v is not greater %v", actualValue, instanceValue)))
			})
			/*
				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is float64 and Instance is int", func() {

				})

				PIt("When Actual is int and Instance is int", func() {

				})

				PIt("When Actual is string-number and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is float64", func() {

				})

				PIt("When Actual is string and Instance is string-number", func() {

				})

				PIt("When Actual is string and Instance is string", func() {

				})

				PIt("When Actual is float64 and Instance is string", func() {

				})

				PIt("When Actual is int and Instance is string", func() {

				})

				PIt("When Actual string-number int and Instance is string", func() {

				})
			*/
		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<commit_msg>Fails When Actual is of type int and Instance is of type int<commit_after>package assertions

import (
	"fmt"
	"strconv"

	"ci.guzzler.io/guzzler/corcel/core"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

type AssertionTestCase struct {
	Actual               interface{}
	Instance             interface{}
	ActualStringNumber   bool
	InstanceStringNumber bool
}

func NewAsssertionTestCase(actual interface{}, instance interface{}) (newInstance AssertionTestCase) {
	newInstance.Actual = actual
	newInstance.Instance = instance
	switch actualType := actual.(type) {
	case string:
		_, err := strconv.ParseFloat(actualType, 64)
		if err == nil {
			newInstance.ActualStringNumber = true
		}
	}
	switch instanceType := instance.(type) {
	case string:
		_, err := strconv.ParseFloat(instanceType, 64)
		if err == nil {
			newInstance.InstanceStringNumber = true
		}
	}
	return
}

var _ = FDescribe("Assertions", func() {

	key := "some:key"

	assert := func(testCases []AssertionTestCase, test func(actual interface{}, instance interface{})) {

		for _, testCase := range testCases {
			actualValue := testCase.Actual
			instanceValue := testCase.Instance
			testName := fmt.Sprintf("When Actual is of type %T and Instance is of type %T", actualValue, instanceValue)
			if testCase.ActualStringNumber {
				testName = fmt.Sprintf("%s. Actual value is a STRING NUMBER in this case", testName)
			}
			if testCase.InstanceStringNumber {
				testName = fmt.Sprintf("%s. Instance value is a STRING NUMBER in this case", testName)
			}
			It(testName, func() {
				test(actualValue, instanceValue)
			})
		}
	}

	/*
	   Using this test function and the test cases we get a nice readable output.  If you run ginkgo -v here is an example of the output

	   Assertions Greater Than Succeeds
	     When Actual is of type int and Instance is of type string. Instance value is a STRING NUMBER
	*/

	var nilValue interface{}

	FContext("Greater Than", func() {

		Context("Succeeds", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(float64(1.1), nilValue),
				NewAsssertionTestCase(int(1), nilValue),
				NewAsssertionTestCase("1", nilValue),
				NewAsssertionTestCase("a", nilValue),
				NewAsssertionTestCase(float64(5), float64(1)),
				NewAsssertionTestCase(int(5), float64(1)),
				NewAsssertionTestCase("2.2", float64(1)),
				NewAsssertionTestCase(int(5), int(1)),
				NewAsssertionTestCase("5", int(1)),
				NewAsssertionTestCase("abc", "a"),
				NewAsssertionTestCase(float64(1.3), "1.2"),
				NewAsssertionTestCase(int(3), "1"),
				NewAsssertionTestCase("3.1", "2"),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				key := "some:key"
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(true))
				Expect(result["message"]).To(BeNil())
			})
		})

		FContext("Fails", func() {
			var testCases = []AssertionTestCase{
				NewAsssertionTestCase(nilValue, nilValue),
				NewAsssertionTestCase(nilValue, int(5)),
				NewAsssertionTestCase(nilValue, "5.1"),
				NewAsssertionTestCase(nilValue, "fubar"),
				NewAsssertionTestCase(nilValue, float64(6.1)),
				NewAsssertionTestCase(float64(5.1), float64(6.1)),
				NewAsssertionTestCase(int(5), float64(6.1)),
				NewAsssertionTestCase("5", float64(6.1)),
				NewAsssertionTestCase(float64(3.1), int(6)),
				NewAsssertionTestCase(int(3), int(6)),
			}

			assert(testCases, func(actualValue interface{}, instanceValue interface{}) {
				executionResult := core.ExecutionResult{
					key: actualValue,
				}

				assertion := GreaterThanAssertion{
					Key:   key,
					Value: instanceValue,
				}

				result := assertion.Assert(executionResult)
				Expect(result["result"]).To(Equal(false))
				Expect(result["message"]).To(Equal(fmt.Sprintf("FAIL: %v is not greater %v", actualValue, instanceValue)))
			})
			/*
				PIt("When Actual is int and Instance is int", func() {

				})

				PIt("When Actual is string-number and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is int", func() {

				})

				PIt("When Actual is string and Instance is float64", func() {

				})

				PIt("When Actual is string and Instance is string-number", func() {

				})

				PIt("When Actual is string and Instance is string", func() {

				})

				PIt("When Actual is float64 and Instance is string", func() {

				})

				PIt("When Actual is int and Instance is string", func() {

				})

				PIt("When Actual string-number int and Instance is string", func() {

				})
			*/
		})

	})

	Context("Not Equal Assertion", func() {

		It("Succeeds", func() {
			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Fails", func() {
			executionResult := core.ExecutionResult{
				key: 7,
			}

			assertion := NotEqualAssertion{
				Key:   key,
				Value: 7,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 7 does match 7"))
		})
	})

	Context("Exact Assertion", func() {
		It("Exact Assertion Succeeds", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: expectedValue,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(true))
			Expect(result["message"]).To(BeNil())
		})

		It("Exact Assertion Fails", func() {
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: 8,
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: 8 does not match 7"))
		})

		//NOTHING is currently using the message when an assertion fails but we will need
		//it for when we put the errors into the report.  One of the edge cases with the message
		//is that say the actual value was a string "7" and the expected is an int 7.  The message
		//will not include the quotes so the message would read 7 does not equal 7 as opposed
		//to "7" does not equal 7.  Notice this is a type mismatch
		PIt("Exact Assertion Fails when actual and expected are different types", func() {
			key := "some:key"
			expectedValue := 7

			executionResult := core.ExecutionResult{
				key: "7",
			}

			assertion := ExactAssertion{
				Key:   key,
				Value: expectedValue,
			}

			result := assertion.Assert(executionResult)
			Expect(result["result"]).To(Equal(false))
			Expect(result["message"]).To(Equal("FAIL: \"7\" does not match 7"))
		})
	})

})
<|endoftext|>


<commit_before>package main

import "encoding/json"
import "flag"
import "io/ioutil"
import "log"
import "net/http"
import "net/url"
import "strings"
import "golang.org/x/net/context"
import "google.golang.org/grpc"
import pb "github.com/brotherlogic/cardserver/card"

func processAccessCode(client string, secret string, code string) []byte {
	urlv := "https://api.instagram.com/oauth/access_token"
	resp, err := http.PostForm(urlv, url.Values{"client_id": {client}, "client_secret": {secret}, "grant_type": {"authorization_code"}, "redirect_uri": {"http://localhost:8090"}, "code": {code}, "scope": {"public_content+likes"}})
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func processInstagramRating(card pb.Card) string {
	pictureID := card.Text
	urlv := "https://api.instagram.com/v1/media/" + pictureID + "/likes"
	return urlv
}

func visitURL(urlv string, accessToken string) []byte {
	resp, err := http.PostForm(urlv, url.Values{"access_token": {accessToken}})

	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func readCards(client string, secret string) {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	clientReader := pb.NewCardServiceClient(conn)
	list, err := clientReader.GetCards(context.Background(), &pb.Empty{})

	var accessCode []byte
	for _, card := range list.Cards {
		if card.Hash == "instagramauthresp" {
			code := card.Text[11:43]
			accessCode = processAccessCode(client, secret, code)
		}
	}

	//Write the access card out to a file
	if len(accessCode) > 0 {
		err = ioutil.WriteFile("access_code", accessCode, 0644)
		if err != nil {
			panic(err)
		}
	}
}

func writeAuthCard(client string) pb.CardList {
	authURL := "https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=http://localhost:8090&response_type=code"
	newAuthURL := strings.Replace(authURL, "CLIENT-ID", client, 1)
	card := pb.Card{}
	card.Text = newAuthURL
	card.Action = pb.Card_VISITURL
	card.Hash = "instagramauth"
	cards := pb.CardList{}
	cards.Cards = append(cards.Cards, &card)

	return cards
}

func writeInstagramCards(user string, accessCode string) pb.CardList {

	cards := pb.CardList{}

	urlv := "https://api.instagram.com/v1/users/" + user + "/media/recent/?access_token=" + accessCode + "&count=10"
	resp, err := http.Get(urlv)
	if err != nil {
		panic(nil)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(nil)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var pics []interface{}
	pics = dat["data"].([]interface{})

	for _, pico := range pics {
		pic := pico.(map[string]interface{})
		captionObj := pic["caption"]
		caption := ""
		if captionObj != nil {
			caption = captionObj.(map[string]interface{})["text"].(string)
		}
		var images map[string]interface{}
		images = pic["images"].(map[string]interface{})
		image := images["standard_resolution"].(map[string]interface{})["url"].(string)

		card := pb.Card{}
		card.Text = caption
		card.Image = image
		cards.Cards = append(cards.Cards, &card)
	}
	return cards
}

func main() {
	var clientID = flag.String("client", "", "Client ID for accessing Instagram")
	var secret = flag.String("secret", "", "Secret for accessing Instagram")
	flag.Parse()

	text, err := ioutil.ReadFile("access_code")

	if err != nil {
		cards := writeAuthCard(*clientID)
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

		readCards(*clientID, *secret)
	} else {
		var dat map[string]interface{}
		if err := json.Unmarshal([]byte(text), &dat); err != nil {
			log.Printf("Error unmarshalling %v with %v and %v", string(text), clientID, secret)
			panic(err)
		}
		if dat == nil || dat["access_token"] == nil {
			log.Printf("Cannot get access token: %v from %v", dat, string(text))
		}
		cards := writeInstagramCards("50987102", dat["access_token"].(string))
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

	}
}
<commit_msg>Improving debugging<commit_after>package main

import "encoding/json"
import "flag"
import "io/ioutil"
import "log"
import "net/http"
import "net/url"
import "strings"
import "golang.org/x/net/context"
import "google.golang.org/grpc"
import pb "github.com/brotherlogic/cardserver/card"

func processAccessCode(client string, secret string, code string) []byte {
	urlv := "https://api.instagram.com/oauth/access_token"
	resp, err := http.PostForm(urlv, url.Values{"client_id": {client}, "client_secret": {secret}, "grant_type": {"authorization_code"}, "redirect_uri": {"http://localhost:8090"}, "code": {code}, "scope": {"public_content+likes"}})
	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func processInstagramRating(card pb.Card) string {
	pictureID := card.Text
	urlv := "https://api.instagram.com/v1/media/" + pictureID + "/likes"
	return urlv
}

func visitURL(urlv string, accessToken string) []byte {
	resp, err := http.PostForm(urlv, url.Values{"access_token": {accessToken}})

	if err != nil {
		panic(err)
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)

	return body
}

func readCards(client string, secret string) {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	clientReader := pb.NewCardServiceClient(conn)
	list, err := clientReader.GetCards(context.Background(), &pb.Empty{})

	var accessCode []byte
	for _, card := range list.Cards {
		if card.Hash == "instagramauthresp" {
			code := card.Text[11:43]
			accessCode = processAccessCode(client, secret, code)
		}
	}

	//Write the access card out to a file
	if len(accessCode) > 0 {
		err = ioutil.WriteFile("access_code", accessCode, 0644)
		if err != nil {
			panic(err)
		}
	}
}

func writeAuthCard(client string) pb.CardList {
	authURL := "https://api.instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=http://localhost:8090&response_type=code"
	newAuthURL := strings.Replace(authURL, "CLIENT-ID", client, 1)
	card := pb.Card{}
	card.Text = newAuthURL
	card.Action = pb.Card_VISITURL
	card.Hash = "instagramauth"
	cards := pb.CardList{}
	cards.Cards = append(cards.Cards, &card)

	return cards
}

func writeInstagramCards(user string, accessCode string) pb.CardList {

	cards := pb.CardList{}

	urlv := "https://api.instagram.com/v1/users/" + user + "/media/recent/?access_token=" + accessCode + "&count=10"
	resp, err := http.Get(urlv)
	if err != nil {
		panic(nil)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(nil)
	}
	var dat map[string]interface{}
	err = json.Unmarshal([]byte(body), &dat)
	if err != nil {
		panic(nil)
	}

	var pics []interface{}
	pics = dat["data"].([]interface{})

	for _, pico := range pics {
		pic := pico.(map[string]interface{})
		captionObj := pic["caption"]
		caption := ""
		if captionObj != nil {
			caption = captionObj.(map[string]interface{})["text"].(string)
		}
		var images map[string]interface{}
		images = pic["images"].(map[string]interface{})
		image := images["standard_resolution"].(map[string]interface{})["url"].(string)

		card := pb.Card{}
		card.Text = caption
		card.Image = image
		cards.Cards = append(cards.Cards, &card)
	}
	return cards
}

func main() {
	var clientID = flag.String("client", "", "Client ID for accessing Instagram")
	var secret = flag.String("secret", "", "Secret for accessing Instagram")
	flag.Parse()

	text, err := ioutil.ReadFile("access_code")

	if err != nil {
		cards := writeAuthCard(*clientID)
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

		readCards(*clientID, *secret)
	} else {
		var dat map[string]interface{}
		if err := json.Unmarshal([]byte(text), &dat); err != nil {
			log.Printf("Error unmarshalling %v with %v and %v", string(text), clientID, secret)
			panic(err)
		}
		if dat == nil || dat["access_token"] == nil {
			log.Printf("Cannot get access token: %v from %v given $v,%v", dat, string(text), clientID, secret)
		}
		cards := writeInstagramCards("50987102", dat["access_token"].(string))
		conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())

		defer conn.Close()
		client := pb.NewCardServiceClient(conn)
		_, err = client.AddCards(context.Background(), &cards)
		if err != nil {
			log.Printf("Problem adding cards %v", err)
		}

	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go 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 span

import (
	"fmt"
	"go/token"
)

// Range represents a source code range in token.Pos form.
// It also carries the FileSet that produced the positions, so that it is
// self contained.
type Range struct {
	FileSet *token.FileSet
	Start   token.Pos
	End     token.Pos
}

// TokenConverter is a Converter backed by a token file set and file.
// It uses the file set methods to work out determine the conversions which
// make if fast and do not require the file contents.
type TokenConverter struct {
	fset *token.FileSet
	file *token.File
}

// NewRange creates a new Range from a FileSet and two positions.
// To represent a point pass a 0 as the end pos.
func NewRange(fset *token.FileSet, start, end token.Pos) Range {
	return Range{
		FileSet: fset,
		Start:   start,
		End:     end,
	}
}

// NewTokenConverter returns an implementation of Converter backed by a
// token.File.
func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
	return &TokenConverter{fset: fset, file: f}
}

// NewContentConverter returns an implementation of Converter for the
// given file content.
func NewContentConverter(filename string, content []byte) *TokenConverter {
	fset := token.NewFileSet()
	f := fset.AddFile(filename, -1, len(content))
	f.SetLinesForContent(content)
	return &TokenConverter{fset: fset, file: f}
}

// IsPoint returns true if the range represents a single point.
func (r Range) IsPoint() bool {
	return r.Start == r.End
}

// Span converts a Range to a Span that represents the Range.
// It will fill in all the members of the Span, calculating the line and column
// information.
func (r Range) Span() (Span, error) {
	f := r.FileSet.File(r.Start)
	if f == nil {
		return Span{}, fmt.Errorf("file not found in FileSet")
	}
	s := Span{v: span{URI: FileURI(f.Name())}}
	s.v.Start.Offset = f.Offset(r.Start)
	if r.End.IsValid() {
		s.v.End.Offset = f.Offset(r.End)
	}
	s.v.Start.clean()
	s.v.End.clean()
	s.v.clean()
	converter := NewTokenConverter(r.FileSet, f)
	return s.WithPosition(converter)
}

// Range converts a Span to a Range that represents the Span for the supplied
// File.
func (s Span) Range(converter *TokenConverter) (Range, error) {
	s, err := s.WithOffset(converter)
	if err != nil {
		return Range{}, err
	}
	return Range{
		FileSet: converter.fset,
		Start:   converter.file.Pos(s.Start().Offset()),
		End:     converter.file.Pos(s.End().Offset()),
	}, nil
}

func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
	//TODO: check offset fits in file
	pos := l.file.Pos(offset)
	p := l.fset.Position(pos)
	return p.Line, p.Column, nil
}

func (l *TokenConverter) ToOffset(line, col int) (int, error) {
	if line < 0 {
		return -1, fmt.Errorf("line is not valid")
	}
	lineMax := l.file.LineCount() + 1
	if line > lineMax {
		return -1, fmt.Errorf("line is beyond end of file")
	} else if line == lineMax {
		if col > 1 {
			return -1, fmt.Errorf("column is beyond end of file")
		}
		// at the end of the file, allowing for a trailing eol
		return l.file.Size(), nil
	}
	pos := lineStart(l.file, line)
	if !pos.IsValid() {
		return -1, fmt.Errorf("line is not in file")
	}
	// we assume that column is in bytes here, and that the first byte of a
	// line is at column 1
	pos += token.Pos(col - 1)
	return l.file.Offset(pos), nil
}
<commit_msg>internal/span: return error when on spans with invalid starts<commit_after>// Copyright 2019 The Go 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 span

import (
	"fmt"
	"go/token"
)

// Range represents a source code range in token.Pos form.
// It also carries the FileSet that produced the positions, so that it is
// self contained.
type Range struct {
	FileSet *token.FileSet
	Start   token.Pos
	End     token.Pos
}

// TokenConverter is a Converter backed by a token file set and file.
// It uses the file set methods to work out determine the conversions which
// make if fast and do not require the file contents.
type TokenConverter struct {
	fset *token.FileSet
	file *token.File
}

// NewRange creates a new Range from a FileSet and two positions.
// To represent a point pass a 0 as the end pos.
func NewRange(fset *token.FileSet, start, end token.Pos) Range {
	return Range{
		FileSet: fset,
		Start:   start,
		End:     end,
	}
}

// NewTokenConverter returns an implementation of Converter backed by a
// token.File.
func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
	return &TokenConverter{fset: fset, file: f}
}

// NewContentConverter returns an implementation of Converter for the
// given file content.
func NewContentConverter(filename string, content []byte) *TokenConverter {
	fset := token.NewFileSet()
	f := fset.AddFile(filename, -1, len(content))
	f.SetLinesForContent(content)
	return &TokenConverter{fset: fset, file: f}
}

// IsPoint returns true if the range represents a single point.
func (r Range) IsPoint() bool {
	return r.Start == r.End
}

// Span converts a Range to a Span that represents the Range.
// It will fill in all the members of the Span, calculating the line and column
// information.
func (r Range) Span() (Span, error) {
	f := r.FileSet.File(r.Start)
	if f == nil {
		return Span{}, fmt.Errorf("file not found in FileSet")
	}
	s := Span{v: span{URI: FileURI(f.Name())}}
	if !r.Start.IsValid() {
		return Span{}, fmt.Errorf("invalid position for start of range")
	}
	s.v.Start.Offset = f.Offset(r.Start)
	if r.End.IsValid() {
		s.v.End.Offset = f.Offset(r.End)
	}
	s.v.Start.clean()
	s.v.End.clean()
	s.v.clean()
	converter := NewTokenConverter(r.FileSet, f)
	return s.WithPosition(converter)
}

// Range converts a Span to a Range that represents the Span for the supplied
// File.
func (s Span) Range(converter *TokenConverter) (Range, error) {
	s, err := s.WithOffset(converter)
	if err != nil {
		return Range{}, err
	}
	return Range{
		FileSet: converter.fset,
		Start:   converter.file.Pos(s.Start().Offset()),
		End:     converter.file.Pos(s.End().Offset()),
	}, nil
}

func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
	//TODO: check offset fits in file
	pos := l.file.Pos(offset)
	p := l.fset.Position(pos)
	return p.Line, p.Column, nil
}

func (l *TokenConverter) ToOffset(line, col int) (int, error) {
	if line < 0 {
		return -1, fmt.Errorf("line is not valid")
	}
	lineMax := l.file.LineCount() + 1
	if line > lineMax {
		return -1, fmt.Errorf("line is beyond end of file")
	} else if line == lineMax {
		if col > 1 {
			return -1, fmt.Errorf("column is beyond end of file")
		}
		// at the end of the file, allowing for a trailing eol
		return l.file.Size(), nil
	}
	pos := lineStart(l.file, line)
	if !pos.IsValid() {
		return -1, fmt.Errorf("line is not in file")
	}
	// we assume that column is in bytes here, and that the first byte of a
	// line is at column 1
	pos += token.Pos(col - 1)
	return l.file.Offset(pos), nil
}
<|endoftext|>


<commit_before>// +build integration

package nakadi

import (
	"bytes"
	"encoding/json"
	"net/http"
	"testing"
	"time"

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

func TestIntegrationSubscriptionAPI_Get(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := helperCreateSubscriptions(t, eventType, &Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, &SubscriptionOptions{Retry: true})

	t.Run("fail not found", func(t *testing.T) {
		_, err := subAPI.Get("does-not-exist")

		require.Error(t, err)
		assert.Regexp(t, "does not exist", err)
	})

	t.Run("success", func(t *testing.T) {
		subscription, err := subAPI.Get(subscriptions[0].ID)

		require.NoError(t, err)
		assert.Equal(t, subscriptions[0].EventTypes, subscription.EventTypes)
		assert.Equal(t, subscriptions[0].OwningApplication, subscription.OwningApplication)
		assert.Equal(t, "default", subscription.ConsumerGroup)
	})
}

func TestIntegrationSubscriptionAPI_List(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := []*Subscription{
		{OwningApplication: "test-app", EventTypes: []string{eventType.Name}},
		{OwningApplication: "test-app2", EventTypes: []string{eventType.Name}}}
	subscriptions = helperCreateSubscriptions(t, eventType, subscriptions...)
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	fetched, err := subAPI.List()
	require.NoError(t, err)
	assert.Len(t, fetched, 2)
}

func TestIntegrationSubscriptionAPI_Create(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	helperCreateEventTypes(t, eventType)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	t.Run("fail invalid subscription", func(t *testing.T) {
		_, err := subAPI.Create(&Subscription{OwningApplication: "test-api"})
		require.Error(t, err)
		assert.Regexp(t, "unable to create subscription", err)
	})

	t.Run("success", func(t *testing.T) {
		created, err := subAPI.Create(&Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
		require.NoError(t, err)
		assert.Equal(t, "test-app", created.OwningApplication)
		assert.Equal(t, []string{eventType.Name}, created.EventTypes)

		helperDeleteSubscriptions(t, eventType, created)
	})
}

func TestIntegrationSubscriptionAPI_Delete(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := helperCreateSubscriptions(t, eventType, &Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	t.Run("fail not found", func(t *testing.T) {
		err := subAPI.Delete("does-not-exist")

		require.Error(t, err)
		assert.Regexp(t, "does not exist", err)
	})

	t.Run("success", func(t *testing.T) {
		err := subAPI.Delete(subscriptions[0].ID)

		require.NoError(t, err)
	})
}

func helperCreateSubscriptions(t *testing.T, eventType *EventType, subscription ...*Subscription) []*Subscription {
	helperCreateEventTypes(t, eventType)
	var subscriptions []*Subscription
	for _, eventType := range subscription {
		serialized, err := json.Marshal(eventType)
		require.NoError(t, err)
		response, err := http.DefaultClient.Post(defaultNakadiURL+"/subscriptions", "application/json", bytes.NewReader(serialized))
		require.NoError(t, err)
		require.Equal(t, http.StatusCreated, response.StatusCode)
		created := &Subscription{}
		err = json.NewDecoder(response.Body).Decode(created)
		require.NoError(t, err)
		subscriptions = append(subscriptions, created)
	}
	return subscriptions
}

func helperDeleteSubscriptions(t *testing.T, eventType *EventType, subscriptions ...*Subscription) {
	for _, sub := range subscriptions {
		request, err := http.NewRequest("DELETE", defaultNakadiURL+"/subscriptions/"+sub.ID, nil)
		require.NoError(t, err)
		_, err = http.DefaultClient.Do(request)
		require.NoError(t, err)
	}
	helperDeleteEventTypes(t, eventType)
}
<commit_msg>Added integration test for GetStats<commit_after>// +build integration

package nakadi

import (
	"bytes"
	"encoding/json"
	"net/http"
	"testing"
	"time"

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

func TestIntegrationSubscriptionAPI_Get(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := helperCreateSubscriptions(t, eventType, &Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, &SubscriptionOptions{Retry: true})

	t.Run("fail not found", func(t *testing.T) {
		_, err := subAPI.Get("does-not-exist")

		require.Error(t, err)
		assert.Regexp(t, "does not exist", err)
	})

	t.Run("success", func(t *testing.T) {
		subscription, err := subAPI.Get(subscriptions[0].ID)

		require.NoError(t, err)
		assert.Equal(t, subscriptions[0].EventTypes, subscription.EventTypes)
		assert.Equal(t, subscriptions[0].OwningApplication, subscription.OwningApplication)
		assert.Equal(t, "default", subscription.ConsumerGroup)
	})
}

func TestIntegrationSubscriptionAPI_List(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := []*Subscription{
		{OwningApplication: "test-app", EventTypes: []string{eventType.Name}},
		{OwningApplication: "test-app2", EventTypes: []string{eventType.Name}}}
	subscriptions = helperCreateSubscriptions(t, eventType, subscriptions...)
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	fetched, err := subAPI.List()
	require.NoError(t, err)
	assert.Len(t, fetched, 2)
}

func TestIntegrationSubscriptionAPI_Create(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	helperCreateEventTypes(t, eventType)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	t.Run("fail invalid subscription", func(t *testing.T) {
		_, err := subAPI.Create(&Subscription{OwningApplication: "test-api"})
		require.Error(t, err)
		assert.Regexp(t, "unable to create subscription", err)
	})

	t.Run("success", func(t *testing.T) {
		created, err := subAPI.Create(&Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
		require.NoError(t, err)
		assert.Equal(t, "test-app", created.OwningApplication)
		assert.Equal(t, []string{eventType.Name}, created.EventTypes)

		helperDeleteSubscriptions(t, eventType, created)
	})
}

func TestIntegrationSubscriptionAPI_Delete(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	subscriptions := helperCreateSubscriptions(t, eventType, &Subscription{OwningApplication: "test-app", EventTypes: []string{eventType.Name}})
	defer helperDeleteSubscriptions(t, eventType, subscriptions...)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, nil)

	t.Run("fail not found", func(t *testing.T) {
		err := subAPI.Delete("does-not-exist")

		require.Error(t, err)
		assert.Regexp(t, "does not exist", err)
	})

	t.Run("success", func(t *testing.T) {
		err := subAPI.Delete(subscriptions[0].ID)

		require.NoError(t, err)
	})
}

func TestIntegrationSubscriptionAPI_GetStats(t *testing.T) {
	eventType := &EventType{}
	helperLoadTestData(t, "event-type-create.json", eventType)
	expected := helperLoadTestData(t, "subscription-stats-integration.json", nil)
	helperCreateEventTypes(t, eventType)

	client := New(defaultNakadiURL, &ClientOptions{ConnectionTimeout: time.Second})
	subAPI := NewSubscriptionAPI(client, &SubscriptionOptions{Retry: true})

	subscription := &Subscription{OwningApplication: "test-app-stats", EventTypes: []string{eventType.Name}}
	subscription, err := subAPI.Create(subscription)
	require.NoError(t, err)
	defer subAPI.Delete(subscription.ID)

	stats, err := subAPI.GetStats(subscription.ID)
	require.NotNil(t, stats)
	require.NoError(t, err)
	assert.Equal(t, subscription.EventTypes[0], stats[0].EventType)

	actual, _ := json.Marshal(stats)
	t.Log(string(actual))
	assert.JSONEq(t, string(expected), string(actual))
}

func helperCreateSubscriptions(t *testing.T, eventType *EventType, subscription ...*Subscription) []*Subscription {
	helperCreateEventTypes(t, eventType)
	var subscriptions []*Subscription
	for _, eventType := range subscription {
		serialized, err := json.Marshal(eventType)
		require.NoError(t, err)
		response, err := http.DefaultClient.Post(defaultNakadiURL+"/subscriptions", "application/json", bytes.NewReader(serialized))
		require.NoError(t, err)
		require.Equal(t, http.StatusCreated, response.StatusCode)
		created := &Subscription{}
		err = json.NewDecoder(response.Body).Decode(created)
		require.NoError(t, err)
		subscriptions = append(subscriptions, created)
	}
	return subscriptions
}

func helperDeleteSubscriptions(t *testing.T, eventType *EventType, subscriptions ...*Subscription) {
	for _, sub := range subscriptions {
		request, err := http.NewRequest("DELETE", defaultNakadiURL+"/subscriptions/"+sub.ID, nil)
		require.NoError(t, err)
		_, err = http.DefaultClient.Do(request)
		require.NoError(t, err)
	}
	helperDeleteEventTypes(t, eventType)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Vanadium 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 templates

import "html/template"

var SelectCaveats = template.Must(selectCaveats.Parse(headPartial))

var selectCaveats = template.Must(template.New("bless").Parse(`<!doctype html>

  {{template "head" .}}

  <title>Add Blessing - Vanadium Identity Provider</title>

      Vanadium
      Identity Provider

      {{.Extension}}

    <form method="POST" id="caveats-form" name="input"
    action="{{.MacaroonURL}}" role="form">
      <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">

Add blessing

        This blessing allows the Vanadium Identity Provider to authorize your
        application's credentials and provides your application access to the
        data associated with your Google Account. Blessing names contain the
        email address associated with your Google Account, and will be visible
        to peers you connect to.

            Using Vanadium in production applications is discouraged at this
          time.

          During this preview, the

            blessing root

          may change without notice.

      Blessing name

        {{.BlessingName}}/{{.Extension}}/
        <input name="blessingExtension" type="text" placeholder="extension">
        <input type="hidden" id="timezoneOffset" name="timezoneOffset">

      Caveats

            Active until revoked

            Expires on

            Allowed methods are

            Allowed peers are

          <select name="caveat" class="caveats hidden">
            <option name="RevocationCaveat" value="RevocationCaveat"
            class="cavOption">Active until revoked</option>
            <option name="ExpiryCaveat" value="ExpiryCaveat"
            class="cavOption">Expires on</option>
            <option name="MethodCaveat" value="MethodCaveat"
            class="cavOption">Allowed methods are</option>
            <option name="PeerBlessingsCaveat" value="PeerBlessingsCaveat"
            class="cavOption">Allowed peers are</option>
          </select>

          <input type="text" class="caveatInput hidden"
            id="RevocationCaveat" name="RevocationCaveat">
          <input type="datetime-local" class="caveatInput expiry hidden"
            id="ExpiryCaveat" name="ExpiryCaveat">
          <input type="text" class="caveatInput hidden"
           id="MethodCaveat" name="MethodCaveat"
           placeholder="comma-separated method list">
          <input type="text" class="caveatInput hidden"
            id="PeerBlessingsCaveat" name="PeerBlessingsCaveat"
            placeholder="comma-separated blessing list">

          Add more caveats

        <button class="button-tertiary" id="cancel">Cancel</button>
        <button class="button-primary" type="submit">Bless</button>

        By clicking "Bless", you agree to the Google
        General Terms of Service,
        APIs Terms of Service,
        and Privacy Policy

    </form>

`))
<commit_msg>TBR: Fix identity server cancel button.<commit_after>// Copyright 2015 The Vanadium 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 templates

import "html/template"

var SelectCaveats = template.Must(selectCaveats.Parse(headPartial))

var selectCaveats = template.Must(template.New("bless").Parse(`<!doctype html>

  {{template "head" .}}

  <title>Add Blessing - Vanadium Identity Provider</title>

      Vanadium
      Identity Provider

      {{.Extension}}

    <form method="POST" id="caveats-form" name="input"
    action="{{.MacaroonURL}}" role="form">
      <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">

Add blessing

        This blessing allows the Vanadium Identity Provider to authorize your
        application's credentials and provides your application access to the
        data associated with your Google Account. Blessing names contain the
        email address associated with your Google Account, and will be visible
        to peers you connect to.

            Using Vanadium in production applications is discouraged at this
          time.

          During this preview, the

            blessing root

          may change without notice.

      Blessing name

        {{.BlessingName}}/{{.Extension}}/
        <input name="blessingExtension" type="text" placeholder="extension">
        <input type="hidden" id="timezoneOffset" name="timezoneOffset">

      Caveats

            Active until revoked

            Expires on

            Allowed methods are

            Allowed peers are

          <select name="caveat" class="caveats hidden">
            <option name="RevocationCaveat" value="RevocationCaveat"
            class="cavOption">Active until revoked</option>
            <option name="ExpiryCaveat" value="ExpiryCaveat"
            class="cavOption">Expires on</option>
            <option name="MethodCaveat" value="MethodCaveat"
            class="cavOption">Allowed methods are</option>
            <option name="PeerBlessingsCaveat" value="PeerBlessingsCaveat"
            class="cavOption">Allowed peers are</option>
          </select>

          <input type="text" class="caveatInput hidden"
            id="RevocationCaveat" name="RevocationCaveat">
          <input type="datetime-local" class="caveatInput expiry hidden"
            id="ExpiryCaveat" name="ExpiryCaveat">
          <input type="text" class="caveatInput hidden"
           id="MethodCaveat" name="MethodCaveat"
           placeholder="comma-separated method list">
          <input type="text" class="caveatInput hidden"
            id="PeerBlessingsCaveat" name="PeerBlessingsCaveat"
            placeholder="comma-separated blessing list">

          Add more caveats

        <button class="button-tertiary" id="cancel" type="button">Cancel</button>
        <button class="button-primary" type="submit">Bless</button>

        By clicking "Bless", you agree to the Google
        General Terms of Service,
        APIs Terms of Service,
        and Privacy Policy

    </form>

`))
<|endoftext|>


<commit_before>package sshprovider

import (
	"context"
	"io"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/sshforward"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// AgentConfig is the config for a single exposed SSH agent
type AgentConfig struct {
	ID    string
	Paths []string
}

// NewSSHAgentProvider creates a session provider that allows access to ssh agent
func NewSSHAgentProvider(confs []AgentConfig) (session.Attachable, error) {
	m := map[string]source{}
	for _, conf := range confs {
		if len(conf.Paths) == 0 || len(conf.Paths) == 1 && conf.Paths[0] == "" {
			conf.Paths = []string{os.Getenv("SSH_AUTH_SOCK")}
		}

		if conf.Paths[0] == "" {
			return nil, errors.Errorf("invalid empty ssh agent socket, make sure SSH_AUTH_SOCK is set")
		}

		src, err := toAgentSource(conf.Paths)
		if err != nil {
			return nil, err
		}
		if conf.ID == "" {
			conf.ID = sshforward.DefaultID
		}
		if _, ok := m[conf.ID]; ok {
			return nil, errors.Errorf("invalid duplicate ID %s", conf.ID)
		}
		m[conf.ID] = src
	}

	return &socketProvider{m: m}, nil
}

type source struct {
	agent  agent.Agent
	socket string
}

type socketProvider struct {
	m map[string]source
}

func (sp *socketProvider) Register(server *grpc.Server) {
	sshforward.RegisterSSHServer(server, sp)
}

func (sp *socketProvider) CheckAgent(ctx context.Context, req *sshforward.CheckAgentRequest) (*sshforward.CheckAgentResponse, error) {
	id := sshforward.DefaultID
	if req.ID != "" {
		id = req.ID
	}
	if _, ok := sp.m[id]; !ok {
		return &sshforward.CheckAgentResponse{}, errors.Errorf("unset ssh forward key %s", id)
	}
	return &sshforward.CheckAgentResponse{}, nil
}

func (sp *socketProvider) ForwardAgent(stream sshforward.SSH_ForwardAgentServer) error {
	id := sshforward.DefaultID

	opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object

	if v, ok := opts[sshforward.KeySSHID]; ok && len(v) > 0 && v[0] != "" {
		id = v[0]
	}

	src, ok := sp.m[id]
	if !ok {
		return errors.Errorf("unset ssh forward key %s", id)
	}

	var a agent.Agent

	if src.socket != "" {
		conn, err := net.DialTimeout("unix", src.socket, time.Second)
		if err != nil {
			return errors.Wrapf(err, "failed to connect to %s", src.socket)
		}

		a = &readOnlyAgent{agent.NewClient(conn)}
		defer conn.Close()
	} else {
		a = src.agent
	}

	s1, s2 := sockPair()

	eg, ctx := errgroup.WithContext(context.TODO())

	eg.Go(func() error {
		return agent.ServeAgent(a, s1)
	})

	eg.Go(func() error {
		defer s1.Close()
		return sshforward.Copy(ctx, s2, stream, nil)
	})

	return eg.Wait()
}

func toAgentSource(paths []string) (source, error) {
	var keys bool
	var socket string
	a := agent.NewKeyring()
	for _, p := range paths {
		if socket != "" {
			return source{}, errors.New("only single socket allowed")
		}
		fi, err := os.Stat(p)
		if err != nil {
			return source{}, errors.WithStack(err)
		}
		if fi.Mode()&os.ModeSocket > 0 {
			if keys {
				return source{}, errors.Errorf("invalid combination of keys and sockets")
			}
			socket = p
			continue
		}
		keys = true
		f, err := os.Open(p)
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to open %s", p)
		}
		dt, err := ioutil.ReadAll(&io.LimitedReader{R: f, N: 100 * 1024})
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to read %s", p)
		}

		k, err := ssh.ParseRawPrivateKey(dt)
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to parse %s", p) // TODO: prompt passphrase?
		}
		if err := a.Add(agent.AddedKey{PrivateKey: k}); err != nil {
			return source{}, errors.Wrapf(err, "failed to add %s to agent", p)
		}
	}

	if socket != "" {
		return source{socket: socket}, nil
	}

	return source{agent: a}, nil
}

func sockPair() (io.ReadWriteCloser, io.ReadWriteCloser) {
	pr1, pw1 := io.Pipe()
	pr2, pw2 := io.Pipe()
	return &sock{pr1, pw2, pw1}, &sock{pr2, pw1, pw2}
}

type sock struct {
	io.Reader
	io.Writer
	io.Closer
}

type readOnlyAgent struct {
	agent.Agent
}

func (a *readOnlyAgent) Add(_ agent.AddedKey) error {
	return errors.Errorf("adding new keys not allowed by buildkit")
}

func (a *readOnlyAgent) Remove(_ ssh.PublicKey) error {
	return errors.Errorf("removing keys not allowed by buildkit")
}

func (a *readOnlyAgent) RemoveAll() error {
	return errors.Errorf("removing keys not allowed by buildkit")
}

func (a *readOnlyAgent) Lock(_ []byte) error {
	return errors.Errorf("locking agent not allowed by buildkit")
}
<commit_msg>Inherit extended agent so we get modern sign hashes<commit_after>package sshprovider

import (
	"context"
	"io"
	"io/ioutil"
	"net"
	"os"
	"time"

	"github.com/moby/buildkit/session"
	"github.com/moby/buildkit/session/sshforward"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

// AgentConfig is the config for a single exposed SSH agent
type AgentConfig struct {
	ID    string
	Paths []string
}

// NewSSHAgentProvider creates a session provider that allows access to ssh agent
func NewSSHAgentProvider(confs []AgentConfig) (session.Attachable, error) {
	m := map[string]source{}
	for _, conf := range confs {
		if len(conf.Paths) == 0 || len(conf.Paths) == 1 && conf.Paths[0] == "" {
			conf.Paths = []string{os.Getenv("SSH_AUTH_SOCK")}
		}

		if conf.Paths[0] == "" {
			return nil, errors.Errorf("invalid empty ssh agent socket, make sure SSH_AUTH_SOCK is set")
		}

		src, err := toAgentSource(conf.Paths)
		if err != nil {
			return nil, err
		}
		if conf.ID == "" {
			conf.ID = sshforward.DefaultID
		}
		if _, ok := m[conf.ID]; ok {
			return nil, errors.Errorf("invalid duplicate ID %s", conf.ID)
		}
		m[conf.ID] = src
	}

	return &socketProvider{m: m}, nil
}

type source struct {
	agent  agent.Agent
	socket string
}

type socketProvider struct {
	m map[string]source
}

func (sp *socketProvider) Register(server *grpc.Server) {
	sshforward.RegisterSSHServer(server, sp)
}

func (sp *socketProvider) CheckAgent(ctx context.Context, req *sshforward.CheckAgentRequest) (*sshforward.CheckAgentResponse, error) {
	id := sshforward.DefaultID
	if req.ID != "" {
		id = req.ID
	}
	if _, ok := sp.m[id]; !ok {
		return &sshforward.CheckAgentResponse{}, errors.Errorf("unset ssh forward key %s", id)
	}
	return &sshforward.CheckAgentResponse{}, nil
}

func (sp *socketProvider) ForwardAgent(stream sshforward.SSH_ForwardAgentServer) error {
	id := sshforward.DefaultID

	opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object

	if v, ok := opts[sshforward.KeySSHID]; ok && len(v) > 0 && v[0] != "" {
		id = v[0]
	}

	src, ok := sp.m[id]
	if !ok {
		return errors.Errorf("unset ssh forward key %s", id)
	}

	var a agent.Agent

	if src.socket != "" {
		conn, err := net.DialTimeout("unix", src.socket, time.Second)
		if err != nil {
			return errors.Wrapf(err, "failed to connect to %s", src.socket)
		}

		a = &readOnlyAgent{agent.NewClient(conn)}
		defer conn.Close()
	} else {
		a = src.agent
	}

	s1, s2 := sockPair()

	eg, ctx := errgroup.WithContext(context.TODO())

	eg.Go(func() error {
		return agent.ServeAgent(a, s1)
	})

	eg.Go(func() error {
		defer s1.Close()
		return sshforward.Copy(ctx, s2, stream, nil)
	})

	return eg.Wait()
}

func toAgentSource(paths []string) (source, error) {
	var keys bool
	var socket string
	a := agent.NewKeyring()
	for _, p := range paths {
		if socket != "" {
			return source{}, errors.New("only single socket allowed")
		}
		fi, err := os.Stat(p)
		if err != nil {
			return source{}, errors.WithStack(err)
		}
		if fi.Mode()&os.ModeSocket > 0 {
			if keys {
				return source{}, errors.Errorf("invalid combination of keys and sockets")
			}
			socket = p
			continue
		}
		keys = true
		f, err := os.Open(p)
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to open %s", p)
		}
		dt, err := ioutil.ReadAll(&io.LimitedReader{R: f, N: 100 * 1024})
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to read %s", p)
		}

		k, err := ssh.ParseRawPrivateKey(dt)
		if err != nil {
			return source{}, errors.Wrapf(err, "failed to parse %s", p) // TODO: prompt passphrase?
		}
		if err := a.Add(agent.AddedKey{PrivateKey: k}); err != nil {
			return source{}, errors.Wrapf(err, "failed to add %s to agent", p)
		}
	}

	if socket != "" {
		return source{socket: socket}, nil
	}

	return source{agent: a}, nil
}

func sockPair() (io.ReadWriteCloser, io.ReadWriteCloser) {
	pr1, pw1 := io.Pipe()
	pr2, pw2 := io.Pipe()
	return &sock{pr1, pw2, pw1}, &sock{pr2, pw1, pw2}
}

type sock struct {
	io.Reader
	io.Writer
	io.Closer
}

type readOnlyAgent struct {
	agent.ExtendedAgent
}

func (a *readOnlyAgent) Add(_ agent.AddedKey) error {
	return errors.Errorf("adding new keys not allowed by buildkit")
}

func (a *readOnlyAgent) Remove(_ ssh.PublicKey) error {
	return errors.Errorf("removing keys not allowed by buildkit")
}

func (a *readOnlyAgent) RemoveAll() error {
	return errors.Errorf("removing keys not allowed by buildkit")
}

func (a *readOnlyAgent) Lock(_ []byte) error {
	return errors.Errorf("locking agent not allowed by buildkit")
}
<|endoftext|>


<commit_before>package storyactions

import (
	"fmt"
	"strings"
	"time"

	"github.com/fragmenta/query"
	"github.com/fragmenta/server/schedule"

	"github.com/kennygrant/gohackernews/src/lib/twitter"
	"github.com/kennygrant/gohackernews/src/stories"
)

// TweetTopStory tweets the top story
func TweetTopStory(context schedule.Context) {
	context.Log("Sending top story tweet")

	// Get the top story which has not been tweeted yet, newer than 1 day (we don't look at older stories)
	q := stories.Popular().Limit(1).Order("rank desc, points desc, id desc")

	// Don't fetch old stories - at some point soon this can come down to 1 day
	// as all older stories will have been tweeted
	// For now omit this as we have a backlog of old unposted stories
	// q.Where("created_at > current_timestamp - interval '60 days'")

	// Don't fetch stories that have already been tweeted
	q.Where("tweeted_at IS NULL")

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		context.Logf("#error getting top story tweet %s", err)
		return
	}

	if len(results) > 0 {
		story := results[0]

		// Link to the primary url for this type of story
		url := story.PrimaryURL()

		if strings.HasPrefix(url, "/") {
			// FIXME use url from config
			url = "https://golangnews.com" + url
		}

		tweet := fmt.Sprintf("%s #golang %s", story.Name, url)

		// If the tweet will be too long for twitter, use GN url
		if len(tweet) > 140 {
			tweet = fmt.Sprintf("%s #golang %s", story.Name, story.URLShow())
		}

		context.Logf("#info sending tweet:%s", tweet)

		_, err := twitter.Tweet(tweet)
		if err != nil {
			context.Logf("#error tweeting top story %s", err)
			return
		}

		// Record that this story has been tweeted in db
		params := map[string]string{"tweeted_at": query.TimeString(time.Now().UTC())}
		err = story.Update(params)
		if err != nil {
			context.Logf("#error updating top story tweet %s", err)
			return
		}
	} else {
		context.Logf("#warn no top story found for tweet")
	}

}
<commit_msg>Fix bug - tweeting long stories results in non-functional url<commit_after>package storyactions

import (
	"fmt"
	"strings"
	"time"

	"github.com/fragmenta/query"
	"github.com/fragmenta/server/schedule"

	"github.com/kennygrant/gohackernews/src/lib/twitter"
	"github.com/kennygrant/gohackernews/src/stories"
)

// TweetTopStory tweets the top story
func TweetTopStory(context schedule.Context) {
	context.Log("Sending top story tweet")

	// Get the top story which has not been tweeted yet, newer than 1 day (we don't look at older stories)
	q := stories.Popular().Limit(1).Order("rank desc, points desc, id desc")

	// Don't fetch old stories - at some point soon this can come down to 1 day
	// as all older stories will have been tweeted
	// For now omit this as we have a backlog of old unposted stories
	// q.Where("created_at > current_timestamp - interval '60 days'")

	// Don't fetch stories that have already been tweeted
	q.Where("tweeted_at IS NULL")

	// Fetch the stories
	results, err := stories.FindAll(q)
	if err != nil {
		context.Logf("#error getting top story tweet %s", err)
		return
	}

	if len(results) > 0 {
		story := results[0]

		TweetStory(context, story)
	} else {
		context.Logf("#warn no top story found for tweet")
	}

}

// TweetStory tweets the given story
func TweetStory(context schedule.Context, story *stories.Story) {

	// Base url from config
	baseURL := context.Config("root_url")

	// Link to the primary url for this type of story
	url := story.PrimaryURL()

	// Check for relative urls
	if strings.HasPrefix(url, "/") {
		url = baseURL + url
	}

	tweet := fmt.Sprintf("%s #golang %s", story.Name, url)

	// If the tweet will be too long for twitter, use GN url
	if len(tweet) > 140 {
		tweet = fmt.Sprintf("%s #golang %s", story.Name, baseURL+story.URLShow())
	}

	context.Logf("#info sending tweet:%s", tweet)

	_, err := twitter.Tweet(tweet)
	if err != nil {
		context.Logf("#error tweeting top story %s", err)
		return
	}

	// Record that this story has been tweeted in db
	params := map[string]string{"tweeted_at": query.TimeString(time.Now().UTC())}
	err = story.Update(params)
	if err != nil {
		context.Logf("#error updating top story tweet %s", err)
		return
	}

}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

// TODO
package fs

import "time"

type EntryType uint32

const (
  TypeFile = iota
	TypeDirectory
	TypeSymlink
)

// DirectoryEntry gives enough information to reconstruct a single entry within
// a backed up directory.
type DirectoryEntry struct {
	Type EntryType

	// The permission bits for this entry.
	Permissions uint32

	// The name of this entry within its directory.
	Name string

	// The modification time of this entry.
	MTime time.Time

	// Zero or more blobs that make up a regular file's contents, to be
	// concatenated in order.
	BlobHashes []blob.Hash
}
<commit_msg>Fixed build errors in fs.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

// TODO
package fs

import "github.com/jacobsa/comeback/blob"
import "time"

type EntryType uint32

const (
  TypeFile = iota
	TypeDirectory
	TypeSymlink
)

// DirectoryEntry gives enough information to reconstruct a single entry within
// a backed up directory.
type DirectoryEntry struct {
	Type EntryType

	// The permission bits for this entry.
	Permissions uint32

	// The name of this entry within its directory.
	Name string

	// The modification time of this entry.
	MTime time.Time

	// The scores of zero or more blobs that make up a regular file's contents,
	// to be concatenated in order.
	Scores []blob.Score
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"regexp"

	. "github.com/bytbox/gabon/irc"
)

var server = "irc.freenode.net:6667"
var channels = []string{
	//"#go-nuts",
	"#go-bots",
}

func main() {
	flag.Parse()

	log.Printf("Connecting to %s", server)
	conn, err := Connect(server, "gabon", "The Go Nut")
	if err != nil {
		log.Fatal("ERR: ", err)
	}

	conn.JoinChannels(channels)

	messages := conn.Listen()

	for m := range messages {
		switch m.Kind {
		case MSG_NOTICE:
			log.Printf("NOTICE\t%s", m.Text)
		case MSG_PRIVMSG:
			handlePriv(conn, m.From, m.To, m.Text)
		default:
			log.Print("WARNING: unhandled message kind")
		}
	}
}

func handlePriv(c *Client, from, to, text string) {
	replyTo := to
	if to[0] != '#' { // not sent to a channel - reply directly to user
		replyTo = from
	} else {
		// exit if this message isn't for us. TODO match ^! ?
		if r, _ := regexp.MatchString("gabon[,:] ", text); !r {
			return
		} else {
			_, text, _ = nextField(text)
		}
	}
	r := getReply(text)
	if r != "" {
		c.PrivMsg(replyTo, "Hi")
	}
}

func getReply(text string) string {
	return ""
}
<commit_msg>Replying to unrecognized commands correctly<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"regexp"
	"strings"

	. "github.com/bytbox/gabon/irc"
)

var server = "irc.freenode.net:6667"
var channels = []string{
	//"#go-nuts",
	"#go-bots",
}

func main() {
	flag.Parse()

	log.Printf("Connecting to %s", server)
	conn, err := Connect(server, "gabon", "The Go Nut")
	if err != nil {
		log.Fatal("ERR: ", err)
	}

	conn.JoinChannels(channels)

	messages := conn.Listen()

	for m := range messages {
		switch m.Kind {
		case MSG_NOTICE:
			log.Printf("NOTICE\t%s", m.Text)
		case MSG_PRIVMSG:
			handlePriv(conn, getNick(m.From), m.To, m.Text)
		default:
			log.Print("WARNING: unhandled message kind")
		}
	}
}

func getNick(s string) string {
	a := strings.SplitN(s, "!", 2)
	return a[0]
}

func handlePriv(c *Client, from, to, text string) {
	replyTo := to
	if to[0] != '#' { // not sent to a channel - reply directly to user
		replyTo = from
	} else {
		// exit if this message isn't for us. TODO match ^! ?
		if r, _ := regexp.MatchString("gabon[,:] ", text); !r {
			return
		} else {
			_, text, _ = nextField(text)
		}
	}
	r, e := getReply(text)
	if r != "" {
		if e {
			r = fmt.Sprintf("%s, %s", from, r)
		}
		c.PrivMsg(replyTo, r)
	}
}

func getReply(text string) (string, bool) {
	f, rest, _ := nextField(text)
	if len(f) < 2 {
		return "", true
	}
	switch f[0] {
	case '@':
		r, e := getReply(rest)
		if !e {
			return f[1:] + ", " + r, false
		}
		return r, true
	}
	return "I don't understand "+f, true
}
<|endoftext|>


<commit_before>package guidserver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/mgutz/ansi"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const gardenDeploymentIP = "10.244.15.2"

const amazingRubyServer = `
require 'webrick'
require 'json'

server = WEBrick::HTTPServer.new :Port => 8080

registered = []
files = {}

server.mount_proc '/register' do |req, res|
  registered << req.body.chomp
  res.status = 200
end

server.mount_proc '/registrations' do |req, res|
  res.body = JSON.generate(registered)
end

trap('INT') {
  server.shutdown
}

server.start
`

type Server struct {
	gardenClient garden.Client
	container    garden.Container

	addr string
}

func Start(helperRootfs string, gardenClient garden.Client) *Server {
	container, err := gardenClient.Create(garden.ContainerSpec{
		RootFSPath: helperRootfs,
		GraceTime:  time.Hour,
	})
	Ω(err).ShouldNot(HaveOccurred())

	_, err = container.Run(garden.ProcessSpec{
		Path: "ruby",
		Args: []string{"-e", amazingRubyServer},
		User: "root",
	}, garden.ProcessIO{
		Stdout: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[o]", "green"), ansi.Color("[guid server]", "magenta")),
			ginkgo.GinkgoWriter,
		),
		Stderr: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server]", "magenta")),
			ginkgo.GinkgoWriter,
		),
	})
	Ω(err).ShouldNot(HaveOccurred())

	hostPort, _, err := container.NetIn(0, 8080)
	Ω(err).ShouldNot(HaveOccurred())

	addr := fmt.Sprintf("%s:%d", gardenDeploymentIP, hostPort)

	Eventually(func() (int, error) {
		curl, err := container.Run(garden.ProcessSpec{
			Path: "curl",
			Args: []string{"-s", "-f", "http://127.0.0.1:8080/registrations"},
			User: "root",
		}, garden.ProcessIO{
			Stdout: gexec.NewPrefixedWriter(
				fmt.Sprintf("%s%s ", ansi.Color("[o]", "green"), ansi.Color("[guid server polling]", "magenta")),
				ginkgo.GinkgoWriter,
			),
			Stderr: gexec.NewPrefixedWriter(
				fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server polling]", "magenta")),
				ginkgo.GinkgoWriter,
			),
		})
		Ω(err).ShouldNot(HaveOccurred())

		return curl.Wait()
	}, 2).Should(Equal(0))

	return &Server{
		gardenClient: gardenClient,
		addr:         addr,
	}
}

func (server *Server) Stop() {
	server.gardenClient.Destroy(server.container.Handle())
}

func (server *Server) CurlCommand() string {
	return fmt.Sprintf("curl -XPOST http://%s/register -d @-", server.addr)
}

func (server *Server) ReportingGuids() []string {
	outBuf := new(bytes.Buffer)

	curl, err := server.container.Run(garden.ProcessSpec{
		Path: "curl",
		Args: []string{"-s", "-f", "http://127.0.0.1:8080/registrations"},
		User: "root",
	}, garden.ProcessIO{
		Stdout: outBuf,
		Stderr: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server polling]", "magenta")),
			ginkgo.GinkgoWriter,
		),
	})
	Ω(err).ShouldNot(HaveOccurred())

	Ω(curl.Wait()).Should(Equal(0))

	var responses []string
	err = json.NewDecoder(outBuf).Decode(&responses)
	Ω(err).ShouldNot(HaveOccurred())

	return responses
}
<commit_msg>fix missing container field<commit_after>package guidserver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"

	"github.com/cloudfoundry-incubator/garden"
	"github.com/mgutz/ansi"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const gardenDeploymentIP = "10.244.15.2"

const amazingRubyServer = `
require 'webrick'
require 'json'

server = WEBrick::HTTPServer.new :Port => 8080

registered = []
files = {}

server.mount_proc '/register' do |req, res|
  registered << req.body.chomp
  res.status = 200
end

server.mount_proc '/registrations' do |req, res|
  res.body = JSON.generate(registered)
end

trap('INT') {
  server.shutdown
}

server.start
`

type Server struct {
	gardenClient garden.Client

	container garden.Container
	addr      string
}

func Start(helperRootfs string, gardenClient garden.Client) *Server {
	container, err := gardenClient.Create(garden.ContainerSpec{
		RootFSPath: helperRootfs,
		GraceTime:  time.Hour,
	})
	Ω(err).ShouldNot(HaveOccurred())

	_, err = container.Run(garden.ProcessSpec{
		Path: "ruby",
		Args: []string{"-e", amazingRubyServer},
		User: "root",
	}, garden.ProcessIO{
		Stdout: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[o]", "green"), ansi.Color("[guid server]", "magenta")),
			ginkgo.GinkgoWriter,
		),
		Stderr: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server]", "magenta")),
			ginkgo.GinkgoWriter,
		),
	})
	Ω(err).ShouldNot(HaveOccurred())

	hostPort, _, err := container.NetIn(0, 8080)
	Ω(err).ShouldNot(HaveOccurred())

	addr := fmt.Sprintf("%s:%d", gardenDeploymentIP, hostPort)

	Eventually(func() (int, error) {
		curl, err := container.Run(garden.ProcessSpec{
			Path: "curl",
			Args: []string{"-s", "-f", "http://127.0.0.1:8080/registrations"},
			User: "root",
		}, garden.ProcessIO{
			Stdout: gexec.NewPrefixedWriter(
				fmt.Sprintf("%s%s ", ansi.Color("[o]", "green"), ansi.Color("[guid server polling]", "magenta")),
				ginkgo.GinkgoWriter,
			),
			Stderr: gexec.NewPrefixedWriter(
				fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server polling]", "magenta")),
				ginkgo.GinkgoWriter,
			),
		})
		Ω(err).ShouldNot(HaveOccurred())

		return curl.Wait()
	}, 2).Should(Equal(0))

	return &Server{
		gardenClient: gardenClient,

		container: container,
		addr:      addr,
	}
}

func (server *Server) Stop() {
	server.gardenClient.Destroy(server.container.Handle())
}

func (server *Server) CurlCommand() string {
	return fmt.Sprintf("curl -XPOST http://%s/register -d @-", server.addr)
}

func (server *Server) ReportingGuids() []string {
	outBuf := new(bytes.Buffer)

	curl, err := server.container.Run(garden.ProcessSpec{
		Path: "curl",
		Args: []string{"-s", "-f", "http://127.0.0.1:8080/registrations"},
		User: "root",
	}, garden.ProcessIO{
		Stdout: outBuf,
		Stderr: gexec.NewPrefixedWriter(
			fmt.Sprintf("%s%s ", ansi.Color("[e]", "red+bright"), ansi.Color("[guid server polling]", "magenta")),
			ginkgo.GinkgoWriter,
		),
	})
	Ω(err).ShouldNot(HaveOccurred())

	Ω(curl.Wait()).Should(Equal(0))

	var responses []string
	err = json.NewDecoder(outBuf).Decode(&responses)
	Ω(err).ShouldNot(HaveOccurred())

	return responses
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// GHToc GitHub TOC
type GHToc []string

// Print print TOC to the console
func (toc *GHToc) Print() {
	for _, tocItem := range *toc {
		fmt.Println(tocItem)
	}
	fmt.Println()
}

// GHDoc GitHub document
type GHDoc struct {
	Path     string
	AbsPaths bool
	Depth    int
	Escape   bool
	GhToken  string
	Indent   int
	Debug    bool
	html     string
}

// NewGHDoc create GHDoc
func NewGHDoc(Path string, AbsPaths bool, Depth int, Escape bool, Token string, Indent int, Debug bool) *GHDoc {
	return &GHDoc{Path, AbsPaths, Depth, Escape, Token, Indent, Debug, ""}
}

func (doc *GHDoc) d(msg string) {
	if doc.Debug {
		log.Println(msg)
	}
}

// IsRemoteFile checks if path is for remote file or not
func (doc *GHDoc) IsRemoteFile() bool {
	u, err := url.Parse(doc.Path)
	if err != nil || u.Scheme == "" {
		doc.d("IsRemoteFile: false")
		return false
	}
	doc.d("IsRemoteFile: true")
	return true
}

// Convert2HTML downloads remote file
func (doc *GHDoc) Convert2HTML() error {
	doc.d("Convert2HTML: start.")
	defer doc.d("Convert2HTML: done.")

	if doc.IsRemoteFile() {
		htmlBody, ContentType, err := httpGet(doc.Path)
		doc.d("Convert2HTML: remote file. content-type: " + ContentType)
		if err != nil {
			return err
		}

		// if not a plain text - return the result (should be html)
		if strings.Split(ContentType, ";")[0] != "text/plain" {
			doc.html = string(htmlBody)
			return nil
		}

		// if remote file's content is a plain text
		// we need to convert it to html
		tmpfile, err := ioutil.TempFile("", "ghtoc-remote-txt")
		if err != nil {
			return err
		}
		defer tmpfile.Close()
		doc.Path = tmpfile.Name()
		if err = ioutil.WriteFile(tmpfile.Name(), htmlBody, 0644); err != nil {
			return err
		}
	}
	doc.d("Convert2HTML: local file: " + doc.Path)
	if _, err := os.Stat(doc.Path); os.IsNotExist(err) {
		return err
	}
	htmlBody, err := ConvertMd2Html(doc.Path, doc.GhToken)
	doc.d("Convert2HTML: converted to html, size: " + strconv.Itoa(len(htmlBody)))
	if err != nil {
		return err
	}
	// doc.d("Convert2HTML: " + htmlBody)
	doc.html = htmlBody
	return nil
}

// GrabToc gets TOC from html
func (doc *GHDoc) GrabToc() *GHToc {
	doc.d("GrabToc: start, html size: " + strconv.Itoa(len(doc.html)))
	defer doc.d("GrabToc: done.")

	re := `(?si)<h(?P<num>[1-6])>\s*` +
		`<a\s*id="user-content-[^"]*"\s*class="anchor"\s*` +
		`href="(?P<href>[^"]*)"[^>]*>\s*` +
		`.*?</a>(?P<name>.*?)</h`
	r := regexp.MustCompile(re)
	listIndentation := generateListIndentation(doc.Indent)

	toc := GHToc{}
	minHeaderNum := 6
	var groups []map[string]string
	doc.d("GrabToc: matching ...")
	for idx, match := range r.FindAllStringSubmatch(doc.html, -1) {
		doc.d("GrabToc: match #" + strconv.Itoa(idx) + " ...")
		group := make(map[string]string)
		// fill map for groups
		for i, name := range r.SubexpNames() {
			if i == 0 || name == "" {
				continue
			}
			doc.d("GrabToc: process group: " + name + " ...")
			group[name] = removeStuf(match[i])
		}
		// update minimum header number
		n, _ := strconv.Atoi(group["num"])
		if n < minHeaderNum {
			minHeaderNum = n
		}
		groups = append(groups, group)
	}

	var tmpSection string
	doc.d("GrabToc: processing groups ...")
	for _, group := range groups {
		// format result
		n, _ := strconv.Atoi(group["num"])
		if doc.Depth > 0 && n > doc.Depth {
			continue
		}

		link := group["href"]
		if doc.AbsPaths {
			link = doc.Path + link
		}

		tmpSection = removeStuf(group["name"])
		if doc.Escape {
			tmpSection = EscapeSpecChars(tmpSection)
		}
		tocItem := strings.Repeat(listIndentation(), n-minHeaderNum) + "* " +
			"[" + tmpSection + "]" +
			"(" + link + ")"
		//fmt.Println(tocItem)
		toc = append(toc, tocItem)
	}

	return &toc
}

// GetToc return GHToc for a document
func (doc *GHDoc) GetToc() *GHToc {
	if err := doc.Convert2HTML(); err != nil {
		log.Fatal(err)
		return nil
	}
	return doc.GrabToc()
}
<commit_msg>Save full response from GH if --debug is set<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// GHToc GitHub TOC
type GHToc []string

// Print print TOC to the console
func (toc *GHToc) Print() {
	for _, tocItem := range *toc {
		fmt.Println(tocItem)
	}
	fmt.Println()
}

// GHDoc GitHub document
type GHDoc struct {
	Path     string
	AbsPaths bool
	Depth    int
	Escape   bool
	GhToken  string
	Indent   int
	Debug    bool
	html     string
}

// NewGHDoc create GHDoc
func NewGHDoc(Path string, AbsPaths bool, Depth int, Escape bool, Token string, Indent int, Debug bool) *GHDoc {
	return &GHDoc{Path, AbsPaths, Depth, Escape, Token, Indent, Debug, ""}
}

func (doc *GHDoc) d(msg string) {
	if doc.Debug {
		log.Println(msg)
	}
}

// IsRemoteFile checks if path is for remote file or not
func (doc *GHDoc) IsRemoteFile() bool {
	u, err := url.Parse(doc.Path)
	if err != nil || u.Scheme == "" {
		doc.d("IsRemoteFile: false")
		return false
	}
	doc.d("IsRemoteFile: true")
	return true
}

// Convert2HTML downloads remote file
func (doc *GHDoc) Convert2HTML() error {
	doc.d("Convert2HTML: start.")
	defer doc.d("Convert2HTML: done.")

	if doc.IsRemoteFile() {
		htmlBody, ContentType, err := httpGet(doc.Path)
		doc.d("Convert2HTML: remote file. content-type: " + ContentType)
		if err != nil {
			return err
		}

		// if not a plain text - return the result (should be html)
		if strings.Split(ContentType, ";")[0] != "text/plain" {
			doc.html = string(htmlBody)
			return nil
		}

		// if remote file's content is a plain text
		// we need to convert it to html
		tmpfile, err := ioutil.TempFile("", "ghtoc-remote-txt")
		if err != nil {
			return err
		}
		defer tmpfile.Close()
		doc.Path = tmpfile.Name()
		if err = ioutil.WriteFile(tmpfile.Name(), htmlBody, 0644); err != nil {
			return err
		}
	}
	doc.d("Convert2HTML: local file: " + doc.Path)
	if _, err := os.Stat(doc.Path); os.IsNotExist(err) {
		return err
	}
	htmlBody, err := ConvertMd2Html(doc.Path, doc.GhToken)
	doc.d("Convert2HTML: converted to html, size: " + strconv.Itoa(len(htmlBody)))
	if err != nil {
		return err
	}
	if doc.Debug {
		htmlFile := doc.Path + ".debug.html"
		doc.d("Convert2HTML: write html file: " + htmlFile)
		ioutil.WriteFile(htmlFile, []byte(htmlBody), 0644)
	}
	doc.html = htmlBody
	return nil
}

// GrabToc gets TOC from html
func (doc *GHDoc) GrabToc() *GHToc {
	doc.d("GrabToc: start, html size: " + strconv.Itoa(len(doc.html)))
	defer doc.d("GrabToc: done.")

	re := `(?si)<h(?P<num>[1-6])>\s*` +
		`<a\s*id="user-content-[^"]*"\s*class="anchor"\s*` +
		`href="(?P<href>[^"]*)"[^>]*>\s*` +
		`.*?</a>(?P<name>.*?)</h`
	r := regexp.MustCompile(re)
	listIndentation := generateListIndentation(doc.Indent)

	toc := GHToc{}
	minHeaderNum := 6
	var groups []map[string]string
	doc.d("GrabToc: matching ...")
	for idx, match := range r.FindAllStringSubmatch(doc.html, -1) {
		doc.d("GrabToc: match #" + strconv.Itoa(idx) + " ...")
		group := make(map[string]string)
		// fill map for groups
		for i, name := range r.SubexpNames() {
			if i == 0 || name == "" {
				continue
			}
			doc.d("GrabToc: process group: " + name + ": " + match[i] + " ...")
			group[name] = removeStuf(match[i])
		}
		// update minimum header number
		n, _ := strconv.Atoi(group["num"])
		if n < minHeaderNum {
			minHeaderNum = n
		}
		groups = append(groups, group)
	}

	var tmpSection string
	doc.d("GrabToc: processing groups ...")
	for _, group := range groups {
		// format result
		n, _ := strconv.Atoi(group["num"])
		if doc.Depth > 0 && n > doc.Depth {
			continue
		}

		link, _ := url.QueryUnescape(group["href"])
		if doc.AbsPaths {
			link = doc.Path + link
		}

		tmpSection = removeStuf(group["name"])
		if doc.Escape {
			tmpSection = EscapeSpecChars(tmpSection)
		}
		tocItem := strings.Repeat(listIndentation(), n-minHeaderNum) + "* " +
			"[" + tmpSection + "]" +
			"(" + link + ")"
		//fmt.Println(tocItem)
		toc = append(toc, tocItem)
	}

	return &toc
}

// GetToc return GHToc for a document
func (doc *GHDoc) GetToc() *GHToc {
	if err := doc.Convert2HTML(); err != nil {
		log.Fatal(err)
		return nil
	}
	return doc.GrabToc()
}
<|endoftext|>


<commit_before>package discovery

import (
	"fmt"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/fsouza/go-dockerclient"
	"github.com/newrelic/sidecar/service"
	"github.com/relistan/go-director"
)

const (
	CACHE_DRAIN_INTERVAL = 10 * time.Minute // Drain the cache every 10 mins
)

type DockerClient interface {
	InspectContainer(id string) (*docker.Container, error)
	ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error)
	AddEventListener(listener chan<- *docker.APIEvents) error
	RemoveEventListener(listener chan *docker.APIEvents) error
	Ping() error
}

type DockerDiscovery struct {
	events         chan *docker.APIEvents       // Where events are announced to us
	endpoint       string                       // The Docker endpoint to talk to
	services       []*service.Service           // The list of services we know about
	ClientProvider func() (DockerClient, error) // Return the client we'll use to connect
	containerCache map[string]*docker.Container // Cache of inspected containers
	sync.RWMutex                                // Reader/Writer lock
}

func NewDockerDiscovery(endpoint string) *DockerDiscovery {
	discovery := DockerDiscovery{
		endpoint:       endpoint,
		events:         make(chan *docker.APIEvents),
		containerCache: make(map[string]*docker.Container),
	}

	// Default to our own method for returning this
	discovery.ClientProvider = discovery.getDockerClient

	return &discovery
}

func (d *DockerDiscovery) getDockerClient() (DockerClient, error) {
	if d.endpoint != "" {
		client, err := docker.NewClient(d.endpoint)
		if err != nil {
			return nil, err
		}

		return client, nil
	}

	client, err := docker.NewClientFromEnv()
	if err != nil {
		return nil, err
	}
	return client, nil
}

// HealthCheck looks up a health check using Docker container labels to
// pass the type of check and the arguments to pass to it.
func (d *DockerDiscovery) HealthCheck(svc *service.Service) (string, string) {
	container, err := d.inspectContainer(svc)
	if err != nil {
		return "", ""
	}

	return container.Config.Labels["HealthCheck"], container.Config.Labels["HealthCheckArgs"]
}

func (d *DockerDiscovery) inspectContainer(svc *service.Service) (*docker.Container, error) {
	// If we have it cached, return it!
	if container, ok := d.containerCache[svc.ID]; ok {
		return container, nil
	}

	// New connection every time
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return nil, err
	}

	container, err := client.InspectContainer(svc.ID)
	if err != nil {
		log.Errorf("Error inspecting container : %v\n", svc.ID)
		return nil, err
	}

	// Cache it for next time
	d.containerCache[svc.ID] = container

	return container, nil
}

// The main loop, poll for containers continuously.
func (d *DockerDiscovery) Run(looper director.Looper) {
	watchEventsQuit := make(chan bool)
	processEventsQuit := make(chan bool)
	drainCacheQuit := make(chan bool)

	go d.watchEvents(watchEventsQuit)
	go d.processEvents(processEventsQuit)
	go d.drainCache(drainCacheQuit)

	go func() {
		// Loop around fetching the whole container list
		looper.Loop(func() error {
			d.getContainers()
			return nil
		})

		// Propagate quit channel message
		go func() { watchEventsQuit <- true }()
		go func() { processEventsQuit <- true }()
		go func() { drainCacheQuit <- true }()
	}()
}

func (d *DockerDiscovery) Services() []service.Service {
	d.RLock()
	defer d.RUnlock()

	svcList := make([]service.Service, len(d.services))

	for i, svc := range d.services {
		svcList[i] = *svc
	}

	return svcList
}

func (d *DockerDiscovery) getContainers() {
	// New connection every time
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return
	}

	containers, err := client.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		return
	}

	d.Lock()
	defer d.Unlock()

	// Temporary set to track if we have seen a container (for cache pruning)
	containerMap := make(map[string]interface{})

	// Build up the service list, and prepare to prune the containerCache
	d.services = make([]*service.Service, 0, len(containers))
	for _, container := range containers {
		// Skip services that are purposely excluded from discovery.
		if container.Labels["SidecarDiscover"] == "false" {
			continue
		}

		svc := service.ToService(&container)
		d.services = append(d.services, &svc)
		containerMap[svc.ID] = true
	}

	d.pruneContainerCache(containerMap)
}

// Loop through the current cache and remove anything that has disappeared
func (d *DockerDiscovery) pruneContainerCache(liveContainers map[string]interface{}) {
	for id, _ := range d.containerCache {
		if _, ok := liveContainers[id]; !ok {
			delete(d.containerCache, id)
		}
	}
}

func (d *DockerDiscovery) watchEvents(quit chan bool) {
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return
	}
	client.AddEventListener(d.events)

	// Health check the connection and set it back up when it goes away.
	for {

		err := client.Ping()
		if err != nil {
			log.Warn("Lost connection to Docker, re-connecting")
			client.RemoveEventListener(d.events)
			d.events = make(chan *docker.APIEvents) // RemoveEventListener closes it

			client, err = docker.NewClient(d.endpoint)
			if err == nil {
				client.AddEventListener(d.events)
			} else {
				log.Error("Can't reconnect to Docker!")
			}
		}

		select {
		case <-quit:
			return
		default:
		}

		time.Sleep(SLEEP_INTERVAL)
	}
}

func (d *DockerDiscovery) handleEvent(event docker.APIEvents) {
	// We're only worried about stopping containers
	if event.Status == "die" || event.Status == "stop" {
		d.Lock()
		defer d.Unlock()

		for i, service := range d.services {
			if len(event.ID) < 12 {
				continue
			}
			if event.ID[:12] == service.ID {
				log.Printf("Deleting %s based on event\n", service.ID)
				// Delete the entry in the slice
				d.services[i] = nil
				d.services = append(d.services[:i], d.services[i+1:]...)
				// Once we found a match, return
				return
			}
		}
	}
}

func (d *DockerDiscovery) processEvents(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		default:
		}

		event := <-d.events
		if event == nil {
			// This usually happens because of a Docker restart.
			// Sleep, let us reconnect in the background, then loop.
			time.Sleep(SLEEP_INTERVAL)
			continue
		}
		log.Debugf("Event: %#v\n", event)
		d.handleEvent(*event)
	}
}

// On a timed basis, drain the containerCache
func (d *DockerDiscovery) drainCache(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		case <-time.After(CACHE_DRAIN_INTERVAL):
			log.Print("Draining containerCache")
			d.Lock()
			// Make a new one, leave the old one for GC
			d.containerCache = make(
				map[string]*docker.Container,
				len(d.services),
			)
			d.Unlock()
		}
	}
}
<commit_msg>Fix imports.<commit_after>package discovery

import (
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/fsouza/go-dockerclient"
	"github.com/newrelic/sidecar/service"
	"github.com/relistan/go-director"
)

const (
	CACHE_DRAIN_INTERVAL = 10 * time.Minute // Drain the cache every 10 mins
)

type DockerClient interface {
	InspectContainer(id string) (*docker.Container, error)
	ListContainers(opts docker.ListContainersOptions) ([]docker.APIContainers, error)
	AddEventListener(listener chan<- *docker.APIEvents) error
	RemoveEventListener(listener chan *docker.APIEvents) error
	Ping() error
}

type DockerDiscovery struct {
	events         chan *docker.APIEvents       // Where events are announced to us
	endpoint       string                       // The Docker endpoint to talk to
	services       []*service.Service           // The list of services we know about
	ClientProvider func() (DockerClient, error) // Return the client we'll use to connect
	containerCache map[string]*docker.Container // Cache of inspected containers
	sync.RWMutex                                // Reader/Writer lock
}

func NewDockerDiscovery(endpoint string) *DockerDiscovery {
	discovery := DockerDiscovery{
		endpoint:       endpoint,
		events:         make(chan *docker.APIEvents),
		containerCache: make(map[string]*docker.Container),
	}

	// Default to our own method for returning this
	discovery.ClientProvider = discovery.getDockerClient

	return &discovery
}

func (d *DockerDiscovery) getDockerClient() (DockerClient, error) {
	if d.endpoint != "" {
		client, err := docker.NewClient(d.endpoint)
		if err != nil {
			return nil, err
		}

		return client, nil
	}

	client, err := docker.NewClientFromEnv()
	if err != nil {
		return nil, err
	}
	return client, nil
}

// HealthCheck looks up a health check using Docker container labels to
// pass the type of check and the arguments to pass to it.
func (d *DockerDiscovery) HealthCheck(svc *service.Service) (string, string) {
	container, err := d.inspectContainer(svc)
	if err != nil {
		return "", ""
	}

	return container.Config.Labels["HealthCheck"], container.Config.Labels["HealthCheckArgs"]
}

func (d *DockerDiscovery) inspectContainer(svc *service.Service) (*docker.Container, error) {
	// If we have it cached, return it!
	if container, ok := d.containerCache[svc.ID]; ok {
		return container, nil
	}

	// New connection every time
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return nil, err
	}

	container, err := client.InspectContainer(svc.ID)
	if err != nil {
		log.Errorf("Error inspecting container : %v\n", svc.ID)
		return nil, err
	}

	// Cache it for next time
	d.containerCache[svc.ID] = container

	return container, nil
}

// The main loop, poll for containers continuously.
func (d *DockerDiscovery) Run(looper director.Looper) {
	watchEventsQuit := make(chan bool)
	processEventsQuit := make(chan bool)
	drainCacheQuit := make(chan bool)

	go d.watchEvents(watchEventsQuit)
	go d.processEvents(processEventsQuit)
	go d.drainCache(drainCacheQuit)

	go func() {
		// Loop around fetching the whole container list
		looper.Loop(func() error {
			d.getContainers()
			return nil
		})

		// Propagate quit channel message
		go func() { watchEventsQuit <- true }()
		go func() { processEventsQuit <- true }()
		go func() { drainCacheQuit <- true }()
	}()
}

func (d *DockerDiscovery) Services() []service.Service {
	d.RLock()
	defer d.RUnlock()

	svcList := make([]service.Service, len(d.services))

	for i, svc := range d.services {
		svcList[i] = *svc
	}

	return svcList
}

func (d *DockerDiscovery) getContainers() {
	// New connection every time
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return
	}

	containers, err := client.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		return
	}

	d.Lock()
	defer d.Unlock()

	// Temporary set to track if we have seen a container (for cache pruning)
	containerMap := make(map[string]interface{})

	// Build up the service list, and prepare to prune the containerCache
	d.services = make([]*service.Service, 0, len(containers))
	for _, container := range containers {
		// Skip services that are purposely excluded from discovery.
		if container.Labels["SidecarDiscover"] == "false" {
			continue
		}

		svc := service.ToService(&container)
		d.services = append(d.services, &svc)
		containerMap[svc.ID] = true
	}

	d.pruneContainerCache(containerMap)
}

// Loop through the current cache and remove anything that has disappeared
func (d *DockerDiscovery) pruneContainerCache(liveContainers map[string]interface{}) {
	for id, _ := range d.containerCache {
		if _, ok := liveContainers[id]; !ok {
			delete(d.containerCache, id)
		}
	}
}

func (d *DockerDiscovery) watchEvents(quit chan bool) {
	client, err := d.ClientProvider()
	if err != nil {
		log.Errorf("Error when creating Docker client: %s\n", err.Error())
		return
	}
	client.AddEventListener(d.events)

	// Health check the connection and set it back up when it goes away.
	for {

		err := client.Ping()
		if err != nil {
			log.Warn("Lost connection to Docker, re-connecting")
			client.RemoveEventListener(d.events)
			d.events = make(chan *docker.APIEvents) // RemoveEventListener closes it

			client, err = docker.NewClient(d.endpoint)
			if err == nil {
				client.AddEventListener(d.events)
			} else {
				log.Error("Can't reconnect to Docker!")
			}
		}

		select {
		case <-quit:
			return
		default:
		}

		time.Sleep(SLEEP_INTERVAL)
	}
}

func (d *DockerDiscovery) handleEvent(event docker.APIEvents) {
	// We're only worried about stopping containers
	if event.Status == "die" || event.Status == "stop" {
		d.Lock()
		defer d.Unlock()

		for i, service := range d.services {
			if len(event.ID) < 12 {
				continue
			}
			if event.ID[:12] == service.ID {
				log.Printf("Deleting %s based on event\n", service.ID)
				// Delete the entry in the slice
				d.services[i] = nil
				d.services = append(d.services[:i], d.services[i+1:]...)
				// Once we found a match, return
				return
			}
		}
	}
}

func (d *DockerDiscovery) processEvents(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		default:
		}

		event := <-d.events
		if event == nil {
			// This usually happens because of a Docker restart.
			// Sleep, let us reconnect in the background, then loop.
			time.Sleep(SLEEP_INTERVAL)
			continue
		}
		log.Debugf("Event: %#v\n", event)
		d.handleEvent(*event)
	}
}

// On a timed basis, drain the containerCache
func (d *DockerDiscovery) drainCache(quit chan bool) {
	for {
		select {
		case <-quit:
			return
		case <-time.After(CACHE_DRAIN_INTERVAL):
			log.Print("Draining containerCache")
			d.Lock()
			// Make a new one, leave the old one for GC
			d.containerCache = make(
				map[string]*docker.Container,
				len(d.services),
			)
			d.Unlock()
		}
	}
}
<|endoftext|>