<commit_before>package vault

import (
	"fmt"
	"time"

	"github.com/armon/go-metrics"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/golang-lru"
	"github.com/hashicorp/vault/logical"
)

const (
	// policySubPath is the sub-path used for the policy store
	// view. This is nested under the system view.
	policySubPath = "policy/"

	// policyCacheSize is the number of policies that are kept cached
	policyCacheSize = 1024
)

// PolicyStore is used to provide durable storage of policy, and to
// manage ACLs associated with them.
type PolicyStore struct {
	view *BarrierView
	lru  *lru.TwoQueueCache
}

// PolicyEntry is used to store a policy by name
type PolicyEntry struct {
	Version int
	Raw     string
}

// NewPolicyStore creates a new PolicyStore that is backed
// using a given view. It used used to durable store and manage named policy.
func NewPolicyStore(view *BarrierView) *PolicyStore {
	cache, _ := lru.New2Q(policyCacheSize)
	p := &PolicyStore{
		view: view,
		lru:  cache,
	}
	return p
}

// setupPolicyStore is used to initialize the policy store
// when the vault is being unsealed.
func (c *Core) setupPolicyStore() error {
	// Create a sub-view
	view := c.systemBarrierView.SubView(policySubPath)

	// Create the policy store
	c.policyStore = NewPolicyStore(view)

	// Ensure that the default policy exists, and if not, create it
	policy, err := c.policyStore.GetPolicy("default")
	if err != nil {
		return errwrap.Wrapf("error fetching default policy from store: {{err}}", err)
	}
	if policy == nil {
		err := c.policyStore.createDefaultPolicy()
		if err != nil {
			return err
		}
	}
	return nil
}

// teardownPolicyStore is used to reverse setupPolicyStore
// when the vault is being sealed.
func (c *Core) teardownPolicyStore() error {
	c.policyStore = nil
	return nil
}

// SetPolicy is used to create or update the given policy
func (ps *PolicyStore) SetPolicy(p *Policy) error {
	defer metrics.MeasureSince([]string{"policy", "set_policy"}, time.Now())
	if p.Name == "root" {
		return fmt.Errorf("cannot update root policy")
	}
	if p.Name == "" {
		return fmt.Errorf("policy name missing")
	}

	// Create the entry
	entry, err := logical.StorageEntryJSON(p.Name, &PolicyEntry{
		Version: 2,
		Raw:     p.Raw,
	})
	if err != nil {
		return fmt.Errorf("failed to create entry: %v", err)
	}
	if err := ps.view.Put(entry); err != nil {
		return fmt.Errorf("failed to persist policy: %v", err)
	}

	// Update the LRU cache
	ps.lru.Add(p.Name, p)
	return nil
}

// GetPolicy is used to fetch the named policy
func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
	defer metrics.MeasureSince([]string{"policy", "get_policy"}, time.Now())
	// Check for cached policy
	if raw, ok := ps.lru.Get(name); ok {
		return raw.(*Policy), nil
	}

	// Special case the root policy
	if name == "root" {
		p := &Policy{Name: "root"}
		ps.lru.Add(p.Name, p)
		return p, nil
	}

	// Load the policy in
	out, err := ps.view.Get(name)
	if err != nil {
		return nil, fmt.Errorf("failed to read policy: %v", err)
	}
	if out == nil {
		return nil, nil
	}

	// In Vault 0.1.X we stored the raw policy, but in
	// Vault 0.2 we switch to the PolicyEntry
	policyEntry := new(PolicyEntry)
	var policy *Policy
	if err := out.DecodeJSON(policyEntry); err == nil {
		// Parse normally
		p, err := Parse(policyEntry.Raw)
		if err != nil {
			return nil, fmt.Errorf("failed to parse policy: %v", err)
		}
		p.Name = name
		policy = p

	} else {
		// On error, attempt to use V1 parsing
		p, err := Parse(string(out.Value))
		if err != nil {
			return nil, fmt.Errorf("failed to parse policy: %v", err)
		}
		p.Name = name

		// V1 used implicit glob, we need to do a fix-up
		for _, pp := range p.Paths {
			pp.Glob = true
		}
		policy = p
	}

	// Update the LRU cache
	ps.lru.Add(name, policy)
	return policy, nil
}

// ListPolicies is used to list the available policies
func (ps *PolicyStore) ListPolicies() ([]string, error) {
	defer metrics.MeasureSince([]string{"policy", "list_policies"}, time.Now())
	// Scan the view, since the policy names are the same as the
	// key names.
	return CollectKeys(ps.view)
}

// DeletePolicy is used to delete the named policy
func (ps *PolicyStore) DeletePolicy(name string) error {
	defer metrics.MeasureSince([]string{"policy", "delete_policy"}, time.Now())
	if name == "root" {
		return fmt.Errorf("cannot delete root policy")
	}
	if name == "default" {
		return fmt.Errorf("cannot delete default policy")
	}
	if err := ps.view.Delete(name); err != nil {
		return fmt.Errorf("failed to delete policy: %v", err)
	}

	// Clear the cache
	ps.lru.Remove(name)
	return nil
}

// ACL is used to return an ACL which is built using the
// named policies.
func (ps *PolicyStore) ACL(names ...string) (*ACL, error) {
	// Fetch the policies
	var policy []*Policy
	for _, name := range names {
		p, err := ps.GetPolicy(name)
		if err != nil {
			return nil, fmt.Errorf("failed to get policy '%s': %v", name, err)
		}
		policy = append(policy, p)
	}

	// Construct the ACL
	acl, err := NewACL(policy)
	if err != nil {
		return nil, fmt.Errorf("failed to construct ACL: %v", err)
	}
	return acl, nil
}

func (ps *PolicyStore) createDefaultPolicy() error {
	policy, err := Parse(`
path "auth/token/lookup-self" {
    capabilities = ["read"]
}

path "auth/token/renew-self" {
    capabilities = ["update"]
}

path "auth/token/revoke-self" {
    capabilities = ["update"]
}

path "cubbyhole/*" {
    capabilities = ["create", "read", "update", "delete", "list"]
}
`)
	if err != nil {
		return errwrap.Wrapf("error parsing default policy: {{err}}", err)
	}

	if policy == nil {
		return fmt.Errorf("parsing default policy resulted in nil policy")
	}

	policy.Name = "default"
	return ps.SetPolicy(policy)
}
<commit_msg>Add listing of cubbyhole's root to the default policy.<commit_after>package vault

import (
	"fmt"
	"time"

	"github.com/armon/go-metrics"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/golang-lru"
	"github.com/hashicorp/vault/logical"
)

const (
	// policySubPath is the sub-path used for the policy store
	// view. This is nested under the system view.
	policySubPath = "policy/"

	// policyCacheSize is the number of policies that are kept cached
	policyCacheSize = 1024
)

// PolicyStore is used to provide durable storage of policy, and to
// manage ACLs associated with them.
type PolicyStore struct {
	view *BarrierView
	lru  *lru.TwoQueueCache
}

// PolicyEntry is used to store a policy by name
type PolicyEntry struct {
	Version int
	Raw     string
}

// NewPolicyStore creates a new PolicyStore that is backed
// using a given view. It used used to durable store and manage named policy.
func NewPolicyStore(view *BarrierView) *PolicyStore {
	cache, _ := lru.New2Q(policyCacheSize)
	p := &PolicyStore{
		view: view,
		lru:  cache,
	}
	return p
}

// setupPolicyStore is used to initialize the policy store
// when the vault is being unsealed.
func (c *Core) setupPolicyStore() error {
	// Create a sub-view
	view := c.systemBarrierView.SubView(policySubPath)

	// Create the policy store
	c.policyStore = NewPolicyStore(view)

	// Ensure that the default policy exists, and if not, create it
	policy, err := c.policyStore.GetPolicy("default")
	if err != nil {
		return errwrap.Wrapf("error fetching default policy from store: {{err}}", err)
	}
	if policy == nil {
		err := c.policyStore.createDefaultPolicy()
		if err != nil {
			return err
		}
	}
	return nil
}

// teardownPolicyStore is used to reverse setupPolicyStore
// when the vault is being sealed.
func (c *Core) teardownPolicyStore() error {
	c.policyStore = nil
	return nil
}

// SetPolicy is used to create or update the given policy
func (ps *PolicyStore) SetPolicy(p *Policy) error {
	defer metrics.MeasureSince([]string{"policy", "set_policy"}, time.Now())
	if p.Name == "root" {
		return fmt.Errorf("cannot update root policy")
	}
	if p.Name == "" {
		return fmt.Errorf("policy name missing")
	}

	// Create the entry
	entry, err := logical.StorageEntryJSON(p.Name, &PolicyEntry{
		Version: 2,
		Raw:     p.Raw,
	})
	if err != nil {
		return fmt.Errorf("failed to create entry: %v", err)
	}
	if err := ps.view.Put(entry); err != nil {
		return fmt.Errorf("failed to persist policy: %v", err)
	}

	// Update the LRU cache
	ps.lru.Add(p.Name, p)
	return nil
}

// GetPolicy is used to fetch the named policy
func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
	defer metrics.MeasureSince([]string{"policy", "get_policy"}, time.Now())
	// Check for cached policy
	if raw, ok := ps.lru.Get(name); ok {
		return raw.(*Policy), nil
	}

	// Special case the root policy
	if name == "root" {
		p := &Policy{Name: "root"}
		ps.lru.Add(p.Name, p)
		return p, nil
	}

	// Load the policy in
	out, err := ps.view.Get(name)
	if err != nil {
		return nil, fmt.Errorf("failed to read policy: %v", err)
	}
	if out == nil {
		return nil, nil
	}

	// In Vault 0.1.X we stored the raw policy, but in
	// Vault 0.2 we switch to the PolicyEntry
	policyEntry := new(PolicyEntry)
	var policy *Policy
	if err := out.DecodeJSON(policyEntry); err == nil {
		// Parse normally
		p, err := Parse(policyEntry.Raw)
		if err != nil {
			return nil, fmt.Errorf("failed to parse policy: %v", err)
		}
		p.Name = name
		policy = p

	} else {
		// On error, attempt to use V1 parsing
		p, err := Parse(string(out.Value))
		if err != nil {
			return nil, fmt.Errorf("failed to parse policy: %v", err)
		}
		p.Name = name

		// V1 used implicit glob, we need to do a fix-up
		for _, pp := range p.Paths {
			pp.Glob = true
		}
		policy = p
	}

	// Update the LRU cache
	ps.lru.Add(name, policy)
	return policy, nil
}

