<commit_before>package file

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/karimra/gnmic/collector"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/protobuf/proto"
)

const (
	defaultFormat    = "json"
	defaultSeparator = "\n"
	defaultFileName  = "gnmic"
)

func init() {
	outputs.Register("file", func() outputs.Output {
		return &File{
			Cfg: &Config{},
			metrics: []prometheus.Collector{
				NumberOfWrittenBytes,
				NumberOfReceivedMsgs,
				NumberOfWrittenMsgs,
			},
		}
	})
}

// File //
type File struct {
	Cfg     *Config
	file    *os.File
	logger  *log.Logger
	metrics []prometheus.Collector
}

// Config //
type Config struct {
	FileName  string `mapstructure:"filename,omitempty"`
	FileType  string `mapstructure:"file-type,omitempty"`
	Format    string `mapstructure:"format,omitempty"`
	Multiline bool   `mapstructure:"multiline,omitempty"`
	Indent    string `mapstructure:"indent,omitempty"`
	Separator string `mapstructure:"separator,omitempty"`
}

func (f *File) String() string {
	b, err := json.Marshal(f)
	if err != nil {
		return ""
	}
	return string(b)
}

// Init //
func (f *File) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, f.Cfg)
	if err != nil {
		return err
	}
	if f.Cfg.Format == "proto" {
		return fmt.Errorf("proto format not supported in output type 'file'")
	}
	if f.Cfg.Separator == "" {
		f.Cfg.Separator = "\n"
	}
	if f.Cfg.FileName == "" {
		f.Cfg.FileName = fmt.Sprintf("%s-%s.log", defaultFileName, time.Now().Format("2006-01-02"))
	}
	var file *os.File
	switch f.Cfg.FileType {
	case "stdout":
		file = os.Stdout
	case "stderr":
		file = os.Stderr
	default:
		file, err = os.OpenFile(f.Cfg.FileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			return err
		}
	}
	f.file = file
	f.logger = log.New(os.Stderr, "file_output ", log.LstdFlags|log.Lmicroseconds)
	if logger != nil {
		f.logger.SetOutput(logger.Writer())
		f.logger.SetFlags(logger.Flags())
	}
	if f.Cfg.Format == "" {
		f.Cfg.Format = "json"
	}
	if f.Cfg.FileType == "stdout" || f.Cfg.FileType == "stderr" {
		f.Cfg.Indent = "  "
		f.Cfg.Multiline = true
	}
	f.logger.Printf("initialized file output: %s", f.String())
	return nil
}

// Write //
func (f *File) Write(rsp proto.Message, meta outputs.Meta) {
	if rsp == nil {
		return
	}
	NumberOfReceivedMsgs.WithLabelValues(f.file.Name()).Inc()
	b, err := collector.Marshal(rsp, f.Cfg.Format, meta, f.Cfg.Multiline, f.Cfg.Indent)
	if err != nil {
		f.logger.Printf("failed marshaling proto msg: %v", err)
		return
	}
	n, err := f.file.Write(append(b, []byte(f.Cfg.Separator)...))
	if err != nil {
		f.logger.Printf("failed to write to file '%s': %v", f.file.Name(), err)
		return
	}
	NumberOfWrittenBytes.WithLabelValues(f.file.Name()).Add(float64(n))
	NumberOfWrittenMsgs.WithLabelValues(f.file.Name()).Inc()
}

// Close //
func (f *File) Close() error {
	f.logger.Printf("closing file '%s' output", f.file.Name())
	return f.file.Close()
}

// Metrics //
func (f *File) Metrics() []prometheus.Collector { return f.metrics }
<commit_msg>default to stdout if filename is empty<commit_after>package file

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/karimra/gnmic/collector"
	"github.com/karimra/gnmic/outputs"
	"github.com/mitchellh/mapstructure"
	"github.com/prometheus/client_golang/prometheus"
	"google.golang.org/protobuf/proto"
)

const (
	defaultFormat    = "json"
	defaultSeparator = "\n"
)

func init() {
	outputs.Register("file", func() outputs.Output {
		return &File{
			Cfg: &Config{},
			metrics: []prometheus.Collector{
				NumberOfWrittenBytes,
				NumberOfReceivedMsgs,
				NumberOfWrittenMsgs,
			},
		}
	})
}

// File //
type File struct {
	Cfg     *Config
	file    *os.File
	logger  *log.Logger
	metrics []prometheus.Collector
}

// Config //
type Config struct {
	FileName  string `mapstructure:"filename,omitempty"`
	FileType  string `mapstructure:"file-type,omitempty"`
	Format    string `mapstructure:"format,omitempty"`
	Multiline bool   `mapstructure:"multiline,omitempty"`
	Indent    string `mapstructure:"indent,omitempty"`
	Separator string `mapstructure:"separator,omitempty"`
}

func (f *File) String() string {
	b, err := json.Marshal(f)
	if err != nil {
		return ""
	}
	return string(b)
}

// Init //
func (f *File) Init(cfg map[string]interface{}, logger *log.Logger) error {
	err := mapstructure.Decode(cfg, f.Cfg)
	if err != nil {
		return err
	}
	if f.Cfg.Format == "proto" {
		return fmt.Errorf("proto format not supported in output type 'file'")
	}
	if f.Cfg.Separator == "" {
		f.Cfg.Separator = "\n"
	}
	if f.Cfg.FileName == "" && f.Cfg.FileType == "" {
		f.Cfg.FileType = "stdout"
	}
	switch f.Cfg.FileType {
	case "stdout":
		f.file = os.Stdout
	case "stderr":
		f.file = os.Stderr
	default:
		f.file, err = os.OpenFile(f.Cfg.FileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
		if err != nil {
			return err
		}
	}
	f.logger = log.New(os.Stderr, "file_output ", log.LstdFlags|log.Lmicroseconds)
	if logger != nil {
		f.logger.SetOutput(logger.Writer())
		f.logger.SetFlags(logger.Flags())
	}
	if f.Cfg.Format == "" {
		f.Cfg.Format = "json"
	}
	if f.Cfg.FileType == "stdout" || f.Cfg.FileType == "stderr" {
		f.Cfg.Indent = "  "
		f.Cfg.Multiline = true
	}
	f.logger.Printf("initialized file output: %s", f.String())
	return nil
}

// Write //
func (f *File) Write(rsp proto.Message, meta outputs.Meta) {
	if rsp == nil {
		return
	}
	NumberOfReceivedMsgs.WithLabelValues(f.file.Name()).Inc()
	b, err := collector.Marshal(rsp, f.Cfg.Format, meta, f.Cfg.Multiline, f.Cfg.Indent)
	if err != nil {
		f.logger.Printf("failed marshaling proto msg: %v", err)
		return
	}
	n, err := f.file.Write(append(b, []byte(f.Cfg.Separator)...))
	if err != nil {
		f.logger.Printf("failed to write to file '%s': %v", f.file.Name(), err)
		return
	}
	NumberOfWrittenBytes.WithLabelValues(f.file.Name()).Add(float64(n))
	NumberOfWrittenMsgs.WithLabelValues(f.file.Name()).Inc()
}

// Close //
func (f *File) Close() error {
	f.logger.Printf("closing file '%s' output", f.file.Name())
	return f.file.Close()
}

// Metrics //
func (f *File) Metrics() []prometheus.Collector { return f.metrics }
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os/user"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/alertmanager/types"
)

type addResponse struct {
	Status string `json:"status"`
	Data   struct {
		SilenceID string `json:"silenceId"`
	} `json:"data,omitempty"`
	ErrorType string `json:"errorType,omitempty"`
	Error     string `json:"error,omitempty"`
}

func username() string {
	user, err := user.Current()
	if err != nil {
		return ""
	}
	return user.Username
}

var (
	addCmd         = silenceCmd.Command("add", "Add a new alertmanager silence")
	author         = addCmd.Flag("author", "Username for CreatedBy field").Short('a').Default(username()).String()
	requireComment = addCmd.Flag("require-comment", "Require comment to be set").Hidden().Default("true").Bool()
	expires        = addCmd.Flag("expires", "Duration of silence (100h)").Short('e').Default("1h").Duration()
	expireOn       = addCmd.Flag("expire-on", "Expire at a certain time (Overwrites expires) RFC3339 format 2006-01-02T15:04:05Z07:00").String()
	comment        = addCmd.Flag("comment", "A comment to help describe the silence").Short('c').String()
	addArgs        = addCmd.Arg("matcher-groups", "Query filter").Strings()
)

func init() {
	addCmd.Action(add)
	longHelpText["silence add"] = `Add a new alertmanager silence

  Amtool uses a simplified prometheus syntax to represent silences. The
  non-option section of arguments constructs a list of "Matcher Groups"
  that will be used to create a number of silences. The following examples
  will attempt to show this behaviour in action:

  amtool silence add alertname=foo node=bar

	This statement will add a silence that matches alerts with the
	alertname=foo and node=bar label value pairs set.

  amtool silence add foo node=bar

	If alertname is ommited and the first argument does not contain a '=' or a
	'=~' then it will be assumed to be the value of the alertname pair.

  amtool silence add 'alertname=~foo.*'

	As well as direct equality, regex matching is also supported. The '=~' syntax
	(similar to prometheus) is used to represent a regex match. Regex matching
	can be used in combination with a direct match.
`
}

func add(element *kingpin.ParseElement, ctx *kingpin.ParseContext) error {
	var err error

	matchers, err := parseMatchers(*addArgs)
	if err != nil {
		return err
	}

	if len(matchers) < 1 {
		return fmt.Errorf("no matchers specified")
	}

	var endsAt time.Time
	if *expireOn != "" {
		endsAt, err = time.Parse(time.RFC3339, *expireOn)
		if err != nil {
			return err
		}
	} else {
		endsAt = time.Now().UTC().Add(*expires)
	}

	if *requireComment && *comment == "" {
		return errors.New("comment required by config")
	}

	typeMatchers, err := TypeMatchers(matchers)
	if err != nil {
		return err
	}

	silence := types.Silence{
		Matchers:  typeMatchers,
		StartsAt:  time.Now().UTC(),
		EndsAt:    endsAt,
		CreatedBy: *author,
		Comment:   *comment,
	}

	silenceId, err := addSilence(&silence)
	if err != nil {
		return err
	}

	_, err = fmt.Println(silenceId)
	return err
}

