<commit_before>package artifactory

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

// NewClient returns a new Artifactory client with the given Config
func NewClient(config Config) Client {
	return DefaultClient{
		config: config,
	}
}

// CreateSnapshotRepository creates a snapshot repository with the given ID.  If the repository creation failed for reasons of transport failure,
// an error is returned.  If the repository creation failed for other business reasons, *HTTPStatus will have the details.
func (c DefaultClient) CreateSnapshotRepository(repositoryID string) (*HTTPStatus, error) {
	repoConfig := LocalRepositoryConfiguration{
		Key:                     repositoryID,
		RClass:                  "local",
		Notes:                   "Created via automation with https://github.com/ae6rt/artifactory Go client [" + time.Now().String() + "]",
		PackageType:             "maven",
		RepoLayoutRef:           "maven-2-default",
		HandleSnapshots:         true,
		HandleReleases:          false,
		MaxUniqueSnapshots:      0,
		SnapshotVersionBehavior: "unique",
	}

	serial, err := json.Marshal(&repoConfig)
	if err != nil {
		return &HTTPStatus{}, err
	}

	req, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), bytes.NewBuffer(serial))
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, nil
	}

	return nil, nil
}

func (c DefaultClient) GetVirtualRepositoryConfiguration(repositoryID string) (VirtualRepositoryConfiguration, error) {

	req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}

	if response.StatusCode/100 == 5 {
		return VirtualRepositoryConfiguration{}, http500{data}
	}

	if response.StatusCode != 200 {
		return VirtualRepositoryConfiguration{HTTPStatus: &HTTPStatus{StatusCode: response.StatusCode, Entity: data}}, nil
	}

	var virtualRepository VirtualRepositoryConfiguration
	err = json.Unmarshal(data, &virtualRepository)
	return virtualRepository, err
}

func (c DefaultClient) LocalRepositoryExists(repositoryID string) (bool, error) {

	req, err := http.NewRequest("HEAD", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return false, err
	}

	req.Header.Set("Accept", "application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json")
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return false, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, err
	}

	if response.StatusCode/100 == 5 {
		return false, http500{data}
	}

	return response.StatusCode == 200, nil
}

func (c DefaultClient) RemoveRepository(repositoryID string) (*HTTPStatus, error) {
	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode/100 == 5 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, http500{data}
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}

	return nil, nil
}

func (c DefaultClient) RemoveItemFromRepository(repositoryID, item string) (*HTTPStatus, error) {
	if item == "" {
		panic("Refusing to remove an item of zero length.")
	}

	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/repositories/%s/%s", c.config.BaseURL, repositoryID, item), nil)
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode/100 == 5 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, http500{data}
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}

	return nil, nil
}

func (c DefaultClient) AddLocalRepositoryToGroup(virtualRepositoryID, localRepositoryID string) (*HTTPStatus, error) {
	r, err := c.GetVirtualRepositoryConfiguration(virtualRepositoryID)
	if err != nil {
		return nil, err
	}
	if r.HTTPStatus != nil {
		return r.HTTPStatus, nil
	}

	if contains(r.Repositories, localRepositoryID) {
		return nil, nil
	}

	r.Repositories = append(r.Repositories, localRepositoryID)

	return c.updateVirtualRepository(r)
}

func (c DefaultClient) RemoveLocalRepositoryFromGroup(virtualRepositoryID, localRepositoryID string) (*HTTPStatus, error) {
	r, err := c.GetVirtualRepositoryConfiguration(virtualRepositoryID)
	if err != nil {
		return nil, err
	}
	if r.HTTPStatus != nil {
		return r.HTTPStatus, nil
	}

	if !contains(r.Repositories, localRepositoryID) {
		return nil, nil
	}

	r.Repositories = remove(r.Repositories, localRepositoryID)

	return c.updateVirtualRepository(r)
}

func (h http500) Error() string {
	return string(h.httpEntity)
}

func contains(arr []string, value string) bool {
	for _, v := range arr {
		if v == value {
			return true
		}
	}
	return false
}

func remove(arr []string, value string) []string {
	var t []string
	for _, v := range arr {
		if v != value {
			t = append(t, v)
		}
	}
	return t
}

func (c DefaultClient) updateVirtualRepository(r VirtualRepositoryConfiguration) (*HTTPStatus, error) {
	serial, err := json.Marshal(&r)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, r.Key), bytes.NewBuffer(serial))
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}
	return nil, nil
}

func (c DefaultClient) setAuthHeaders(req *http.Request) {
	if c.config.APIKey != "" {
		req.Header.Set("X-JFrog-Art-Api", c.config.APIKey)
	} else {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}
}
<commit_msg>Better comments<commit_after>package artifactory

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

// NewClient returns a new Artifactory client with the given Config
func NewClient(config Config) Client {
	return DefaultClient{
		config: config,
	}
}

// CreateSnapshotRepository creates a snapshot repository with the given ID.  If the repository creation failed for reasons of transport failure,
// an error is returned.  If the repository creation failed for other business reasons, *HTTPStatus will have the details.
func (c DefaultClient) CreateSnapshotRepository(repositoryID string) (*HTTPStatus, error) {
	repoConfig := LocalRepositoryConfiguration{
		Key:                     repositoryID,
		RClass:                  "local",
		Notes:                   "Created via automation with https://github.com/ae6rt/artifactory Go client [" + time.Now().String() + "]",
		PackageType:             "maven",
		RepoLayoutRef:           "maven-2-default",
		HandleSnapshots:         true,
		HandleReleases:          false,
		MaxUniqueSnapshots:      0,
		SnapshotVersionBehavior: "unique",
	}

	serial, err := json.Marshal(&repoConfig)
	if err != nil {
		return &HTTPStatus{}, err
	}

	req, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), bytes.NewBuffer(serial))
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, nil
	}

	return nil, nil
}

// GetVirtualRepositoryConfiguration returns the configuration of the given virtual repository.
func (c DefaultClient) GetVirtualRepositoryConfiguration(repositoryID string) (VirtualRepositoryConfiguration, error) {

	req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return VirtualRepositoryConfiguration{}, err
	}

	if response.StatusCode/100 == 5 {
		return VirtualRepositoryConfiguration{}, http500{data}
	}

	if response.StatusCode != 200 {
		return VirtualRepositoryConfiguration{HTTPStatus: &HTTPStatus{StatusCode: response.StatusCode, Entity: data}}, nil
	}

	var virtualRepository VirtualRepositoryConfiguration
	err = json.Unmarshal(data, &virtualRepository)
	return virtualRepository, err
}

// LocalRepositoryExists returns whether the given local repository exists.
func (c DefaultClient) LocalRepositoryExists(repositoryID string) (bool, error) {

	req, err := http.NewRequest("HEAD", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return false, err
	}

	req.Header.Set("Accept", "application/vnd.org.jfrog.artifactory.repositories.LocalRepositoryConfiguration+json")
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return false, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return false, err
	}

	if response.StatusCode/100 == 5 {
		return false, http500{data}
	}

	return response.StatusCode == 200, nil
}

// RemoveRepository removes the given repository.  Check error for transport or marshaling errors.  Check HTTPStatus for other business errors.
func (c DefaultClient) RemoveRepository(repositoryID string) (*HTTPStatus, error) {
	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, repositoryID), nil)
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode/100 == 5 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, http500{data}
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}

	return nil, nil
}

// RemoveItemFromRepository removes the given item from a repository.  Check error for transport or marshaling errors.
// Check HTTPStatus for other business errors.
func (c DefaultClient) RemoveItemFromRepository(repositoryID, item string) (*HTTPStatus, error) {
	if item == "" {
		panic("Refusing to remove an item of zero length.")
	}

	req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/repositories/%s/%s", c.config.BaseURL, repositoryID, item), nil)
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode/100 == 5 {
		return &HTTPStatus{StatusCode: response.StatusCode, Entity: data}, http500{data}
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}

	return nil, nil
}

// AddLocalRepositoryToGroup adds the given local repository to a virtual repository.  Check error for transport or marshaling errors.
// Check HTTPStatus for other business errors.
func (c DefaultClient) AddLocalRepositoryToGroup(virtualRepositoryID, localRepositoryID string) (*HTTPStatus, error) {
	r, err := c.GetVirtualRepositoryConfiguration(virtualRepositoryID)
	if err != nil {
		return nil, err
	}
	if r.HTTPStatus != nil {
		return r.HTTPStatus, nil
	}

	if contains(r.Repositories, localRepositoryID) {
		return nil, nil
	}

	r.Repositories = append(r.Repositories, localRepositoryID)

	return c.updateVirtualRepository(r)
}

// RemoveLocalRepositoryFromGroup removes the given local repository to a virtual repository.  Check error for transport or marshaling errors.
// Check HTTPStatus for other business errors.
func (c DefaultClient) RemoveLocalRepositoryFromGroup(virtualRepositoryID, localRepositoryID string) (*HTTPStatus, error) {
	r, err := c.GetVirtualRepositoryConfiguration(virtualRepositoryID)
	if err != nil {
		return nil, err
	}
	if r.HTTPStatus != nil {
		return r.HTTPStatus, nil
	}

	if !contains(r.Repositories, localRepositoryID) {
		return nil, nil
	}

	r.Repositories = remove(r.Repositories, localRepositoryID)

	return c.updateVirtualRepository(r)
}

func (h http500) Error() string {
	return string(h.httpEntity)
}

func contains(arr []string, value string) bool {
	for _, v := range arr {
		if v == value {
			return true
		}
	}
	return false
}

func remove(arr []string, value string) []string {
	var t []string
	for _, v := range arr {
		if v != value {
			t = append(t, v)
		}
	}
	return t
}

func (c DefaultClient) updateVirtualRepository(r VirtualRepositoryConfiguration) (*HTTPStatus, error) {
	serial, err := json.Marshal(&r)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/repositories/%s", c.config.BaseURL, r.Key), bytes.NewBuffer(serial))
	if err != nil {
		return &HTTPStatus{}, err
	}
	c.setAuthHeaders(req)

	req.Header.Set("Accept", "*/*")
	req.Header.Set("Content-type", "application/vnd.org.jfrog.artifactory.repositories.VirtualRepositoryConfiguration+json")

	response, err := c.config.Doer.Do(req)
	if err != nil {
		return &HTTPStatus{}, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	if response.StatusCode != 200 {
		return &HTTPStatus{response.StatusCode, data}, nil
	}
	return nil, nil
}

func (c DefaultClient) setAuthHeaders(req *http.Request) {
	if c.config.APIKey != "" {
		req.Header.Set("X-JFrog-Art-Api", c.config.APIKey)
	} else {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/andrebq/assimp"
	"io"
	"os"
	"strings"
)

var (
	_if  = flag.String("if", "", "Input file")
	_of  = flag.String("of", "-", "Output file")
	help = flag.Bool("h", false, "Help")
)

func main() {
	flag.Parse()

	if *help {
		printUsage("")
	}

	if *_if == "" {
		printUsage("The input file is required")
	}
	if scene, err := loadAsset(*_if); err != nil {
		log("Unable to load scene.\nCause: %v", err)
	} else {
		dumpScene(scene, *_of)
	}
}

// Dump a scene loaded from assimp to a gob file
// this file can later be used to load resources into the game
// or manipulated to a faster format.
func dumpScene(s *assimp.Scene, outpath string) {
	w, err := openWriterFor(outpath)
	if err != nil {
		fatal("Error opening %v for write. Cause: %v", outpath, err)
	}
	if w, ok := w.(io.Closer); ok {
		defer w.Close()
	}
}

func openWriterFor(file string) (io.Writer, error) {
	if file == "-" {
		return os.Stdout, nil
	} else {
		f, err := os.Create(file)
		if err != nil {
			return nil, err
		}
		return f, err
	}
	panic("Not reached")
	return nil, nil
}

// Just log some information
func log(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, args...)
	if !strings.HasSuffix(msg, "\n") || !strings.HasSuffix(msg, "\r\n") {
		fmt.Fprintf(os.Stderr, "\n")
	}
}

// just like log, but call's os.Exit(1) after
func fatal(msg string, args ...interface{}) {
	log(msg, args...)
	os.Exit(1)
}

// print usage
func printUsage(msg string) {
	if msg != "" {
		log(msg)
	}
	flag.Usage()
	os.Exit(1)
}
<commit_msg>Fix loadAsset to conv.LoadAsset<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/andrebq/assimp"
	"github.com/andrebq/assimp/conv"
	"io"
	"os"
	"strings"
)

