<commit_before>package graylog

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal/tls"
	"github.com/influxdata/telegraf/plugins/inputs"
)

type ResponseMetrics struct {
	total   int
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	FullName string                 `json:"full_name"`
	Name     string                 `json:"name"`
	Type     string                 `json:"type"`
	Fields   map[string]interface{} `json:"metric"`
}

type GrayLog struct {
	Servers  []string
	Metrics  []string
	Username string
	Password string
	tls.ClientConfig

	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP respons object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type Messagebody struct {
	Metrics []string `json:"metrics"`
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## API endpoint, currently supported API:
  ##
  ##   - multiple  (Ex http://<host>:12900/system/metrics/multiple)
  ##   - namespace (Ex http://<host>:12900/system/metrics/namespace/{namespace})
  ##
  ## For namespace endpoint, the metrics array will be ignored for that call.
  ## Endpoint can contain namespace and multiple type calls.
  ##
  ## Please check http://[graylog-server-ip]:12900/api-browser for full list
  ## of endpoints
  servers = [
    "http://[graylog-server-ip]:12900/system/metrics/multiple",
  ]

  ## Metrics list
  ## List of metrics can be found on Graylog webservice documentation.
  ## Or by hitting the the web service api at:
  ##   http://[graylog-host]:12900/system/metrics
  metrics = [
    "jvm.cl.loaded",
    "jvm.memory.pools.Metaspace.committed"
  ]

  ## Username and password
  username = ""
  password = ""

  ## Optional TLS Config
  # tls_ca = "/etc/telegraf/ca.pem"
  # tls_cert = "/etc/telegraf/cert.pem"
  # tls_key = "/etc/telegraf/key.pem"
  ## Use TLS but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *GrayLog) SampleConfig() string {
	return sampleConfig
}

func (h *GrayLog) Description() string {
	return "Read flattened metrics from one or more GrayLog HTTP endpoints"
}

// Gathers data for all servers.
func (h *GrayLog) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := h.ClientConfig.TLSConfig()
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: time.Duration(3 * time.Second),
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   time.Duration(4 * time.Second),
		}
		h.client.SetHTTPClient(client)
	}

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			acc.AddError(h.gatherServer(acc, server))
		}(server)
	}

	wg.Wait()

	return nil
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *GrayLog) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)
	if err != nil {
		return err
	}
	requestURL, err := url.Parse(serverURL)
	host, port, _ := net.SplitHostPort(requestURL.Host)
	var dat ResponseMetrics
	if err != nil {
		return err
	}
	if err := json.Unmarshal([]byte(resp), &dat); err != nil {
		return err
	}
	for _, m_item := range dat.Metrics {
		fields := make(map[string]interface{})
		tags := map[string]string{
			"server": host,
			"port":   port,
			"name":   m_item.Name,
			"type":   m_item.Type,
		}
		h.flatten(m_item.Fields, fields, "")
		acc.AddFields(m_item.FullName, fields, tags)
	}
	return nil
}

// Flatten JSON hierarchy to produce field name and field value
// Parameters:
//    item: Item map to flatten
//    fields: Map to store generated fields.
//    id: Prefix for top level metric (empty string "")
// Returns:
//    void
func (h *GrayLog) flatten(item map[string]interface{}, fields map[string]interface{}, id string) {
	if id != "" {
		id = id + "_"
	}
	for k, i := range item {
		switch i.(type) {
		case int:
			fields[id+k] = i.(float64)
		case float64:
			fields[id+k] = i.(float64)
		case map[string]interface{}:
			h.flatten(i.(map[string]interface{}), fields, id+k)
		default:
		}
	}
}

// Sends an HTTP request to the server using the GrayLog object's HTTPClient.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *GrayLog) sendRequest(serverURL string) (string, float64, error) {
	headers := map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/json",
	}
	method := "GET"
	content := bytes.NewBufferString("")
	headers["Authorization"] = "Basic " + base64.URLEncoding.EncodeToString([]byte(h.Username+":"+h.Password))
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
	}
	if strings.Contains(requestURL.String(), "multiple") {
		m := &Messagebody{Metrics: h.Metrics}
		http_body, err := json.Marshal(m)
		if err != nil {
			return "", -1, fmt.Errorf("Invalid list of Metrics %s", h.Metrics)
		}
		method = "POST"
		content = bytes.NewBuffer(http_body)
	}
	req, err := http.NewRequest(method, requestURL.String(), content)
	if err != nil {
		return "", -1, err
	}
	// Add header parameters
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return string(body), responseTime, err
	}

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}
	return string(body), responseTime, err
}

func init() {
	inputs.Add("graylog", func() telegraf.Input {
		return &GrayLog{
			client: &RealHTTPClient{},
		}
	})
}
<commit_msg>Add X-Requested-By header to graylog input (#5011)<commit_after>package graylog

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal/tls"
	"github.com/influxdata/telegraf/plugins/inputs"
)

type ResponseMetrics struct {
	total   int
	Metrics []Metric `json:"metrics"`
}

type Metric struct {
	FullName string                 `json:"full_name"`
	Name     string                 `json:"name"`
	Type     string                 `json:"type"`
	Fields   map[string]interface{} `json:"metric"`
}

type GrayLog struct {
	Servers  []string
	Metrics  []string
	Username string
	Password string
	tls.ClientConfig

	client HTTPClient
}

type HTTPClient interface {
	// Returns the result of an http request
	//
	// Parameters:
	// req: HTTP request object
	//
	// Returns:
	// http.Response:  HTTP respons object
	// error        :  Any error that may have occurred
	MakeRequest(req *http.Request) (*http.Response, error)

	SetHTTPClient(client *http.Client)
	HTTPClient() *http.Client
}

type Messagebody struct {
	Metrics []string `json:"metrics"`
}

type RealHTTPClient struct {
	client *http.Client
}

func (c *RealHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) {
	return c.client.Do(req)
}

func (c *RealHTTPClient) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *RealHTTPClient) HTTPClient() *http.Client {
	return c.client
}

var sampleConfig = `
  ## API endpoint, currently supported API:
  ##
  ##   - multiple  (Ex http://<host>:12900/system/metrics/multiple)
  ##   - namespace (Ex http://<host>:12900/system/metrics/namespace/{namespace})
  ##
  ## For namespace endpoint, the metrics array will be ignored for that call.
  ## Endpoint can contain namespace and multiple type calls.
  ##
  ## Please check http://[graylog-server-ip]:12900/api-browser for full list
  ## of endpoints
  servers = [
    "http://[graylog-server-ip]:12900/system/metrics/multiple",
  ]

  ## Metrics list
  ## List of metrics can be found on Graylog webservice documentation.
  ## Or by hitting the the web service api at:
  ##   http://[graylog-host]:12900/system/metrics
  metrics = [
    "jvm.cl.loaded",
    "jvm.memory.pools.Metaspace.committed"
  ]

  ## Username and password
  username = ""
  password = ""

  ## Optional TLS Config
  # tls_ca = "/etc/telegraf/ca.pem"
  # tls_cert = "/etc/telegraf/cert.pem"
  # tls_key = "/etc/telegraf/key.pem"
  ## Use TLS but skip chain & host verification
  # insecure_skip_verify = false
`

func (h *GrayLog) SampleConfig() string {
	return sampleConfig
}

func (h *GrayLog) Description() string {
	return "Read flattened metrics from one or more GrayLog HTTP endpoints"
}

// Gathers data for all servers.
func (h *GrayLog) Gather(acc telegraf.Accumulator) error {
	var wg sync.WaitGroup

	if h.client.HTTPClient() == nil {
		tlsCfg, err := h.ClientConfig.TLSConfig()
		if err != nil {
			return err
		}
		tr := &http.Transport{
			ResponseHeaderTimeout: time.Duration(3 * time.Second),
			TLSClientConfig:       tlsCfg,
		}
		client := &http.Client{
			Transport: tr,
			Timeout:   time.Duration(4 * time.Second),
		}
		h.client.SetHTTPClient(client)
	}

	for _, server := range h.Servers {
		wg.Add(1)
		go func(server string) {
			defer wg.Done()
			acc.AddError(h.gatherServer(acc, server))
		}(server)
	}

	wg.Wait()

	return nil
}

// Gathers data from a particular server
// Parameters:
//     acc      : The telegraf Accumulator to use
//     serverURL: endpoint to send request to
//     service  : the service being queried
//
// Returns:
//     error: Any error that may have occurred
func (h *GrayLog) gatherServer(
	acc telegraf.Accumulator,
	serverURL string,
) error {
	resp, _, err := h.sendRequest(serverURL)
	if err != nil {
		return err
	}
	requestURL, err := url.Parse(serverURL)
	host, port, _ := net.SplitHostPort(requestURL.Host)
	var dat ResponseMetrics
	if err != nil {
		return err
	}
	if err := json.Unmarshal([]byte(resp), &dat); err != nil {
		return err
	}
	for _, m_item := range dat.Metrics {
		fields := make(map[string]interface{})
		tags := map[string]string{
			"server": host,
			"port":   port,
			"name":   m_item.Name,
			"type":   m_item.Type,
		}
		h.flatten(m_item.Fields, fields, "")
		acc.AddFields(m_item.FullName, fields, tags)
	}
	return nil
}

// Flatten JSON hierarchy to produce field name and field value
// Parameters:
//    item: Item map to flatten
//    fields: Map to store generated fields.
//    id: Prefix for top level metric (empty string "")
// Returns:
//    void
func (h *GrayLog) flatten(item map[string]interface{}, fields map[string]interface{}, id string) {
	if id != "" {
		id = id + "_"
	}
	for k, i := range item {
		switch i.(type) {
		case int:
			fields[id+k] = i.(float64)
		case float64:
			fields[id+k] = i.(float64)
		case map[string]interface{}:
			h.flatten(i.(map[string]interface{}), fields, id+k)
		default:
		}
	}
}

// Sends an HTTP request to the server using the GrayLog object's HTTPClient.
// Parameters:
//     serverURL: endpoint to send request to
//
// Returns:
//     string: body of the response
//     error : Any error that may have occurred
func (h *GrayLog) sendRequest(serverURL string) (string, float64, error) {
	headers := map[string]string{
		"Content-Type": "application/json",
		"Accept":       "application/json",
	}
	method := "GET"
	content := bytes.NewBufferString("")
	headers["Authorization"] = "Basic " + base64.URLEncoding.EncodeToString([]byte(h.Username+":"+h.Password))
	// Prepare URL
	requestURL, err := url.Parse(serverURL)
	if err != nil {
		return "", -1, fmt.Errorf("Invalid server URL \"%s\"", serverURL)
	}
	// Add X-Requested-By header
	headers["X-Requested-By"] = requestURL.Hostname()

	if strings.Contains(requestURL.String(), "multiple") {
		m := &Messagebody{Metrics: h.Metrics}
		http_body, err := json.Marshal(m)
		if err != nil {
			return "", -1, fmt.Errorf("Invalid list of Metrics %s", h.Metrics)
		}
		method = "POST"
		content = bytes.NewBuffer(http_body)
	}
	req, err := http.NewRequest(method, requestURL.String(), content)
	if err != nil {
		return "", -1, err
	}
	// Add header parameters
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	start := time.Now()
	resp, err := h.client.MakeRequest(req)
	if err != nil {
		return "", -1, err
	}

	defer resp.Body.Close()
	responseTime := time.Since(start).Seconds()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return string(body), responseTime, err
	}

	// Process response
	if resp.StatusCode != http.StatusOK {
		err = fmt.Errorf("Response from url \"%s\" has status code %d (%s), expected %d (%s)",
			requestURL.String(),
			resp.StatusCode,
			http.StatusText(resp.StatusCode),
			http.StatusOK,
			http.StatusText(http.StatusOK))
		return string(body), responseTime, err
	}
	return string(body), responseTime, err
}