// ListPolicies is used to list the available policies
func (ps *PolicyStore) ListPolicies() ([]string, error) {
	defer metrics.MeasureSince([]string{"policy", "list_policies"}, time.Now())
	// Scan the view, since the policy names are the same as the
	// key names.
	return CollectKeys(ps.view)
}

// DeletePolicy is used to delete the named policy
func (ps *PolicyStore) DeletePolicy(name string) error {
	defer metrics.MeasureSince([]string{"policy", "delete_policy"}, time.Now())
	if name == "root" {
		return fmt.Errorf("cannot delete root policy")
	}
	if name == "default" {
		return fmt.Errorf("cannot delete default policy")
	}
	if err := ps.view.Delete(name); err != nil {
		return fmt.Errorf("failed to delete policy: %v", err)
	}

	// Clear the cache
	ps.lru.Remove(name)
	return nil
}

// ACL is used to return an ACL which is built using the
// named policies.
func (ps *PolicyStore) ACL(names ...string) (*ACL, error) {
	// Fetch the policies
	var policy []*Policy
	for _, name := range names {
		p, err := ps.GetPolicy(name)
		if err != nil {
			return nil, fmt.Errorf("failed to get policy '%s': %v", name, err)
		}
		policy = append(policy, p)
	}

	// Construct the ACL
	acl, err := NewACL(policy)
	if err != nil {
		return nil, fmt.Errorf("failed to construct ACL: %v", err)
	}
	return acl, nil
}

func (ps *PolicyStore) createDefaultPolicy() error {
	policy, err := Parse(`
path "auth/token/lookup-self" {
    capabilities = ["read"]
}

path "auth/token/renew-self" {
    capabilities = ["update"]
}

path "auth/token/revoke-self" {
    capabilities = ["update"]
}

path "cubbyhole/*" {
    capabilities = ["create", "read", "update", "delete", "list"]
}

path "cubbyhole" {
    capabilities = ["list"]
}
`)
	if err != nil {
		return errwrap.Wrapf("error parsing default policy: {{err}}", err)
	}

	if policy == nil {
		return fmt.Errorf("parsing default policy resulted in nil policy")
	}

	policy.Name = "default"
	return ps.SetPolicy(policy)
}
<|endoftext|>


<commit_before>package notifiers

import (
	"fmt"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const DefaultDingdingMsgType = "link"
const DingdingOptionsTemplate = `

DingDing settings

        Url
        <input type="text" required class="gf-form-input max-width-26" ng-model="ctrl.model.settings.url" placeholder="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"></input>

        MessageType
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + DefaultDingdingMsgType + `'"></select>

`

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:            "dingding",
		Name:            "DingDing",
		Description:     "Sends HTTP POST request to DingDing",
		Factory:         NewDingDingNotifier,
		OptionsTemplate: DingdingOptionsTemplate,
	})

}

func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType)

	return &DingDingNotifier{
		NotifierBase: NewNotifierBase(model),
		MsgType:      msgType,
		Url:          url,
		log:          log.New("alerting.notifier.dingding"),
	}, nil
}

type DingDingNotifier struct {
	NotifierBase
	MsgType string
	Url     string
	log     log.Logger
}

func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Sending dingding")

	messageUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name)
		messageUrl = ""
	}
	this.log.Info("messageUrl:" + messageUrl)

	message := evalContext.Rule.Message
	picUrl := evalContext.ImagePublicUrl
	title := evalContext.GetNotificationTitle()
	if message == "" {
		message = title
	}

	for i, match := range evalContext.EvalMatches {
		message += fmt.Sprintf("\\n%2d. %s value %s", i+1, match.Metric, match.Value)
	}

	var bodyStr string
	if this.MsgType == "actionCard" {
		bodyStr = `{
			"msgtype": "actionCard",
			"actionCard": {
				"text": "` + message + `",
				"title": "` + title + `",
				"singleTitle": "More",
				"singleURL": "` + messageUrl + `"
			}
		}`
	} else {
		bodyStr = `{
			"msgtype": "link",
			"link": {
				"text": "` + message + `",
				"title": "` + title + `",
				"picUrl": "` + picUrl + `",
				"messageUrl": "` + messageUrl + `"
			}
		}`
	}

	bodyJSON, err := simplejson.NewJson([]byte(bodyStr))

	if err != nil {
		this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
	}

	body, err := bodyJSON.MarshalJSON()
	if err != nil {
		return err
	}

	cmd := &m.SendWebhookSync{
		Url:  this.Url,
		Body: string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send DingDing", "error", err, "dingding", this.Name)
		return err
	}

	return nil
}
<commit_msg>Optimize the Dingding match values format<commit_after>package notifiers

import (
	"fmt"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const DefaultDingdingMsgType = "link"
const DingdingOptionsTemplate = `

DingDing settings

        Url
        <input type="text" required class="gf-form-input max-width-26" ng-model="ctrl.model.settings.url" placeholder="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"></input>

        MessageType
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + DefaultDingdingMsgType + `'"></select>

`

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:            "dingding",
		Name:            "DingDing",
		Description:     "Sends HTTP POST request to DingDing",
		Factory:         NewDingDingNotifier,
		OptionsTemplate: DingdingOptionsTemplate,
	})

}

func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType)

	return &DingDingNotifier{
		NotifierBase: NewNotifierBase(model),
		MsgType:      msgType,
		Url:          url,
		log:          log.New("alerting.notifier.dingding"),
	}, nil
}

type DingDingNotifier struct {
	NotifierBase
	MsgType string
	Url     string
	log     log.Logger
}