var (
	_if  = flag.String("if", "", "Input file")
	_of  = flag.String("of", "-", "Output file")
	help = flag.Bool("h", false, "Help")
)

func main() {
	flag.Parse()

	if *help {
		printUsage("")
	}

	if *_if == "" {
		printUsage("The input file is required")
	}
	if scene, err := conv.LoadAsset(*_if); err != nil {
		log("Unable to load scene.\nCause: %v", err)
	} else {
		dumpScene(scene, *_of)
	}
}

// Dump a scene loaded from assimp to a gob file
// this file can later be used to load resources into the game
// or manipulated to a faster format.
func dumpScene(s *assimp.Scene, outpath string) {
	w, err := openWriterFor(outpath)
	if err != nil {
		fatal("Error opening %v for write. Cause: %v", outpath, err)
	}
	if w, ok := w.(io.Closer); ok {
		defer w.Close()
	}
}

func openWriterFor(file string) (io.Writer, error) {
	if file == "-" {
		return os.Stdout, nil
	} else {
		f, err := os.Create(file)
		if err != nil {
			return nil, err
		}
		return f, err
	}
	panic("Not reached")
	return nil, nil
}

// Just log some information
func log(msg string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, msg, args...)
	if !strings.HasSuffix(msg, "\n") || !strings.HasSuffix(msg, "\r\n") {
		fmt.Fprintf(os.Stderr, "\n")
	}
}

// just like log, but call's os.Exit(1) after
func fatal(msg string, args ...interface{}) {
	log(msg, args...)
	os.Exit(1)
}

// print usage
func printUsage(msg string) {
	if msg != "" {
		log(msg)
	}
	flag.Usage()
	os.Exit(1)
}
<|endoftext|>


<commit_before>package aws

import (
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
)

var describerClient InstanceDescriber

var (
	co             ClientOptions
	coInit         sync.Once
	sdkSession     *session.Session
	sdkSessionInit sync.Once
)

// ClientOptions -
type ClientOptions struct {
	Timeout time.Duration
}

// Ec2Info -
type Ec2Info struct {
	describer  func() (InstanceDescriber, error)
	metaClient *Ec2Meta
	cache      map[string]interface{}
}

// InstanceDescriber - A subset of ec2iface.EC2API that we can use to call EC2.DescribeInstances
type InstanceDescriber interface {
	DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
}

// GetClientOptions - Centralised reading of AWS_TIMEOUT
// ... but cannot use in vault/auth.go as different strconv.Atoi error handling
func GetClientOptions() ClientOptions {
	coInit.Do(func() {
		timeout := os.Getenv("AWS_TIMEOUT")
		if timeout == "" {
			timeout = "500"
		}

		t, err := strconv.Atoi(timeout)
		if err != nil {
			panic(errors.Wrapf(err, "Invalid AWS_TIMEOUT value '%s' - must be an integer\n", timeout))
		}

		co.Timeout = time.Duration(t) * time.Millisecond
	})
	return co
}

// SDKSession -
func SDKSession(region ...string) *session.Session {
	sdkSessionInit.Do(func() {
		options := GetClientOptions()
		timeout := options.Timeout
		if timeout == 0 {
			timeout = 500 * time.Millisecond
		}

		config := aws.NewConfig()
		config = config.WithHTTPClient(&http.Client{Timeout: timeout})

		if os.Getenv("AWS_ANON") == "true" {
			config = config.WithCredentials(credentials.AnonymousCredentials)
		}

		metaRegion := ""
		if len(region) > 0 {
			metaRegion = region[0]
		} else {
			var err error
			metaRegion, err = getRegion()
			if err != nil {
				panic(errors.Wrap(err, "failed to determine EC2 region"))
			}
		}
		config = config.WithRegion(metaRegion)
		config = config.WithCredentialsChainVerboseErrors(true)

		sdkSession = session.Must(session.NewSessionWithOptions(session.Options{
			Config:            *config,
			SharedConfigState: session.SharedConfigEnable,
		}))
	})
	return sdkSession
}

// Attempts to get the EC2 region to use. If we're running on an EC2 Instance
// and neither AWS_REGION nor AWS_DEFAULT_REGION are set, we'll infer from EC2
// metadata.
// Once https://github.com/aws/aws-sdk-go/issues/1103 is resolve this should be
// tidier!
func getRegion(m ...*Ec2Meta) (string, error) {
	region := ""
	_, default1 := os.LookupEnv("AWS_REGION")
	_, default2 := os.LookupEnv("AWS_DEFAULT_REGION")
	if !default1 && !default2 {
		// Maybe we're in EC2, let's try to read metadata
		var metaClient *Ec2Meta
		if len(m) > 0 {
			metaClient = m[0]
		} else {
			metaClient = NewEc2Meta(GetClientOptions())
		}
		var err error
		region, err = metaClient.Region()
		if err != nil {
			return "", errors.Wrap(err, "failed to determine EC2 region")
		}
	}
	return region, nil
}

// NewEc2Info -
func NewEc2Info(options ClientOptions) (info *Ec2Info) {
	metaClient := NewEc2Meta(options)
	return &Ec2Info{
		describer: func() (InstanceDescriber, error) {
			if describerClient == nil {
				session := SDKSession()
				describerClient = ec2.New(session)
			}
			return describerClient, nil
		},
		metaClient: metaClient,
		cache:      make(map[string]interface{}),
	}
}

// Tag -
func (e *Ec2Info) Tag(tag string, def ...string) (string, error) {
	output, err := e.describeInstance()
	if err != nil {
		return "", err
	}
	if output == nil {
		return returnDefault(def), nil
	}

	if len(output.Reservations) > 0 &&
		len(output.Reservations[0].Instances) > 0 &&
		len(output.Reservations[0].Instances[0].Tags) > 0 {
		for _, v := range output.Reservations[0].Instances[0].Tags {
			if *v.Key == tag {
				return *v.Value, nil
			}
		}
	}

	return returnDefault(def), nil
}

func (e *Ec2Info) describeInstance() (output *ec2.DescribeInstancesOutput, err error) {
	// cache the InstanceDescriber here
	d, err := e.describer()
	if err != nil || e.metaClient.nonAWS {
		return nil, err
	}

	if cached, ok := e.cache["DescribeInstances"]; ok {
		output = cached.(*ec2.DescribeInstancesOutput)
	} else {
		instanceID, err := e.metaClient.Meta("instance-id")
		if err != nil {
			return nil, err
		}
		input := &ec2.DescribeInstancesInput{
			InstanceIds: aws.StringSlice([]string{instanceID}),
		}

		output, err = d.DescribeInstances(input)
		if err != nil {
			// default to nil if we can't describe the instance - this could be for any reason
			return nil, nil
		}
		e.cache["DescribeInstances"] = output
	}
	return output, nil
}
<commit_msg>Fix AWS Session unknown region bug<commit_after>package aws

import (
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
)

var describerClient InstanceDescriber

var (
	co             ClientOptions
	coInit         sync.Once
	sdkSession     *session.Session
	sdkSessionInit sync.Once
)

// ClientOptions -
type ClientOptions struct {
	Timeout time.Duration
}

// Ec2Info -
type Ec2Info struct {
	describer  func() (InstanceDescriber, error)
	metaClient *Ec2Meta
	cache      map[string]interface{}
}

// InstanceDescriber - A subset of ec2iface.EC2API that we can use to call EC2.DescribeInstances
type InstanceDescriber interface {
	DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
}

// GetClientOptions - Centralised reading of AWS_TIMEOUT
// ... but cannot use in vault/auth.go as different strconv.Atoi error handling
func GetClientOptions() ClientOptions {
	coInit.Do(func() {
		timeout := os.Getenv("AWS_TIMEOUT")
		if timeout == "" {
			timeout = "500"
		}

		t, err := strconv.Atoi(timeout)
		if err != nil {
			panic(errors.Wrapf(err, "Invalid AWS_TIMEOUT value '%s' - must be an integer\n", timeout))
		}

		co.Timeout = time.Duration(t) * time.Millisecond
	})
	return co
}

// SDKSession -
func SDKSession(region ...string) *session.Session {
	sdkSessionInit.Do(func() {
		options := GetClientOptions()
		timeout := options.Timeout
		if timeout == 0 {
			timeout = 500 * time.Millisecond
		}

		config := aws.NewConfig()
		config = config.WithHTTPClient(&http.Client{Timeout: timeout})

		if os.Getenv("AWS_ANON") == "true" {
			config = config.WithCredentials(credentials.AnonymousCredentials)
		}

		metaRegion := ""
		if len(region) > 0 {
			metaRegion = region[0]
		} else {
			var err error
			metaRegion, err = getRegion()
			if err != nil {
				panic(errors.Wrap(err, "failed to determine EC2 region"))
			}
		}
		if metaRegion != "" && metaRegion != unknown {
			config = config.WithRegion(metaRegion)
		}
		config = config.WithCredentialsChainVerboseErrors(true)

		sdkSession = session.Must(session.NewSessionWithOptions(session.Options{
			Config:            *config,
			SharedConfigState: session.SharedConfigEnable,
		}))
	})
	return sdkSession
}

// Attempts to get the EC2 region to use. If we're running on an EC2 Instance
// and neither AWS_REGION nor AWS_DEFAULT_REGION are set, we'll infer from EC2
// metadata.
// Once https://github.com/aws/aws-sdk-go/issues/1103 is resolve this should be
// tidier!
func getRegion(m ...*Ec2Meta) (string, error) {
	region := ""
	_, default1 := os.LookupEnv("AWS_REGION")
	_, default2 := os.LookupEnv("AWS_DEFAULT_REGION")
	if !default1 && !default2 {
		// Maybe we're in EC2, let's try to read metadata
		var metaClient *Ec2Meta
		if len(m) > 0 {
			metaClient = m[0]
		} else {
			metaClient = NewEc2Meta(GetClientOptions())
		}
		var err error
		region, err = metaClient.Region()
		if err != nil {
			return "", errors.Wrap(err, "failed to determine EC2 region")
		}
	}
	return region, nil
}

// NewEc2Info -
func NewEc2Info(options ClientOptions) (info *Ec2Info) {
	metaClient := NewEc2Meta(options)
	return &Ec2Info{
		describer: func() (InstanceDescriber, error) {
			if describerClient == nil {
				session := SDKSession()
				describerClient = ec2.New(session)
			}
			return describerClient, nil
		},
		metaClient: metaClient,
		cache:      make(map[string]interface{}),
	}
}

// Tag -
func (e *Ec2Info) Tag(tag string, def ...string) (string, error) {
	output, err := e.describeInstance()
	if err != nil {
		return "", err
	}
	if output == nil {
		return returnDefault(def), nil
	}

	if len(output.Reservations) > 0 &&
		len(output.Reservations[0].Instances) > 0 &&
		len(output.Reservations[0].Instances[0].Tags) > 0 {
		for _, v := range output.Reservations[0].Instances[0].Tags {
			if *v.Key == tag {
				return *v.Value, nil
			}
		}
	}

	return returnDefault(def), nil
}

func (e *Ec2Info) describeInstance() (output *ec2.DescribeInstancesOutput, err error) {
	// cache the InstanceDescriber here
	d, err := e.describer()
	if err != nil || e.metaClient.nonAWS {
		return nil, err
	}

	if cached, ok := e.cache["DescribeInstances"]; ok {
		output = cached.(*ec2.DescribeInstancesOutput)
	} else {
		instanceID, err := e.metaClient.Meta("instance-id")
		if err != nil {
			return nil, err
		}
		input := &ec2.DescribeInstancesInput{
			InstanceIds: aws.StringSlice([]string{instanceID}),
		}

		output, err = d.DescribeInstances(input)
		if err != nil {
			// default to nil if we can't describe the instance - this could be for any reason
			return nil, nil
		}
		e.cache["DescribeInstances"] = output
	}
	return output, nil
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)

func ExampleMarshal() {
	type ColorGroup struct {
		ID     int
		Name   string
		Colors []string
	}
	group := ColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}
	b, err := json.Marshal(group)
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b)
	// Output:
	// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}

func ExampleUnmarshal() {
	var jsonBlob = []byte(`[
		{"Name": "Platypus", "Order": "Monotremata"},
		{"Name": "Quoll",    "Order": "Dasyuromorphia"}
	]`)
	type Animal struct {
		Name  string
		Order string
	}
	var animals []Animal
	err := json.Unmarshal(jsonBlob, &animals)
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Printf("%+v", animals)
	// Output:
	// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
}

