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

package grafana

import (
	"encoding/json"
	"time"

	"github.com/spoof/go-grafana/grafana/panel"
	"github.com/spoof/go-grafana/pkg/field"
)

type (
	DashboardID    uint64
	dashboardStyle string
)

const (
	dashboardDarkStyle  dashboardStyle = "dark"
	dashboardLightStyle dashboardStyle = "light"
)

type Dashboard struct {
	ID            DashboardID `json:"-"`
	Version       uint64      `json:"-"`
	SchemaVersion int         `json:"schemaVersion"`

	Editable     bool           `json:"editable"`
	GraphTooltip uint8          `json:"graphTooltip"`
	HideControls bool           `json:"hideControls"`
	Rows         []*Row         `json:"rows"`
	Style        dashboardStyle `json:"style"`
	Timezone     string         `json:"timezone"`
	Title        string         `json:"title"`
	tags         *field.Tags

	Meta *DashboardMeta `json:"-"`
}

// NewDashboard creates new Dashboard.
func NewDashboard(title string) *Dashboard {
	return &Dashboard{
		Title:         title,
		Editable:      true,
		SchemaVersion: 14,
		Style:         dashboardDarkStyle,
		tags:          field.NewTags(),
	}
}

// MarshalJSON implements json.Marshaler interface
func (d *Dashboard) MarshalJSON() ([]byte, error) {
	panelID := 1
	rows := make([]*Row, len(d.Rows))
	for i, r := range d.Rows {
		rr := *r

		panels := make([]Panel, len(r.Panels))
		for j, p := range r.Panels {
			panels[j] = &probePanel{
				ID:    uint(panelID),
				panel: p,
			}
			panelID++
		}
		rr.Panels = panels
		rows[i] = &rr
	}

	type JSONDashboard Dashboard
	jd := &struct {
		JSONDashboard
		Rows []*Row   `json:"rows"`
		Tags []string `json:"tags"`
	}{
		JSONDashboard: (JSONDashboard)(*d),
		Rows:          rows,
		Tags:          d.tags.Value(),
	}
	return json.Marshal(jd)
}

// UnmarshalJSON implements json.Unmarshaler interface
func (d *Dashboard) UnmarshalJSON(data []byte) error {
	type JSONDashboard Dashboard
	inDashboard := struct {
		*JSONDashboard
		ID      *DashboardID `json:"id"`
		Version *uint64      `json:"version"`

		Tags []string       `json:"tags"`
		Meta *DashboardMeta `json:"meta"`
	}{
		JSONDashboard: (*JSONDashboard)(d),
		ID:            &d.ID,
		Version:       &d.Version,
		Meta:          d.Meta,
	}
	if err := json.Unmarshal(data, &inDashboard); err != nil {
		return err
	}

	d.tags = field.NewTags(inDashboard.Tags...)

	return nil
}

type DashboardMeta struct {
	Slug    string `json:"slug"`
	Type    string `json:"type"`
	Version int    `json:"version"`

	CanEdit bool `json:"canEdit"`
	CanSave bool `json:"canSave"`
	CanStar bool `json:"canStar"`

	Created   time.Time `json:"created"`
	CreatedBy string    `json:"createdBy"`
	Expires   time.Time `json:"expires"`
	Updated   time.Time `json:"updated"`
	UpdatedBy string    `json:"updatedBy"`
}

func (dm *DashboardMeta) String() string {
	return Stringify(dm)
}

// Row is panel's row
type Row struct {
	Collapsed bool              `json:"collapse"`
	Editable  bool              `json:"editable"`
	Height    field.ForceString `json:"height"`
	Panels    []Panel           `json:"panels"`
	RepeatFor string            `json:"repeat"` // repeat row for given variable
	ShowTitle bool              `json:"showTitle"`
	Title     string            `json:"title"`
	TitleSize string            `json:"titleSize"` // TODO: validation: h1-h6
}

// NewRow creates new Row with somw defaults.
func NewRow() *Row {
	return &Row{Editable: true}
}

// UnmarshalJSON implements json.Unmarshaler interface
func (r *Row) UnmarshalJSON(data []byte) error {
	type JSONRow Row
	jr := struct {
		*JSONRow
		Panels []probePanel `json:"panels"`
	}{
		JSONRow: (*JSONRow)(r),
	}

	if err := json.Unmarshal(data, &jr); err != nil {
		return err
	}

	panels := make([]Panel, len(jr.Panels))
	for i, p := range jr.Panels {
		panels[i] = p.panel
	}
	r.Panels = panels
	return nil
}

type Panel interface {
	GeneralOptions() *panel.GeneralOptions
}

type panelType string

const (
	textPanelType       panelType = "text"
	singlestatPanelType panelType = "singlestat"
	graphPanelType      panelType = "graph"
)

type probePanel struct {
	ID   uint      `json:"id"`
	Type panelType `json:"type"`

	panel Panel
}

func (p *probePanel) GeneralOptions() *panel.GeneralOptions {
	return p.panel.GeneralOptions()
}

func (p *probePanel) UnmarshalJSON(data []byte) error {
	type JSONPanel probePanel
	jp := struct {
		*JSONPanel
	}{
		JSONPanel: (*JSONPanel)(p),
	}
	if err := json.Unmarshal(data, &jp); err != nil {
		return err
	}

	var pp Panel
	switch jp.Type {
	case textPanelType:
		pp = new(panel.Text)
	case singlestatPanelType:
		pp = new(panel.Singlestat)
	case graphPanelType:
		pp = new(panel.Graph)
	default:
		return nil
	}

	if err := json.Unmarshal(data, pp); err != nil {
		return err
	}

	var generalOptions panel.GeneralOptions
	if err := json.Unmarshal(data, &generalOptions); err != nil {
		return err
	}
	gOpts := pp.GeneralOptions()
	*gOpts = generalOptions

	//var queriesOptions PanelQueriesOptions
	//if err := json.Unmarshal(data, &queriesOptions); err != nil {
	//	return err
	//}

	p.panel = pp
	return nil
}

// MarshalJSON implements json.Marshaler interface
func (p *probePanel) MarshalJSON() ([]byte, error) {
	type JSONPanel probePanel
	jp := struct {
		*JSONPanel

		*panel.Text
		*panel.Singlestat
		*panel.Graph

		*panel.GeneralOptions
	}{
		JSONPanel:      (*JSONPanel)(p),
		GeneralOptions: p.GeneralOptions(),
	}

	switch v := p.panel.(type) {
	case *panel.Text:
		jp.Text = v
		jp.Type = textPanelType
	case *panel.Singlestat:
		jp.Singlestat = v
		jp.Type = singlestatPanelType
	case *panel.Graph:
		jp.Graph = v
		jp.Type = graphPanelType
	}
	return json.Marshal(jp)
}

type panelGeneralOptions struct {
	id        uint
	panelType panelType
}

type probeQuery struct {
	// PrometheusQuery
	IntervalFactor *uint   `json:"intervalFactor"`
	Expression     *string `json:"expr"`

	// GraphiteQuery fields
	Target *string `json:"target"`

	query Query
}

func (q *probeQuery) UnmarshalJSON(data []byte) error {
	type JSONQuery probeQuery
	var jq JSONQuery
	if err := json.Unmarshal(data, &jq); err != nil {
		return err
	}

	//var query Query
	//if jq.Expression != nil && jq.IntervalFactor != nil {
	//	query = new(PrometheusQuery)
	//} else if jq.Target != nil {
	//	query = new(GraphiteQuery)
	//}

	// TODO: Initialize Unknown query here instead
	//if query == nil {
	//	return nil
	//}

	//if err := json.Unmarshal(data, &query); err != nil {
	//	return err
	//}

	//q.query = query
	return nil
}

type Query interface {
	RefID() string
	Datasource() string
	//commonOptions() *query.commonQuery
}

// PanelQueriesOptions is a part of panel that placed in 'Metrics' tab. It represents set of panel queries.
type PanelQueriesOptions struct {
	Datasource string  `json:"datasource,omitempty"`
	Queries    []Query `json:"targets"`
}

func (o *PanelQueriesOptions) UnmarshalJSON(data []byte) error {
	type JSONOptions PanelQueriesOptions

	var queries []*probeQuery
	jo := struct {
		*JSONOptions
		Queries *[]*probeQuery `json:"targets,omitempty"`
	}{
		JSONOptions: (*JSONOptions)(o),
		Queries:     &queries,
	}
	if err := json.Unmarshal(data, &jo); err != nil {
		return err
	}

	//o.Queries = []Query{}
	//for _, q := range queries {
	//	// TODO: queies shouldn't be nil in future. This check will be obsolete
	//	if q.query == nil {
	//		continue
	//	}
	//	o.Queries = append(o.Queries, q.query)
	//}

	return nil
}

// MarshalJSON implements encoding/json.Marshaler
func (o *PanelQueriesOptions) MarshalJSON() ([]byte, error) {
	type JSONOptions PanelQueriesOptions
	jo := (*JSONOptions)(o)

	// FIXME: add checking for uniqueness of refids
	//for i, q := range jo.Queries {
	//	if q.commonOptions().RefID != "" {
	//		continue
	//	}
	//	q.commonOptions().RefID = makeRefID(i)
	//}

	// TODO: if there are a several types of datasources we need to set 'main' datasouce to "Mixed"

	return json.Marshal(jo)
}

// makeRefID returns symbolic ID for given index.
// TODO: It has very rough implementation. Needs refactoring.
func makeRefID(index int) string {
	letters := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

	var id string
	if index >= len(letters) {
		id += makeRefID(index % len(letters))
	} else {
		id = string(letters[index])
	}

	var result string
	for _, v := range id {
		result = string(v) + result
	}
	return result
}
<commit_msg>Remove unused panelGeneralOptions struct<commit_after>// Copyright 2017 Sergey Safonov
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package grafana

import (
	"encoding/json"
	"time"

	"github.com/spoof/go-grafana/grafana/panel"
	"github.com/spoof/go-grafana/pkg/field"
)

type (
	DashboardID    uint64
	dashboardStyle string
)

const (
	dashboardDarkStyle  dashboardStyle = "dark"
	dashboardLightStyle dashboardStyle = "light"
)

type Dashboard struct {
	ID            DashboardID `json:"-"`
	Version       uint64      `json:"-"`
	SchemaVersion int         `json:"schemaVersion"`

	Editable     bool           `json:"editable"`
	GraphTooltip uint8          `json:"graphTooltip"`
	HideControls bool           `json:"hideControls"`
	Rows         []*Row         `json:"rows"`
	Style        dashboardStyle `json:"style"`
	Timezone     string         `json:"timezone"`
	Title        string         `json:"title"`
	tags         *field.Tags

	Meta *DashboardMeta `json:"-"`
}

// NewDashboard creates new Dashboard.
func NewDashboard(title string) *Dashboard {
	return &Dashboard{
		Title:         title,
		Editable:      true,
		SchemaVersion: 14,
		Style:         dashboardDarkStyle,
		tags:          field.NewTags(),
	}
}

// MarshalJSON implements json.Marshaler interface
func (d *Dashboard) MarshalJSON() ([]byte, error) {
	panelID := 1
	rows := make([]*Row, len(d.Rows))
	for i, r := range d.Rows {
		rr := *r

		panels := make([]Panel, len(r.Panels))
		for j, p := range r.Panels {
			panels[j] = &probePanel{
				ID:    uint(panelID),
				panel: p,
			}
			panelID++
		}
		rr.Panels = panels
		rows[i] = &rr
	}

	type JSONDashboard Dashboard
	jd := &struct {
		JSONDashboard
		Rows []*Row   `json:"rows"`
		Tags []string `json:"tags"`
	}{
		JSONDashboard: (JSONDashboard)(*d),
		Rows:          rows,
		Tags:          d.tags.Value(),
	}
	return json.Marshal(jd)
}