func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Sending dingding")

	messageUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name)
		messageUrl = ""
	}
	this.log.Info("messageUrl:" + messageUrl)

	message := evalContext.Rule.Message
	picUrl := evalContext.ImagePublicUrl
	title := evalContext.GetNotificationTitle()
	if message == "" {
		message = title
	}

	for i, match := range evalContext.EvalMatches {
		message += fmt.Sprintf("\\n%2d. %s: %s", i+1, match.Metric, match.Value)
	}

	var bodyStr string
	if this.MsgType == "actionCard" {
		bodyStr = `{
			"msgtype": "actionCard",
			"actionCard": {
				"text": "` + message + `",
				"title": "` + title + `",
				"singleTitle": "More",
				"singleURL": "` + messageUrl + `"
			}
		}`
	} else {
		bodyStr = `{
			"msgtype": "link",
			"link": {
				"text": "` + message + `",
				"title": "` + title + `",
				"picUrl": "` + picUrl + `",
				"messageUrl": "` + messageUrl + `"
			}
		}`
	}

	bodyJSON, err := simplejson.NewJson([]byte(bodyStr))

	if err != nil {
		this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
	}

	body, err := bodyJSON.MarshalJSON()
	if err != nil {
		return err
	}

	cmd := &m.SendWebhookSync{
		Url:  this.Url,
		Body: string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send DingDing", "error", err, "dingding", this.Name)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package notifiers

import (
	"fmt"
	"net/url"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const DefaultDingdingMsgType = "link"
const DingdingOptionsTemplate = `

DingDing settings

        Url
        <input type="text" required class="gf-form-input max-width-70" ng-model="ctrl.model.settings.url" placeholder="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"></input>

        MessageType
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + DefaultDingdingMsgType + `'"></select>

        OpenInBrowser
        <gf-form-switch class="gf-form" checked="ctrl.model.settings.openInBrowser"></gf-form-switch>
        <info-popover mode="right-normal">Open the message url in browser instead of inside of Dingding</info-popover>

`

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:            "dingding",
		Name:            "DingDing",
		Description:     "Sends HTTP POST request to DingDing",
		Factory:         NewDingDingNotifier,
		OptionsTemplate: DingdingOptionsTemplate,
	})

}

func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType)
	openInBrowser := model.Settings.Get("openInBrowser").MustBool(true)

	return &DingDingNotifier{
		NotifierBase:  NewNotifierBase(model),
		OpenInBrowser: openInBrowser,
		MsgType:       msgType,
		Url:           url,
		log:           log.New("alerting.notifier.dingding"),
	}, nil
}

type DingDingNotifier struct {
	NotifierBase
	MsgType       string
	OpenInBrowser bool //Set whether the message url will open outside of Dingding
	Url           string
	log           log.Logger
}

func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Sending dingding")

	messageUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name)
		messageUrl = ""
	}

	if this.OpenInBrowser {
		q := url.Values{
			"pc_slide": {"false"},
			"url":      {messageUrl},
		}

		// Use special link to auto open the message url outside of Dingding
		// Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9
		messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode()
	}

	this.log.Info("messageUrl:" + messageUrl)

	message := evalContext.Rule.Message
	picUrl := evalContext.ImagePublicUrl
	title := evalContext.GetNotificationTitle()
	if message == "" {
		message = title
	}

	for i, match := range evalContext.EvalMatches {
		message += fmt.Sprintf("\\n%2d. %s: %s", i+1, match.Metric, match.Value)
	}

	var bodyStr string
	if this.MsgType == "actionCard" {
		// Embed the pic into the markdown directly because actionCard doesn't have a picUrl field
		if picUrl != "" {
			message = "![](" + picUrl + ")\\n\\n" + message
		}

		bodyStr = `{
			"msgtype": "actionCard",
			"actionCard": {
				"text": "` + message + `",
				"title": "` + title + `",
				"singleTitle": "More",
				"singleURL": "` + messageUrl + `"
			}
		}`
	} else {
		bodyStr = `{
			"msgtype": "link",
			"link": {
				"text": "` + message + `",
				"title": "` + title + `",
				"picUrl": "` + picUrl + `",
				"messageUrl": "` + messageUrl + `"
			}
		}`
	}

	bodyJSON, err := simplejson.NewJson([]byte(bodyStr))

	if err != nil {
		this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
	}

	body, err := bodyJSON.MarshalJSON()
	if err != nil {
		return err
	}

	cmd := &m.SendWebhookSync{
		Url:  this.Url,
		Body: string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send DingDing", "error", err, "dingding", this.Name)
		return err
	}

	return nil
}
<commit_msg>Remove option used to control within browser<commit_after>package notifiers

import (
	"fmt"
	"net/url"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const DefaultDingdingMsgType = "link"
const DingdingOptionsTemplate = `

DingDing settings

        Url
        <input type="text" required class="gf-form-input max-width-70" ng-model="ctrl.model.settings.url" placeholder="https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx"></input>

        MessageType
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.msgType" ng-options="s for s in ['link','actionCard']" ng-init="ctrl.model.settings.msgType=ctrl.model.settings.msgType || '` + DefaultDingdingMsgType + `'"></select>

`

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:            "dingding",
		Name:            "DingDing",
		Description:     "Sends HTTP POST request to DingDing",
		Factory:         NewDingDingNotifier,
		OptionsTemplate: DingdingOptionsTemplate,
	})

}

func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	msgType := model.Settings.Get("msgType").MustString(DefaultDingdingMsgType)

	return &DingDingNotifier{
		NotifierBase: NewNotifierBase(model),
		MsgType:      msgType,
		Url:          url,
		log:          log.New("alerting.notifier.dingding"),
	}, nil
}

type DingDingNotifier struct {
	NotifierBase
	MsgType string
	Url     string
	log     log.Logger
}

func (this *DingDingNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Sending dingding")

	messageUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed to get messageUrl", "error", err, "dingding", this.Name)
		messageUrl = ""
	}

	q := url.Values{
		"pc_slide": {"false"},
		"url":      {messageUrl},
	}

	// Use special link to auto open the message url outside of Dingding
	// Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9
	messageUrl = "dingtalk://dingtalkclient/page/link?" + q.Encode()

	this.log.Info("messageUrl:" + messageUrl)

	message := evalContext.Rule.Message
	picUrl := evalContext.ImagePublicUrl
	title := evalContext.GetNotificationTitle()
	if message == "" {
		message = title
	}

	for i, match := range evalContext.EvalMatches {
		message += fmt.Sprintf("\\n%2d. %s: %s", i+1, match.Metric, match.Value)
	}

	var bodyStr string
	if this.MsgType == "actionCard" {
		// Embed the pic into the markdown directly because actionCard doesn't have a picUrl field
		if picUrl != "" {
			message = "![](" + picUrl + ")\\n\\n" + message
		}

		bodyStr = `{
			"msgtype": "actionCard",
			"actionCard": {
				"text": "` + message + `",
				"title": "` + title + `",
				"singleTitle": "More",
				"singleURL": "` + messageUrl + `"
			}
		}`
	} else {
		bodyStr = `{
			"msgtype": "link",
			"link": {
				"text": "` + message + `",
				"title": "` + title + `",
				"picUrl": "` + picUrl + `",
				"messageUrl": "` + messageUrl + `"
			}
		}`
	}

	bodyJSON, err := simplejson.NewJson([]byte(bodyStr))

	if err != nil {
		this.log.Error("Failed to create Json data", "error", err, "dingding", this.Name)
	}

	body, err := bodyJSON.MarshalJSON()
	if err != nil {
		return err
	}

	cmd := &m.SendWebhookSync{
		Url:  this.Url,
		Body: string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send DingDing", "error", err, "dingding", this.Name)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package sysregistriesv2

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/BurntSushi/toml"
	"github.com/containers/image/types"
)

// systemRegistriesConfPath is the path to the system-wide registry
// configuration file and is used to add/subtract potential registries for
// obtaining images.  You can override this at build time with
// -ldflags '-X github.com/containers/image/sysregistries.systemRegistriesConfPath=$your_path'
var systemRegistriesConfPath = builtinRegistriesConfPath

// builtinRegistriesConfPath is the path to the registry configuration file.
// DO NOT change this, instead see systemRegistriesConfPath above.
const builtinRegistriesConfPath = "/etc/containers/registries.conf"

// Mirror represents a mirror. Mirrors can be used as pull-through caches for
// registries.
type Mirror struct {
	// The mirror's URL.
	URL string `toml:"url"`
	// If true, certs verification will be skipped and HTTP (non-TLS)
	// connections will be allowed.
	Insecure bool `toml:"insecure"`
}

// Registry represents a registry.
type Registry struct {
	// Serializable registry URL.
	URL string `toml:"url"`
	// The registry's mirrors.
	Mirrors []Mirror `toml:"mirror"`
	// If true, pulling from the registry will be blocked.
	Blocked bool `toml:"blocked"`
	// If true, certs verification will be skipped and HTTP (non-TLS)
	// connections will be allowed.
	Insecure bool `toml:"insecure"`
	// If true, the registry can be used when pulling an unqualified image.
	Search bool `toml:"unqualified-search"`
	// Prefix is used for matching images, and to translate one namespace to
	// another.  If `Prefix="example.com/bar"`, `URL="example.com/foo/bar"`
	// and we pull from "example.com/bar/myimage:latest", the image will
	// effectively be pulled from "example.com/foo/bar/myimage:latest".
	// If no Prefix is specified, it defaults to the specified URL.
	Prefix string `toml:"prefix"`
}

// backwards compatability to sysregistries v1
type v1TOMLregistries struct {
	Registries []string `toml:"registries"`
}

// tomlConfig is the data type used to unmarshal the toml config.
type tomlConfig struct {
	Registries []Registry `toml:"registry"`
	// backwards compatability to sysregistries v1
	V1Registries struct {
		Search   v1TOMLregistries `toml:"search"`
		Insecure v1TOMLregistries `toml:"insecure"`
		Block    v1TOMLregistries `toml:"block"`
	} `toml:"registries"`
}

// InvalidRegistries represents an invalid registry configurations.  An example
// is when "registry.com" is defined multiple times in the configuration but
// with conflicting security settings.
type InvalidRegistries struct {
	s string
}

// Error returns the error string.
func (e *InvalidRegistries) Error() string {
	return e.s
}

// parseURL parses the input string, performs some sanity checks and returns
// the sanitized input string.  An error is returned in case parsing fails or
// or if URI scheme or user is set.
func parseURL(input string) (string, error) {
	trimmed := strings.TrimRight(input, "/")

	if trimmed == "" {
		return "", &InvalidRegistries{s: "invalid URL: cannot be empty"}
	}

	uri, err := url.Parse(trimmed)
	if err != nil {
		return "", &InvalidRegistries{s: fmt.Sprintf("invalid URL '%s': %v", input, err)}
	}

	// Check if a URI SCheme is set.
	// Note that URLs that do not start with a slash after the scheme are
	// interpreted as `scheme:opaque[?query][#fragment]`.
	if uri.Scheme != "" && uri.Opaque == "" {
		msg := fmt.Sprintf("invalid URL '%s': URI schemes are not supported", input)
		return "", &InvalidRegistries{s: msg}
	}

	uri, err = url.Parse("http://" + trimmed)
	if err != nil {
		msg := fmt.Sprintf("invalid URL '%s': sanitized URL did not parse: %v", input, err)
		return "", &InvalidRegistries{s: msg}
	}

	if uri.User != nil {
		msg := fmt.Sprintf("invalid URL '%s': user/password are not supported", trimmed)
		return "", &InvalidRegistries{s: msg}
	}

	return trimmed, nil
}

// getV1Registries transforms v1 registries in the config into an array of v2
// registries of type Registry.
func getV1Registries(config *tomlConfig) ([]Registry, error) {
	regMap := make(map[string]*Registry)

	getRegistry := func(url string) (*Registry, error) { // Note: _pointer_ to a long-lived object
		var err error
		url, err = parseURL(url)
		if err != nil {
			return nil, err
		}
		reg, exists := regMap[url]
		if !exists {
			reg = &Registry{
				URL:     url,
				Mirrors: []Mirror{},
				Prefix:  url,
			}
			regMap[url] = reg
		}
		return reg, nil
	}

	for _, search := range config.V1Registries.Search.Registries {
		reg, err := getRegistry(search)
		if err != nil {
			return nil, err
		}
		reg.Search = true
	}
	for _, blocked := range config.V1Registries.Block.Registries {
		reg, err := getRegistry(blocked)
		if err != nil {
			return nil, err
		}
		reg.Blocked = true
	}
	for _, insecure := range config.V1Registries.Insecure.Registries {
		reg, err := getRegistry(insecure)
		if err != nil {
			return nil, err
		}
		reg.Insecure = true
	}

	registries := []Registry{}
	for _, reg := range regMap {
		registries = append(registries, *reg)
	}
	return registries, nil
}

// postProcessRegistries checks the consistency of all registries (e.g., set
// the Prefix to URL if not set) and applies conflict checks.  It returns an
// array of cleaned registries and error in case of conflicts.
func postProcessRegistries(regs []Registry) ([]Registry, error) {
	var registries []Registry
	regMap := make(map[string][]Registry)

	for _, reg := range regs {
		var err error

		// make sure URL and Prefix are valid
		reg.URL, err = parseURL(reg.URL)
		if err != nil {
			return nil, err
		}

		if reg.Prefix == "" {
			reg.Prefix = reg.URL
		} else {
			reg.Prefix, err = parseURL(reg.Prefix)
			if err != nil {
				return nil, err
			}
		}

		// make sure mirrors are valid
		for _, mir := range reg.Mirrors {
			mir.URL, err = parseURL(mir.URL)
			if err != nil {
				return nil, err
			}
		}
		registries = append(registries, reg)
		regMap[reg.URL] = append(regMap[reg.URL], reg)
	}

	// Given a registry can be mentioned multiple times (e.g., to have
	// multiple prefixes backed by different mirrors), we need to make sure
	// there are no conflicts among them.
	//
	// Note: we need to iterate over the registries array to ensure a
	// deterministic behavior which is not guaranteed by maps.
	for _, reg := range registries {
		others, _ := regMap[reg.URL]
		for _, other := range others {
			if reg.Insecure != other.Insecure {
				msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.URL)

				return nil, &InvalidRegistries{s: msg}
			}
			if reg.Blocked != other.Blocked {
				msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.URL)
				return nil, &InvalidRegistries{s: msg}
			}
		}
	}

	return registries, nil
}

// GetRegistries loads and returns the registries specified in the config.
func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
	config, err := loadRegistryConf(ctx)
	if err != nil {
		return nil, err
	}

	registries := config.Registries

	// backwards compatibility for v1 configs
	v1Registries, err := getV1Registries(config)
	if err != nil {
		return nil, err
	}
	if len(v1Registries) > 0 {
		if len(registries) > 0 {
			return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
		}
		registries = v1Registries
	}

	return postProcessRegistries(registries)
}

// FindUnqualifiedSearchRegistries returns all registries that are configured
// for unqualified image search (i.e., with Registry.Search == true).
func FindUnqualifiedSearchRegistries(registries []Registry) []Registry {
	unqualified := []Registry{}
	for _, reg := range registries {
		if reg.Search {
			unqualified = append(unqualified, reg)
		}
	}
	return unqualified
}