// This example uses a Decoder to decode a stream of distinct JSON values.
func ExampleDecoder() {
	const jsonStream = `
		{"Name": "Ed", "Text": "Knock knock."}
		{"Name": "Sam", "Text": "Who's there?"}
		{"Name": "Ed", "Text": "Go fmt."}
		{"Name": "Sam", "Text": "Go fmt who?"}
		{"Name": "Ed", "Text": "Go fmt yourself!"}
	`
	type Message struct {
		Name, Text string
	}
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	for {
		var m Message
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s: %s\n", m.Name, m.Text)
	}
	// Output:
	// Ed: Knock knock.
	// Sam: Who's there?
	// Ed: Go fmt.
	// Sam: Go fmt who?
	// Ed: Go fmt yourself!
}

// This example uses a Decoder to decode a stream of distinct JSON values.
func ExampleDecoder_Token() {
	const jsonStream = `
		{"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234}
	`
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	for {
		t, err := dec.Token()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%T: %v", t, t)
		if dec.More() {
			fmt.Printf(" (more)")
		}
		fmt.Printf("\n")
	}
	// Output:
	// json.Delim: { (more)
	// string: Message (more)
	// string: Hello (more)
	// string: Array (more)
	// json.Delim: [ (more)
	// float64: 1 (more)
	// float64: 2 (more)
	// float64: 3
	// json.Delim: ] (more)
	// string: Null (more)
	// <nil>: <nil> (more)
	// string: Number (more)
	// float64: 1.234
	// json.Delim: }
}

// This example uses a Decoder to decode a streaming array of JSON objects.
func ExampleDecoder_Decode_stream() {
	const jsonStream = `
		[
			{"Name": "Ed", "Text": "Knock knock."},
			{"Name": "Sam", "Text": "Who's there?"},
			{"Name": "Ed", "Text": "Go fmt."},
			{"Name": "Sam", "Text": "Go fmt who?"},
			{"Name": "Ed", "Text": "Go fmt yourself!"}
		]
	`
	type Message struct {
		Name, Text string
	}
	dec := json.NewDecoder(strings.NewReader(jsonStream))

	// read open bracket
	t, err := dec.Token()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%T: %v\n", t, t)

	// while the array contains values
	for dec.More() {
		var m Message
		// decode an array value (Message)
		err := dec.Decode(&m)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("%v: %v\n", m.Name, m.Text)
	}

	// read closing bracket
	t, err = dec.Token()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%T: %v\n", t, t)

	// Output:
	// json.Delim: [
	// Ed: Knock knock.
	// Sam: Who's there?
	// Ed: Go fmt.
	// Sam: Go fmt who?
	// Ed: Go fmt yourself!
	// json.Delim: ]

}

// This example uses RawMessage to delay parsing part of a JSON message.
func ExampleRawMessage() {
	type Color struct {
		Space string
		Point json.RawMessage // delay parsing until we know the color space
	}
	type RGB struct {
		R uint8
		G uint8
		B uint8
	}
	type YCbCr struct {
		Y  uint8
		Cb int8
		Cr int8
	}

	var j = []byte(`[
		{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
		{"Space": "RGB",   "Point": {"R": 98, "G": 218, "B": 255}}
	]`)
	var colors []Color
	err := json.Unmarshal(j, &colors)
	if err != nil {
		log.Fatalln("error:", err)
	}

	for _, c := range colors {
		var dst interface{}
		switch c.Space {
		case "RGB":
			dst = new(RGB)
		case "YCbCr":
			dst = new(YCbCr)
		}
		err := json.Unmarshal(c.Point, dst)
		if err != nil {
			log.Fatalln("error:", err)
		}
		fmt.Println(c.Space, dst)
	}
	// Output:
	// YCbCr &{255 0 -10}
	// RGB &{98 218 255}
}

func ExampleIndent() {
	type Road struct {
		Name   string
		Number int
	}
	roads := []Road{
		{"Diamond Fork", 29},
		{"Sheep Creek", 51},
	}

	b, err := json.Marshal(roads)
	if err != nil {
		log.Fatal(err)
	}

	var out bytes.Buffer
	json.Indent(&out, b, "=", "\t")
	out.WriteTo(os.Stdout)
	// Output:
	// [
	// =	{
	// =		"Name": "Diamond Fork",
	// =		"Number": 29
	// =	},
	// =	{
	// =		"Name": "Sheep Creek",
	// =		"Number": 51
	// =	}
	// =]
}
<commit_msg>encoding/json: add example for RawMessage marshalling<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
)

func ExampleMarshal() {
	type ColorGroup struct {
		ID     int
		Name   string
		Colors []string
	}
	group := ColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}
	b, err := json.Marshal(group)
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b)
	// Output:
	// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}

func ExampleUnmarshal() {
	var jsonBlob = []byte(`[
		{"Name": "Platypus", "Order": "Monotremata"},
		{"Name": "Quoll",    "Order": "Dasyuromorphia"}
	]`)
	type Animal struct {
		Name  string
		Order string
	}
	var animals []Animal
	err := json.Unmarshal(jsonBlob, &animals)
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Printf("%+v", animals)
	// Output:
	// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
}

// This example uses a Decoder to decode a stream of distinct JSON values.
func ExampleDecoder() {
	const jsonStream = `
		{"Name": "Ed", "Text": "Knock knock."}
		{"Name": "Sam", "Text": "Who's there?"}
		{"Name": "Ed", "Text": "Go fmt."}
		{"Name": "Sam", "Text": "Go fmt who?"}
		{"Name": "Ed", "Text": "Go fmt yourself!"}
	`
	type Message struct {
		Name, Text string
	}
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	for {
		var m Message
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s: %s\n", m.Name, m.Text)
	}
	// Output:
	// Ed: Knock knock.
	// Sam: Who's there?
	// Ed: Go fmt.
	// Sam: Go fmt who?
	// Ed: Go fmt yourself!
}

// This example uses a Decoder to decode a stream of distinct JSON values.
func ExampleDecoder_Token() {
	const jsonStream = `
		{"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234}
	`
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	for {
		t, err := dec.Token()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%T: %v", t, t)
		if dec.More() {
			fmt.Printf(" (more)")
		}
		fmt.Printf("\n")
	}
	// Output:
	// json.Delim: { (more)
	// string: Message (more)
	// string: Hello (more)
	// string: Array (more)
	// json.Delim: [ (more)
	// float64: 1 (more)
	// float64: 2 (more)
	// float64: 3
	// json.Delim: ] (more)
	// string: Null (more)
	// <nil>: <nil> (more)
	// string: Number (more)
	// float64: 1.234
	// json.Delim: }
}

// This example uses a Decoder to decode a streaming array of JSON objects.
func ExampleDecoder_Decode_stream() {
	const jsonStream = `
		[
			{"Name": "Ed", "Text": "Knock knock."},
			{"Name": "Sam", "Text": "Who's there?"},
			{"Name": "Ed", "Text": "Go fmt."},
			{"Name": "Sam", "Text": "Go fmt who?"},
			{"Name": "Ed", "Text": "Go fmt yourself!"}
		]
	`
	type Message struct {
		Name, Text string
	}
	dec := json.NewDecoder(strings.NewReader(jsonStream))

	// read open bracket
	t, err := dec.Token()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%T: %v\n", t, t)

	// while the array contains values
	for dec.More() {
		var m Message
		// decode an array value (Message)
		err := dec.Decode(&m)
		if err != nil {
			log.Fatal(err)
		}

		fmt.Printf("%v: %v\n", m.Name, m.Text)
	}

	// read closing bracket
	t, err = dec.Token()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%T: %v\n", t, t)

	// Output:
	// json.Delim: [
	// Ed: Knock knock.
	// Sam: Who's there?
	// Ed: Go fmt.
	// Sam: Go fmt who?
	// Ed: Go fmt yourself!
	// json.Delim: ]

}

// This example uses RawMessage to delay parsing part of a JSON message.
func ExampleRawMessage_unmarshal() {
	type Color struct {
		Space string
		Point json.RawMessage // delay parsing until we know the color space
	}
	type RGB struct {
		R uint8
		G uint8
		B uint8
	}
	type YCbCr struct {
		Y  uint8
		Cb int8
		Cr int8
	}

	var j = []byte(`[
		{"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
		{"Space": "RGB",   "Point": {"R": 98, "G": 218, "B": 255}}
	]`)
	var colors []Color
	err := json.Unmarshal(j, &colors)
	if err != nil {
		log.Fatalln("error:", err)
	}

	for _, c := range colors {
		var dst interface{}
		switch c.Space {
		case "RGB":
			dst = new(RGB)
		case "YCbCr":
			dst = new(YCbCr)
		}
		err := json.Unmarshal(c.Point, dst)
		if err != nil {
			log.Fatalln("error:", err)
		}
		fmt.Println(c.Space, dst)
	}
	// Output:
	// YCbCr &{255 0 -10}
	// RGB &{98 218 255}
}

// This example uses RawMessage to use a precomputed JSON during marshal.
func ExampleRawMessage_marshal() {
	h := json.RawMessage(`{"precomputed": true}`)

	c := struct {
		Header *json.RawMessage `json:"header"`
		Body   string           `json:"body"`
	}{Header: &h, Body: "Hello Gophers!"}

	b, err := json.MarshalIndent(&c, "", "\t")
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b)

	// Output:
	// {
	// 	"header": {
	// 		"precomputed": true
	// 	},
	// 	"body": "Hello Gophers!"
	// }
}

func ExampleIndent() {
	type Road struct {
		Name   string
		Number int
	}
	roads := []Road{
		{"Diamond Fork", 29},
		{"Sheep Creek", 51},
	}

	b, err := json.Marshal(roads)
	if err != nil {
		log.Fatal(err)
	}

	var out bytes.Buffer
	json.Indent(&out, b, "=", "\t")
	out.WriteTo(os.Stdout)
	// Output:
	// [
	// =	{
	// =		"Name": "Diamond Fork",
	// =		"Number": 29
	// =	},
	// =	{
	// =		"Name": "Sheep Creek",
	// =		"Number": 51
	// =	}
	// =]
}
<|endoftext|>


<commit_before>// Copyright © 2012-2013, Greg Ward. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package runtime

import (
	"testing"

	"github.com/stretchrcom/testify/assert"

	"fubsy/dag"
	"fubsy/dsl"
	"fubsy/types"
)

func Test_assign(t *testing.T) {
	// AST for a = "foo"
	node := dsl.NewASTAssignment("a", stringnode("foo"))
	ns := types.NewValueMap()

	errs := assign(ns, node)
	assert.Equal(t, 0, len(errs))
	expect := types.FuString("foo")
	assertIn(t, ns, "a", expect)

	// AST for a = foo (another variable, to provoke an error)
	node = dsl.NewASTAssignment("b", dsl.NewASTName("foo"))
	errs = assign(ns, node)
	assert.Equal(t, "name not defined: 'foo'", errs[0].Error())
	_, ok := ns.Lookup("b")
	assert.False(t, ok)
}

// evaluate simple expressions (no operators)
func Test_evaluate_simple(t *testing.T) {
	// the expression "meep" evaluates to the string "meep"
	var expect types.FuObject
	snode := stringnode("meep")
	ns := types.NewValueMap()
	expect = types.FuString("meep")
	assertEvaluateOK(t, ns, expect, snode)

	// the expression foo evaluates to the string "meep" if foo is set
	// to that string
	ns.Assign("foo", expect)
	nnode := dsl.NewASTName("foo")
	assertEvaluateOK(t, ns, expect, nnode)

	// ... and to an error if the variable is not defined
	location := dsl.NewStubLocation("hello, sailor")
	nnode = dsl.NewASTName("boo", location)
	assertEvaluateFail(t, ns, "hello, sailor: name not defined: 'boo'", nnode)

	// expression <*.c blah> evaluates to a FinderNode with two
	// include patterns
	patterns := []string{"*.c", "blah"}
	fnode := dsl.NewASTFileFinder(patterns)
	expect = dag.NewFinderNode([]string{"*.c", "blah"})
	assertEvaluateOK(t, ns, expect, fnode)
}