// UnmarshalJSON implements json.Unmarshaler interface
func (d *Dashboard) UnmarshalJSON(data []byte) error {
	type JSONDashboard Dashboard
	inDashboard := struct {
		*JSONDashboard
		ID      *DashboardID `json:"id"`
		Version *uint64      `json:"version"`

		Tags []string       `json:"tags"`
		Meta *DashboardMeta `json:"meta"`
	}{
		JSONDashboard: (*JSONDashboard)(d),
		ID:            &d.ID,
		Version:       &d.Version,
		Meta:          d.Meta,
	}
	if err := json.Unmarshal(data, &inDashboard); err != nil {
		return err
	}

	d.tags = field.NewTags(inDashboard.Tags...)

	return nil
}

type DashboardMeta struct {
	Slug    string `json:"slug"`
	Type    string `json:"type"`
	Version int    `json:"version"`

	CanEdit bool `json:"canEdit"`
	CanSave bool `json:"canSave"`
	CanStar bool `json:"canStar"`

	Created   time.Time `json:"created"`
	CreatedBy string    `json:"createdBy"`
	Expires   time.Time `json:"expires"`
	Updated   time.Time `json:"updated"`
	UpdatedBy string    `json:"updatedBy"`
}

func (dm *DashboardMeta) String() string {
	return Stringify(dm)
}

// Row is panel's row
type Row struct {
	Collapsed bool              `json:"collapse"`
	Editable  bool              `json:"editable"`
	Height    field.ForceString `json:"height"`
	Panels    []Panel           `json:"panels"`
	RepeatFor string            `json:"repeat"` // repeat row for given variable
	ShowTitle bool              `json:"showTitle"`
	Title     string            `json:"title"`
	TitleSize string            `json:"titleSize"` // TODO: validation: h1-h6
}

// NewRow creates new Row with somw defaults.
func NewRow() *Row {
	return &Row{Editable: true}
}

// UnmarshalJSON implements json.Unmarshaler interface
func (r *Row) UnmarshalJSON(data []byte) error {
	type JSONRow Row
	jr := struct {
		*JSONRow
		Panels []probePanel `json:"panels"`
	}{
		JSONRow: (*JSONRow)(r),
	}

	if err := json.Unmarshal(data, &jr); err != nil {
		return err
	}

	panels := make([]Panel, len(jr.Panels))
	for i, p := range jr.Panels {
		panels[i] = p.panel
	}
	r.Panels = panels
	return nil
}

type Panel interface {
	GeneralOptions() *panel.GeneralOptions
}

type panelType string

const (
	textPanelType       panelType = "text"
	singlestatPanelType panelType = "singlestat"
	graphPanelType      panelType = "graph"
)

type probePanel struct {
	ID   uint      `json:"id"`
	Type panelType `json:"type"`

	panel Panel
}

func (p *probePanel) GeneralOptions() *panel.GeneralOptions {
	return p.panel.GeneralOptions()
}

func (p *probePanel) UnmarshalJSON(data []byte) error {
	type JSONPanel probePanel
	jp := struct {
		*JSONPanel
	}{
		JSONPanel: (*JSONPanel)(p),
	}
	if err := json.Unmarshal(data, &jp); err != nil {
		return err
	}

	var pp Panel
	switch jp.Type {
	case textPanelType:
		pp = new(panel.Text)
	case singlestatPanelType:
		pp = new(panel.Singlestat)
	case graphPanelType:
		pp = new(panel.Graph)
	default:
		return nil
	}

	if err := json.Unmarshal(data, pp); err != nil {
		return err
	}

	var generalOptions panel.GeneralOptions
	if err := json.Unmarshal(data, &generalOptions); err != nil {
		return err
	}
	gOpts := pp.GeneralOptions()
	*gOpts = generalOptions

	//var queriesOptions PanelQueriesOptions
	//if err := json.Unmarshal(data, &queriesOptions); err != nil {
	//	return err
	//}

	p.panel = pp
	return nil
}

// MarshalJSON implements json.Marshaler interface
func (p *probePanel) MarshalJSON() ([]byte, error) {
	type JSONPanel probePanel
	jp := struct {
		*JSONPanel

		*panel.Text
		*panel.Singlestat
		*panel.Graph

		*panel.GeneralOptions
	}{
		JSONPanel:      (*JSONPanel)(p),
		GeneralOptions: p.GeneralOptions(),
	}

	switch v := p.panel.(type) {
	case *panel.Text:
		jp.Text = v
		jp.Type = textPanelType
	case *panel.Singlestat:
		jp.Singlestat = v
		jp.Type = singlestatPanelType
	case *panel.Graph:
		jp.Graph = v
		jp.Type = graphPanelType
	}
	return json.Marshal(jp)
}

type probeQuery struct {
	// PrometheusQuery
	IntervalFactor *uint   `json:"intervalFactor"`
	Expression     *string `json:"expr"`

	// GraphiteQuery fields
	Target *string `json:"target"`

	query Query
}

func (q *probeQuery) UnmarshalJSON(data []byte) error {
	type JSONQuery probeQuery
	var jq JSONQuery
	if err := json.Unmarshal(data, &jq); err != nil {
		return err
	}

	//var query Query
	//if jq.Expression != nil && jq.IntervalFactor != nil {
	//	query = new(PrometheusQuery)
	//} else if jq.Target != nil {
	//	query = new(GraphiteQuery)
	//}

	// TODO: Initialize Unknown query here instead
	//if query == nil {
	//	return nil
	//}

	//if err := json.Unmarshal(data, &query); err != nil {
	//	return err
	//}

	//q.query = query
	return nil
}

type Query interface {
	RefID() string
	Datasource() string
	//commonOptions() *query.commonQuery
}

// PanelQueriesOptions is a part of panel that placed in 'Metrics' tab. It represents set of panel queries.
type PanelQueriesOptions struct {
	Datasource string  `json:"datasource,omitempty"`
	Queries    []Query `json:"targets"`
}

func (o *PanelQueriesOptions) UnmarshalJSON(data []byte) error {
	type JSONOptions PanelQueriesOptions

	var queries []*probeQuery
	jo := struct {
		*JSONOptions
		Queries *[]*probeQuery `json:"targets,omitempty"`
	}{
		JSONOptions: (*JSONOptions)(o),
		Queries:     &queries,
	}
	if err := json.Unmarshal(data, &jo); err != nil {
		return err
	}

	//o.Queries = []Query{}
	//for _, q := range queries {
	//	// TODO: queies shouldn't be nil in future. This check will be obsolete
	//	if q.query == nil {
	//		continue
	//	}
	//	o.Queries = append(o.Queries, q.query)
	//}

	return nil
}

// MarshalJSON implements encoding/json.Marshaler
func (o *PanelQueriesOptions) MarshalJSON() ([]byte, error) {
	type JSONOptions PanelQueriesOptions
	jo := (*JSONOptions)(o)

	// FIXME: add checking for uniqueness of refids
	//for i, q := range jo.Queries {
	//	if q.commonOptions().RefID != "" {
	//		continue
	//	}
	//	q.commonOptions().RefID = makeRefID(i)
	//}

	// TODO: if there are a several types of datasources we need to set 'main' datasouce to "Mixed"

	return json.Marshal(jo)
}

// makeRefID returns symbolic ID for given index.
// TODO: It has very rough implementation. Needs refactoring.
func makeRefID(index int) string {
	letters := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

	var id string
	if index >= len(letters) {
		id += makeRefID(index % len(letters))
	} else {
		id = string(letters[index])
	}

	var result string
	for _, v := range id {
		result = string(v) + result
	}
	return result
}
<|endoftext|>


<commit_before>package ddtracer