// FindRegistry returns the Registry with the longest prefix for ref.  If no
// Registry prefixes the image, nil is returned.
func FindRegistry(ref string, registries []Registry) *Registry {
	reg := Registry{}
	prefixLen := 0
	for _, r := range registries {
		if strings.HasPrefix(ref, r.Prefix) {
			length := len(r.Prefix)
			if length > prefixLen {
				reg = r
				prefixLen = length
			}
		}
	}
	if prefixLen != 0 {
		return &reg
	}
	return nil
}

// Reads the global registry file from the filesystem. Returns a byte array.
func readRegistryConf(ctx *types.SystemContext) ([]byte, error) {
	dirPath := systemRegistriesConfPath
	if ctx != nil {
		if ctx.SystemRegistriesConfPath != "" {
			dirPath = ctx.SystemRegistriesConfPath
		} else if ctx.RootForImplicitAbsolutePaths != "" {
			dirPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
		}
	}
	configBytes, err := ioutil.ReadFile(dirPath)
	return configBytes, err
}

// Used in unittests to parse custom configs without a types.SystemContext.
var readConf = readRegistryConf

// Loads the registry configuration file from the filesystem and then unmarshals
// it.  Returns the unmarshalled object.
func loadRegistryConf(ctx *types.SystemContext) (*tomlConfig, error) {
	config := &tomlConfig{}

	configBytes, err := readConf(ctx)
	if err != nil {
		return nil, err
	}

	err = toml.Unmarshal(configBytes, &config)
	return config, err
}
<commit_msg>Document the subtleties of using url.Parse for validating registry prefixes.<commit_after>package sysregistriesv2

import (
	"fmt"
	"io/ioutil"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/BurntSushi/toml"
	"github.com/containers/image/types"
)

// systemRegistriesConfPath is the path to the system-wide registry
// configuration file and is used to add/subtract potential registries for
// obtaining images.  You can override this at build time with
// -ldflags '-X github.com/containers/image/sysregistries.systemRegistriesConfPath=$your_path'
var systemRegistriesConfPath = builtinRegistriesConfPath

// builtinRegistriesConfPath is the path to the registry configuration file.
// DO NOT change this, instead see systemRegistriesConfPath above.
const builtinRegistriesConfPath = "/etc/containers/registries.conf"

// Mirror represents a mirror. Mirrors can be used as pull-through caches for
// registries.
type Mirror struct {
	// The mirror's URL.
	URL string `toml:"url"`
	// If true, certs verification will be skipped and HTTP (non-TLS)
	// connections will be allowed.
	Insecure bool `toml:"insecure"`
}

// Registry represents a registry.
type Registry struct {
	// Serializable registry URL.
	URL string `toml:"url"`
	// The registry's mirrors.
	Mirrors []Mirror `toml:"mirror"`
	// If true, pulling from the registry will be blocked.
	Blocked bool `toml:"blocked"`
	// If true, certs verification will be skipped and HTTP (non-TLS)
	// connections will be allowed.
	Insecure bool `toml:"insecure"`
	// If true, the registry can be used when pulling an unqualified image.
	Search bool `toml:"unqualified-search"`
	// Prefix is used for matching images, and to translate one namespace to
	// another.  If `Prefix="example.com/bar"`, `URL="example.com/foo/bar"`
	// and we pull from "example.com/bar/myimage:latest", the image will
	// effectively be pulled from "example.com/foo/bar/myimage:latest".
	// If no Prefix is specified, it defaults to the specified URL.
	Prefix string `toml:"prefix"`
}

// backwards compatability to sysregistries v1
type v1TOMLregistries struct {
	Registries []string `toml:"registries"`
}

// tomlConfig is the data type used to unmarshal the toml config.
type tomlConfig struct {
	Registries []Registry `toml:"registry"`
	// backwards compatability to sysregistries v1
	V1Registries struct {
		Search   v1TOMLregistries `toml:"search"`
		Insecure v1TOMLregistries `toml:"insecure"`
		Block    v1TOMLregistries `toml:"block"`
	} `toml:"registries"`
}

// InvalidRegistries represents an invalid registry configurations.  An example
// is when "registry.com" is defined multiple times in the configuration but
// with conflicting security settings.
type InvalidRegistries struct {
	s string
}

// Error returns the error string.
func (e *InvalidRegistries) Error() string {
	return e.s
}

// parseURL parses the input string, performs some sanity checks and returns
// the sanitized input string.  An error is returned in case parsing fails or
// or if URI scheme or user is set.
func parseURL(input string) (string, error) {
	trimmed := strings.TrimRight(input, "/")

	if trimmed == "" {
		return "", &InvalidRegistries{s: "invalid URL: cannot be empty"}
	}

	// Ultimately, we expect input of the form example.com[/namespace/…], a prefix
	// of a fully-expended reference (containers/image/docker/Reference.String()).
	// c/image/docker/Reference does not currently provide such a parser.
	// So, we use url.Parse("http://"+trimmed) below to ~verify the format, possibly
	// letting some invalid input in, trading that off for a simpler parser.
	//
	// url.Parse("http://"+trimmed) is, sadly, too permissive, notably for
	// trimmed == "http://example.com/…", url.Parse("http://http://example.com/…")
	// is accepted and parsed as
	// {Scheme: "http", Host: "http:", Path: "//example.com/…"}.
	//
	// So, first we do an explicit check for an unwanted scheme prefix:

	// This will parse trimmed=="http://example.com/…" with Scheme: "http".  Perhaps surprisingly,
	// it also succeeds for the input we want to accept, in different ways:
	// "example.com" -> {Scheme:"", Opaque:"", Path:"example.com"}
	// "example.com/repo" -> {Scheme:"", Opaque:"", Path:"example.com/repo"}
	// "example.com:5000" -> {Scheme:"example.com", Opaque:"5000"}
	// "example.com:5000/repo" -> {Scheme:"example.com", Opaque:"5000/repo"}
	uri, err := url.Parse(trimmed)
	if err != nil {
		return "", &InvalidRegistries{s: fmt.Sprintf("invalid URL '%s': %v", input, err)}
	}

	// Check if a URI Scheme is set.
	// Note that URLs that do not start with a slash after the scheme are
	// interpreted as `scheme:opaque[?query][#fragment]`; see above for examples.
	if uri.Scheme != "" && uri.Opaque == "" {
		msg := fmt.Sprintf("invalid URL '%s': URI schemes are not supported", input)
		return "", &InvalidRegistries{s: msg}
	}

	uri, err = url.Parse("http://" + trimmed)
	if err != nil {
		msg := fmt.Sprintf("invalid URL '%s': sanitized URL did not parse: %v", input, err)
		return "", &InvalidRegistries{s: msg}
	}

	if uri.User != nil {
		msg := fmt.Sprintf("invalid URL '%s': user/password are not supported", trimmed)
		return "", &InvalidRegistries{s: msg}
	}

	return trimmed, nil
}

// getV1Registries transforms v1 registries in the config into an array of v2
// registries of type Registry.
func getV1Registries(config *tomlConfig) ([]Registry, error) {
	regMap := make(map[string]*Registry)

	getRegistry := func(url string) (*Registry, error) { // Note: _pointer_ to a long-lived object
		var err error
		url, err = parseURL(url)
		if err != nil {
			return nil, err
		}
		reg, exists := regMap[url]
		if !exists {
			reg = &Registry{
				URL:     url,
				Mirrors: []Mirror{},
				Prefix:  url,
			}
			regMap[url] = reg
		}
		return reg, nil
	}

	for _, search := range config.V1Registries.Search.Registries {
		reg, err := getRegistry(search)
		if err != nil {
			return nil, err
		}
		reg.Search = true
	}
	for _, blocked := range config.V1Registries.Block.Registries {
		reg, err := getRegistry(blocked)
		if err != nil {
			return nil, err
		}
		reg.Blocked = true
	}
	for _, insecure := range config.V1Registries.Insecure.Registries {
		reg, err := getRegistry(insecure)
		if err != nil {
			return nil, err
		}
		reg.Insecure = true
	}

	registries := []Registry{}
	for _, reg := range regMap {
		registries = append(registries, *reg)
	}
	return registries, nil
}

// postProcessRegistries checks the consistency of all registries (e.g., set
// the Prefix to URL if not set) and applies conflict checks.  It returns an
// array of cleaned registries and error in case of conflicts.
func postProcessRegistries(regs []Registry) ([]Registry, error) {
	var registries []Registry
	regMap := make(map[string][]Registry)

	for _, reg := range regs {
		var err error

		// make sure URL and Prefix are valid
		reg.URL, err = parseURL(reg.URL)
		if err != nil {
			return nil, err
		}

		if reg.Prefix == "" {
			reg.Prefix = reg.URL
		} else {
			reg.Prefix, err = parseURL(reg.Prefix)
			if err != nil {
				return nil, err
			}
		}

		// make sure mirrors are valid
		for _, mir := range reg.Mirrors {
			mir.URL, err = parseURL(mir.URL)
			if err != nil {
				return nil, err
			}
		}
		registries = append(registries, reg)
		regMap[reg.URL] = append(regMap[reg.URL], reg)
	}

	// Given a registry can be mentioned multiple times (e.g., to have
	// multiple prefixes backed by different mirrors), we need to make sure
	// there are no conflicts among them.
	//
	// Note: we need to iterate over the registries array to ensure a
	// deterministic behavior which is not guaranteed by maps.
	for _, reg := range registries {
		others, _ := regMap[reg.URL]
		for _, other := range others {
			if reg.Insecure != other.Insecure {
				msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.URL)

				return nil, &InvalidRegistries{s: msg}
			}
			if reg.Blocked != other.Blocked {
				msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.URL)
				return nil, &InvalidRegistries{s: msg}
			}
		}
	}

	return registries, nil
}

// GetRegistries loads and returns the registries specified in the config.
func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
	config, err := loadRegistryConf(ctx)
	if err != nil {
		return nil, err
	}

	registries := config.Registries

	// backwards compatibility for v1 configs
	v1Registries, err := getV1Registries(config)
	if err != nil {
		return nil, err
	}
	if len(v1Registries) > 0 {
		if len(registries) > 0 {
			return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
		}
		registries = v1Registries
	}

	return postProcessRegistries(registries)
}

// FindUnqualifiedSearchRegistries returns all registries that are configured
// for unqualified image search (i.e., with Registry.Search == true).
func FindUnqualifiedSearchRegistries(registries []Registry) []Registry {
	unqualified := []Registry{}
	for _, reg := range registries {
		if reg.Search {
			unqualified = append(unqualified, reg)
		}
	}
	return unqualified
}