// evaluate more complex expressions
func Test_evaluate_complex(t *testing.T) {
	// a + b evaluates to various things, depending on the value
	// of those two variables
	addnode := dsl.NewASTAdd(
		dsl.NewASTName("a", dsl.NewStubLocation("loc1")),
		dsl.NewASTName("b", dsl.NewStubLocation("loc2")))

	// case 1: two strings just get concatenated
	ns := types.NewValueMap()
	ns.Assign("a", types.FuString("foo"))
	ns.Assign("b", types.FuString("bar"))
	expect := types.FuString("foobar")
	assertEvaluateOK(t, ns, expect, addnode)

	// case 2: adding a function to a string fails
	ns.Assign("b", types.NewFixedFunction("b", 0, nil))
	assertEvaluateFail(t, ns,
		"loc1loc2: unsupported operation: cannot add function to string",
		addnode)

	// case 3: undefined name
	delete(ns, "b")
	assertEvaluateFail(t, ns, "loc2: name not defined: 'b'", addnode)
}

func stringnode(value string) *dsl.ASTString {
	// NewASTString takes a token, which comes quoted
	value = "\"" + value + "\""
	return dsl.NewASTString(value)
}

func assertIn(
	t *testing.T, ns types.ValueMap, name string, expect types.FuObject) {
	if actual, ok := ns[name]; ok {
		if actual != expect {
			t.Errorf("expected %#v, but got %#v", expect, actual)
		}
	} else {
		t.Errorf("expected to find name '%s' in namespace", name)
	}
}

func assertEvaluateOK(
	t *testing.T,
	ns types.Namespace,
	expect types.FuObject,
	input dsl.ASTExpression) {

	obj, err := evaluate(ns, input)
	assert.Nil(t, err)

	if !expect.Equal(obj) {
		t.Errorf("expected\n%#v\nbut got\n%#v", expect, obj)
	}
}

func assertEvaluateFail(
	t *testing.T,
	ns types.Namespace,
	expecterr string,
	input dsl.ASTExpression) {

	obj, errs := evaluate(ns, input)
	assert.Equal(t, expecterr, errs[0].Error())
	assert.Nil(t, obj)
}
<commit_msg>runtime: add unit test for evaluateCall()<commit_after>// Copyright © 2012-2013, Greg Ward. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package runtime

import (
	"errors"
	"testing"

	"github.com/stretchrcom/testify/assert"

	"fubsy/dag"
	"fubsy/dsl"
	"fubsy/types"
)

func Test_assign(t *testing.T) {
	// AST for a = "foo"
	node := dsl.NewASTAssignment("a", stringnode("foo"))
	ns := types.NewValueMap()

	errs := assign(ns, node)
	assert.Equal(t, 0, len(errs))
	expect := types.FuString("foo")
	assertIn(t, ns, "a", expect)

	// AST for a = foo (another variable, to provoke an error)
	node = dsl.NewASTAssignment("b", dsl.NewASTName("foo"))
	errs = assign(ns, node)
	assert.Equal(t, "name not defined: 'foo'", errs[0].Error())
	_, ok := ns.Lookup("b")
	assert.False(t, ok)
}

// evaluate simple expressions (no operators)
func Test_evaluate_simple(t *testing.T) {
	// the expression "meep" evaluates to the string "meep"
	var expect types.FuObject
	snode := stringnode("meep")
	ns := types.NewValueMap()
	expect = types.FuString("meep")
	assertEvaluateOK(t, ns, expect, snode)

	// the expression foo evaluates to the string "meep" if foo is set
	// to that string
	ns.Assign("foo", expect)
	nnode := dsl.NewASTName("foo")
	assertEvaluateOK(t, ns, expect, nnode)

	// ... and to an error if the variable is not defined
	location := dsl.NewStubLocation("hello, sailor")
	nnode = dsl.NewASTName("boo", location)
	assertEvaluateFail(t, ns, "hello, sailor: name not defined: 'boo'", nnode)

	// expression <*.c blah> evaluates to a FinderNode with two
	// include patterns
	patterns := []string{"*.c", "blah"}
	fnode := dsl.NewASTFileFinder(patterns)
	expect = dag.NewFinderNode([]string{"*.c", "blah"})
	assertEvaluateOK(t, ns, expect, fnode)
}

// evaluate more complex expressions
func Test_evaluate_complex(t *testing.T) {
	// a + b evaluates to various things, depending on the value
	// of those two variables
	addnode := dsl.NewASTAdd(
		dsl.NewASTName("a", dsl.NewStubLocation("loc1")),
		dsl.NewASTName("b", dsl.NewStubLocation("loc2")))

	// case 1: two strings just get concatenated
	ns := types.NewValueMap()
	ns.Assign("a", types.FuString("foo"))
	ns.Assign("b", types.FuString("bar"))
	expect := types.FuString("foobar")
	assertEvaluateOK(t, ns, expect, addnode)

	// case 2: adding a function to a string fails
	ns.Assign("b", types.NewFixedFunction("b", 0, nil))
	assertEvaluateFail(t, ns,
		"loc1loc2: unsupported operation: cannot add function to string",
		addnode)

	// case 3: undefined name
	delete(ns, "b")
	assertEvaluateFail(t, ns, "loc2: name not defined: 'b'", addnode)
}

func Test_evaluateCall(t *testing.T) {
	// foo() takes no args and always succeeds;
	// bar() takes exactly one arg and always fails
	calls := make([]string, 0) // list of function names

	fn_foo := func(args []types.FuObject, kwargs map[string]types.FuObject) (
		types.FuObject, []error) {
		if len(args) != 0 {
			panic("foo() called with wrong number of args")
		}
		calls = append(calls, "foo")
		return types.FuString("foo!"), nil
	}
	fn_bar := func(args []types.FuObject, kwargs map[string]types.FuObject) (
		types.FuObject, []error) {
		if len(args) != 1 {
			panic("bar() called with wrong number of args")
		}
		calls = append(calls, "bar")
		return nil, []error{errors.New("bar failed")}
	}

	ns := types.NewValueMap()
	ns.Assign("foo", types.NewFixedFunction("foo", 0, fn_foo))
	ns.Assign("bar", types.NewFixedFunction("bar", 1, fn_bar))
	ns.Assign("src", types.FuString("main.c"))

	var result types.FuObject
	var errors []error

	fooname := dsl.NewASTName("foo")
	barname := dsl.NewASTName("bar")
	noargs := []dsl.ASTExpression{}
	onearg := []dsl.ASTExpression{dsl.NewASTString("\"meep\"")}

	// call foo() correctly (no args)
	ast := dsl.NewASTFunctionCall(fooname, noargs)
	result, errors = evaluateCall(ns, ast)
	assert.Equal(t, "foo!", result.String())
	assert.Equal(t, 0, len(errors))
	assert.Equal(t, []string{"foo"}, calls)

	// call foo() incorrectly (1 arg)
	ast = dsl.NewASTFunctionCall(fooname, onearg)
	result, errors = evaluateCall(ns, ast)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t,
		"function foo() takes no arguments (got 1)", errors[0].Error())
	assert.Equal(t, []string{"foo"}, calls)

	// call bar() correctly (1 arg)
	ast = dsl.NewASTFunctionCall(barname, onearg)
	result, errors = evaluateCall(ns, ast)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t, "bar failed", errors[0].Error())
	assert.Equal(t, []string{"foo", "bar"}, calls)

	// call bar() incorrectly (no args)
	ast = dsl.NewASTFunctionCall(barname, noargs)
	result, errors = evaluateCall(ns, ast)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t,
		"function bar() takes exactly 1 arguments (got 0)", errors[0].Error())
	assert.Equal(t, []string{"foo", "bar"}, calls)

	// call bar() incorrectly (1 arg, but it's an undefined name)
	ast = dsl.NewASTFunctionCall(
		barname, []dsl.ASTExpression{dsl.NewASTName("bogus")})
	result, errors = evaluateCall(ns, ast)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t,
		"name not defined: 'bogus'", errors[0].Error())

	// attempt to call non-existent function
	ast = dsl.NewASTFunctionCall(dsl.NewASTName("bogus"), onearg)
	result, errors = evaluateCall(ns, ast)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t,
		"name not defined: 'bogus'", errors[0].Error())

	// attempt to call something that is not a function
	ast = dsl.NewASTFunctionCall(dsl.NewASTName("src"), onearg)
	result, errors = evaluateCall(ns, ast)
	assert.Nil(t, result)
	assert.Equal(t, 1, len(errors))
	assert.Equal(t,
		"not a function or method: 'src'", errors[0].Error())

	assert.Equal(t, []string{"foo", "bar"}, calls)
}

func stringnode(value string) *dsl.ASTString {
	// NewASTString takes a token, which comes quoted
	value = "\"" + value + "\""
	return dsl.NewASTString(value)
}

func assertIn(
	t *testing.T, ns types.ValueMap, name string, expect types.FuObject) {
	if actual, ok := ns[name]; ok {
		if actual != expect {
			t.Errorf("expected %#v, but got %#v", expect, actual)
		}
	} else {
		t.Errorf("expected to find name '%s' in namespace", name)
	}
}

func assertEvaluateOK(
	t *testing.T,
	ns types.Namespace,
	expect types.FuObject,
	input dsl.ASTExpression) {

	obj, err := evaluate(ns, input)
	assert.Nil(t, err)

	if !expect.Equal(obj) {
		t.Errorf("expected\n%#v\nbut got\n%#v", expect, obj)
	}
}

func assertEvaluateFail(
	t *testing.T,
	ns types.Namespace,
	expecterr string,
	input dsl.ASTExpression) {

	obj, errs := evaluate(ns, input)
	assert.Equal(t, expecterr, errs[0].Error())
	assert.Nil(t, obj)
}
<|endoftext|>


<commit_before>package solution

import (
	"log"
	"math"

	"github.com/ready-steady/adapt/algorithm"
	"github.com/ready-steady/adapt/algorithm/hybrid"
	"github.com/turing-complete/laboratory/src/internal/config"
	"github.com/turing-complete/laboratory/src/internal/quantity"
)

type strategy struct {
	hybrid.Strategy

	target    quantity.Quantity
	reference quantity.Quantity

	nmax uint

	ns uint
	nn uint

	active []uint
}

func newStrategy(target, reference quantity.Quantity, guide hybrid.Guide,
	config *config.Solution) *strategy {

	ni, no := target.Dimensions()
	return &strategy{
		Strategy: *hybrid.NewStrategy(ni, no, guide, config.MinLevel,
			config.MaxLevel, config.LocalError, config.TotalError),

		target:    target,
		reference: reference,

		nmax: config.MaxEvaluations,
	}
}

func (self *strategy) Done(state *algorithm.State, surrogate *algorithm.Surrogate) bool {
	if self.ns == 0 {
		log.Printf("%5s %15s %15s %15s\n", "Step", "Old Nodes", "New Nodes", "New Level")
	}

	if self.Strategy.Done(state, surrogate) {
		return true
	}

	ni := surrogate.Inputs
	nn := uint(len(state.Indices)) / ni
	if self.nn+nn > self.nmax {
		return true
	}

	level := maxLevel(state.Lindices, ni)

	log.Printf("%5d %15d %15d %15d\n", self.ns, self.nn, nn, level)

	self.ns += 1
	self.nn += nn
	self.active = append(self.active, nn)

	return false
}

func (self *strategy) Score(element *algorithm.Element) float64 {
	return maxAbsolute(element.Surplus) * element.Volume
}

func maxAbsolute(data []float64) (value float64) {
	for i, n := uint(0), uint(len(data)); i < n; i++ {
		value = math.Max(value, math.Abs(data[i]))
	}
	return
}

func maxLevel(lindices []uint64, ni uint) (level uint64) {
	nn := uint(len(lindices)) / ni
	for i := uint(0); i < nn; i++ {
		l := uint64(0)
		for j := uint(0); j < ni; j++ {
			l += lindices[i*ni+j]
		}
		if l > level {
			level = l
		}
	}
	return
}
<commit_msg>i/solution: update the usage of adapt<commit_after>package solution

import (
	"log"
	"math"

	"github.com/ready-steady/adapt/algorithm"
	"github.com/ready-steady/adapt/algorithm/hybrid"
	"github.com/turing-complete/laboratory/src/internal/config"
	"github.com/turing-complete/laboratory/src/internal/quantity"
)

type strategy struct {
	hybrid.Strategy

	target    quantity.Quantity
	reference quantity.Quantity

	nmax uint

	ns uint
	nn uint

	active []uint
}