func init() {
	inputs.Add("graylog", func() telegraf.Input {
		return &GrayLog{
			client: &RealHTTPClient{},
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package upload

import (
	"io"
	"os"
	"testing"

	"github.com/issue9/assert"
)

// 复制文件到output目录下，并重命名。
func copyBackgroundFile(a *assert.Assertion, dest, src string) {
	destFile, err := os.Create(dest)
	a.NotError(err).NotNil(destFile)

	srcFile, err := os.Open(src)
	a.NotError(err).NotNil(srcFile)

	n, err := io.Copy(destFile, srcFile)
	a.NotError(err).True(n > 0)

	destFile.Close()
	srcFile.Close()
}

// 输出各种组合的水印图片。
// bgExt 表示背景图片的扩展名。
// water 表示水印图片的扩展名。
func output(a *assert.Assertion, pos Pos, bgExt, waterExt string) {
	water := "./testdata/watermark" + waterExt
	src := "./testdata/background" + bgExt
	dest := "./testdata/output/" + waterExt[1:] + bgExt

	copyBackgroundFile(a, dest, src)

	// 添加水印
	w, err := NewWatermark(water, 10, pos)
	a.NotError(err).NotNil(w)
	a.NotError(w.MarkFile(dest))
}

func TestUploadWatermark(t *testing.T) {
	a := assert.New(t)

	output(a, TopLeft, ".jpg", ".jpg")
	output(a, TopRight, ".jpg", ".png")
	output(a, Center, ".jpg", ".gif")

	output(a, BottomLeft, ".png", ".jpg")
	output(a, BottomRight, ".png", ".png")
	output(a, Center, ".png", ".gif")

	output(a, BottomLeft, ".gif", ".jpg")
	output(a, BottomRight, ".gif", ".png")
	output(a, Center, ".gif", ".gif")
}

// BenchmarkWater_MakeImage_500xJPEG	   50000	     30030 ns/op
func BenchmarkWater_MakeImage_500xJPEG(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.jpg", "./testdata/background.jpg")

	w, err := NewWatermark("./testdata/watermark.jpg", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/bench.jpg", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".jpg")
	}
}

// BenchmarkWater_MakeImage_500xPNG	  500000	      2482 ns/op
func BenchmarkWater_MakeImage_500xPNG(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.png", "./testdata/background.png")

	w, err := NewWatermark("./testdata/watermark.png", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/bench.png", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".png")
	}
}

// BenchmarkWater_MakeImage_500xGIF	  200000	      9389 ns/op
func BenchmarkWater_MakeImage_500xGIF(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.gif", "./testdata/background.gif")

	w, err := NewWatermark("./testdata/watermark.gif", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/gif.png", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".gif")
	}
}
<commit_msg>修正BenchmarkWater_MakeImage_500xGIF的错误<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package upload

import (
	"io"
	"os"
	"testing"

	"github.com/issue9/assert"
)

// 复制文件到output目录下，并重命名。
func copyBackgroundFile(a *assert.Assertion, dest, src string) {
	destFile, err := os.Create(dest)
	a.NotError(err).NotNil(destFile)

	srcFile, err := os.Open(src)
	a.NotError(err).NotNil(srcFile)

	n, err := io.Copy(destFile, srcFile)
	a.NotError(err).True(n > 0)

	destFile.Close()
	srcFile.Close()
}

// 输出各种组合的水印图片。
// bgExt 表示背景图片的扩展名。
// water 表示水印图片的扩展名。
func output(a *assert.Assertion, pos Pos, bgExt, waterExt string) {
	water := "./testdata/watermark" + waterExt
	src := "./testdata/background" + bgExt
	dest := "./testdata/output/" + waterExt[1:] + bgExt

	copyBackgroundFile(a, dest, src)

	// 添加水印
	w, err := NewWatermark(water, 10, pos)
	a.NotError(err).NotNil(w)
	a.NotError(w.MarkFile(dest))
}

func TestUploadWatermark(t *testing.T) {
	a := assert.New(t)

	output(a, TopLeft, ".jpg", ".jpg")
	output(a, TopRight, ".jpg", ".png")
	output(a, Center, ".jpg", ".gif")

	output(a, BottomLeft, ".png", ".jpg")
	output(a, BottomRight, ".png", ".png")
	output(a, Center, ".png", ".gif")

	output(a, BottomLeft, ".gif", ".jpg")
	output(a, BottomRight, ".gif", ".png")
	output(a, Center, ".gif", ".gif")
}

// BenchmarkWater_MakeImage_500xJPEG	   50000	     30030 ns/op
func BenchmarkWater_MakeImage_500xJPEG(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.jpg", "./testdata/background.jpg")

	w, err := NewWatermark("./testdata/watermark.jpg", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/bench.jpg", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".jpg")
	}
}

// BenchmarkWater_MakeImage_500xPNG	  500000	      2482 ns/op
func BenchmarkWater_MakeImage_500xPNG(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.png", "./testdata/background.png")

	w, err := NewWatermark("./testdata/watermark.png", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/bench.png", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".png")
	}
}

// BenchmarkWater_MakeImage_500xGIF	  100000	     13633 ns/op
func BenchmarkWater_MakeImage_500xGIF(b *testing.B) {
	a := assert.New(b)

	copyBackgroundFile(a, "./testdata/output/bench.gif", "./testdata/background.gif")

	w, err := NewWatermark("./testdata/watermark.gif", 10, TopLeft)
	a.NotError(err).NotNil(w)

	file, err := os.OpenFile("./testdata/output/bench.gif", os.O_RDWR, os.ModePerm)
	a.NotError(err).NotNil(file)
	defer file.Close()

	for i := 0; i < b.N; i++ {
		w.Mark(file, ".gif")
	}
}
<|endoftext|>


<commit_before>package envs

import (
	"encoding/json"
	"net/http"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// Delete : Deletes a service by name
func Delete(au models.User, name string) (int, []byte) {
	var err error
	var def models.Definition
	var s models.Env
	var dt models.Project

	if s, err = s.FindLastByName(name); err != nil {
		h.L.Error(err.Error())
		return 500, []byte("Internal error")
	}

	if s.ID == "" {
		println("innnn")
		return 404, []byte("Specified environment name does not exist")
	}

	// Get datacenter
	if err = dt.FindByID(s.DatacenterID); err != nil {
		h.L.Error(err.Error())
		return 400, []byte("Specified project does not exist")
	}

	if st, res := h.IsAuthorizedToResource(&au, h.DeleteEnv, s.GetType(), name); st != 200 {
		return st, res
	}

	if s.Status == "in_progress" {
		return 400, []byte(`"Environment is already applying some changes, please wait until they are done"`)
	}

	credentials := models.Project{}
	if s.Credentials != nil {
		newDT := models.Project{
			Credentials: s.Credentials,
		}
		credentials.Override(newDT)
	}

	dt.Override(credentials)
	rawDatacenter, err := json.Marshal(dt)
	if err != nil {
		h.L.Error(err.Error())
		return 500, []byte("Internal error trying to get the project")
	}

	query := []byte(`{"previous_id":"` + s.ID + `","datacenter":` + string(rawDatacenter) + `}`)
	//++++++++++++++++++
	body, err := def.MapDeletion(query)

	if err != nil {
		h.L.Error(err.Error())
		return 500, []byte(`"Couldn't map the environment"`)
	}
	if err := s.RequestDeletion(body); err != nil {
		h.L.Error(err.Error())
		return 500, []byte(`"Couldn't call service.delete"`)
	}

	return http.StatusOK, []byte(`{"id":"` + s.ID + `"}`)
}
<commit_msg>fixing credential mapping on deletion<commit_after>package envs

import (
	"encoding/json"
	"net/http"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/ernestio/api-gateway/models"
)

// Delete : Deletes a service by name
func Delete(au models.User, name string) (int, []byte) {
	var err error
	var def models.Definition
	var s models.Env

	dt := models.Project{
		Credentials: make(map[string]interface{}),
	}

	if s, err = s.FindLastByName(name); err != nil {
		h.L.Error(err.Error())
		return 500, []byte("Internal error")
	}

	if s.ID == "" {
		println("innnn")
		return 404, []byte("Specified environment name does not exist")
	}

	// Get datacenter
	if err = dt.FindByID(s.DatacenterID); err != nil {
		h.L.Error(err.Error())
		return 400, []byte("Specified project does not exist")
	}

	if st, res := h.IsAuthorizedToResource(&au, h.DeleteEnv, s.GetType(), name); st != 200 {
		return st, res
	}

	if s.Status == "in_progress" {
		return 400, []byte(`"Environment is already applying some changes, please wait until they are done"`)
	}

	credentials := models.Project{}
	if s.Credentials != nil {
		newDT := models.Project{
			Credentials: s.Credentials,
		}
		credentials.Override(newDT)
	}

	dt.Override(credentials)
	rawDatacenter, err := json.Marshal(dt)
	if err != nil {
		h.L.Error(err.Error())
		return 500, []byte("Internal error trying to get the project")
	}

	query := []byte(`{"previous_id":"` + s.ID + `","datacenter":` + string(rawDatacenter) + `}`)
	//++++++++++++++++++
	body, err := def.MapDeletion(query)

	if err != nil {
		h.L.Error(err.Error())
		return 500, []byte(`"Couldn't map the environment"`)
	}
	if err := s.RequestDeletion(body); err != nil {
		h.L.Error(err.Error())
		return 500, []byte(`"Couldn't call service.delete"`)
	}

	return http.StatusOK, []byte(`{"id":"` + s.ID + `"}`)
}
<|endoftext|>


<commit_before>package cloudup

import (
	"encoding/json"
	"fmt"
	"github.com/golang/glog"
	"k8s.io/kops/upup/pkg/api"
	"k8s.io/kops/upup/pkg/fi/vfs"
	"strings"
)

const IAMPolicyDefaultVersion = "2012-10-17"

type IAMPolicy struct {
	Version   string
	Statement []*IAMStatement
}

func (p *IAMPolicy) AsJSON() (string, error) {
	j, err := json.MarshalIndent(p, "", "  ")
	if err != nil {
		return "", fmt.Errorf("error marshaling policy to JSON: %v", err)
	}
	return string(j), nil
}

type IAMStatementEffect string

const IAMStatementEffectAllow IAMStatementEffect = "Allow"

type IAMStatement struct {
	Effect   IAMStatementEffect
	Action   []string
	Resource []string
}

type IAMPolicyBuilder struct {
	Cluster *api.Cluster
	Role    api.InstanceGroupRole
	Region  string
}

func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
	iamPrefix := b.IAMPrefix()

	p := &IAMPolicy{
		Version: IAMPolicyDefaultVersion,
	}

	if b.Role == api.InstanceGroupRoleNode {
		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"ec2:Describe*"},
			Resource: []string{"*"},
		})

		// No longer needed in 1.3
		//p.Statement = append(p.Statement, &IAMStatement{
		//	Effect: IAMStatementEffectAllow,
		//	Action: []string{ "ec2:AttachVolume" },
		//	Resource: []string{"*"},
		//})
		//p.Statement = append(p.Statement, &IAMStatement{
		//	Effect: IAMStatementEffectAllow,
		//	Action: []string{ "ec2:DetachVolume" },
		//	Resource: []string{"*"},
		//})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"route53:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect: IAMStatementEffectAllow,
			Action: []string{
				"ecr:GetAuthorizationToken",
				"ecr:BatchCheckLayerAvailability",
				"ecr:GetDownloadUrlForLayer",
				"ecr:GetRepositoryPolicy",
				"ecr:DescribeRepositories",
				"ecr:ListImages",
				"ecr:BatchGetImage",
			},
			Resource: []string{"*"},
		})
	}

	if b.Role == api.InstanceGroupRoleMaster {
		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"ec2:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"route53:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"elasticloadbalancing:*"},
			Resource: []string{"*"},
		})

		// Restrict the KMS permissions to only the keys that are being used
		var set = make(map[string]bool)
		for _, e := range b.Cluster.Spec.EtcdClusters {
			for _, m := range e.Members {
				if m.KmsKeyId != nil {
					set[*m.KmsKeyId] = true
				}
			}
		}

		keyIds := make([]string, 0, len(set))
		for k := range set {
			keyIds = append(keyIds, k)
		}

		if len(keyIds) > 0 {
			p.Statement = append(p.Statement, &IAMStatement{
				Effect:   IAMStatementEffectAllow,
				Action:   []string{
					"kms:Encrypt",
					"kms:Decrypt",
					"kms:ReEncrypt*",
					"kms:GenerateDataKey*",
					"kms:DescribeKey",
					"kms:CreateGrant",
					"kms:ListGrants",
					"kms:RevokeGrant",
				},
				Resource: keyIds,
			})
		}
	}

	// For S3 IAM permissions, we grant permissions to subtrees.  So find the parents;
	// we don't need to grant mypath and mypath/child.
	var roots []string
	{
		var locations []string

		for _, p := range []string{
			b.Cluster.Spec.KeyStore,
			b.Cluster.Spec.SecretStore,
			b.Cluster.Spec.ConfigStore,
		} {
			if p == "" {
				continue
			}

			if !strings.HasSuffix(p, "/") {
				p = p + "/"
			}
			locations = append(locations, p)
		}

		for i, l := range locations {
			isTopLevel := true
			for j := range locations {
				if i == j {
					continue
				}
				if strings.HasPrefix(l, locations[j]) {
					glog.V(4).Infof("Ignoring location %q because found parent %q", l, locations[j])
					isTopLevel = false
				}
			}
			if isTopLevel {
				glog.V(4).Infof("Found root location %q", l)
				roots = append(roots, l)
			}
		}
	}

	for _, root := range roots {
		vfsPath, err := vfs.Context.BuildVfsPath(root)
		if err != nil {
			return nil, fmt.Errorf("cannot parse VFS path %q: %v", root, err)
		}

		if s3Path, ok := vfsPath.(*vfs.S3Path); ok {
			// Note that the config store may itself be a subdirectory of a bucket
			iamS3Path := s3Path.Bucket() + "/" + s3Path.Key()
			iamS3Path = strings.TrimSuffix(iamS3Path, "/")

			p.Statement = append(p.Statement, &IAMStatement{
				Effect: IAMStatementEffectAllow,
				Action: []string{"s3:*"},
				Resource: []string{
					iamPrefix + ":s3:::" + iamS3Path,
					iamPrefix + ":s3:::" + iamS3Path + "/*",
				},
			})

			p.Statement = append(p.Statement, &IAMStatement{
				Effect: IAMStatementEffectAllow,
				Action: []string{"s3:GetBucketLocation", "s3:ListBucket"},
				Resource: []string{
					iamPrefix + ":s3:::" + s3Path.Bucket(),
				},
			})
		} else {
			// We could implement this approach, but it seems better to get all clouds using cluster-readable storage
			return nil, fmt.Errorf("path is not cluster readable: %v", root)
		}
	}

	return p, nil
}