import (
	"net/http"
	"testing"

	"github.com/DataDog/dd-trace-go/tracer"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPropagationInject(t *testing.T) {
	tr := NewTracer()
	span := tr.StartSpan("span").(*Span)
	span.SpanID = 0xaa
	span.TraceID = 0xbb

	req, _ := http.NewRequest("GET", "/", nil)
	err := tr.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
	require.NoError(t, err)

	assert.Equal(t, "aa", req.Header.Get("Dd-Trace-Spanid"))
	assert.Equal(t, "bb", req.Header.Get("Dd-Trace-Traceid"))

	t.Run("Parent doesn't set ParentID", func(t *testing.T) {
		assert.Empty(t, req.Header.Get("Dd-Trace-Parentid"))
	})

	t.Run("Child sets ParentID", func(t *testing.T) {
		span.ParentID = 0xcc
		err := tr.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
		require.NoError(t, err)
		assert.Equal(t, "cc", req.Header.Get("Dd-Trace-Parentid"))

	})
}

func TestPropagationExtract(t *testing.T) {
	tr := NewTracer()
	req, _ := http.NewRequest("GET", "/", nil)
	req.Header.Set("Dd-Trace-Spanid", "aa")
	req.Header.Set("Dd-Trace-Traceid", "bb")
	req.Header.Set("Dd-Trace-Parentid", "cc")

	spanContext, err := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
	require.NoError(t, err)

	ctx := spanContext.(*SpanContext)
	span, ok := tracer.SpanFromContext(ctx.ctx)
	require.True(t, ok)

	assert.Equal(t, uint64(0xaa), span.SpanID)
	assert.Equal(t, uint64(0xbb), span.TraceID)
	assert.Equal(t, uint64(0xcc), span.ParentID)

}
<commit_msg>Test Corrupted Context<commit_after>package ddtracer

import (
	"net/http"
	"testing"

	"github.com/DataDog/dd-trace-go/tracer"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestPropagationInject(t *testing.T) {
	tr := NewTracer()
	span := tr.StartSpan("span").(*Span)
	span.SpanID = 0xaa
	span.TraceID = 0xbb

	req, _ := http.NewRequest("GET", "/", nil)
	err := tr.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
	require.NoError(t, err)

	assert.Equal(t, "aa", req.Header.Get("Dd-Trace-Spanid"))
	assert.Equal(t, "bb", req.Header.Get("Dd-Trace-Traceid"))

	t.Run("Parent doesn't set ParentID", func(t *testing.T) {
		assert.Empty(t, req.Header.Get("Dd-Trace-Parentid"))
	})

	t.Run("Child sets ParentID", func(t *testing.T) {
		span.ParentID = 0xcc
		err := tr.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
		require.NoError(t, err)
		assert.Equal(t, "cc", req.Header.Get("Dd-Trace-Parentid"))

	})
}

func TestPropagationExtract(t *testing.T) {
	tr := NewTracer()
	req, _ := http.NewRequest("GET", "/", nil)
	req.Header.Set("Dd-Trace-Spanid", "aa")
	req.Header.Set("Dd-Trace-Traceid", "bb")
	req.Header.Set("Dd-Trace-Parentid", "cc")

	spanContext, err := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
	require.NoError(t, err)

	ctx := spanContext.(*SpanContext)
	span, ok := tracer.SpanFromContext(ctx.ctx)
	require.True(t, ok)

	assert.Equal(t, uint64(0xaa), span.SpanID)
	assert.Equal(t, uint64(0xbb), span.TraceID)
	assert.Equal(t, uint64(0xcc), span.ParentID)

	t.Run("CorruptedContext", func(t *testing.T) {
		for _, key := range []string{"Spanid", "Parentid", "Traceid"} {
			_, err := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(http.Header{
				"Dd-Trace-" + key: []string{"NaN"},
			}))
			assert.Equal(t, opentracing.ErrSpanContextCorrupted, err)
		}
	})

}
<|endoftext|>


<commit_before>package main

import (
	"os"

	"github.com/mackerelio/mkr/format"
	"github.com/mackerelio/mkr/logger"
	"github.com/mackerelio/mkr/mackerelclient"
	"github.com/urfave/cli"
)

var commandChannels = cli.Command{
	Name:  "channels",
	Usage: "List notification channels",
	Description: `
	Lists notification channels.
	Requests APIs under "/api/v0/channels". See https://mackerel.io/api-docs/entry/channels .
	`,
	Action: doChannelsList,
}

func doChannelsList(c *cli.Context) error {
	// Waiting for mackerel-client-go to be bumped to version supporting FindChannels.
	client := mackerelclient.NewFromContext(c)
	channels, err := client.FindChannels()
	logger.DieIf(err)

	format.PrettyPrintJSON(os.Stdout, channels)
	return nil
}
<commit_msg>adjust comments<commit_after>package main

import (
	"os"

	"github.com/mackerelio/mkr/format"
	"github.com/mackerelio/mkr/logger"
	"github.com/mackerelio/mkr/mackerelclient"
	"github.com/urfave/cli"
)

var commandChannels = cli.Command{
	Name:  "channels",
	Usage: "List notification channels",
	Description: `
	Lists notification channels. With no subcommand specified, this will show all channels.
	Requests APIs under "/api/v0/channels". See https://mackerel.io/api-docs/entry/channels .
	`,
	Action: doChannelsList,
}

func doChannelsList(c *cli.Context) error {
	client := mackerelclient.NewFromContext(c)
	channels, err := client.FindChannels()
	logger.DieIf(err)

	format.PrettyPrintJSON(os.Stdout, channels)
	return nil
}
<|endoftext|>


<commit_before>// 29 december 2012
package main

import (
	"fmt"
	"os"
	"io"
	"path/filepath"
	"encoding/hex"
	"encoding/binary"
	"bytes"
	"strings"
	"log"
)

// TODO:
// - select one constructor syntax for the maps?

type CHDs map[string]*CHD

var sha1Off = map[uint32]int64{
	// right now using standard; comment has parent (raw if also available)
	3:	80,		// 100
	4:	48,		// 68 (raw 88)
	5:	84,		// 104 (raw 64)
}

const versionFieldOff = 12

func sha1check_chd(f *os.File, expectstring string) (bool, error) {
	expected, err := hex.DecodeString(expectstring)
	if err != nil {
		log.Fatalf("hex decode error reading sha1 (%q): %v", expectstring, err)
	}

	var version uint32
	var sha1 [20]byte

	_, err = f.Seek(versionFieldOff, 0)
	if err != nil {
		return false, fmt.Errorf("seek in CHD to find version number failed: %v", err)
	}
	err = binary.Read(f, binary.BigEndian, &version)
	if err != nil {
		return false, fmt.Errorf("read version number from CHD failed: %v", err)
	}

	if sha1Off[version] == 0 {
		return false, fmt.Errorf("invalid CHD version %d", version)
	}
	_, err = f.Seek(sha1Off[version], 0)
	if err != nil {
		return false, fmt.Errorf("seek in CHD to get SHA-1 sum failed: %v", err)
	}
	_, err = io.ReadFull(f, sha1[:])
	if err != nil {
		return false, fmt.Errorf("read of SHA-1 failed: %v", err)
	}

	return bytes.Equal(expected, sha1[:]), nil
}

// TODO change filename_CHD to not be part of Game as well
func filename_CHD(rompath string, gamename string, CHDname string) string {
	return filepath.Join(rompath, gamename, CHDname + ".chd")
}

func (g *Game) checkCHDIn(rompath string, chd *CHD) (bool, string, error) {
	try := func(dir string) (bool, string, error) {
		fn := filename_CHD(rompath, dir, chd.Name)
		file, err := os.Open(fn)
		if os.IsNotExist(err) {
			return false, "", nil
		} else if err != nil {
			return false, "", fmt.Errorf("could not open CHD file %s: %v", fn, err)
		}
		good, err := sha1check_chd(file, chd.SHA1)
		file.Close()
		if err != nil {
			return false, "", fmt.Errorf("could not calculate SHA-1 sum of CHD %s: %v", fn, err)
		}
		if !good {
			return false, "", nil
		}
		return true, fn, nil
	}

	// first try the game
	found, path, err := try(g.Name)
	if err != nil {
		return false, "", err
	}
	if found {
		return true, path, nil
	}

	// then its parents
	for _, p := range g.Parents {
		found, path, err := try(p)
		if err != nil {
			return false, "", err
		}
		if found {
			return true, path, nil
		}
	}

	// nope
	return false, "", nil
}

// remove all CHDs belonging to this set and its parents from the list
func (g *Game) strikeCHDs(chds CHDs) {
	for _, rom := range g.CHDs {
		delete(chds, rom.Name)
	}
	for _, parent := range g.Parents {
		games[parent].strikeCHDs(chds)
	}
}

func (g *Game) findCHDs() (found bool, err error) {
	g.CHDLoc = map[string]string{}

	// populate list of CHDs
	var chds = make(CHDs)
	for i := range g.CHDs {
		if g.CHDs[i].Status != nodump {		// otherwise games with known undumped CHDs will return "not found" because the map never depletes
			// some ROM sets (scregg, for instance) have trailing spaces in the filenames given in he XML file (dc0.c6, in this example)
			// TODO this will also remove leading spaces; is that correct?
			chds[strings.TrimSpace(g.CHDs[i].Name)] = &(g.CHDs[i])
		}
	}

	// find the parents and remove their CHDs rom the list
	for _, parent := range g.Parents {
		found, err := games[parent].Find()
		if err != nil {
			return false, fmt.Errorf("error finding parent %s: %v", parent, err)
		}
		if !found {
			return false, nil		// TODO return parent not found as an error?
		}
		games[parent].strikeCHDs(chds)
	}

	if len(chds) == 0 {		// no CHDs left to check (either has no CHDs or we are done)
		return true, nil
	}

	// go through the directories, finding the right file
	n := len(chds)
	for name, chd := range chds {
		for _, d := range dirs {
			found, path, err := g.checkCHDIn(d, chd)
			if err != nil {
				return false, err
			}
			if found {
				g.CHDLoc[name] = path
				n--
				break		// found it in this dir; stop scanning dirs and go to the next CHD
			}
		}
	}

	if n == 0 {		// all found!
		return true, nil
	}

	// nope
	return false, nil
}
<commit_msg>Remove leftover TODO; filename_CHD is not part of Game.<commit_after>// 29 december 2012
package main

import (
	"fmt"
	"os"
	"io"
	"path/filepath"
	"encoding/hex"
	"encoding/binary"
	"bytes"
	"strings"
	"log"
)

// TODO:
// - select one constructor syntax for the maps?

type CHDs map[string]*CHD

var sha1Off = map[uint32]int64{
	// right now using standard; comment has parent (raw if also available)
	3:	80,		// 100
	4:	48,		// 68 (raw 88)
	5:	84,		// 104 (raw 64)
}

const versionFieldOff = 12

func sha1check_chd(f *os.File, expectstring string) (bool, error) {
	expected, err := hex.DecodeString(expectstring)
	if err != nil {
		log.Fatalf("hex decode error reading sha1 (%q): %v", expectstring, err)
	}

	var version uint32
	var sha1 [20]byte

	_, err = f.Seek(versionFieldOff, 0)
	if err != nil {
		return false, fmt.Errorf("seek in CHD to find version number failed: %v", err)
	}
	err = binary.Read(f, binary.BigEndian, &version)
	if err != nil {
		return false, fmt.Errorf("read version number from CHD failed: %v", err)
	}

	if sha1Off[version] == 0 {
		return false, fmt.Errorf("invalid CHD version %d", version)
	}
	_, err = f.Seek(sha1Off[version], 0)
	if err != nil {
		return false, fmt.Errorf("seek in CHD to get SHA-1 sum failed: %v", err)
	}
	_, err = io.ReadFull(f, sha1[:])
	if err != nil {
		return false, fmt.Errorf("read of SHA-1 failed: %v", err)
	}

	return bytes.Equal(expected, sha1[:]), nil
}

func filename_CHD(rompath string, gamename string, CHDname string) string {
	return filepath.Join(rompath, gamename, CHDname + ".chd")
}

func (g *Game) checkCHDIn(rompath string, chd *CHD) (bool, string, error) {
	try := func(dir string) (bool, string, error) {
		fn := filename_CHD(rompath, dir, chd.Name)
		file, err := os.Open(fn)
		if os.IsNotExist(err) {
			return false, "", nil
		} else if err != nil {
			return false, "", fmt.Errorf("could not open CHD file %s: %v", fn, err)
		}
		good, err := sha1check_chd(file, chd.SHA1)
		file.Close()
		if err != nil {
			return false, "", fmt.Errorf("could not calculate SHA-1 sum of CHD %s: %v", fn, err)
		}
		if !good {
			return false, "", nil
		}
		return true, fn, nil
	}

	// first try the game
	found, path, err := try(g.Name)
	if err != nil {
		return false, "", err
	}
	if found {
		return true, path, nil
	}

	// then its parents
	for _, p := range g.Parents {
		found, path, err := try(p)
		if err != nil {
			return false, "", err
		}
		if found {
			return true, path, nil
		}
	}

	// nope
	return false, "", nil
}

// remove all CHDs belonging to this set and its parents from the list
func (g *Game) strikeCHDs(chds CHDs) {
	for _, rom := range g.CHDs {
		delete(chds, rom.Name)
	}
	for _, parent := range g.Parents {
		games[parent].strikeCHDs(chds)
	}
}

func (g *Game) findCHDs() (found bool, err error) {
	g.CHDLoc = map[string]string{}

	// populate list of CHDs
	var chds = make(CHDs)
	for i := range g.CHDs {
		if g.CHDs[i].Status != nodump {		// otherwise games with known undumped CHDs will return "not found" because the map never depletes
			// some ROM sets (scregg, for instance) have trailing spaces in the filenames given in he XML file (dc0.c6, in this example)
			// TODO this will also remove leading spaces; is that correct?
			chds[strings.TrimSpace(g.CHDs[i].Name)] = &(g.CHDs[i])
		}
	}

	// find the parents and remove their CHDs rom the list
	for _, parent := range g.Parents {
		found, err := games[parent].Find()
		if err != nil {
			return false, fmt.Errorf("error finding parent %s: %v", parent, err)
		}
		if !found {
			return false, nil		// TODO return parent not found as an error?
		}
		games[parent].strikeCHDs(chds)
	}

	if len(chds) == 0 {		// no CHDs left to check (either has no CHDs or we are done)
		return true, nil
	}

	// go through the directories, finding the right file
	n := len(chds)
	for name, chd := range chds {
		for _, d := range dirs {
			found, path, err := g.checkCHDIn(d, chd)
			if err != nil {
				return false, err
			}
			if found {
				g.CHDLoc[name] = path
				n--
				break		// found it in this dir; stop scanning dirs and go to the next CHD
			}
		}
	}

	if n == 0 {		// all found!
		return true, nil
	}

	// nope
	return false, nil
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	var gridDim int
	fmt.Print("Enter circular array size: ")
	fmt.Scan(&gridDim)

	var cellValue int = 1
	var col1, col2, row1, row2 int = 0, gridDim - 1, 0, gridDim - 1
	var gridLen int = gridDim * gridDim

	// allocate composed 2d array
	grid := make([][]int, gridDim)
	for i := range grid {
		grid[i] = make([]int, gridDim)
	}

	for cellValue <= gridLen {
		for i := col1; i <= col2; i++ {
			grid[row1][i] = cellValue
			cellValue++
		}

		for j := row1 + 1; j <= row2; j++ {
			grid[j][col2] = cellValue
			cellValue++
		}

		for i := col2 - 1; i >= col1; i-- {
			grid[row2][i] = cellValue
			cellValue++
		}

		for j := row2 - 1; j >= row1+1; j-- {
			grid[j][col1] = cellValue
			cellValue++
		}

		col1++
		col2--
		row1++
		row2--
	}

	/* printing the circular matrix */
	println("Here is your circular matrix is:")
	for i := 0; i < gridDim; i++ {
		for j := 0; j < gridDim; j++ {
			fmt.Printf("%d\t", grid[i][j])
		}
		println()
	}

}<commit_msg>Added comments<commit_after>package main

import "fmt"

// main creates a square matrix of size (n*n) and fills it in a circular fashion
func main() {
	var gridDim int
	fmt.Print("Enter circular array size: ")
	fmt.Scan(&gridDim)

	var cellValue int = 1
	var col1, col2, row1, row2 int = 0, gridDim - 1, 0, gridDim - 1
	var gridLen int = gridDim * gridDim

	// allocate composed 2d array
	grid := make([][]int, gridDim)
	for i := range grid {
		grid[i] = make([]int, gridDim)
	}

	for cellValue <= gridLen {
		for i := col1; i <= col2; i++ {
			grid[row1][i] = cellValue
			cellValue++
		}

		for j := row1 + 1; j <= row2; j++ {
			grid[j][col2] = cellValue
			cellValue++
		}

		for i := col2 - 1; i >= col1; i-- {
			grid[row2][i] = cellValue
			cellValue++
		}

		for j := row2 - 1; j >= row1+1; j-- {
			grid[j][col1] = cellValue
			cellValue++
		}

		col1++
		col2--
		row1++
		row2--
	}

	/* printing the circular matrix */
	println("Here is your circular matrix is:")
	for i := 0; i < gridDim; i++ {
		for j := 0; j < gridDim; j++ {
			fmt.Printf("%d\t", grid[i][j])
		}
		println()
	}

}<|endoftext|>


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

package mem

import (
	"sync"
	"time"

	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
)

// Alerts gives access to a set of alerts. All methods are goroutine-safe.
type Alerts struct {
	mtx        sync.RWMutex
	alerts     map[model.Fingerprint]*types.Alert
	marker     types.Marker
	intervalGC time.Duration
	stopGC     chan struct{}

	listeners map[int]chan *types.Alert
	next      int
}

// NewAlerts returns a new alert provider.
func NewAlerts(m types.Marker, intervalGC time.Duration) (*Alerts, error) {
	a := &Alerts{
		alerts:     map[model.Fingerprint]*types.Alert{},
		marker:     m,
		intervalGC: intervalGC,
		stopGC:     make(chan struct{}),
		listeners:  map[int]chan *types.Alert{},
		next:       0,
	}
	go a.runGC()

	return a, nil
}

func (a *Alerts) runGC() {
	for {
		select {
		case <-a.stopGC:
			return
		case <-time.After(a.intervalGC):
		}

		a.mtx.Lock()

		for fp, alert := range a.alerts {
			// As we don't persist alerts, we no longer consider them after
			// they are resolved. Alerts waiting for resolved notifications are
			// held in memory in aggregation groups redundantly.
			if alert.EndsAt.Before(time.Now()) {
				delete(a.alerts, fp)
				a.marker.Delete(fp)
			}
		}

		a.mtx.Unlock()
	}
}

// Close the alert provider.
func (a *Alerts) Close() error {
	close(a.stopGC)
	return nil
}

// Subscribe returns an iterator over active alerts that have not been
// resolved and successfully notified about.
// They are not guaranteed to be in chronological order.
func (a *Alerts) Subscribe() provider.AlertIterator {
	var (
		ch   = make(chan *types.Alert, 200)
		done = make(chan struct{})
	)
	alerts, err := a.getPending()

	a.mtx.Lock()
	i := a.next
	a.next++
	a.listeners[i] = ch
	a.mtx.Unlock()

	go func() {
		defer func() {
			a.mtx.Lock()
			delete(a.listeners, i)
			close(ch)
			a.mtx.Unlock()
		}()

		for _, a := range alerts {
			select {
			case ch <- a:
			case <-done:
				return
			}
		}

		<-done
	}()

	return provider.NewAlertIterator(ch, done, err)
}

// GetPending returns an iterator over all alerts that have
// pending notifications.
func (a *Alerts) GetPending() provider.AlertIterator {
	var (
		ch   = make(chan *types.Alert, 200)
		done = make(chan struct{})
	)

	alerts, err := a.getPending()

	go func() {
		defer close(ch)

		for _, a := range alerts {
			select {
			case ch <- a:
			case <-done:
				return
			}
		}
	}()

	return provider.NewAlertIterator(ch, done, err)
}

func (a *Alerts) getPending() ([]*types.Alert, error) {
	a.mtx.RLock()
	defer a.mtx.RUnlock()

	res := make([]*types.Alert, 0, len(a.alerts))

	for _, alert := range a.alerts {
		res = append(res, alert)
	}

	return res, nil
}

// Get returns the alert for a given fingerprint.
func (a *Alerts) Get(fp model.Fingerprint) (*types.Alert, error) {
	a.mtx.RLock()
	defer a.mtx.RUnlock()

	alert, ok := a.alerts[fp]
	if !ok {
		return nil, provider.ErrNotFound
	}
	return alert, nil
}

// Put adds the given alert to the set.
func (a *Alerts) Put(alerts ...*types.Alert) error {
	a.mtx.Lock()
	defer a.mtx.Unlock()

	for _, alert := range alerts {
		fp := alert.Fingerprint()

		if old, ok := a.alerts[fp]; ok {
			// Merge alerts if there is an overlap in activity range.
			if (alert.EndsAt.After(old.StartsAt) && alert.EndsAt.Before(old.EndsAt)) ||
				(alert.StartsAt.After(old.StartsAt) && alert.StartsAt.Before(old.EndsAt)) {
				alert = old.Merge(alert)
			}
		}

		a.alerts[fp] = alert

		for _, ch := range a.listeners {
			ch <- alert
		}
	}

	return nil
}
<commit_msg>Avoid listener blocking (#1482)<commit_after>// Copyright 2016 Prometheus Team
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/lic:wenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mem

import (
	"sync"
	"time"

	"github.com/prometheus/alertmanager/provider"
	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
)

// Alerts gives access to a set of alerts. All methods are goroutine-safe.
type Alerts struct {
	mtx        sync.RWMutex
	alerts     map[model.Fingerprint]*types.Alert
	marker     types.Marker
	intervalGC time.Duration
	stopGC     chan struct{}
	listeners  map[int]listeningAlerts
	next       int
}

type listeningAlerts struct {
	alerts chan *types.Alert
	done   chan struct{}
}

// NewAlerts returns a new alert provider.
func NewAlerts(m types.Marker, intervalGC time.Duration) (*Alerts, error) {
	a := &Alerts{
		alerts:     map[model.Fingerprint]*types.Alert{},
		marker:     m,
		intervalGC: intervalGC,
		stopGC:     make(chan struct{}),
		listeners:  map[int]listeningAlerts{},
		next:       0,
	}
	go a.runGC()

	return a, nil
}

func (a *Alerts) runGC() {
	for {
		select {
		case <-a.stopGC:
			return
		case <-time.After(a.intervalGC):
		}

		a.mtx.Lock()

		for fp, alert := range a.alerts {
			// As we don't persist alerts, we no longer consider them after
			// they are resolved. Alerts waiting for resolved notifications are
			// held in memory in aggregation groups redundantly.
			if alert.EndsAt.Before(time.Now()) {
				delete(a.alerts, fp)
				a.marker.Delete(fp)
			}
		}

		a.mtx.Unlock()
	}
}

// Close the alert provider.
func (a *Alerts) Close() error {
	close(a.stopGC)
	return nil
}

// Subscribe returns an iterator over active alerts that have not been
// resolved and successfully notified about.
// They are not guaranteed to be in chronological order.
func (a *Alerts) Subscribe() provider.AlertIterator {
	var (
		ch   = make(chan *types.Alert, 200)
		done = make(chan struct{})
	)
	alerts, err := a.getPending()

	a.mtx.Lock()
	i := a.next
	a.next++
	a.listeners[i] = listeningAlerts{alerts: ch, done: done}
	a.mtx.Unlock()

	go func() {
		defer func() {
			a.mtx.Lock()
			delete(a.listeners, i)
			close(ch)
			a.mtx.Unlock()
		}()

		for _, a := range alerts {
			select {
			case ch <- a:
			case <-done:
				return
			}
		}

		<-done
	}()

	return provider.NewAlertIterator(ch, done, err)
}

// GetPending returns an iterator over all alerts that have
// pending notifications.
func (a *Alerts) GetPending() provider.AlertIterator {
	var (
		ch   = make(chan *types.Alert, 200)
		done = make(chan struct{})
	)

	alerts, err := a.getPending()

	go func() {
		defer close(ch)

		for _, a := range alerts {
			select {
			case ch <- a:
			case <-done:
				return
			}
		}
	}()

	return provider.NewAlertIterator(ch, done, err)
}

func (a *Alerts) getPending() ([]*types.Alert, error) {
	a.mtx.RLock()
	defer a.mtx.RUnlock()

	res := make([]*types.Alert, 0, len(a.alerts))

	for _, alert := range a.alerts {
		res = append(res, alert)
	}

	return res, nil
}

// Get returns the alert for a given fingerprint.
func (a *Alerts) Get(fp model.Fingerprint) (*types.Alert, error) {
	a.mtx.RLock()
	defer a.mtx.RUnlock()

	alert, ok := a.alerts[fp]
	if !ok {
		return nil, provider.ErrNotFound
	}
	return alert, nil
}

// Put adds the given alert to the set.
func (a *Alerts) Put(alerts ...*types.Alert) error {
	a.mtx.Lock()
	defer a.mtx.Unlock()

	for _, alert := range alerts {
		fp := alert.Fingerprint()

		if old, ok := a.alerts[fp]; ok {
			// Merge alerts if there is an overlap in activity range.
			if (alert.EndsAt.After(old.StartsAt) && alert.EndsAt.Before(old.EndsAt)) ||
				(alert.StartsAt.After(old.StartsAt) && alert.StartsAt.Before(old.EndsAt)) {
				alert = old.Merge(alert)
			}
		}

		a.alerts[fp] = alert

		for _, l := range a.listeners {
			select {
			case l.alerts <- alert:
			case <-l.done:
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package kube

import (
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"

	"github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"k8s.io/test-infra/prow/version"
)

func kubeConfigs(loader clientcmd.ClientConfigLoader) (map[string]rest.Config, string, error) {
	cfg, err := loader.Load()
	if err != nil {
		return nil, "", fmt.Errorf("failed to load: %v", err)
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, loader).ClientConfig()
		if err != nil {
			return nil, "", fmt.Errorf("create %s client: %v", context, err)
		}
		contextCfg.UserAgent = version.UserAgent()
		configs[context] = *contextCfg
		logrus.Infof("Parsed kubeconfig context: %s", context)
	}
	return configs, cfg.CurrentContext, nil
}

func mergeConfigs(local *rest.Config, foreign map[string]rest.Config, currentContext string) (map[string]rest.Config, error) {
	ret := map[string]rest.Config{}
	for ctx, cfg := range foreign {
		ret[ctx] = cfg
	}
	if local != nil {
		ret[InClusterContext] = *local
	} else if currentContext != "" {
		ret[InClusterContext] = ret[currentContext]
	} else {
		return nil, errors.New("no prow cluster access: in-cluster current kubecfg context required")
	}
	if len(ret) == 0 {
		return nil, errors.New("no client contexts found")
	}
	if _, ok := ret[DefaultClusterAlias]; !ok {
		ret[DefaultClusterAlias] = ret[InClusterContext]
	}
	return ret, nil
}

// LoadClusterConfigs loads rest.Configs for creation of clients according to the given options.
// Errors are returned if a file/dir is specified in the options and invalid or if no valid contexts are found.
func LoadClusterConfigs(opts *Options) (map[string]rest.Config, error) {

	logrus.Infof("Loading cluster contexts...")
	// This will work if we are running inside kubernetes
	localCfg, err := rest.InClusterConfig()
	if err != nil {
		logrus.WithError(err).Warn("Could not create in-cluster config (expected when running outside the cluster).")
	} else {
		localCfg.UserAgent = version.UserAgent()
	}
	if localCfg != nil && opts.projectedTokenFile != "" {
		localCfg.BearerToken = ""
		localCfg.BearerTokenFile = opts.projectedTokenFile
		logrus.WithField("tokenfile", opts.projectedTokenFile).Info("Using projected token file")
	}

	var candidates []string
	if opts.file != "" {
		candidates = append(candidates, opts.file)
	}
	if opts.dir != "" {
		files, err := ioutil.ReadDir(opts.dir)
		if err != nil {
			return nil, fmt.Errorf("kubecfg dir: %v", err)
		}
		for _, file := range files {
			candidates = append(candidates, filepath.Join(opts.dir, file.Name()))
		}
	}

	allKubeCfgs := map[string]rest.Config{}
	var currentContext string
	if len(candidates) == 0 {
		// loading from the defaults, e.g., ${KUBECONFIG}
		if allKubeCfgs, currentContext, err = kubeConfigs(clientcmd.NewDefaultClientConfigLoadingRules()); err != nil {
			logrus.WithError(err).Warn("Cannot load kubecfg")
		}
	} else {
		for _, candidate := range candidates {
			logrus.Infof("Loading kubeconfig from: %q", candidate)
			kubeCfgs, tempCurrentContext, err := kubeConfigs(&clientcmd.ClientConfigLoadingRules{ExplicitPath: candidate})
			if err != nil {
				return nil, fmt.Errorf("fail to load kubecfg from %q: %v", candidate, err)
			}
			currentContext = tempCurrentContext
			for c, k := range kubeCfgs {
				if _, ok := allKubeCfgs[c]; ok {
					return nil, fmt.Errorf("context %s occurred more than once in kubeconfig dir %q", c, opts.dir)
				}
				allKubeCfgs[c] = k
			}
		}
	}

	return mergeConfigs(localCfg, allKubeCfgs, currentContext)
}

// Options defines how to load kubeconfigs files
type Options struct {
	file               string
	dir                string
	projectedTokenFile string
}

type ConfigOptions func(*Options)

// ConfigDir configures the directory containing kubeconfig files
func ConfigDir(dir string) ConfigOptions {
	return func(kc *Options) {
		kc.dir = dir
	}
}

// ConfigFile configures the path to a kubeconfig file
func ConfigFile(file string) ConfigOptions {
	return func(kc *Options) {
		kc.file = file
	}
}

// ConfigFile configures the path to a projectedToken file
func ConfigProjectedTokenFile(projectedTokenFile string) ConfigOptions {
	return func(kc *Options) {
		kc.projectedTokenFile = projectedTokenFile
	}
}

// NewConfig builds Options according to the given ConfigOptions
func NewConfig(opts ...ConfigOptions) *Options {
	kc := &Options{}
	for _, opt := range opts {
		opt(kc)
	}
	return kc
}
<commit_msg>Ignore folders while loading kuebconfig files<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package kube

import (
	"errors"
	"fmt"
	"io/ioutil"
	"path/filepath"

	"github.com/sirupsen/logrus"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"k8s.io/test-infra/prow/version"
)

func kubeConfigs(loader clientcmd.ClientConfigLoader) (map[string]rest.Config, string, error) {
	cfg, err := loader.Load()
	if err != nil {
		return nil, "", fmt.Errorf("failed to load: %v", err)
	}
	configs := map[string]rest.Config{}
	for context := range cfg.Contexts {
		contextCfg, err := clientcmd.NewNonInteractiveClientConfig(*cfg, context, &clientcmd.ConfigOverrides{}, loader).ClientConfig()
		if err != nil {
			return nil, "", fmt.Errorf("create %s client: %v", context, err)
		}
		contextCfg.UserAgent = version.UserAgent()
		configs[context] = *contextCfg
		logrus.Infof("Parsed kubeconfig context: %s", context)
	}
	return configs, cfg.CurrentContext, nil
}

func mergeConfigs(local *rest.Config, foreign map[string]rest.Config, currentContext string) (map[string]rest.Config, error) {
	ret := map[string]rest.Config{}
	for ctx, cfg := range foreign {
		ret[ctx] = cfg
	}
	if local != nil {
		ret[InClusterContext] = *local
	} else if currentContext != "" {
		ret[InClusterContext] = ret[currentContext]
	} else {
		return nil, errors.New("no prow cluster access: in-cluster current kubecfg context required")
	}
	if len(ret) == 0 {
		return nil, errors.New("no client contexts found")
	}
	if _, ok := ret[DefaultClusterAlias]; !ok {
		ret[DefaultClusterAlias] = ret[InClusterContext]
	}
	return ret, nil
}

// LoadClusterConfigs loads rest.Configs for creation of clients according to the given options.
// Errors are returned if a file/dir is specified in the options and invalid or if no valid contexts are found.
func LoadClusterConfigs(opts *Options) (map[string]rest.Config, error) {

	logrus.Infof("Loading cluster contexts...")
	// This will work if we are running inside kubernetes
	localCfg, err := rest.InClusterConfig()
	if err != nil {
		logrus.WithError(err).Warn("Could not create in-cluster config (expected when running outside the cluster).")
	} else {
		localCfg.UserAgent = version.UserAgent()
	}
	if localCfg != nil && opts.projectedTokenFile != "" {
		localCfg.BearerToken = ""
		localCfg.BearerTokenFile = opts.projectedTokenFile
		logrus.WithField("tokenfile", opts.projectedTokenFile).Info("Using projected token file")
	}

	var candidates []string
	if opts.file != "" {
		candidates = append(candidates, opts.file)
	}
	if opts.dir != "" {
		files, err := ioutil.ReadDir(opts.dir)
		if err != nil {
			return nil, fmt.Errorf("kubecfg dir: %v", err)
		}
		for _, file := range files {
			if file.IsDir() {
				continue
			}
			candidates = append(candidates, filepath.Join(opts.dir, file.Name()))
		}
	}

	allKubeCfgs := map[string]rest.Config{}
	var currentContext string
	if len(candidates) == 0 {
		// loading from the defaults, e.g., ${KUBECONFIG}
		if allKubeCfgs, currentContext, err = kubeConfigs(clientcmd.NewDefaultClientConfigLoadingRules()); err != nil {
			logrus.WithError(err).Warn("Cannot load kubecfg")
		}
	} else {
		for _, candidate := range candidates {
			logrus.Infof("Loading kubeconfig from: %q", candidate)
			kubeCfgs, tempCurrentContext, err := kubeConfigs(&clientcmd.ClientConfigLoadingRules{ExplicitPath: candidate})
			if err != nil {
				return nil, fmt.Errorf("fail to load kubecfg from %q: %v", candidate, err)
			}
			currentContext = tempCurrentContext
			for c, k := range kubeCfgs {
				if _, ok := allKubeCfgs[c]; ok {
					return nil, fmt.Errorf("context %s occurred more than once in kubeconfig dir %q", c, opts.dir)
				}
				allKubeCfgs[c] = k
			}
		}
	}

	return mergeConfigs(localCfg, allKubeCfgs, currentContext)
}

// Options defines how to load kubeconfigs files
type Options struct {
	file               string
	dir                string
	projectedTokenFile string
}

type ConfigOptions func(*Options)

// ConfigDir configures the directory containing kubeconfig files
func ConfigDir(dir string) ConfigOptions {
	return func(kc *Options) {
		kc.dir = dir
	}
}

// ConfigFile configures the path to a kubeconfig file
func ConfigFile(file string) ConfigOptions {
	return func(kc *Options) {
		kc.file = file
	}
}

// ConfigFile configures the path to a projectedToken file
func ConfigProjectedTokenFile(projectedTokenFile string) ConfigOptions {
	return func(kc *Options) {
		kc.projectedTokenFile = projectedTokenFile
	}
}

// NewConfig builds Options according to the given ConfigOptions
func NewConfig(opts ...ConfigOptions) *Options {
	kc := &Options{}
	for _, opt := range opts {
		opt(kc)
	}
	return kc
}
<|endoftext|>


<commit_before>package proxy

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"testing"
)

func TestResponse(t *testing.T) {
	t.Run("should support alias match with request path", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		if res.Code != 200 {
			t.Fatalf("expected HTTP code 200, got %d", res.Code)
		}

		var body metadataCredentials
		err = json.NewDecoder(res.Body).Decode(&body)
		fatalOnErr(t, err)

		expectedCreds := defaultCreds()
		stringsEqual(t, [][2]string{
			[2]string{"Success", body.Code},
			[2]string{*expectedCreds.AccessKeyId, body.AccessKeyID},
			[2]string{"AWS-HMAC", body.Type},
			[2]string{*expectedCreds.SecretAccessKey, body.SecretAccessKey},
			[2]string{*expectedCreds.SessionToken, body.Token},
			[2]string{stsSvc.output.Credentials.Expiration.String(), body.Expiration.String()},
		})

		if *stsSvc.input.RoleArn != config.AliasToARN["noperms"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["noperms"], *stsSvc.input.RoleArn)
		}
		if stsSvc.input.Policy != nil {
			t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
		}
	})

	t.Run("should detect alias mismatch with request path", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/invalid", config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		if res.Code != 404 {
			t.Fatalf("expected HTTP code 404, got %d", res.Code)
		}

		bodyBytes, err := ioutil.ReadAll(res.Body)
		fatalOnErr(t, err)
		if len(bodyBytes) != 0 {
			t.Fatalf("expected empty body, got [%s]", string(bodyBytes))
		}

		if *stsSvc.input.RoleArn != config.AliasToARN["noperms"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["noperms"], *stsSvc.input.RoleArn)
		}
		if stsSvc.input.Policy != nil {
			t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
		}
	})

	t.Run("should detect request path without role", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/", config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		if res.Code != 200 {
			t.Fatalf("expected HTTP code 200, got %d", res.Code)
		}

		bodyBytes, err := ioutil.ReadAll(res.Body)
		fatalOnErr(t, err)
		if len(bodyBytes) == 0 {
			t.Fatal("expected non-empty body")
		}
		bodyBytesStr := string(bodyBytes)

		if bodyBytesStr != defaultRoleARNFriendlyName {
			t.Fatalf("expected role ARN [%s], got [%s]", defaultRoleARNFriendlyName, bodyBytesStr)
		}

		if *stsSvc.input.RoleArn != config.AliasToARN["noperms"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["noperms"], *stsSvc.input.RoleArn)
		}
		if stsSvc.input.Policy != nil {
			t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
		}
	})

	t.Run("should apply default role", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		if res.Code != 200 {
			t.Fatalf("expected HTTP code 200, got %d", res.Code)
		}

		var body metadataCredentials
		err = json.NewDecoder(res.Body).Decode(&body)
		fatalOnErr(t, err)

		expectedCreds := defaultCreds()
		stringsEqual(t, [][2]string{
			[2]string{"Success", body.Code},
			[2]string{*expectedCreds.AccessKeyId, body.AccessKeyID},
			[2]string{"AWS-HMAC", body.Type},
			[2]string{*expectedCreds.SecretAccessKey, body.SecretAccessKey},
			[2]string{*expectedCreds.SessionToken, body.Token},
			[2]string{stsSvc.output.Credentials.Expiration.String(), body.Expiration.String()},
		})

		if *stsSvc.input.RoleArn != config.AliasToARN["noperms"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["noperms"], *stsSvc.input.RoleArn)
		}
		if stsSvc.input.Policy != nil {
			t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
		}
	})

	t.Run("should apply default policy", func(t *testing.T) {
		config := defaultConfig()
		config.DefaultPolicy = defaultPolicy

		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		if res.Code != 200 {
			t.Fatalf("expected HTTP code 200, got %d", res.Code)
		}

		var body metadataCredentials
		err = json.NewDecoder(res.Body).Decode(&body)
		fatalOnErr(t, err)

		expectedCreds := defaultCreds()
		stringsEqual(t, [][2]string{
			[2]string{"Success", body.Code},
			[2]string{*expectedCreds.AccessKeyId, body.AccessKeyID},
			[2]string{"AWS-HMAC", body.Type},
			[2]string{*expectedCreds.SecretAccessKey, body.SecretAccessKey},
			[2]string{*expectedCreds.SessionToken, body.Token},
			[2]string{stsSvc.output.Credentials.Expiration.String(), body.Expiration.String()},
		})

		if *stsSvc.input.RoleArn != config.AliasToARN["noperms"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["noperms"], *stsSvc.input.RoleArn)
		}
		if *stsSvc.input.Policy != defaultPolicy {
			t.Fatalf("expected custom policy [%s], got [%s]", defaultPolicy, *stsSvc.input.Policy)
		}
	})

	t.Run("should support custom labels", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/"+dbRoleARNFriendlyName, config, stsSvc, containerSvc, ipWithAllLabels)
		fatalOnErr(t, err)

		if res.Code != 200 {
			t.Fatalf("expected HTTP code 200, got %d", res.Code)
		}

		var body metadataCredentials
		err = json.NewDecoder(res.Body).Decode(&body)
		fatalOnErr(t, err)

		expectedCreds := defaultCreds()
		stringsEqual(t, [][2]string{
			[2]string{"Success", body.Code},
			[2]string{*expectedCreds.AccessKeyId, body.AccessKeyID},
			[2]string{"AWS-HMAC", body.Type},
			[2]string{*expectedCreds.SecretAccessKey, body.SecretAccessKey},
			[2]string{*expectedCreds.SessionToken, body.Token},
			[2]string{stsSvc.output.Credentials.Expiration.String(), body.Expiration.String()},
		})

		if *stsSvc.input.RoleArn != config.AliasToARN["db"] {
			t.Fatalf("expected assumed role to be [%s], instead [%s]", config.AliasToARN["db"], *stsSvc.input.RoleArn)
		}
		if *stsSvc.input.Policy != defaultCustomPolicy {
			t.Fatalf("expected custom policy [%s], got [%s]", defaultCustomPolicy, *stsSvc.input.Policy)
		}
	})

	t.Run("should support no selected defaults", func(t *testing.T) {
		config := defaultConfig()
		config.DefaultAlias = ""

		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		if res.Code != 404 {
			t.Fatalf("expected HTTP code 404, got %d", res.Code)
		}

		bodyBytes, err := ioutil.ReadAll(res.Body)
		fatalOnErr(t, err)
		if len(bodyBytes) != 0 {
			t.Fatalf("expected empty body, got [%s]", string(bodyBytes))
		}

		if *stsSvc.input.RoleArn != "" {
			t.Fatalf("expected assumed role to be empty, instead [%s]", *stsSvc.input.RoleArn)
		}
		if stsSvc.input.Policy != nil {
			t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
		}
	})
}
<commit_msg>test: refactor response coverage<commit_after>package proxy

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http/httptest"
	"testing"
)

