<commit_before>package reporter

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/summerwind/h2spec/spec"
)

type JUnitTestReport struct {
	XMLName    xml.Name          `xml:"testsuites"`
	TestSuites []*JUnitTestSuite `xml:"testsuite"`
}

type JUnitTestSuite struct {
	XMLName   xml.Name         `xml:"testsuite"`
	Name      string           `xml:"name,attr"`
	Package   string           `xml:"package,attr"`
	ID        string           `xml:"id,attr"`
	Tests     int              `xml:"tests,attr"`
	Skipped   int              `xml:"skipped,attr"`
	Failures  int              `xml:"failures,attr"`
	Errors    int              `xml:"errors,attr"`
	TestCases []*JUnitTestCase `xml:"testcase"`
}

type JUnitTestCase struct {
	XMLName   xml.Name      `xml:"testcase"`
	Package   string        `xml:"package,attr"`
	ClassName string        `xml:"classname,attr"`
	Time      string        `xml:"time,attr"`
	Failure   *JUnitFailure `xml:"failure"`
	Skipped   *JUnitSkipped `xml:"skipped"`
}

type JUnitFailure struct {
	XMLName xml.Name `xml:"failure"`
	Content string   `xml:",innerxml"`
}

type JUnitSkipped struct {
	XMLName xml.Name `xml:"skipped"`
	Content string   `xml:",innerxml"`
}

func JUnitReport(groups []*spec.TestGroup, filePath string) error {
	report := JUnitTestReport{
		TestSuites: convertJUnitReport(groups),
	}

	buf, err := xml.MarshalIndent(report, "", "  ")
	if err != nil {
		return err
	}

	body := fmt.Sprintf("%s%s", xml.Header, buf)
	return ioutil.WriteFile(filePath, []byte(body), os.ModePerm)
}

func convertJUnitReport(groups []*spec.TestGroup) []*JUnitTestSuite {
	ts := make([]*JUnitTestSuite, 20)

	for _, tg := range groups {
		tests := append(tg.Tests, tg.StrictTests...)
		if len(tests) == 0 {
			ts = append(ts, convertJUnitReport(tg.Groups)...)
			continue
		}

		jts := &JUnitTestSuite{
			Package:   tg.ID(),
			Name:      fmt.Sprintf("%s. %s", tg.Section, tg.Name),
			ID:        tg.Section,
			Tests:     0,
			Skipped:   0,
			Failures:  0,
			Errors:    0,
			TestCases: make([]*JUnitTestCase, 20),
		}

		for _, tc := range tests {
			if tc.Result == nil {
				continue
			}

			jtc := &JUnitTestCase{
				Package:   tg.ID(),
				ClassName: tc.Desc,
				Time:      fmt.Sprintf("%.04f", tc.Result.Duration.Seconds()),
			}

			jts.Tests += 1
			if tc.Result.Skipped {
				jts.Skipped += 1
				jtc.Skipped = &JUnitSkipped{}
			} else if tc.Result.Failed {
				jts.Failures += 1

				err := tc.Result.Error.(*spec.TestError)
				expected := strings.Join(err.Expected, "\n")
				actual := err.Actual

				jtc.Failure = &JUnitFailure{
					Content: fmt.Sprintf("Expect:\n%s\nActual:\n%s", expected, actual),
				}
			}

			jts.TestCases = append(jts.TestCases, jtc)
		}

		ts = append(ts, jts)
		ts = append(ts, convertJUnitReport(tg.Groups)...)
	}

	return ts
}
<commit_msg>Fix for issue #63, Test error string is valid to report alongside spec.TestError.<commit_after>package reporter

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/summerwind/h2spec/spec"
)

type JUnitTestReport struct {
	XMLName    xml.Name          `xml:"testsuites"`
	TestSuites []*JUnitTestSuite `xml:"testsuite"`
}

type JUnitTestSuite struct {
	XMLName   xml.Name         `xml:"testsuite"`
	Name      string           `xml:"name,attr"`
	Package   string           `xml:"package,attr"`
	ID        string           `xml:"id,attr"`
	Tests     int              `xml:"tests,attr"`
	Skipped   int              `xml:"skipped,attr"`
	Failures  int              `xml:"failures,attr"`
	Errors    int              `xml:"errors,attr"`
	TestCases []*JUnitTestCase `xml:"testcase"`
}

type JUnitTestCase struct {
	XMLName   xml.Name      `xml:"testcase"`
	Package   string        `xml:"package,attr"`
	ClassName string        `xml:"classname,attr"`
	Time      string        `xml:"time,attr"`
	Failure   *JUnitFailure `xml:"failure"`
	Skipped   *JUnitSkipped `xml:"skipped"`
}

type JUnitFailure struct {
	XMLName xml.Name `xml:"failure"`
	Content string   `xml:",innerxml"`
}

type JUnitSkipped struct {
	XMLName xml.Name `xml:"skipped"`
	Content string   `xml:",innerxml"`
}

func JUnitReport(groups []*spec.TestGroup, filePath string) error {
	report := JUnitTestReport{
		TestSuites: convertJUnitReport(groups),
	}

	buf, err := xml.MarshalIndent(report, "", "  ")
	if err != nil {
		return err
	}

	body := fmt.Sprintf("%s%s", xml.Header, buf)
	return ioutil.WriteFile(filePath, []byte(body), os.ModePerm)
}

func convertJUnitReport(groups []*spec.TestGroup) []*JUnitTestSuite {
	ts := make([]*JUnitTestSuite, 20)

	for _, tg := range groups {
		tests := append(tg.Tests, tg.StrictTests...)
		if len(tests) == 0 {
			ts = append(ts, convertJUnitReport(tg.Groups)...)
			continue
		}

		jts := &JUnitTestSuite{
			Package:   tg.ID(),
			Name:      fmt.Sprintf("%s. %s", tg.Section, tg.Name),
			ID:        tg.Section,
			Tests:     0,
			Skipped:   0,
			Failures:  0,
			Errors:    0,
			TestCases: make([]*JUnitTestCase, 20),
		}

		for _, tc := range tests {
			if tc.Result == nil {
				continue
			}

			jtc := &JUnitTestCase{
				Package:   tg.ID(),
				ClassName: tc.Desc,
				Time:      fmt.Sprintf("%.04f", tc.Result.Duration.Seconds()),
			}

			jts.Tests += 1
			if tc.Result.Skipped {
				jts.Skipped += 1
				jtc.Skipped = &JUnitSkipped{}
			} else if tc.Result.Failed {
				jts.Failures += 1
				switch tc.Result.Error.(type) {
				case spec.TestError:
					err := tc.Result.Error.(*spec.TestError)
					expected := strings.Join(err.Expected, "\n")
					actual := err.Actual

					jtc.Failure = &JUnitFailure{
						Content: fmt.Sprintf("Expect:\n%s\nActual:\n%s", expected, actual),
					}
				default:
					err := tc.Result.Error
					jtc.Failure = &JUnitFailure{
						Content: fmt.Sprintf("Test in Error: %s", err),
					}
				}
			}

			jts.TestCases = append(jts.TestCases, jtc)
		}

		ts = append(ts, jts)
		ts = append(ts, convertJUnitReport(tg.Groups)...)
	}

	return ts
}
<|endoftext|>


<commit_before>package aws