// IAMPrefix returns the prefix for AWS ARNs in the current region, for use with IAM
// it is arn:aws everywhere but in cn-north, where it is arn:aws-cn
func (b *IAMPolicyBuilder) IAMPrefix() string {
	switch b.Region {
	case "cn-north-1":
		return "arn:aws-cn"
	default:
		return "arn:aws"
	}
}
<commit_msg>Use sets.String when building IAM policy<commit_after>package cloudup

import (
	"encoding/json"
	"fmt"
	"github.com/golang/glog"
	"k8s.io/kops/upup/pkg/api"
	"k8s.io/kops/upup/pkg/fi/vfs"
	"k8s.io/kubernetes/pkg/util/sets"
	"strings"
)

const IAMPolicyDefaultVersion = "2012-10-17"

type IAMPolicy struct {
	Version   string
	Statement []*IAMStatement
}

func (p *IAMPolicy) AsJSON() (string, error) {
	j, err := json.MarshalIndent(p, "", "  ")
	if err != nil {
		return "", fmt.Errorf("error marshaling policy to JSON: %v", err)
	}
	return string(j), nil
}

type IAMStatementEffect string

const IAMStatementEffectAllow IAMStatementEffect = "Allow"

type IAMStatement struct {
	Effect   IAMStatementEffect
	Action   []string
	Resource []string
}

type IAMPolicyBuilder struct {
	Cluster *api.Cluster
	Role    api.InstanceGroupRole
	Region  string
}

func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
	iamPrefix := b.IAMPrefix()

	p := &IAMPolicy{
		Version: IAMPolicyDefaultVersion,
	}

	if b.Role == api.InstanceGroupRoleNode {
		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"ec2:Describe*"},
			Resource: []string{"*"},
		})

		// No longer needed in 1.3
		//p.Statement = append(p.Statement, &IAMStatement{
		//	Effect: IAMStatementEffectAllow,
		//	Action: []string{ "ec2:AttachVolume" },
		//	Resource: []string{"*"},
		//})
		//p.Statement = append(p.Statement, &IAMStatement{
		//	Effect: IAMStatementEffectAllow,
		//	Action: []string{ "ec2:DetachVolume" },
		//	Resource: []string{"*"},
		//})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"route53:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect: IAMStatementEffectAllow,
			Action: []string{
				"ecr:GetAuthorizationToken",
				"ecr:BatchCheckLayerAvailability",
				"ecr:GetDownloadUrlForLayer",
				"ecr:GetRepositoryPolicy",
				"ecr:DescribeRepositories",
				"ecr:ListImages",
				"ecr:BatchGetImage",
			},
			Resource: []string{"*"},
		})
	}

	if b.Role == api.InstanceGroupRoleMaster {
		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"ec2:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"route53:*"},
			Resource: []string{"*"},
		})

		p.Statement = append(p.Statement, &IAMStatement{
			Effect:   IAMStatementEffectAllow,
			Action:   []string{"elasticloadbalancing:*"},
			Resource: []string{"*"},
		})

		// Restrict the KMS permissions to only the keys that are being used
		kmsKeyIDs := sets.NewString()
		for _, e := range b.Cluster.Spec.EtcdClusters {
			for _, m := range e.Members {
				if m.KmsKeyId != nil {
					kmsKeyIDs.Insert(*m.KmsKeyId)
				}
			}
		}

		if kmsKeyIDs.Len() > 0 {
			p.Statement = append(p.Statement, &IAMStatement{
				Effect: IAMStatementEffectAllow,
				Action: []string{
					"kms:Encrypt",
					"kms:Decrypt",
					"kms:ReEncrypt*",
					"kms:GenerateDataKey*",
					"kms:DescribeKey",
					"kms:CreateGrant",
					"kms:ListGrants",
					"kms:RevokeGrant",
				},
				Resource: kmsKeyIDs.List(),
			})
		}
	}

	// For S3 IAM permissions, we grant permissions to subtrees.  So find the parents;
	// we don't need to grant mypath and mypath/child.
	var roots []string
	{
		var locations []string

		for _, p := range []string{
			b.Cluster.Spec.KeyStore,
			b.Cluster.Spec.SecretStore,
			b.Cluster.Spec.ConfigStore,
		} {
			if p == "" {
				continue
			}

			if !strings.HasSuffix(p, "/") {
				p = p + "/"
			}
			locations = append(locations, p)
		}

		for i, l := range locations {
			isTopLevel := true
			for j := range locations {
				if i == j {
					continue
				}
				if strings.HasPrefix(l, locations[j]) {
					glog.V(4).Infof("Ignoring location %q because found parent %q", l, locations[j])
					isTopLevel = false
				}
			}
			if isTopLevel {
				glog.V(4).Infof("Found root location %q", l)
				roots = append(roots, l)
			}
		}
	}

	for _, root := range roots {
		vfsPath, err := vfs.Context.BuildVfsPath(root)
		if err != nil {
			return nil, fmt.Errorf("cannot parse VFS path %q: %v", root, err)
		}

		if s3Path, ok := vfsPath.(*vfs.S3Path); ok {
			// Note that the config store may itself be a subdirectory of a bucket
			iamS3Path := s3Path.Bucket() + "/" + s3Path.Key()
			iamS3Path = strings.TrimSuffix(iamS3Path, "/")

			p.Statement = append(p.Statement, &IAMStatement{
				Effect: IAMStatementEffectAllow,
				Action: []string{"s3:*"},
				Resource: []string{
					iamPrefix + ":s3:::" + iamS3Path,
					iamPrefix + ":s3:::" + iamS3Path + "/*",
				},
			})

			p.Statement = append(p.Statement, &IAMStatement{
				Effect: IAMStatementEffectAllow,
				Action: []string{"s3:GetBucketLocation", "s3:ListBucket"},
				Resource: []string{
					iamPrefix + ":s3:::" + s3Path.Bucket(),
				},
			})
		} else {
			// We could implement this approach, but it seems better to get all clouds using cluster-readable storage
			return nil, fmt.Errorf("path is not cluster readable: %v", root)
		}
	}

	return p, nil
}