func TestResponse(t *testing.T) {
	t.Run("should support alias match with request path", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 200)
		credsEqualDefaults(t, res.Body, stsSvc)
		assumeRoleAliasIs(t, "noperms", config, stsSvc)
		assumeRolePolicyIsNil(t, stsSvc)
	})

	t.Run("should detect alias mismatch with request path", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/invalid", config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 404)
		bodyIsEmpty(t, res.Body)
		assumeRoleAliasIs(t, "noperms", config, stsSvc)
		assumeRolePolicyIsNil(t, stsSvc)
	})

	t.Run("should detect request path without role", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/", config, stsSvc, containerSvc, defaultIP)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 200)
		body := bodyIsNonEmpty(t, res.Body)

		if body != defaultRoleARNFriendlyName {
			t.Fatalf("expected role ARN [%s], got [%s]", defaultRoleARNFriendlyName, body)
		}

		assumeRoleAliasIs(t, "noperms", config, stsSvc)
		assumeRolePolicyIsNil(t, stsSvc)
	})

	t.Run("should apply default role", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 200)
		credsEqualDefaults(t, res.Body, stsSvc)
		assumeRoleAliasIs(t, "noperms", config, stsSvc)
		assumeRolePolicyIsNil(t, stsSvc)
	})

	t.Run("should apply default policy", func(t *testing.T) {
		config := defaultConfig()
		config.DefaultPolicy = defaultPolicy

		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 200)
		credsEqualDefaults(t, res.Body, stsSvc)
		assumeRoleAliasIs(t, "noperms", config, stsSvc)
		assumeRolePolicyIs(t, defaultPolicy, stsSvc)
	})

	t.Run("should support custom labels", func(t *testing.T) {
		config := defaultConfig()
		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReqBase+"/"+dbRoleARNFriendlyName, config, stsSvc, containerSvc, ipWithAllLabels)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 200)
		credsEqualDefaults(t, res.Body, stsSvc)
		assumeRoleAliasIs(t, "db", config, stsSvc)
		assumeRolePolicyIs(t, defaultCustomPolicy, stsSvc)
	})

	t.Run("should support no selected defaults", func(t *testing.T) {
		config := defaultConfig()
		config.DefaultAlias = ""

		stsSvc := defaultStsSvcStub()
		containerSvc := defaultContainerSvcStub()

		res, _, err := stubRequest(defaultPathSpec, defaultPathReq, config, stsSvc, containerSvc, ipWithNoLabels)
		fatalOnErr(t, err)

		responseCodeIs(t, res, 404)
		bodyIsEmpty(t, res.Body)
		assumeRoleAliasIsEmpty(t, stsSvc)
		assumeRolePolicyIsNil(t, stsSvc)
	})
}