func addSilence(silence *types.Silence) (string, error) {
	u := GetAlertmanagerURL("/api/v1/silences")

	buf := bytes.NewBuffer([]byte{})
	err := json.NewEncoder(buf).Encode(silence)
	if err != nil {
		return "", err
	}

	res, err := http.Post(u.String(), "application/json", buf)
	if err != nil {
		return "", err
	}

	defer res.Body.Close()
	response := addResponse{}
	err = json.NewDecoder(res.Body).Decode(&response)
	if err != nil {
		return "", fmt.Errorf("unable to parse silence json response from %s", u.String())
	}

	if response.Status == "error" {
		return "", fmt.Errorf("[%s] %s", response.ErrorType, response.Error)
	}

	return response.Data.SilenceID, nil
}
<commit_msg>Re-introduce prometheus durations in amtool silence creation (#1185)<commit_after>package cli

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os/user"
	"time"

	"github.com/alecthomas/kingpin"
	"github.com/prometheus/alertmanager/types"
	"github.com/prometheus/common/model"
)

type addResponse struct {
	Status string `json:"status"`
	Data   struct {
		SilenceID string `json:"silenceId"`
	} `json:"data,omitempty"`
	ErrorType string `json:"errorType,omitempty"`
	Error     string `json:"error,omitempty"`
}

func username() string {
	user, err := user.Current()
	if err != nil {
		return ""
	}
	return user.Username
}

var (
	addCmd         = silenceCmd.Command("add", "Add a new alertmanager silence")
	author         = addCmd.Flag("author", "Username for CreatedBy field").Short('a').Default(username()).String()
	requireComment = addCmd.Flag("require-comment", "Require comment to be set").Hidden().Default("true").Bool()
	expires        = addCmd.Flag("expires", "Duration of silence").Short('e').Default("1h").String()
	expireOn       = addCmd.Flag("expire-on", "Expire at a certain time (Overwrites expires) RFC3339 format 2006-01-02T15:04:05Z07:00").String()
	comment        = addCmd.Flag("comment", "A comment to help describe the silence").Short('c').String()
	addArgs        = addCmd.Arg("matcher-groups", "Query filter").Strings()
)

func init() {
	addCmd.Action(add)
	longHelpText["silence add"] = `Add a new alertmanager silence

  Amtool uses a simplified prometheus syntax to represent silences. The
  non-option section of arguments constructs a list of "Matcher Groups"
  that will be used to create a number of silences. The following examples
  will attempt to show this behaviour in action:

  amtool silence add alertname=foo node=bar

	This statement will add a silence that matches alerts with the
	alertname=foo and node=bar label value pairs set.

  amtool silence add foo node=bar

	If alertname is ommited and the first argument does not contain a '=' or a
	'=~' then it will be assumed to be the value of the alertname pair.

  amtool silence add 'alertname=~foo.*'

	As well as direct equality, regex matching is also supported. The '=~' syntax
	(similar to prometheus) is used to represent a regex match. Regex matching
	can be used in combination with a direct match.
`
}

func add(element *kingpin.ParseElement, ctx *kingpin.ParseContext) error {
	var err error

	matchers, err := parseMatchers(*addArgs)
	if err != nil {
		return err
	}

	if len(matchers) < 1 {
		return fmt.Errorf("no matchers specified")
	}

	var endsAt time.Time
	if *expireOn != "" {
		endsAt, err = time.Parse(time.RFC3339, *expireOn)
		if err != nil {
			return err
		}
	} else {
		duration, err := model.ParseDuration(*expires)
		if err != nil {
			return err
		}
		if duration == 0 {
			return fmt.Errorf("silence duration must be greater than 0")
		}
		endsAt = time.Now().UTC().Add(time.Duration(duration))
	}

	if *requireComment && *comment == "" {
		return errors.New("comment required by config")
	}

	typeMatchers, err := TypeMatchers(matchers)
	if err != nil {
		return err
	}

	silence := types.Silence{
		Matchers:  typeMatchers,
		StartsAt:  time.Now().UTC(),
		EndsAt:    endsAt,
		CreatedBy: *author,
		Comment:   *comment,
	}

	silenceId, err := addSilence(&silence)
	if err != nil {
		return err
	}

	_, err = fmt.Println(silenceId)
	return err
}

func addSilence(silence *types.Silence) (string, error) {
	u := GetAlertmanagerURL("/api/v1/silences")

	buf := bytes.NewBuffer([]byte{})
	err := json.NewEncoder(buf).Encode(silence)
	if err != nil {
		return "", err
	}

	res, err := http.Post(u.String(), "application/json", buf)
	if err != nil {
		return "", err
	}

	defer res.Body.Close()
	response := addResponse{}
	err = json.NewDecoder(res.Body).Decode(&response)
	if err != nil {
		return "", fmt.Errorf("unable to parse silence json response from %s", u.String())
	}

	if response.Status == "error" {
		return "", fmt.Errorf("[%s] %s", response.ErrorType, response.Error)
	}

	return response.Data.SilenceID, nil
}
<|endoftext|>


<commit_before>package acceptance