// IAMPrefix returns the prefix for AWS ARNs in the current region, for use with IAM
// it is arn:aws everywhere but in cn-north, where it is arn:aws-cn
func (b *IAMPolicyBuilder) IAMPrefix() string {
	switch b.Region {
	case "cn-north-1":
		return "arn:aws-cn"
	default:
		return "arn:aws"
	}
}
<|endoftext|>


<commit_before>package eventstore

import (
	. "github.com/gucumber/gucumber"
	"github.com/xtracdev/envinject"
	"os"
)

var testEnv *envinject.InjectedEnv
var configErrors []string

func init() {
	Given(`^some tests to run$`, func() {
	})

	Then(`^the database connection configuration is read from the environment$`, func() {
	})

	GlobalContext.BeforeAll(func() {
		os.Unsetenv(envinject.ParamPrefixEnvVar)
		var err error
		testEnv, err = envinject.NewInjectedEnv()
		if err != nil {
			configErrors = append(configErrors, err.Error())
		}
	})

}
<commit_msg>formatting<commit_after>package eventstore

import (
	. "github.com/gucumber/gucumber"
	"github.com/xtracdev/envinject"
	"os"
)

var testEnv *envinject.InjectedEnv
var configErrors []string

func init() {
	Given(`^some tests to run$`, func() {
	})

	Then(`^the database connection configuration is read from the environment$`, func() {
	})

	GlobalContext.BeforeAll(func() {
		os.Unsetenv(envinject.ParamPathEnvVar)
		var err error
		testEnv, err = envinject.NewInjectedEnv()
		if err != nil {
			configErrors = append(configErrors, err.Error())
		}
	})

}
<|endoftext|>


<commit_before>package httptracker

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/zeebo/bencode"
)

type HTTPTracker struct {
	rawURL            string
	url               *url.URL
	log               logger.Logger
	http              *http.Client
	transport         *http.Transport
	trackerID         string
	userAgent         string
	maxResponseLength int64
}

var _ tracker.Tracker = (*HTTPTracker)(nil)

func New(rawURL string, u *url.URL, timeout time.Duration, t *http.Transport, userAgent string, maxResponseLength int64) *HTTPTracker {
	return &HTTPTracker{
		rawURL:            rawURL,
		url:               u,
		log:               logger.New("tracker " + u.String()),
		transport:         t,
		userAgent:         userAgent,
		maxResponseLength: maxResponseLength,
		http: &http.Client{
			Timeout:   timeout,
			Transport: t,
		},
	}
}

func (t *HTTPTracker) URL() string {
	return t.rawURL
}

func (t *HTTPTracker) Announce(ctx context.Context, req tracker.AnnounceRequest) (*tracker.AnnounceResponse, error) {
	u := *t.url

	q := u.Query()
	q.Set("info_hash", string(req.Torrent.InfoHash[:]))
	q.Set("peer_id", string(req.Torrent.PeerID[:]))
	q.Set("port", strconv.FormatUint(uint64(req.Torrent.Port), 10))
	q.Set("uploaded", strconv.FormatInt(req.Torrent.BytesUploaded, 10))
	q.Set("downloaded", strconv.FormatInt(req.Torrent.BytesDownloaded, 10))
	q.Set("left", strconv.FormatInt(req.Torrent.BytesLeft, 10))
	q.Set("compact", "1")
	q.Set("no_peer_id", "1")
	q.Set("numwant", strconv.Itoa(req.NumWant))
	if req.Event != tracker.EventNone {
		q.Set("event", req.Event.String())
	}
	if t.trackerID != "" {
		q.Set("trackerid", t.trackerID)
	}

	u.RawQuery = q.Encode()
	t.log.Debugf("making request to: %q", u.String())

	httpReq := &http.Request{
		Method:     http.MethodGet,
		URL:        &u,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
		Host:       u.Host,
	}
	httpReq = httpReq.WithContext(ctx)

	httpReq.Header.Set("User-Agent", t.userAgent)

	doReq := func() ([]byte, error) {
		resp, err := t.http.Do(httpReq)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		if resp.StatusCode != 200 {
			data, _ := ioutil.ReadAll(resp.Body)
			return nil, fmt.Errorf("status not 200 OK (status: %d body: %q)", resp.StatusCode, string(data))
		}
		if resp.ContentLength > t.maxResponseLength {
			return nil, fmt.Errorf("tracker respsonse too large: %d", resp.ContentLength)
		}
		r := io.LimitReader(resp.Body, t.maxResponseLength)
		return ioutil.ReadAll(r)
	}

	body, err := doReq()
	if uerr, ok := err.(*url.Error); ok && uerr.Err == context.Canceled {
		return nil, context.Canceled
	}
	if err != nil {
		return nil, err
	}

	var response announceResponse
	err = bencode.DecodeBytes(body, &response)
	if err != nil {
		return nil, err
	}

	if response.WarningMessage != "" {
		t.log.Warning(response.WarningMessage)
	}
	if response.FailureReason != "" {
		retryIn, _ := strconv.Atoi(response.RetryIn)
		return nil, &tracker.Error{
			FailureReason: response.FailureReason,
			RetryIn:       time.Duration(retryIn) * time.Minute,
		}
	}

	if response.TrackerID != "" {
		t.trackerID = response.TrackerID
	}

	// Peers may be in binary or dictionary model.
	var peers []*net.TCPAddr
	if len(response.Peers) > 0 {
		if response.Peers[0] == 'l' {
			peers, err = parsePeersDictionary(response.Peers)
		} else {
			var b []byte
			err = bencode.DecodeBytes(response.Peers, &b)
			if err != nil {
				return nil, err
			}
			peers, err = tracker.DecodePeersCompact(b)
		}
	}
	if err != nil {
		return nil, err
	}

	// Filter external IP
	if len(response.ExternalIP) != 0 {
		for i, p := range peers {
			if bytes.Equal(p.IP[:], response.ExternalIP) {
				peers[i], peers = peers[len(peers)-1], peers[:len(peers)-1]
				break
			}
		}
	}

	return &tracker.AnnounceResponse{
		Interval:    time.Duration(response.Interval) * time.Second,
		MinInterval: time.Duration(response.MinInterval) * time.Second,
		Leechers:    response.Incomplete,
		Seeders:     response.Complete,
		Peers:       peers,
	}, nil
}

func parsePeersDictionary(b bencode.RawMessage) ([]*net.TCPAddr, error) {
	var peers []struct {
		IP   string `bencode:"ip"`
		Port uint16 `bencode:"port"`
	}
	err := bencode.DecodeBytes(b, &peers)
	if err != nil {
		return nil, err
	}

	addrs := make([]*net.TCPAddr, len(peers))
	for i, p := range peers {
		pe := &net.TCPAddr{IP: net.ParseIP(p.IP), Port: int(p.Port)}
		addrs[i] = pe
	}
	return addrs, err
}
<commit_msg>check http status code only if cannot decode response<commit_after>package httptracker

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/cenkalti/rain/internal/logger"
	"github.com/cenkalti/rain/internal/tracker"
	"github.com/zeebo/bencode"
)

type HTTPTracker struct {
	rawURL            string
	url               *url.URL
	log               logger.Logger
	http              *http.Client
	transport         *http.Transport
	trackerID         string
	userAgent         string
	maxResponseLength int64
}

var _ tracker.Tracker = (*HTTPTracker)(nil)

func New(rawURL string, u *url.URL, timeout time.Duration, t *http.Transport, userAgent string, maxResponseLength int64) *HTTPTracker {
	return &HTTPTracker{
		rawURL:            rawURL,
		url:               u,
		log:               logger.New("tracker " + u.String()),
		transport:         t,
		userAgent:         userAgent,
		maxResponseLength: maxResponseLength,
		http: &http.Client{
			Timeout:   timeout,
			Transport: t,
		},
	}
}

func (t *HTTPTracker) URL() string {
	return t.rawURL
}

func (t *HTTPTracker) Announce(ctx context.Context, req tracker.AnnounceRequest) (*tracker.AnnounceResponse, error) {
	u := *t.url

	q := u.Query()
	q.Set("info_hash", string(req.Torrent.InfoHash[:]))
	q.Set("peer_id", string(req.Torrent.PeerID[:]))
	q.Set("port", strconv.FormatUint(uint64(req.Torrent.Port), 10))
	q.Set("uploaded", strconv.FormatInt(req.Torrent.BytesUploaded, 10))
	q.Set("downloaded", strconv.FormatInt(req.Torrent.BytesDownloaded, 10))
	q.Set("left", strconv.FormatInt(req.Torrent.BytesLeft, 10))
	q.Set("compact", "1")
	q.Set("no_peer_id", "1")
	q.Set("numwant", strconv.Itoa(req.NumWant))
	if req.Event != tracker.EventNone {
		q.Set("event", req.Event.String())
	}
	if t.trackerID != "" {
		q.Set("trackerid", t.trackerID)
	}

	u.RawQuery = q.Encode()
	t.log.Debugf("making request to: %q", u.String())

	httpReq := &http.Request{
		Method:     http.MethodGet,
		URL:        &u,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
		Host:       u.Host,
	}
	httpReq = httpReq.WithContext(ctx)

	httpReq.Header.Set("User-Agent", t.userAgent)

	doReq := func() (int, []byte, error) {
		resp, err := t.http.Do(httpReq)
		if err != nil {
			return 0, nil, err
		}
		defer resp.Body.Close()
		if resp.ContentLength > t.maxResponseLength {
			return 0, nil, fmt.Errorf("tracker respsonse too large: %d", resp.ContentLength)
		}
		r := io.LimitReader(resp.Body, t.maxResponseLength)
		data, err := ioutil.ReadAll(r)
		return resp.StatusCode, data, err
	}

	code, body, err := doReq()
	if uerr, ok := err.(*url.Error); ok && uerr.Err == context.Canceled {
		return nil, context.Canceled
	}
	if err != nil {
		return nil, err
	}

	var response announceResponse
	err = bencode.DecodeBytes(body, &response)
	if err != nil {
		if code != 200 {
			return nil, fmt.Errorf("status not 200 OK (status: %d body: %q)", code, string(body))
		}
		return nil, err
	}

	if response.WarningMessage != "" {
		t.log.Warning(response.WarningMessage)
	}
	if response.FailureReason != "" {
		retryIn, _ := strconv.Atoi(response.RetryIn)
		return nil, &tracker.Error{
			FailureReason: response.FailureReason,
			RetryIn:       time.Duration(retryIn) * time.Minute,
		}
	}

	if response.TrackerID != "" {
		t.trackerID = response.TrackerID
	}

	// Peers may be in binary or dictionary model.
	var peers []*net.TCPAddr
	if len(response.Peers) > 0 {
		if response.Peers[0] == 'l' {
			peers, err = parsePeersDictionary(response.Peers)
		} else {
			var b []byte
			err = bencode.DecodeBytes(response.Peers, &b)
			if err != nil {
				return nil, err
			}
			peers, err = tracker.DecodePeersCompact(b)
		}
	}
	if err != nil {
		return nil, err
	}

	// Filter external IP
	if len(response.ExternalIP) != 0 {
		for i, p := range peers {
			if bytes.Equal(p.IP[:], response.ExternalIP) {
				peers[i], peers = peers[len(peers)-1], peers[:len(peers)-1]
				break
			}
		}
	}

	return &tracker.AnnounceResponse{
		Interval:    time.Duration(response.Interval) * time.Second,
		MinInterval: time.Duration(response.MinInterval) * time.Second,
		Leechers:    response.Incomplete,
		Seeders:     response.Complete,
		Peers:       peers,
	}, nil
}