func newStrategy(target, reference quantity.Quantity, guide hybrid.Guide,
	config *config.Solution) *strategy {

	ni, no := target.Dimensions()
	return &strategy{
		Strategy: *hybrid.NewStrategy(ni, no, guide, config.MinLevel,
			config.MaxLevel, config.LocalError, config.TotalError),

		target:    target,
		reference: reference,

		nmax: config.MaxEvaluations,
	}
}

func (self *strategy) Next(state *algorithm.State,
	surrogate *algorithm.Surrogate) *algorithm.State {

	if self.ns == 0 {
		log.Printf("%5s %15s %15s %15s\n", "Step", "Old Nodes", "New Nodes", "New Level")
	}

	ni := surrogate.Inputs

	nn := uint(len(state.Indices)) / ni
	self.nn += nn
	self.active = append(self.active, nn)

	state = self.Strategy.Next(state, surrogate)
	if state == nil {
		return nil
	}

	nn = uint(len(state.Indices)) / ni
	if self.nn+nn > self.nmax {
		return nil
	}

	level := maxLevel(state.Lindices, ni)
	log.Printf("%5d %15d %15d %15d\n", self.ns, self.nn, nn, level)

	self.ns += 1

	return state
}

func (self *strategy) Score(element *algorithm.Element) float64 {
	return maxAbsolute(element.Surplus) * element.Volume
}

func maxAbsolute(data []float64) (value float64) {
	for i, n := uint(0), uint(len(data)); i < n; i++ {
		value = math.Max(value, math.Abs(data[i]))
	}
	return
}

func maxLevel(lindices []uint64, ni uint) (level uint64) {
	nn := uint(len(lindices)) / ni
	for i := uint(0); i < nn; i++ {
		l := uint64(0)
		for j := uint(0); j < ni; j++ {
			l += lindices[i*ni+j]
		}
		if l > level {
			level = l
		}
	}
	return
}
<|endoftext|>


<commit_before>package solution

import (
	"log"
	"math"

	"github.com/ready-steady/adapt/algorithm"
	"github.com/ready-steady/adapt/algorithm/hybrid"
	"github.com/turing-complete/laboratory/src/internal/config"
	"github.com/turing-complete/laboratory/src/internal/quantity"
)

type strategy struct {
	hybrid.Strategy

	target    quantity.Quantity
	reference quantity.Quantity

	nmax uint

	ns uint
	nn uint

	active []uint
}

func newStrategy(target, reference quantity.Quantity, guide hybrid.Guide,
	config *config.Solution) *strategy {

	ni, no := target.Dimensions()
	return &strategy{
		Strategy: *hybrid.NewStrategy(ni, no, guide, config.MinLevel,
			config.MaxLevel, config.LocalError, config.TotalError),

		target:    target,
		reference: reference,

		nmax: config.MaxEvaluations,
	}
}

func (self *strategy) Next(state *algorithm.State,
	surrogate *algorithm.Surrogate) *algorithm.State {

	if self.ns == 0 {
		log.Printf("%5s %15s %15s %15s\n", "Step", "Old Nodes", "New Nodes", "New Level")
	}

	ni := surrogate.Inputs

	nn := uint(len(state.Indices)) / ni
	self.nn += nn
	self.active = append(self.active, nn)

	state = self.Strategy.Next(state, surrogate)
	if state == nil {
		return nil
	}

	nn = uint(len(state.Indices)) / ni
	if self.nn+nn > self.nmax {
		return nil
	}

	level := maxLevel(state.Lindices, ni)
	log.Printf("%5d %15d %15d %15d\n", self.ns, self.nn, nn, level)

	self.ns += 1

	return state
}

func (self *strategy) Score(element *algorithm.Element) float64 {
	return maxAbsolute(element.Surplus) * element.Volume
}

func maxAbsolute(data []float64) (value float64) {
	for i, n := uint(0), uint(len(data)); i < n; i++ {
		value = math.Max(value, math.Abs(data[i]))
	}
	return
}

func maxLevel(lindices []uint64, ni uint) (level uint64) {
	nn := uint(len(lindices)) / ni
	for i := uint(0); i < nn; i++ {
		l := uint64(0)
		for j := uint(0); j < ni; j++ {
			l += lindices[i*ni+j]
		}
		if l > level {
			level = l
		}
	}
	return
}
<commit_msg>Update the usage of adapt<commit_after>package solution

import (
	"log"
	"math"

	"github.com/ready-steady/adapt/algorithm"
	"github.com/ready-steady/adapt/algorithm/hybrid"
	"github.com/turing-complete/laboratory/src/internal/config"
	"github.com/turing-complete/laboratory/src/internal/quantity"
)

type strategy struct {
	hybrid.Strategy

	target    quantity.Quantity
	reference quantity.Quantity

	nmax uint

	ns uint
	nn uint

	active []uint
}

func newStrategy(target, reference quantity.Quantity, guide hybrid.Guide,
	config *config.Solution) *strategy {

	ni, no := target.Dimensions()
	return &strategy{
		Strategy: *hybrid.NewStrategy(ni, no, guide, config.MinLevel,
			config.MaxLevel, config.LocalError, config.TotalError),

		target:    target,
		reference: reference,

		nmax: config.MaxEvaluations,
	}
}

func (self *strategy) Next(state *algorithm.State,
	surrogate *algorithm.Surrogate) *algorithm.State {

	if self.ns == 0 {
		log.Printf("%5s %15s %15s %15s\n", "Step", "Old Nodes", "New Nodes", "New Level")
	}

	ni := surrogate.Inputs

	nn := uint(len(state.Indices)) / ni
	self.nn += nn
	self.active = append(self.active, nn)

	state = self.Strategy.Next(state, surrogate)
	if state == nil {
		return nil
	}

	nn = uint(len(state.Indices)) / ni
	if self.nn+nn > self.nmax {
		return nil
	}

	level := maxLevel(state.Lndices, ni)
	log.Printf("%5d %15d %15d %15d\n", self.ns, self.nn, nn, level)

	self.ns += 1

	return state
}

func (self *strategy) Score(element *algorithm.Element) float64 {
	return maxAbsolute(element.Surplus) * element.Volume
}

func maxAbsolute(data []float64) (value float64) {
	for i, n := uint(0), uint(len(data)); i < n; i++ {
		value = math.Max(value, math.Abs(data[i]))
	}
	return
}

func maxLevel(lndices []uint64, ni uint) (level uint64) {
	nn := uint(len(lndices)) / ni
	for i := uint(0); i < nn; i++ {
		l := uint64(0)
		for j := uint(0); j < ni; j++ {
			l += lndices[i*ni+j]
		}
		if l > level {
			level = l
		}
	}
	return
}
<|endoftext|>


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

package vmtable

import (
	"errors"
	"os"
	"strings"

	"github.com/rogpeppe/rjson"
	compute_v1 "google.golang.org/api/compute/v1"
	"google.golang.org/cloud/compute/metadata"
)

var (
	NotPreemtibleError = errors.New("instance template in config is not preemptible")
)

type Config struct {
	SecondsToRest        int
	SecondsForExhaustion int
	Prefix               string
	AllowedZones         []string
	MachineType          string
	GCEImage             string

	Target int

	Instance rjson.RawMessage
}

func ConfigFromMetadata() (Config, error) {
	attrName := os.Getenv("PREVMTABLE_ATTRIBUTE")
	if attrName == "" {
		attrName = "prevmtable"
	}

	cfgData, err := metadata.ProjectAttributeValue(attrName)
	if err != nil {
		return Config{}, err
	}

	var cfg Config

	if err := rjson.NewDecoder(strings.NewReader(cfgData)).Decode(&cfg); err != nil {
		return Config{}, err
	}

	i := &compute_v1.Instance{}
	instanceData := string(cfg.Instance)
	instanceData = strings.Replace(instanceData, "{project}", "proj", -1)
	instanceData = strings.Replace(instanceData, "{zone}", "zone", -1)
	instanceData = strings.Replace(instanceData, "{name}", "name", -1)

	if err := rjson.Unmarshal([]byte(instanceData), i); err != nil {
		return cfg, err
	}

	if !i.Scheduling.Preemptible {
		return cfg, NotPreemtibleError
	}

	return cfg, nil
}

func Project() (string, error) {
	return metadata.ProjectID()
}
<commit_msg>Commentary for config<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vmtable

import (
	"errors"
	"os"
	"strings"

	"github.com/rogpeppe/rjson"
	compute_v1 "google.golang.org/api/compute/v1"
	"google.golang.org/cloud/compute/metadata"
)

var (
	NotPreemtibleError = errors.New("instance template in config is not preemptible")
)

type Config struct {
	// Seconds between updates.
	SecondsToRest int

	// Seconds to wait before retrying a zone that got exhausted.
	SecondsForExhaustion int

	// Prefix to put on the name of each VM.
	Prefix string

	// The zones to create VMs in.
	AllowedZones []string

	// Number of VMs to maintain. If there are more, delete. If there are fewer, create.
	TargetVMCount int

	// Template to use for instance creation.
	Instance rjson.RawMessage
}

func ConfigFromMetadata() (Config, error) {
	attrName := os.Getenv("PREVMTABLE_ATTRIBUTE")
	if attrName == "" {
		attrName = "prevmtable"
	}

	cfgData, err := metadata.ProjectAttributeValue(attrName)
	if err != nil {
		return Config{}, err
	}

	var cfg Config

	if err := rjson.NewDecoder(strings.NewReader(cfgData)).Decode(&cfg); err != nil {
		return Config{}, err
	}

	i := &compute_v1.Instance{}
	instanceData := string(cfg.Instance)
	instanceData = strings.Replace(instanceData, "{project}", "proj", -1)
	instanceData = strings.Replace(instanceData, "{zone}", "zone", -1)
	instanceData = strings.Replace(instanceData, "{name}", "name", -1)

	if err := rjson.Unmarshal([]byte(instanceData), i); err != nil {
		return cfg, err
	}

	if !i.Scheduling.Preemptible {
		return cfg, NotPreemtibleError
	}

	return cfg, nil
}

func Project() (string, error) {
	return metadata.ProjectID()
}
<|endoftext|>


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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"

	"github.com/hashicorp/go-multierror"
	shell "github.com/kballard/go-shellquote"
	k8sProwConfig "k8s.io/test-infra/prow/config"
	"sigs.k8s.io/yaml"

	"istio.io/test-infra/tools/prowgen/pkg"
	"istio.io/test-infra/tools/prowgen/pkg/spec"
)

var (
	// regex to match the test image tags.
	tagRegex = regexp.MustCompile(`^(.+):(.+)-([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2})$`)

	inputDir            = flag.String("input-dir", "./prow/config/jobs", "directory of input jobs")
	outputDir           = flag.String("output-dir", "./prow/cluster/jobs", "directory of output jobs")
	preprocessCommand   = flag.String("pre-process-command", "", "command to run to preprocess the meta config files")
	postprocessCommand  = flag.String("post-process-command", "", "command to run to postprocess the generated config files")
	longJobNamesAllowed = flag.Bool("allow-long-job-names", false, "allow job names that are longer than 63 characters")
)

