<commit_before>package goku

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type TestJob struct {
	Foo int
	Bar string
}

func (tj TestJob) Name() string {
	return "test_job"
}

var tjWasCalled bool

func (tj TestJob) Execute() error {
	tjWasCalled = true
	return nil
}

func TestBroker(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	hostport := "127.0.0.1:6379"
	queueName := "goku_test"

	broker, err := NewBroker(BrokerConfig{
		Hostport:     hostport,
		Timeout:      time.Second,
		DefaultQueue: queueName,
	})

	require.NoError(err)

	job := TestJob{
		Foo: 4,
		Bar: "sup",
	}

	err = broker.Run(job)
	assert.NoError(err)

	conn, err := redis.Dial("tcp", hostport)
	require.NoError(err)

	jsn, err := redis.Bytes(conn.Do("LPOP", queueName))
	assert.NoError(err)

	var m map[string]interface{}
	json.Unmarshal(jsn, &m)
	args := m["A"].(map[string]interface{})

	assert.Equal(m["N"], job.Name())
	assert.Equal(args["Foo"], float64(4))
	assert.Equal(args["Bar"], "sup")
}

func TestWorker(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	queue := "goku_test"
	hostport := "127.0.0.1:6379"

	config := WorkerConfig{
		NumWorkers: 1,
		Queues:     []string{queue},
		Hostport:   hostport,
		Timeout:    time.Second,
	}

	opts := WorkerPoolOptions{
		Failure: nil,
		Jobs: []Job{
			TestJob{},
		},
	}

	// start the worker
	wp, err := NewWorkerPool(config, opts)
	assert.NoError(err)
	wp.Start()

	tjWasCalled = false

	// schedule the job from the broker
	broker, err := NewBroker(BrokerConfig{
		Hostport:     hostport,
		Timeout:      time.Second,
		DefaultQueue: queue,
	})

	require.NoError(err)

	job := TestJob{
		Foo: 4,
		Bar: "sup",
	}

	err = broker.Run(job)
	assert.NoError(err)
	time.Sleep(time.Second) // give workers some time to pull the job out of the queue
	wp.Stop()

	assert.True(tjWasCalled)
}
<commit_msg>More tests<commit_after>package goku

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

type TestJob struct {
	Foo int
	Bar string
}

func (tj TestJob) Name() string {
	return "test_job"
}

var tjWasCalled bool

func (tj TestJob) Execute() error {
	tjWasCalled = true
	return nil
}

func TestBroker(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	hostport := "127.0.0.1:6379"
	queueName := "goku_test"

	broker, err := NewBroker(BrokerConfig{
		Hostport:     hostport,
		Timeout:      time.Second,
		DefaultQueue: queueName,
	})

	require.NoError(err)

	job := TestJob{
		Foo: 4,
		Bar: "sup",
	}

	err = broker.Run(job)
	assert.NoError(err)

	conn, err := redis.Dial("tcp", hostport)
	require.NoError(err)

	jsn, err := redis.Bytes(conn.Do("LPOP", queueName))
	assert.NoError(err)

	var m map[string]interface{}
	json.Unmarshal(jsn, &m)
	args := m["A"].(map[string]interface{})

	assert.Equal(m["N"], job.Name())
	assert.Equal(args["Foo"], float64(4))
	assert.Equal(args["Bar"], "sup")
}

func TestWorker(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	queue := "goku_test"
	hostport := "127.0.0.1:6379"

	config := WorkerConfig{
		NumWorkers: 1,
		Queues:     []string{queue},
		Hostport:   hostport,
		Timeout:    time.Second,
	}

	opts := WorkerPoolOptions{
		Failure: nil,
		Jobs: []Job{
			TestJob{},
		},
	}

	// start the worker
	wp, err := NewWorkerPool(config, opts)
	assert.NoError(err)
	wp.Start()

	tjWasCalled = false

	// schedule the job from the broker
	broker, err := NewBroker(BrokerConfig{
		Hostport:     hostport,
		Timeout:      time.Second,
		DefaultQueue: queue,
	})

	require.NoError(err)

	job := TestJob{
		Foo: 4,
		Bar: "sup",
	}

	err = broker.Run(job)
	assert.NoError(err)
	time.Sleep(time.Second) // give workers some time to pull the job out of the queue
	wp.Stop()

	assert.True(tjWasCalled)
}

func TestBrokerBadConfig(t *testing.T) {
	_, err := NewBroker(BrokerConfig{})
	assert.Error(t, err)
}

func TestWorkerPoolBadConfig(t *testing.T) {
	_, err := NewWorkerPool(WorkerConfig{}, WorkerPoolOptions{})
	assert.Error(t, err)
}

func TestConfigureBadConfig(t *testing.T) {
	err := Configure(BrokerConfig{})
	assert.Error(t, err)
}

func TestConfigureGoodConfig(t *testing.T) {
	hostport := "127.0.0.1:6379"
	queueName := "goku_test"
	err := Configure(BrokerConfig{
		Hostport:     hostport,
		Timeout:      time.Second,
		DefaultQueue: queueName,
	})
	assert.NoError(t, err)
}
<|endoftext|>


<commit_before>package main

/*
Consider all integer combinations of ab for 2 ≤ a ≤ 5 and 2 ≤ b ≤ 5:
2**2=4,  2**3=8,   2**4=16,  2**5=32
3**2=9,  3**3=27,  3**4=81,  3**5=243 4**2=16, 4**3=64,  4**4=256, 4**5=1024
5**2=25, 5**3=125, 5**4=625, 5**5=3125
If they are then placed in numerical order, with any repeats removed, we get the following sequence of 15 distinct terms:

4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125

How many distinct terms are in the sequence generated by ab for 2 ≤ a ≤ 100 and 2 ≤ b ≤ 100?
*/

import (
	"fmt"

	"github.com/fenglyu/projecteuler/golang/common"
)

func main() {
	//	ar := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}

	for i := 2; i < 101; i++ {
		res := common.DivisorV3(i)
		if len(res) < 2 {
			continue
		}

		fmt.Println("num: ", i, res)
	}

}

/*

2**(2*2), 4 ** 2
2**(2*2*2), 4 ** 4
2**(2*2*2*2), 4**8, 16**2
2**(2*2*2*2*2), 4**16, 16**8,
2**(2*2*2*2*2*2), 4**32, 16**16,

2**(3*2), 8**2
2**(3*2*2), 8**4
2**(3*2*2*2), 8**8, 64**4
2**(3*2*2*2), 8**16, 64**8
2**(3*2*2*2*2), 8**32, 64**16

2**(3*3), 8**3
2**(3*3*3), 8**9
2**(3*3*3*3), 8**27

2**(3*3*2), 8**6, 64**3
2**(3*3*2*2), 8**12, 64**6

2**(5*2), 32**2
2**(5*2*2), 32**4
2**(5*2*2*2), 32**4

2**(5*3*2)), 32**6
2**(5*3*2*2)), 32**12

2**(5*5), 32**5

2**(7*2), 4**7
2**(7*2*2), 4**16, 16**7
2**(7*2*2*2), 4**28， 16**14

2**(9*2)  4**9
2**(9*2*2) 4**18
*/
<commit_msg>update<commit_after>package main

/*
Consider all integer combinations of ab for 2 ≤ a ≤ 5 and 2 ≤ b ≤ 5:
2**2=4,  2**3=8,   2**4=16,  2**5=32
3**2=9,  3**3=27,  3**4=81,  3**5=243 4**2=16, 4**3=64,  4**4=256, 4**5=1024
5**2=25, 5**3=125, 5**4=625, 5**5=3125
If they are then placed in numerical order, with any repeats removed, we get the following sequence of 15 distinct terms:

4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125

How many distinct terms are in the sequence generated by ab for 2 ≤ a ≤ 100 and 2 ≤ b ≤ 100?
*/

import (
	"fmt"

	"github.com/fenglyu/projecteuler/golang/common"
)

func main() {
	//	ar := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}

	m := make(map[int][]int)
	for i := 2; i < 101; i++ {
		res := common.DivisorV3(i)
		if len(res) < 2 {
			continue
		}
		//	fmt.Println("num: ", i, res)
		m[i] = res
	}

	//	fmt.Println("map: ", m)
	for j := 2; j <= 10; j++ {
		for k, v := range m {

		}
	}
}

/*

2**(2*2), 4 ** 2
2**(2*2*2), 4 ** 4
2**(2*2*2*2), 4**8, 16**2
2**(2*2*2*2*2), 4**16, 16**8,
2**(2*2*2*2*2*2), 4**32, 16**16,

2**(3*2), 8**2
2**(3*2*2), 8**4
2**(3*2*2*2), 8**8, 64**4
2**(3*2*2*2), 8**16, 64**8
2**(3*2*2*2*2), 8**32, 64**16

2**(3*3), 8**3
2**(3*3*3), 8**9
2**(3*3*3*3), 8**27

2**(3*3*2), 8**6, 64**3
2**(3*3*2*2), 8**12, 64**6

2**(5*2), 32**2
2**(5*2*2), 32**4
2**(5*2*2*2), 32**4

2**(5*3*2)), 32**6
2**(5*3*2*2)), 32**12

2**(5*5), 32**5

2**(7*2), 4**7
2**(7*2*2), 4**16, 16**7
2**(7*2*2*2), 4**28， 16**14

2**(9*2)  4**9
2**(9*2*2) 4**18
*/
<|endoftext|>