func credsEqualDefaults(t *testing.T, body *bytes.Buffer, stsSvc *assumeRoleStub) {
	var c metadataCredentials
	err := json.NewDecoder(body).Decode(&c)
	fatalOnErr(t, err)

	expectedCreds := defaultCreds()
	stringsEqual(t, [][2]string{
		[2]string{"Success", c.Code},
		[2]string{*expectedCreds.AccessKeyId, c.AccessKeyID},
		[2]string{"AWS-HMAC", c.Type},
		[2]string{*expectedCreds.SecretAccessKey, c.SecretAccessKey},
		[2]string{*expectedCreds.SessionToken, c.Token},
		[2]string{stsSvc.output.Credentials.Expiration.String(), c.Expiration.String()},
	})
}

func bodyIsEmpty(t *testing.T, body *bytes.Buffer) {
	bodyBytes, err := ioutil.ReadAll(body)
	fatalOnErr(t, err)
	if len(bodyBytes) != 0 {
		t.Fatalf("expected empty body, got [%s]", string(bodyBytes))
	}
}

func bodyIsNonEmpty(t *testing.T, body *bytes.Buffer) string {
	bodyBytes, err := ioutil.ReadAll(body)
	fatalOnErr(t, err)
	if len(bodyBytes) == 0 {
		t.Fatal("expected non-empty body")
	}
	return string(bodyBytes)
}