// FindRegistry returns the Registry with the longest prefix for ref.  If no
// Registry prefixes the image, nil is returned.
func FindRegistry(ref string, registries []Registry) *Registry {
	reg := Registry{}
	prefixLen := 0
	for _, r := range registries {
		if strings.HasPrefix(ref, r.Prefix) {
			length := len(r.Prefix)
			if length > prefixLen {
				reg = r
				prefixLen = length
			}
		}
	}
	if prefixLen != 0 {
		return &reg
	}
	return nil
}

// Reads the global registry file from the filesystem. Returns a byte array.
func readRegistryConf(ctx *types.SystemContext) ([]byte, error) {
	dirPath := systemRegistriesConfPath
	if ctx != nil {
		if ctx.SystemRegistriesConfPath != "" {
			dirPath = ctx.SystemRegistriesConfPath
		} else if ctx.RootForImplicitAbsolutePaths != "" {
			dirPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
		}
	}
	configBytes, err := ioutil.ReadFile(dirPath)
	return configBytes, err
}

// Used in unittests to parse custom configs without a types.SystemContext.
var readConf = readRegistryConf

// Loads the registry configuration file from the filesystem and then unmarshals
// it.  Returns the unmarshalled object.
func loadRegistryConf(ctx *types.SystemContext) (*tomlConfig, error) {
	config := &tomlConfig{}

	configBytes, err := readConf(ctx)
	if err != nil {
		return nil, err
	}

	err = toml.Unmarshal(configBytes, &config)
	return config, err
}
<|endoftext|>


<commit_before>/*
   Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file 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.
*/

resp, err := svc.UpdateService((&ecs.UpdateServiceInput{}).
	SetService("myService").
	SetDeploymentConfiguration((&ecs.DeploymentConfiguration{}).
		SetMinimumHealthyPrecent(80),
	),
)
<commit_msg>Fixed typo<commit_after>/*
   Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file 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.
*/

resp, err := svc.UpdateService((&ecs.UpdateServiceInput{}).
	SetService("myService").
	SetDeploymentConfiguration((&ecs.DeploymentConfiguration{}).
		SetMinimumHealthyPercent(80),
	),
)
<|endoftext|>


<commit_before>package volume

import "testing"

// Set up fake volume
var fakeVol = Volume{
	Target:          "/foo",
	BackupDir:       "/back",
	Mount:           "/mnt",
	FullIfOlderThan: "3W",
	RemoveOlderThan: "1Y",
}

// TestNewVolume checks the creation of a new volume
func TestNewVolume(t *testing.T) {
	if fakeVol.Target != "/foo" {
		t.Fatalf("Volume target is wrong. Expected /foo, got %v", fakeVol.Target)
	}

	if fakeVol.BackupDir != "/back" {
		t.Fatalf("Volume backup dir is wrong. Expected /back, got %v", fakeVol.BackupDir)
	}

	if fakeVol.Mount != "/mnt" {
		t.Fatalf("Volume mount dir is wrong. Expected /mnt, got %v", fakeVol.Mount)
	}

	if fakeVol.FullIfOlderThan != "3W" {
		t.Fatalf("Volume FullIfOlderThan is wrong. Expected 3W, got %v", fakeVol.FullIfOlderThan)
	}

	if fakeVol.RemoveOlderThan != "1Y" {
		t.Fatalf("Volume RemoveOlderThan is wrong. Expected 1Y, got %v", fakeVol.RemoveOlderThan)
	}
}
<commit_msg>Fix volume tests<commit_after>package volume

import "testing"

// Set up fake volume
var fakeVol = Volume{
	Target:    "/foo",
	BackupDir: "/back",
	Mount:     "/mnt",
	Config: &Config{
		FullIfOlderThan: "3W",
		RemoveOlderThan: "1Y",
	},
}

// TestNewVolume checks the creation of a new volume
func TestNewVolume(t *testing.T) {
	if fakeVol.Target != "/foo" {
		t.Fatalf("Volume target is wrong. Expected /foo, got %v", fakeVol.Target)
	}

	if fakeVol.BackupDir != "/back" {
		t.Fatalf("Volume backup dir is wrong. Expected /back, got %v", fakeVol.BackupDir)
	}

	if fakeVol.Mount != "/mnt" {
		t.Fatalf("Volume mount dir is wrong. Expected /mnt, got %v", fakeVol.Mount)
	}

	if fakeVol.Config.FullIfOlderThan != "3W" {
		t.Fatalf("Volume FullIfOlderThan is wrong. Expected 3W, got %v", fakeVol.Config.FullIfOlderThan)
	}

	if fakeVol.Config.RemoveOlderThan != "1Y" {
		t.Fatalf("Volume RemoveOlderThan is wrong. Expected 1Y, got %v", fakeVol.Config.RemoveOlderThan)
	}
}
<|endoftext|>


<commit_before>package migrations

import (
	"fmt"
	"os"

	"github.com/antonve/logger-api/config"
	"github.com/antonve/logger-api/models"

	"github.com/DavidHuie/gomigrate"
)

func getMigrator() (*gomigrate.Migrator, error) {
	// @TODO: change how migration path is handled
	appPath := os.Getenv("APP_PATH")
	if appPath == "" {
		appPath = fmt.Sprintf("%s/src/github.com/antonve/logger-api", os.Getenv("GOPATH"))
	}

	return gomigrate.NewMigrator(models.GetSQLDatabase(), gomigrate.Postgres{}, fmt.Sprintf("%s/%s", appPath, config.GetConfig().MigrationsPath))
}

// Migrate migrates the database
func Migrate() error {
	migrator, err := getMigrator()

	if err != nil {
		return err
	}

	err = migrator.Migrate()

	return err
}

// Destroy the current environment's database
func Destroy() error {
	if config.GetConfig().Environment == config.Environments["prod"] {
		return fmt.Errorf("Cannot destroy production.")
	}

	db := models.GetSQLConnection()
	defer db.Close()

	// Drop database
	_, err := db.Exec("DROP DATABASE IF EXISTS " + config.GetConfig().Database)
	if err != nil {
		return err
	}

	return nil
}

// Create a new database
func Create() error {
	db := models.GetSQLConnection()
	defer db.Close()

	// Create database
	_, err := db.Exec("CREATE DATABASE " + config.GetConfig().Database)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Log which environment is being migrated<commit_after>package migrations

import (
	"fmt"
	"log"
	"os"

	"github.com/antonve/logger-api/config"
	"github.com/antonve/logger-api/models"

	"github.com/DavidHuie/gomigrate"
)

func getMigrator() (*gomigrate.Migrator, error) {
	// @TODO: change how migration path is handled
	appPath := os.Getenv("APP_PATH")
	if appPath == "" {
		appPath = fmt.Sprintf("%s/src/github.com/antonve/logger-api", os.Getenv("GOPATH"))
	}

	return gomigrate.NewMigrator(models.GetSQLDatabase(), gomigrate.Postgres{}, fmt.Sprintf("%s/%s", appPath, config.GetConfig().MigrationsPath))
}

// Migrate migrates the database
func Migrate() error {
	log.Printf("Migrating in environment: %s", config.GetConfig().Environment)
	migrator, err := getMigrator()

	if err != nil {
		return err
	}

	err = migrator.Migrate()

	return err
}

// Destroy the current environment's database
func Destroy() error {
	if config.GetConfig().Environment == config.Environments["prod"] {
		return fmt.Errorf("Cannot destroy production.")
	}

	db := models.GetSQLConnection()
	defer db.Close()

	// Drop database
	_, err := db.Exec("DROP DATABASE IF EXISTS " + config.GetConfig().Database)
	if err != nil {
		return err
	}

	return nil
}

// Create a new database
func Create() error {
	db := models.GetSQLConnection()
	defer db.Close()

	// Create database
	_, err := db.Exec("CREATE DATABASE " + config.GetConfig().Database)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"errors"
	"fmt"
	"regexp"

	"github.com/FactomProject/btcutil/base58"
	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/goleveldb/leveldb"
)

var (
	ErrFeeTooHigh    = errors.New("wallet: Overpaying Fee")
	ErrFeeTooLow     = errors.New("wallet: Insufficient Fee")
	ErrNoSuchAddress = errors.New("wallet: No such address")
	ErrTXExists      = errors.New("wallet: Transaction name already exists")
	ErrTXNotExists   = errors.New("wallet: Transaction name was not found")
	ErrTXNoInputs    = errors.New("wallet: Transaction has no inputs")
	ErrTXInvalidName = errors.New("wallet: Transaction name is not valid")
)

func (w *Wallet) NewTransaction(name string) error {
	if _, exist := w.transactions[name]; exist {
		return ErrTXExists
	}

	// check that the transaction name is valid
	if name == "" {
		return ErrTXInvalidName
	}
	if len(name) > 32 {
		return ErrTXInvalidName
	}
	if match, err := regexp.MatchString("[^a-zA-Z0-9_-]", name); err != nil {
		return err
	} else if match {
		return ErrTXInvalidName
	}

	t := new(factoid.Transaction)
	t.SetTimestamp(primitives.NewTimestampNow())
	w.transactions[name] = t
	return nil
}

func (w *Wallet) DeleteTransaction(name string) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	delete(w.transactions, name)
	return nil
}

func (w *Wallet) AddInput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	a, err := w.GetFCTAddress(address)
	if err == leveldb.ErrNotFound {
		return ErrNoSuchAddress
	} else if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	// First look if this is really an update
	for _, input := range trans.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			input.SetAmount(amount)
			return nil
		}
	}

	// Add our new input
	trans.AddInput(adr, amount)
	trans.AddRCD(factoid.NewRCD_1(a.PubBytes()))

	return nil
}

func (w *Wallet) AddOutput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	trans.AddOutput(adr, amount)

	return nil
}

func (w *Wallet) AddECOutput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	trans.AddECOutput(adr, amount)

	return nil
}

func (w *Wallet) AddFee(name, address string, rate uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	{
		ins, err := trans.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := trans.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := trans.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	transfee, err := trans.CalculateFee(rate)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	for _, input := range trans.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			amt, err := factoid.ValidateAmounts(input.GetAmount(), transfee)
			if err != nil {
				return err
			}
			input.SetAmount(amt)
			return nil
		}
	}
	return fmt.Errorf("%s is not an input to the transaction.", address)
}