func main() {
	flag.Parse()

	// TODO: deserves a better CLI...
	if len(flag.Args()) < 1 {
		panic("must provide one of write, print, check, branch")
	} else if flag.Arg(0) == "branch" {
		if len(flag.Args()) != 2 {
			panic("must specify branch name")
		}
	} else if len(flag.Args()) != 1 {
		panic("too many arguments")
	}

	var bc spec.BaseConfig
	if _, err := os.Stat(filepath.Join(*inputDir, ".base.yaml")); !os.IsNotExist(err) {
		bc = pkg.ReadBase(nil, filepath.Join(*inputDir, ".base.yaml"))
	}

	if os.Args[1] == "branch" {
		if err := filepath.WalkDir(*inputDir, func(path string, d os.DirEntry, err error) error {
			if !d.IsDir() {
				return nil
			}
			if err != nil {
				log.Fatal(err)
			}
			baseConfig := bc
			if _, err := os.Stat(filepath.Join(path, ".base.yaml")); !os.IsNotExist(err) {
				baseConfig = pkg.ReadBase(&baseConfig, filepath.Join(path, ".base.yaml"))
			}
			cli := pkg.Client{BaseConfig: baseConfig, LongJobNamesAllowed: *longJobNamesAllowed}

			files, _ := ioutil.ReadDir(path)
			for _, file := range files {
				if file.IsDir() {
					continue
				}

				if (filepath.Ext(file.Name()) != ".yaml" && filepath.Ext(file.Name()) != ".yml") ||
					file.Name() == ".base.yaml" {
					log.Println("skipping non-yaml file: ", file.Name())
					continue
				}

				src := filepath.Join(path, file.Name())
				jobs := cli.ReadJobsConfig(src)
				jobs.Jobs = pkg.FilterReleaseBranchingJobs(jobs.Jobs)

				if jobs.SupportReleaseBranching {
					match := tagRegex.FindStringSubmatch(jobs.Image)
					branch := "release-" + flag.Arg(1)
					if len(match) == 4 {
						// HACK: replacing the branch name in the image tag and
						// adding it as a new tag.
						// For example, if the test image in the current Prow job
						// config is
						// `gcr.io/istio-testing/build-tools:release-1.10-2021-08-09T16-46-08`,
						// and the Prow job config for release-1.11 branch is
						// supposed to be generated, the image will be added a
						// new `release-1.11-2021-08-09T16-46-08` tag.
						// This is only needed for creating Prow jobs for a new
						// release branch for the first time, and the image tag
						// will be overwritten by Automator the next time the
						// image for the new branch is updated.
						newImage := fmt.Sprintf("%s:%s-%s", match[1], branch, match[3])
						if err := exec.Command("gcloud", "container", "images", "add-tag", match[0], newImage).Run(); err != nil {
							log.Fatalf("Unable to add image tag %q: %v", newImage, err)
						} else {
							jobs.Image = newImage
						}
					}
					jobs.Branches = []string{branch}
					jobs.SupportReleaseBranching = false

					name := file.Name()
					ext := filepath.Ext(name)
					name = name[:len(name)-len(ext)] + "-" + flag.Arg(1) + ext

					dst := filepath.Join(*inputDir, name)
					bytes, err := yaml.Marshal(jobs)
					if err != nil {
						log.Fatalf("Error marshaling jobs config: %v", err)
					}

					// Writes the job yaml
					if err := ioutil.WriteFile(dst, bytes, 0o644); err != nil {
						log.Fatalf("Error writing branches config: %v", err)
					}
				}
			}

			return nil
		}); err != nil {
			log.Fatalf("Walking through the meta config files failed: %v", err)
		}
	} else {
		if *preprocessCommand != "" {
			if err := runProcessCommand(*preprocessCommand); err != nil {
				log.Fatalf("Error running preprocess command %q: %v", *preprocessCommand, err)
			}
		}

		type ref struct {
			org    string
			repo   string
			branch string
		}
		// Store the job config generated from all meta-config files in a cache map, and combine the
		// job configs before we generate the final config files.
		// In this way we can have multiple meta-config files for the same org/repo:branch
		cachedOutput := map[ref]k8sProwConfig.JobConfig{}
		if err := filepath.WalkDir(*inputDir, func(path string, d os.DirEntry, err error) error {
			if !d.IsDir() {
				return nil
			}
			if err != nil {
				log.Fatal(err)
			}

			baseConfig := bc
			if _, err := os.Stat(filepath.Join(path, ".base.yaml")); !os.IsNotExist(err) {
				baseConfig = pkg.ReadBase(&baseConfig, filepath.Join(path, ".base.yaml"))
			}
			cli := pkg.Client{BaseConfig: baseConfig, LongJobNamesAllowed: *longJobNamesAllowed}

			files, _ := ioutil.ReadDir(path)
			for _, file := range files {
				if file.IsDir() {
					continue
				}

				if (filepath.Ext(file.Name()) != ".yaml" && filepath.Ext(file.Name()) != ".yml") ||
					file.Name() == ".base.yaml" {
					log.Println("skipping non-yaml file: ", file.Name())
					continue
				}

				src := filepath.Join(path, file.Name())
				jobs := cli.ReadJobsConfig(src)
				for _, branch := range jobs.Branches {
					output, err := cli.ConvertJobConfig(file.Name(), jobs, branch)
					if err != nil {
						log.Fatal(err)
					}
					rf := ref{jobs.Org, jobs.Repo, branch}
					if _, ok := cachedOutput[rf]; !ok {
						cachedOutput[rf] = output
					} else {
						cachedOutput[rf] = combineJobConfigs(cachedOutput[rf], output,
							fmt.Sprintf("%s/%s", jobs.Org, jobs.Repo))
					}
				}
			}
			return nil
		}); err != nil {
			log.Fatalf("Walking through the meta config files failed: %v", err)
		}

		var err error
		for r, output := range cachedOutput {
			fname := outputFileName(r.repo, r.org, r.branch)
			switch flag.Arg(0) {
			case "write":
				if e := pkg.Write(output, fname, bc.AutogenHeader); e != nil {
					err = multierror.Append(err, e)
				}
				if *postprocessCommand != "" {
					if e := runProcessCommand(*postprocessCommand); e != nil {
						err = multierror.Append(err, e)
					}
				}
			case "check":
				if e := pkg.Check(output, fname, bc.AutogenHeader); e != nil {
					err = multierror.Append(err, e)
				}
			case "print":
				pkg.Print(output)
			}
		}

		if err != nil {
			log.Fatalf("Get errors for the %q operation:\n%v", flag.Arg(0), err)
		}
	}
}

func runProcessCommand(rawCommand string) error {
	log.Printf("⚙ %s", rawCommand)
	cmdSplit, err := shell.Split(rawCommand)
	if len(cmdSplit) == 0 || err != nil {
		return fmt.Errorf("error parsing the command %q: %w", rawCommand, err)
	}
	cmd := exec.Command(cmdSplit[0], cmdSplit[1:]...)

	// Set INPUT and OUTPUT env vars for the pre-process and post-process
	// commands to consume.
	cmd.Env = append(os.Environ(), "INPUT="+*inputDir, "OUTPUT="+*outputDir)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

func outputFileName(repo string, org string, branch string) string {
	key := fmt.Sprintf("%s.%s.%s.gen.yaml", org, repo, branch)
	return path.Join(*outputDir, org, repo, key)
}

func combineJobConfigs(jc1, jc2 k8sProwConfig.JobConfig, orgRepo string) k8sProwConfig.JobConfig {
	presubmits := jc1.PresubmitsStatic
	postsubmits := jc1.PostsubmitsStatic
	periodics := jc1.Periodics

	presubmits[orgRepo] = append(presubmits[orgRepo], jc2.PresubmitsStatic[orgRepo]...)
	postsubmits[orgRepo] = append(postsubmits[orgRepo], jc2.PostsubmitsStatic[orgRepo]...)
	periodics = append(periodics, jc2.Periodics...)

	return k8sProwConfig.JobConfig{
		PresubmitsStatic:  presubmits,
		PostsubmitsStatic: postsubmits,
		Periodics:         periodics,
	}
}
<commit_msg>Update regex to also find sha and not just data for build images (#4340)<commit_after>// Copyright 2019 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"

	"github.com/hashicorp/go-multierror"
	shell "github.com/kballard/go-shellquote"
	k8sProwConfig "k8s.io/test-infra/prow/config"
	"sigs.k8s.io/yaml"

	"istio.io/test-infra/tools/prowgen/pkg"
	"istio.io/test-infra/tools/prowgen/pkg/spec"
)

var (
	// regex to match the test image tags.
	tagRegex = regexp.MustCompile(`^(.+):(.+)-([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}-[0-9]{2}-[0-9]{2}|[0-9a-f]{40})$`)

	inputDir            = flag.String("input-dir", "./prow/config/jobs", "directory of input jobs")
	outputDir           = flag.String("output-dir", "./prow/cluster/jobs", "directory of output jobs")
	preprocessCommand   = flag.String("pre-process-command", "", "command to run to preprocess the meta config files")
	postprocessCommand  = flag.String("post-process-command", "", "command to run to postprocess the generated config files")
	longJobNamesAllowed = flag.Bool("allow-long-job-names", false, "allow job names that are longer than 63 characters")
)

func main() {
	flag.Parse()

	// TODO: deserves a better CLI...
	if len(flag.Args()) < 1 {
		panic("must provide one of write, print, check, branch")
	} else if flag.Arg(0) == "branch" {
		if len(flag.Args()) != 2 {
			panic("must specify branch name")
		}
	} else if len(flag.Args()) != 1 {
		panic("too many arguments")
	}

	var bc spec.BaseConfig
	if _, err := os.Stat(filepath.Join(*inputDir, ".base.yaml")); !os.IsNotExist(err) {
		bc = pkg.ReadBase(nil, filepath.Join(*inputDir, ".base.yaml"))
	}

	if os.Args[1] == "branch" {
		if err := filepath.WalkDir(*inputDir, func(path string, d os.DirEntry, err error) error {
			if !d.IsDir() {
				return nil
			}
			if err != nil {
				log.Fatal(err)
			}
			baseConfig := bc
			if _, err := os.Stat(filepath.Join(path, ".base.yaml")); !os.IsNotExist(err) {
				baseConfig = pkg.ReadBase(&baseConfig, filepath.Join(path, ".base.yaml"))
			}
			cli := pkg.Client{BaseConfig: baseConfig, LongJobNamesAllowed: *longJobNamesAllowed}

			files, _ := ioutil.ReadDir(path)
			for _, file := range files {
				if file.IsDir() {
					continue
				}

				if (filepath.Ext(file.Name()) != ".yaml" && filepath.Ext(file.Name()) != ".yml") ||
					file.Name() == ".base.yaml" {
					log.Println("skipping non-yaml file: ", file.Name())
					continue
				}

				src := filepath.Join(path, file.Name())
				jobs := cli.ReadJobsConfig(src)
				jobs.Jobs = pkg.FilterReleaseBranchingJobs(jobs.Jobs)

				if jobs.SupportReleaseBranching {
					match := tagRegex.FindStringSubmatch(jobs.Image)
					branch := "release-" + flag.Arg(1)
					if len(match) == 4 {
						// HACK: replacing the branch name in the image tag and
						// adding it as a new tag.
						// For example, if the test image in the current Prow job
						// config is
						// `gcr.io/istio-testing/build-tools:release-1.10-2021-08-09T16-46-08`,
						// and the Prow job config for release-1.11 branch is
						// supposed to be generated, the image will be added a
						// new `release-1.11-2021-08-09T16-46-08` tag.
						// This is only needed for creating Prow jobs for a new
						// release branch for the first time, and the image tag
						// will be overwritten by Automator the next time the
						// image for the new branch is updated.
						newImage := fmt.Sprintf("%s:%s-%s", match[1], branch, match[3])
						if err := exec.Command("gcloud", "container", "images", "add-tag", match[0], newImage).Run(); err != nil {
							log.Fatalf("Unable to add image tag %q: %v", newImage, err)
						} else {
							jobs.Image = newImage
						}
					}
					jobs.Branches = []string{branch}
					jobs.SupportReleaseBranching = false

					name := file.Name()
					ext := filepath.Ext(name)
					name = name[:len(name)-len(ext)] + "-" + flag.Arg(1) + ext

					dst := filepath.Join(*inputDir, name)
					bytes, err := yaml.Marshal(jobs)
					if err != nil {
						log.Fatalf("Error marshaling jobs config: %v", err)
					}

					// Writes the job yaml
					if err := ioutil.WriteFile(dst, bytes, 0o644); err != nil {
						log.Fatalf("Error writing branches config: %v", err)
					}
				}
			}

			return nil
		}); err != nil {
			log.Fatalf("Walking through the meta config files failed: %v", err)
		}
	} else {
		if *preprocessCommand != "" {
			if err := runProcessCommand(*preprocessCommand); err != nil {
				log.Fatalf("Error running preprocess command %q: %v", *preprocessCommand, err)
			}
		}

		type ref struct {
			org    string
			repo   string
			branch string
		}
		// Store the job config generated from all meta-config files in a cache map, and combine the
		// job configs before we generate the final config files.
		// In this way we can have multiple meta-config files for the same org/repo:branch
		cachedOutput := map[ref]k8sProwConfig.JobConfig{}
		if err := filepath.WalkDir(*inputDir, func(path string, d os.DirEntry, err error) error {
			if !d.IsDir() {
				return nil
			}
			if err != nil {
				log.Fatal(err)
			}

			baseConfig := bc
			if _, err := os.Stat(filepath.Join(path, ".base.yaml")); !os.IsNotExist(err) {
				baseConfig = pkg.ReadBase(&baseConfig, filepath.Join(path, ".base.yaml"))
			}
			cli := pkg.Client{BaseConfig: baseConfig, LongJobNamesAllowed: *longJobNamesAllowed}

			files, _ := ioutil.ReadDir(path)
			for _, file := range files {
				if file.IsDir() {
					continue
				}

				if (filepath.Ext(file.Name()) != ".yaml" && filepath.Ext(file.Name()) != ".yml") ||
					file.Name() == ".base.yaml" {
					log.Println("skipping non-yaml file: ", file.Name())
					continue
				}

				src := filepath.Join(path, file.Name())
				jobs := cli.ReadJobsConfig(src)
				for _, branch := range jobs.Branches {
					output, err := cli.ConvertJobConfig(file.Name(), jobs, branch)
					if err != nil {
						log.Fatal(err)
					}
					rf := ref{jobs.Org, jobs.Repo, branch}
					if _, ok := cachedOutput[rf]; !ok {
						cachedOutput[rf] = output
					} else {
						cachedOutput[rf] = combineJobConfigs(cachedOutput[rf], output,
							fmt.Sprintf("%s/%s", jobs.Org, jobs.Repo))
					}
				}
			}
			return nil
		}); err != nil {
			log.Fatalf("Walking through the meta config files failed: %v", err)
		}

		var err error
		for r, output := range cachedOutput {
			fname := outputFileName(r.repo, r.org, r.branch)
			switch flag.Arg(0) {
			case "write":
				if e := pkg.Write(output, fname, bc.AutogenHeader); e != nil {
					err = multierror.Append(err, e)
				}
				if *postprocessCommand != "" {
					if e := runProcessCommand(*postprocessCommand); e != nil {
						err = multierror.Append(err, e)
					}
				}
			case "check":
				if e := pkg.Check(output, fname, bc.AutogenHeader); e != nil {
					err = multierror.Append(err, e)
				}
			case "print":
				pkg.Print(output)
			}
		}

		if err != nil {
			log.Fatalf("Get errors for the %q operation:\n%v", flag.Arg(0), err)
		}
	}
}

func runProcessCommand(rawCommand string) error {
	log.Printf("⚙ %s", rawCommand)
	cmdSplit, err := shell.Split(rawCommand)
	if len(cmdSplit) == 0 || err != nil {
		return fmt.Errorf("error parsing the command %q: %w", rawCommand, err)
	}
	cmd := exec.Command(cmdSplit[0], cmdSplit[1:]...)

	// Set INPUT and OUTPUT env vars for the pre-process and post-process
	// commands to consume.
	cmd.Env = append(os.Environ(), "INPUT="+*inputDir, "OUTPUT="+*outputDir)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

func outputFileName(repo string, org string, branch string) string {
	key := fmt.Sprintf("%s.%s.%s.gen.yaml", org, repo, branch)
	return path.Join(*outputDir, org, repo, key)
}

func combineJobConfigs(jc1, jc2 k8sProwConfig.JobConfig, orgRepo string) k8sProwConfig.JobConfig {
	presubmits := jc1.PresubmitsStatic
	postsubmits := jc1.PostsubmitsStatic
	periodics := jc1.Periodics

	presubmits[orgRepo] = append(presubmits[orgRepo], jc2.PresubmitsStatic[orgRepo]...)
	postsubmits[orgRepo] = append(postsubmits[orgRepo], jc2.PostsubmitsStatic[orgRepo]...)
	periodics = append(periodics, jc2.Periodics...)

	return k8sProwConfig.JobConfig{
		PresubmitsStatic:  presubmits,
		PostsubmitsStatic: postsubmits,
		Periodics:         periodics,
	}
}
<|endoftext|>


<commit_before>package client

import (
	"errors"
	"fmt"
	"io"

	"github.com/docker/distribution/reference"
	"github.com/docker/docker/api/client/lib"
	"github.com/docker/docker/api/types"
	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/pkg/jsonmessage"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/registry"
)

// CmdPush pushes an image or repository to the registry.
//
// Usage: docker push NAME[:TAG]
func (cli *DockerCli) CmdPush(args ...string) error {
	cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, Cli.DockerCommands["push"].Description, true)
	addTrustedFlags(cmd, false)
	cmd.Require(flag.Exact, 1)

	cmd.ParseFlags(args, true)

	ref, err := reference.ParseNamed(cmd.Arg(0))
	if err != nil {
		return err
	}

	var tag string
	switch x := ref.(type) {
	case reference.Digested:
		return errors.New("cannot push a digest reference")
	case reference.Tagged:
		tag = x.Tag()
	}

	// Resolve the Repository name from fqn to RepositoryInfo
	repoInfo, err := registry.ParseRepositoryInfo(ref)
	if err != nil {
		return err
	}
	// Resolve the Auth config relevant for this server
	authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)
	// If we're not using a custom registry, we know the restrictions
	// applied to repository names and can warn the user in advance.
	// Custom repositories can have different rules, and we must also
	// allow pushing by image ID.
	if repoInfo.Official {
		username := authConfig.Username
		if username == "" {
			username = "<user>"
		}
		return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository to <user>/<repo> (ex: %s/%s)", username, repoInfo.LocalName)
	}

	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
	if isTrusted() {
		return cli.trustedPush(repoInfo, tag, authConfig, requestPrivilege)
	}

	return cli.imagePushPrivileged(authConfig, ref.Name(), tag, cli.out, requestPrivilege)
}