import (
	"log"
	"os"

	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/go-pivnet/logshim"
	"github.com/pivotal-cf/pivnet-resource/gp"
	"github.com/robdimsdale/sanitizer"

	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var (
	inPath    string
	checkPath string
	outPath   string

	endpoint string

	productSlug  string
	refreshToken string

	pivnetClient *gp.Client
	additionalBeforeSuite func()
)

func TestAcceptance(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Acceptance Suite")
}

var _ = BeforeSuite(func() {
	var err error
	By("Getting product slug from environment variables")
	productSlug = os.Getenv("PRODUCT_SLUG")
	Expect(productSlug).NotTo(BeEmpty(), "$PRODUCT_SLUG must be provided")

	By("Getting endpoint from environment variables")
	endpoint = os.Getenv("PIVNET_ENDPOINT")
	Expect(endpoint).NotTo(BeEmpty(), "$PIVNET_ENDPOINT must be provided")

	By("Getting refresh token from environment variables")
	refreshToken = os.Getenv("PIVNET_RESOURCE_REFRESH_TOKEN")
	Expect(refreshToken).NotTo(BeEmpty(), "$PIVNET_RESOURCE_REFRESH_TOKEN must be provided")

	By("Compiling check binary")
	checkPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/check", "-race")
	Expect(err).NotTo(HaveOccurred())

	By("Compiling out binary")
	outPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/out", "-race")
	Expect(err).NotTo(HaveOccurred())

	By("Compiling in binary")
	inPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/in")
	Expect(err).NotTo(HaveOccurred())

	By("Sanitizing acceptance test output")
	sanitized := map[string]string{
		refreshToken: "***sanitized-refresh-token***",
	}
	sanitizedWriter := sanitizer.NewSanitizer(sanitized, GinkgoWriter)
	GinkgoWriter = sanitizedWriter

	By("Creating pivnet client (for out-of-band operations)")

	testLogger := log.New(sanitizedWriter, "", log.LstdFlags)
	verbose := true
	ls := logshim.NewLogShim(testLogger, testLogger, verbose)

	clientConfig := pivnet.ClientConfig{
		Host:      endpoint,
		UserAgent: "pivnet-resource/integration-test",
	}

	pivnetClient = gp.NewClient(pivnet.NewAccessTokenOrLegacyToken(refreshToken, endpoint), clientConfig, ls)

	if additionalBeforeSuite != nil {
		additionalBeforeSuite()
	}
})

var _ = AfterSuite(func() {
	gexec.CleanupBuildArtifacts()
})
<commit_msg>Add missing parameter for go pivent access token method<commit_after>package acceptance

import (
	"log"
	"os"

	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/go-pivnet/logshim"
	"github.com/pivotal-cf/pivnet-resource/gp"
	"github.com/robdimsdale/sanitizer"

	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var (
	inPath    string
	checkPath string
	outPath   string

	endpoint string

	productSlug  string
	refreshToken string

	pivnetClient *gp.Client
	additionalBeforeSuite func()
)

func TestAcceptance(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Acceptance Suite")
}

var _ = BeforeSuite(func() {
	var err error
	By("Getting product slug from environment variables")
	productSlug = os.Getenv("PRODUCT_SLUG")
	Expect(productSlug).NotTo(BeEmpty(), "$PRODUCT_SLUG must be provided")

	By("Getting endpoint from environment variables")
	endpoint = os.Getenv("PIVNET_ENDPOINT")
	Expect(endpoint).NotTo(BeEmpty(), "$PIVNET_ENDPOINT must be provided")

	By("Getting refresh token from environment variables")
	refreshToken = os.Getenv("PIVNET_RESOURCE_REFRESH_TOKEN")
	Expect(refreshToken).NotTo(BeEmpty(), "$PIVNET_RESOURCE_REFRESH_TOKEN must be provided")

	By("Compiling check binary")
	checkPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/check", "-race")
	Expect(err).NotTo(HaveOccurred())

	By("Compiling out binary")
	outPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/out", "-race")
	Expect(err).NotTo(HaveOccurred())

	By("Compiling in binary")
	inPath, err = gexec.Build("github.com/pivotal-cf/pivnet-resource/cmd/in")
	Expect(err).NotTo(HaveOccurred())

	By("Sanitizing acceptance test output")
	sanitized := map[string]string{
		refreshToken: "***sanitized-refresh-token***",
	}
	sanitizedWriter := sanitizer.NewSanitizer(sanitized, GinkgoWriter)
	GinkgoWriter = sanitizedWriter

	By("Creating pivnet client (for out-of-band operations)")

	testLogger := log.New(sanitizedWriter, "", log.LstdFlags)
	verbose := true
	ls := logshim.NewLogShim(testLogger, testLogger, verbose)

	clientConfig := pivnet.ClientConfig{
		Host:      endpoint,
		UserAgent: "pivnet-resource/integration-test",
	}

	pivnetClient = gp.NewClient(pivnet.NewAccessTokenOrLegacyToken(refreshToken, endpoint, false), clientConfig, ls)

	if additionalBeforeSuite != nil {
		additionalBeforeSuite()
	}
})

var _ = AfterSuite(func() {
	gexec.CleanupBuildArtifacts()
})
<|endoftext|>


<commit_before>package hipchat

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
)

// RoomService gives access to the room related methods of the API.
type RoomService struct {
	client *Client
}

// Rooms represents a HipChat room list.
type Rooms struct {
	Items      []Room    `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Room represents a HipChat room.
type Room struct {
	ID                int            `json:"id"`
	Links             RoomLinks      `json:"links"`
	Name              string         `json:"name"`
	XmppJid           string         `json:"xmpp_jid"`
	Statistics        RoomStatistics `json:"statistics"`
	Created           string         `json:"created"`
	IsArchived        bool           `json:"is_archived"`
	Privacy           string         `json:"privacy"`
	IsGuestAccessible bool           `json:"is_guess_accessible"`
	Topic             string         `json:"topic"`
	Participants      []User         `json:"participants"`
	Owner             User           `json:"owner"`
	GuestAccessURL    string         `json:"guest_access_url"`
}

// RoomStatistics represents the HipChat room statistics.
type RoomStatistics struct {
	Links Links `json:"links"`
}

// CreateRoomRequest represents a HipChat room creation request.
type CreateRoomRequest struct {
	Topic       string `json:"topic,omitempty"`
	GuestAccess bool   `json:"guest_access,omitempty"`
	Name        string `json:"name,omitempty"`
	OwnerUserID string `json:"owner_user_id,omitempty"`
	Privacy     string `json:"privacy,omitempty"`
}

// UpdateRoomRequest represents a HipChat room update request.
type UpdateRoomRequest struct {
	Name          string `json:"name"`
	Topic         string `json:"topic"`
	IsGuestAccess bool   `json:"is_guest_access"`
	IsArchived    bool   `json:"is_archived"`
	Privacy       string `json:"privacy"`
	Owner         ID     `json:"owner"`
}

// RoomLinks represents the HipChat room links.
type RoomLinks struct {
	Links
	Webhooks     string `json:"webhooks"`
	Members      string `json:"members"`
	Participants string `json:"participants"`
}

// NotificationRequest represents a HipChat room notification request.
type NotificationRequest struct {
	Color         string `json:"color,omitempty"`
	Message       string `json:"message,omitempty"`
	Notify        bool   `json:"notify,omitempty"`
	MessageFormat string `json:"message_format,omitempty"`
	Card          Card   `json:"card,omitempty"`
}

// Card represents a HipChat card
type Card struct {
	Style       string      `json:"style"`
	ID          string      `json:"id"`
	URL         string      `json:"url"`
	Title       string      `json:"title"`
	Description string      `json:"description"`
	Site        string      `json:"site,omitempty"`
	Icon        Icon        `json:"icon,omitempty"`
	Date        uint32      `json:"date,omitempty"`
	Attributes  []Attribute `json:"attributes,omitempty"`
	Activity    *Activity   `json:"activity,omitempty"`
}

// Icon represents an icon
type Icon struct {
	URL string `json:"url"`
}

// Attribute represents an attribute on a card
type Attribute struct {
	Label string         `json:"label,omitempty"`
	Value AttributeValue `json:"value"`
}

// AttributeValue represents the value of an attribute
type AttributeValue struct {
	Icon  string  `json:"icon,omitempty"`
	Label *string `json:"label,omitempty"`
	URL   string  `json:"url,omitempty"`
}

// Activity represents an activity that occurred
type Activity struct {
	Icon string          `json:"icon,omitempty"`
	HTML json.RawMessage `json:"html,omitempty"`
}

// ShareFileRequest represents a HipChat room file share request.
type ShareFileRequest struct {
	Path     string `json:"path"`
	Filename string `json:"filename,omitempty"`
	Message  string `json:"message,omitempty"`
}

// HistoryRequest represents a HipChat room chat history request.
type HistoryRequest struct {
	Date       string `json:"date"`
	Timezone   string `json:"timezone"`
	StartIndex int    `json:"start-index"`
	MaxResults int    `json:"max-results"`
	Reverse    bool   `json:"reverse"`
}

// History represents a HipChat room chat history.
type History struct {
	Items      []Message `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Message represents a HipChat message.
type Message struct {
	Date          string      `json:"date"`
	From          interface{} `json:"from"` // string | obj <- weak
	Id            string      `json:"id"`
	Mentions      []User      `json:"mentions"`
	Message       string      `json:"message"`
	MessageFormat string      `json:"message_format"`
	Type          string      `json:"type"`
}

// SetTopicRequest represents a hipchat update topic request
type SetTopicRequest struct {
	Topic string `json:"topic"`
}

// InviteRequest represents a hipchat invite to room request
type InviteRequest struct {
	Reason string `json:"reason"`
}

// AddAttribute adds an attribute to a Card
func (c *Card) AddAttribute(mainLabel, subLabel, url, iconURL string) {
	attr := Attribute{Label: mainLabel}
	attr.Value = AttributeValue{Label: &subLabel, URL: url, Icon: iconURL}

	c.Attributes = append(c.Attributes, attr)
}

// List returns all the rooms authorized.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_all_rooms
func (r *RoomService) List() (*Rooms, *http.Response, error) {
	req, err := r.client.NewRequest("GET", "room", nil)
	if err != nil {
		return nil, nil, err
	}

	rooms := new(Rooms)
	resp, err := r.client.Do(req, rooms)
	if err != nil {
		return nil, resp, err
	}
	return rooms, resp, nil
}

// Get returns the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_room
func (r *RoomService) Get(id string) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("GET", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Notification sends a notification to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/send_room_notification
func (r *RoomService) Notification(id string, notifReq *NotificationRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/notification", id), notifReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// ShareFile sends a file to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/share_file_with_room
func (r *RoomService) ShareFile(id string, shareFileReq *ShareFileRequest) (*http.Response, error) {
	req, err := r.client.NewFileUploadRequest("POST", fmt.Sprintf("room/%s/share/file", id), shareFileReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Create creates a new room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/create_room
func (r *RoomService) Create(roomReq *CreateRoomRequest) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("POST", "room", roomReq)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Delete deletes an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/delete_room
func (r *RoomService) Delete(id string) (*http.Response, error) {
	req, err := r.client.NewRequest("DELETE", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Update updates an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/update_room
func (r *RoomService) Update(id string, roomReq *UpdateRoomRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s", id), roomReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// History fetches a room's chat history.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/view_room_history
func (r *RoomService) History(id string, roomReq *HistoryRequest) (*History, *http.Response, error) {
	u := fmt.Sprintf("room/%s/history", id)
	// Form query parameters
	if roomReq != nil {
		p := url.Values{}
		if roomReq.Date != "" {
			p.Add("date", roomReq.Date)
		}
		if roomReq.Timezone != "" {
			p.Add("timezone", roomReq.Timezone)
		}
		if roomReq.StartIndex != 0 {
			p.Add("start-index", strconv.FormatInt(int64(roomReq.StartIndex), 10))
		}
		if roomReq.MaxResults != 0 {
			p.Add("max-results", strconv.FormatInt(int64(roomReq.MaxResults), 10))
		}
		// There's no way to tell whether caller set a boolean or not. We have to always set
		// it.
		p.Add("reverse", strconv.FormatBool(roomReq.Reverse))
		u += "?" + p.Encode()
	}
	req, err := r.client.NewRequest("GET", u, nil)
	h := new(History)
	resp, err := r.client.Do(req, &h)
	if err != nil {
		return nil, resp, err
	}
	return h, resp, nil
}

// Set Room topic.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/set_topic
func (r *RoomService) SetTopic(id string, topic string) (*http.Response, error) {
	topicReq := &SetTopicRequest{Topic: topic}

	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s/topic", id), topicReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Invite someone to the Room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/invite_user
func (r *RoomService) Invite(room string, user string, reason string) (*http.Response, error) {
	reasonReq := &InviteRequest{Reason: reason}

	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/invite/%s", room, user), reasonReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}
<commit_msg>more things again<commit_after>package hipchat

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
)

// RoomService gives access to the room related methods of the API.
type RoomService struct {
	client *Client
}

// Rooms represents a HipChat room list.
type Rooms struct {
	Items      []Room    `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Room represents a HipChat room.
type Room struct {
	ID                int            `json:"id"`
	Links             RoomLinks      `json:"links"`
	Name              string         `json:"name"`
	XmppJid           string         `json:"xmpp_jid"`
	Statistics        RoomStatistics `json:"statistics"`
	Created           string         `json:"created"`
	IsArchived        bool           `json:"is_archived"`
	Privacy           string         `json:"privacy"`
	IsGuestAccessible bool           `json:"is_guess_accessible"`
	Topic             string         `json:"topic"`
	Participants      []User         `json:"participants"`
	Owner             User           `json:"owner"`
	GuestAccessURL    string         `json:"guest_access_url"`
}

// RoomStatistics represents the HipChat room statistics.
type RoomStatistics struct {
	Links Links `json:"links"`
}

// CreateRoomRequest represents a HipChat room creation request.
type CreateRoomRequest struct {
	Topic       string `json:"topic,omitempty"`
	GuestAccess bool   `json:"guest_access,omitempty"`
	Name        string `json:"name,omitempty"`
	OwnerUserID string `json:"owner_user_id,omitempty"`
	Privacy     string `json:"privacy,omitempty"`
}

// UpdateRoomRequest represents a HipChat room update request.
type UpdateRoomRequest struct {
	Name          string `json:"name"`
	Topic         string `json:"topic"`
	IsGuestAccess bool   `json:"is_guest_access"`
	IsArchived    bool   `json:"is_archived"`
	Privacy       string `json:"privacy"`
	Owner         ID     `json:"owner"`
}

// RoomLinks represents the HipChat room links.
type RoomLinks struct {
	Links
	Webhooks     string `json:"webhooks"`
	Members      string `json:"members"`
	Participants string `json:"participants"`
}

// NotificationRequest represents a HipChat room notification request.
type NotificationRequest struct {
	Color         string `json:"color,omitempty"`
	Message       string `json:"message,omitempty"`
	Notify        bool   `json:"notify,omitempty"`
	MessageFormat string `json:"message_format,omitempty"`
	Card          Card   `json:"card,omitempty"`
}

// Card represents a HipChat card
type Card struct {
	Style       string      `json:"style"`
	ID          string      `json:"id"`
	URL         string      `json:"url"`
	Title       string      `json:"title"`
	Description string      `json:"description"`
	Site        string      `json:"site,omitempty"`
	Icon        Icon        `json:"icon,omitempty"`
	Date        uint32      `json:"date,omitempty"`
	Attributes  []Attribute `json:"attributes,omitempty"`
	Activity    *Activity   `json:"activity,omitempty"`
}

// Icon represents an icon
type Icon struct {
	URL string `json:"url"`
}

// Attribute represents an attribute on a card
type Attribute struct {
	Label *string        `json:"label,omitempty"`
	Value AttributeValue `json:"value"`
}

// AttributeValue represents the value of an attribute
type AttributeValue struct {
	Icon  string `json:"icon,omitempty"`
	Label string `json:"label,omitempty"`
	URL   string `json:"url,omitempty"`
}

// Activity represents an activity that occurred
type Activity struct {
	Icon string          `json:"icon,omitempty"`
	HTML json.RawMessage `json:"html,omitempty"`
}

// ShareFileRequest represents a HipChat room file share request.
type ShareFileRequest struct {
	Path     string `json:"path"`
	Filename string `json:"filename,omitempty"`
	Message  string `json:"message,omitempty"`
}

// HistoryRequest represents a HipChat room chat history request.
type HistoryRequest struct {
	Date       string `json:"date"`
	Timezone   string `json:"timezone"`
	StartIndex int    `json:"start-index"`
	MaxResults int    `json:"max-results"`
	Reverse    bool   `json:"reverse"`
}

// History represents a HipChat room chat history.
type History struct {
	Items      []Message `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Message represents a HipChat message.
type Message struct {
	Date          string      `json:"date"`
	From          interface{} `json:"from"` // string | obj <- weak
	Id            string      `json:"id"`
	Mentions      []User      `json:"mentions"`
	Message       string      `json:"message"`
	MessageFormat string      `json:"message_format"`
	Type          string      `json:"type"`
}

// SetTopicRequest represents a hipchat update topic request
type SetTopicRequest struct {
	Topic string `json:"topic"`
}

// InviteRequest represents a hipchat invite to room request
type InviteRequest struct {
	Reason string `json:"reason"`
}

// AddAttribute adds an attribute to a Card
func (c *Card) AddAttribute(mainLabel *string, subLabel, url, iconURL string) {
	attr := Attribute{Label: mainLabel}
	attr.Value = AttributeValue{Label: subLabel, URL: url, Icon: iconURL}

	c.Attributes = append(c.Attributes, attr)
}

// List returns all the rooms authorized.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_all_rooms
func (r *RoomService) List() (*Rooms, *http.Response, error) {
	req, err := r.client.NewRequest("GET", "room", nil)
	if err != nil {
		return nil, nil, err
	}

	rooms := new(Rooms)
	resp, err := r.client.Do(req, rooms)
	if err != nil {
		return nil, resp, err
	}
	return rooms, resp, nil
}

// Get returns the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_room
func (r *RoomService) Get(id string) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("GET", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Notification sends a notification to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/send_room_notification
func (r *RoomService) Notification(id string, notifReq *NotificationRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/notification", id), notifReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// ShareFile sends a file to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/share_file_with_room
func (r *RoomService) ShareFile(id string, shareFileReq *ShareFileRequest) (*http.Response, error) {
	req, err := r.client.NewFileUploadRequest("POST", fmt.Sprintf("room/%s/share/file", id), shareFileReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Create creates a new room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/create_room
func (r *RoomService) Create(roomReq *CreateRoomRequest) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("POST", "room", roomReq)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Delete deletes an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/delete_room
func (r *RoomService) Delete(id string) (*http.Response, error) {
	req, err := r.client.NewRequest("DELETE", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Update updates an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/update_room
func (r *RoomService) Update(id string, roomReq *UpdateRoomRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s", id), roomReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// History fetches a room's chat history.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/view_room_history
func (r *RoomService) History(id string, roomReq *HistoryRequest) (*History, *http.Response, error) {
	u := fmt.Sprintf("room/%s/history", id)
	// Form query parameters
	if roomReq != nil {
		p := url.Values{}
		if roomReq.Date != "" {
			p.Add("date", roomReq.Date)
		}
		if roomReq.Timezone != "" {
			p.Add("timezone", roomReq.Timezone)
		}
		if roomReq.StartIndex != 0 {
			p.Add("start-index", strconv.FormatInt(int64(roomReq.StartIndex), 10))
		}
		if roomReq.MaxResults != 0 {
			p.Add("max-results", strconv.FormatInt(int64(roomReq.MaxResults), 10))
		}
		// There's no way to tell whether caller set a boolean or not. We have to always set
		// it.
		p.Add("reverse", strconv.FormatBool(roomReq.Reverse))
		u += "?" + p.Encode()
	}
	req, err := r.client.NewRequest("GET", u, nil)
	h := new(History)
	resp, err := r.client.Do(req, &h)
	if err != nil {
		return nil, resp, err
	}
	return h, resp, nil
}

// Set Room topic.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/set_topic
func (r *RoomService) SetTopic(id string, topic string) (*http.Response, error) {
	topicReq := &SetTopicRequest{Topic: topic}

	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s/topic", id), topicReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Invite someone to the Room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/invite_user
func (r *RoomService) Invite(room string, user string, reason string) (*http.Response, error) {
	reasonReq := &InviteRequest{Reason: reason}

	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/invite/%s", room, user), reasonReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package gogitlab

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

type HookObjAttr struct {
	Id              int       `json:"id,omitempty"`
	Ref             string    `json:"ref,omitempty"`
	Tag             bool      `json:"tag,omitempty"`
	Sha             string    `json:"sha,omitempty"`
	BeforeSha       string    `json:"before_sha,omitempty"`
	Title           string    `json:"title,omitempty"`
	AssigneeId      int       `json:"assignee_id,omitempty"`
	AuthorId        int       `json:"author_id,omitempty"`
	ProjectId       int       `json:"project_id,omitempty"`
	Status          string    `json:"status,omitempty"`
	Stages          []string  `json:"stages,omitempty"`
	CreatedAt       time.Time `json:"created_at,omitempty"`
	UpdatedAt       time.Time `json:"updated_at,omitempty"`
	FinishedAt      time.Time `json:"finished_at,omitempty"`
	Duration        int       `json:"duration,omitempty"`
	Position        int       `json:"position,omitempty"`
	BranchName      string    `json:"branch_name,omitempty"`
	Description     string    `json:"description,omitempty"`
	MilestoneId     int       `json:"milestone_id,omitempty"`
	State           string    `json:"state,omitempty"`
	IId             int       `json:"iid,omitempty"`
	TargetBranch    string    `json:"target_branch,omitempty"`
	SourceBranch    string    `json:"source_branch,omitempty"`
	SourceProjectId int       `json:"source_project_id,omitempty"`
	StCommits       string    `json:"st_commits,omitempty"`
	StDiffs         string    `json:"st_diffs,omitempty"`
	MergeStatus     string    `json:"merge_status,omitempty"`
	TargetProjectId int       `json:"target_project_id,omitempty"`
}

type hRepository struct {
	Name        string `json:"name,omitempty"`
	URL         string `json:"url,omitempty"`
	Description string `json:"description,omitempty"`
	Homepage    string `json:"homepage,omitempty"`
}

type hProject struct {
	Project
	// Overwrite type *gogitlab.Namespace with type string,
	// otherwise the project hash passed for pipeline hooks is
	// identical
	Namespace string `json:"namespace,omitempty"`
}

type hCommit struct {
	Id        string    `json:"id,omitempty"`
	Message   string    `json:"message,omitempty"`
	Timestamp time.Time `json:"timestamp,omitempty"`
	URL       string    `json:"url,omitempty"`
	Author    *Person   `json:"author,omitempty"`
}

type HookPayload struct {
	Before            string       `json:"before,omitempty"`
	After             string       `json:"after,omitempty"`
	Ref               string       `json:"ref,omitempty"`
	UserId            int          `json:"user_id,omitempty"`
	UserName          string       `json:"user_name,omitempty"`
	ProjectId         int          `json:"project_id,omitempty"`
	Project           *hProject    `json:"project,omitempty"`
	Repository        *hRepository `json:"repository,omitempty"`
	Commits           []hCommit    `json:"commits,omitempty"`
	Commit            *hCommit     `json:"commit,omitempty"`
	TotalCommitsCount int          `json:"total_commits_count,omitempty"`
	ObjectKind        string       `json:"object_kind,omitempty"`
	ObjectAttributes  *HookObjAttr `json:"object_attributes,omitempty"`
}

// ParseHook parses hook payload from GitLab
func ParseHook(payload []byte) (*HookPayload, error) {
	hp := HookPayload{}
	if err := json.Unmarshal(payload, &hp); err != nil {
		return nil, err
	}

	// Basic sanity check
	switch {
	case len(hp.ObjectKind) == 0:
		// Assume this is a post-receive within repository
		if len(hp.After) == 0 {
			return nil, fmt.Errorf("Invalid hook received, commit hash not found.")
		}
	case hp.ObjectKind == "pipeline":
		fallthrough
	case hp.ObjectKind == "issue":
		fallthrough
	case hp.ObjectKind == "merge_request":
		if hp.ObjectAttributes == nil {
			return nil, fmt.Errorf("Invalid hook received, attributes not found.")
		}
	default:
		return nil, fmt.Errorf("Invalid hook received, payload format not recognized.")
	}

	return &hp, nil
}

// Branch returns current branch for pipeline and push event hook
// payload
// This function returns empty string for any other events
func (h *HookPayload) Branch() string {
	ref := h.Ref
	if h.ObjectAttributes != nil && len(h.ObjectAttributes.Ref) > 0 {
		ref = h.ObjectAttributes.Ref
	}

	return strings.TrimPrefix(ref, "refs/heads/")
}

// Head returns the latest changeset for push event hook payload
func (h *HookPayload) Head() hCommit {
	c := hCommit{}
	for _, cm := range h.Commits {
		if h.After == cm.Id {
			return cm
		}
	}
	return c
}
<commit_msg>Add builds to payload struct for pipeline payloads<commit_after>package gogitlab

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

type HookObjAttr struct {
	Id              int       `json:"id,omitempty"`
	Ref             string    `json:"ref,omitempty"`
	Tag             bool      `json:"tag,omitempty"`
	Sha             string    `json:"sha,omitempty"`
	BeforeSha       string    `json:"before_sha,omitempty"`
	Title           string    `json:"title,omitempty"`
	AssigneeId      int       `json:"assignee_id,omitempty"`
	AuthorId        int       `json:"author_id,omitempty"`
	ProjectId       int       `json:"project_id,omitempty"`
	Status          string    `json:"status,omitempty"`
	Stages          []string  `json:"stages,omitempty"`
	CreatedAt       time.Time `json:"created_at,omitempty"`
	UpdatedAt       time.Time `json:"updated_at,omitempty"`
	FinishedAt      time.Time `json:"finished_at,omitempty"`
	Duration        int       `json:"duration,omitempty"`
	Position        int       `json:"position,omitempty"`
	BranchName      string    `json:"branch_name,omitempty"`
	Description     string    `json:"description,omitempty"`
	MilestoneId     int       `json:"milestone_id,omitempty"`
	State           string    `json:"state,omitempty"`
	IId             int       `json:"iid,omitempty"`
	TargetBranch    string    `json:"target_branch,omitempty"`
	SourceBranch    string    `json:"source_branch,omitempty"`
	SourceProjectId int       `json:"source_project_id,omitempty"`
	StCommits       string    `json:"st_commits,omitempty"`
	StDiffs         string    `json:"st_diffs,omitempty"`
	MergeStatus     string    `json:"merge_status,omitempty"`
	TargetProjectId int       `json:"target_project_id,omitempty"`
}

type hRepository struct {
	Name        string `json:"name,omitempty"`
	URL         string `json:"url,omitempty"`
	Description string `json:"description,omitempty"`
	Homepage    string `json:"homepage,omitempty"`
}

type hProject struct {
	Project
	// Overwrite type *gogitlab.Namespace with type string,
	// otherwise the project hash passed for pipeline hooks is
	// identical
	Namespace string `json:"namespace,omitempty"`
}

type hCommit struct {
	Id        string    `json:"id,omitempty"`
	Message   string    `json:"message,omitempty"`
	Timestamp time.Time `json:"timestamp,omitempty"`
	URL       string    `json:"url,omitempty"`
	Author    *Person   `json:"author,omitempty"`
}

type HookPayload struct {
	Before            string       `json:"before,omitempty"`
	After             string       `json:"after,omitempty"`
	Ref               string       `json:"ref,omitempty"`
	UserId            int          `json:"user_id,omitempty"`
	UserName          string       `json:"user_name,omitempty"`
	ProjectId         int          `json:"project_id,omitempty"`
	Project           *hProject    `json:"project,omitempty"`
	Repository        *hRepository `json:"repository,omitempty"`
	Commits           []hCommit    `json:"commits,omitempty"`
	Commit            *hCommit     `json:"commit,omitempty"`
	TotalCommitsCount int          `json:"total_commits_count,omitempty"`
	ObjectKind        string       `json:"object_kind,omitempty"`
	ObjectAttributes  *HookObjAttr `json:"object_attributes,omitempty"`
	Builds            []*Build     `json:"builds,omitempty"`
}

// ParseHook parses hook payload from GitLab
func ParseHook(payload []byte) (*HookPayload, error) {
	hp := HookPayload{}
	if err := json.Unmarshal(payload, &hp); err != nil {
		return nil, err
	}

	// Basic sanity check
	switch {
	case len(hp.ObjectKind) == 0:
		// Assume this is a post-receive within repository
		if len(hp.After) == 0 {
			return nil, fmt.Errorf("Invalid hook received, commit hash not found.")
		}
	case hp.ObjectKind == "pipeline":
		fallthrough
	case hp.ObjectKind == "issue":
		fallthrough
	case hp.ObjectKind == "merge_request":
		if hp.ObjectAttributes == nil {
			return nil, fmt.Errorf("Invalid hook received, attributes not found.")
		}
	default:
		return nil, fmt.Errorf("Invalid hook received, payload format not recognized.")
	}

	return &hp, nil
}

// Branch returns current branch for pipeline and push event hook
// payload
// This function returns empty string for any other events
func (h *HookPayload) Branch() string {
	ref := h.Ref
	if h.ObjectAttributes != nil && len(h.ObjectAttributes.Ref) > 0 {
		ref = h.ObjectAttributes.Ref
	}

	return strings.TrimPrefix(ref, "refs/heads/")
}

// Head returns the latest changeset for push event hook payload
func (h *HookPayload) Head() hCommit {
	c := hCommit{}
	for _, cm := range h.Commits {
		if h.After == cm.Id {
			return cm
		}
	}
	return c
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strconv"
	"net/http"
	"log"
	"time"
	"html/template"
	"encoding/json"
	"bytes"
	"os"
	"github.com/bradrydzewski/go.auth"
)

var templates *template.Template
var homeTemplate *template.Template

func logHandler(w http.ResponseWriter, r *http.Request) {
	m := validLog.FindStringSubmatch(r.URL.Path)
	if len(m) == 0 {
		http.Error(w, "Sensor not found", http.StatusNotFound)
		return
	}
	debug("Sensor " + m[1] + " sent value " + m[2])
	tnew := time.Now()
	if m[4] != "" {
		t, _ := strconv.ParseInt(m[5], 10, 64)
		if m[4] == "t" {
			tnew = time.Unix(t, 0)
		} else { // m[4] == "s"
			tnew = time.Unix(time.Now().Unix() - t, 0)
		}
	}
	// From down here there is a bit of duplication with sendRemoteReading(). Remember to change both if needed.
	if ! d.Exists(m[1]) { // Remove when you add code to add/delete sensors instead of adding them automatically.
		h.Pipe <- Hometicker{"Unknown sensor: " + m[1], "fa-times-circle", "danger",
			"Sensor " + m[1] + " isn't registered. Ignored."}
		// h.Pipe <- Hometicker{"New sensor: " + m[1], "fa-check-circle", "success",
		//	"Sensor " + m[1] + " succesfully added."}
		// d.AddT(m[1], tnew) // This is not needed. Sensors are added automatically upon first reading. It is here only to make the next command to work.
		// sensorList()
		// latlonList()
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
	}
	// We can't check this with rrd cache. We do it though on database flush.
	// _, told := d.Last(m[1])
	// if ! tnew.After(told) {
	// 	http.Error(w, "Sensor send out of order timestamp", http.StatusNotFound)
	// 	h.Pipe <- Hometicker{m[1] + ": out of order reading", "fa-times-circle", "danger",
	// 		m[1] + " sent out of order value " + m[2] + " at " + tnew.String() + ". Ignored."}
	// 	return
	// }

	d.StoreT(m[1], m[2], tnew)
	srC.Pipe <- remoteReading{m[1], m[2], tnew}
	t := d.Info(m[1]).Name
	h.Pipe <- Hometicker{"" + t + ": new reading", "fa-plus-circle", "info",
		t + " sent value " + m[2] + " at " + tnew.String() + ""}
	sh.Pipe <- m[1]
	fmt.Fprintf(w, "ok")
}

func queryHandler(w http.ResponseWriter, r *http.Request) {
	m := validQuery.FindStringSubmatch(r.URL.Path)
        if len(m) == 0 {
                http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	if ! d.Exists(m[1]) {
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	debugln("Query for sensor " + m[1])
	a, _ := json.Marshal(d.Load(m[1]))
	fmt.Fprintf(w, string(a))
}

func reloadHandler(w http.ResponseWriter, r *http.Request) {
	loadTemplates()
	fmt.Fprintf(w, "templates reloaded")
}

var SensorList template.HTML

func sensorList() { // When we add/remove sensors manually, make this run once and store its value for performance?
	// This is nice. Initially I used a string instead of a buffer. But concatenating huge strings is slow.
	// So, when testing with 1.000.000 sensors the code needed many minutes (maybe hour?) to enumerate the
	// sensors. Now it takes 1 second!!!
	var buffer bytes.Buffer
	for _, s := range d.List() {
		buffer.WriteString(`

                   <a href="/view/` + s + `">` + d.Info(s).Name + `

`)
	}
	SensorList = template.HTML(buffer.String())
}

type HomePage struct {
	Title string
        LoginInfo template.HTML
	SensorList template.HTML
	CustomScript template.HTML
	LatLonList template.JS
}

const homeCustomScript = template.HTML(`

`)

func userMenu(u auth.User) template.HTML {
	if u != nil {
		return template.HTML(`

<a href="` + u.Link() + `"><img class="img-rounded" height="50px" src="`+ u.Picture() +`" /> ` + u.Name() + `

 Logout

`)
	} else {
		return template.HTML(`

 Login
`)
	}
}

func homeHandler(w http.ResponseWriter, r *http.Request, u auth.User) {
	err := templates.ExecuteTemplate(w,
		"home.html",
		HomePage{"Datanomics™ alpha", userMenu(u), SensorList, homeCustomScript, LatLonList})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
	}
}

var LatLonList template.JS

func latlonList() {
	var buffer bytes.Buffer
	buffer.WriteString("var Locs = [")
	for _, s := range d.List() {
		v := d.Info(s)
		buffer.WriteString("{lat:"+ strconv.FormatFloat(v.Lat, 'f', -1, 64) + ", lon:" + strconv.FormatFloat(v.Lon, 'f', 1, 64) + ", title:'" + v.Name + "'},")
	}
	buffer.Truncate(buffer.Len() - 1)
	buffer.WriteString("];")
	LatLonList = template.JS(buffer.String())
}

type ViewPage struct {
	Title string
	LoginInfo template.HTML
	Sensor string
	Data template.JS
	SensorList template.HTML
	CustomScript template.HTML
}

const viewCustomScript = template.HTML(`

    <link rel="stylesheet" id="themeCSS" href="/assets/js/plugins/jqrangeslider/css/classic-min.css">

`)

func viewHandler(w http.ResponseWriter, r *http.Request, u auth.User) {
	m := validView.FindStringSubmatch(r.URL.Path)
        if ! d.Exists(m[1]) {
		err := templates.ExecuteTemplate(w,
			"sensor.html",
			ViewPage{"Datanomics™ alpha | Sensor not found", userMenu(u), "Error", "Sensor not found", SensorList, viewCustomScript})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	} else {
		//d.Graph(m[1])
		//a, _ := json.Marshal(d.Load(m[1]))
		s := d.Load(m[1])
		s += "; var sensorID = '" + m[1] + "';"
		n := d.Info(m[1]).Name
		err := templates.ExecuteTemplate(w,
                        "sensor.html",
                        ViewPage{"Datanomics™ alpha | " + n, userMenu(u), n, template.JS(s), SensorList, viewCustomScript})
                if err != nil {
                        http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

func queryInfoHandler(w http.ResponseWriter, r *http.Request) {
	m := validInfoQuery.FindStringSubmatch(r.URL.Path)
        if len(m) == 0 {
                http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	if ! d.Exists(m[1]) {
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	debugln("Query for sensor " + m[1])
	a, _ := json.Marshal(d.Info(m[1]))
	fmt.Fprintf(w, string(a))
}

type ServerStats struct {
	Sensors int
	OpenSensors int
	RemoteServers []string
	WebsocketClientsHome int
	WebsocketClientsSensors int
	DatabaseSize int64
}

func statsHandler(w http.ResponseWriter, r *http.Request) {
	var wsch = 0
	for i := range h.Connections {
		if h.Connections[i] == true {
			wsch++
		}
	}
	var wscc = 0
	for i := range sh.Connections {
		wscc += len(sh.Connections[i])
	}
	dbdir, _ := os.Open(sensorDataDir)
	dbfiles, _ := dbdir.Readdir(-1)
	var ds int64
	for _, i := range dbfiles {
		ds += i.Size()
	}
	t := ServerStats{d.Count(), d.OpenCount(), remoteServers, wsch, wscc, ds}
	a, _ := json.Marshal(t)
	fmt.Fprintf(w, string(a))
}

func logOutHandler(w http.ResponseWriter, r *http.Request) {
	auth.DeleteUserCookie(w, r)
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "login.html", nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
	}
}

<commit_msg>TFIX: Longitude had wrong (one digit only) precision.<commit_after>package main

import (
	"fmt"
	"strconv"
	"net/http"
	"log"
	"time"
	"html/template"
	"encoding/json"
	"bytes"
	"os"
	"github.com/bradrydzewski/go.auth"
)

var templates *template.Template
var homeTemplate *template.Template

func logHandler(w http.ResponseWriter, r *http.Request) {
	m := validLog.FindStringSubmatch(r.URL.Path)
	if len(m) == 0 {
		http.Error(w, "Sensor not found", http.StatusNotFound)
		return
	}
	debug("Sensor " + m[1] + " sent value " + m[2])
	tnew := time.Now()
	if m[4] != "" {
		t, _ := strconv.ParseInt(m[5], 10, 64)
		if m[4] == "t" {
			tnew = time.Unix(t, 0)
		} else { // m[4] == "s"
			tnew = time.Unix(time.Now().Unix() - t, 0)
		}
	}
	// From down here there is a bit of duplication with sendRemoteReading(). Remember to change both if needed.
	if ! d.Exists(m[1]) { // Remove when you add code to add/delete sensors instead of adding them automatically.
		h.Pipe <- Hometicker{"Unknown sensor: " + m[1], "fa-times-circle", "danger",
			"Sensor " + m[1] + " isn't registered. Ignored."}
		// h.Pipe <- Hometicker{"New sensor: " + m[1], "fa-check-circle", "success",
		//	"Sensor " + m[1] + " succesfully added."}
		// d.AddT(m[1], tnew) // This is not needed. Sensors are added automatically upon first reading. It is here only to make the next command to work.
		// sensorList()
		// latlonList()
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
	}
	// We can't check this with rrd cache. We do it though on database flush.
	// _, told := d.Last(m[1])
	// if ! tnew.After(told) {
	// 	http.Error(w, "Sensor send out of order timestamp", http.StatusNotFound)
	// 	h.Pipe <- Hometicker{m[1] + ": out of order reading", "fa-times-circle", "danger",
	// 		m[1] + " sent out of order value " + m[2] + " at " + tnew.String() + ". Ignored."}
	// 	return
	// }

	d.StoreT(m[1], m[2], tnew)
	srC.Pipe <- remoteReading{m[1], m[2], tnew}
	t := d.Info(m[1]).Name
	h.Pipe <- Hometicker{"" + t + ": new reading", "fa-plus-circle", "info",
		t + " sent value " + m[2] + " at " + tnew.String() + ""}
	sh.Pipe <- m[1]
	fmt.Fprintf(w, "ok")
}

func queryHandler(w http.ResponseWriter, r *http.Request) {
	m := validQuery.FindStringSubmatch(r.URL.Path)
        if len(m) == 0 {
                http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	if ! d.Exists(m[1]) {
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	debugln("Query for sensor " + m[1])
	a, _ := json.Marshal(d.Load(m[1]))
	fmt.Fprintf(w, string(a))
}

func reloadHandler(w http.ResponseWriter, r *http.Request) {
	loadTemplates()
	fmt.Fprintf(w, "templates reloaded")
}

var SensorList template.HTML

func sensorList() { // When we add/remove sensors manually, make this run once and store its value for performance?
	// This is nice. Initially I used a string instead of a buffer. But concatenating huge strings is slow.
	// So, when testing with 1.000.000 sensors the code needed many minutes (maybe hour?) to enumerate the
	// sensors. Now it takes 1 second!!!
	var buffer bytes.Buffer
	for _, s := range d.List() {
		buffer.WriteString(`

                   <a href="/view/` + s + `">` + d.Info(s).Name + `

`)
	}
	SensorList = template.HTML(buffer.String())
}

type HomePage struct {
	Title string
        LoginInfo template.HTML
	SensorList template.HTML
	CustomScript template.HTML
	LatLonList template.JS
}

const homeCustomScript = template.HTML(`

`)

func userMenu(u auth.User) template.HTML {
	if u != nil {
		return template.HTML(`

<a href="` + u.Link() + `"><img class="img-rounded" height="50px" src="`+ u.Picture() +`" /> ` + u.Name() + `

 Logout

`)
	} else {
		return template.HTML(`

 Login
`)
	}
}

func homeHandler(w http.ResponseWriter, r *http.Request, u auth.User) {
	err := templates.ExecuteTemplate(w,
		"home.html",
		HomePage{"Datanomics™ alpha", userMenu(u), SensorList, homeCustomScript, LatLonList})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
	}
}

var LatLonList template.JS

func latlonList() {
	var buffer bytes.Buffer
	buffer.WriteString("var Locs = [")
	for _, s := range d.List() {
		v := d.Info(s)
		buffer.WriteString("{lat:"+ strconv.FormatFloat(v.Lat, 'f', -1, 64) + ", lon:" + strconv.FormatFloat(v.Lon, 'f', -1, 64) + ", title:'" + v.Name + "'},")
	}
	buffer.Truncate(buffer.Len() - 1)
	buffer.WriteString("];")
	LatLonList = template.JS(buffer.String())
}

type ViewPage struct {
	Title string
	LoginInfo template.HTML
	Sensor string
	Data template.JS
	SensorList template.HTML
	CustomScript template.HTML
}

const viewCustomScript = template.HTML(`

    <link rel="stylesheet" id="themeCSS" href="/assets/js/plugins/jqrangeslider/css/classic-min.css">

`)

func viewHandler(w http.ResponseWriter, r *http.Request, u auth.User) {
	m := validView.FindStringSubmatch(r.URL.Path)
        if ! d.Exists(m[1]) {
		err := templates.ExecuteTemplate(w,
			"sensor.html",
			ViewPage{"Datanomics™ alpha | Sensor not found", userMenu(u), "Error", "Sensor not found", SensorList, viewCustomScript})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	} else {
		//d.Graph(m[1])
		//a, _ := json.Marshal(d.Load(m[1]))
		s := d.Load(m[1])
		s += "; var sensorID = '" + m[1] + "';"
		n := d.Info(m[1]).Name
		err := templates.ExecuteTemplate(w,
                        "sensor.html",
                        ViewPage{"Datanomics™ alpha | " + n, userMenu(u), n, template.JS(s), SensorList, viewCustomScript})
                if err != nil {
                        http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

func queryInfoHandler(w http.ResponseWriter, r *http.Request) {
	m := validInfoQuery.FindStringSubmatch(r.URL.Path)
        if len(m) == 0 {
                http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	if ! d.Exists(m[1]) {
		http.Error(w, "Sensor not found", http.StatusNotFound)
                return
        }
	debugln("Query for sensor " + m[1])
	a, _ := json.Marshal(d.Info(m[1]))
	fmt.Fprintf(w, string(a))
}

type ServerStats struct {
	Sensors int
	OpenSensors int
	RemoteServers []string
	WebsocketClientsHome int
	WebsocketClientsSensors int
	DatabaseSize int64
}

func statsHandler(w http.ResponseWriter, r *http.Request) {
	var wsch = 0
	for i := range h.Connections {
		if h.Connections[i] == true {
			wsch++
		}
	}
	var wscc = 0
	for i := range sh.Connections {
		wscc += len(sh.Connections[i])
	}
	dbdir, _ := os.Open(sensorDataDir)
	dbfiles, _ := dbdir.Readdir(-1)
	var ds int64
	for _, i := range dbfiles {
		ds += i.Size()
	}
	t := ServerStats{d.Count(), d.OpenCount(), remoteServers, wsch, wscc, ds}
	a, _ := json.Marshal(t)
	fmt.Fprintf(w, string(a))
}

func logOutHandler(w http.ResponseWriter, r *http.Request) {
	auth.DeleteUserCookie(w, r)
	http.Redirect(w, r, "/", http.StatusSeeOther)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	err := templates.ExecuteTemplate(w, "login.html", nil)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Println(err)
	}
}

<|endoftext|>


<commit_before>package delmo

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	config := `
system:
  name: test
  file: docker-compose.yml

tasks:
- name: redis_is_running
  image: redis
  run:
    path: echo
    args: [hello, world]

tests:
- name: simple
  spec:
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis
    build: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", config, compose, t)
	defer os.Remove(tmpDir)

	suite, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", suite.System.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	service, ok := suite.System.Services["redis"]
	if !ok {
		t.Errorf("Compose file not read correctly. Missing service %s", "redis")
	}
	if want, got := "redis", service.Image; want != got {
		t.Errorf("Image not set correctly. Want: %s, got: %s", want, got)
	}
	if want, got := 2, len(suite.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<commit_msg>Fix test<commit_after>package delmo

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

func writeFiles(name, config, compose string, t *testing.T) (string, string) {
	tmpDir, err := ioutil.TempDir("", name)
	if err != nil {
		t.Fatal("Couldn't create temp dir", err)
	}

	configFile := fmt.Sprintf("%s/%s", tmpDir, "delmo.yml")
	err = ioutil.WriteFile(configFile, []byte(config), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write config", err)
	}
	t.Logf("Written config file %s", configFile)

	composeFile := fmt.Sprintf("%s/%s", tmpDir, "docker-compose.yml")
	err = ioutil.WriteFile(composeFile, []byte(compose), os.ModePerm)
	if err != nil {
		t.Fatal("Couldn't write compose", err)
	}
	t.Logf("Written compose files %s", composeFile)

	return tmpDir, configFile
}

func TestConfig_Load(t *testing.T) {
	t.Parallel()

	config := `
suite:
  name: test
  file: docker-compose.yml

tasks:
- name: redis_is_running
  image: redis
  run:
    path: echo
    args: [hello, world]

tests:
- name: simple
  spec:
  - {stop: [redis]}
  - {start: [redis]}`

	compose := `
version: '2'
services:
  redis:
    image: redis
    build: redis`

	tmpDir, configFile := writeFiles("TestSuite_Load", config, compose, t)
	defer os.Remove(tmpDir)

	suite, err := LoadConfig(configFile)
	if err != nil {
		t.Fatal("Load Suite Failed!", err)
	}

	if want, got := "test", suite.System.Name; want != got {
		t.Errorf("Name not correct. Want: %s, got: %s", want, got)
	}

	service, ok := suite.System.Services["redis"]
	if !ok {
		t.Errorf("Compose file not read correctly. Missing service %s", "redis")
	}
	if want, got := "redis", service.Image; want != got {
		t.Errorf("Image not set correctly. Want: %s, got: %s", want, got)
	}
	if want, got := 2, len(suite.Tests[0].Spec); want != got {
		t.Errorf("Spec not parsed correctly. Want: %d, got: %d", want, got)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"net"
	"sync"
	"time"

	"github.com/awslabs/aws-sdk-go/aws/credentials"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
	// TODO: Move the validation to this, requires conditional schemas
	// TODO: Move the configuration to this, requires validation

	// These variables are closed within the `getCreds` function below.
	// This function is responsible for reading credentials from the
	// environment in the case that they're not explicitly specified
	// in the Terraform configuration.
	//
	// By using the getCreds function here instead of making the default
	// empty, we avoid asking for input on credentials if they're available
	// in the environment.
	var credVal credentials.Value
	var credErr error
	var once sync.Once
	getCreds := func() {
		// Build the list of providers to look for creds in
		providers := []credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{},
		}

		// We only look in the EC2 metadata API if we can connect
		// to the metadata service within a reasonable amount of time
		conn, err := net.DialTimeout("tcp", "169.254.169.254:80", 100*time.Millisecond)
		if err == nil {
			conn.Close()
			providers = append(providers, &credentials.EC2RoleProvider{})
		}

		credVal, credErr = credentials.NewChainCredentials(providers).Get()

		// If we didn't successfully find any credentials, just
		// set the error to nil.
		if credErr == credentials.ErrNoValidProvidersFoundInChain {
			credErr = nil
		}
	}

	// getCredDefault is a function used by DefaultFunc below to
	// get the default value for various parts of the credentials.
	// This function properly handles loading the credentials, checking
	// for errors, etc.
	getCredDefault := func(def interface{}, f func() string) (interface{}, error) {
		once.Do(getCreds)

		// If there was an error, that is always first
		if credErr != nil {
			return nil, credErr
		}

		// If the value is empty string, return nil (not set)
		val := f()
		if val == "" {
			return def, nil
		}

		return val, nil
	}

	// The actual provider
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"access_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault(nil, func() string {
						return credVal.AccessKeyID
					})
				},
				Description: descriptions["access_key"],
			},

			"secret_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault(nil, func() string {
						return credVal.SecretAccessKey
					})
				},
				Description: descriptions["secret_key"],
			},

			"token": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault("", func() string {
						return credVal.SessionToken
					})
				},
				Description: descriptions["token"],
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
					"AWS_REGION",
					"AWS_DEFAULT_REGION",
				}, nil),
				Description:  descriptions["region"],
				InputDefault: "us-east-1",
			},

			"max_retries": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Default:     11,
				Description: descriptions["max_retries"],
			},

			"allowed_account_ids": &schema.Schema{
				Type:          schema.TypeSet,
				Elem:          &schema.Schema{Type: schema.TypeString},
				Optional:      true,
				ConflictsWith: []string{"forbidden_account_ids"},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"forbidden_account_ids": &schema.Schema{
				Type:          schema.TypeSet,
				Elem:          &schema.Schema{Type: schema.TypeString},
				Optional:      true,
				ConflictsWith: []string{"allowed_account_ids"},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"dynamodb_endpoint": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Default:     "",
				Description: descriptions["dynamodb_endpoint"],
			},
		},

		ResourcesMap: map[string]*schema.Resource{
			"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
			"aws_autoscaling_group":            resourceAwsAutoscalingGroup(),
			"aws_autoscaling_notification":     resourceAwsAutoscalingNotification(),
			"aws_autoscaling_policy":           resourceAwsAutoscalingPolicy(),
			"aws_cloudwatch_metric_alarm":      resourceAwsCloudWatchMetricAlarm(),
			"aws_customer_gateway":             resourceAwsCustomerGateway(),
			"aws_db_instance":                  resourceAwsDbInstance(),
			"aws_db_parameter_group":           resourceAwsDbParameterGroup(),
			"aws_db_security_group":            resourceAwsDbSecurityGroup(),
			"aws_db_subnet_group":              resourceAwsDbSubnetGroup(),
			"aws_dynamodb_table":               resourceAwsDynamoDbTable(),
			"aws_ebs_volume":                   resourceAwsEbsVolume(),
			"aws_ecs_cluster":                  resourceAwsEcsCluster(),
			"aws_ecs_service":                  resourceAwsEcsService(),
			"aws_ecs_task_definition":          resourceAwsEcsTaskDefinition(),
			"aws_eip":                          resourceAwsEip(),
			"aws_elasticache_cluster":          resourceAwsElasticacheCluster(),
			"aws_elasticache_parameter_group":  resourceAwsElasticacheParameterGroup(),
			"aws_elasticache_security_group":   resourceAwsElasticacheSecurityGroup(),
			"aws_elasticache_subnet_group":     resourceAwsElasticacheSubnetGroup(),
			"aws_elb":                          resourceAwsElb(),
			"aws_flow_log":                     resourceAwsFlowLog(),
			"aws_iam_access_key":               resourceAwsIamAccessKey(),
			"aws_iam_group_policy":             resourceAwsIamGroupPolicy(),
			"aws_iam_group":                    resourceAwsIamGroup(),
			"aws_iam_group_membership":         resourceAwsIamGroupMembership(),
			"aws_iam_instance_profile":         resourceAwsIamInstanceProfile(),
			"aws_iam_policy":                   resourceAwsIamPolicy(),
			"aws_iam_policy_attachment":        resourceAwsIamPolicyAttachment(),
			"aws_iam_role_policy":              resourceAwsIamRolePolicy(),
			"aws_iam_role":                     resourceAwsIamRole(),
			"aws_iam_server_certificate":       resourceAwsIAMServerCertificate(),
			"aws_iam_user_policy":              resourceAwsIamUserPolicy(),
			"aws_iam_user":                     resourceAwsIamUser(),
			"aws_instance":                     resourceAwsInstance(),
			"aws_internet_gateway":             resourceAwsInternetGateway(),
			"aws_key_pair":                     resourceAwsKeyPair(),
			"aws_kinesis_stream":               resourceAwsKinesisStream(),
			"aws_lambda_function":              resourceAwsLambdaFunction(),
			"aws_launch_configuration":         resourceAwsLaunchConfiguration(),
			"aws_lb_cookie_stickiness_policy":  resourceAwsLBCookieStickinessPolicy(),
			"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
			"aws_network_acl":                  resourceAwsNetworkAcl(),
			"aws_network_interface":            resourceAwsNetworkInterface(),
			"aws_proxy_protocol_policy":        resourceAwsProxyProtocolPolicy(),
			"aws_route53_delegation_set":       resourceAwsRoute53DelegationSet(),
			"aws_route53_record":               resourceAwsRoute53Record(),
			"aws_route53_zone_association":     resourceAwsRoute53ZoneAssociation(),
			"aws_route53_zone":                 resourceAwsRoute53Zone(),
			"aws_route53_health_check":         resourceAwsRoute53HealthCheck(),
			"aws_route_table":                  resourceAwsRouteTable(),
			"aws_route_table_association":      resourceAwsRouteTableAssociation(),
			"aws_s3_bucket":                    resourceAwsS3Bucket(),
			"aws_security_group":               resourceAwsSecurityGroup(),
			"aws_security_group_rule":          resourceAwsSecurityGroupRule(),
			"aws_spot_instance_request":        resourceAwsSpotInstanceRequest(),
			"aws_sqs_queue":                    resourceAwsSqsQueue(),
			"aws_sns_topic":                    resourceAwsSnsTopic(),
			"aws_sns_topic_subscription":       resourceAwsSnsTopicSubscription(),
			"aws_subnet":                       resourceAwsSubnet(),
			"aws_volume_attachment":            resourceAwsVolumeAttachment(),
			"aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(),
			"aws_vpc_dhcp_options":             resourceAwsVpcDhcpOptions(),
			"aws_vpc_peering_connection":       resourceAwsVpcPeeringConnection(),
			"aws_vpc":                          resourceAwsVpc(),
			"aws_vpc_endpoint":                 resourceAwsVpcEndpoint(),
			"aws_vpn_connection":               resourceAwsVpnConnection(),
			"aws_vpn_connection_route":         resourceAwsVpnConnectionRoute(),
			"aws_vpn_gateway":                  resourceAwsVpnGateway(),
		},

		ConfigureFunc: providerConfigure,
	}
}

var descriptions map[string]string

func init() {
	descriptions = map[string]string{
		"region": "The region where AWS operations will take place. Examples\n" +
			"are us-east-1, us-west-2, etc.",

		"access_key": "The access key for API operations. You can retrieve this\n" +
			"from the 'Security & Credentials' section of the AWS console.",

		"secret_key": "The secret key for API operations. You can retrieve this\n" +
			"from the 'Security & Credentials' section of the AWS console.",

		"token": "session token. A session token is only required if you are\n" +
			"using temporary security credentials.",

		"max_retries": "The maximum number of times an AWS API request is\n" +
			"being executed. If the API request still fails, an error is\n" +
			"thrown.",

		"dynamodb_endpoint": "Use this to override the default endpoint URL constructed from the :region.\n" +
			"It's typically used to connect to dynamodb-local.",
	}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
	config := Config{
		AccessKey:        d.Get("access_key").(string),
		SecretKey:        d.Get("secret_key").(string),
		Token:            d.Get("token").(string),
		Region:           d.Get("region").(string),
		MaxRetries:       d.Get("max_retries").(int),
		DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
	}

	if v, ok := d.GetOk("allowed_account_ids"); ok {
		config.AllowedAccountIds = v.(*schema.Set).List()
	}

	if v, ok := d.GetOk("forbidden_account_ids"); ok {
		config.ForbiddenAccountIds = v.(*schema.Set).List()
	}

	return config.Client()
}
<commit_msg>dynamodb-local Use ` instead of : to refer region to keep the consistency with the provider docs<commit_after>package aws

import (
	"net"
	"sync"
	"time"

	"github.com/awslabs/aws-sdk-go/aws/credentials"
	"github.com/hashicorp/terraform/helper/hashcode"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
	// TODO: Move the validation to this, requires conditional schemas
	// TODO: Move the configuration to this, requires validation

	// These variables are closed within the `getCreds` function below.
	// This function is responsible for reading credentials from the
	// environment in the case that they're not explicitly specified
	// in the Terraform configuration.
	//
	// By using the getCreds function here instead of making the default
	// empty, we avoid asking for input on credentials if they're available
	// in the environment.
	var credVal credentials.Value
	var credErr error
	var once sync.Once
	getCreds := func() {
		// Build the list of providers to look for creds in
		providers := []credentials.Provider{
			&credentials.EnvProvider{},
			&credentials.SharedCredentialsProvider{},
		}

		// We only look in the EC2 metadata API if we can connect
		// to the metadata service within a reasonable amount of time
		conn, err := net.DialTimeout("tcp", "169.254.169.254:80", 100*time.Millisecond)
		if err == nil {
			conn.Close()
			providers = append(providers, &credentials.EC2RoleProvider{})
		}

		credVal, credErr = credentials.NewChainCredentials(providers).Get()

		// If we didn't successfully find any credentials, just
		// set the error to nil.
		if credErr == credentials.ErrNoValidProvidersFoundInChain {
			credErr = nil
		}
	}

	// getCredDefault is a function used by DefaultFunc below to
	// get the default value for various parts of the credentials.
	// This function properly handles loading the credentials, checking
	// for errors, etc.
	getCredDefault := func(def interface{}, f func() string) (interface{}, error) {
		once.Do(getCreds)

		// If there was an error, that is always first
		if credErr != nil {
			return nil, credErr
		}

		// If the value is empty string, return nil (not set)
		val := f()
		if val == "" {
			return def, nil
		}

		return val, nil
	}

	// The actual provider
	return &schema.Provider{
		Schema: map[string]*schema.Schema{
			"access_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault(nil, func() string {
						return credVal.AccessKeyID
					})
				},
				Description: descriptions["access_key"],
			},

			"secret_key": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault(nil, func() string {
						return credVal.SecretAccessKey
					})
				},
				Description: descriptions["secret_key"],
			},

			"token": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				DefaultFunc: func() (interface{}, error) {
					return getCredDefault("", func() string {
						return credVal.SessionToken
					})
				},
				Description: descriptions["token"],
			},

			"region": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
					"AWS_REGION",
					"AWS_DEFAULT_REGION",
				}, nil),
				Description:  descriptions["region"],
				InputDefault: "us-east-1",
			},

			"max_retries": &schema.Schema{
				Type:        schema.TypeInt,
				Optional:    true,
				Default:     11,
				Description: descriptions["max_retries"],
			},

			"allowed_account_ids": &schema.Schema{
				Type:          schema.TypeSet,
				Elem:          &schema.Schema{Type: schema.TypeString},
				Optional:      true,
				ConflictsWith: []string{"forbidden_account_ids"},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"forbidden_account_ids": &schema.Schema{
				Type:          schema.TypeSet,
				Elem:          &schema.Schema{Type: schema.TypeString},
				Optional:      true,
				ConflictsWith: []string{"allowed_account_ids"},
				Set: func(v interface{}) int {
					return hashcode.String(v.(string))
				},
			},

			"dynamodb_endpoint": &schema.Schema{
				Type:        schema.TypeString,
				Optional:    true,
				Default:     "",
				Description: descriptions["dynamodb_endpoint"],
			},
		},

		ResourcesMap: map[string]*schema.Resource{
			"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
			"aws_autoscaling_group":            resourceAwsAutoscalingGroup(),
			"aws_autoscaling_notification":     resourceAwsAutoscalingNotification(),
			"aws_autoscaling_policy":           resourceAwsAutoscalingPolicy(),
			"aws_cloudwatch_metric_alarm":      resourceAwsCloudWatchMetricAlarm(),
			"aws_customer_gateway":             resourceAwsCustomerGateway(),
			"aws_db_instance":                  resourceAwsDbInstance(),
			"aws_db_parameter_group":           resourceAwsDbParameterGroup(),
			"aws_db_security_group":            resourceAwsDbSecurityGroup(),
			"aws_db_subnet_group":              resourceAwsDbSubnetGroup(),
			"aws_dynamodb_table":               resourceAwsDynamoDbTable(),
			"aws_ebs_volume":                   resourceAwsEbsVolume(),
			"aws_ecs_cluster":                  resourceAwsEcsCluster(),
			"aws_ecs_service":                  resourceAwsEcsService(),
			"aws_ecs_task_definition":          resourceAwsEcsTaskDefinition(),
			"aws_eip":                          resourceAwsEip(),
			"aws_elasticache_cluster":          resourceAwsElasticacheCluster(),
			"aws_elasticache_parameter_group":  resourceAwsElasticacheParameterGroup(),
			"aws_elasticache_security_group":   resourceAwsElasticacheSecurityGroup(),
			"aws_elasticache_subnet_group":     resourceAwsElasticacheSubnetGroup(),
			"aws_elb":                          resourceAwsElb(),
			"aws_flow_log":                     resourceAwsFlowLog(),
			"aws_iam_access_key":               resourceAwsIamAccessKey(),
			"aws_iam_group_policy":             resourceAwsIamGroupPolicy(),
			"aws_iam_group":                    resourceAwsIamGroup(),
			"aws_iam_group_membership":         resourceAwsIamGroupMembership(),
			"aws_iam_instance_profile":         resourceAwsIamInstanceProfile(),
			"aws_iam_policy":                   resourceAwsIamPolicy(),
			"aws_iam_policy_attachment":        resourceAwsIamPolicyAttachment(),
			"aws_iam_role_policy":              resourceAwsIamRolePolicy(),
			"aws_iam_role":                     resourceAwsIamRole(),
			"aws_iam_server_certificate":       resourceAwsIAMServerCertificate(),
			"aws_iam_user_policy":              resourceAwsIamUserPolicy(),
			"aws_iam_user":                     resourceAwsIamUser(),
			"aws_instance":                     resourceAwsInstance(),
			"aws_internet_gateway":             resourceAwsInternetGateway(),
			"aws_key_pair":                     resourceAwsKeyPair(),
			"aws_kinesis_stream":               resourceAwsKinesisStream(),
			"aws_lambda_function":              resourceAwsLambdaFunction(),
			"aws_launch_configuration":         resourceAwsLaunchConfiguration(),
			"aws_lb_cookie_stickiness_policy":  resourceAwsLBCookieStickinessPolicy(),
			"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
			"aws_network_acl":                  resourceAwsNetworkAcl(),
			"aws_network_interface":            resourceAwsNetworkInterface(),
			"aws_proxy_protocol_policy":        resourceAwsProxyProtocolPolicy(),
			"aws_route53_delegation_set":       resourceAwsRoute53DelegationSet(),
			"aws_route53_record":               resourceAwsRoute53Record(),
			"aws_route53_zone_association":     resourceAwsRoute53ZoneAssociation(),
			"aws_route53_zone":                 resourceAwsRoute53Zone(),
			"aws_route53_health_check":         resourceAwsRoute53HealthCheck(),
			"aws_route_table":                  resourceAwsRouteTable(),
			"aws_route_table_association":      resourceAwsRouteTableAssociation(),
			"aws_s3_bucket":                    resourceAwsS3Bucket(),
			"aws_security_group":               resourceAwsSecurityGroup(),
			"aws_security_group_rule":          resourceAwsSecurityGroupRule(),
			"aws_spot_instance_request":        resourceAwsSpotInstanceRequest(),
			"aws_sqs_queue":                    resourceAwsSqsQueue(),
			"aws_sns_topic":                    resourceAwsSnsTopic(),
			"aws_sns_topic_subscription":       resourceAwsSnsTopicSubscription(),
			"aws_subnet":                       resourceAwsSubnet(),
			"aws_volume_attachment":            resourceAwsVolumeAttachment(),
			"aws_vpc_dhcp_options_association": resourceAwsVpcDhcpOptionsAssociation(),
			"aws_vpc_dhcp_options":             resourceAwsVpcDhcpOptions(),
			"aws_vpc_peering_connection":       resourceAwsVpcPeeringConnection(),
			"aws_vpc":                          resourceAwsVpc(),
			"aws_vpc_endpoint":                 resourceAwsVpcEndpoint(),
			"aws_vpn_connection":               resourceAwsVpnConnection(),
			"aws_vpn_connection_route":         resourceAwsVpnConnectionRoute(),
			"aws_vpn_gateway":                  resourceAwsVpnGateway(),
		},

		ConfigureFunc: providerConfigure,
	}
}

var descriptions map[string]string

func init() {
	descriptions = map[string]string{
		"region": "The region where AWS operations will take place. Examples\n" +
			"are us-east-1, us-west-2, etc.",

		"access_key": "The access key for API operations. You can retrieve this\n" +
			"from the 'Security & Credentials' section of the AWS console.",

		"secret_key": "The secret key for API operations. You can retrieve this\n" +
			"from the 'Security & Credentials' section of the AWS console.",

		"token": "session token. A session token is only required if you are\n" +
			"using temporary security credentials.",

		"max_retries": "The maximum number of times an AWS API request is\n" +
			"being executed. If the API request still fails, an error is\n" +
			"thrown.",

		"dynamodb_endpoint": "Use this to override the default endpoint URL constructed from the `region`.\n" +
			"It's typically used to connect to dynamodb-local.",
	}
}

func providerConfigure(d *schema.ResourceData) (interface{}, error) {
	config := Config{
		AccessKey:        d.Get("access_key").(string),
		SecretKey:        d.Get("secret_key").(string),
		Token:            d.Get("token").(string),
		Region:           d.Get("region").(string),
		MaxRetries:       d.Get("max_retries").(int),
		DynamoDBEndpoint: d.Get("dynamodb_endpoint").(string),
	}

	if v, ok := d.GetOk("allowed_account_ids"); ok {
		config.AllowedAccountIds = v.(*schema.Set).List()
	}

	if v, ok := d.GetOk("forbidden_account_ids"); ok {
		config.ForbiddenAccountIds = v.(*schema.Set).List()
	}

	return config.Client()
}
<|endoftext|>