func parsePeersDictionary(b bencode.RawMessage) ([]*net.TCPAddr, error) {
	var peers []struct {
		IP   string `bencode:"ip"`
		Port uint16 `bencode:"port"`
	}
	err := bencode.DecodeBytes(b, &peers)
	if err != nil {
		return nil, err
	}

	addrs := make([]*net.TCPAddr, len(peers))
	for i, p := range peers {
		pe := &net.TCPAddr{IP: net.ParseIP(p.IP), Port: int(p.Port)}
		addrs[i] = pe
	}
	return addrs, err
}
<|endoftext|>


<commit_before>package oak

import (
	"image"
	"math/rand"
	"time"

	"github.com/oakmound/oak/alg/floatgeom"
)

// A ScreenShaker knows how to shake a screen by a (or up to a) given magnitude.
// If Random is true, the Shaker will shake up to the (negative or positive)
// magnitude of each the X and Y axes. Otherwise, it will oscillate between
// negative magnitude and positive magnitude.
type ScreenShaker struct {
	Random    bool
	Magnitude floatgeom.Point2
}

var (
	// DefShaker is the global default shaker, used when oak.Shake is called.
	DefShaker = ScreenShaker{false, floatgeom.Point2{1.0, 1.0}}
)

// ShakeScreen will Shake using the package global DefShaker
func ShakeScreen(dur time.Duration) {
	DefShaker.Shake(dur)
}

// Shake shakes the screen based on this shaker's attributes. See ScreenShaker.
func (ss *ScreenShaker) Shake(dur time.Duration) {
	doneTime := time.Now().Add(dur)
	mag := ss.Magnitude

	setViewPos := ViewPos
	// If we end up doing this pattern more,
	// we need to replace defaultUpdateScreen
	// with a local definition of what updateScreen
	// was when this was called
	updateScreen = func(x, y int) {
		setViewPos = image.Point{x, y}
		defaultUpdateScreen(x, y)
	}
	if ss.Random {
		randOff := mag
		go func() {
			for time.Now().Before(doneTime) {
				ViewPos = setViewPos
				ViewPos.X += int(randOff.X())
				ViewPos.Y += int(randOff.Y())

				mag = mag.MulConst(-1)
				randOff = mag.MulConst(rand.Float64())
			}
			updateScreen = defaultUpdateScreen
			updateScreen(setViewPos.X, setViewPos.Y)
		}()
	} else {
		go func() {
			for time.Now().Before(doneTime) {
				ViewPos = setViewPos
				ViewPos.X += int(mag.X())
				ViewPos.Y += int(mag.Y())

				mag = mag.MulConst(-1)
			}
			updateScreen = defaultUpdateScreen
			updateScreen(setViewPos.X, setViewPos.Y)
		}()
	}
}
<commit_msg>Updated shaker comments to be clearer<commit_after>package oak

import (
	"image"
	"math/rand"
	"time"

	"github.com/oakmound/oak/alg/floatgeom"
)

// A ScreenShaker knows how to shake a screen by a (or up to a) given magnitude.
// If Random is true, the Shaker will shake up to the (negative or positive)
// magnitude of each the X and Y axes. Otherwise, it will oscillate between
// negative magnitude and positive magnitude.
type ScreenShaker struct {
	Random    bool
	Magnitude floatgeom.Point2
}

var (
	// DefShaker is the global default shaker, used when oak.ShakeScreen is called.
	DefShaker = ScreenShaker{false, floatgeom.Point2{1.0, 1.0}}
)

// ShakeScreen will Shake using the package global DefShaker
func ShakeScreen(dur time.Duration) {
	DefShaker.Shake(dur)
}

// Shake shakes the screen based on this ScreenShaker's attributes.
// See DefShaker for an example shaker setup
func (ss *ScreenShaker) Shake(dur time.Duration) {
	doneTime := time.Now().Add(dur)
	mag := ss.Magnitude

	setViewPos := ViewPos
	// If we end up doing this pattern more,
	// we need to replace defaultUpdateScreen
	// with a local definition of what updateScreen
	// was when this was called
	updateScreen = func(x, y int) {
		setViewPos = image.Point{x, y}
		defaultUpdateScreen(x, y)
	}
	if ss.Random {
		randOff := mag
		go func() {
			for time.Now().Before(doneTime) {
				ViewPos = setViewPos
				ViewPos.X += int(randOff.X())
				ViewPos.Y += int(randOff.Y())

				mag = mag.MulConst(-1)
				randOff = mag.MulConst(rand.Float64())
			}
			updateScreen = defaultUpdateScreen
			updateScreen(setViewPos.X, setViewPos.Y)
		}()
	} else {
		go func() {
			for time.Now().Before(doneTime) {
				ViewPos = setViewPos
				ViewPos.X += int(mag.X())
				ViewPos.Y += int(mag.Y())

				mag = mag.MulConst(-1)
			}
			updateScreen = defaultUpdateScreen
			updateScreen(setViewPos.X, setViewPos.Y)
		}()
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"github.com/ninjasphere/app-scheduler/model"
	"testing"
)

func TestBadInitNil(t *testing.T) {
	stub := &window{}
	err := stub.init(nil)
	if err == nil {
		t.Fatalf("expected error on init(nil)")
	}
}

func TestBadInitAfterNil(t *testing.T) {
	stub := &window{}
	err := stub.init(&model.Window{
		After:  nil,
		Before: afterNowTimestampModel,
	})
	if err == nil {
		t.Fatalf("expected error on init.from == nil")
	}
}

func TestBadInitBeforeNil(t *testing.T) {
	stub := &window{}
	err := stub.init(&model.Window{
		Before: nil,
		After:  afterNowTimestampModel,
	})
	if err == nil {
		t.Fatalf("expected error on init.until == nil")
	}
}

func runNonOverlappingWindow(t *testing.T, m *model.Window, permanentlyClosed bool) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	err := stub.init(m)
	if err != nil {
		t.Fatalf("unexpected error while opening window: %v", err)
	}
	if stub.isOpen(testTime, testTime) {
		t.Fatalf("window should not be open now")
	}
	if permanentlyClosed != stub.isPermanentlyClosed(testTime) {
		t.Fatalf("expecting permanentlyClosed == %v, but found opposite %v", permanentlyClosed, stub)
	}
}

func runOverlappingWindow(t *testing.T, m *model.Window) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	err := stub.init(m)
	if err != nil {
		t.Fatalf("unexpected error while opening window: %v", err)
	}
	if !stub.isOpen(testTime, testTime) {
		t.Fatalf("window should be open now")
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("expecting permanentlyClosed == false, but found opposite")
	}
}

func TestEarlierTimeOfDayWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimeOfDayWindow, false)
}

func TestLaterTimeOfDayWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimeOfDayWindow, false)
}

func TestOverlappingTimeOfDayWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimeOfDayWindow)
}

func TestEarlierTimestampWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimestampWindow, true)
}

func TestLaterTimestampWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimestampWindow, false)
}

func TestOverlappingTimestampWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimestampWindow)
}

func TestOverlappingTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimestampOpenDelayCloseWindow)
}

func TestEarlierTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimestampOpenDelayCloseWindow, true)
}

func TestLaterTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimestampOpenDelayCloseWindow, false)
}

func TestSunriseSunsetWindow(t *testing.T) {
	runNonOverlappingWindow(t, sunriseSunsetWindow, false)
}

func TestNowNeverWindow(t *testing.T) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	if err := stub.init(nowNeverWindow); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !stub.isOpen(testTime, testTime) {
		t.Fatalf("nowNeverWindow.isOpen() was %v, wanted %v", false, true)
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("nowNeverWindow.isPermanentlyClosed() was %v, wanted %v", true, false)
	}
}

func TestNeverNowWindow(t *testing.T) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	if err := stub.init(neverNowWindow); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if stub.isOpen(testTime, testTime) {
		t.Fatalf("neverNowWindow.isOpen() was %v, wanted %v", true, false)
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("neverNowWindow.isPermanentlyClosed() was %v, wanted %v", true, false)
	}
}
<commit_msg>Add a unit test for skipped open events.<commit_after>package controller

import (
	"github.com/ninjasphere/app-scheduler/model"
	"testing"
	"time"
)

func TestBadInitNil(t *testing.T) {
	stub := &window{}
	err := stub.init(nil)
	if err == nil {
		t.Fatalf("expected error on init(nil)")
	}
}

func TestBadInitAfterNil(t *testing.T) {
	stub := &window{}
	err := stub.init(&model.Window{
		After:  nil,
		Before: afterNowTimestampModel,
	})
	if err == nil {
		t.Fatalf("expected error on init.from == nil")
	}
}

func TestBadInitBeforeNil(t *testing.T) {
	stub := &window{}
	err := stub.init(&model.Window{
		Before: nil,
		After:  afterNowTimestampModel,
	})
	if err == nil {
		t.Fatalf("expected error on init.until == nil")
	}
}

func runNonOverlappingWindow(t *testing.T, m *model.Window, permanentlyClosed bool) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	err := stub.init(m)
	if err != nil {
		t.Fatalf("unexpected error while opening window: %v", err)
	}
	if stub.isOpen(testTime, testTime) {
		t.Fatalf("window should not be open now")
	}
	if permanentlyClosed != stub.isPermanentlyClosed(testTime) {
		t.Fatalf("expecting permanentlyClosed == %v, but found opposite %v", permanentlyClosed, stub)
	}
}

func runOverlappingWindow(t *testing.T, m *model.Window) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	err := stub.init(m)
	if err != nil {
		t.Fatalf("unexpected error while opening window: %v", err)
	}
	if !stub.isOpen(testTime, testTime) {
		t.Fatalf("window should be open now")
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("expecting permanentlyClosed == false, but found opposite")
	}
}

func TestEarlierTimeOfDayWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimeOfDayWindow, false)
}

func TestLaterTimeOfDayWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimeOfDayWindow, false)
}

func TestOverlappingTimeOfDayWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimeOfDayWindow)
}

func TestEarlierTimestampWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimestampWindow, true)
}

func TestLaterTimestampWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimestampWindow, false)
}

func TestOverlappingTimestampWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimestampWindow)
}

func TestOverlappingTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runOverlappingWindow(t, overlappingTimestampOpenDelayCloseWindow)
}

func TestEarlierTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, earlierTimestampOpenDelayCloseWindow, true)
}