func (cli *DockerCli) imagePushPrivileged(authConfig cliconfig.AuthConfig, imageID, tag string, outputStream io.Writer, requestPrivilege lib.RequestPrivilegeFunc) error {
	encodedAuth, err := authConfig.EncodeToBase64()
	if err != nil {
		return err
	}
	options := types.ImagePushOptions{
		ImageID:      imageID,
		Tag:          tag,
		RegistryAuth: encodedAuth,
	}

	responseBody, err := cli.client.ImagePush(options, requestPrivilege)
	if err != nil {
		return err
	}
	defer responseBody.Close()

	return jsonmessage.DisplayJSONMessagesStream(responseBody, outputStream, cli.outFd, cli.isTerminalOut)
}
<commit_msg>Removing the restriction to push to a 'official' repo<commit_after>package client

import (
	"errors"
	"io"

	"github.com/docker/distribution/reference"
	"github.com/docker/docker/api/client/lib"
	"github.com/docker/docker/api/types"
	Cli "github.com/docker/docker/cli"
	"github.com/docker/docker/cliconfig"
	"github.com/docker/docker/pkg/jsonmessage"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/registry"
)

// CmdPush pushes an image or repository to the registry.
//
// Usage: docker push NAME[:TAG]
func (cli *DockerCli) CmdPush(args ...string) error {
	cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, Cli.DockerCommands["push"].Description, true)
	addTrustedFlags(cmd, false)
	cmd.Require(flag.Exact, 1)

	cmd.ParseFlags(args, true)

	ref, err := reference.ParseNamed(cmd.Arg(0))
	if err != nil {
		return err
	}

	var tag string
	switch x := ref.(type) {
	case reference.Digested:
		return errors.New("cannot push a digest reference")
	case reference.Tagged:
		tag = x.Tag()
	}

	// Resolve the Repository name from fqn to RepositoryInfo
	repoInfo, err := registry.ParseRepositoryInfo(ref)
	if err != nil {
		return err
	}
	// Resolve the Auth config relevant for this server
	authConfig := registry.ResolveAuthConfig(cli.configFile.AuthConfigs, repoInfo.Index)

	requestPrivilege := cli.registryAuthenticationPrivilegedFunc(repoInfo.Index, "push")
	if isTrusted() {
		return cli.trustedPush(repoInfo, tag, authConfig, requestPrivilege)
	}

	return cli.imagePushPrivileged(authConfig, ref.Name(), tag, cli.out, requestPrivilege)
}

func (cli *DockerCli) imagePushPrivileged(authConfig cliconfig.AuthConfig, imageID, tag string, outputStream io.Writer, requestPrivilege lib.RequestPrivilegeFunc) error {
	encodedAuth, err := authConfig.EncodeToBase64()
	if err != nil {
		return err
	}
	options := types.ImagePushOptions{
		ImageID:      imageID,
		Tag:          tag,
		RegistryAuth: encodedAuth,
	}

	responseBody, err := cli.client.ImagePush(options, requestPrivilege)
	if err != nil {
		return err
	}
	defer responseBody.Close()

	return jsonmessage.DisplayJSONMessagesStream(responseBody, outputStream, cli.outFd, cli.isTerminalOut)
}
<|endoftext|>


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

/*
Package ecbrates 0.2.0
This package helps parse the ECB exchange rates and use it for an applications

Example 1:

	package main

	import (
		"fmt"
		"log"

		"github.com/openprovider/ecbrates"
	)

	func main() {
		r, err := ecbrates.New()
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Case 1: get dollar rate relative to euro
		if value, ok := r.Rate[ecbrates.USD].(string); ok {
			fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", value)
		}

		// Case 2: convert of 100 euros to dollars
		if value, err := r.Convert(100, ecbrates.EUR, ecbrates.USD); err == nil {
			fmt.Println("Exchange rate", r.Date, ": EUR 100.0 -> USD", value)
		}
	}

Example 2:

	package main

	import (
		"fmt"
		"log"

		"github.com/openprovider/ecbrates"
	)

	func main() {
		rates, err := ecbrates.Load() // load last 90 days
		// rates, err := ecbrates.LoadAll() // <- load ALL historical data, lots of data!
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Show history of exchange rates for EUR -> USD
		for _, r := range rates {
			if value, ok := r.Rate[ecbrates.USD].(string); ok {
				fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", value)
			}
		}
	}

The European Central Bank exchange rates
*/
package ecbrates

import (
	"encoding/xml"
	"errors"
	"math"
	"net/http"
	"strconv"
)

// List of all supported currencies
const (
	AUD Currency = "AUD" // Australian Dollar (A$)
	BGN Currency = "BGN" // Bulgarian Lev (BGN)
	BRL Currency = "BRL" // Brazilian Real (R$)
	CAD Currency = "CAD" // Canadian Dollar (CA$)
	CHF Currency = "CHF" // Swiss Franc (CHF)
	CNY Currency = "CNY" // Chinese Yuan (CN¥)
	CZK Currency = "CZK" // Czech Republic Koruna (CZK)
	DKK Currency = "DKK" // Danish Krone (DKK)
	EUR Currency = "EUR" // Euro (€)
	GBP Currency = "GBP" // British Pound Sterling (£)
	HKD Currency = "HKD" // Hong Kong Dollar (HK$)
	HRK Currency = "HRK" // Croatian Kuna (HRK)
	HUF Currency = "HUF" // Hungarian Forint (HUF)
	IDR Currency = "IDR" // Indonesian Rupiah (IDR)
	ILS Currency = "ILS" // Israeli New Sheqel (₪)
	INR Currency = "INR" // Indian Rupee (Rs.)
	JPY Currency = "JPY" // Japanese Yen (¥)
	KRW Currency = "KRW" // South Korean Won (₩)
	LTL Currency = "LTL" // Lithuanian Litas (LTL)
	MXN Currency = "MXN" // Mexican Peso (MX$)
	MYR Currency = "MYR" // Malaysian Ringgit (MYR)
	NOK Currency = "NOK" // Norwegian Krone (NOK)
	NZD Currency = "NZD" // New Zealand Dollar (NZ$)
	PHP Currency = "PHP" // Philippine Peso (Php)
	PLN Currency = "PLN" // Polish Zloty (PLN)
	RON Currency = "RON" // Romanian Leu (RON)
	RUB Currency = "RUB" // Russian Ruble (RUB)
	SEK Currency = "SEK" // Swedish Krona (SEK)
	SGD Currency = "SGD" // Singapore Dollar (SGD)
	THB Currency = "THB" // Thai Baht (฿)
	TRY Currency = "TRY" // Turkish Lira (TRY)
	USD Currency = "USD" // US Dollar ($)
	ZAR Currency = "ZAR" // South African Rand (ZAR)

	// Historical currencies
	CYP Currency = "CYP"
	EEK Currency = "EEK"
	ISK Currency = "ISK"
	LVL Currency = "LVL"
	MTL Currency = "MTL"
	SIT Currency = "SIT"
	SKK Currency = "SKK"
	ROL Currency = "ROL"
	TRL Currency = "TRL"

	ratesLastURL   = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
	rates90daysURL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml"
	ratesAllURL    = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml"
)

// Currency type as a link to string
type Currency string

// Rates represent date and currency exchange rates
type Rates struct {
	Date string
	Rate map[Currency]interface{}
}