func (w *Wallet) SubFee(name, address string, rate uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	{
		ins, err := trans.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := trans.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := trans.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	transfee, err := trans.CalculateFee(rate)
	if err != nil {
		return err
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	for _, output := range trans.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(output.GetAmount() - transfee)
			return nil
		}
	}
	return fmt.Errorf("%s is not an output to the transaction.", address)
}

func (w *Wallet) SignTransaction(name string) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if err := checkFee(trans); err != nil {
		return err
	}

	data, err := trans.MarshalBinarySig()
	if err != nil {
		return err
	}

	rcds := trans.GetRCDs()
	if len(rcds) == 0 {
		return ErrTXNoInputs
	}
	for i, rcd := range rcds {
		a, err := rcd.GetAddress()
		if err != nil {
			return err
		}

		f, err := w.GetFCTAddress(primitives.ConvertFctAddressToUserStr(a))
		if err != nil {
			return err
		}
		sig := factoid.NewSingleSignatureBlock(f.SecBytes(), data)
		trans.SetSignatureBlock(i, sig)
	}

	return nil
}

func (w *Wallet) GetTransactions() map[string]*factoid.Transaction {
	return w.transactions
}

func (w *Wallet) ComposeTransaction(name string) (*factom.JSON2Request, error) {
	if _, exists := w.transactions[name]; !exists {
		return nil, ErrTXNotExists
	}
	trans := w.transactions[name]

	type txreq struct {
		Transaction string `json:"transaction"`
	}

	param := new(txreq)
	if p, err := trans.MarshalBinary(); err != nil {
		return nil, err
	} else {
		param.Transaction = hex.EncodeToString(p)
	}

	req := factom.NewJSON2Request("factoid-submit", apiCounter(), param)

	return req, nil
}

func checkFee(t *factoid.Transaction) error {
	ins, err := t.TotalInputs()
	if err != nil {
		return err
	}
	outs, err := t.TotalOutputs()
	if err != nil {
		return err
	}
	ecs, err := t.TotalECs()
	if err != nil {
		return err
	}

	// fee is the fee that will be paid
	fee := int64(ins) - int64(outs) - int64(ecs)
	fmt.Println("DEBUG: fee is", fee)

	if fee <= 0 {
		return ErrFeeTooLow
	}

	rate, err := factom.GetRate()
	if err != nil {
		return err
	}

	// cfee is the fee calculated for the transaction
	var cfee int64
	if c, err := t.CalculateFee(rate); err != nil {
		return err
	} else if c == 0 {
		return errors.New("wallet: Could not calculate fee")
	} else {
		cfee = int64(c)
		fmt.Println("DEBUG: cfee is", cfee)
	}

	// fee is too low
	if fee < cfee {
		return ErrFeeTooLow
	}

	// fee is too high (over 10x cfee)
	if fee >= cfee*10 {
		return ErrFeeTooHigh
	}

	return nil
}
<commit_msg>removed debug messages<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"errors"
	"fmt"
	"regexp"

	"github.com/FactomProject/btcutil/base58"
	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/goleveldb/leveldb"
)

var (
	ErrFeeTooHigh    = errors.New("wallet: Overpaying Fee")
	ErrFeeTooLow     = errors.New("wallet: Insufficient Fee")
	ErrNoSuchAddress = errors.New("wallet: No such address")
	ErrTXExists      = errors.New("wallet: Transaction name already exists")
	ErrTXNotExists   = errors.New("wallet: Transaction name was not found")
	ErrTXNoInputs    = errors.New("wallet: Transaction has no inputs")
	ErrTXInvalidName = errors.New("wallet: Transaction name is not valid")
)

func (w *Wallet) NewTransaction(name string) error {
	if _, exist := w.transactions[name]; exist {
		return ErrTXExists
	}

	// check that the transaction name is valid
	if name == "" {
		return ErrTXInvalidName
	}
	if len(name) > 32 {
		return ErrTXInvalidName
	}
	if match, err := regexp.MatchString("[^a-zA-Z0-9_-]", name); err != nil {
		return err
	} else if match {
		return ErrTXInvalidName
	}

	t := new(factoid.Transaction)
	t.SetTimestamp(primitives.NewTimestampNow())
	w.transactions[name] = t
	return nil
}

func (w *Wallet) DeleteTransaction(name string) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	delete(w.transactions, name)
	return nil
}

func (w *Wallet) AddInput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	a, err := w.GetFCTAddress(address)
	if err == leveldb.ErrNotFound {
		return ErrNoSuchAddress
	} else if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	// First look if this is really an update
	for _, input := range trans.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			input.SetAmount(amount)
			return nil
		}
	}

	// Add our new input
	trans.AddInput(adr, amount)
	trans.AddRCD(factoid.NewRCD_1(a.PubBytes()))

	return nil
}

func (w *Wallet) AddOutput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	trans.AddOutput(adr, amount)

	return nil
}

func (w *Wallet) AddECOutput(name, address string, amount uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	trans.AddECOutput(adr, amount)

	return nil
}

func (w *Wallet) AddFee(name, address string, rate uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	{
		ins, err := trans.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := trans.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := trans.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	transfee, err := trans.CalculateFee(rate)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	for _, input := range trans.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			amt, err := factoid.ValidateAmounts(input.GetAmount(), transfee)
			if err != nil {
				return err
			}
			input.SetAmount(amt)
			return nil
		}
	}
	return fmt.Errorf("%s is not an input to the transaction.", address)
}

func (w *Wallet) SubFee(name, address string, rate uint64) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	{
		ins, err := trans.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := trans.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := trans.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	transfee, err := trans.CalculateFee(rate)
	if err != nil {
		return err
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	for _, output := range trans.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(output.GetAmount() - transfee)
			return nil
		}
	}
	return fmt.Errorf("%s is not an output to the transaction.", address)
}

func (w *Wallet) SignTransaction(name string) error {
	if _, exists := w.transactions[name]; !exists {
		return ErrTXNotExists
	}
	trans := w.transactions[name]

	if err := checkFee(trans); err != nil {
		return err
	}

	data, err := trans.MarshalBinarySig()
	if err != nil {
		return err
	}

	rcds := trans.GetRCDs()
	if len(rcds) == 0 {
		return ErrTXNoInputs
	}
	for i, rcd := range rcds {
		a, err := rcd.GetAddress()
		if err != nil {
			return err
		}

		f, err := w.GetFCTAddress(primitives.ConvertFctAddressToUserStr(a))
		if err != nil {
			return err
		}
		sig := factoid.NewSingleSignatureBlock(f.SecBytes(), data)
		trans.SetSignatureBlock(i, sig)
	}

	return nil
}

func (w *Wallet) GetTransactions() map[string]*factoid.Transaction {
	return w.transactions
}

func (w *Wallet) ComposeTransaction(name string) (*factom.JSON2Request, error) {
	if _, exists := w.transactions[name]; !exists {
		return nil, ErrTXNotExists
	}
	trans := w.transactions[name]

	type txreq struct {
		Transaction string `json:"transaction"`
	}

	param := new(txreq)
	if p, err := trans.MarshalBinary(); err != nil {
		return nil, err
	} else {
		param.Transaction = hex.EncodeToString(p)
	}

	req := factom.NewJSON2Request("factoid-submit", apiCounter(), param)

	return req, nil
}

func checkFee(t *factoid.Transaction) error {
	ins, err := t.TotalInputs()
	if err != nil {
		return err
	}
	outs, err := t.TotalOutputs()
	if err != nil {
		return err
	}
	ecs, err := t.TotalECs()
	if err != nil {
		return err
	}

	// fee is the fee that will be paid
	fee := int64(ins) - int64(outs) - int64(ecs)

	if fee <= 0 {
		return ErrFeeTooLow
	}

	rate, err := factom.GetRate()
	if err != nil {
		return err
	}

	// cfee is the fee calculated for the transaction
	var cfee int64
	if c, err := t.CalculateFee(rate); err != nil {
		return err
	} else if c == 0 {
		return errors.New("wallet: Could not calculate fee")
	} else {
		cfee = int64(c)
	}

	// fee is too low
	if fee < cfee {
		return ErrFeeTooLow
	}

	// fee is too high (over 10x cfee)
	if fee >= cfee*10 {
		return ErrFeeTooHigh
	}

	return nil
}
<|endoftext|>


<commit_before>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequest1Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Latitude)
	test.Equals(t, -123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Longitude)

	test.Equals(t, "1234 Random Road, Anytown, CA 12345, United States", req.OriginalRequest.Data.Device.Location.FormattedAddress)
	test.Equals(t, "12345", req.OriginalRequest.Data.Device.Location.ZipCode)
	test.Equals(t, "Anytown", req.OriginalRequest.Data.Device.Location.City)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "37151f7c-a409-48b8-9890-cd980cd2548e", req.SessionID)
}

func TestRequest2Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request2.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "f4b72ee9-cabb-4acd-af9b-2d2cb6ff53d2", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-12-24T07:23:46.64Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

}

<commit_msg>Start asserting elements<commit_after>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequest1Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Latitude)
	test.Equals(t, -123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Longitude)

	test.Equals(t, "1234 Random Road, Anytown, CA 12345, United States", req.OriginalRequest.Data.Device.Location.FormattedAddress)
	test.Equals(t, "12345", req.OriginalRequest.Data.Device.Location.ZipCode)
	test.Equals(t, "Anytown", req.OriginalRequest.Data.Device.Location.City)

	test.Equals(t, 200, req.Status.Code)
	test.Equals(t, "success", req.Status.ErrorType)

	test.Equals(t, "37151f7c-a409-48b8-9890-cd980cd2548e", req.SessionID)
}

func TestRequest2Parsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request2.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "f4b72ee9-cabb-4acd-af9b-2d2cb6ff53d2", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-12-24T07:23:46.64Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "i wonder if you are ready for me?", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "get_status", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, map[string]string{}, req.Result.Parameters)

}

<|endoftext|>


<commit_before>package models

import (
	"testing"

	"github.com/pborman/uuid"
	"github.com/stretchr/testify/assert"
)

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

	s := "a,b,c"
	l := toList(s)

	assert.EqualValues("a", l[0])
	assert.EqualValues("b", l[1])
	assert.EqualValues("c", l[2])
	assert.Equal(3, len(l))

	s = ""
	l = toList(s)
	assert.Equal(0, len(l))
}

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

	s := []string{"a:a", "b:b", "c:c"}
	l := toMap(s)

	assert.EqualValues("a", l["a"])
	assert.EqualValues("b", l["b"])
	assert.EqualValues("c", l["c"])
}

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

	s := ""
	nids := toUUIDList(s)
	assert.Equal(0, len(nids))

	ids := []uuid.UUID{uuid.NewUUID(), uuid.NewUUID()}
	s = ids[0].String() + "," + ids[1].String()

	nids = toUUIDList(s)

	assert.Equal(2, len(nids))
	assert.Equal(ids[0], nids[0])
	assert.Equal(ids[1], nids[1])
}
<commit_msg>update models test<commit_after>package models