func responseCodeIs(t *testing.T, res *httptest.ResponseRecorder, expected int) {
	if res.Code != expected {
		t.Fatalf("expected HTTP code %d, got %d", expected, res.Code)
	}
}

func assumeRoleAliasIsEmpty(t *testing.T, stsSvc *assumeRoleStub) {
	if *stsSvc.input.RoleArn != "" {
		t.Fatalf("expected assume role to be empty, instead [%s]", *stsSvc.input.RoleArn)
	}
}

func assumeRoleAliasIs(t *testing.T, alias string, config Config, stsSvc *assumeRoleStub) {
	if *stsSvc.input.RoleArn != config.AliasToARN[alias] {
		t.Fatalf("expected assume role to be [%s] with alias [%s], instead [%s]", config.AliasToARN[alias], alias, *stsSvc.input.RoleArn)
	}
}

func assumeRolePolicyIs(t *testing.T, policy string, stsSvc *assumeRoleStub) {
	if *stsSvc.input.Policy != policy {
		t.Fatalf("expected policy [%s], got [%s]", policy, *stsSvc.input.Policy)
	}
}

func assumeRolePolicyIsNil(t *testing.T, stsSvc *assumeRoleStub) {
	if stsSvc.input.Policy != nil {
		t.Fatalf("expected no policy, got [%s]", *stsSvc.input.Policy)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"github.com/blackjack/syslog"
	"math/rand"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"os/user"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var config = &Configuration{
	Verbose:         false,
	ConfigFile:      "/etc/go-ssmtp.ini",
	Port:            25,
	Server:          "127.0.0.1",
	Postmaster:      "postmaster",
	ScanMessage:     false,
	Message_Subject: "(no subject)",
}

type Configuration struct {
	Verbose                           bool
	ConfigFile                        string
	Hostname                          string
	Server                            string
	Port                              int
	Postmaster                        string
	ScanMessage                       bool
	Authentication_User               string
	Authentication_Password           string
	Authentication_Identity           string
	Authentication_Mechanism          string
	Authentication_ForceStartTLS      bool
	Authentication_InsecureSkipVerify bool
	Message_To                        []string
	Message_From                      string
	Message_FromName                  string
	Message_Subject                   string
	Message_FromCronDaemon            bool
}

func generateMessageId() string {
	const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	bytes := make([]byte, 16)

	for i, r := 0, rand.New(rand.NewSource(time.Now().UnixNano())); i < len(bytes); i++ {
		bytes[i] = CHARS[r.Intn(len(CHARS))]
	}

	return string(bytes)
}

func (c *Configuration) ParseFile(file string) error {
	var matchSection = regexp.MustCompile(`^\[([^]]+)\]$`)
	var matchPair = regexp.MustCompile(`^([^#;=]+)=(.*)$`)

	f, err := os.Open(file)

	if err != nil {
		return err
	}

	defer f.Close()

	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)
	var n, section = 1, ""

	for s.Scan() {
		l := s.Text()

		if 0 == len(l) || ';' == l[0] {
			continue
		} else if parts := matchSection.FindStringSubmatch(l); parts != nil {
			section = parts[1]
		} else if parts := matchPair.FindStringSubmatch(l); parts != nil {
			k, v := parts[1], parts[2]

			if section != "" {
				k = section + "_" + k
			}

			if !c.Get(k).IsValid() {
				fmt.Fprintf(os.Stderr, "Warning: unknown configuration variable %s, line %d\n", k, n)
			} else if "string" == config.Get(k).Type().String() {
				c.Get(k).SetString(v)
			} else if "bool" == config.Get(k).Type().String() {
				c.Get(k).SetBool("1" == v)
			} else if "int" == config.Get(k).Type().String() {
				if i, err := strconv.ParseInt(v, 10, 64); err != nil {
					fmt.Fprintf(os.Stderr, "Warning: could not parse value `%s` for %s, line %d\n", v, k, n)
				} else {
					c.Get(k).SetInt(i)
				}
			} else {
				fmt.Fprintf(os.Stderr, "Warning: unsupported type %v for %s\n", config.Get(k).Type(), k)
			}
		} else {
			return fmt.Errorf("Failed to parse config, line %d: %s", n, l)
		}

		n++
	}

	return nil
}

func (c *Configuration) Get(k string) reflect.Value {
	r := reflect.ValueOf(c)
	return reflect.Indirect(r).FieldByName(k)
}

func compose() (*mail.Message, error) {
	// Make sure we can re-use Stdin even after being consumed by mail.ReadMessage
	b := bytes.Buffer{}
	b.ReadFrom(os.Stdin)
	msg := b.String()

	m, err := mail.ReadMessage(bytes.NewBufferString(msg))
	if err != nil {
		if config.ScanMessage {
			return nil, fmt.Errorf("ScanMessage: cannot parse message: %s", err)
		}

		// Assume there are no headers in the message
		m = &mail.Message{
			Header: mail.Header(textproto.MIMEHeader{}),
			Body:   bufio.NewReader(bytes.NewBufferString(msg)),
		}
	}

	// Make sure all required fields are set
	if 0 == len(m.Header["From"]) {
		m.Header["From"] = []string{(&mail.Address{config.Message_FromName, config.Message_From}).String()}
	} else if from, err := mail.ParseAddress(m.Header["From"][0]); config.ScanMessage && err == nil {
		// Parse and put in config; to be used by c.Mail
		config.Message_From = from.Address
	}

	if 0 == len(m.Header["To"]) {
		m.Header["To"] = config.Message_To
	}

	if 0 == len(m.Header["Date"]) {
		m.Header["Date"] = []string{time.Now().Format("Mon, 2 Jan 2006 15:04:05 -0700")}
	}

	if 0 == len(m.Header["Message-Id"]) {
		m.Header["Message-Id"] = []string{"<GOSSMTP." + generateMessageId() + "@" + config.Hostname + ">"}
	}

	if 0 == len(m.Header["Subject"]) {
		m.Header["Subject"] = []string{config.Message_Subject}
	}

	return m, nil
}

func connect() (*smtp.Client, error) {
	c, err := smtp.Dial(fmt.Sprintf("%s:%d", config.Server, config.Port))

	if err != nil {
		return nil, fmt.Errorf("while connecting to %s on port %d: %s", config.Server, config.Port, err)
	}

	if err := c.Hello(config.Hostname); err != nil {
		return nil, fmt.Errorf("while sending Hello `%s`: %s", config.Hostname, err)
	}

	if ok, _ := c.Extension("STARTTLS"); ok {
		if err = c.StartTLS(&tls.Config{ServerName: config.Server, InsecureSkipVerify: config.Authentication_InsecureSkipVerify}); err != nil {
			return nil, fmt.Errorf("while enabling StartTLS: %s", err)
		}
	} else if config.Authentication_ForceStartTLS {
		return nil, fmt.Errorf("server does not support StartTLS")
	}

	switch config.Authentication_Mechanism {
	case "CRAM-MD5":
		auth := smtp.CRAMMD5Auth(
			config.Authentication_User,
			config.Authentication_Password,
		)

		if ok, _ := c.Extension("AUTH"); ok {
			if err = c.Auth(auth); err != nil {
				return nil, fmt.Errorf("while authenticating: %s", err)
			} else if config.Verbose {
				return nil, fmt.Errorf("Info: using authentication: CRAM-MD5")
			}
		}

	case "PLAIN":
		auth := smtp.PlainAuth(
			config.Authentication_Identity,
			config.Authentication_User,
			config.Authentication_Password,
			config.Server,
		)

		if ok, _ := c.Extension("AUTH"); ok {
			if err = c.Auth(auth); err != nil {
				return nil, fmt.Errorf("while authenticating: %s", err)
			} else if config.Verbose {
				fmt.Println("Info: using authentication: PLAIN")
			}
		}

	default:
		if config.Verbose {
			fmt.Println("Info: not using authentication")
		}
	}

	return c, nil
}

func send(c *smtp.Client, m *mail.Message) error {
	if err := c.Mail(config.Message_From); err != nil {
		return fmt.Errorf("while setting From `%s`: %s", config.Message_From, err)
	}

	if config.ScanMessage {
		for _, i := range []string{"To", "Cc", "Bcc"} {
			if 0 == len(m.Header[i]) {
				continue
			}

			if l, err := m.Header.AddressList(i); err != nil {
				fmt.Fprintf(os.Stderr, "ScanMessage: Could not parse recipients in %s `%s`; %s", i, l, err)
			} else {
				for _, v := range l {
					config.Message_To = append(config.Message_To, v.Address)
				}
			}
		}

		if 0 == len(config.Message_To) {
			fmt.Fprintln(os.Stderr, "ScanMessage: No recipients found in message-body")
		}
	}

	for _, to := range config.Message_To {
		if err := c.Rcpt(to); err != nil {
			return fmt.Errorf("while setting Recipient `%s`: %s", to, err)
		}
	}

	w, err := c.Data()
	if err != nil {
		return fmt.Errorf("while setting Data: %s", err)
	}

	var s = ""
	for k, h := range m.Header {
		for _, v := range h {
			s += k + ": " + v + "\r\n"
		}
	}

	b := bytes.Buffer{}
	b.ReadFrom(m.Body)

	if _, err := w.Write([]byte(s + "\r\n" + b.String())); err != nil {
		return fmt.Errorf("while sending message: %s", err)
	}

	if err = w.Close(); err != nil {
		return fmt.Errorf("while closing message: %s", err)
	}

	if err = c.Quit(); err != nil {
		return fmt.Errorf("while closing connection: %s", err)
	}

	return nil
}

func init() {
	if h, err := os.Hostname(); err == nil {
		config.Hostname = h
	} else {
		config.Hostname = "localhost"
	}

	if u, err := user.Current(); err == nil {
		config.Message_From = u.Username + "@" + config.Hostname

		if u.Name != "" {
			config.Message_FromName = u.Name
		} else {
			config.Message_FromName = u.Username
		}
	}

	if -1 == strings.Index(config.Postmaster, "@") {
		config.Postmaster += "@" + config.Hostname
	}

	syslog.Openlog("go-ssmtp", syslog.LOG_PID, syslog.LOG_USER)

	// rewrite os.Args so we can parse -FFrom > -F From
	newArgs := []string{os.Args[0]}
	for _, arg := range os.Args[1:] {
		if len(arg) > 2 && arg[0] == '-' {
			newArgs = append(newArgs, arg[0:2], arg[2:])
		} else {
			newArgs = append(newArgs, arg)
		}
	}
	os.Args = newArgs

	var ignoreBool bool
	var ignoreString string
	flag.BoolVar(&ignoreBool, "i", false, "Ignore dots alone on lines - ignored")
	flag.StringVar(&ignoreString, "o", "", "Set option x to the specified - ignored")
	flag.BoolVar(&config.Verbose, "v", config.Verbose, "Enable verbose mode")
	flag.StringVar(&config.ConfigFile, "C", config.ConfigFile, "Use alternate configuration file")
	flag.StringVar(&config.Message_From, "f", config.Message_From, "Manually specify the sender-address of the email")
	flag.StringVar(&config.Message_FromName, "F", config.Message_FromName, "Manually specify the sender-name of the email")
	flag.StringVar(&config.Message_Subject, "S", config.Message_Subject, "Manually specify the subject of the email")
	flag.BoolVar(&config.ScanMessage, "t", config.ScanMessage, "Scan message for recipients")
}

func main() {
	flag.Parse()

	if config.Message_FromCronDaemon {
		config.Message_FromName = "CronDaemon"
	}

	if err := config.ParseFile(config.ConfigFile); err != nil {
		panic("error: parsing configuration: " + err.Error())
	}

	// Map all local users to Postmaster address
	config.Message_To = flag.Args()
	for i, to := range config.Message_To {
		if -1 == strings.Index(to, "@") {
			config.Message_To[i] = config.Postmaster
		}
	}

	if config.Verbose {
		fmt.Printf("%#v\n", *config)
	}

	if len(config.Message_To) == 0 && !config.ScanMessage {
		panic("error: no recipients supplied")
	}

	if m, err := compose(); err != nil {
		syslog.Errf("compose: %s", err)
		panic("compose: " + err.Error())
	} else if c, err := connect(); err != nil {
		syslog.Errf("connect: %s", err)
		panic("connect: " + err.Error())
	} else if err := send(c, m); err != nil {
		syslog.Errf("send: %s", err)
		panic("send: " + err.Error())
	} else {
		syslog.Syslogf(syslog.LOG_INFO, "[%s] Sent mail; subject \"%s\"; from %s; to %#v", m.Header["Message-Id"][0], m.Header["Subject"][0], config.Message_From, config.Message_To)
	}

	if config.Verbose {
		fmt.Println("Info: send successful")
	}
}
<commit_msg>tweak: composite literal uses unkeyed fields<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"github.com/blackjack/syslog"
	"math/rand"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"os/user"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var config = &Configuration{
	Verbose:         false,
	ConfigFile:      "/etc/go-ssmtp.ini",
	Port:            25,
	Server:          "127.0.0.1",
	Postmaster:      "postmaster",
	ScanMessage:     false,
	Message_Subject: "(no subject)",
}

type Configuration struct {
	Verbose                           bool
	ConfigFile                        string
	Hostname                          string
	Server                            string
	Port                              int
	Postmaster                        string
	ScanMessage                       bool
	Authentication_User               string
	Authentication_Password           string
	Authentication_Identity           string
	Authentication_Mechanism          string
	Authentication_ForceStartTLS      bool
	Authentication_InsecureSkipVerify bool
	Message_To                        []string
	Message_From                      string
	Message_FromName                  string
	Message_Subject                   string
	Message_FromCronDaemon            bool
}

func generateMessageId() string {
	const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	bytes := make([]byte, 16)

	for i, r := 0, rand.New(rand.NewSource(time.Now().UnixNano())); i < len(bytes); i++ {
		bytes[i] = CHARS[r.Intn(len(CHARS))]
	}

	return string(bytes)
}

func (c *Configuration) ParseFile(file string) error {
	var matchSection = regexp.MustCompile(`^\[([^]]+)\]$`)
	var matchPair = regexp.MustCompile(`^([^#;=]+)=(.*)$`)

	f, err := os.Open(file)

	if err != nil {
		return err
	}

	defer f.Close()

	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)
	var n, section = 1, ""

	for s.Scan() {
		l := s.Text()

		if 0 == len(l) || ';' == l[0] {
			continue
		} else if parts := matchSection.FindStringSubmatch(l); parts != nil {
			section = parts[1]
		} else if parts := matchPair.FindStringSubmatch(l); parts != nil {
			k, v := parts[1], parts[2]

			if section != "" {
				k = section + "_" + k
			}

			if !c.Get(k).IsValid() {
				fmt.Fprintf(os.Stderr, "Warning: unknown configuration variable %s, line %d\n", k, n)
			} else if "string" == config.Get(k).Type().String() {
				c.Get(k).SetString(v)
			} else if "bool" == config.Get(k).Type().String() {
				c.Get(k).SetBool("1" == v)
			} else if "int" == config.Get(k).Type().String() {
				if i, err := strconv.ParseInt(v, 10, 64); err != nil {
					fmt.Fprintf(os.Stderr, "Warning: could not parse value `%s` for %s, line %d\n", v, k, n)
				} else {
					c.Get(k).SetInt(i)
				}
			} else {
				fmt.Fprintf(os.Stderr, "Warning: unsupported type %v for %s\n", config.Get(k).Type(), k)
			}
		} else {
			return fmt.Errorf("Failed to parse config, line %d: %s", n, l)
		}

		n++
	}

	return nil
}