// Currencies are valid values for currency
var Currencies = []Currency{
	AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EUR, GBP, HKD,
	HRK, HUF, IDR, ILS, INR, JPY, KRW, LTL, MXN, MYR, NOK,
	NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, USD, ZAR,

	// Historical currencies
	CYP, EEK, ISK, LVL, MTL, SIT, SKK, ROL, TRL,
}

// IsValid check Currency for valid value
func (c Currency) IsValid() bool {
	for _, value := range Currencies {
		if value == c {
			return true
		}
	}

	return false
}

// New - create a new instance of the rates and fetch a data from ECB
func New() (*Rates, error) {
	r := new(Rates)
	err := r.fetchDay()
	return r, err
}

// Load - create a new instances of the rates and fetch data for the last 90 days from ECB
func Load() ([]Rates, error) {
	return fetch90days()
}

// LoadAll - create a new instances of the rates and fetch all historical data from ECB
func LoadAll() ([]Rates, error) {
	return fetchAll()
}

// Convert a value "from" one Currency -> "to" other Currency
func (r *Rates) Convert(value float64, from, to Currency) (float64, error) {
	if r.Rate[to] == nil || r.Rate[from] == nil {
		return 0, errors.New("Perhaps one of the values of currencies does not exist")
	}
	errorMessage := "Perhaps one of the values of currencies could not parsed correctly"
	strFrom, okFrom := r.Rate[from].(string)
	strTo, okTo := r.Rate[to].(string)
	if !okFrom || !okTo {
		return 0, errors.New(errorMessage)
	}
	vFrom, err := strconv.ParseFloat(strFrom, 32)
	if err != nil {
		return 0, errors.New(errorMessage)
	}
	vTo, err := strconv.ParseFloat(strTo, 32)
	if err != nil {
		return 0, errors.New(errorMessage)
	}
	return round64(value*round64(vTo, 4)/round64(vFrom, 4), 4), nil

}

// ECB XML envelope
type envelope struct {
	Data []struct {
		Date  string `xml:"time,attr"`
		Rates []struct {
			Currency string `xml:"currency,attr"`
			Rate     string `xml:"rate,attr"`
		} `xml:"Cube"`
	} `xml:"Cube>Cube"`
}

// Fetch an exchange rates
func (r *Rates) fetchDay() error {

	response, err := http.Get(ratesLastURL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return err
	}

	for _, day := range raw.Data {
		r.Rate = make(map[Currency]interface{})

		// an exchange rates fetched relatively the EUR currency
		r.Rate[EUR] = "1"

		r.Date = day.Date

		for _, item := range day.Rates {
			r.Rate[Currency(item.Currency)] = item.Rate
		}
		break
	}

	return nil
}

// Fetch a lot of exchange rates
func fetch90days() ([]Rates, error) {
	return fetchHistorical(rates90daysURL)
}

// Fetch even more exchange rates
func fetchAll() ([]Rates, error) {
	return fetchHistorical(ratesAllURL)
}

func fetchHistorical(url string) ([]Rates, error) {

	var rates []Rates

	response, err := http.Get(url)
	if err != nil {
		return rates, err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return rates, err
	}

	for _, day := range raw.Data {

		var r Rates
		r.Rate = make(map[Currency]interface{})

		// an exchange rates fetched relatively the EUR currency
		r.Rate[EUR] = "1"

		r.Date = day.Date
		for _, item := range day.Rates {
			r.Rate[Currency(item.Currency)] = item.Rate
		}
		rates = append(rates, r)
	}
	return rates, nil
}

func round64(x float64, prec int) float64 {
	if math.IsNaN(x) || math.IsInf(x, 0) {
		return x
	}

	sign := 1.0
	if x < 0 {
		sign = -1
		x *= -1
	}

	var rounder float64
	pow := math.Pow(10, float64(prec))
	intermed := x * pow
	_, frac := math.Modf(intermed)

	if frac >= 0.5 {
		rounder = math.Ceil(intermed)
	} else {
		rounder = math.Floor(intermed)
	}

	return rounder / pow * sign
}
<commit_msg>Bumped version number to 0.3.0<commit_after>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

/*
Package ecbrates 0.3.0
This package helps parse the ECB exchange rates and use it for an applications

Example 1:

	package main

	import (
		"fmt"
		"log"

		"github.com/openprovider/ecbrates"
	)

	func main() {
		r, err := ecbrates.New()
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Case 1: get dollar rate relative to euro
		if value, ok := r.Rate[ecbrates.USD].(string); ok {
			fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", value)
		}

		// Case 2: convert of 100 euros to dollars
		if value, err := r.Convert(100, ecbrates.EUR, ecbrates.USD); err == nil {
			fmt.Println("Exchange rate", r.Date, ": EUR 100.0 -> USD", value)
		}
	}

Example 2:

	package main

	import (
		"fmt"
		"log"

		"github.com/openprovider/ecbrates"
	)

	func main() {
		rates, err := ecbrates.Load() // load last 90 days
		// rates, err := ecbrates.LoadAll() // <- load ALL historical data, lots of data!
		if err != nil {
			log.Fatal("Error: ", err)
		}

		// Show history of exchange rates for EUR -> USD
		for _, r := range rates {
			if value, ok := r.Rate[ecbrates.USD].(string); ok {
				fmt.Println("Exchange rate", r.Date, ": EUR 1 -> USD", value)
			}
		}
	}

The European Central Bank exchange rates
*/
package ecbrates

import (
	"encoding/xml"
	"errors"
	"math"
	"net/http"
	"strconv"
)

// List of all supported currencies
const (
	AUD Currency = "AUD" // Australian Dollar (A$)
	BGN Currency = "BGN" // Bulgarian Lev (BGN)
	BRL Currency = "BRL" // Brazilian Real (R$)
	CAD Currency = "CAD" // Canadian Dollar (CA$)
	CHF Currency = "CHF" // Swiss Franc (CHF)
	CNY Currency = "CNY" // Chinese Yuan (CN¥)
	CZK Currency = "CZK" // Czech Republic Koruna (CZK)
	DKK Currency = "DKK" // Danish Krone (DKK)
	EUR Currency = "EUR" // Euro (€)
	GBP Currency = "GBP" // British Pound Sterling (£)
	HKD Currency = "HKD" // Hong Kong Dollar (HK$)
	HRK Currency = "HRK" // Croatian Kuna (HRK)
	HUF Currency = "HUF" // Hungarian Forint (HUF)
	IDR Currency = "IDR" // Indonesian Rupiah (IDR)
	ILS Currency = "ILS" // Israeli New Sheqel (₪)
	INR Currency = "INR" // Indian Rupee (Rs.)
	JPY Currency = "JPY" // Japanese Yen (¥)
	KRW Currency = "KRW" // South Korean Won (₩)
	LTL Currency = "LTL" // Lithuanian Litas (LTL)
	MXN Currency = "MXN" // Mexican Peso (MX$)
	MYR Currency = "MYR" // Malaysian Ringgit (MYR)
	NOK Currency = "NOK" // Norwegian Krone (NOK)
	NZD Currency = "NZD" // New Zealand Dollar (NZ$)
	PHP Currency = "PHP" // Philippine Peso (Php)
	PLN Currency = "PLN" // Polish Zloty (PLN)
	RON Currency = "RON" // Romanian Leu (RON)
	RUB Currency = "RUB" // Russian Ruble (RUB)
	SEK Currency = "SEK" // Swedish Krona (SEK)
	SGD Currency = "SGD" // Singapore Dollar (SGD)
	THB Currency = "THB" // Thai Baht (฿)
	TRY Currency = "TRY" // Turkish Lira (TRY)
	USD Currency = "USD" // US Dollar ($)
	ZAR Currency = "ZAR" // South African Rand (ZAR)

	// Historical currencies
	CYP Currency = "CYP"
	EEK Currency = "EEK"
	ISK Currency = "ISK"
	LVL Currency = "LVL"
	MTL Currency = "MTL"
	SIT Currency = "SIT"
	SKK Currency = "SKK"
	ROL Currency = "ROL"
	TRL Currency = "TRL"

	ratesLastURL   = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
	rates90daysURL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml"
	ratesAllURL    = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml"
)

// Currency type as a link to string
type Currency string

// Rates represent date and currency exchange rates
type Rates struct {
	Date string
	Rate map[Currency]interface{}
}

// Currencies are valid values for currency
var Currencies = []Currency{
	AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, EUR, GBP, HKD,
	HRK, HUF, IDR, ILS, INR, JPY, KRW, LTL, MXN, MYR, NOK,
	NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, USD, ZAR,

	// Historical currencies
	CYP, EEK, ISK, LVL, MTL, SIT, SKK, ROL, TRL,
}

// IsValid check Currency for valid value
func (c Currency) IsValid() bool {
	for _, value := range Currencies {
		if value == c {
			return true
		}
	}

	return false
}

// New - create a new instance of the rates and fetch a data from ECB
func New() (*Rates, error) {
	r := new(Rates)
	err := r.fetchDay()
	return r, err
}

// Load - create a new instances of the rates and fetch data for the last 90 days from ECB
func Load() ([]Rates, error) {
	return fetch90days()
}

// LoadAll - create a new instances of the rates and fetch all historical data from ECB
func LoadAll() ([]Rates, error) {
	return fetchAll()
}

// Convert a value "from" one Currency -> "to" other Currency
func (r *Rates) Convert(value float64, from, to Currency) (float64, error) {
	if r.Rate[to] == nil || r.Rate[from] == nil {
		return 0, errors.New("Perhaps one of the values of currencies does not exist")
	}
	errorMessage := "Perhaps one of the values of currencies could not parsed correctly"
	strFrom, okFrom := r.Rate[from].(string)
	strTo, okTo := r.Rate[to].(string)
	if !okFrom || !okTo {
		return 0, errors.New(errorMessage)
	}
	vFrom, err := strconv.ParseFloat(strFrom, 32)
	if err != nil {
		return 0, errors.New(errorMessage)
	}
	vTo, err := strconv.ParseFloat(strTo, 32)
	if err != nil {
		return 0, errors.New(errorMessage)
	}
	return round64(value*round64(vTo, 4)/round64(vFrom, 4), 4), nil

}

// ECB XML envelope
type envelope struct {
	Data []struct {
		Date  string `xml:"time,attr"`
		Rates []struct {
			Currency string `xml:"currency,attr"`
			Rate     string `xml:"rate,attr"`
		} `xml:"Cube"`
	} `xml:"Cube>Cube"`
}

// Fetch an exchange rates
func (r *Rates) fetchDay() error {

	response, err := http.Get(ratesLastURL)
	if err != nil {
		return err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return err
	}

	for _, day := range raw.Data {
		r.Rate = make(map[Currency]interface{})

		// an exchange rates fetched relatively the EUR currency
		r.Rate[EUR] = "1"

		r.Date = day.Date

		for _, item := range day.Rates {
			r.Rate[Currency(item.Currency)] = item.Rate
		}
		break
	}

	return nil
}

// Fetch a lot of exchange rates
func fetch90days() ([]Rates, error) {
	return fetchHistorical(rates90daysURL)
}

// Fetch even more exchange rates
func fetchAll() ([]Rates, error) {
	return fetchHistorical(ratesAllURL)
}

func fetchHistorical(url string) ([]Rates, error) {

	var rates []Rates

	response, err := http.Get(url)
	if err != nil {
		return rates, err
	}
	defer response.Body.Close()

	var raw envelope

	if err := xml.NewDecoder(response.Body).Decode(&raw); err != nil {
		return rates, err
	}

	for _, day := range raw.Data {

		var r Rates
		r.Rate = make(map[Currency]interface{})

		// an exchange rates fetched relatively the EUR currency
		r.Rate[EUR] = "1"

		r.Date = day.Date
		for _, item := range day.Rates {
			r.Rate[Currency(item.Currency)] = item.Rate
		}
		rates = append(rates, r)
	}
	return rates, nil
}

func round64(x float64, prec int) float64 {
	if math.IsNaN(x) || math.IsInf(x, 0) {
		return x
	}

	sign := 1.0
	if x < 0 {
		sign = -1
		x *= -1
	}

	var rounder float64
	pow := math.Pow(10, float64(prec))
	intermed := x * pow
	_, frac := math.Modf(intermed)

	if frac >= 0.5 {
		rounder = math.Ceil(intermed)
	} else {
		rounder = math.Floor(intermed)
	}

	return rounder / pow * sign
}
<|endoftext|>