<commit_before>package golangsdk

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func GetRetailer(retailer string) (Retailer, error) {
	switch retailer {
	case "amazon":
		return Amazon, nil
	case "amazon_uk":
		return AmazonUK, nil
	case "amazon_ca":
		return AmazonCA, nil
	case "amazon_mx":
		return AmazonMX, nil
	case "walmart":
		return Walmart, nil
	case "aliexpress":
		return Aliexpress, nil
	default:
		return Amazon, fmt.Errorf("Invalid retailer string")
	}
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool             `json:"available"`
	Addon                bool             `json:"addon"`
	Condition            string           `json:"condition"`
	ShippingOptions      []ShippingOption `json:"shipping_options"`
	HandlingDays         HandlingDays     `json:"handling_days"`
	PrimeOnly            bool             `json:"prime_only"`
	MarketplaceFulfilled bool             `json:"marketplace_fulfilled"`
	Currency             string           `json:"currency"`
	Seller               Seller           `json:"seller"`
	BuyBoxWinner         bool             `json:"buy_box_winner"`
	International        bool             `json:"international"`
	OfferId              string           `json:"offer_id"`
	Price                int              `json:"price"`
}

type ShippingOption struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	PostDescription    string              `json:"post_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          json.Number         `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Brand              string              `json:"brand"`
	MPN                string              `json:"mpn"`
	Images             []string            `json:"images"`
	FeatureBullets     []string            `json:"feature_bullets"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message string `json:"message"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	Priority  int           `json:"priority"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

type ZincError struct {
	ErrorMessage string            `json:"error"`
	Data         ErrorDataResponse `json:"data"`
}

func (z ZincError) Error() string {
	return z.ErrorMessage
}

func SimpleError(errorStr string) ZincError {
	return ZincError{ErrorMessage: errorStr}
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v/offers?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductOffersResponse
	if err := z.SendGetRequest(requestPath, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	if options.Priority != 0 {
		values.Set("priority", strconv.Itoa(options.Priority))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductDetailsResponse
	if err := z.SendGetRequest(requestPath, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func cleanRespBody(respBody []byte) []byte {
	str := string(respBody)
	i := strings.Index(str, "HTTP/1.1 200 OK")
	if i == -1 {
		return respBody
	}
	return []byte(str[:i])
}

func (z Zinc) SendGetRequest(requestPath string, timeout time.Duration, resp interface{}) error {
	httpReq, err := http.NewRequest("GET", requestPath, nil)
	if err != nil {
		return err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	httpResp, err := client.Do(httpReq)
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()
	respBody, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return err
	}
	cleanedBody := cleanRespBody(respBody)
	if err := json.Unmarshal(cleanedBody, resp); err != nil {
		log.Printf("[Golangsdk] Unable to unmarshal response request_path=%v body=%v", requestPath, string(cleanedBody))
		return SimpleError(err.Error())
	}
	return nil
}
<commit_msg>Add order response to golangsdk.<commit_after>package golangsdk

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func GetRetailer(retailer string) (Retailer, error) {
	switch retailer {
	case "amazon":
		return Amazon, nil
	case "amazon_uk":
		return AmazonUK, nil
	case "amazon_ca":
		return AmazonCA, nil
	case "amazon_mx":
		return AmazonMX, nil
	case "walmart":
		return Walmart, nil
	case "aliexpress":
		return Aliexpress, nil
	default:
		return Amazon, fmt.Errorf("Invalid retailer string")
	}
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type OrderResponse struct {
	Type             string            `json:"_type"`
	Code             string            `json:"code"`
	Data             ErrorDataResponse `json:"data"`
	ErrorMessage     string            `json:"message"`
	PriceComponents  PriceComponents   `json:"price_components"`
	MerchantOrderIds []MerchantOrderId `json:"merchant_order_ids"`
	Tracking         []Tracking        `json:"tracking"`
}

type PriceComponents struct {
	Shipping int `json:"shipping"`
	Subtotal int `json:"subtotal"`
	Tax      int `json:"tax"`
	Total    int `json:"total"`
}

type MerchantOrderId struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	Merchant        string    `json:"merchant"`
	Account         string    `json:"account"`
	PlacedAt        time.Time `json:"placed_at"`
}

type Tracking struct {
	MerchantOrderId string    `json:"merchant_order_id"`
	ObtainedAt      time.Time `json:"obtained_at"`
	Carrier         string    `json:"carrier"`
	TrackingNumber  string    `json:"tracking_number"`
	ProductIds      []string  `json:"product_ids"`
	TrackingURL     string    `json:"tracking_url"`
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool             `json:"available"`
	Addon                bool             `json:"addon"`
	Condition            string           `json:"condition"`
	ShippingOptions      []ShippingOption `json:"shipping_options"`
	HandlingDays         HandlingDays     `json:"handling_days"`
	PrimeOnly            bool             `json:"prime_only"`
	MarketplaceFulfilled bool             `json:"marketplace_fulfilled"`
	Currency             string           `json:"currency"`
	Seller               Seller           `json:"seller"`
	BuyBoxWinner         bool             `json:"buy_box_winner"`
	International        bool             `json:"international"`
	OfferId              string           `json:"offer_id"`
	Price                int              `json:"price"`
}

type ShippingOption struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	PostDescription    string              `json:"post_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          json.Number         `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Brand              string              `json:"brand"`
	MPN                string              `json:"mpn"`
	Images             []string            `json:"images"`
	FeatureBullets     []string            `json:"feature_bullets"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message string `json:"message"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	Priority  int           `json:"priority"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

type ZincError struct {
	ErrorMessage string            `json:"error"`
	Data         ErrorDataResponse `json:"data"`
}

func (z ZincError) Error() string {
	return z.ErrorMessage
}

func SimpleError(errorStr string) ZincError {
	return ZincError{ErrorMessage: errorStr}
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v/offers?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductOffersResponse
	if err := z.SendGetRequest(requestPath, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	if options.Priority != 0 {
		values.Set("priority", strconv.Itoa(options.Priority))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	var resp ProductDetailsResponse
	if err := z.SendGetRequest(requestPath, options.Timeout, &resp); err != nil {
		return nil, SimpleError(err.Error())
	}
	if resp.Status == "failed" {
		msg := fmt.Sprintf("Zinc API returned status 'failed' data=%+v", resp.Data)
		return &resp, ZincError{ErrorMessage: msg, Data: resp.Data}
	}
	return &resp, nil
}

func cleanRespBody(respBody []byte) []byte {
	str := string(respBody)
	i := strings.Index(str, "HTTP/1.1 200 OK")
	if i == -1 {
		return respBody
	}
	return []byte(str[:i])
}

func (z Zinc) SendGetRequest(requestPath string, timeout time.Duration, resp interface{}) error {
	httpReq, err := http.NewRequest("GET", requestPath, nil)
	if err != nil {
		return err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	httpResp, err := client.Do(httpReq)
	if err != nil {
		return err
	}
	defer httpResp.Body.Close()
	respBody, err := ioutil.ReadAll(httpResp.Body)
	if err != nil {
		return err
	}
	cleanedBody := cleanRespBody(respBody)
	if err := json.Unmarshal(cleanedBody, resp); err != nil {
		log.Printf("[Golangsdk] Unable to unmarshal response request_path=%v body=%v", requestPath, string(cleanedBody))
		return SimpleError(err.Error())
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
  "encoding/json"
	"io/ioutil"
  "os"
	"strings"
	"github.com/adabei/goldenbot/greeter"
	"github.com/adabei/goldenbot/rcon"
	"github.com/adabei/goldenbot/tails"
	"github.com/adabei/goldenbot/votes"
	"github.com/adabei/goldenbot/advert"
)

type GoldenConfig struct {
  Address string
  RCONPassword string
  LogfilePath string
  SayPrefix string
}

func main() {
  fi, _ := os.Open("golden.cfg")
  b, _ := ioutil.ReadAll(fi)
  var cfg GoldenConfig
  json.Unmarshal(b, &cfg)
	rch := make(chan rcon.RCONRequest, 10)
	rcon := rcon.NewRCON(cfg.Address, cfg.RCONPassword, rch)
	greetings := greeter.NewGreeter("Greetings! Welcome to the server, %s.", rch)

	votekick := votes.NewVote(rch)
  advert := advert.NewAdvert("ads.txt", 60000, rch)
	chain := daisy(greetings, votekick, advert)
	go rcon.Relay()

	logch := make(chan string)
	go tails.Tail(cfg.LogfilePath, logch, false)
	for {
		line := <-logch
		//remove go
		func(ch chan string) { ch <- strings.TrimSpace(line) }(chain)
	}
	os.Exit(0)
}

type Plugin interface {
	Start(prev, next chan string)
}

// Daisy sets up the daisy chain of plugins for message passing.
// Returns a channel on which we can send in messages.
func daisy(plugins ...Plugin) chan string {
	last := make(chan string)
	prev := last
	next := last

	for _, p := range plugins {
		prev = make(chan string)
		go p.Start(next, prev)
		next = prev
	}
	// drain the last channel in the chain
	go func(ch chan string) { <-last }(last)
	return prev
}
<commit_msg>cleans up goldenbot.go<commit_after>package main

import (
  "encoding/json"
  "flag"
	"io/ioutil"
  "log"
  "os"
	"strings"
	"github.com/adabei/goldenbot/greeter"
	"github.com/adabei/goldenbot/rcon"
	"github.com/adabei/goldenbot/tails"
	"github.com/adabei/goldenbot/votes"
	"github.com/adabei/goldenbot/advert"
)

type GoldenConfig struct {
  Address string
  RCONPassword string
  LogfilePath string
  SayPrefix string
}

func main() {
  // Parse command line flags
  configPath := *flag.String("config", "golden.cfg", "the config file to use")
  flag.Parse()

  // Read config
  fi, err := os.Open(configPath)
  if err != nil {
    log.Fatal("Couldn't open config file: ", err)
  }

  b, err := ioutil.ReadAll(fi)
  if err != nil {
    log.Fatal("Couldn't read config file: ", err)
  }

  var cfg GoldenConfig
  json.Unmarshal(b, &cfg)

  // Setup RCON connection
	rch := make(chan rcon.RCONRequest, 10)
	rcon := rcon.NewRCON(cfg.Address, cfg.RCONPassword, rch)

  // Setup plugins
  greetings := greeter.NewGreeter("Greetings! Welcome to the server, %s.", rch)
	votekick := votes.NewVote(rch)
  advert := advert.NewAdvert("ads.txt", 60000, rch)

  chain := daisy(greetings, votekick, advert)
	go rcon.Relay()

	logchan := make(chan string)
	go tails.Tail(cfg.LogfilePath, logchan, false)
	for {
		line := <-logch
    chain <- strings.TrimSpace(line)
	}
}

type Plugin interface {
	Start(prev, next chan string)
}

// Daisy sets up the daisy chain of plugins for message passing.
// Returns a channel on which we can send in messages.
func daisy(plugins ...Plugin) chan string {
	last := make(chan string)
	prev := last
	next := last

	for _, p := range plugins {
		prev = make(chan string)
		go p.Start(next, prev)
		next = prev
	}
	// drain the last channel in the chain
	go func(ch chan string) { <-last }(last)
	return prev
}
<|endoftext|>


<commit_before>// A simple database migrator for PostgreSQL.

package gomigrate

import (
	"database/sql"
	"errors"
	"io/ioutil"
	"log"
	"path/filepath"
	"regexp"
	"sort"
)

const (
	migrationTableName = "gomigrate"
)

var (
	upMigrationFile       = regexp.MustCompile(`(\d+)_(\w+)_up\.(\w+)`)
	downMigrationFile     = regexp.MustCompile(`(\d+)_(\w+)_down\.(\w+)`)
	InvalidMigrationFile  = errors.New("Invalid migration file")
	InvalidMigrationPair  = errors.New("Invalid pair of migration files")
	InvalidMigrationsPath = errors.New("Invalid migrations path")
)

type Migrator struct {
	DB             *sql.DB
	MigrationsPath string
	dbAdapter      Migratable
	migrations     map[uint64]*Migration
}

// Returns true if the migration table already exists.
func (m *Migrator) MigrationTableExists() (bool, error) {
	row := db.QueryRow(m.dbAdapter.SelectMigrationTableSql(), migrationTableName)
	var tableName string
	err := row.Scan(&tableName)
	if err == sql.ErrNoRows {
		log.Print("Migrations table not found")
		return false, nil
	}
	if err != nil {
		log.Printf("Error checking for migration table: %v", err)
		return false, err
	}
	log.Print("Migrations table found")
	return true, nil
}

// Creates the migrations table if it doesn't exist.
func (m *Migrator) CreateMigrationsTable() error {
	log.Print("Creating migrations table")

	_, err := db.Query(m.dbAdapter.CreateMigrationTableSql())
	if err != nil {
		log.Fatalf("Error creating migrations table: %v", err)
	}

	log.Printf("Created migrations table: %s", migrationTableName)

	return nil
}

// Returns a new migrator.
func NewMigrator(db *sql.DB, adapter Migratable, migrationsPath string) (*Migrator, error) {
	// Normalize the migrations path.
	path := []byte(migrationsPath)
	pathLength := len(path)
	if path[pathLength-1] != '/' {
		path = append(path, '/')
	}

	log.Printf("Migrations path: %s", path)

	migrator := Migrator{
		db,
		string(path),
		adapter,
		make(map[uint64]*Migration),
	}

	// Create the migrations table if it doesn't exist.
	tableExists, err := migrator.MigrationTableExists()
	if err != nil {
		return nil, err
	}
	if !tableExists {
		if err := migrator.CreateMigrationsTable(); err != nil {
			return nil, err
		}
	}

	// Get all metadata from the database.
	if err := migrator.fetchMigrations(); err != nil {
		return nil, err
	}
	if err := migrator.getMigrationStatuses(); err != nil {
		return nil, err
	}

	return &migrator, nil
}

// Populates a migrator with a sorted list of migrations from the file system.
func (m *Migrator) fetchMigrations() error {
	pathGlob := append([]byte(m.MigrationsPath), []byte("*")...)

	log.Printf("Migrations path glob: %s", pathGlob)

	matches, err := filepath.Glob(string(pathGlob))
	if err != nil {
		log.Fatalf("Error while globbing migrations: %v", err)
	}

	for _, match := range matches {
		num, migrationType, name, err := parseMigrationPath(match)
		if err != nil {
			log.Printf("Invalid migration file found: %s", match)
			continue
		}

		log.Printf("Migration file found: %s", match)

		migration, ok := m.migrations[num]
		if !ok {
			migration = &Migration{Id: num, Name: name, Status: Inactive}
			m.migrations[num] = migration
		}
		if migrationType == "up" {
			migration.UpPath = match
		} else {
			migration.DownPath = match
		}
	}

	// Validate each migration.
	for _, migration := range m.migrations {
		if !migration.valid() {
			path := migration.UpPath
			if path == "" {
				path = migration.DownPath
			}
			log.Printf("Invalid migration pair for path: %s", path)
			return InvalidMigrationPair
		}
	}

	log.Printf("Migrations file pairs found: %v", len(m.migrations))

	return nil
}

// Queries the migration table to determine the status of each
// migration.
func (m *Migrator) getMigrationStatuses() error {
	for _, migration := range m.migrations {
		rows, err := m.DB.Query(m.dbAdapter.MigrationStatusSql(), migration.Name)
		if err != nil {
			log.Printf(
				"Error getting migration status for %s: %v",
				migration.Name,
				err,
			)
			return err
		}
		for rows.Next() {
			var status int
			err := rows.Scan(&status)
			if err != nil {
				log.Printf("Error getting migration status: %v", err)
				return err
			}
			migration.Status = status
			log.Printf(
				"Migration %s found with status: %v",
				migration.Name,
				status,
			)
		}
	}
	return nil
}

// Returns a sorted list of migration ids for a given status.
func (m *Migrator) Migrations(status int) []*Migration {
	// Sort all migration ids.
	ids := make([]uint64, 0)
	for id, _ := range m.migrations {
		ids = append(ids, id)
	}
	sort.Sort(uint64slice(ids))

	// Find ids for the given status.
	migrations := make([]*Migration, 0)
	for _, id := range ids {
		migration := m.migrations[id]
		if migration.Status == status {
			migrations = append(migrations, migration)
		}
	}
	return migrations
}

// Applies all inactive migrations.
func (m *Migrator) Migrate() error {
	for _, migration := range m.Migrations(Inactive) {
		log.Printf("Applying migration: %s", migration.Name)

		sql, err := ioutil.ReadFile(migration.UpPath)
		if err != nil {
			log.Printf("Error reading up migration: %s", migration.Name)
			return err
		}
		transaction, err := m.DB.Begin()
		if err != nil {
			log.Printf("Error opening transaction: %v", err)
			return err
		}
		// Perform the migration.
		if _, err = transaction.Exec(string(sql)); err != nil {
			log.Printf("Error executing migration: %v", err)
			if rollbackErr := transaction.Rollback(); rollbackErr != nil {
				log.Printf("Error rolling back transaction: %v", rollbackErr)
				return rollbackErr
			}
			return err
		}
		// Log the exception in the migrations table.
		_, err = transaction.Exec(
			m.dbAdapter.MigrationLogInsertSql(),
			migration.Id,
			migration.Name,
			Active,
		)
		if err != nil {
			log.Printf("Error logging migration: %v", err)
			if rollbackErr := transaction.Rollback(); rollbackErr != nil {
				log.Printf("Error rolling back transaction: %v", rollbackErr)
				return rollbackErr
			}
			return err
		}
		if err := transaction.Commit(); err != nil {
			log.Printf("Error commiting transaction: %v", err)
			return err
		}

		// Do this as the last step to ensure that the database has
		// been updated.
		migration.Status = Active
	}
	return nil
}

var (
	NoActiveMigrations = errors.New("No active migrations to rollback")
)

// Rolls back the last migration
func (m *Migrator) Rollback() error {
	migrations := m.Migrations(Active)

	if len(migrations) == 0 {
		return NoActiveMigrations
	}

	lastMigration := migrations[len(migrations)-1]

	log.Printf("Rolling back migration: %v", lastMigration.Name)

	sql, err := ioutil.ReadFile(lastMigration.DownPath)
	if err != nil {
		log.Printf("Error reading migration: %s", lastMigration.DownPath)
		return err
	}
	transaction, err := m.DB.Begin()
	if err != nil {
		log.Printf("Error creating transaction: %v", err)
		return err
	}
	_, err = transaction.Exec(string(sql))
	if err != nil {
		transaction.Rollback()
		log.Printf("Error rolling back transaction: %v", err)
		return err
	}

	// Change the status in the migrations table.
	_, err = transaction.Exec(
		m.dbAdapter.MigrationLogUpdateSql(),
		Inactive,
		lastMigration.Id,
	)
	if err != nil {
		log.Printf("Error logging rollback: %v", err)
		if rollbackErr := transaction.Rollback(); rollbackErr != nil {
			log.Printf("Error rolling back transaction: %v", rollbackErr)
			return rollbackErr
		}
		return err
	}

	err = transaction.Commit()
	if err != nil {
		log.Printf("Error commiting transaction: %v", err)
		return err
	}
	lastMigration.Status = Inactive
	return nil
}
<commit_msg>Bug fix<commit_after>// A simple database migrator for PostgreSQL.

package gomigrate

import (
	"database/sql"
	"errors"
	"io/ioutil"
	"log"
	"path/filepath"
	"regexp"
	"sort"
)

const (
	migrationTableName = "gomigrate"
)

var (
	upMigrationFile       = regexp.MustCompile(`(\d+)_(\w+)_up\.(\w+)`)
	downMigrationFile     = regexp.MustCompile(`(\d+)_(\w+)_down\.(\w+)`)
	InvalidMigrationFile  = errors.New("Invalid migration file")
	InvalidMigrationPair  = errors.New("Invalid pair of migration files")
	InvalidMigrationsPath = errors.New("Invalid migrations path")
)

type Migrator struct {
	DB             *sql.DB
	MigrationsPath string
	dbAdapter      Migratable
	migrations     map[uint64]*Migration
}

// Returns true if the migration table already exists.
func (m *Migrator) MigrationTableExists() (bool, error) {
	row := m.DB.QueryRow(m.dbAdapter.SelectMigrationTableSql(), migrationTableName)
	var tableName string
	err := row.Scan(&tableName)
	if err == sql.ErrNoRows {
		log.Print("Migrations table not found")
		return false, nil
	}
	if err != nil {
		log.Printf("Error checking for migration table: %v", err)
		return false, err
	}
	log.Print("Migrations table found")
	return true, nil
}

// Creates the migrations table if it doesn't exist.
func (m *Migrator) CreateMigrationsTable() error {
	log.Print("Creating migrations table")

	_, err := m.DB.Query(m.dbAdapter.CreateMigrationTableSql())
	if err != nil {
		log.Fatalf("Error creating migrations table: %v", err)
	}

	log.Printf("Created migrations table: %s", migrationTableName)

	return nil
}

// Returns a new migrator.
func NewMigrator(db *sql.DB, adapter Migratable, migrationsPath string) (*Migrator, error) {
	// Normalize the migrations path.
	path := []byte(migrationsPath)
	pathLength := len(path)
	if path[pathLength-1] != '/' {
		path = append(path, '/')
	}

	log.Printf("Migrations path: %s", path)

	migrator := Migrator{
		db,
		string(path),
		adapter,
		make(map[uint64]*Migration),
	}

	// Create the migrations table if it doesn't exist.
	tableExists, err := migrator.MigrationTableExists()
	if err != nil {
		return nil, err
	}
	if !tableExists {
		if err := migrator.CreateMigrationsTable(); err != nil {
			return nil, err
		}
	}

	// Get all metadata from the database.
	if err := migrator.fetchMigrations(); err != nil {
		return nil, err
	}
	if err := migrator.getMigrationStatuses(); err != nil {
		return nil, err
	}

	return &migrator, nil
}

// Populates a migrator with a sorted list of migrations from the file system.
func (m *Migrator) fetchMigrations() error {
	pathGlob := append([]byte(m.MigrationsPath), []byte("*")...)

	log.Printf("Migrations path glob: %s", pathGlob)

	matches, err := filepath.Glob(string(pathGlob))
	if err != nil {
		log.Fatalf("Error while globbing migrations: %v", err)
	}

	for _, match := range matches {
		num, migrationType, name, err := parseMigrationPath(match)
		if err != nil {
			log.Printf("Invalid migration file found: %s", match)
			continue
		}

		log.Printf("Migration file found: %s", match)

		migration, ok := m.migrations[num]
		if !ok {
			migration = &Migration{Id: num, Name: name, Status: Inactive}
			m.migrations[num] = migration
		}
		if migrationType == "up" {
			migration.UpPath = match
		} else {
			migration.DownPath = match
		}
	}

	// Validate each migration.
	for _, migration := range m.migrations {
		if !migration.valid() {
			path := migration.UpPath
			if path == "" {
				path = migration.DownPath
			}
			log.Printf("Invalid migration pair for path: %s", path)
			return InvalidMigrationPair
		}
	}

	log.Printf("Migrations file pairs found: %v", len(m.migrations))

	return nil
}

// Queries the migration table to determine the status of each
// migration.
func (m *Migrator) getMigrationStatuses() error {
	for _, migration := range m.migrations {
		rows, err := m.DB.Query(m.dbAdapter.MigrationStatusSql(), migration.Name)
		if err != nil {
			log.Printf(
				"Error getting migration status for %s: %v",
				migration.Name,
				err,
			)
			return err
		}
		for rows.Next() {
			var status int
			err := rows.Scan(&status)
			if err != nil {
				log.Printf("Error getting migration status: %v", err)
				return err
			}
			migration.Status = status
			log.Printf(
				"Migration %s found with status: %v",
				migration.Name,
				status,
			)
		}
	}
	return nil
}

// Returns a sorted list of migration ids for a given status.
func (m *Migrator) Migrations(status int) []*Migration {
	// Sort all migration ids.
	ids := make([]uint64, 0)
	for id, _ := range m.migrations {
		ids = append(ids, id)
	}
	sort.Sort(uint64slice(ids))

	// Find ids for the given status.
	migrations := make([]*Migration, 0)
	for _, id := range ids {
		migration := m.migrations[id]
		if migration.Status == status {
			migrations = append(migrations, migration)
		}
	}
	return migrations
}

// Applies all inactive migrations.
func (m *Migrator) Migrate() error {
	for _, migration := range m.Migrations(Inactive) {
		log.Printf("Applying migration: %s", migration.Name)

		sql, err := ioutil.ReadFile(migration.UpPath)
		if err != nil {
			log.Printf("Error reading up migration: %s", migration.Name)
			return err
		}
		transaction, err := m.DB.Begin()
		if err != nil {
			log.Printf("Error opening transaction: %v", err)
			return err
		}
		// Perform the migration.
		if _, err = transaction.Exec(string(sql)); err != nil {
			log.Printf("Error executing migration: %v", err)
			if rollbackErr := transaction.Rollback(); rollbackErr != nil {
				log.Printf("Error rolling back transaction: %v", rollbackErr)
				return rollbackErr
			}
			return err
		}
		// Log the exception in the migrations table.
		_, err = transaction.Exec(
			m.dbAdapter.MigrationLogInsertSql(),
			migration.Id,
			migration.Name,
			Active,
		)
		if err != nil {
			log.Printf("Error logging migration: %v", err)
			if rollbackErr := transaction.Rollback(); rollbackErr != nil {
				log.Printf("Error rolling back transaction: %v", rollbackErr)
				return rollbackErr
			}
			return err
		}
		if err := transaction.Commit(); err != nil {
			log.Printf("Error commiting transaction: %v", err)
			return err
		}

		// Do this as the last step to ensure that the database has
		// been updated.
		migration.Status = Active
	}
	return nil
}

var (
	NoActiveMigrations = errors.New("No active migrations to rollback")
)

// Rolls back the last migration
func (m *Migrator) Rollback() error {
	migrations := m.Migrations(Active)

	if len(migrations) == 0 {
		return NoActiveMigrations
	}

	lastMigration := migrations[len(migrations)-1]

	log.Printf("Rolling back migration: %v", lastMigration.Name)

	sql, err := ioutil.ReadFile(lastMigration.DownPath)
	if err != nil {
		log.Printf("Error reading migration: %s", lastMigration.DownPath)
		return err
	}
	transaction, err := m.DB.Begin()
	if err != nil {
		log.Printf("Error creating transaction: %v", err)
		return err
	}
	_, err = transaction.Exec(string(sql))
	if err != nil {
		transaction.Rollback()
		log.Printf("Error rolling back transaction: %v", err)
		return err
	}

	// Change the status in the migrations table.
	_, err = transaction.Exec(
		m.dbAdapter.MigrationLogUpdateSql(),
		Inactive,
		lastMigration.Id,
	)
	if err != nil {
		log.Printf("Error logging rollback: %v", err)
		if rollbackErr := transaction.Rollback(); rollbackErr != nil {
			log.Printf("Error rolling back transaction: %v", rollbackErr)
			return rollbackErr
		}
		return err
	}

	err = transaction.Commit()
	if err != nil {
		log.Printf("Error commiting transaction: %v", err)
		return err
	}
	lastMigration.Status = Inactive
	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/asaskevich/govalidator"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (Config, error)
	DeleteAll(config Config) error
	LoadOrCreate(deployArgs *DeployArgs) (Config, bool, bool, error)
	Update(Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Iaas         iaas.Provider
	Project      string
	Namespace    string
	BucketName   string
	BucketExists bool
	BucketError  error
	Config       *Config
}

// New instantiates a new client
func New(iaas iaas.Provider, project, namespace string) *Client {
	namespace = determineNamespace(namespace, iaas.Region())
	bucketName, exists, err := determineBucketName(iaas, namespace, project)

	if !exists && err == nil {
		err = iaas.CreateBucket(bucketName)
	}

	return &Client{
		iaas,
		project,
		namespace,
		bucketName,
		exists,
		err,
		&Config{},
	}
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return client.Iaas.WriteFile(client.configBucket(),
		filename,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return client.Iaas.LoadFile(
		client.configBucket(),
		filename,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return client.Iaas.DeleteFile(
		client.configBucket(),
		filename,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return client.Iaas.HasFile(
		client.configBucket(),
		filename,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return client.Iaas.WriteFile(client.configBucket(), configFilePath, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config Config) error {
	return client.Iaas.DeleteVersionedBucket(config.ConfigBucket)
}

// Load loads an existing config file from S3
func (client *Client) Load() (Config, error) {
	if client.BucketError != nil {
		return Config{}, client.BucketError
	}

	configBytes, err := client.Iaas.LoadFile(
		client.configBucket(),
		configFilePath,
	)
	if err != nil {
		return Config{}, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return Config{}, err
	}

	return conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *DeployArgs) (Config, bool, bool, error) {

	var isDomainUpdated bool

	if client.BucketError != nil {
		return Config{}, false, false, client.BucketError
	}

	config, err := generateDefaultConfig(
		client.Project,
		deployment(client.Project),
		client.configBucket(),
		client.Iaas.Region(),
		client.Namespace,
	)
	if err != nil {
		return Config{}, false, false, err
	}

	defaultConfigBytes, err := json.Marshal(&config)
	if err != nil {
		return Config{}, false, false, err
	}

	configBytes, newConfigCreated, err := client.Iaas.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		defaultConfigBytes,
	)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	allow, err := parseCIDRBlocks(deployArgs.AllowIPs)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	config, err = updateAllowedIPs(config, allow)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	if newConfigCreated {
		config.IAAS = deployArgs.IAAS
	}

	if deployArgs.ZoneIsSet {
		config.AvailabilityZone = deployArgs.Zone
	}
	if newConfigCreated || deployArgs.WorkerCountIsSet {
		config.ConcourseWorkerCount = deployArgs.WorkerCount
	}
	if newConfigCreated || deployArgs.WorkerSizeIsSet {
		config.ConcourseWorkerSize = deployArgs.WorkerSize
	}
	if newConfigCreated || deployArgs.WebSizeIsSet {
		config.ConcourseWebSize = deployArgs.WebSize
	}
	if newConfigCreated || deployArgs.DBSizeIsSet {
		config.RDSInstanceClass = DBSizes[deployArgs.DBSize]
	}
	if newConfigCreated || deployArgs.GithubAuthIsSet {
		config.GithubClientID = deployArgs.GithubAuthClientID
		config.GithubClientSecret = deployArgs.GithubAuthClientSecret
		config.GithubAuthIsSet = deployArgs.GithubAuthIsSet
	}
	if newConfigCreated || deployArgs.TagsIsSet {
		config.Tags = deployArgs.Tags
	}
	if newConfigCreated || deployArgs.SpotIsSet {
		config.Spot = deployArgs.Spot
	}
	if newConfigCreated || deployArgs.WorkerTypeIsSet {
		config.WorkerType = deployArgs.WorkerType
	}

	if newConfigCreated || deployArgs.DomainIsSet {
		if config.Domain != deployArgs.Domain {
			isDomainUpdated = true
		}
		config.Domain = deployArgs.Domain
	} else {
		if govalidator.IsIPv4(config.Domain) {
			config.Domain = ""
		}
	}
	return config, newConfigCreated, isDomainUpdated, nil
}

func (client *Client) configBucket() string {
	return client.BucketName
}

func deployment(project string) string {
	return fmt.Sprintf("concourse-up-%s", project)
}

func createBucketName(deployment, extension string) string {
	return fmt.Sprintf("%s-%s-config", deployment, extension)
}

func determineBucketName(iaas iaas.Provider, namespace, project string) (string, bool, error) {
	regionBucketName := createBucketName(deployment(project), iaas.Region())
	namespaceBucketName := createBucketName(deployment(project), namespace)

	foundRegionNamedBucket, err := iaas.BucketExists(regionBucketName)
	if err != nil {
		return "", false, err
	}
	foundNamespacedBucket, err := iaas.BucketExists(namespaceBucketName)
	if err != nil {
		return "", false, err
	}

	foundOne := foundRegionNamedBucket || foundNamespacedBucket

	switch {
	case !foundRegionNamedBucket && foundNamespacedBucket:
		return namespaceBucketName, foundOne, nil
	case foundRegionNamedBucket && !foundNamespacedBucket:
		return regionBucketName, foundOne, nil
	default:
		return namespaceBucketName, foundOne, nil
	}
}

type cidrBlocks []*net.IPNet

func parseCIDRBlocks(s string) (cidrBlocks, error) {
	var x cidrBlocks
	for _, ip := range strings.Split(s, ",") {
		ip = strings.TrimSpace(ip)
		_, ipNet, err := net.ParseCIDR(ip)
		if err != nil {
			ipNet = &net.IPNet{
				IP:   net.ParseIP(ip),
				Mask: net.CIDRMask(32, 32),
			}
		}
		if ipNet.IP == nil {
			return nil, fmt.Errorf("could not parse %q as an IP address or CIDR range", ip)
		}
		x = append(x, ipNet)
	}
	return x, nil
}

func (b cidrBlocks) String() (string, error) {
	var buf bytes.Buffer
	for i, ipNet := range b {
		if i > 0 {
			_, err := fmt.Fprintf(&buf, ", %q", ipNet)
			if err != nil {
				return "", err
			}
		} else {
			_, err := fmt.Fprintf(&buf, "%q", ipNet)
			if err != nil {
				return "", err
			}
		}
	}
	return buf.String(), nil
}

func determineNamespace(namespace, region string) string {
	if namespace == "" {
		return region
	}
	return namespace
}
<commit_msg>erroneous zone arguments should be trapped while redeploying<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/asaskevich/govalidator"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (Config, error)
	DeleteAll(config Config) error
	LoadOrCreate(deployArgs *DeployArgs) (Config, bool, bool, error)
	Update(Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Iaas         iaas.Provider
	Project      string
	Namespace    string
	BucketName   string
	BucketExists bool
	BucketError  error
	Config       *Config
}

// New instantiates a new client
func New(iaas iaas.Provider, project, namespace string) *Client {
	namespace = determineNamespace(namespace, iaas.Region())
	bucketName, exists, err := determineBucketName(iaas, namespace, project)

	if !exists && err == nil {
		err = iaas.CreateBucket(bucketName)
	}

	return &Client{
		iaas,
		project,
		namespace,
		bucketName,
		exists,
		err,
		&Config{},
	}
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return client.Iaas.WriteFile(client.configBucket(),
		filename,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return client.Iaas.LoadFile(
		client.configBucket(),
		filename,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return client.Iaas.DeleteFile(
		client.configBucket(),
		filename,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return client.Iaas.HasFile(
		client.configBucket(),
		filename,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return client.Iaas.WriteFile(client.configBucket(), configFilePath, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config Config) error {
	return client.Iaas.DeleteVersionedBucket(config.ConfigBucket)
}

// Load loads an existing config file from S3
func (client *Client) Load() (Config, error) {
	if client.BucketError != nil {
		return Config{}, client.BucketError
	}

	configBytes, err := client.Iaas.LoadFile(
		client.configBucket(),
		configFilePath,
	)
	if err != nil {
		return Config{}, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return Config{}, err
	}

	return conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *DeployArgs) (Config, bool, bool, error) {

	var isDomainUpdated bool

	if client.BucketError != nil {
		return Config{}, false, false, client.BucketError
	}

	config, err := generateDefaultConfig(
		client.Project,
		deployment(client.Project),
		client.configBucket(),
		client.Iaas.Region(),
		client.Namespace,
	)
	if err != nil {
		return Config{}, false, false, err
	}

	defaultConfigBytes, err := json.Marshal(&config)
	if err != nil {
		return Config{}, false, false, err
	}

	configBytes, newConfigCreated, err := client.Iaas.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		defaultConfigBytes,
	)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	allow, err := parseCIDRBlocks(deployArgs.AllowIPs)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	config, err = updateAllowedIPs(config, allow)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	if newConfigCreated {
		config.IAAS = deployArgs.IAAS
	}

	if deployArgs.ZoneIsSet {
		// This is a safeguard for a redeployment where zone does not belong to the region where the original deployment has happened
		if !newConfigCreated && !strings.Contains(deployArgs.Zone, config.AvailabilityZone) {
			return Config{}, false, false, fmt.Errorf("Zone %s does not belong to region %s", deployArgs.Zone, config.AvailabilityZone)
		}
		config.AvailabilityZone = deployArgs.Zone
	}
	if newConfigCreated || deployArgs.WorkerCountIsSet {
		config.ConcourseWorkerCount = deployArgs.WorkerCount
	}
	if newConfigCreated || deployArgs.WorkerSizeIsSet {
		config.ConcourseWorkerSize = deployArgs.WorkerSize
	}
	if newConfigCreated || deployArgs.WebSizeIsSet {
		config.ConcourseWebSize = deployArgs.WebSize
	}
	if newConfigCreated || deployArgs.DBSizeIsSet {
		config.RDSInstanceClass = DBSizes[deployArgs.DBSize]
	}
	if newConfigCreated || deployArgs.GithubAuthIsSet {
		config.GithubClientID = deployArgs.GithubAuthClientID
		config.GithubClientSecret = deployArgs.GithubAuthClientSecret
		config.GithubAuthIsSet = deployArgs.GithubAuthIsSet
	}
	if newConfigCreated || deployArgs.TagsIsSet {
		config.Tags = deployArgs.Tags
	}
	if newConfigCreated || deployArgs.SpotIsSet {
		config.Spot = deployArgs.Spot
	}
	if newConfigCreated || deployArgs.WorkerTypeIsSet {
		config.WorkerType = deployArgs.WorkerType
	}

	if newConfigCreated || deployArgs.DomainIsSet {
		if config.Domain != deployArgs.Domain {
			isDomainUpdated = true
		}
		config.Domain = deployArgs.Domain
	} else {
		if govalidator.IsIPv4(config.Domain) {
			config.Domain = ""
		}
	}
	return config, newConfigCreated, isDomainUpdated, nil
}

func (client *Client) configBucket() string {
	return client.BucketName
}

func deployment(project string) string {
	return fmt.Sprintf("concourse-up-%s", project)
}

func createBucketName(deployment, extension string) string {
	return fmt.Sprintf("%s-%s-config", deployment, extension)
}

func determineBucketName(iaas iaas.Provider, namespace, project string) (string, bool, error) {
	regionBucketName := createBucketName(deployment(project), iaas.Region())
	namespaceBucketName := createBucketName(deployment(project), namespace)

	foundRegionNamedBucket, err := iaas.BucketExists(regionBucketName)
	if err != nil {
		return "", false, err
	}
	foundNamespacedBucket, err := iaas.BucketExists(namespaceBucketName)
	if err != nil {
		return "", false, err
	}

	foundOne := foundRegionNamedBucket || foundNamespacedBucket

	switch {
	case !foundRegionNamedBucket && foundNamespacedBucket:
		return namespaceBucketName, foundOne, nil
	case foundRegionNamedBucket && !foundNamespacedBucket:
		return regionBucketName, foundOne, nil
	default:
		return namespaceBucketName, foundOne, nil
	}
}

type cidrBlocks []*net.IPNet

func parseCIDRBlocks(s string) (cidrBlocks, error) {
	var x cidrBlocks
	for _, ip := range strings.Split(s, ",") {
		ip = strings.TrimSpace(ip)
		_, ipNet, err := net.ParseCIDR(ip)
		if err != nil {
			ipNet = &net.IPNet{
				IP:   net.ParseIP(ip),
				Mask: net.CIDRMask(32, 32),
			}
		}
		if ipNet.IP == nil {
			return nil, fmt.Errorf("could not parse %q as an IP address or CIDR range", ip)
		}
		x = append(x, ipNet)
	}
	return x, nil
}

func (b cidrBlocks) String() (string, error) {
	var buf bytes.Buffer
	for i, ipNet := range b {
		if i > 0 {
			_, err := fmt.Fprintf(&buf, ", %q", ipNet)
			if err != nil {
				return "", err
			}
		} else {
			_, err := fmt.Fprintf(&buf, "%q", ipNet)
			if err != nil {
				return "", err
			}
		}
	}
	return buf.String(), nil
}

func determineNamespace(namespace, region string) string {
	if namespace == "" {
		return region
	}
	return namespace
}
<|endoftext|>


<commit_before>package config

import (
	"flag"
	"github.com/GeertJohan/go.rice"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
)

var configFileName = "./config.yml"

func init() {
	flag.StringVar(&configFileName, "config", "./config.yml", "Config File Location")
}

type Config struct {
	Site              SiteConfig    `yaml:"site"`             // Site/HTML Configuration
	DB                DBConfig      `yaml:"db"`               // Database Configuration
	HostnameWhiteList []string      `yaml:"hosts"`            // List of valid hostnames, ignored if empty
	ListenOn          string        `yaml:"listen_on"`        // Address & Port to use
	MasterSecret      string        `yaml:"secret"`           // Master Secret for keychain
	DisableSecurity   bool          `yaml:"disable_security"` // Disables various flags to ensure non-HTTPS requests work
	Captcha           CaptchaConfig `yaml:"captcha"`
}

const (
	CaptchaRecaptcha = "recaptcha"
	CaptchaInternal  = "internal"
	CaptchaHybrid    = "hybrid"
	CaptchaDisabled  = "disabled"
)

type CaptchaConfig struct {
	Mode     string            `yaml:"mode"` // Captcha Mode
	Settings map[string]string `yaml:"settings,inline"`
}

type SiteConfig struct {
	Title        string `yaml:"title"`       // Site Title
	Description  string `yaml:"description"` // Site Description
	PrimaryColor string `yaml:"color"`       // Primary Color for Size
}

type DBConfig struct {
	File     string `yaml:"file"`
	ReadOnly bool   `yaml:"read_only"`
}

func Load() (*Config, error) {
	var config = &Config{
		Site: SiteConfig{
			Title:        "NyxChan",
			PrimaryColor: "#78909c",
			Description:  "NyxChan Default Configuration",
		},
		DB: DBConfig{
			File:     ":memory:",
			ReadOnly: false,
		},
		HostnameWhiteList: []string{},
		ListenOn:          ":8080",
		MasterSecret:      "changeme",
		DisableSecurity:   false,
		Captcha: CaptchaConfig{
			Mode: CaptchaDisabled,
		},
	}
	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		return config, nil
	}
	dat, err := ioutil.ReadFile(configFileName)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(dat, config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func (c Config) IsHostNameValid(hostname string) bool {
	if c.HostnameWhiteList == nil {
		return true
	}
	if len(c.HostnameWhiteList) == 0 {
		return true
	}
	for _, v := range c.HostnameWhiteList {
		if v == hostname {
			return true
		}
	}
	return false
}
<commit_msg>Removed unused import<commit_after>package config

import (
	"flag"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
)

var configFileName = "./config.yml"

func init() {
	flag.StringVar(&configFileName, "config", "./config.yml", "Config File Location")
}

type Config struct {
	Site              SiteConfig    `yaml:"site"`             // Site/HTML Configuration
	DB                DBConfig      `yaml:"db"`               // Database Configuration
	HostnameWhiteList []string      `yaml:"hosts"`            // List of valid hostnames, ignored if empty
	ListenOn          string        `yaml:"listen_on"`        // Address & Port to use
	MasterSecret      string        `yaml:"secret"`           // Master Secret for keychain
	DisableSecurity   bool          `yaml:"disable_security"` // Disables various flags to ensure non-HTTPS requests work
	Captcha           CaptchaConfig `yaml:"captcha"`
}

const (
	CaptchaRecaptcha = "recaptcha"
	CaptchaInternal  = "internal"
	CaptchaHybrid    = "hybrid"
	CaptchaDisabled  = "disabled"
)

type CaptchaConfig struct {
	Mode     string            `yaml:"mode"` // Captcha Mode
	Settings map[string]string `yaml:"settings,inline"`
}

type SiteConfig struct {
	Title        string `yaml:"title"`       // Site Title
	Description  string `yaml:"description"` // Site Description
	PrimaryColor string `yaml:"color"`       // Primary Color for Size
}

type DBConfig struct {
	File     string `yaml:"file"`
	ReadOnly bool   `yaml:"read_only"`
}

func Load() (*Config, error) {
	var config = &Config{
		Site: SiteConfig{
			Title:        "NyxChan",
			PrimaryColor: "#78909c",
			Description:  "NyxChan Default Configuration",
		},
		DB: DBConfig{
			File:     ":memory:",
			ReadOnly: false,
		},
		HostnameWhiteList: []string{},
		ListenOn:          ":8080",
		MasterSecret:      "changeme",
		DisableSecurity:   false,
		Captcha: CaptchaConfig{
			Mode: CaptchaDisabled,
		},
	}
	if _, err := os.Stat(configFileName); os.IsNotExist(err) {
		return config, nil
	}
	dat, err := ioutil.ReadFile(configFileName)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(dat, config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func (c Config) IsHostNameValid(hostname string) bool {
	if c.HostnameWhiteList == nil {
		return true
	}
	if len(c.HostnameWhiteList) == 0 {
		return true
	}
	for _, v := range c.HostnameWhiteList {
		if v == hostname {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*
	config loads and understands the tegola config format.
*/
package config

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/BurntSushi/toml"

	"github.com/go-spatial/tegola"
	"github.com/go-spatial/tegola/config/env"
	"github.com/go-spatial/tegola/internal/log"
)

// Config represents a tegola config file.
type Config struct {
	// the tile buffer to use
	TileBuffer int64 `toml:"tile_buffer"`
	// LocationName is the file name or http server that the config was read from.
	// If this is an empty string, it means that the location was unknown. This is the case if
	// the Parse() function is used directly.
	LocationName string
	Webserver    Webserver              `toml:"webserver"`
	Cache        map[string]interface{} `toml:"cache"`
	// Map of providers.
	Providers []map[string]interface{}
	Maps      []Map
}

type Webserver struct {
	HostName          env.String `toml:"hostname"`
	Port              env.String `toml:"port"`
	CORSAllowedOrigin env.String `toml:"cors_allowed_origin"`
}

// A Map represents a map in the Tegola Config file.
type Map struct {
	Name        env.String   `toml:"name"`
	Attribution env.String   `toml:"attribution"`
	Bounds      []env.Float  `toml:"bounds"`
	Center      [3]env.Float `toml:"center"`
	Layers      []MapLayer   `toml:"layers"`
}

type MapLayer struct {
	// Name is optional. If it's not defined the name of the ProviderLayer will be used.
	// Name can also be used to group multiple ProviderLayers under the same namespace.
	Name          env.String  `toml:"name"`
	ProviderLayer env.String  `toml:"provider_layer"`
	MinZoom       *env.Uint   `toml:"min_zoom"`
	MaxZoom       *env.Uint   `toml:"max_zoom"`
	DefaultTags   interface{} `toml:"default_tags"`
	// DontSimplify indicates wheather feature simplification should be applied.
	// We use a negative in the name so the default is to simplify
	DontSimplify env.Bool `toml:"dont_simplify"`
}

// GetName helper to get the name we care about.
func (ml MapLayer) GetName() (string, error) {
	if ml.Name != "" {
		return ml.Name, nil
	}
	// split the provider layer (syntax is provider.layer)
	plParts := strings.Split(ml.ProviderLayer, ".")
	if len(plParts) != 2 {
		return "", ErrInvalidProviderLayerName{ProviderLayerName: ml.ProviderLayer}
	}

	return plParts[1], nil
}

// checks the config for issues
func (c *Config) Validate() error {

	// check for map layer name / zoom collisions
	// map of layers to providers
	mapLayers := map[string]map[string]MapLayer{}
	for mapKey, m := range c.Maps {
		if _, ok := mapLayers[string(m.Name)]; !ok {
			mapLayers[string(m.Name)] = map[string]MapLayer{}
		}

		for layerKey, l := range m.Layers {
			name, err := l.GetName()
			if err != nil {
				return err
			}

			// MaxZoom default
			if l.MaxZoom == nil {
				ph := env.Uint(tegola.MaxZ)
				// set in iterated value
				l.MaxZoom = &ph
				// set in underlying config struct
				c.Maps[mapKey].Layers[layerKey].MaxZoom = &ph
			}
			// MinZoom default
			if l.MinZoom == nil {
				ph := env.Uint(0)
				// set in iterated value
				l.MinZoom = &ph
				// set in underlying config struct
				c.Maps[mapKey].Layers[layerKey].MinZoom = &ph
			}

			// check if we already have this layer
			if val, ok := mapLayers[string(m.Name)][name]; ok {
				// we have a hit. check for zoom range overlap
				if uint(*val.MinZoom) <= uint(*l.MaxZoom) && uint(*l.MinZoom) <= uint(*val.MaxZoom) {
					return ErrOverlappingLayerZooms{
						ProviderLayer1: string(val.ProviderLayer),
						ProviderLayer2: string(l.ProviderLayer),
					}
				}
				continue
			}

			// add the MapLayer to our map
			mapLayers[string(m.Name)][name] = l
		}
	}

	return nil
}

// Parse will parse the Tegola config file provided by the io.Reader.
func Parse(reader io.Reader, location string) (conf Config, err error) {
	// decode conf file, don't care about the meta data.
	_, err = toml.DecodeReader(reader, &conf)
	conf.LocationName = location

	return conf, err
}

// Load will load and parse the config file from the given location.
func Load(location string) (conf Config, err error) {
	var reader io.Reader

	// check for http prefix
	if strings.HasPrefix(location, "http") {
		log.Infof("loading remote config (%v)", location)

		// setup http client with a timeout
		var httpClient = &http.Client{
			Timeout: time.Second * 10,
		}

		// make the http request
		res, err := httpClient.Get(location)
		if err != nil {
			return conf, fmt.Errorf("error fetching remote config file (%v): %v ", location, err)
		}

		// set the reader to the response body
		reader = res.Body
	} else {
		log.Infof("loading local config (%v)", location)

		// check the conf file exists
		if _, err := os.Stat(location); os.IsNotExist(err) {
			return conf, fmt.Errorf("config file at location (%v) not found!", location)
		}
		// open the confi file
		reader, err = os.Open(location)
		if err != nil {
			return conf, fmt.Errorf("error opening local config file (%v): %v ", location, err)
		}
	}

	return Parse(reader, location)
}

func LoadAndValidate(filename string) (cfg Config, err error) {
	cfg, err = Load(filename)
	if err != nil {
		return cfg, err
	}
	// validate our config
	return cfg, cfg.Validate()
}
<commit_msg>Forgot to run test after merge conflict.<commit_after>/*
	config loads and understands the tegola config format.
*/
package config

import (
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/BurntSushi/toml"

	"github.com/go-spatial/tegola"
	"github.com/go-spatial/tegola/config/env"
	"github.com/go-spatial/tegola/internal/log"
)

// Config represents a tegola config file.
type Config struct {
	// the tile buffer to use
	TileBuffer int64 `toml:"tile_buffer"`
	// LocationName is the file name or http server that the config was read from.
	// If this is an empty string, it means that the location was unknown. This is the case if
	// the Parse() function is used directly.
	LocationName string
	Webserver    Webserver              `toml:"webserver"`
	Cache        map[string]interface{} `toml:"cache"`
	// Map of providers.
	Providers []map[string]interface{}
	Maps      []Map
}

type Webserver struct {
	HostName          env.String `toml:"hostname"`
	Port              env.String `toml:"port"`
	CORSAllowedOrigin env.String `toml:"cors_allowed_origin"`
}

// A Map represents a map in the Tegola Config file.
type Map struct {
	Name        env.String   `toml:"name"`
	Attribution env.String   `toml:"attribution"`
	Bounds      []env.Float  `toml:"bounds"`
	Center      [3]env.Float `toml:"center"`
	Layers      []MapLayer   `toml:"layers"`
}

type MapLayer struct {
	// Name is optional. If it's not defined the name of the ProviderLayer will be used.
	// Name can also be used to group multiple ProviderLayers under the same namespace.
	Name          env.String  `toml:"name"`
	ProviderLayer env.String  `toml:"provider_layer"`
	MinZoom       *env.Uint   `toml:"min_zoom"`
	MaxZoom       *env.Uint   `toml:"max_zoom"`
	DefaultTags   interface{} `toml:"default_tags"`
	// DontSimplify indicates wheather feature simplification should be applied.
	// We use a negative in the name so the default is to simplify
	DontSimplify env.Bool `toml:"dont_simplify"`
}

// GetName helper to get the name we care about.
func (ml MapLayer) GetName() (string, error) {
	if ml.Name != "" {
		return string(ml.Name), nil
	}
	// split the provider layer (syntax is provider.layer)
	plParts := strings.Split(string(ml.ProviderLayer), ".")
	if len(plParts) != 2 {
		return "", ErrInvalidProviderLayerName{ProviderLayerName: string(ml.ProviderLayer)}
	}

	return plParts[1], nil
}

// checks the config for issues
func (c *Config) Validate() error {

	// check for map layer name / zoom collisions
	// map of layers to providers
	mapLayers := map[string]map[string]MapLayer{}
	for mapKey, m := range c.Maps {
		if _, ok := mapLayers[string(m.Name)]; !ok {
			mapLayers[string(m.Name)] = map[string]MapLayer{}
		}

		for layerKey, l := range m.Layers {
			name, err := l.GetName()
			if err != nil {
				return err
			}

			// MaxZoom default
			if l.MaxZoom == nil {
				ph := env.Uint(tegola.MaxZ)
				// set in iterated value
				l.MaxZoom = &ph
				// set in underlying config struct
				c.Maps[mapKey].Layers[layerKey].MaxZoom = &ph
			}
			// MinZoom default
			if l.MinZoom == nil {
				ph := env.Uint(0)
				// set in iterated value
				l.MinZoom = &ph
				// set in underlying config struct
				c.Maps[mapKey].Layers[layerKey].MinZoom = &ph
			}

			// check if we already have this layer
			if val, ok := mapLayers[string(m.Name)][name]; ok {
				// we have a hit. check for zoom range overlap
				if uint(*val.MinZoom) <= uint(*l.MaxZoom) && uint(*l.MinZoom) <= uint(*val.MaxZoom) {
					return ErrOverlappingLayerZooms{
						ProviderLayer1: string(val.ProviderLayer),
						ProviderLayer2: string(l.ProviderLayer),
					}
				}
				continue
			}

			// add the MapLayer to our map
			mapLayers[string(m.Name)][name] = l
		}
	}

	return nil
}

// Parse will parse the Tegola config file provided by the io.Reader.
func Parse(reader io.Reader, location string) (conf Config, err error) {
	// decode conf file, don't care about the meta data.
	_, err = toml.DecodeReader(reader, &conf)
	conf.LocationName = location

	return conf, err
}

// Load will load and parse the config file from the given location.
func Load(location string) (conf Config, err error) {
	var reader io.Reader

	// check for http prefix
	if strings.HasPrefix(location, "http") {
		log.Infof("loading remote config (%v)", location)

		// setup http client with a timeout
		var httpClient = &http.Client{
			Timeout: time.Second * 10,
		}

		// make the http request
		res, err := httpClient.Get(location)
		if err != nil {
			return conf, fmt.Errorf("error fetching remote config file (%v): %v ", location, err)
		}

		// set the reader to the response body
		reader = res.Body
	} else {
		log.Infof("loading local config (%v)", location)

		// check the conf file exists
		if _, err := os.Stat(location); os.IsNotExist(err) {
			return conf, fmt.Errorf("config file at location (%v) not found!", location)
		}
		// open the confi file
		reader, err = os.Open(location)
		if err != nil {
			return conf, fmt.Errorf("error opening local config file (%v): %v ", location, err)
		}
	}

	return Parse(reader, location)
}

func LoadAndValidate(filename string) (cfg Config, err error) {
	cfg, err = Load(filename)
	if err != nil {
		return cfg, err
	}
	// validate our config
	return cfg, cfg.Validate()
}
<|endoftext|>


<commit_before>package config

import (
	"os"

	"github.com/jinzhu/configor"
	"github.com/qor/auth/providers/facebook"
	"github.com/qor/auth/providers/github"
	"github.com/qor/auth/providers/google"
	"github.com/qor/auth/providers/twitter"
	"github.com/qor/location"
	"github.com/qor/mailer"
	"github.com/qor/mailer/logger"
	"github.com/qor/media/oss"
	"github.com/qor/oss/s3"
	"github.com/qor/redirect_back"
	"github.com/qor/session/manager"
)

type SMTPConfig struct {
	Host     string
	Port     string
	User     string
	Password string
}

var Config = struct {
	Port uint `default:"7000" env:"PORT"`
	DB   struct {
		Name     string `env:"DBName" default:"qor_example"`
		Adapter  string `env:"DBAdapter" default:"mysql"`
		Host     string `env:"DBHost" default:"localhost"`
		Port     string `env:"DBPort" default:"3306"`
		User     string `env:"DBUser"`
		Password string `env:"DBPassword"`
	}
	S3 struct {
		AccessKeyID     string `env:"AWS_ACCESS_KEY_ID"`
		SecretAccessKey string `env:"AWS_SECRET_ACCESS_KEY"`
		Region          string `env:"AWS_Region"`
		S3Bucket        string `env:"AWS_Bucket"`
	}
	SMTP         SMTPConfig
	Github       github.Config
	Google       google.Config
	Facebook     facebook.Config
	Twitter      twitter.Config
	GoogleAPIKey string `env:"GoogleAPIKey"`
	BaiduAPIKey  string `env:"BaiduAPIKey"`
}{}

var (
	Root         = os.Getenv("GOPATH") + "/src/github.com/qor/qor-example"
	Mailer       *mailer.Mailer
	RedirectBack = redirect_back.New(&redirect_back.Config{
		SessionManager:  manager.SessionManager,
		IgnoredPrefixes: []string{"/auth"},
	})
)

func init() {
	if err := configor.Load(&Config, "config/database.yml", "config/smtp.yml", "config/application.yml"); err != nil {
		panic(err)
	}

	location.GoogleAPIKey = Config.GoogleAPIKey
	location.BaiduAPIKey = Config.BaiduAPIKey

	if Config.S3.AccessKeyID != "" {
		oss.Storage = s3.New(&s3.Config{
			AccessID:  Config.S3.AccessKeyID,
			AccessKey: Config.S3.SecretAccessKey,
			Region:    Config.S3.Region,
			Bucket:    Config.S3.S3Bucket,
		})
	}

	// dialer := gomail.NewDialer(Config.SMTP.Host, Config.SMTP.Port, Config.SMTP.User, Config.SMTP.Password)
	// sender, err := dialer.Dial()

	// Mailer = mailer.New(&mailer.Config{
	// 	Sender: gomailer.New(&gomailer.Config{Sender: sender}),
	// })
	Mailer = mailer.New(&mailer.Config{
		Sender: logger.New(&logger.Config{}),
	})
}
<commit_msg>Add amazon pay config<commit_after>package config

import (
	"os"

	"github.com/jinzhu/configor"
	"github.com/qor/auth/providers/facebook"
	"github.com/qor/auth/providers/github"
	"github.com/qor/auth/providers/google"
	"github.com/qor/auth/providers/twitter"
	"github.com/qor/location"
	"github.com/qor/mailer"
	"github.com/qor/mailer/logger"
	"github.com/qor/media/oss"
	"github.com/qor/oss/s3"
	"github.com/qor/redirect_back"
	"github.com/qor/session/manager"
)

type SMTPConfig struct {
	Host     string
	Port     string
	User     string
	Password string
}

var Config = struct {
	Port uint `default:"7000" env:"PORT"`
	DB   struct {
		Name     string `env:"DBName" default:"qor_example"`
		Adapter  string `env:"DBAdapter" default:"mysql"`
		Host     string `env:"DBHost" default:"localhost"`
		Port     string `env:"DBPort" default:"3306"`
		User     string `env:"DBUser"`
		Password string `env:"DBPassword"`
	}
	S3 struct {
		AccessKeyID     string `env:"AWS_ACCESS_KEY_ID"`
		SecretAccessKey string `env:"AWS_SECRET_ACCESS_KEY"`
		Region          string `env:"AWS_Region"`
		S3Bucket        string `env:"AWS_Bucket"`
	}
	AmazonPay struct {
		MerchantID   string `env:"AmazonPayMerchantID"`
		AccessKey    string `env:"AmazonPayAccessKey"`
		SecretKey    string `env:"AmazonPaySecretKey"`
		Sandbox      bool   `env:"AmazonPaySandbox"`
		CurrencyCode string `env:"AmazonPayCurrencyCode"`
	}
	SMTP         SMTPConfig
	Github       github.Config
	Google       google.Config
	Facebook     facebook.Config
	Twitter      twitter.Config
	GoogleAPIKey string `env:"GoogleAPIKey"`
	BaiduAPIKey  string `env:"BaiduAPIKey"`
}{}

var (
	Root         = os.Getenv("GOPATH") + "/src/github.com/qor/qor-example"
	Mailer       *mailer.Mailer
	RedirectBack = redirect_back.New(&redirect_back.Config{
		SessionManager:  manager.SessionManager,
		IgnoredPrefixes: []string{"/auth"},
	})
)

func init() {
	if err := configor.Load(&Config, "config/database.yml", "config/smtp.yml", "config/application.yml"); err != nil {
		panic(err)
	}

	location.GoogleAPIKey = Config.GoogleAPIKey
	location.BaiduAPIKey = Config.BaiduAPIKey

	if Config.S3.AccessKeyID != "" {
		oss.Storage = s3.New(&s3.Config{
			AccessID:  Config.S3.AccessKeyID,
			AccessKey: Config.S3.SecretAccessKey,
			Region:    Config.S3.Region,
			Bucket:    Config.S3.S3Bucket,
		})
	}

	// dialer := gomail.NewDialer(Config.SMTP.Host, Config.SMTP.Port, Config.SMTP.User, Config.SMTP.Password)
	// sender, err := dialer.Dial()

	// Mailer = mailer.New(&mailer.Config{
	// 	Sender: gomailer.New(&gomailer.Config{Sender: sender}),
	// })
	Mailer = mailer.New(&mailer.Config{
		Sender: logger.New(&logger.Config{}),
	})
}
<|endoftext|>