import (
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSElasticacheCluster_importBasic(t *testing.T) {
	resourceName := "aws_elasticache_cluster.bar"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSElasticacheClusterDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSElasticacheClusterConfig,
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<commit_msg>provider/aws: Set AWS region in elasticache cluster import test<commit_after>package aws

import (
	"os"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
)

func TestAccAWSElasticacheCluster_importBasic(t *testing.T) {
	oldvar := os.Getenv("AWS_DEFAULT_REGION")
	os.Setenv("AWS_DEFAULT_REGION", "us-east-1")
	defer os.Setenv("AWS_DEFAULT_REGION", oldvar)

	resourceName := "aws_elasticache_cluster.bar"

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSElasticacheClusterDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSElasticacheClusterConfig,
			},

			resource.TestStep{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}
<|endoftext|>


<commit_before>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 unpacker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"time"

	"github.com/pengsrc/go-shared/convert"

	"github.com/yunify/qingstor-sdk-go/logger"
	"github.com/yunify/qingstor-sdk-go/request/data"
)

// BaseUnpacker is the base unpacker for all services.
type BaseUnpacker struct {
	operation    *data.Operation
	httpResponse *http.Response
	output       *reflect.Value
}

// UnpackHTTPRequest unpacks http response with an operation and an output.
func (b *BaseUnpacker) UnpackHTTPRequest(o *data.Operation, r *http.Response, x *reflect.Value) error {
	b.operation = o
	b.httpResponse = r
	b.output = x

	err := b.exposeStatusCode()
	if err != nil {
		return err
	}
	err = b.parseResponseHeaders()
	if err != nil {
		return err
	}
	err = b.parseResponseBody()
	if err != nil {
		return err
	}
	err = b.parseResponseElements()
	if err != nil {
		return err
	}

	return nil
}

func (b *BaseUnpacker) exposeStatusCode() error {
	value := b.output.Elem().FieldByName("StatusCode")
	if value.IsValid() {
		switch value.Interface().(type) {
		case *int:
			logger.Infof(nil, fmt.Sprintf(
				"QingStor response status code: [%d] %d",
				convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
				b.httpResponse.StatusCode,
			))
			value.Set(reflect.ValueOf(&b.httpResponse.StatusCode))
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseHeaders() error {
	logger.Infof(nil, fmt.Sprintf(
		"QingStor response headers: [%d] %s",
		convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
		fmt.Sprint(b.httpResponse.Header),
	))

	if b.isResponseRight() {
		fields := b.output.Elem()
		for i := 0; i < fields.NumField(); i++ {
			field := fields.Field(i)
			fieldTagName := fields.Type().Field(i).Tag.Get("name")
			fieldTagLocation := fields.Type().Field(i).Tag.Get("location")
			fieldStringValue := b.httpResponse.Header.Get(fieldTagName)

			if fieldTagName != "" && fieldTagLocation == "headers" {
				switch field.Interface().(type) {
				case *string:
					field.Set(reflect.ValueOf(&fieldStringValue))
				case *int:
					intValue, err := strconv.Atoi(fieldStringValue)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&intValue))
				case *int64:
					int64Value, err := strconv.ParseInt(fieldStringValue, 10, 64)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&int64Value))
				case *bool:
				case *time.Time:
					formatString := fields.Type().Field(i).Tag.Get("format")
					format := ""
					switch formatString {
					case "RFC 822":
						format = convert.RFC822
					case "ISO 8601":
						format = convert.ISO8601
					}
					timeValue, err := convert.StringToTime(fieldStringValue, format)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&timeValue))
				}
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseBody() error {
	if b.isResponseRight() {
		value := b.output.Elem().FieldByName("Body")
		if value.IsValid() {
			switch value.Type().String() {
			case "string":
				buffer := &bytes.Buffer{}
				buffer.ReadFrom(b.httpResponse.Body)
				b.httpResponse.Body.Close()

				logger.Infof(nil, fmt.Sprintf(
					"QingStor response body string: [%d] %s",
					convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
					string(buffer.Bytes()),
				))

				value.SetString(string(buffer.Bytes()))
			case "io.ReadCloser":
				value.Set(reflect.ValueOf(b.httpResponse.Body))
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseElements() error {
	if b.isResponseRight() {
		if b.httpResponse.Header.Get("Content-Type") == "application/json" {
			buffer := &bytes.Buffer{}
			buffer.ReadFrom(b.httpResponse.Body)
			b.httpResponse.Body.Close()

			logger.Infof(nil, fmt.Sprintf(
				"QingStor response body string: [%d] %s",
				convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
				string(buffer.Bytes()),
			))

			err := json.Unmarshal(buffer.Bytes(), b.output.Interface())
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) isResponseRight() bool {
	rightStatusCodes := b.operation.StatusCodes
	if len(rightStatusCodes) == 0 {
		rightStatusCodes = append(rightStatusCodes, 200)
	}

	flag := false
	for _, statusCode := range rightStatusCodes {
		if statusCode == b.httpResponse.StatusCode {
			flag = true
		}
	}

	return flag
}
<commit_msg>request: Skip empty header<commit_after>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 unpacker

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"time"

	"github.com/pengsrc/go-shared/convert"

	"github.com/yunify/qingstor-sdk-go/logger"
	"github.com/yunify/qingstor-sdk-go/request/data"
)

// BaseUnpacker is the base unpacker for all services.
type BaseUnpacker struct {
	operation    *data.Operation
	httpResponse *http.Response
	output       *reflect.Value
}

// UnpackHTTPRequest unpacks http response with an operation and an output.
func (b *BaseUnpacker) UnpackHTTPRequest(o *data.Operation, r *http.Response, x *reflect.Value) error {
	b.operation = o
	b.httpResponse = r
	b.output = x

	err := b.exposeStatusCode()
	if err != nil {
		return err
	}
	err = b.parseResponseHeaders()
	if err != nil {
		return err
	}
	err = b.parseResponseBody()
	if err != nil {
		return err
	}
	err = b.parseResponseElements()
	if err != nil {
		return err
	}

	return nil
}

func (b *BaseUnpacker) exposeStatusCode() error {
	value := b.output.Elem().FieldByName("StatusCode")
	if value.IsValid() {
		switch value.Interface().(type) {
		case *int:
			logger.Infof(nil, fmt.Sprintf(
				"QingStor response status code: [%d] %d",
				convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
				b.httpResponse.StatusCode,
			))
			value.Set(reflect.ValueOf(&b.httpResponse.StatusCode))
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseHeaders() error {
	logger.Infof(nil, fmt.Sprintf(
		"QingStor response headers: [%d] %s",
		convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
		fmt.Sprint(b.httpResponse.Header),
	))

	if b.isResponseRight() {
		fields := b.output.Elem()
		for i := 0; i < fields.NumField(); i++ {
			field := fields.Field(i)
			fieldTagName := fields.Type().Field(i).Tag.Get("name")
			fieldTagLocation := fields.Type().Field(i).Tag.Get("location")
			fieldStringValue := b.httpResponse.Header.Get(fieldTagName)

			// Empty value should be ignored.
			if fieldStringValue == "" {
				continue
			}

			if fieldTagName != "" && fieldTagLocation == "headers" {
				switch field.Interface().(type) {
				case *string:
					field.Set(reflect.ValueOf(&fieldStringValue))
				case *int:
					intValue, err := strconv.Atoi(fieldStringValue)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&intValue))
				case *int64:
					int64Value, err := strconv.ParseInt(fieldStringValue, 10, 64)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&int64Value))
				case *bool:
				case *time.Time:
					formatString := fields.Type().Field(i).Tag.Get("format")
					format := ""
					switch formatString {
					case "RFC 822":
						format = convert.RFC822
					case "ISO 8601":
						format = convert.ISO8601
					}
					timeValue, err := convert.StringToTime(fieldStringValue, format)
					if err != nil {
						return err
					}
					field.Set(reflect.ValueOf(&timeValue))
				}
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseBody() error {
	if b.isResponseRight() {
		value := b.output.Elem().FieldByName("Body")
		if value.IsValid() {
			switch value.Type().String() {
			case "string":
				buffer := &bytes.Buffer{}
				buffer.ReadFrom(b.httpResponse.Body)
				b.httpResponse.Body.Close()

				logger.Infof(nil, fmt.Sprintf(
					"QingStor response body string: [%d] %s",
					convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
					string(buffer.Bytes()),
				))

				value.SetString(string(buffer.Bytes()))
			case "io.ReadCloser":
				value.Set(reflect.ValueOf(b.httpResponse.Body))
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) parseResponseElements() error {
	if b.isResponseRight() {
		if b.httpResponse.Header.Get("Content-Type") == "application/json" {
			buffer := &bytes.Buffer{}
			buffer.ReadFrom(b.httpResponse.Body)
			b.httpResponse.Body.Close()

			logger.Infof(nil, fmt.Sprintf(
				"QingStor response body string: [%d] %s",
				convert.StringToUnixTimestamp(b.httpResponse.Header.Get("Date"), convert.RFC822),
				string(buffer.Bytes()),
			))

			err := json.Unmarshal(buffer.Bytes(), b.output.Interface())
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (b *BaseUnpacker) isResponseRight() bool {
	rightStatusCodes := b.operation.StatusCodes
	if len(rightStatusCodes) == 0 {
		rightStatusCodes = append(rightStatusCodes, 200)
	}

	flag := false
	for _, statusCode := range rightStatusCodes {
		if statusCode == b.httpResponse.StatusCode {
			flag = true
		}
	}

	return flag
}
<|endoftext|>


<commit_before>package main

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

	"github.com/codegangsta/cli"
	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func prepare(amqpUri string, queueName string) (*amqp.Connection, *amqp.Channel) {
	conn, err := amqp.Dial(amqpUri)
	failOnError(err, "Failed to connect to AMPQ broker")

	channel, err := conn.Channel()
	failOnError(err, "Failed to open a channel")

	_, err = channel.QueueDeclare(
		queueName, // name
		true,      // durable
		false,     // delete when unused
		false,     // exclusive
		false,     // no-wait
		nil,       // arguments
	)
	failOnError(err, "Failed to declare queue")

	return conn, channel
}

func main() {
	app := cli.NewApp()
	app.Name = "pipecat"
	app.Usage = "Connect unix pipes and message queues"

	globalFlags := []cli.Flag{
		cli.StringFlag{
			Name:   "amqpUri",
			Value:  "amqp://guest:guest@localhost:5672/",
			Usage:  "AMQP URI",
			EnvVar: "AMQP_URI",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "publish",
			Aliases: []string{"p"},
			Usage:   "Publish messages to queue",
			Flags:   globalFlags,
			Action: func(c *cli.Context) {
				queueName := c.Args().First()
				if queueName == "" {
					fmt.Println("Please provide name of the queue")
					os.Exit(1)
				}

				conn, channel := prepare(c.String("amqpUri"), queueName)
				defer conn.Close()
				defer channel.Close()

				scanner := bufio.NewScanner(os.Stdin)
				for scanner.Scan() {
					line := scanner.Text()
					err := channel.Publish(
						"",        // exchange
						queueName, // routing key
						false,     // mandatory
						false,     // immediate
						amqp.Publishing{
							ContentType: "text/plain",
							Body:        []byte(line),
						})

					failOnError(err, "Failed to publish a message")
					fmt.Println(line)
				}
				if err := scanner.Err(); err != nil {
					fmt.Fprintln(os.Stderr, "Reading standard input:", err)
				}

			},
		},
		{
			Name:    "consume",
			Flags:   globalFlags,
			Aliases: []string{"c"},
			Usage:   "Consume messages from queue",
			Action: func(c *cli.Context) {
				queueName := c.Args().First()
				if queueName == "" {
					fmt.Println("Please provide name of the queue")
					os.Exit(1)
				}

				conn, channel := prepare(c.String("amqpUri"), queueName)
				defer conn.Close()
				defer channel.Close()

				var mutex sync.Mutex
				unackedMessages := make([]amqp.Delivery, 100)

				msgs, err := channel.Consume(
					queueName, // queue
					"",        // consumer
					false,     // auto-ack
					false,     // exclusive
					false,     // no-local
					false,     // no-wait
					nil,       // args
				)
				failOnError(err, "Failed to register a consumer")

				forever := make(chan bool)

				go func() {

					scanner := bufio.NewScanner(os.Stdin)
					for scanner.Scan() {
						ackedLine := scanner.Text()

						mutex.Lock()
						for _, msg := range unackedMessages {
							unackedLine := fmt.Sprintf("%s", msg.Body)
							if unackedLine == ackedLine {
								msg.Ack(false)
							}
						}
						mutex.Unlock()
					}
					if err := scanner.Err(); err != nil {
						fmt.Fprintln(os.Stderr, "Reading standard input:", err)
					}

				}()

				go func() {
					for msg := range msgs {
						mutex.Lock()
						unackedMessages = append(unackedMessages, msg)
						mutex.Unlock()

						line := fmt.Sprintf("%s", msg.Body)
						fmt.Println(line)
					}
				}()
				<-forever

			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>Pulled consume and publish methods out<commit_after>package main

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

	"github.com/codegangsta/cli"
	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func prepare(amqpUri string, queueName string) (*amqp.Connection, *amqp.Channel) {
	conn, err := amqp.Dial(amqpUri)
	failOnError(err, "Failed to connect to AMPQ broker")

	channel, err := conn.Channel()
	failOnError(err, "Failed to open a channel")

	_, err = channel.QueueDeclare(
		queueName, // name
		true,      // durable
		false,     // delete when unused
		false,     // exclusive
		false,     // no-wait
		nil,       // arguments
	)
	failOnError(err, "Failed to declare queue")

	return conn, channel
}

func publish(c *cli.Context) {
	queueName := c.Args().First()
	if queueName == "" {
		fmt.Println("Please provide name of the queue")
		os.Exit(1)
	}

	conn, channel := prepare(c.String("amqpUri"), queueName)
	defer conn.Close()
	defer channel.Close()

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		line := scanner.Text()
		err := channel.Publish(
			"",        // exchange
			queueName, // routing key
			false,     // mandatory
			false,     // immediate
			amqp.Publishing{
				ContentType: "text/plain",
				Body:        []byte(line),
			})

		failOnError(err, "Failed to publish a message")
		fmt.Println(line)
	}
	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "Reading standard input:", err)
	}

}

func consume(c *cli.Context) {
	queueName := c.Args().First()
	if queueName == "" {
		fmt.Println("Please provide name of the queue")
		os.Exit(1)
	}

	conn, channel := prepare(c.String("amqpUri"), queueName)
	defer conn.Close()
	defer channel.Close()

	var mutex sync.Mutex
	unackedMessages := make([]amqp.Delivery, 100)

	msgs, err := channel.Consume(
		queueName, // queue
		"",        // consumer
		false,     // auto-ack
		false,     // exclusive
		false,     // no-local
		false,     // no-wait
		nil,       // args
	)
	failOnError(err, "Failed to register a consumer")

	forever := make(chan bool)

	go func() {

		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			ackedLine := scanner.Text()

			mutex.Lock()
			for _, msg := range unackedMessages {
				unackedLine := fmt.Sprintf("%s", msg.Body)
				if unackedLine == ackedLine {
					msg.Ack(false)
				}
			}
			mutex.Unlock()
		}
		if err := scanner.Err(); err != nil {
			fmt.Fprintln(os.Stderr, "Reading standard input:", err)
		}

	}()

	go func() {
		for msg := range msgs {
			mutex.Lock()
			unackedMessages = append(unackedMessages, msg)
			mutex.Unlock()

			line := fmt.Sprintf("%s", msg.Body)
			fmt.Println(line)
		}
	}()
	<-forever
}

func main() {
	app := cli.NewApp()
	app.Name = "pipecat"
	app.Usage = "Connect unix pipes and message queues"

	globalFlags := []cli.Flag{
		cli.StringFlag{
			Name:   "amqpUri",
			Value:  "amqp://guest:guest@localhost:5672/",
			Usage:  "AMQP URI",
			EnvVar: "AMQP_URI",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "publish",
			Aliases: []string{"p"},
			Usage:   "Publish messages to queue",
			Flags:   globalFlags,
			Action:  publish,
		},
		{
			Name:    "consume",
			Flags:   globalFlags,
			Aliases: []string{"c"},
			Usage:   "Consume messages from queue",
			Action:  consume,
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// 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 remotestorage

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

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

type Uploader interface {
	UploadFile(bucket, src, dst string, opts ...OpOption) error
	UploadDir(bucket, src, dst string, opts ...OpOption) error
}

// GoogleCloudStorage wraps Google Cloud Storage API.
type GoogleCloudStorage struct {
	JSONKey []byte
	Project string
	Config  *jwt.Config
}

func NewGoogleCloudStorage(key []byte, project string) (Uploader, error) {
	conf, err := google.JWTConfigFromJSON(
		key,
		storage.ScopeFullControl,
	)
	if err != nil {
		return nil, err
	}
	return &GoogleCloudStorage{
		JSONKey: key,
		Project: project,
		Config:  conf,
	}, nil
}

func (g *GoogleCloudStorage) UploadFile(bucket, src, dst string, opts ...OpOption) error {
	if g == nil {
		return fmt.Errorf("GoogleCloudStorage is nil")
	}
	ret := &Op{}
	ret.applyOpts(opts)

	ctx := context.Background()
	admin, err := storage.NewAdminClient(ctx, g.Project, cloud.WithTokenSource(g.Config.TokenSource(ctx)))
	if err != nil {
		return err
	}
	defer admin.Close()

	if err := admin.CreateBucket(context.Background(), bucket, nil); err != nil {
		if !strings.Contains(err.Error(), "You already own this bucket. Please select another name") {
			return err
		}
	}

	sctx := context.Background()
	client, err := storage.NewClient(sctx, cloud.WithTokenSource(g.Config.TokenSource(sctx)))
	if err != nil {
		return err
	}
	defer client.Close()

	wc := client.Bucket(bucket).Object(dst).NewWriter(context.Background())
	if ret.ContentType != "" {
		wc.ContentType = ret.ContentType
	}

	fmt.Println("UploadFile:")
	fmt.Println()
	fmt.Println(src)
	fmt.Println("--->")
	fmt.Println(dst)
	fmt.Println()

	bts, err := ioutil.ReadFile(src)
	if err != nil {
		return fmt.Errorf("ioutil.ReadFile(%s) %v", src, err)
	}
	if _, err := wc.Write(bts); err != nil {
		return err
	}
	if err := wc.Close(); err != nil {
		return err
	}

	fmt.Println()
	fmt.Println("UploadFile Done!")
	fmt.Println()
	return nil
}

func (g *GoogleCloudStorage) UploadDir(bucket, src, dst string, opts ...OpOption) error {
	if g == nil {
		return fmt.Errorf("GoogleCloudStorage is nil")
	}
	ret := &Op{}
	ret.applyOpts(opts)

	ctx := context.Background()
	admin, err := storage.NewAdminClient(ctx, g.Project, cloud.WithTokenSource(g.Config.TokenSource(ctx)))
	if err != nil {
		return err
	}
	defer admin.Close()

	if err := admin.CreateBucket(context.Background(), bucket, nil); err != nil {
		if !strings.Contains(err.Error(), "You already own this bucket. Please select another name") {
			return err
		}
	}

	sctx := context.Background()
	client, err := storage.NewClient(sctx, cloud.WithTokenSource(g.Config.TokenSource(sctx)))
	if err != nil {
		return err
	}
	defer client.Close()

	fmap, err := walkRecursive(src)
	if err != nil {
		return err
	}

	fmt.Println("UploadDir:")
	donec, errc := make(chan struct{}), make(chan error)
	for source := range fmap {
		go func(source string) {
			s := strings.Replace(source, src, "", -1)
			tp := filepath.Join(dst, s)

			fmt.Println()
			fmt.Println(source)
			fmt.Println("--->")
			fmt.Println(tp)
			fmt.Println()

			wc := client.Bucket(bucket).Object(tp).NewWriter(context.Background())
			if ret.ContentType != "" {
				wc.ContentType = ret.ContentType
			}
			bts, err := ioutil.ReadFile(source)
			if err != nil {
				errc <- fmt.Errorf("ioutil.ReadFile(%s) %v", source, err)
				return
			}
			if _, err := wc.Write(bts); err != nil {
				errc <- err
				return
			}
			if err := wc.Close(); err != nil {
				errc <- err
				return
			}
			donec <- struct{}{}
		}(source)
	}

	cnt, num := 0, len(fmap)
	for cnt != num {
		select {
		case <-donec:
		case err := <-errc:
			return err
		}
		cnt++
	}
	fmt.Println()
	fmt.Println("UploadDir Done!")
	fmt.Println()
	return nil
}
<commit_msg>remotestorage: clean up vars<commit_after>// Copyright 2016 CoreOS, Inc.
//
// 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 remotestorage

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

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"golang.org/x/oauth2/jwt"
	"google.golang.org/cloud"
	"google.golang.org/cloud/storage"
)

type Uploader interface {
	UploadFile(bucket, src, dst string, opts ...OpOption) error
	UploadDir(bucket, src, dst string, opts ...OpOption) error
}

// GoogleCloudStorage wraps Google Cloud Storage API.
type GoogleCloudStorage struct {
	JSONKey []byte
	Project string
	Config  *jwt.Config
}

func NewGoogleCloudStorage(key []byte, project string) (Uploader, error) {
	conf, err := google.JWTConfigFromJSON(
		key,
		storage.ScopeFullControl,
	)
	if err != nil {
		return nil, err
	}
	return &GoogleCloudStorage{
		JSONKey: key,
		Project: project,
		Config:  conf,
	}, nil
}

func (g *GoogleCloudStorage) UploadFile(bucket, src, dst string, opts ...OpOption) error {
	if g == nil {
		return fmt.Errorf("GoogleCloudStorage is nil")
	}
	ret := &Op{}
	ret.applyOpts(opts)

	ctx := context.Background()
	admin, err := storage.NewAdminClient(ctx, g.Project, cloud.WithTokenSource(g.Config.TokenSource(ctx)))
	if err != nil {
		return err
	}
	defer admin.Close()

	if err := admin.CreateBucket(context.Background(), bucket, nil); err != nil {
		if !strings.Contains(err.Error(), "You already own this bucket. Please select another name") {
			return err
		}
	}

	sctx := context.Background()
	client, err := storage.NewClient(sctx, cloud.WithTokenSource(g.Config.TokenSource(sctx)))
	if err != nil {
		return err
	}
	defer client.Close()

	wc := client.Bucket(bucket).Object(dst).NewWriter(context.Background())
	if ret.ContentType != "" {
		wc.ContentType = ret.ContentType
	}

	fmt.Println("UploadFile:")
	fmt.Println()
	fmt.Println(src)
	fmt.Println("--->")
	fmt.Println(dst)
	fmt.Println()

	bts, err := ioutil.ReadFile(src)
	if err != nil {
		return fmt.Errorf("ioutil.ReadFile(%s) %v", src, err)
	}
	if _, err := wc.Write(bts); err != nil {
		return err
	}
	if err := wc.Close(); err != nil {
		return err
	}

	fmt.Println()
	fmt.Println("UploadFile Done!")
	fmt.Println()
	return nil
}

func (g *GoogleCloudStorage) UploadDir(bucket, src, dst string, opts ...OpOption) error {
	if g == nil {
		return fmt.Errorf("GoogleCloudStorage is nil")
	}
	ret := &Op{}
	ret.applyOpts(opts)

	ctx := context.Background()
	admin, err := storage.NewAdminClient(ctx, g.Project, cloud.WithTokenSource(g.Config.TokenSource(ctx)))
	if err != nil {
		return err
	}
	defer admin.Close()

	if err := admin.CreateBucket(context.Background(), bucket, nil); err != nil {
		if !strings.Contains(err.Error(), "You already own this bucket. Please select another name") {
			return err
		}
	}

	sctx := context.Background()
	client, err := storage.NewClient(sctx, cloud.WithTokenSource(g.Config.TokenSource(sctx)))
	if err != nil {
		return err
	}
	defer client.Close()

	fmap, err := walkRecursive(src)
	if err != nil {
		return err
	}

	fmt.Println("UploadDir:")
	donec, errc := make(chan struct{}), make(chan error)
	for fpath := range fmap {
		go func(fpath string) {
			targetPath := filepath.Join(dst, strings.Replace(fpath, src, "", -1))
			fmt.Println()
			fmt.Println(fpath)
			fmt.Println("--->")
			fmt.Println(targetPath)
			fmt.Println()

			wc := client.Bucket(bucket).Object(targetPath).NewWriter(context.Background())
			if ret.ContentType != "" {
				wc.ContentType = ret.ContentType
			}
			bts, err := ioutil.ReadFile(fpath)
			if err != nil {
				errc <- fmt.Errorf("ioutil.ReadFile(%s) %v", fpath, err)
				return
			}
			if _, err := wc.Write(bts); err != nil {
				errc <- err
				return
			}
			if err := wc.Close(); err != nil {
				errc <- err
				return
			}
			donec <- struct{}{}
		}(fpath)
	}

	cnt, num := 0, len(fmap)
	for cnt != num {
		select {
		case <-donec:
		case err := <-errc:
			return err
		}
		cnt++
	}
	fmt.Println()
	fmt.Println("UploadDir Done!")
	fmt.Println()
	return nil
}
<|endoftext|>


<commit_before>package particle

import (
	"math"
	"time"

	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/physics"
	"github.com/oakmound/oak/v3/render"
)

const (
	//IgnoreEnd refers to the life value given to particles that want to skip their source's end function.
	IgnoreEnd = -2000 / 2
)

// A Source is used to store and control a set of particles.
type Source struct {
	render.Layered
	Generator Generator
	*Allocator

	particles    [blockSize]Particle
	nextPID      int
	CID          event.CID
	pIDBlock     int
	stackLevel   int
	EndFunc      func()
	stopRotateAt time.Time
	paused       bool
	started      bool
	stopped      bool
}

// NewSource creates a new source
func NewSource(g Generator, stackLevel int) *Source {
	ps := new(Source)
	ps.Generator = g
	ps.stackLevel = stackLevel
	ps.Allocator = DefaultAllocator
	ps.Init()
	return ps
}

// Init allows a source to be considered as an entity, and initializes it
func (ps *Source) Init() event.CID {
	CID := event.NextID(ps)
	ps.stopRotateAt = time.Now().Add(
		time.Duration(ps.Generator.GetBaseGenerator().Duration.Poll()) * time.Millisecond)
	CID.Bind(event.Enter, rotateParticles)
	ps.CID = CID
	ps.pIDBlock = ps.Allocate(ps.CID)
	return CID
}

func (ps *Source) cycleParticles() bool {
	pg := ps.Generator.GetBaseGenerator()
	cycled := false
	for i := 0; i < ps.nextPID; i++ {
		p := ps.particles[i]
		bp := p.GetBaseParticle()
		for bp.Life <= 0 {
			p.Undraw()
			cycled = true
			if pg.EndFunc != nil && bp.Life > IgnoreEnd {
				pg.EndFunc(p)
			}
			ps.nextPID--
			if i == ps.nextPID {
				return cycled
			}
			ps.particles[i], ps.particles[ps.nextPID] = ps.particles[ps.nextPID], ps.particles[i]

			p = ps.particles[i]
			p.setPID(i + ps.pIDBlock*blockSize)
			bp = p.GetBaseParticle()
		}
		// Ignore dead particles
		if bp.Life > 0 {
			cycled = true
			bp.Life--
			// Apply rotational acceleration
			if pg.Rotation != nil {
				bp.Vel = bp.Vel.Rotate(pg.Rotation.Poll())
			}

			if pg.SpeedDecay.X() != 0 {
				bp.Vel = bp.Vel.SetX(bp.Vel.X() * pg.SpeedDecay.X())
				if math.Abs(bp.Vel.X()) < 0.2 {
					bp.Vel = bp.Vel.SetX(0)
				}
			}
			if pg.SpeedDecay.Y() != 0 {
				bp.Vel = bp.Vel.SetY(bp.Vel.Y() * pg.SpeedDecay.Y())
				if math.Abs(bp.Vel.Y()) < 0.2 {
					bp.Vel = bp.Vel.SetY(0)
				}
			}

			bp.Vel.Add(pg.Gravity)
			bp.Add(bp.Vel)
			bp.SetLayer(ps.Layer(bp.GetPos()))
			p.Cycle(ps.Generator)
		}
	}
	return cycled
}

// Layer is shorthand for getting the base generator behind a source's layer
func (ps *Source) Layer(v physics.Vector) int {
	return ps.Generator.GetBaseGenerator().LayerFunc(v)
}

func (ps *Source) addParticles() {
	pg := ps.Generator.GetBaseGenerator()
	// Regularly create particles (up until max particles)
	newParticleCount := int(pg.NewPerFrame.Poll())

	if ps.nextPID+newParticleCount >= blockSize {
		newParticleCount = blockSize - ps.nextPID
	}

	if pg.ParticleLimit != 0 {
		if ps.nextPID+newParticleCount >= pg.ParticleLimit {
			newParticleCount = pg.ParticleLimit - ps.nextPID
		}
	}

	var p Particle
	var bp *baseParticle
	for i := 0; i < newParticleCount; i++ {
		angle := pg.Angle.Poll()
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		// If this particle has not been allocated yet
		if ps.particles[ps.nextPID] == nil {
			bp = &baseParticle{
				LayeredPoint: render.NewLayeredPoint(
					pg.X()+floatFromSpread(pg.Spread.X()),
					pg.Y()+floatFromSpread(pg.Spread.Y()),
					0,
				),
				Src: ps,
				Vel: physics.NewVector(
					speed*math.Cos(angle)*-1,
					speed*math.Sin(angle)*-1),
				Life:      startLife,
				totalLife: startLife,
				pID:       ps.nextPID + ps.pIDBlock*blockSize,
			}

			p = ps.Generator.GenerateParticle(bp)

			// If this is a 'recycled' particle waiting to be redrawn
		} else {
			bp = ps.particles[ps.nextPID].GetBaseParticle()
			bp.LayeredPoint = render.NewLayeredPoint(
				pg.X()+floatFromSpread(pg.Spread.X()),
				pg.Y()+floatFromSpread(pg.Spread.Y()),
				0)
			bp.Vel = physics.NewVector(
				speed*math.Cos(angle)*-1,
				speed*math.Sin(angle)*-1)
			bp.Life = startLife
			bp.totalLife = startLife
			p = ps.Generator.GenerateParticle(bp)

		}
		ps.particles[ps.nextPID] = p
		ps.nextPID++
		p.SetLayer(ps.Layer(bp.GetPos()))
		render.Draw(p, ps.stackLevel)
	}

}

// rotateParticles updates particles over time as long
// as a Source is active.
func rotateParticles(id event.CID, payload interface{}) int {
	ps := id.E().(*Source)
	if !ps.started {
		ps.started = true
	}
	if !ps.paused {
		ps.cycleParticles()
		ps.addParticles()
	}
	if time.Now().After(ps.stopRotateAt) {
		return event.UnbindSingle
	}
	return 0
}

// clearParticles is used after a Source has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id event.CID, nothing interface{}) int {
	if ps, ok := id.E().(*Source); ok {
		if !ps.paused {
			if ps.cycleParticles() {
			} else {
				if ps.EndFunc != nil {
					ps.EndFunc()
				}
				event.DestroyEntity(id)
				ps.Deallocate(ps.pIDBlock)
				return event.UnbindEvent
			}
		}

		return 0
	}
	return event.UnbindEvent
}

// Stop manually stops a Source, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *Source) Stop() {
	if ps == nil {
		return
	}
	ps.stopped = true
	ps.CID.UnbindAllAndRebind([]event.Bindable{clearParticles}, []string{event.Enter})
}

// Pause on a Source just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *Source) Pause() {
	ps.paused = true
}

// UnPause on a source a Source rebinds it's rotate function.
func (ps *Source) UnPause() {
	ps.paused = false
}

// IsPaused checks for whether the source is currently in a paused state.
// It probably would have made more sense to export paused but this way if a lock is needed here in the future...
// Then it wont change the api.
func (ps *Source) IsPaused() bool {
	return ps.paused
}

// ShiftX shift's a source's underlying generator
func (ps *Source) ShiftX(x float64) {
	ps.Generator.ShiftX(x)
}

// ShiftY shift's a source's underlying generator
func (ps *Source) ShiftY(y float64) {
	ps.Generator.ShiftY(y)
}

// SetPos sets a source's underlying generator
func (ps *Source) SetPos(x, y float64) {
	ps.Generator.SetPos(x, y)
}
<commit_msg>render/particle/source: handle on timeout remaining particles gracefully with new timing<commit_after>package particle

import (
	"fmt"
	"math"
	"time"

	"github.com/oakmound/oak/v3/event"
	"github.com/oakmound/oak/v3/physics"
	"github.com/oakmound/oak/v3/render"
)

const (
	//IgnoreEnd refers to the life value given to particles that want to skip their source's end function.
	IgnoreEnd = -2000 / 2
)

// A Source is used to store and control a set of particles.
type Source struct {
	render.Layered
	Generator Generator
	*Allocator

	particles    [blockSize]Particle
	nextPID      int
	CID          event.CID
	pIDBlock     int
	stackLevel   int
	EndFunc      func()
	stopRotateAt time.Time
	paused       bool
	started      bool
	stopped      bool
}

// NewSource creates a new source
func NewSource(g Generator, stackLevel int) *Source {
	ps := new(Source)
	ps.Generator = g
	ps.stackLevel = stackLevel
	ps.Allocator = DefaultAllocator
	ps.Init()
	return ps
}

// Init allows a source to be considered as an entity, and initializes it
func (ps *Source) Init() event.CID {
	CID := event.NextID(ps)
	ps.stopRotateAt = time.Now().Add(
		time.Duration(ps.Generator.GetBaseGenerator().Duration.Poll()) * time.Millisecond)
	CID.Bind(event.Enter, rotateParticles)
	ps.CID = CID
	ps.pIDBlock = ps.Allocate(ps.CID)
	return CID
}

func (ps *Source) cycleParticles() bool {
	pg := ps.Generator.GetBaseGenerator()
	cycled := false
	for i := 0; i < ps.nextPID; i++ {
		p := ps.particles[i]
		bp := p.GetBaseParticle()
		for bp.Life <= 0 {
			p.Undraw()
			fmt.Println("UNDREWWWWW it")
			cycled = true
			if pg.EndFunc != nil && bp.Life > IgnoreEnd {
				pg.EndFunc(p)
			}
			ps.nextPID--
			if i == ps.nextPID {
				return cycled
			}
			ps.particles[i], ps.particles[ps.nextPID] = ps.particles[ps.nextPID], ps.particles[i]

			p = ps.particles[i]
			p.setPID(i + ps.pIDBlock*blockSize)
			bp = p.GetBaseParticle()
		}
		// Ignore dead particles
		if bp.Life > 0 {
			cycled = true
			bp.Life--
			// Apply rotational acceleration
			if pg.Rotation != nil {
				bp.Vel = bp.Vel.Rotate(pg.Rotation.Poll())
			}

			if pg.SpeedDecay.X() != 0 {
				bp.Vel = bp.Vel.SetX(bp.Vel.X() * pg.SpeedDecay.X())
				if math.Abs(bp.Vel.X()) < 0.2 {
					bp.Vel = bp.Vel.SetX(0)
				}
			}
			if pg.SpeedDecay.Y() != 0 {
				bp.Vel = bp.Vel.SetY(bp.Vel.Y() * pg.SpeedDecay.Y())
				if math.Abs(bp.Vel.Y()) < 0.2 {
					bp.Vel = bp.Vel.SetY(0)
				}
			}

			bp.Vel.Add(pg.Gravity)
			bp.Add(bp.Vel)
			bp.SetLayer(ps.Layer(bp.GetPos()))
			p.Cycle(ps.Generator)
		}
	}
	return cycled
}

// Layer is shorthand for getting the base generator behind a source's layer
func (ps *Source) Layer(v physics.Vector) int {
	return ps.Generator.GetBaseGenerator().LayerFunc(v)
}

func (ps *Source) addParticles() {
	pg := ps.Generator.GetBaseGenerator()
	// Regularly create particles (up until max particles)
	newParticleCount := int(pg.NewPerFrame.Poll())

	if ps.nextPID+newParticleCount >= blockSize {
		newParticleCount = blockSize - ps.nextPID
	}

	if pg.ParticleLimit != 0 {
		if ps.nextPID+newParticleCount >= pg.ParticleLimit {
			newParticleCount = pg.ParticleLimit - ps.nextPID
		}
	}

	var p Particle
	var bp *baseParticle
	for i := 0; i < newParticleCount; i++ {
		angle := pg.Angle.Poll()
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		// If this particle has not been allocated yet
		if ps.particles[ps.nextPID] == nil {
			bp = &baseParticle{
				LayeredPoint: render.NewLayeredPoint(
					pg.X()+floatFromSpread(pg.Spread.X()),
					pg.Y()+floatFromSpread(pg.Spread.Y()),
					0,
				),
				Src: ps,
				Vel: physics.NewVector(
					speed*math.Cos(angle)*-1,
					speed*math.Sin(angle)*-1),
				Life:      startLife,
				totalLife: startLife,
				pID:       ps.nextPID + ps.pIDBlock*blockSize,
			}

			p = ps.Generator.GenerateParticle(bp)

			// If this is a 'recycled' particle waiting to be redrawn
		} else {
			bp = ps.particles[ps.nextPID].GetBaseParticle()
			bp.LayeredPoint = render.NewLayeredPoint(
				pg.X()+floatFromSpread(pg.Spread.X()),
				pg.Y()+floatFromSpread(pg.Spread.Y()),
				0)
			bp.Vel = physics.NewVector(
				speed*math.Cos(angle)*-1,
				speed*math.Sin(angle)*-1)
			bp.Life = startLife
			bp.totalLife = startLife
			p = ps.Generator.GenerateParticle(bp)

		}
		ps.particles[ps.nextPID] = p
		ps.nextPID++
		p.SetLayer(ps.Layer(bp.GetPos()))
		render.Draw(p, ps.stackLevel)
	}

}

// rotateParticles updates particles over time as long
// as a Source is active.
func rotateParticles(id event.CID, payload interface{}) int {
	ps := id.E().(*Source)
	if !ps.started {
		ps.started = true
	}
	if !ps.paused {
		ps.cycleParticles()
		ps.addParticles()
	}
	if time.Now().After(ps.stopRotateAt) {
		ps.CID.Bind(event.Enter, rotateParticles)
		return event.UnbindSingle
	}
	return 0
}

// clearParticles is used after a Source has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id event.CID, nothing interface{}) int {
	if ps, ok := id.E().(*Source); ok {
		if !ps.paused {
			if ps.cycleParticles() {
			} else {
				if ps.EndFunc != nil {
					ps.EndFunc()
				}
				event.DestroyEntity(id)
				ps.Deallocate(ps.pIDBlock)
				return event.UnbindEvent
			}
		}

		return 0
	}
	return event.UnbindEvent
}

// Stop manually stops a Source, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *Source) Stop() {
	if ps == nil {
		return
	}
	ps.stopped = true
	ps.CID.UnbindAllAndRebind([]event.Bindable{clearParticles}, []string{event.Enter})
}

// Pause on a Source just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *Source) Pause() {
	ps.paused = true
}

// UnPause on a source a Source rebinds it's rotate function.
func (ps *Source) UnPause() {
	ps.paused = false
}

// IsPaused checks for whether the source is currently in a paused state.
// It probably would have made more sense to export paused but this way if a lock is needed here in the future...
// Then it wont change the api.
func (ps *Source) IsPaused() bool {
	return ps.paused
}

// ShiftX shift's a source's underlying generator
func (ps *Source) ShiftX(x float64) {
	ps.Generator.ShiftX(x)
}

// ShiftY shift's a source's underlying generator
func (ps *Source) ShiftY(y float64) {
	ps.Generator.ShiftY(y)
}

// SetPos sets a source's underlying generator
func (ps *Source) SetPos(x, y float64) {
	ps.Generator.SetPos(x, y)
}
<|endoftext|>


<commit_before>package particle

import (
	"math"
	"time"

	"bitbucket.org/oakmoundstudio/oak/event"
	"bitbucket.org/oakmoundstudio/oak/physics"
	"bitbucket.org/oakmoundstudio/oak/render"
	"bitbucket.org/oakmoundstudio/oak/alg"
)

const (
	RECYCLED = -1000
)

// A Source is used to store and control a set of particles.
type Source struct {
	render.Layered
	Generator     Generator
	particles     [BLOCK_SIZE]Particle
	nextPID       int
	recycled      []int
	rotateBinding event.Binding
	clearBinding  event.Binding
	cID           event.CID
	paused        bool
	pIDBlock      int
}

func NewSource(g Generator) *Source {
	ps := new(Source)
	ps.Generator = g
	ps.Init()
	return ps
}

func (ps *Source) Init() event.CID {
	cID := event.NextID(ps)
	cID.Bind(rotateParticles, "EnterFrame")
	ps.cID = cID
	ps.pIDBlock = Allocate(ps.cID)
	if ps.Generator.GetBaseGenerator().Duration != Inf {
		go func(ps_p *Source, duration alg.IntRange) {
			<-time.After(time.Duration(duration.Poll()) * time.Millisecond)
			ps_p.Stop()
		}(ps, ps.Generator.GetBaseGenerator().Duration)
	}
	return cID
}

func (ps *Source) CycleParticles() {

	pg := ps.Generator.GetBaseGenerator()

	for i := 0; i < ps.nextPID; i++ {
		p := ps.particles[i]
		bp := p.GetBaseParticle()

		// Ignore dead particles
		if bp.Life > 0 {
			bp.Life--

			// Apply rotational acceleration
			if pg.Rotation != nil {
				bp.Vel.Rotate(pg.Rotation.Poll())
			}

			if pg.SpeedDecay != nil {
				if pg.SpeedDecay.X != 0 {
					bp.Vel.X *= pg.SpeedDecay.X
					if math.Abs(bp.Vel.X) < 0.2 {
						bp.Vel.X = 0
					}
				}
				if pg.SpeedDecay.Y != 0 {
					bp.Vel.Y *= pg.SpeedDecay.Y
					if math.Abs(bp.Vel.Y) < 0.2 {
						bp.Vel.Y = 0
					}
				}
			}

			if pg.Gravity != nil {
				bp.Vel.Add(pg.Gravity)
			}
			bp.Pos.Add(bp.Vel)
			bp.SetLayer(ps.Layer(bp.Pos))
			p.Cycle(ps.Generator)

		} else if bp.Life != RECYCLED {
			p.UnDraw()
			if pg.EndFunc != nil {
				pg.EndFunc(p)
			}
			// This relies on Life going down by 1 per frame
			bp.Life = RECYCLED
			ps.recycled = append(ps.recycled, bp.pID%BLOCK_SIZE)
		}

	}
}

// Shorthand
func (ps *Source) Layer(v *physics.Vector) int {
	return ps.Generator.GetBaseGenerator().LayerFunc(v)
}

func (ps *Source) AddParticles() {
	pg := ps.Generator.GetBaseGenerator()
	// Regularly create particles (up until max particles)
	newParticleCount := int(pg.NewPerFrame.Poll())
	ri := 0
	for ri < len(ps.recycled) && ri < newParticleCount {

		j := ps.recycled[ri]
		bp := ps.particles[j].GetBaseParticle()
		angle := pg.Angle.Poll() * math.Pi / 180.0
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		bp.Pos = physics.NewVector(
			pg.X+floatFromSpread(pg.Spread.X),
			pg.Y+floatFromSpread(pg.Spread.Y))
		bp.Vel = physics.NewVector(
			speed*math.Cos(angle)*-1,
			speed*math.Sin(angle)*-1)
		bp.Life = startLife
		bp.totalLife = startLife
		ps.particles[ps.recycled[ri]] = ps.Generator.GenerateParticle(bp)

		render.Draw(ps.particles[ps.recycled[ri]], ps.Layer(bp.Pos))
		ri++
	}
	newParticleCount -= len(ps.recycled)
	if ri > 0 {
		ps.recycled = ps.recycled[ri-1:]
	}

	if ps.nextPID >= BLOCK_SIZE {
		return
	}
	for i := 0; i < newParticleCount; i++ {
		angle := pg.Angle.Poll() * math.Pi / 180.0
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		bp := &BaseParticle{
			Src: ps,
			Pos: physics.NewVector(
				pg.X+floatFromSpread(pg.Spread.X),
				pg.Y+floatFromSpread(pg.Spread.Y)),
			Vel: physics.NewVector(
				speed*math.Cos(angle)*-1,
				speed*math.Sin(angle)*-1),
			Life:      startLife,
			totalLife: startLife,
			pID:       ps.nextPID + ps.pIDBlock*BLOCK_SIZE,
		}

		p := ps.Generator.GenerateParticle(bp)
		ps.particles[ps.nextPID] = p
		ps.nextPID++
		if ps.nextPID >= BLOCK_SIZE {
			return
		}
		render.Draw(p, ps.Layer(bp.Pos))
	}

}

// rotateParticles updates particles over time as long
// as a Source is active.
func rotateParticles(id int, nothing interface{}) int {
	ps := event.GetEntity(id).(*Source)
	if !ps.paused {
		ps.CycleParticles()
		ps.AddParticles()
	}
	return 0
}

// clearParticles is used after a Source has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id int, nothing interface{}) int {
	ps := event.GetEntity(id).(*Source)
	if !ps.paused {
		if len(ps.particles) > 0 {
			ps.CycleParticles()
		} else {
			event.DestroyEntity(id)
			Deallocate(ps.pIDBlock)
			return event.UNBIND_EVENT
		}
	}
	return 0
}

func clearAtExit(id int, nothing interface{}) int {
	if event.HasEntity(id) {
		t := event.GetEntity(id)
		switch t.(type) {
		case *Source:
			ps := t.(*Source)
			ps.cID.Bind(clearParticles, "ExitFrame")
			return event.UNBIND_EVENT
		}
	}
	return 0
}

// Stop manually stops a Source, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *Source) Stop() {
	ps.cID.Bind(clearAtExit, "EnterFrame")
}

// Pausing a Source just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *Source) Pause() {
	ps.paused = true
}

// Unpausing a Source rebinds it's rotate function.
func (ps *Source) UnPause() {
	ps.paused = false
}

// Placeholder
func (ps *Source) String() string {
	return "ParticleSource"
}

func (ps *Source) ShiftX(x float64) {
	ps.Generator.ShiftX(x)
}

func (ps *Source) ShiftY(y float64) {
	ps.Generator.ShiftY(y)
}

func (ps *Source) SetPos(x, y float64) {
	ps.Generator.SetPos(x, y)
}
<commit_msg>Fixed bug where particle effects with exactly 1 new per frame would leak something<commit_after>package particle

import (
	"math"
	"time"

	"bitbucket.org/oakmoundstudio/oak/alg"
	"bitbucket.org/oakmoundstudio/oak/event"
	"bitbucket.org/oakmoundstudio/oak/physics"
	"bitbucket.org/oakmoundstudio/oak/render"
)

const (
	RECYCLED = -1000
)

// A Source is used to store and control a set of particles.
type Source struct {
	render.Layered
	Generator     Generator
	particles     [BLOCK_SIZE]Particle
	nextPID       int
	recycled      []int
	rotateBinding event.Binding
	clearBinding  event.Binding
	cID           event.CID
	paused        bool
	pIDBlock      int
}

func NewSource(g Generator) *Source {
	ps := new(Source)
	ps.Generator = g
	ps.Init()
	return ps
}

func (ps *Source) Init() event.CID {
	cID := event.NextID(ps)
	cID.Bind(rotateParticles, "EnterFrame")
	ps.cID = cID
	ps.pIDBlock = Allocate(ps.cID)
	if ps.Generator.GetBaseGenerator().Duration != Inf {
		go func(ps_p *Source, duration alg.IntRange) {
			<-time.After(time.Duration(duration.Poll()) * time.Millisecond)
			ps_p.Stop()
		}(ps, ps.Generator.GetBaseGenerator().Duration)
	}
	return cID
}

func (ps *Source) CycleParticles() {

	pg := ps.Generator.GetBaseGenerator()

	for i := 0; i < ps.nextPID; i++ {
		p := ps.particles[i]
		bp := p.GetBaseParticle()

		// Ignore dead particles
		if bp.Life > 0 {
			bp.Life--

			// Apply rotational acceleration
			if pg.Rotation != nil {
				bp.Vel.Rotate(pg.Rotation.Poll())
			}

			if pg.SpeedDecay != nil {
				if pg.SpeedDecay.X != 0 {
					bp.Vel.X *= pg.SpeedDecay.X
					if math.Abs(bp.Vel.X) < 0.2 {
						bp.Vel.X = 0
					}
				}
				if pg.SpeedDecay.Y != 0 {
					bp.Vel.Y *= pg.SpeedDecay.Y
					if math.Abs(bp.Vel.Y) < 0.2 {
						bp.Vel.Y = 0
					}
				}
			}

			if pg.Gravity != nil {
				bp.Vel.Add(pg.Gravity)
			}
			bp.Pos.Add(bp.Vel)
			bp.SetLayer(ps.Layer(bp.Pos))
			p.Cycle(ps.Generator)

		} else if bp.Life != RECYCLED {
			p.UnDraw()
			if pg.EndFunc != nil {
				pg.EndFunc(p)
			}
			// This relies on Life going down by 1 per frame
			bp.Life = RECYCLED
			ps.recycled = append(ps.recycled, bp.pID%BLOCK_SIZE)
		}

	}
}

// Shorthand
func (ps *Source) Layer(v *physics.Vector) int {
	return ps.Generator.GetBaseGenerator().LayerFunc(v)
}

func (ps *Source) AddParticles() {
	pg := ps.Generator.GetBaseGenerator()
	// Regularly create particles (up until max particles)
	newParticleCount := int(pg.NewPerFrame.Poll())
	ri := 0
	for ri < len(ps.recycled) && ri < newParticleCount {

		j := ps.recycled[ri]
		bp := ps.particles[j].GetBaseParticle()
		angle := pg.Angle.Poll() * math.Pi / 180.0
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		bp.Pos = physics.NewVector(
			pg.X+floatFromSpread(pg.Spread.X),
			pg.Y+floatFromSpread(pg.Spread.Y))
		bp.Vel = physics.NewVector(
			speed*math.Cos(angle)*-1,
			speed*math.Sin(angle)*-1)
		bp.Life = startLife
		bp.totalLife = startLife
		ps.particles[ps.recycled[ri]] = ps.Generator.GenerateParticle(bp)

		render.Draw(ps.particles[ps.recycled[ri]], ps.Layer(bp.Pos))
		ri++
	}
	newParticleCount -= len(ps.recycled)
	if ri > 0 {
		ps.recycled = ps.recycled[ri:]
	}

	if ps.nextPID >= BLOCK_SIZE {
		return
	}
	for i := 0; i < newParticleCount; i++ {
		angle := pg.Angle.Poll() * math.Pi / 180.0
		speed := pg.Speed.Poll()
		startLife := pg.LifeSpan.Poll()

		bp := &BaseParticle{
			Src: ps,
			Pos: physics.NewVector(
				pg.X+floatFromSpread(pg.Spread.X),
				pg.Y+floatFromSpread(pg.Spread.Y)),
			Vel: physics.NewVector(
				speed*math.Cos(angle)*-1,
				speed*math.Sin(angle)*-1),
			Life:      startLife,
			totalLife: startLife,
			pID:       ps.nextPID + ps.pIDBlock*BLOCK_SIZE,
		}

		p := ps.Generator.GenerateParticle(bp)
		ps.particles[ps.nextPID] = p
		ps.nextPID++
		if ps.nextPID >= BLOCK_SIZE {
			return
		}
		render.Draw(p, ps.Layer(bp.Pos))
	}

}

// rotateParticles updates particles over time as long
// as a Source is active.
func rotateParticles(id int, nothing interface{}) int {
	ps := event.GetEntity(id).(*Source)
	if !ps.paused {
		ps.CycleParticles()
		ps.AddParticles()
	}
	return 0
}

// clearParticles is used after a Source has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id int, nothing interface{}) int {
	ps := event.GetEntity(id).(*Source)
	if !ps.paused {
		if len(ps.particles) > 0 {
			ps.CycleParticles()
		} else {
			event.DestroyEntity(id)
			Deallocate(ps.pIDBlock)
			return event.UNBIND_EVENT
		}
	}
	return 0
}

func clearAtExit(id int, nothing interface{}) int {
	if event.HasEntity(id) {
		t := event.GetEntity(id)
		switch t.(type) {
		case *Source:
			ps := t.(*Source)
			ps.cID.Bind(clearParticles, "ExitFrame")
			return event.UNBIND_EVENT
		}
	}
	return 0
}

// Stop manually stops a Source, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *Source) Stop() {
	ps.cID.Bind(clearAtExit, "EnterFrame")
}

// Pausing a Source just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *Source) Pause() {
	ps.paused = true
}

// Unpausing a Source rebinds it's rotate function.
func (ps *Source) UnPause() {
	ps.paused = false
}

// Placeholder
func (ps *Source) String() string {
	return "ParticleSource"
}

func (ps *Source) ShiftX(x float64) {
	ps.Generator.ShiftX(x)
}

func (ps *Source) ShiftY(y float64) {
	ps.Generator.ShiftY(y)
}

func (ps *Source) SetPos(x, y float64) {
	ps.Generator.SetPos(x, y)
}
<|endoftext|>


<commit_before>// scrapexchange is the web admin and API server for the scrap exchange service.
//
// See http://go/scrap-exchange for more details.
package main

import (
	"context"
	"flag"
	"html/template"
	"net/http"
	"path/filepath"

	"cloud.google.com/go/storage"
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
	"go.skia.org/infra/go/allowed"
	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/baseapp"
	"go.skia.org/infra/go/gcs/gcsclient"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/scrap/go/api"
	"google.golang.org/api/option"
)

// flags
var (
	authGroup          = flag.String("auth_group", "google/skia-staff@google.com", "The chrome infra auth group to use for restricting access.")
	chromeInfraAuthJWT = flag.String("chrome_infra_auth_jwt", "/var/secrets/skia-public-auth/key.json", "The JWT key for the service account that has access to chrome infra auth.")
	internalPort       = flag.String("internal_port", ":9000", "HTTP internal service address (e.g., ':9000') for unauthenticated in-cluster requests.")
	bucket             = flag.String("bucket", "", "The Google Cloud Storage bucket that scraps are stored in.")
)

// server is the state of the server.
type server struct {
	apiEndpoints *api.Api
	templates    *template.Template
}

// New implements baseapp.Constructor.
func New() (baseapp.App, error) {
	if *bucket == "" {
		return nil, skerr.Fmt("--bucket is a required flag.")
	}
	var allow allowed.Allow
	if !*baseapp.Local {
		ts, err := auth.NewJWTServiceAccountTokenSource("", *chromeInfraAuthJWT, auth.SCOPE_USERINFO_EMAIL)
		if err != nil {
			return nil, skerr.Wrap(err)
		}
		client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
		allow, err = allowed.NewAllowedFromChromeInfraAuth(client, *authGroup)
		if err != nil {
			return nil, skerr.Wrap(err)
		}
	} else {
		allow = allowed.NewAllowedFromList([]string{"fred@example.org", "barney@example.org", "wilma@example.org"})
	}

	login.SimpleInitWithAllow(*baseapp.Port, *baseapp.Local, nil, nil, allow)

	ctx := context.Background()
	ts, err := auth.NewDefaultTokenSource(*baseapp.Local, storage.ScopeReadOnly)
	if err != nil {
		return nil, skerr.Wrap(err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).WithoutRetries().Client()
	storageClient, err := storage.NewClient(ctx, option.WithHTTPClient(client))
	if err != nil {
		return nil, skerr.Wrap(err)
	}
	gcsClient := gcsclient.New(storageClient, *bucket)
	apiEndpoints, err := api.New(gcsClient)
	if err != nil {
		return nil, skerr.Wrap(err)
	}

	srv := &server{
		apiEndpoints: apiEndpoints,
	}
	srv.loadTemplates()

	srv.startInternalServer()

	return srv, nil
}

func (srv *server) loadTemplates() {
	srv.templates = template.Must(template.New("").Delims("{%", "%}").ParseFiles(
		filepath.Join(*baseapp.ResourcesDir, "index.html"),
	))
}

func (srv *server) mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *baseapp.Local {
		srv.loadTemplates()
	}
	if err := srv.templates.ExecuteTemplate(w, "index.html", map[string]string{
		// Look in webpack.config.js for where the nonce templates are injected.
		"Nonce": secure.CSPNonce(r.Context()),
	}); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// user returns the currently logged in user, or a placeholder if running locally.
func (srv *server) user(r *http.Request) string {
	user := "barney@example.org"
	if !*baseapp.Local {
		user = login.LoggedInAs(r)
	}
	return user
}

// AddHandlers implements baseapp.App.
func (srv *server) AddHandlers(r *mux.Router) {
	r.HandleFunc("/", srv.mainHandler)
	r.HandleFunc("/loginstatus/", login.StatusHandler).Methods("GET")
	srv.apiEndpoints.AddHandlers(r, api.DoNotAddProtectedEndpoints)
}

// AddMiddleware implements baseapp.App.
func (srv *server) AddMiddleware() []mux.MiddlewareFunc {
	ret := []mux.MiddlewareFunc{}
	if !*baseapp.Local {
		ret = append(ret, login.ForceAuthMiddleware(login.DEFAULT_REDIRECT_URL), login.RestrictViewer)
	}
	return ret
}

func (srv *server) startInternalServer() {
	// Internal endpoints that are only accessible from within the cluster.
	internal := mux.NewRouter()
	srv.apiEndpoints.AddHandlers(internal, api.AddProtectedEndpoints)
	go func() {
		sklog.Fatal(http.ListenAndServe(*internalPort, internal))
	}()
}

func main() {
	// allowedHosts is the empty list since we want all hosts to be able to use the REST API endpoints.
	baseapp.Serve(New, []string{""})
}
<commit_msg>[scrap] Only restrict the admin UI.<commit_after>// scrapexchange is the web admin and API server for the scrap exchange service.
//
// See http://go/scrap-exchange for more details.
package main

import (
	"context"
	"flag"
	"html/template"
	"net/http"
	"path/filepath"

	"cloud.google.com/go/storage"
	"github.com/gorilla/mux"
	"github.com/unrolled/secure"
	"go.skia.org/infra/go/allowed"
	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/baseapp"
	"go.skia.org/infra/go/gcs/gcsclient"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/scrap/go/api"
	"google.golang.org/api/option"
)

// flags
var (
	authGroup          = flag.String("auth_group", "google/skia-staff@google.com", "The chrome infra auth group to use for restricting access.")
	chromeInfraAuthJWT = flag.String("chrome_infra_auth_jwt", "/var/secrets/skia-public-auth/key.json", "The JWT key for the service account that has access to chrome infra auth.")
	internalPort       = flag.String("internal_port", ":9000", "HTTP internal service address (e.g., ':9000') for unauthenticated in-cluster requests.")
	bucket             = flag.String("bucket", "", "The Google Cloud Storage bucket that scraps are stored in.")
)

// server is the state of the server.
type server struct {
	apiEndpoints *api.Api
	templates    *template.Template
}

// New implements baseapp.Constructor.
func New() (baseapp.App, error) {
	if *bucket == "" {
		return nil, skerr.Fmt("--bucket is a required flag.")
	}
	var admin allowed.Allow
	if !*baseapp.Local {
		ts, err := auth.NewJWTServiceAccountTokenSource("", *chromeInfraAuthJWT, auth.SCOPE_USERINFO_EMAIL)
		if err != nil {
			return nil, skerr.Wrap(err)
		}
		client := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()
		admin, err = allowed.NewAllowedFromChromeInfraAuth(client, *authGroup)
		if err != nil {
			return nil, skerr.Wrap(err)
		}
	} else {
		admin = allowed.NewAllowedFromList([]string{"fred@example.org", "barney@example.org", "wilma@example.org"})
	}

	login.SimpleInitWithAllow(*baseapp.Port, *baseapp.Local, admin, nil, nil)

	ctx := context.Background()
	ts, err := auth.NewDefaultTokenSource(*baseapp.Local, storage.ScopeReadOnly)
	if err != nil {
		return nil, skerr.Wrap(err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).WithoutRetries().Client()
	storageClient, err := storage.NewClient(ctx, option.WithHTTPClient(client))
	if err != nil {
		return nil, skerr.Wrap(err)
	}
	gcsClient := gcsclient.New(storageClient, *bucket)
	apiEndpoints, err := api.New(gcsClient)
	if err != nil {
		return nil, skerr.Wrap(err)
	}

	srv := &server{
		apiEndpoints: apiEndpoints,
	}
	srv.loadTemplates()

	srv.startInternalServer()

	return srv, nil
}

func (srv *server) loadTemplates() {
	srv.templates = template.Must(template.New("").Delims("{%", "%}").ParseFiles(
		filepath.Join(*baseapp.ResourcesDir, "index.html"),
	))
}

func (srv *server) mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *baseapp.Local {
		srv.loadTemplates()
	}
	if err := srv.templates.ExecuteTemplate(w, "index.html", map[string]string{
		// Look in webpack.config.js for where the nonce templates are injected.
		"Nonce": secure.CSPNonce(r.Context()),
	}); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// user returns the currently logged in user, or a placeholder if running locally.
func (srv *server) user(r *http.Request) string {
	user := "barney@example.org"
	if !*baseapp.Local {
		user = login.LoggedInAs(r)
	}
	return user
}

// AddHandlers implements baseapp.App.
func (srv *server) AddHandlers(r *mux.Router) {
	r.HandleFunc("/", login.RestrictAdminFn(srv.mainHandler))
	r.HandleFunc("/loginstatus/", login.StatusHandler).Methods("GET")
	srv.apiEndpoints.AddHandlers(r, api.DoNotAddProtectedEndpoints)
}

// AddMiddleware implements baseapp.App.
func (srv *server) AddMiddleware() []mux.MiddlewareFunc {
	return []mux.MiddlewareFunc{}
}

func (srv *server) startInternalServer() {
	// Internal endpoints that are only accessible from within the cluster.
	internal := mux.NewRouter()
	srv.apiEndpoints.AddHandlers(internal, api.AddProtectedEndpoints)
	go func() {
		sklog.Fatal(http.ListenAndServe(*internalPort, internal))
	}()
}

func main() {
	baseapp.Serve(New, []string{"scrap.skia.org"})
}
<|endoftext|>


<commit_before>package doc_test

import (
	"bytes"
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
)

func ExampleCommand_GenManTree() {
	cmd := &cobra.Command{
		Use:   "test",
		Short: "my test program",
	}
	header := &doc.GenManHeader{
		Title:   "MINE",
		Section: "3",
	}
	doc.GenManTree(cmd, header, "/tmp")
}

func ExampleCommand_GenMan() {
	cmd := &cobra.Command{
		Use:   "test",
		Short: "my test program",
	}
	header := &doc.GenManHeader{
		Title:   "MINE",
		Section: "3",
	}
	out := new(bytes.Buffer)
	doc.GenMan(cmd, header, out)
	fmt.Print(out.String())
}
<commit_msg>doc: Fix govet complaints (#408)<commit_after>package doc_test

import (
	"bytes"
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
)

func ExampleGenManTree() {
	cmd := &cobra.Command{
		Use:   "test",
		Short: "my test program",
	}
	header := &doc.GenManHeader{
		Title:   "MINE",
		Section: "3",
	}
	doc.GenManTree(cmd, header, "/tmp")
}

func ExampleGenMan() {
	cmd := &cobra.Command{
		Use:   "test",
		Short: "my test program",
	}
	header := &doc.GenManHeader{
		Title:   "MINE",
		Section: "3",
	}
	out := new(bytes.Buffer)
	doc.GenMan(cmd, header, out)
	fmt.Print(out.String())
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package util

import (
	"fmt"
	"net"
	"os"
	"time"

	"github.com/golang/glog"
	federation_v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	utilnet "k8s.io/kubernetes/pkg/util/net"
	"k8s.io/kubernetes/pkg/util/wait"
)

const (
	KubeAPIQPS              = 20.0
	KubeAPIBurst            = 30
	KubeconfigSecretDataKey = "kubeconfig"
	getSecretTimeout        = 1 * time.Minute
)

func BuildClusterConfig(c *federation_v1beta1.Cluster) (*restclient.Config, error) {
	var serverAddress string
	var clusterConfig *restclient.Config
	hostIP, err := utilnet.ChooseHostInterface()
	if err != nil {
		return nil, err
	}

	for _, item := range c.Spec.ServerAddressByClientCIDRs {
		_, cidrnet, err := net.ParseCIDR(item.ClientCIDR)
		if err != nil {
			return nil, err
		}
		myaddr := net.ParseIP(hostIP.String())
		if cidrnet.Contains(myaddr) == true {
			serverAddress = item.ServerAddress
			break
		}
	}
	if serverAddress != "" {
		if c.Spec.SecretRef == nil {
			glog.Infof("didnt find secretRef for cluster %s. Trying insecure access", c.Name)
			clusterConfig, err = clientcmd.BuildConfigFromFlags(serverAddress, "")
		} else {
			kubeconfigGetter := KubeconfigGetterForCluster(c)
			clusterConfig, err = clientcmd.BuildConfigFromKubeconfigGetter(serverAddress, kubeconfigGetter)
		}
		if err != nil {
			return nil, err
		}
		clusterConfig.QPS = KubeAPIQPS
		clusterConfig.Burst = KubeAPIBurst
	}
	return clusterConfig, nil
}

// This is to inject a different kubeconfigGetter in tests.
// We dont use the standard one which calls NewInCluster in tests to avoid having to setup service accounts and mount files with secret tokens.
var KubeconfigGetterForCluster = func(c *federation_v1beta1.Cluster) clientcmd.KubeconfigGetter {
	return func() (*clientcmdapi.Config, error) {
		secretRefName := ""
		if c.Spec.SecretRef != nil {
			secretRefName = c.Spec.SecretRef.Name
		} else {
			glog.Infof("didnt find secretRef for cluster %s. Trying insecure access", c.Name)
		}
		return KubeconfigGetterForSecret(secretRefName)()
	}
}

// KubeconfigGettterForSecret is used to get the kubeconfig from the given secret.
var KubeconfigGetterForSecret = func(secretName string) clientcmd.KubeconfigGetter {
	return func() (*clientcmdapi.Config, error) {
		var data []byte
		if secretName != "" {
			// Get the namespace this is running in from the env variable.
			namespace := os.Getenv("POD_NAMESPACE")
			if namespace == "" {
				return nil, fmt.Errorf("unexpected: POD_NAMESPACE env var returned empty string")
			}
			// Get a client to talk to the k8s apiserver, to fetch secrets from it.
			client, err := client.NewInCluster()
			if err != nil {
				return nil, fmt.Errorf("error in creating in-cluster client: %s", err)
			}
			data = []byte{}
			var secret *api.Secret
			err = wait.PollImmediate(1*time.Second, getSecretTimeout, func() (bool, error) {
				secret, err = client.Secrets(namespace).Get(secretName)
				if err == nil {
					return true, nil
				}
				glog.Warningf("error in fetching secret: %s", err)
				return false, nil
			})
			if err != nil {
				return nil, fmt.Errorf("timed out waiting for secret: %s", err)
			}
			if secret == nil {
				return nil, fmt.Errorf("unexpected: received null secret %s", secretName)
			}
			ok := false
			data, ok = secret.Data[KubeconfigSecretDataKey]
			if !ok {
				return nil, fmt.Errorf("secret does not have data with key: %s", KubeconfigSecretDataKey)
			}
		}
		return clientcmd.Load(data)
	}
}
<commit_msg>Federation - common libs - get clientset for cluster<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package util

import (
	"fmt"
	"net"
	"os"
	"time"

	"github.com/golang/glog"
	federation_v1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
	federation_release_1_4 "k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_4"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/restclient"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
	clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
	utilnet "k8s.io/kubernetes/pkg/util/net"
	"k8s.io/kubernetes/pkg/util/wait"
)

const (
	KubeAPIQPS              = 20.0
	KubeAPIBurst            = 30
	KubeconfigSecretDataKey = "kubeconfig"
	getSecretTimeout        = 1 * time.Minute
)

func BuildClusterConfig(c *federation_v1beta1.Cluster) (*restclient.Config, error) {
	var serverAddress string
	var clusterConfig *restclient.Config
	hostIP, err := utilnet.ChooseHostInterface()
	if err != nil {
		return nil, err
	}

	for _, item := range c.Spec.ServerAddressByClientCIDRs {
		_, cidrnet, err := net.ParseCIDR(item.ClientCIDR)
		if err != nil {
			return nil, err
		}
		myaddr := net.ParseIP(hostIP.String())
		if cidrnet.Contains(myaddr) == true {
			serverAddress = item.ServerAddress
			break
		}
	}
	if serverAddress != "" {
		if c.Spec.SecretRef == nil {
			glog.Infof("didnt find secretRef for cluster %s. Trying insecure access", c.Name)
			clusterConfig, err = clientcmd.BuildConfigFromFlags(serverAddress, "")
		} else {
			kubeconfigGetter := KubeconfigGetterForCluster(c)
			clusterConfig, err = clientcmd.BuildConfigFromKubeconfigGetter(serverAddress, kubeconfigGetter)
		}
		if err != nil {
			return nil, err
		}
		clusterConfig.QPS = KubeAPIQPS
		clusterConfig.Burst = KubeAPIBurst
	}
	return clusterConfig, nil
}

// This is to inject a different kubeconfigGetter in tests.
// We dont use the standard one which calls NewInCluster in tests to avoid having to setup service accounts and mount files with secret tokens.
var KubeconfigGetterForCluster = func(c *federation_v1beta1.Cluster) clientcmd.KubeconfigGetter {
	return func() (*clientcmdapi.Config, error) {
		secretRefName := ""
		if c.Spec.SecretRef != nil {
			secretRefName = c.Spec.SecretRef.Name
		} else {
			glog.Infof("didnt find secretRef for cluster %s. Trying insecure access", c.Name)
		}
		return KubeconfigGetterForSecret(secretRefName)()
	}
}

// KubeconfigGettterForSecret is used to get the kubeconfig from the given secret.
var KubeconfigGetterForSecret = func(secretName string) clientcmd.KubeconfigGetter {
	return func() (*clientcmdapi.Config, error) {
		var data []byte
		if secretName != "" {
			// Get the namespace this is running in from the env variable.
			namespace := os.Getenv("POD_NAMESPACE")
			if namespace == "" {
				return nil, fmt.Errorf("unexpected: POD_NAMESPACE env var returned empty string")
			}
			// Get a client to talk to the k8s apiserver, to fetch secrets from it.
			client, err := client.NewInCluster()
			if err != nil {
				return nil, fmt.Errorf("error in creating in-cluster client: %s", err)
			}
			data = []byte{}
			var secret *api.Secret
			err = wait.PollImmediate(1*time.Second, getSecretTimeout, func() (bool, error) {
				secret, err = client.Secrets(namespace).Get(secretName)
				if err == nil {
					return true, nil
				}
				glog.Warningf("error in fetching secret: %s", err)
				return false, nil
			})
			if err != nil {
				return nil, fmt.Errorf("timed out waiting for secret: %s", err)
			}
			if secret == nil {
				return nil, fmt.Errorf("unexpected: received null secret %s", secretName)
			}
			ok := false
			data, ok = secret.Data[KubeconfigSecretDataKey]
			if !ok {
				return nil, fmt.Errorf("secret does not have data with key: %s", KubeconfigSecretDataKey)
			}
		}
		return clientcmd.Load(data)
	}
}

// Retruns Clientset for the given cluster.
func GetClientsetForCluster(cluster *federation_v1beta1.Cluster) (*federation_release_1_4.Clientset, error) {
	clusterConfig, err := BuildClusterConfig(cluster)
	if err != nil && clusterConfig != nil {
		clientset := federation_release_1_4.NewForConfigOrDie(restclient.AddUserAgent(clusterConfig, userAgentName))
		return clientset, nil
	}
	return nil, err
}
<|endoftext|>