func TestLaterTimestampOpenDelayCloseWindowIsNotOpen(t *testing.T) {
	runNonOverlappingWindow(t, laterTimestampOpenDelayCloseWindow, false)
}

func TestSunriseSunsetWindow(t *testing.T) {
	runNonOverlappingWindow(t, sunriseSunsetWindow, false)
}

func TestNowNeverWindow(t *testing.T) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	if err := stub.init(nowNeverWindow); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !stub.isOpen(testTime, testTime) {
		t.Fatalf("nowNeverWindow.isOpen() was %v, wanted %v", false, true)
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("nowNeverWindow.isPermanentlyClosed() was %v, wanted %v", true, false)
	}
}

func TestNeverNowWindow(t *testing.T) {
	initMockClock(testTime, defaultJitter)
	stub := &window{}
	if err := stub.init(neverNowWindow); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if stub.isOpen(testTime, testTime) {
		t.Fatalf("neverNowWindow.isOpen() was %v, wanted %v", true, false)
	}
	if stub.isPermanentlyClosed(testTime) {
		t.Fatalf("neverNowWindow.isPermanentlyClosed() was %v, wanted %v", true, false)
	}
}

func Test20150205(t *testing.T) {

	scheduledAt := time.Date(2015, 02, 05, 12, 47, 30, 337083531, time.Now().Location())
	// exactTime := time.Date(2015, 02, 05, 12, 48, 24, 0, time.Now().Location())
	ref := time.Date(2015, 02, 05, 12, 48, 24, 4585963, time.Now().Location())

	testWindow := &model.Window{
		After: &model.Event{
			Rule:  "time-of-day",
			Param: "12:48:24",
		},
		Before: &model.Event{
			Rule:  "delay",
			Param: "00:01:00",
		},
	}

	mockClock := initMockClock(scheduledAt, defaultJitter)
	stub := &window{}
	stub.init(testWindow)

	if stub.isPermanentlyClosed(scheduledAt) {
		t.Fatalf("was %v, wanted %v", true, false)
	}

	if stub.isOpen(scheduledAt, scheduledAt) {
		t.Fatalf("was %v, wanted %v", true, false)
	}

	wakeup := make(chan time.Time)

	go func() {
		done := stub.whenOpen(scheduledAt)

		select {
		case openSignal := <-done:
			wakeup <- openSignal
		}
	}()

	mockClock.SetNow(ref)

	now := <-wakeup

	if stub.isPermanentlyClosed(now) {
		t.Fatalf("was %v, wanted %v", true, false)
	}

	if !stub.isOpen(scheduledAt, now) {
		t.Fatalf("was %v, wanted %v", false, true)
	}

}
<|endoftext|>


<commit_before>package strand

import "testing"

const targetTestVersion = 3

func TestRNATranscription(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
	for _, test := range rnaTests {
		if actual := ToRNA(test.input); actual != test.expected {
			t.Errorf("ToRNA(%s): %s, expected %s",
				test.input, actual, test.expected)
		}
	}
}

func BenchmarkRNATranscription(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range rnaTests {
			ToRNA(test.input)
		}
	}
}
<commit_msg>rna-transcription: Ensure test versioning consistency... (#580)<commit_after>package strand

import "testing"

const targetTestVersion = 3

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
}

func TestRNATranscription(t *testing.T) {
	for _, test := range rnaTests {
		if actual := ToRNA(test.input); actual != test.expected {
			t.Errorf("ToRNA(%s): %s, expected %s",
				test.input, actual, test.expected)
		}
	}
}

func BenchmarkRNATranscription(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, test := range rnaTests {
			ToRNA(test.input)
		}
	}
}
<|endoftext|>


<commit_before>package network

import (
	"context"
	"time"
)

// DialPeerTimeout is the default timeout for a single call to `DialPeer`. When
// there are multiple concurrent calls to `DialPeer`, this timeout will apply to
// each independently.
var DialPeerTimeout = 60 * time.Second

type noDialCtxKey struct{}
type dialPeerTimeoutCtxKey struct{}
type forceDirectDialCtxKey struct{}
type useTransientCtxKey struct{}

var noDial = noDialCtxKey{}
var forceDirectDial = forceDirectDialCtxKey{}
var useTransient = useTransientCtxKey{}

// EXPERIMENTAL
// WithForceDirectDial constructs a new context with an option that instructs the network
// to attempt to force a direct connection to a peer via a dial even if a proxied connection to it already exists.
func WithForceDirectDial(ctx context.Context, reason string) context.Context {
	return context.WithValue(ctx, forceDirectDial, reason)
}

// EXPERIMENTAL
// GetForceDirectDial returns true if the force direct dial option is set in the context.
func GetForceDirectDial(ctx context.Context) (forceDirect bool, reason string) {
	v := ctx.Value(forceDirectDial)
	if v != nil {
		return true, v.(string)
	}

	return false, ""
}

// WithNoDial constructs a new context with an option that instructs the network
// to not attempt a new dial when opening a stream.
func WithNoDial(ctx context.Context, reason string) context.Context {
	return context.WithValue(ctx, noDial, reason)
}

// GetNoDial returns true if the no dial option is set in the context.
func GetNoDial(ctx context.Context) (nodial bool, reason string) {
	v := ctx.Value(noDial)
	if v != nil {
		return true, v.(string)
	}

	return false, ""
}

// GetDialPeerTimeout returns the current DialPeer timeout (or the default).
func GetDialPeerTimeout(ctx context.Context) time.Duration {
	if to, ok := ctx.Value(dialPeerTimeoutCtxKey{}).(time.Duration); ok {
		return to
	}
	return DialPeerTimeout
}

// WithDialPeerTimeout returns a new context with the DialPeer timeout applied.
//
// This timeout overrides the default DialPeerTimeout and applies per-dial
// independently.
func WithDialPeerTimeout(ctx context.Context, timeout time.Duration) context.Context {
	return context.WithValue(ctx, dialPeerTimeoutCtxKey{}, timeout)
}

// WithUseTransient constructs a new context with an option that instructs to network
// that it is acceptable to use a transient connection when opening a new stream.
func WithUseTransient(ctx context.Context) context.Context {
	return context.WithValue(ctx, useTransient, true)
}

// GetUseTransient returns true if the use transient option is set in the context.
func GetUseTransient(ctx context.Context) bool {
	v := ctx.Value(useTransient)
	if v != nil {
		return true
	}
	return false
}
<commit_msg>Update network/context.go<commit_after>package network

import (
	"context"
	"time"
)

// DialPeerTimeout is the default timeout for a single call to `DialPeer`. When
// there are multiple concurrent calls to `DialPeer`, this timeout will apply to
// each independently.
var DialPeerTimeout = 60 * time.Second

type noDialCtxKey struct{}
type dialPeerTimeoutCtxKey struct{}
type forceDirectDialCtxKey struct{}
type useTransientCtxKey struct{}

var noDial = noDialCtxKey{}
var forceDirectDial = forceDirectDialCtxKey{}
var useTransient = useTransientCtxKey{}

// EXPERIMENTAL
// WithForceDirectDial constructs a new context with an option that instructs the network
// to attempt to force a direct connection to a peer via a dial even if a proxied connection to it already exists.
func WithForceDirectDial(ctx context.Context, reason string) context.Context {
	return context.WithValue(ctx, forceDirectDial, reason)
}

// EXPERIMENTAL
// GetForceDirectDial returns true if the force direct dial option is set in the context.
func GetForceDirectDial(ctx context.Context) (forceDirect bool, reason string) {
	v := ctx.Value(forceDirectDial)
	if v != nil {
		return true, v.(string)
	}

	return false, ""
}

// WithNoDial constructs a new context with an option that instructs the network
// to not attempt a new dial when opening a stream.
func WithNoDial(ctx context.Context, reason string) context.Context {
	return context.WithValue(ctx, noDial, reason)
}

// GetNoDial returns true if the no dial option is set in the context.
func GetNoDial(ctx context.Context) (nodial bool, reason string) {
	v := ctx.Value(noDial)
	if v != nil {
		return true, v.(string)
	}

	return false, ""
}

// GetDialPeerTimeout returns the current DialPeer timeout (or the default).
func GetDialPeerTimeout(ctx context.Context) time.Duration {
	if to, ok := ctx.Value(dialPeerTimeoutCtxKey{}).(time.Duration); ok {
		return to
	}
	return DialPeerTimeout
}

// WithDialPeerTimeout returns a new context with the DialPeer timeout applied.
//
// This timeout overrides the default DialPeerTimeout and applies per-dial
// independently.
func WithDialPeerTimeout(ctx context.Context, timeout time.Duration) context.Context {
	return context.WithValue(ctx, dialPeerTimeoutCtxKey{}, timeout)
}

// WithUseTransient constructs a new context with an option that instructs the network
// that it is acceptable to use a transient connection when opening a new stream.
func WithUseTransient(ctx context.Context) context.Context {
	return context.WithValue(ctx, useTransient, true)
}

// GetUseTransient returns true if the use transient option is set in the context.
func GetUseTransient(ctx context.Context) bool {
	v := ctx.Value(useTransient)
	if v != nil {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/user"

	local "github.com/eirka/eirka-post/config"
)

func init() {

	// Database connection settings
	dbase := db.Database{

		User:           local.Settings.Database.User,
		Password:       local.Settings.Database.Password,
		Proto:          local.Settings.Database.Proto,
		Host:           local.Settings.Database.Host,
		Database:       local.Settings.Database.Database,
		MaxIdle:        local.Settings.Database.MaxIdle,
		MaxConnections: local.Settings.Database.MaxConnections,
	}

	// Set up DB connection
	dbase.NewDb()

	// Get limits and stuff from database
	config.GetDatabaseSettings()

	user.Secret = "secret"
}

func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJwtJsonRequest(r http.Handler, method, path, token string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJwtFormRequest(r http.Handler, method, path, token string, body bytes.Buffer) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, &body)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	req.Header.Set("Content-Type", "multipart/form-data")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func TestEmailController(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	first := performRequest(router, "POST", "/email")

	assert.Equal(t, first.Code, 401, "HTTP request code should match")

	u := user.DefaultUser()
	u.SetId(2)
	u.SetAuthenticated()
	u.Password()

	assert.True(t, u.ComparePassword("testpassword"), "Test user password should be set")

	token, err := u.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	request1 := []byte(`{"ib": 1, "email": "test@test.com"}`)

	second := performJwtJsonRequest(router, "POST", "/email", token, request1)

	assert.Equal(t, second.Code, 200, "HTTP request code should match")
	assert.Equal(t, second.Body.String(), `{"success_message":"Email Updated"}`, "HTTP response should match")

	request2 := []byte(`{"ib": 1, "email": "test@test.com"}`)

	third := performJwtJsonRequest(router, "POST", "/email", token, request2)

	assert.Equal(t, third.Code, 400, "HTTP request code should match")
	assert.Equal(t, third.Body.String(), `{"error_message":"Email address the same"}`, "HTTP response should match")

	request3 := []byte(`{"ib": 1, "email": "test@test.com"}`)

	fourth := performJwtJsonRequest(router, "POST", "/email", token, request3)

	assert.Equal(t, fourth.Code, 200, "HTTP request code should match")
	assert.Equal(t, fourth.Body.String(), `{"success_message":"Email Updated"}`, "HTTP response should match")

}
<commit_msg>add email test<commit_after>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/config"
	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/user"

	local "github.com/eirka/eirka-post/config"
)