import (
	"testing"

	"github.com/pborman/uuid"
	"github.com/stretchr/testify/assert"
)

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

	s := "a,b,c"
	l := ToList(s)

	assert.EqualValues("a", l[0])
	assert.EqualValues("b", l[1])
	assert.EqualValues("c", l[2])
	assert.Equal(3, len(l))

	s = ""
	l = ToList(s)
	assert.Equal(0, len(l))
}

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

	s := []string{"a:a", "b:b", "c:c"}
	l := toMap(s)

	assert.EqualValues("a", l["a"])
	assert.EqualValues("b", l["b"])
	assert.EqualValues("c", l["c"])
}

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

	s := ""
	nids := toUUIDList(s)
	assert.Equal(0, len(nids))

	ids := []uuid.UUID{uuid.NewUUID(), uuid.NewUUID()}
	s = ids[0].String() + "," + ids[1].String()

	nids = toUUIDList(s)

	assert.Equal(2, len(nids))
	assert.Equal(ids[0], nids[0])
	assert.Equal(ids[1], nids[1])
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/textproto"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/dustin/go-nntp"
	"github.com/dustin/go-nntp/server"

	"code.google.com/p/dsallings-couch-go"
)

var groupCacheTimeout = flag.Int("groupTimeout", 60,
	"Time (in seconds), group cache is valid")

type GroupRow struct {
	Group string        `json:"key"`
	Value []interface{} `json:"value"`
}

type GroupResults struct {
	Rows []GroupRow
}

type Attachment struct {
	Type string `json:"content-type"`
	Data []byte `json:"data"`
}

func removeSpace(r rune) rune {
	if r == ' ' || r == '\n' || r == '\r' {
		return -1
	}
	return r
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	m := map[string]string{
		"content_type": a.Type,
		"data":         strings.Map(removeSpace, base64.StdEncoding.EncodeToString(a.Data)),
	}
	return json.Marshal(m)
}

type Article struct {
	MsgId       string                 `json:"_id"`
	DocType     string                 `json:"type"`
	Headers     map[string][]string    `json:"headers"`
	Bytes       int                    `json:"bytes"`
	Lines       int                    `json:"lines"`
	Nums        map[string]int64       `json:"nums"`
	Attachments map[string]*Attachment `json:"_attachments"`
}

type ArticleResults struct {
	Rows []struct {
		Key     []interface{} `json:"key"`
		Article Article       `json:"doc"`
	}
}

type couchBackend struct {
	db        *couch.Database
	groups    map[string]*nntp.Group
	grouplock sync.Mutex
}

func (cb *couchBackend) clearGroups() {
	cb.grouplock.Lock()
	defer cb.grouplock.Unlock()

	log.Printf("Dumping group cache")
	cb.groups = nil
}

func (cb *couchBackend) fetchGroups() error {
	cb.grouplock.Lock()
	defer cb.grouplock.Unlock()

	if cb.groups != nil {
		return nil
	}

	log.Printf("Filling group cache")

	results := GroupResults{}
	err := cb.db.Query("_design/groups/_view/list", map[string]interface{}{
		"group": true,
	}, &results)
	if err != nil {
		return err
	}
	cb.groups = make(map[string]*nntp.Group)
	for _, gr := range results.Rows {
		if gr.Value[0].(string) != "" {
			group := nntp.Group{
				Name:        gr.Group,
				Description: gr.Value[0].(string),
				Count:       int64(gr.Value[1].(float64)),
				Low:         int64(gr.Value[2].(float64)),
				High:        int64(gr.Value[3].(float64)),
			}
			cb.groups[group.Name] = &group
		}
	}

	go func() {
		time.Sleep(time.Duration(*groupCacheTimeout) * time.Second)
		cb.clearGroups()
	}()

	return nil
}

func (cb *couchBackend) ListGroups(max int) ([]*nntp.Group, error) {
	if cb.groups == nil {
		if err := cb.fetchGroups(); err != nil {
			return nil, err
		}
	}
	rv := make([]*nntp.Group, 0, len(cb.groups))
	for _, g := range cb.groups {
		rv = append(rv, g)
	}
	return rv, nil
}

func (cb *couchBackend) GetGroup(name string) (*nntp.Group, error) {
	if cb.groups == nil {
		if err := cb.fetchGroups(); err != nil {
			return nil, err
		}
	}
	g, exists := cb.groups[name]
	if !exists {
		return nil, nntpserver.NoSuchGroup
	}
	return g, nil
}

func (cb *couchBackend) mkArticle(ar Article) *nntp.Article {
	url := fmt.Sprintf("%s/%s/article", cb.db.DBURL(), cleanupId(ar.MsgId))
	return &nntp.Article{
		Header: textproto.MIMEHeader(ar.Headers),
		Body:   &lazyOpener{url, nil, nil},
		Bytes:  ar.Bytes,
		Lines:  ar.Lines,
	}
}

func (cb *couchBackend) GetArticle(group *nntp.Group, id string) (*nntp.Article, error) {
	var ar Article
	if intid, err := strconv.ParseInt(id, 10, 64); err == nil {
		results := ArticleResults{}
		cb.db.Query("_design/articles/_view/list", map[string]interface{}{
			"include_docs": true,
			"key":          []interface{}{group.Name, intid},
		}, &results)

		if len(results.Rows) != 1 {
			return nil, nntpserver.InvalidArticleNumber
		}

		ar = results.Rows[0].Article
	} else {
		err := cb.db.Retrieve(cleanupId(id), &ar)
		if err != nil {
			return nil, nntpserver.InvalidMessageId
		}
	}

	return cb.mkArticle(ar), nil
}

func (cb *couchBackend) GetArticles(group *nntp.Group,
	from, to int64) ([]nntpserver.NumberedArticle, error) {

	rv := make([]nntpserver.NumberedArticle, 0, 100)

	results := ArticleResults{}
	cb.db.Query("_design/articles/_view/list", map[string]interface{}{
		"include_docs": true,
		"start_key":    []interface{}{group.Name, from},
		"end_key":      []interface{}{group.Name, to},
	}, &results)

	for _, r := range results.Rows {
		rv = append(rv, nntpserver.NumberedArticle{
			Num:     int64(r.Key[1].(float64)),
			Article: cb.mkArticle(r.Article),
		})
	}

	return rv, nil
}

func (tb *couchBackend) AllowPost() bool {
	return true
}

func cleanupId(msgid string) string {
	s := strings.TrimFunc(msgid, func(r rune) bool {
		return r == ' ' || r == '<' || r == '>'
	})
	return url.QueryEscape(s)
}

func (cb *couchBackend) Post(article *nntp.Article) error {
	a := Article{
		DocType:     "article",
		Headers:     map[string][]string(article.Header),
		Nums:        make(map[string]int64),
		MsgId:       cleanupId(article.Header.Get("Message-Id")),
		Attachments: make(map[string]*Attachment),
	}

	b := []byte{}
	buf := bytes.NewBuffer(b)
	n, err := io.Copy(buf, article.Body)
	if err != nil {
		return err
	}
	log.Printf("Read %d bytes of body", n)

	b = buf.Bytes()
	a.Bytes = len(b)
	a.Lines = bytes.Count(b, []byte{'\n'})

	a.Attachments["article"] = &Attachment{"text/plain", b}

	for _, g := range strings.Split(article.Header.Get("Newsgroups"), ",") {
		g = strings.TrimSpace(g)
		group, err := cb.GetGroup(g)
		if err == nil {
			a.Nums[g] = atomic.AddInt64(&group.High, 1)
		} else {
			log.Printf("Error getting group %q:  %v", g, err)
		}
	}

	if len(a.Nums) == 0 {
		log.Printf("Found no matching groups in %v",
			article.Header["Newsgroups"])
		return nntpserver.PostingFailed
	}

	_, _, err = cb.db.Insert(&a)
	if err != nil {
		log.Printf("error posting article: %v", err)
		return nntpserver.PostingFailed
	}

	return nil
}

func (tb *couchBackend) Authorized() bool {
	return true
}

func (tb *couchBackend) Authenticate(user, pass string) (nntpserver.Backend, error) {
	return nil, nntpserver.AuthRejected
}

func maybefatal(err error, f string, a ...interface{}) {
	if err != nil {
		log.Fatalf(f, a...)
	}
}

func main() {

	couchUrl := flag.String("couch", "http://localhost:5984/news",
		"Couch DB.")

	flag.Parse()

	a, err := net.ResolveTCPAddr("tcp", ":1119")
	maybefatal(err, "Error resolving listener: %v", err)
	l, err := net.ListenTCP("tcp", a)
	maybefatal(err, "Error setting up listener: %v", err)
	defer l.Close()

	db, err := couch.Connect(*couchUrl)
	maybefatal(err, "Can't connect to the couch: %v", err)
	err = ensureViews(&db)
	maybefatal(err, "Error setting up views: %v", err)

	backend := couchBackend{
		db: &db,
	}

	s := nntpserver.NewServer(&backend)

	for {
		c, err := l.AcceptTCP()
		maybefatal(err, "Error accepting connection: %v", err)
		go s.Process(c)
	}
}
<commit_msg>Added option to optimistically store articles.<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/textproto"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/dustin/go-nntp"
	"github.com/dustin/go-nntp/server"

	"code.google.com/p/dsallings-couch-go"
)

var groupCacheTimeout = flag.Int("groupTimeout", 60,
	"Time (in seconds), group cache is valid")
var optimisticPost = flag.Bool("optimistic", false,
	"Optimistically return success on store before storing")

type GroupRow struct {
	Group string        `json:"key"`
	Value []interface{} `json:"value"`
}

type GroupResults struct {
	Rows []GroupRow
}

type Attachment struct {
	Type string `json:"content-type"`
	Data []byte `json:"data"`
}

func removeSpace(r rune) rune {
	if r == ' ' || r == '\n' || r == '\r' {
		return -1
	}
	return r
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	m := map[string]string{
		"content_type": a.Type,
		"data":         strings.Map(removeSpace, base64.StdEncoding.EncodeToString(a.Data)),
	}
	return json.Marshal(m)
}