func (c *Configuration) Get(k string) reflect.Value {
	r := reflect.ValueOf(c)
	return reflect.Indirect(r).FieldByName(k)
}

func compose() (*mail.Message, error) {
	// Make sure we can re-use Stdin even after being consumed by mail.ReadMessage
	b := bytes.Buffer{}
	b.ReadFrom(os.Stdin)
	msg := b.String()

	m, err := mail.ReadMessage(bytes.NewBufferString(msg))
	if err != nil {
		if config.ScanMessage {
			return nil, fmt.Errorf("ScanMessage: cannot parse message: %s", err)
		}

		// Assume there are no headers in the message
		m = &mail.Message{
			Header: mail.Header(textproto.MIMEHeader{}),
			Body:   bufio.NewReader(bytes.NewBufferString(msg)),
		}
	}

	// Make sure all required fields are set
	if 0 == len(m.Header["From"]) {
		m.Header["From"] = []string{(&mail.Address{
			Name:    config.Message_FromName,
			Address: config.Message_From,
		}).String()}
	} else if from, err := mail.ParseAddress(m.Header["From"][0]); config.ScanMessage && err == nil {
		// Parse and put in config; to be used by c.Mail
		config.Message_From = from.Address
	}

	if 0 == len(m.Header["To"]) {
		m.Header["To"] = config.Message_To
	}

	if 0 == len(m.Header["Date"]) {
		m.Header["Date"] = []string{time.Now().Format("Mon, 2 Jan 2006 15:04:05 -0700")}
	}

	if 0 == len(m.Header["Message-Id"]) {
		m.Header["Message-Id"] = []string{"<GOSSMTP." + generateMessageId() + "@" + config.Hostname + ">"}
	}

	if 0 == len(m.Header["Subject"]) {
		m.Header["Subject"] = []string{config.Message_Subject}
	}

	return m, nil
}