func init() {

	// Database connection settings
	dbase := db.Database{

		User:           local.Settings.Database.User,
		Password:       local.Settings.Database.Password,
		Proto:          local.Settings.Database.Proto,
		Host:           local.Settings.Database.Host,
		Database:       local.Settings.Database.Database,
		MaxIdle:        local.Settings.Database.MaxIdle,
		MaxConnections: local.Settings.Database.MaxConnections,
	}

	// Set up DB connection
	dbase.NewDb()

	// Get limits and stuff from database
	config.GetDatabaseSettings()

	user.Secret = "secret"
}

func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJwtJsonRequest(r http.Handler, method, path, token string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJwtFormRequest(r http.Handler, method, path, token string, body bytes.Buffer) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, &body)
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
	req.Header.Set("Content-Type", "multipart/form-data")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func TestEmailController(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(true))

	router.POST("/email", EmailController)

	first := performRequest(router, "POST", "/email")

	assert.Equal(t, first.Code, 401, "HTTP request code should match")

	u := user.DefaultUser()
	u.SetId(2)
	u.SetAuthenticated()
	u.Password()

	assert.True(t, u.ComparePassword("testpassword"), "Test user password should be set")

	token, err := u.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	request1 := []byte(`{"ib": 1, "email": "test@test.com"}`)

	second := performJwtJsonRequest(router, "POST", "/email", token, request1)

	assert.Equal(t, second.Code, 200, "HTTP request code should match")
	assert.Equal(t, second.Body.String(), `{"success_message":"Email Updated"}`, "HTTP response should match")

	request2 := []byte(`{"ib": 1, "email": "test@test.com"}`)

	third := performJwtJsonRequest(router, "POST", "/email", token, request2)

	assert.Equal(t, third.Code, 400, "HTTP request code should match")
	assert.Equal(t, third.Body.String(), `{"error_message":"Email address the same"}`, "HTTP response should match")

	request3 := []byte(`{"ib": 1, "email": "test@cool.com"}`)

	fourth := performJwtJsonRequest(router, "POST", "/email", token, request3)

	assert.Equal(t, fourth.Code, 200, "HTTP request code should match")
	assert.Equal(t, fourth.Body.String(), `{"success_message":"Email Updated"}`, "HTTP response should match")

}
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings, Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package main

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/yahoo/athenz/clients/go/zts"
)

type signer struct {
	key       crypto.Signer
	algorithm x509.SignatureAlgorithm
}

func main() {

	var ztsURL, svcKeyFile, svcCertFile, roleKeyFile, dom, svc string
	var caCertFile, roleCertFile, roleDomain, roleName, dnsDomain string
	var subjC, subjO, subjOU, ip, uri string
	var spiffe, csr bool

	flag.StringVar(&roleKeyFile, "role-key-file", "", "role cert private key file (default: service identity private key)")
	flag.StringVar(&roleCertFile, "role-cert-file", "", "output role certificate file")
	flag.StringVar(&caCertFile, "cacert", "", "CA certificate file")
	flag.StringVar(&svcKeyFile, "svc-key-file", "", "service identity private key file (required)")
	flag.StringVar(&svcCertFile, "svc-cert-file", "", "service identity certificate file (required)")
	flag.StringVar(&dom, "domain", "", "domain of service")
	flag.StringVar(&svc, "service", "", "name of service")
	flag.StringVar(&ztsURL, "zts", "", "url of the ZTS Service (required)")
	flag.StringVar(&roleDomain, "role-domain", "", "requested role domain name (required)")
	flag.StringVar(&roleName, "role-name", "", "requested role name in the role-domain (required)")
	flag.StringVar(&dnsDomain, "dns-domain", "", "dns domain suffix to be included in the csr (required)")
	flag.StringVar(&subjC, "subj-c", "US", "Subject C/Country field")
	flag.StringVar(&subjO, "subj-o", "Oath Inc.", "Subject O/Organization field")
	flag.StringVar(&subjOU, "subj-ou", "Athenz", "Subject OU/OrganizationalUnit field")
	flag.StringVar(&ip, "ip", "", "IP address")
	flag.BoolVar(&spiffe, "spiffe", false, "include spiffe uri in csr")
	flag.BoolVar(&csr, "csr", false, "request csr only")
	flag.Parse()

	if svcKeyFile == "" || svcCertFile == "" || roleDomain == "" || roleName == "" ||
		ztsURL == "" || dnsDomain == "" {
		log.Fatalln("Error: missing required attributes. Run with -help for command line arguments")
	}

	// If a separate private key is not provided for role cert csr, use service identity private key
	if roleKeyFile == "" {
		roleKeyFile = svcKeyFile
	}

	// let's extract our domain/service values from our certificate

	domain, service, err := extractServiceDetailsFromCert(svcCertFile)
	if err != nil {
		log.Fatalf("Unable to extract service details from certificate: %v\n", err)
	}
	hyphenDomain := strings.Replace(domain, ".", "-", -1)
	host := fmt.Sprintf("%s.%s.%s", service, hyphenDomain, dnsDomain)
	rfc822 := fmt.Sprintf("%s.%s@%s", domain, service, dnsDomain)
	if spiffe {
		uri = fmt.Sprintf("spiffe://%s/ra/%s", roleDomain, roleName)
	}

	//note: RFC 6125 states that if the SAN (Subject Alternative Name) exists,
	//it is used, not the CA. So, we will always put the Athens name in the CN
	//(it is *not* a DNS domain name), and put the host name into the SAN.

	commonName := fmt.Sprintf("%s:role.%s", roleDomain, roleName)
	subj := pkix.Name{
		CommonName:         commonName,
		OrganizationalUnit: []string{subjOU},
		Organization:       []string{subjO},
		Country:            []string{subjC},
	}

	// load private key
	bytes, err := ioutil.ReadFile(roleKeyFile)
	if err != nil {
		log.Fatalf("Unable to read private key file %s, err: %v\n", roleKeyFile, err)
	}

	// get our private key signer for csr
	pkSigner, err := newSigner(bytes)
	if err != nil {
		log.Fatalf("Unable to retrieve private key %s, err: %v\n", svcKeyFile, err)
	}

	csrData, err := generateCSR(pkSigner, subj, host, rfc822, ip, uri)
	if err != nil {
		log.Fatalf("Unable to generate CSR for %s, err: %v\n", roleName, err)
	}

	// if we're provided the csr flag then we're going to display
	// it and return right away
	if csr {
		fmt.Println(csrData)
		return
	}

	client, err := ztsClient(ztsURL, svcKeyFile, svcCertFile, caCertFile)
	if err != nil {
		log.Fatalf("Unable to initialize ZTS Client for %s, err: %v\n", ztsURL, err)
	}

	getRoleCertificate(client, csrData, roleDomain, roleName, roleCertFile)
}

func extractServiceDetailsFromCert(certFile string) (string, string, error) {
	data, err := ioutil.ReadFile(certFile)
	if err != nil {
		return "", "", err
	}
	block, _ := pem.Decode(data)
	cert, err := x509.ParseCertificate(block.Bytes)
	cn := cert.Subject.CommonName
	idx := strings.LastIndex(cn, ".")
	if idx < 0 {
		return "", "", fmt.Errorf("cannot determine domain/service from certificate: %s", cn)
	}
	return cn[:idx], cn[idx+1:], nil
}

func generateCSR(keySigner *signer, subj pkix.Name, host, rfc822, ip, uri string) (string, error) {

	template := x509.CertificateRequest{
		Subject:            subj,
		SignatureAlgorithm: keySigner.algorithm,
	}
	if host != "" {
		template.DNSNames = []string{host}
	}
	if rfc822 != "" {
		template.EmailAddresses = []string{rfc822}
	}
	if ip != "" {
		template.IPAddresses = []net.IP{net.ParseIP(ip)}
	}
	if uri != "" {
		uriptr, err := url.Parse(uri)
		if err == nil {
			template.URIs = []*url.URL{uriptr}
		}
	}
	csr, err := x509.CreateCertificateRequest(rand.Reader, &template, keySigner.key)
	if err != nil {
		return "", fmt.Errorf("Cannot create CSR: %v", err)
	}
	block := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csr,
	}
	var buf bytes.Buffer
	err = pem.Encode(&buf, block)
	if err != nil {
		return "", fmt.Errorf("Cannot encode CSR to PEM: %v", err)
	}
	return buf.String(), nil
}

func getRoleCertificate(client *zts.ZTSClient, csr, roleDomain, roleName, roleCertFile string) {

	var roleRequest = new(zts.RoleCertificateRequest)
	roleRequest.Csr = csr
	roleToken, err := client.PostRoleCertificateRequest(zts.DomainName(roleDomain), zts.EntityName(roleName), roleRequest)
	if err != nil {
		log.Fatalf("PostRoleCertificateRequest failed for %s, err: %v\n", roleName, err)
	}

	if roleCertFile != "" {
		err = ioutil.WriteFile(roleCertFile, []byte(roleToken.Token), 0444)
		if err != nil {
			log.Fatalf("Unable to save role token certificate in %s, err: %v\n", roleCertFile, err)
		}
	} else {
		fmt.Println(roleToken.Token)
	}
}

func ztsClient(ztsURL, keyFile, certFile, caFile string) (*zts.ZTSClient, error) {
	config, err := tlsConfiguration(keyFile, certFile, caFile)
	if err != nil {
		return nil, err
	}
	tr := &http.Transport{
		TLSClientConfig: config,
	}
	client := zts.NewClient(ztsURL, tr)
	return &client, nil
}

func tlsConfiguration(keyFile, certFile, caFile string) (*tls.Config, error) {
	var capem []byte
	var err error
	if caFile != "" {
		capem, err = ioutil.ReadFile(caFile)
		if err != nil {
			return nil, err
		}
	}
	var keypem []byte
	var certpem []byte
	if keyFile != "" && certFile != "" {
		keypem, err = ioutil.ReadFile(keyFile)
		if err != nil {
			return nil, err
		}
		certpem, err = ioutil.ReadFile(certFile)
		if err != nil {
			return nil, err
		}
	}
	return tlsConfigurationFromPEM(keypem, certpem, capem)
}