type Article struct {
	MsgId       string                 `json:"_id"`
	DocType     string                 `json:"type"`
	Headers     map[string][]string    `json:"headers"`
	Bytes       int                    `json:"bytes"`
	Lines       int                    `json:"lines"`
	Nums        map[string]int64       `json:"nums"`
	Attachments map[string]*Attachment `json:"_attachments"`
}

type ArticleResults struct {
	Rows []struct {
		Key     []interface{} `json:"key"`
		Article Article       `json:"doc"`
	}
}

type couchBackend struct {
	db        *couch.Database
	groups    map[string]*nntp.Group
	grouplock sync.Mutex
}

func (cb *couchBackend) clearGroups() {
	cb.grouplock.Lock()
	defer cb.grouplock.Unlock()

	log.Printf("Dumping group cache")
	cb.groups = nil
}

func (cb *couchBackend) fetchGroups() error {
	cb.grouplock.Lock()
	defer cb.grouplock.Unlock()

	if cb.groups != nil {
		return nil
	}

	log.Printf("Filling group cache")

	results := GroupResults{}
	err := cb.db.Query("_design/groups/_view/list", map[string]interface{}{
		"group": true,
	}, &results)
	if err != nil {
		return err
	}
	cb.groups = make(map[string]*nntp.Group)
	for _, gr := range results.Rows {
		if gr.Value[0].(string) != "" {
			group := nntp.Group{
				Name:        gr.Group,
				Description: gr.Value[0].(string),
				Count:       int64(gr.Value[1].(float64)),
				Low:         int64(gr.Value[2].(float64)),
				High:        int64(gr.Value[3].(float64)),
			}
			cb.groups[group.Name] = &group
		}
	}

	go func() {
		time.Sleep(time.Duration(*groupCacheTimeout) * time.Second)
		cb.clearGroups()
	}()

	return nil
}

func (cb *couchBackend) ListGroups(max int) ([]*nntp.Group, error) {
	if cb.groups == nil {
		if err := cb.fetchGroups(); err != nil {
			return nil, err
		}
	}
	rv := make([]*nntp.Group, 0, len(cb.groups))
	for _, g := range cb.groups {
		rv = append(rv, g)
	}
	return rv, nil
}

func (cb *couchBackend) GetGroup(name string) (*nntp.Group, error) {
	if cb.groups == nil {
		if err := cb.fetchGroups(); err != nil {
			return nil, err
		}
	}
	g, exists := cb.groups[name]
	if !exists {
		return nil, nntpserver.NoSuchGroup
	}
	return g, nil
}

func (cb *couchBackend) mkArticle(ar Article) *nntp.Article {
	url := fmt.Sprintf("%s/%s/article", cb.db.DBURL(), cleanupId(ar.MsgId))
	return &nntp.Article{
		Header: textproto.MIMEHeader(ar.Headers),
		Body:   &lazyOpener{url, nil, nil},
		Bytes:  ar.Bytes,
		Lines:  ar.Lines,
	}
}

func (cb *couchBackend) GetArticle(group *nntp.Group, id string) (*nntp.Article, error) {
	var ar Article
	if intid, err := strconv.ParseInt(id, 10, 64); err == nil {
		results := ArticleResults{}
		cb.db.Query("_design/articles/_view/list", map[string]interface{}{
			"include_docs": true,
			"key":          []interface{}{group.Name, intid},
		}, &results)

		if len(results.Rows) != 1 {
			return nil, nntpserver.InvalidArticleNumber
		}

		ar = results.Rows[0].Article
	} else {
		err := cb.db.Retrieve(cleanupId(id), &ar)
		if err != nil {
			return nil, nntpserver.InvalidMessageId
		}
	}

	return cb.mkArticle(ar), nil
}

func (cb *couchBackend) GetArticles(group *nntp.Group,
	from, to int64) ([]nntpserver.NumberedArticle, error) {

	rv := make([]nntpserver.NumberedArticle, 0, 100)

	results := ArticleResults{}
	cb.db.Query("_design/articles/_view/list", map[string]interface{}{
		"include_docs": true,
		"start_key":    []interface{}{group.Name, from},
		"end_key":      []interface{}{group.Name, to},
	}, &results)

	for _, r := range results.Rows {
		rv = append(rv, nntpserver.NumberedArticle{
			Num:     int64(r.Key[1].(float64)),
			Article: cb.mkArticle(r.Article),
		})
	}

	return rv, nil
}

func (tb *couchBackend) AllowPost() bool {
	return true
}

func cleanupId(msgid string) string {
	s := strings.TrimFunc(msgid, func(r rune) bool {
		return r == ' ' || r == '<' || r == '>'
	})
	return url.QueryEscape(s)
}

func (cb *couchBackend) Post(article *nntp.Article) error {
	a := Article{
		DocType:     "article",
		Headers:     map[string][]string(article.Header),
		Nums:        make(map[string]int64),
		MsgId:       cleanupId(article.Header.Get("Message-Id")),
		Attachments: make(map[string]*Attachment),
	}

	b := []byte{}
	buf := bytes.NewBuffer(b)
	n, err := io.Copy(buf, article.Body)
	if err != nil {
		return err
	}
	log.Printf("Read %d bytes of body", n)

	b = buf.Bytes()
	a.Bytes = len(b)
	a.Lines = bytes.Count(b, []byte{'\n'})

	a.Attachments["article"] = &Attachment{"text/plain", b}

	for _, g := range strings.Split(article.Header.Get("Newsgroups"), ",") {
		g = strings.TrimSpace(g)
		group, err := cb.GetGroup(g)
		if err == nil {
			a.Nums[g] = atomic.AddInt64(&group.High, 1)
		} else {
			log.Printf("Error getting group %q:  %v", g, err)
		}
	}

	if len(a.Nums) == 0 {
		log.Printf("Found no matching groups in %v",
			article.Header["Newsgroups"])
		return nntpserver.PostingFailed
	}

	if *optimisticPost {
		go func() {
			_, _, err = cb.db.Insert(&a)
			if err != nil {
				log.Printf("error optimistically posting article: %v", err)
			}
		}()
	} else {
		_, _, err = cb.db.Insert(&a)
		if err != nil {
			log.Printf("error posting article: %v", err)
			return nntpserver.PostingFailed
		}
	}

	return nil
}

func (tb *couchBackend) Authorized() bool {
	return true
}

func (tb *couchBackend) Authenticate(user, pass string) (nntpserver.Backend, error) {
	return nil, nntpserver.AuthRejected
}

func maybefatal(err error, f string, a ...interface{}) {
	if err != nil {
		log.Fatalf(f, a...)
	}
}

func main() {

	couchUrl := flag.String("couch", "http://localhost:5984/news",
		"Couch DB.")

	flag.Parse()

	a, err := net.ResolveTCPAddr("tcp", ":1119")
	maybefatal(err, "Error resolving listener: %v", err)
	l, err := net.ListenTCP("tcp", a)
	maybefatal(err, "Error setting up listener: %v", err)
	defer l.Close()

	db, err := couch.Connect(*couchUrl)
	maybefatal(err, "Can't connect to the couch: %v", err)
	err = ensureViews(&db)
	maybefatal(err, "Error setting up views: %v", err)

	backend := couchBackend{
		db: &db,
	}

	s := nntpserver.NewServer(&backend)

	for {
		c, err := l.AcceptTCP()
		maybefatal(err, "Error accepting connection: %v", err)
		go s.Process(c)
	}
}
<|endoftext|>


<commit_before>package module3rd

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/util"
)

func DownloadAndExtractParallel(m Module3rd, wg *sync.WaitGroup) {
	if util.FileExists(m.Name) {
		log.Printf("%s already exists.", m.Name)
		wg.Done()
		return
	}

	if m.Form != "local" {
		if len(m.Rev) > 0 {
			log.Printf("Download %s-%s.....", m.Name, m.Rev)
		} else {
			log.Printf("Download %s.....", m.Name)
		}

		logName := fmt.Sprintf("%s.log", m.Name)

		err := download(m, logName)
		if err != nil {
			util.PrintFatalMsg(err, logName)
		}
	} else if !util.FileExists(m.Url) {
		log.Fatalf("no such directory:%s", m.Url)
	}

	wg.Done()
}

func download(m Module3rd, logName string) error {
	form := m.Form
	url := m.Url

	switch form {
	case "git":
		fallthrough
	case "hg":
		args := []string{form, "clone", url}
		if command.VerboseEnabled {
			return command.Run(args)
		}

		f, err := os.Create(logName)
		if err != nil {
			return command.Run(args)
		}
		defer f.Close()

		cmd, err := command.Make(args)
		if err != nil {
			return err
		}

		writer := bufio.NewWriter(f)
		defer writer.Flush()

		cmd.Stderr = writer

		return cmd.Run()
	case "local":
		return nil
	}

	return fmt.Errorf("form=%s is not supported", form)
}
<commit_msg>added comment.<commit_after>package module3rd

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"sync"

	"github.com/cubicdaiya/nginx-build/command"
	"github.com/cubicdaiya/nginx-build/util"
)

func DownloadAndExtractParallel(m Module3rd, wg *sync.WaitGroup) {
	if util.FileExists(m.Name) {
		log.Printf("%s already exists.", m.Name)
		wg.Done()
		return
	}

	if m.Form != "local" {
		if len(m.Rev) > 0 {
			log.Printf("Download %s-%s.....", m.Name, m.Rev)
		} else {
			log.Printf("Download %s.....", m.Name)
		}

		logName := fmt.Sprintf("%s.log", m.Name)

		err := download(m, logName)
		if err != nil {
			util.PrintFatalMsg(err, logName)
		}
	} else if !util.FileExists(m.Url) {
		log.Fatalf("no such directory:%s", m.Url)
	}

	wg.Done()
}

func download(m Module3rd, logName string) error {
	form := m.Form
	url := m.Url

	switch form {
	case "git":
		fallthrough
	case "hg":
		args := []string{form, "clone", url}
		if command.VerboseEnabled {
			return command.Run(args)
		}

		f, err := os.Create(logName)
		if err != nil {
			return command.Run(args)
		}
		defer f.Close()

		cmd, err := command.Make(args)
		if err != nil {
			return err
		}

		writer := bufio.NewWriter(f)
		defer writer.Flush()

		cmd.Stderr = writer

		return cmd.Run()
	case "local": // not implemented yet
		return nil
	}

	return fmt.Errorf("form=%s is not supported", form)
}
<|endoftext|>