func connect() (*smtp.Client, error) {
	c, err := smtp.Dial(fmt.Sprintf("%s:%d", config.Server, config.Port))

	if err != nil {
		return nil, fmt.Errorf("while connecting to %s on port %d: %s", config.Server, config.Port, err)
	}

	if err := c.Hello(config.Hostname); err != nil {
		return nil, fmt.Errorf("while sending Hello `%s`: %s", config.Hostname, err)
	}

	if ok, _ := c.Extension("STARTTLS"); ok {
		if err = c.StartTLS(&tls.Config{ServerName: config.Server, InsecureSkipVerify: config.Authentication_InsecureSkipVerify}); err != nil {
			return nil, fmt.Errorf("while enabling StartTLS: %s", err)
		}
	} else if config.Authentication_ForceStartTLS {
		return nil, fmt.Errorf("server does not support StartTLS")
	}

	switch config.Authentication_Mechanism {
	case "CRAM-MD5":
		auth := smtp.CRAMMD5Auth(
			config.Authentication_User,
			config.Authentication_Password,
		)

		if ok, _ := c.Extension("AUTH"); ok {
			if err = c.Auth(auth); err != nil {
				return nil, fmt.Errorf("while authenticating: %s", err)
			} else if config.Verbose {
				return nil, fmt.Errorf("Info: using authentication: CRAM-MD5")
			}
		}

	case "PLAIN":
		auth := smtp.PlainAuth(
			config.Authentication_Identity,
			config.Authentication_User,
			config.Authentication_Password,
			config.Server,
		)

		if ok, _ := c.Extension("AUTH"); ok {
			if err = c.Auth(auth); err != nil {
				return nil, fmt.Errorf("while authenticating: %s", err)
			} else if config.Verbose {
				fmt.Println("Info: using authentication: PLAIN")
			}
		}

	default:
		if config.Verbose {
			fmt.Println("Info: not using authentication")
		}
	}

	return c, nil
}

func send(c *smtp.Client, m *mail.Message) error {
	if err := c.Mail(config.Message_From); err != nil {
		return fmt.Errorf("while setting From `%s`: %s", config.Message_From, err)
	}

	if config.ScanMessage {
		for _, i := range []string{"To", "Cc", "Bcc"} {
			if 0 == len(m.Header[i]) {
				continue
			}

			if l, err := m.Header.AddressList(i); err != nil {
				fmt.Fprintf(os.Stderr, "ScanMessage: Could not parse recipients in %s `%s`; %s", i, l, err)
			} else {
				for _, v := range l {
					config.Message_To = append(config.Message_To, v.Address)
				}
			}
		}

		if 0 == len(config.Message_To) {
			fmt.Fprintln(os.Stderr, "ScanMessage: No recipients found in message-body")
		}
	}

	for _, to := range config.Message_To {
		if err := c.Rcpt(to); err != nil {
			return fmt.Errorf("while setting Recipient `%s`: %s", to, err)
		}
	}

	w, err := c.Data()
	if err != nil {
		return fmt.Errorf("while setting Data: %s", err)
	}

	var s = ""
	for k, h := range m.Header {
		for _, v := range h {
			s += k + ": " + v + "\r\n"
		}
	}

	b := bytes.Buffer{}
	b.ReadFrom(m.Body)

	if _, err := w.Write([]byte(s + "\r\n" + b.String())); err != nil {
		return fmt.Errorf("while sending message: %s", err)
	}

	if err = w.Close(); err != nil {
		return fmt.Errorf("while closing message: %s", err)
	}

	if err = c.Quit(); err != nil {
		return fmt.Errorf("while closing connection: %s", err)
	}

	return nil
}

func init() {
	if h, err := os.Hostname(); err == nil {
		config.Hostname = h
	} else {
		config.Hostname = "localhost"
	}

	if u, err := user.Current(); err == nil {
		config.Message_From = u.Username + "@" + config.Hostname

		if u.Name != "" {
			config.Message_FromName = u.Name
		} else {
			config.Message_FromName = u.Username
		}
	}

	if -1 == strings.Index(config.Postmaster, "@") {
		config.Postmaster += "@" + config.Hostname
	}

	syslog.Openlog("go-ssmtp", syslog.LOG_PID, syslog.LOG_USER)

	// rewrite os.Args so we can parse -FFrom > -F From
	newArgs := []string{os.Args[0]}
	for _, arg := range os.Args[1:] {
		if len(arg) > 2 && arg[0] == '-' {
			newArgs = append(newArgs, arg[0:2], arg[2:])
		} else {
			newArgs = append(newArgs, arg)
		}
	}
	os.Args = newArgs

	var ignoreBool bool
	var ignoreString string
	flag.BoolVar(&ignoreBool, "i", false, "Ignore dots alone on lines - ignored")
	flag.StringVar(&ignoreString, "o", "", "Set option x to the specified - ignored")
	flag.BoolVar(&config.Verbose, "v", config.Verbose, "Enable verbose mode")
	flag.StringVar(&config.ConfigFile, "C", config.ConfigFile, "Use alternate configuration file")
	flag.StringVar(&config.Message_From, "f", config.Message_From, "Manually specify the sender-address of the email")
	flag.StringVar(&config.Message_FromName, "F", config.Message_FromName, "Manually specify the sender-name of the email")
	flag.StringVar(&config.Message_Subject, "S", config.Message_Subject, "Manually specify the subject of the email")
	flag.BoolVar(&config.ScanMessage, "t", config.ScanMessage, "Scan message for recipients")
}

func main() {
	flag.Parse()

	if config.Message_FromCronDaemon {
		config.Message_FromName = "CronDaemon"
	}

	if err := config.ParseFile(config.ConfigFile); err != nil {
		panic("error: parsing configuration: " + err.Error())
	}

	// Map all local users to Postmaster address
	config.Message_To = flag.Args()
	for i, to := range config.Message_To {
		if -1 == strings.Index(to, "@") {
			config.Message_To[i] = config.Postmaster
		}
	}

	if config.Verbose {
		fmt.Printf("%#v\n", *config)
	}

	if len(config.Message_To) == 0 && !config.ScanMessage {
		panic("error: no recipients supplied")
	}

	if m, err := compose(); err != nil {
		syslog.Errf("compose: %s", err)
		panic("compose: " + err.Error())
	} else if c, err := connect(); err != nil {
		syslog.Errf("connect: %s", err)
		panic("connect: " + err.Error())
	} else if err := send(c, m); err != nil {
		syslog.Errf("send: %s", err)
		panic("send: " + err.Error())
	} else {
		syslog.Syslogf(syslog.LOG_INFO, "[%s] Sent mail; subject \"%s\"; from %s; to %#v", m.Header["Message-Id"][0], m.Header["Subject"][0], config.Message_From, config.Message_To)
	}

	if config.Verbose {
		fmt.Println("Info: send successful")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
)

var src, dst string

func init() {
	flag.StringVar(&src, "src", "", "source file: -src filepath")
	flag.StringVar(&dst, "dst", "", "dst file: -dst filepath")
	flag.Parse()
}
func main() {

	var funcPattern string = `^func\s(\w+)\(.*?\)`
	var methodPattern string = `^func\s\(\w.*?\s(.*?)\)\s(\w+)`
	var typePattern string = `^type\s(.*?)\s\w+\s{`

	fmt.Println(src, dst)
	funcCompile, _ := regexp.Compile(funcPattern)
	methodCompile, _ := regexp.Compile(methodPattern)
	typeCompile, _ := regexp.Compile(typePattern)
	srcfile, err := os.Open(src)
	check(err)
	defer srcfile.Close()

	dstfile, err := os.Create(dst)
	check(err)
	defer srcfile.Close()
	scanner := bufio.NewScanner(srcfile)

	mflag := false

	for scanner.Scan() {

		line := scanner.Text()
		if mflag {
			fmt.Fprintln(dstfile, line)
			if line == "}" {
				fmt.Fprint(dstfile, "```\n\n")
				mflag = false
				continue
			}
		}

		if ok := funcCompile.MatchString(line); ok {
			funcName := funcCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", funcName[0])
			withgo(dstfile, line)
			fmt.Fprint(dstfile, "```\n\n")
		}

		if ok := methodCompile.MatchString(line); ok {
			methodName := methodCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", methodName[0])
			withgo(dstfile, line)
			fmt.Fprint(dstfile, "```\n\n")

		}

		if ok := typeCompile.MatchString(line); ok {
			typeName := typeCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", strings.Trim(typeName[0], "{"))
			withgo(dstfile, line)
			mflag = true
		}

	}
	fmt.Println("completed!")

}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func withgo(f *os.File, line string) {
	fmt.Fprint(f, "```go\n")
	fmt.Fprintln(f, line)
}
<commit_msg>修改godoc2md.go，改为管道输入<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strings"
)

var src string
var dst string

func init() {
	flag.StringVar(&dst, "dst", "", "dst file: -dst filepath")
	flag.Parse()
}
func main() {

	var funcPattern string = `^func\s(\w+)\(.*?\)`
	var methodPattern string = `^func\s\(\w.*?\s(.*?)\)\s(\w+)`
	var typePattern string = `^type\s(.*?)\s\w+\s{`

	fmt.Println(src, dst)
	funcCompile, _ := regexp.Compile(funcPattern)
	methodCompile, _ := regexp.Compile(methodPattern)
	typeCompile, _ := regexp.Compile(typePattern)

	srcfile := os.Stdin

	dstfile, err := os.Create(dst)
	check(err)
	defer dstfile.Close()

	scanner := bufio.NewScanner(srcfile)
	defer srcfile.Close()
	mflag := false

	for scanner.Scan() {

		line := scanner.Text()
		if mflag {
			fmt.Fprintln(dstfile, line)
			if line == "}" {
				fmt.Fprint(dstfile, "```\n\n")
				mflag = false
				continue
			}
		}

		if ok := funcCompile.MatchString(line); ok {
			funcName := funcCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", funcName[0])
			withgo(dstfile, line)
			fmt.Fprint(dstfile, "```\n\n")
		}

		if ok := methodCompile.MatchString(line); ok {
			methodName := methodCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", methodName[0])
			withgo(dstfile, line)
			fmt.Fprint(dstfile, "```\n\n")
		}

		if ok := typeCompile.MatchString(line); ok {
			typeName := typeCompile.FindStringSubmatch(line)
			fmt.Fprintf(dstfile, "###%s\n", strings.Trim(typeName[0], "{"))
			withgo(dstfile, line)
			mflag = true
		}

	}
	fmt.Println("completed!")

}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func withgo(f *os.File, line string) {
	fmt.Fprint(f, "```go\n")
	fmt.Fprintln(f, line)
}
<|endoftext|>