func tlsConfigurationFromPEM(keypem, certpem, capem []byte) (*tls.Config, error) {
	config := &tls.Config{}

	certPool := x509.NewCertPool()
	if capem != nil {
		if !certPool.AppendCertsFromPEM(capem) {
			return nil, fmt.Errorf("Failed to append certs to pool")
		}
		config.RootCAs = certPool
	}

	if certpem != nil && keypem != nil {
		mycert, err := tls.X509KeyPair(certpem, keypem)
		if err != nil {
			return nil, err
		}
		config.Certificates = make([]tls.Certificate, 1)
		config.Certificates[0] = mycert

		config.ClientCAs = certPool
		config.ClientAuth = tls.VerifyClientCertIfGiven
	}

	//Use only modern ciphers
	config.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}

	//Use only TLS v1.2
	config.MinVersion = tls.VersionTLS12

	//Don't allow session resumption
	config.SessionTicketsDisabled = true
	return config, nil
}

func newSigner(privateKeyPEM []byte) (*signer, error) {
	block, _ := pem.Decode(privateKeyPEM)
	if block == nil {
		return nil, fmt.Errorf("Unable to load private key")
	}

	switch block.Type {
	case "EC PRIVATE KEY":
		key, err := x509.ParseECPrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.ECDSAWithSHA256}, nil
	case "RSA PRIVATE KEY":
		key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.SHA256WithRSA}, nil
	default:
		return nil, fmt.Errorf("Unsupported private key type: %s", block.Type)
	}
}
<commit_msg>expose expiry time in role cert utility (#766)<commit_after>// Copyright 2017 Yahoo Holdings, Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.

package main

import (
	"bytes"
	"crypto"
	"crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/yahoo/athenz/clients/go/zts"
)

type signer struct {
	key       crypto.Signer
	algorithm x509.SignatureAlgorithm
}

func main() {

	var ztsURL, svcKeyFile, svcCertFile, roleKeyFile, dom, svc string
	var caCertFile, roleCertFile, roleDomain, roleName, dnsDomain string
	var subjC, subjO, subjOU, ip, uri string
	var spiffe, csr bool
	var expiryTime int

	flag.StringVar(&roleKeyFile, "role-key-file", "", "role cert private key file (default: service identity private key)")
	flag.StringVar(&roleCertFile, "role-cert-file", "", "output role certificate file")
	flag.StringVar(&caCertFile, "cacert", "", "CA certificate file")
	flag.StringVar(&svcKeyFile, "svc-key-file", "", "service identity private key file (required)")
	flag.StringVar(&svcCertFile, "svc-cert-file", "", "service identity certificate file (required)")
	flag.StringVar(&dom, "domain", "", "domain of service")
	flag.StringVar(&svc, "service", "", "name of service")
	flag.StringVar(&ztsURL, "zts", "", "url of the ZTS Service (required)")
	flag.StringVar(&roleDomain, "role-domain", "", "requested role domain name (required)")
	flag.StringVar(&roleName, "role-name", "", "requested role name in the role-domain (required)")
	flag.StringVar(&dnsDomain, "dns-domain", "", "dns domain suffix to be included in the csr (required)")
	flag.StringVar(&subjC, "subj-c", "US", "Subject C/Country field")
	flag.StringVar(&subjO, "subj-o", "Oath Inc.", "Subject O/Organization field")
	flag.StringVar(&subjOU, "subj-ou", "Athenz", "Subject OU/OrganizationalUnit field")
	flag.StringVar(&ip, "ip", "", "IP address")
	flag.BoolVar(&spiffe, "spiffe", false, "include spiffe uri in csr")
	flag.BoolVar(&csr, "csr", false, "request csr only")
	flag.IntVar(&expiryTime, "expiry-time", 0, "expiry time in minutes")

	flag.Parse()

	if svcKeyFile == "" || svcCertFile == "" || roleDomain == "" || roleName == "" ||
		ztsURL == "" || dnsDomain == "" {
		log.Fatalln("Error: missing required attributes. Run with -help for command line arguments")
	}

	// If a separate private key is not provided for role cert csr, use service identity private key
	if roleKeyFile == "" {
		roleKeyFile = svcKeyFile
	}

	// let's extract our domain/service values from our certificate

	domain, service, err := extractServiceDetailsFromCert(svcCertFile)
	if err != nil {
		log.Fatalf("Unable to extract service details from certificate: %v\n", err)
	}
	hyphenDomain := strings.Replace(domain, ".", "-", -1)
	host := fmt.Sprintf("%s.%s.%s", service, hyphenDomain, dnsDomain)
	rfc822 := fmt.Sprintf("%s.%s@%s", domain, service, dnsDomain)
	if spiffe {
		uri = fmt.Sprintf("spiffe://%s/ra/%s", roleDomain, roleName)
	}

	//note: RFC 6125 states that if the SAN (Subject Alternative Name) exists,
	//it is used, not the CA. So, we will always put the Athens name in the CN
	//(it is *not* a DNS domain name), and put the host name into the SAN.

	commonName := fmt.Sprintf("%s:role.%s", roleDomain, roleName)
	subj := pkix.Name{
		CommonName:         commonName,
		OrganizationalUnit: []string{subjOU},
		Organization:       []string{subjO},
		Country:            []string{subjC},
	}

	// load private key
	bytes, err := ioutil.ReadFile(roleKeyFile)
	if err != nil {
		log.Fatalf("Unable to read private key file %s, err: %v\n", roleKeyFile, err)
	}

	// get our private key signer for csr
	pkSigner, err := newSigner(bytes)
	if err != nil {
		log.Fatalf("Unable to retrieve private key %s, err: %v\n", svcKeyFile, err)
	}

	csrData, err := generateCSR(pkSigner, subj, host, rfc822, ip, uri)
	if err != nil {
		log.Fatalf("Unable to generate CSR for %s, err: %v\n", roleName, err)
	}

	// if we're provided the csr flag then we're going to display
	// it and return right away
	if csr {
		fmt.Println(csrData)
		return
	}

	client, err := ztsClient(ztsURL, svcKeyFile, svcCertFile, caCertFile)
	if err != nil {
		log.Fatalf("Unable to initialize ZTS Client for %s, err: %v\n", ztsURL, err)
	}

	getRoleCertificate(client, csrData, roleDomain, roleName, roleCertFile, int64(expiryTime))
}

func extractServiceDetailsFromCert(certFile string) (string, string, error) {
	data, err := ioutil.ReadFile(certFile)
	if err != nil {
		return "", "", err
	}
	block, _ := pem.Decode(data)
	cert, err := x509.ParseCertificate(block.Bytes)
	cn := cert.Subject.CommonName
	idx := strings.LastIndex(cn, ".")
	if idx < 0 {
		return "", "", fmt.Errorf("cannot determine domain/service from certificate: %s", cn)
	}
	return cn[:idx], cn[idx+1:], nil
}

func generateCSR(keySigner *signer, subj pkix.Name, host, rfc822, ip, uri string) (string, error) {

	template := x509.CertificateRequest{
		Subject:            subj,
		SignatureAlgorithm: keySigner.algorithm,
	}
	if host != "" {
		template.DNSNames = []string{host}
	}
	if rfc822 != "" {
		template.EmailAddresses = []string{rfc822}
	}
	if ip != "" {
		template.IPAddresses = []net.IP{net.ParseIP(ip)}
	}
	if uri != "" {
		uriptr, err := url.Parse(uri)
		if err == nil {
			template.URIs = []*url.URL{uriptr}
		}
	}
	csr, err := x509.CreateCertificateRequest(rand.Reader, &template, keySigner.key)
	if err != nil {
		return "", fmt.Errorf("Cannot create CSR: %v", err)
	}
	block := &pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: csr,
	}
	var buf bytes.Buffer
	err = pem.Encode(&buf, block)
	if err != nil {
		return "", fmt.Errorf("Cannot encode CSR to PEM: %v", err)
	}
	return buf.String(), nil
}

func getRoleCertificate(client *zts.ZTSClient, csr, roleDomain, roleName, roleCertFile string, expiryTime int64) {

	roleRequest := &zts.RoleCertificateRequest{
		Csr:        csr,
		ExpiryTime: expiryTime,
	}
	roleToken, err := client.PostRoleCertificateRequest(zts.DomainName(roleDomain), zts.EntityName(roleName), roleRequest)
	if err != nil {
		log.Fatalf("PostRoleCertificateRequest failed for %s, err: %v\n", roleName, err)
	}

	if roleCertFile != "" {
		err = ioutil.WriteFile(roleCertFile, []byte(roleToken.Token), 0444)
		if err != nil {
			log.Fatalf("Unable to save role token certificate in %s, err: %v\n", roleCertFile, err)
		}
	} else {
		fmt.Println(roleToken.Token)
	}
}

func ztsClient(ztsURL, keyFile, certFile, caFile string) (*zts.ZTSClient, error) {
	config, err := tlsConfiguration(keyFile, certFile, caFile)
	if err != nil {
		return nil, err
	}
	tr := &http.Transport{
		TLSClientConfig: config,
	}
	client := zts.NewClient(ztsURL, tr)
	return &client, nil
}

func tlsConfiguration(keyFile, certFile, caFile string) (*tls.Config, error) {
	var capem []byte
	var err error
	if caFile != "" {
		capem, err = ioutil.ReadFile(caFile)
		if err != nil {
			return nil, err
		}
	}
	var keypem []byte
	var certpem []byte
	if keyFile != "" && certFile != "" {
		keypem, err = ioutil.ReadFile(keyFile)
		if err != nil {
			return nil, err
		}
		certpem, err = ioutil.ReadFile(certFile)
		if err != nil {
			return nil, err
		}
	}
	return tlsConfigurationFromPEM(keypem, certpem, capem)
}

func tlsConfigurationFromPEM(keypem, certpem, capem []byte) (*tls.Config, error) {
	config := &tls.Config{}

	certPool := x509.NewCertPool()
	if capem != nil {
		if !certPool.AppendCertsFromPEM(capem) {
			return nil, fmt.Errorf("Failed to append certs to pool")
		}
		config.RootCAs = certPool
	}

	if certpem != nil && keypem != nil {
		mycert, err := tls.X509KeyPair(certpem, keypem)
		if err != nil {
			return nil, err
		}
		config.Certificates = make([]tls.Certificate, 1)
		config.Certificates[0] = mycert

		config.ClientCAs = certPool
		config.ClientAuth = tls.VerifyClientCertIfGiven
	}

	//Use only modern ciphers
	config.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
		tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
		tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}

	//Use only TLS v1.2
	config.MinVersion = tls.VersionTLS12

	//Don't allow session resumption
	config.SessionTicketsDisabled = true
	return config, nil
}

func newSigner(privateKeyPEM []byte) (*signer, error) {
	block, _ := pem.Decode(privateKeyPEM)
	if block == nil {
		return nil, fmt.Errorf("Unable to load private key")
	}

	switch block.Type {
	case "EC PRIVATE KEY":
		key, err := x509.ParseECPrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.ECDSAWithSHA256}, nil
	case "RSA PRIVATE KEY":
		key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
		if err != nil {
			return nil, err
		}
		return &signer{key: key, algorithm: x509.SHA256WithRSA}, nil
	default:
		return nil, fmt.Errorf("Unsupported private key type: %s", block.Type)
	}
}
<|endoftext|>