<commit_before>// Copyright 2016, RadiantBlue Technologies, 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 piazza

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"os"
)

type ConfigMode string

const (
	ConfigModeLocal ConfigMode = "local"
	ConfigModeTest  ConfigMode = "test"
	ConfigModeCloud ConfigMode = "cloud"
)

type Config struct {
	mode            ConfigMode
	serviceName     ServiceName
	serviceAddress  string
	discoverAddress string
	bindtoAddress   string
}

func NewConfig(serviceName ServiceName, configType ConfigMode) (*Config, error) {

	var config *Config
	var err error

	switch configType {
	case ConfigModeLocal:
		config = getLocalConfig(serviceName)
	case ConfigModeTest:
		config = getTestConfig(serviceName)
	case ConfigModeCloud:
		config, err = getPCFConfig(serviceName)
		if err != nil {
			return nil, err
		}
	}

	log.Printf("Config.mode: %s", string(config.mode))
	log.Printf("Config.serviceName: %s", config.GetName())
	log.Printf("Config.serviceAddress: %s", config.GetAddress())
	log.Printf("Config.discoverAddress: %s", config.discoverAddress)
	log.Printf("Config.bindtoAddress: %s", config.bindtoAddress)

	return config, err
}

func (config Config) GetName() ServiceName {
	return config.serviceName
}

func (config Config) GetAddress() string {
	return config.serviceAddress
}

func (config Config) GetBindToAddress() string {
	return config.bindtoAddress
}

func IsLocalConfig() bool {
	localFlag := flag.Bool("local", false, "use localhost ports")
	flag.Parse()
	return *localFlag
}

func getLocalConfig(serviceName ServiceName) *Config {

	var localHosts = map[ServiceName]string{
		PzLogger:   "pz-logger.cf.piazzageo.io/",
		PzUuidgen:  "pz-uuidgen.cf.piazzageo.io/",
		PzWorkflow: "localhost:12342",
		PzDiscover: "pz-discover.cf.piazzageo.io",
	}

	config := Config{
		mode:            ConfigModeLocal,
		serviceName:     serviceName,
		serviceAddress:  localHosts[serviceName],
		discoverAddress: localHosts[PzDiscover],
		bindtoAddress:   localHosts[serviceName],
	}

	return &config
}

func getTestConfig(serviceName ServiceName) *Config {

	config := Config{
		mode:            ConfigModeTest,
		serviceName:     serviceName,
		serviceAddress:  "localhost:0",
		discoverAddress: "",
		bindtoAddress:   "localhost:0",
	}

	return &config
}

func getPCFConfig(serviceName ServiceName) (*Config, error) {

	const nonlocalDiscoverHost = "pz-discover.cf.piazzageo.io"

	var config Config
	var err error

	config.mode = ConfigModeCloud

	config.serviceName, config.serviceAddress, err = determineVcapServerAddress()
	if err != nil {
		return nil, err
	}

	log.Printf("got config.ServerAddress: %s", config.serviceAddress)

	config.discoverAddress = nonlocalDiscoverHost

	port := os.Getenv("PORT")
	if port == "" {
		return nil, errors.New("$PORT not found: unable to determine bindto address")
	}
	log.Printf("got port: %s", port)
	config.bindtoAddress = ":" + port
	if err != nil {
		return nil, err
	}
	log.Printf("got config.bindtoAddress: %s", config.bindtoAddress)

	return &config, nil
}

func determineVcapServerAddress() (serviceName ServiceName, serverAddress string, err error) {

	vcapString := os.Getenv("VCAP_APPLICATION")
	if vcapString == "" {
		return "", "", errors.New("$VCAP_APPLICATION not found: unable to determine server address")
	}
	type VcapData struct {
		ApplicationID   string   `json:"application_id"`
		ApplicationName string   `json:"application_name"`
		ApplicationURIs []string `json:"application_uris"`
	}
	var vcap VcapData
	err = json.Unmarshal([]byte(vcapString), &vcap)
	if err != nil {
		return "", "", err
	}
	serviceName = ServiceName(vcap.ApplicationName)
	serverAddress = vcap.ApplicationURIs[0]
	return serviceName, serverAddress, nil
}
<commit_msg>use local uuidgen too<commit_after>// Copyright 2016, RadiantBlue Technologies, 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 piazza

import (
	"encoding/json"
	"errors"
	"flag"
	"log"
	"os"
)

type ConfigMode string

const (
	ConfigModeLocal ConfigMode = "local"
	ConfigModeTest  ConfigMode = "test"
	ConfigModeCloud ConfigMode = "cloud"
)

type Config struct {
	mode            ConfigMode
	serviceName     ServiceName
	serviceAddress  string
	discoverAddress string
	bindtoAddress   string
}

func NewConfig(serviceName ServiceName, configType ConfigMode) (*Config, error) {

	var config *Config
	var err error

	switch configType {
	case ConfigModeLocal:
		config = getLocalConfig(serviceName)
	case ConfigModeTest:
		config = getTestConfig(serviceName)
	case ConfigModeCloud:
		config, err = getPCFConfig(serviceName)
		if err != nil {
			return nil, err
		}
	}

	log.Printf("Config.mode: %s", string(config.mode))
	log.Printf("Config.serviceName: %s", config.GetName())
	log.Printf("Config.serviceAddress: %s", config.GetAddress())
	log.Printf("Config.discoverAddress: %s", config.discoverAddress)
	log.Printf("Config.bindtoAddress: %s", config.bindtoAddress)

	return config, err
}

func (config Config) GetName() ServiceName {
	return config.serviceName
}

func (config Config) GetAddress() string {
	return config.serviceAddress
}

func (config Config) GetBindToAddress() string {
	return config.bindtoAddress
}

func IsLocalConfig() bool {
	localFlag := flag.Bool("local", false, "use localhost ports")
	flag.Parse()
	return *localFlag
}

func getLocalConfig(serviceName ServiceName) *Config {

	var localHosts = map[ServiceName]string{
		PzLogger:   "pz-logger.cf.piazzageo.io/",
		PzUuidgen:  "localhost:12340",
		PzWorkflow: "localhost:12342",
		PzDiscover: "pz-discover.cf.piazzageo.io",
	}

	config := Config{
		mode:            ConfigModeLocal,
		serviceName:     serviceName,
		serviceAddress:  localHosts[serviceName],
		discoverAddress: localHosts[PzDiscover],
		bindtoAddress:   localHosts[serviceName],
	}

	return &config
}

func getTestConfig(serviceName ServiceName) *Config {

	config := Config{
		mode:            ConfigModeTest,
		serviceName:     serviceName,
		serviceAddress:  "localhost:0",
		discoverAddress: "",
		bindtoAddress:   "localhost:0",
	}

	return &config
}

func getPCFConfig(serviceName ServiceName) (*Config, error) {

	const nonlocalDiscoverHost = "pz-discover.cf.piazzageo.io"

	var config Config
	var err error

	config.mode = ConfigModeCloud

	config.serviceName, config.serviceAddress, err = determineVcapServerAddress()
	if err != nil {
		return nil, err
	}

	log.Printf("got config.ServerAddress: %s", config.serviceAddress)

	config.discoverAddress = nonlocalDiscoverHost

	port := os.Getenv("PORT")
	if port == "" {
		return nil, errors.New("$PORT not found: unable to determine bindto address")
	}
	log.Printf("got port: %s", port)
	config.bindtoAddress = ":" + port
	if err != nil {
		return nil, err
	}
	log.Printf("got config.bindtoAddress: %s", config.bindtoAddress)

	return &config, nil
}

func determineVcapServerAddress() (serviceName ServiceName, serverAddress string, err error) {

	vcapString := os.Getenv("VCAP_APPLICATION")
	if vcapString == "" {
		return "", "", errors.New("$VCAP_APPLICATION not found: unable to determine server address")
	}
	type VcapData struct {
		ApplicationID   string   `json:"application_id"`
		ApplicationName string   `json:"application_name"`
		ApplicationURIs []string `json:"application_uris"`
	}
	var vcap VcapData
	err = json.Unmarshal([]byte(vcapString), &vcap)
	if err != nil {
		return "", "", err
	}
	serviceName = ServiceName(vcap.ApplicationName)
	serverAddress = vcap.ApplicationURIs[0]
	return serviceName, serverAddress, nil
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"image/color"
	"sort"

	"sync"

	"engo.io/ecs"
	"engo.io/engo"
	"engo.io/gl"
)

const (
	// RenderSystemPriority is the priority of the RenderSystem
	RenderSystemPriority = -1000
)

type renderChangeMessage struct{}

func (renderChangeMessage) Type() string {
	return "renderChangeMessage"
}

// Drawable is that which can be rendered to OpenGL.
type Drawable interface {
	Texture() *gl.Texture
	Width() float32
	Height() float32
	View() (float32, float32, float32, float32)
	Close()
}

// TextureRepeating is the method used to repeat a texture in OpenGL.
type TextureRepeating uint8

const (
	// NoRepeat does not repeat the texture.
	NoRepeat TextureRepeating = iota
	// ClampToEdge stretches the texture to the edge of the viewport.
	ClampToEdge
	// ClampToBorder stretches the texture to the border of the viewpport.
	ClampToBorder
	// Repeat repeats the texture until the border of the viewport.
	Repeat
	// MirroredRepeat repeats a mirror image of the texture until the border of the viewport.
	MirroredRepeat
)

// ZoomFilter is a filter used when zooming in or out of a texture.
type ZoomFilter uint8

const (
	// FilterNearest is a simple nearest neighbor algorithm
	FilterNearest ZoomFilter = iota
	// FilterLinear is a bilinear interpolation algorithm
	FilterLinear
)

// RenderComponent is the component needed to render an entity.
type RenderComponent struct {
	// Hidden is used to prevent drawing by OpenGL
	Hidden bool
	// Scale is the scale at which to render, in the X and Y axis. Not defining Scale, will default to engo.Point{1, 1}
	Scale engo.Point
	// Color defines how much of the color-components of the texture get used
	Color color.Color
	// Drawable refers to the Texture that should be drawn
	Drawable Drawable
	// Repeat defines how to repeat the Texture if the SpaceComponent of the entity
	// is larger than the texture itself, after applying scale. Defaults to NoRepeat
	// which allows the texture to draw entirely without regard to th SpaceComponent
	// Do not set to anything other than NoRepeat for textures in a sprite sheet.
	// This does not yet work with sprite sheets.
	Repeat TextureRepeating
	// Buffer represents the buffer object itself
	// Avoid using it unless your are writing a custom shader
	Buffer *gl.Buffer
	// BufferContent contains the buffer data
	// Avoid using it unless your are writing a custom shader
	BufferContent []float32

	magFilter, minFilter               ZoomFilter
	magFilterChanged, minFilterChanged bool

	shader Shader
	zIndex float32
}

// SetShader sets the shader used by the RenderComponent.
func (r *RenderComponent) SetShader(s Shader) {
	r.shader = s
	engo.Mailbox.Dispatch(&renderChangeMessage{})
}

// SetZIndex sets the order that the RenderComponent is drawn to the screen. Higher z-indices are drawn on top of
// lower ones if they overlap.
func (r *RenderComponent) SetZIndex(index float32) {
	r.zIndex = index
	engo.Mailbox.Dispatch(&renderChangeMessage{})
}

// SetMinFilter sets the ZoomFilter used for minimizing the RenderComponent
func (r *RenderComponent) SetMinFilter(z ZoomFilter) {
	r.minFilter = z
	r.minFilterChanged = true
}

// SetMagFilter sets the ZoomFilter used for magnifying the RenderComponent
func (r *RenderComponent) SetMagFilter(z ZoomFilter) {
	r.magFilter = z
	r.magFilterChanged = true
}

type renderEntity struct {
	*ecs.BasicEntity
	*RenderComponent
	*SpaceComponent
}

type renderEntityList []renderEntity

func (r renderEntityList) Len() int {
	return len(r)
}

func (r renderEntityList) Less(i, j int) bool {
	// Sort by shader-pointer if they have the same zIndex
	if r[i].RenderComponent.zIndex == r[j].RenderComponent.zIndex {
		// TODO: optimize this for performance
		return fmt.Sprintf("%p", &r[i].RenderComponent.shader) < fmt.Sprintf("%p", &r[j].RenderComponent.shader)
	}

	return r[i].RenderComponent.zIndex < r[j].RenderComponent.zIndex
}

func (r renderEntityList) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

// RenderSystem is the system that draws entities on the OpenGL surface. It requires
// a CameraSystem to work. If a CameraSystem is not in the World when you add RenderSystem
// one is automatically added to the world.
type RenderSystem struct {
	entities renderEntityList
	world    *ecs.World

	sortingNeeded, newCamera bool
	currentShader            Shader
}

// Priority implements the ecs.Prioritizer interface.
func (*RenderSystem) Priority() int { return RenderSystemPriority }

// New initializes the RenderSystem
func (rs *RenderSystem) New(w *ecs.World) {
	rs.world = w

	engo.Mailbox.Listen("NewCameraMessage", func(engo.Message) {
		rs.newCamera = true
	})

	addCameraSystemOnce(w)

	if !engo.Headless() {
		if err := initShaders(w); err != nil {
			panic(err)
		}
		engo.Gl.Enable(engo.Gl.MULTISAMPLE)
	}

	engo.Mailbox.Listen("renderChangeMessage", func(engo.Message) {
		rs.sortingNeeded = true
	})
}

var cameraInitMutex sync.Mutex

func addCameraSystemOnce(w *ecs.World) {
	cameraInitMutex.Lock()
	defer cameraInitMutex.Unlock()

	camSystemAlreadyAdded := false
	for _, system := range w.Systems() {
		switch system.(type) {
		case *CameraSystem:
			camSystemAlreadyAdded = true
		}
	}
	if !camSystemAlreadyAdded {
		w.AddSystem(&CameraSystem{})
	}
}

// Add adds an entity to the RenderSystem. The entity needs a basic, render, and space component to be added to the system.
func (rs *RenderSystem) Add(basic *ecs.BasicEntity, render *RenderComponent, space *SpaceComponent) {
	// Do nothing if entity already exists
	if rs.EntityExists(basic) >= 0 {
		return
	}

	// Setting default shader
	if render.shader == nil {
		switch render.Drawable.(type) {
		case Triangle:
			render.shader = LegacyShader
		case Circle:
			render.shader = LegacyShader
		case Rectangle:
			render.shader = LegacyShader
		case ComplexTriangles:
			render.shader = LegacyShader
		case Text:
			render.shader = TextShader
		default:
			render.shader = DefaultShader
		}
	}

	// This is to prevent users from using the wrong one
	if render.shader == HUDShader {
		switch render.Drawable.(type) {
		case Triangle:
			render.shader = LegacyHUDShader
		case Circle:
			render.shader = LegacyHUDShader
		case Rectangle:
			render.shader = LegacyHUDShader
		case ComplexTriangles:
			render.shader = LegacyHUDShader
		case Text:
			render.shader = TextHUDShader
		default:
			render.shader = HUDShader
		}
	}

	rs.entities = append(rs.entities, renderEntity{basic, render, space})
	rs.sortingNeeded = true
}

// EntityExists looks if the entity is already into the System's entities. It will return the index >= 0 of the object into de rs.entities or -1 if it could not be found.
func (rs *RenderSystem) EntityExists(basic *ecs.BasicEntity) int {
	for index, entity := range rs.entities {
		if entity.ID() == basic.ID() {
			return index
		}
	}

	return -1
}

// AddByInterface adds any Renderable to the render system. Any Entity containing a BasicEntity,RenderComponent, and SpaceComponent anonymously does this automatically
func (rs *RenderSystem) AddByInterface(i ecs.Identifier) {
	o, _ := i.(Renderable)
	rs.Add(o.GetBasicEntity(), o.GetRenderComponent(), o.GetSpaceComponent())
}

// Remove removes an entity from the RenderSystem
func (rs *RenderSystem) Remove(basic ecs.BasicEntity) {
	var delete = rs.EntityExists(&basic)
	if delete >= 0 {
		rs.entities = append(rs.entities[:delete], rs.entities[delete+1:]...)
		rs.sortingNeeded = true
	}
}

// Update draws the entities in the RenderSystem to the OpenGL Surface.
func (rs *RenderSystem) Update(dt float32) {
	if engo.Headless() {
		return
	}

	if rs.sortingNeeded {
		sort.Sort(rs.entities)
		rs.sortingNeeded = false
	}

	if rs.newCamera {
		newCamera(rs.world)
		rs.newCamera = false
	}

	engo.Gl.Clear(engo.Gl.COLOR_BUFFER_BIT)

	// TODO: it's linear for now, but that might very well be a bad idea
	for _, e := range rs.entities {
		if e.RenderComponent.Hidden {
			continue // with other entities
		}

		// Retrieve a shader, may be the default one -- then use it if we aren't already using it
		shader := e.RenderComponent.shader

		// Change Shader if we have to
		if shader != rs.currentShader {
			if rs.currentShader != nil {
				rs.currentShader.Post()
			}
			shader.Pre()
			rs.currentShader = shader
		}

		// Setting default scale to 1
		if e.RenderComponent.Scale.X == 0 && e.RenderComponent.Scale.Y == 0 {
			e.RenderComponent.Scale = engo.Point{X: 1, Y: 1}
		}

		// Setting default to white
		if e.RenderComponent.Color == nil {
			e.RenderComponent.Color = color.White
		}

		rs.currentShader.Draw(e.RenderComponent, e.SpaceComponent)
	}

	if rs.currentShader != nil {
		rs.currentShader.Post()
		rs.currentShader = nil
	}
}

// SetBackground sets the OpenGL ClearColor to the provided color.
func SetBackground(c color.Color) {
	if !engo.Headless() {
		r, g, b, a := c.RGBA()

		engo.Gl.ClearColor(float32(r)/0xffff, float32(g)/0xffff, float32(b)/0xffff, float32(a)/0xffff)
	}
}
<commit_msg>Fix issue with comparing shaders.<commit_after>package common

import (
	"fmt"
	"image/color"
	"sort"

	"sync"

	"engo.io/ecs"
	"engo.io/engo"
	"engo.io/gl"
)

const (
	// RenderSystemPriority is the priority of the RenderSystem
	RenderSystemPriority = -1000
)

type renderChangeMessage struct{}

func (renderChangeMessage) Type() string {
	return "renderChangeMessage"
}

// Drawable is that which can be rendered to OpenGL.
type Drawable interface {
	Texture() *gl.Texture
	Width() float32
	Height() float32
	View() (float32, float32, float32, float32)
	Close()
}

// TextureRepeating is the method used to repeat a texture in OpenGL.
type TextureRepeating uint8

const (
	// NoRepeat does not repeat the texture.
	NoRepeat TextureRepeating = iota
	// ClampToEdge stretches the texture to the edge of the viewport.
	ClampToEdge
	// ClampToBorder stretches the texture to the border of the viewpport.
	ClampToBorder
	// Repeat repeats the texture until the border of the viewport.
	Repeat
	// MirroredRepeat repeats a mirror image of the texture until the border of the viewport.
	MirroredRepeat
)

// ZoomFilter is a filter used when zooming in or out of a texture.
type ZoomFilter uint8

const (
	// FilterNearest is a simple nearest neighbor algorithm
	FilterNearest ZoomFilter = iota
	// FilterLinear is a bilinear interpolation algorithm
	FilterLinear
)

// RenderComponent is the component needed to render an entity.
type RenderComponent struct {
	// Hidden is used to prevent drawing by OpenGL
	Hidden bool
	// Scale is the scale at which to render, in the X and Y axis. Not defining Scale, will default to engo.Point{1, 1}
	Scale engo.Point
	// Color defines how much of the color-components of the texture get used
	Color color.Color
	// Drawable refers to the Texture that should be drawn
	Drawable Drawable
	// Repeat defines how to repeat the Texture if the SpaceComponent of the entity
	// is larger than the texture itself, after applying scale. Defaults to NoRepeat
	// which allows the texture to draw entirely without regard to th SpaceComponent
	// Do not set to anything other than NoRepeat for textures in a sprite sheet.
	// This does not yet work with sprite sheets.
	Repeat TextureRepeating
	// Buffer represents the buffer object itself
	// Avoid using it unless your are writing a custom shader
	Buffer *gl.Buffer
	// BufferContent contains the buffer data
	// Avoid using it unless your are writing a custom shader
	BufferContent []float32

	magFilter, minFilter               ZoomFilter
	magFilterChanged, minFilterChanged bool

	shader Shader
	zIndex float32
}

// SetShader sets the shader used by the RenderComponent.
func (r *RenderComponent) SetShader(s Shader) {
	r.shader = s
	engo.Mailbox.Dispatch(&renderChangeMessage{})
}

// SetZIndex sets the order that the RenderComponent is drawn to the screen. Higher z-indices are drawn on top of
// lower ones if they overlap.
func (r *RenderComponent) SetZIndex(index float32) {
	r.zIndex = index
	engo.Mailbox.Dispatch(&renderChangeMessage{})
}

// SetMinFilter sets the ZoomFilter used for minimizing the RenderComponent
func (r *RenderComponent) SetMinFilter(z ZoomFilter) {
	r.minFilter = z
	r.minFilterChanged = true
}

// SetMagFilter sets the ZoomFilter used for magnifying the RenderComponent
func (r *RenderComponent) SetMagFilter(z ZoomFilter) {
	r.magFilter = z
	r.magFilterChanged = true
}

type renderEntity struct {
	*ecs.BasicEntity
	*RenderComponent
	*SpaceComponent
}

type renderEntityList []renderEntity

func (r renderEntityList) Len() int {
	return len(r)
}

func (r renderEntityList) Less(i, j int) bool {
	// Sort by shader-pointer if they have the same zIndex
	if r[i].RenderComponent.zIndex == r[j].RenderComponent.zIndex {
		// TODO: optimize this for performance
		p1 := fmt.Sprintf("%p", r[i].RenderComponent.shader)
		p2 := fmt.Sprintf("%p", r[j].RenderComponent.shader)
		return p1 < p2
	}

	return r[i].RenderComponent.zIndex < r[j].RenderComponent.zIndex
}

func (r renderEntityList) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

// RenderSystem is the system that draws entities on the OpenGL surface. It requires
// a CameraSystem to work. If a CameraSystem is not in the World when you add RenderSystem
// one is automatically added to the world.
type RenderSystem struct {
	entities renderEntityList
	world    *ecs.World

	sortingNeeded, newCamera bool
	currentShader            Shader
}

// Priority implements the ecs.Prioritizer interface.
func (*RenderSystem) Priority() int { return RenderSystemPriority }

// New initializes the RenderSystem
func (rs *RenderSystem) New(w *ecs.World) {
	rs.world = w

	engo.Mailbox.Listen("NewCameraMessage", func(engo.Message) {
		rs.newCamera = true
	})

	addCameraSystemOnce(w)

	if !engo.Headless() {
		if err := initShaders(w); err != nil {
			panic(err)
		}
		engo.Gl.Enable(engo.Gl.MULTISAMPLE)
	}

	engo.Mailbox.Listen("renderChangeMessage", func(engo.Message) {
		rs.sortingNeeded = true
	})
}

var cameraInitMutex sync.Mutex

func addCameraSystemOnce(w *ecs.World) {
	cameraInitMutex.Lock()
	defer cameraInitMutex.Unlock()

	camSystemAlreadyAdded := false
	for _, system := range w.Systems() {
		switch system.(type) {
		case *CameraSystem:
			camSystemAlreadyAdded = true
		}
	}
	if !camSystemAlreadyAdded {
		w.AddSystem(&CameraSystem{})
	}
}

// Add adds an entity to the RenderSystem. The entity needs a basic, render, and space component to be added to the system.
func (rs *RenderSystem) Add(basic *ecs.BasicEntity, render *RenderComponent, space *SpaceComponent) {
	// Do nothing if entity already exists
	if rs.EntityExists(basic) >= 0 {
		return
	}

	// Setting default shader
	if render.shader == nil {
		switch render.Drawable.(type) {
		case Triangle:
			render.shader = LegacyShader
		case Circle:
			render.shader = LegacyShader
		case Rectangle:
			render.shader = LegacyShader
		case ComplexTriangles:
			render.shader = LegacyShader
		case Text:
			render.shader = TextShader
		default:
			render.shader = DefaultShader
		}
	}

	// This is to prevent users from using the wrong one
	if render.shader == HUDShader {
		switch render.Drawable.(type) {
		case Triangle:
			render.shader = LegacyHUDShader
		case Circle:
			render.shader = LegacyHUDShader
		case Rectangle:
			render.shader = LegacyHUDShader
		case ComplexTriangles:
			render.shader = LegacyHUDShader
		case Text:
			render.shader = TextHUDShader
		default:
			render.shader = HUDShader
		}
	}

	rs.entities = append(rs.entities, renderEntity{basic, render, space})
	rs.sortingNeeded = true
}

// EntityExists looks if the entity is already into the System's entities. It will return the index >= 0 of the object into de rs.entities or -1 if it could not be found.
func (rs *RenderSystem) EntityExists(basic *ecs.BasicEntity) int {
	for index, entity := range rs.entities {
		if entity.ID() == basic.ID() {
			return index
		}
	}

	return -1
}

// AddByInterface adds any Renderable to the render system. Any Entity containing a BasicEntity,RenderComponent, and SpaceComponent anonymously does this automatically
func (rs *RenderSystem) AddByInterface(i ecs.Identifier) {
	o, _ := i.(Renderable)
	rs.Add(o.GetBasicEntity(), o.GetRenderComponent(), o.GetSpaceComponent())
}

// Remove removes an entity from the RenderSystem
func (rs *RenderSystem) Remove(basic ecs.BasicEntity) {
	var delete = rs.EntityExists(&basic)
	if delete >= 0 {
		rs.entities = append(rs.entities[:delete], rs.entities[delete+1:]...)
		rs.sortingNeeded = true
	}
}

// Update draws the entities in the RenderSystem to the OpenGL Surface.
func (rs *RenderSystem) Update(dt float32) {
	if engo.Headless() {
		return
	}

	if rs.sortingNeeded {
		sort.Sort(rs.entities)
		rs.sortingNeeded = false
	}

	if rs.newCamera {
		newCamera(rs.world)
		rs.newCamera = false
	}

	engo.Gl.Clear(engo.Gl.COLOR_BUFFER_BIT)

	// TODO: it's linear for now, but that might very well be a bad idea
	for _, e := range rs.entities {
		if e.RenderComponent.Hidden {
			continue // with other entities
		}

		// Retrieve a shader, may be the default one -- then use it if we aren't already using it
		shader := e.RenderComponent.shader

		// Change Shader if we have to
		if shader != rs.currentShader {
			if rs.currentShader != nil {
				rs.currentShader.Post()
			}
			shader.Pre()
			rs.currentShader = shader
		}

		// Setting default scale to 1
		if e.RenderComponent.Scale.X == 0 && e.RenderComponent.Scale.Y == 0 {
			e.RenderComponent.Scale = engo.Point{X: 1, Y: 1}
		}

		// Setting default to white
		if e.RenderComponent.Color == nil {
			e.RenderComponent.Color = color.White
		}

		rs.currentShader.Draw(e.RenderComponent, e.SpaceComponent)
	}

	if rs.currentShader != nil {
		rs.currentShader.Post()
		rs.currentShader = nil
	}
}

// SetBackground sets the OpenGL ClearColor to the provided color.
func SetBackground(c color.Color) {
	if !engo.Headless() {
		r, g, b, a := c.RGBA()

		engo.Gl.ClearColor(float32(r)/0xffff, float32(g)/0xffff, float32(b)/0xffff, float32(a)/0xffff)
	}
}
<|endoftext|>


<commit_before>//
// Copyright 2017, TCN Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of TCN Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package generator

import (
	"database/sql/driver"
	"fmt"
	"github.com/Sirupsen/logrus"
	"cloud.google.com/go/spanner"
	"github.com/xwb1989/sqlparser"
)

// because spanner has multiple primary keys support,  EVERY field
// found in the query is assumed to be a primary key.  It will build the spanner.Key with the fields in the
// query in the order they are discovered. For Example if i have two queries:
//    q1 = "DELETE FROM test_table WHERE id = 1 AND simple_string="test_string"
//    q2 = "DELETE FROM test_table WHERE simple_string="test_string" AND id = 1
//    q1 would produce key: { 1, "test_string" }
//    q2 would produce key: { "test_string", 2 }
// Try to construct your queries with ANDs  instead of ORs. Because different fields are
// interpreted as primary keys,  it gets too difficult to parse what is meant by queries like:
//    q1 = "DELETE FROM test_table WHERE (id < 1 OR id >= 10) AND simple_string = "test_string"
// it might be possible in the future to parse the meaning of statements like this,  but for now it was
// easier to just drop support of statements for OR expressions
//   Other Rules:
// - NOT expressions are not supported, It is not possible to tell a spanner key what "not"  means.
// - currently only one key range, per primary key is permitted.  Just use two queries. ex.
//    not permitted: DELETE FROM test_table WHERE id > 1 AND id < 10 AND id > 20 AND id < 100
// - Does not support cross table queries
func extractSpannerKeyFromDelete(del *sqlparser.Delete) (*MergableKeyRange, error) {
	where := del.Where
	if where == nil {
		return nil, fmt.Errorf("Must include a where clause that contain primary keys in delete statement")
	}
	myArgs := &Args{}
	logrus.Debugf("where type: %+v\n", where.Type)
	aKeySet := &AwareKeySet{
		Args:     myArgs,
		Keys:     make(map[string]*Key),
		KeyOrder: make([]string, 0),
	}
	err := aKeySet.walkBoolExpr(where.Expr)
	if err != nil {
		return nil, err
	}
	return aKeySet.packKeySet()
}

type Key struct {
	Name       string
	LowerValue interface{}
	UpperValue interface{}
	LowerOpen  bool
	UpperOpen  bool
	HaveLower  bool
	HaveUpper  bool
}

type AwareKeySet struct {
	Keys     map[string]*Key
	KeyOrder []string
	Args     *Args
}

type MergableKeyRange struct {
	Start     *partialArgSlice
	End       *partialArgSlice
	LowerOpen bool
	UpperOpen bool
	HaveLower bool
	HaveUpper bool
}

// all lower bounds are turned into a key together.
// all upper bounds are turned into a key together.
// it is expected that all fields in a query belong together
func (a *AwareKeySet) packKeySet() (*MergableKeyRange, error) {
	var prev *MergableKeyRange
	//makes sure all we dont have holes in our key ranges,  that is undefined behaviour
	for i := len(a.KeyOrder) - 1; i > 0; i-- { // dont check before the first elem
		me := a.Keys[a.KeyOrder[i]]
		keyBeforeMe := a.Keys[a.KeyOrder[i-1]]
		if me.HaveLower {
			if !keyBeforeMe.HaveLower {
				return nil, fmt.Errorf("cannot have a lower bound on a key range without defining all higher priority lower bounds")
			}
		}
		if me.HaveUpper {
			if !keyBeforeMe.HaveUpper {
				return nil, fmt.Errorf("cannot have a upper bound on a key range without defining all higher priority upper bounds")
			}
		}
	}
	for _, k := range a.KeyOrder {
		key := a.Keys[k]
		if prev == nil {
			prev = &MergableKeyRange{Start: newPartialArgSlice(), End: newPartialArgSlice()}
			prev.fromKey(key)
		} else {
			logrus.Debugf("key that will populate prev: %#v\n\n", key)
			err := prev.mergeKey(key)
			logrus.Debugf("merged prev with m %#v\n\n", prev)
			if err != nil {
				return nil, err
			}
		}
	}
	return prev, nil
}

func (k1 *MergableKeyRange) fromKey(key *Key) {
	if key == nil {
		return
	}
	k1.LowerOpen = key.LowerOpen
	k1.UpperOpen = key.UpperOpen
	k1.HaveLower = key.HaveLower
	k1.HaveUpper = key.HaveUpper
	k1.Start.AddArgs(key.LowerValue)
	k1.End.AddArgs(key.UpperValue)
}

func (k *MergableKeyRange) ToKeyRange(args []driver.Value) (*spanner.KeyRange, error) {
	low := k.LowerOpen
	up := k.UpperOpen

	var kind spanner.KeyRangeKind

	if low && up {
		kind = spanner.OpenOpen
	} else if low && !up {
		kind = spanner.OpenClosed
	} else if !low && up {
		kind = spanner.ClosedOpen
	} else {
		kind = spanner.ClosedClosed
	}
	start, err := k.Start.GetFilledArgs(args)
	if err != nil {
		return nil, err
	}
	end, err := k.End.GetFilledArgs(args)
	if err != nil {
		return nil, err
	}
	return &spanner.KeyRange{
		Start: start,
		End:   end,
		Kind:  kind,
	}, nil
}

func (k1 *MergableKeyRange) mergeKey(k2 *Key) error {
	logrus.Debug("\nmerging into k1: %#v\n  k2: %#v\n\n", k1, k2)
	if k2.HaveLower {
		if k1.LowerOpen != k2.LowerOpen {
			return fmt.Errorf("Kinds in ranges must all match")
		}
		k1.Start.AddArgs(k2.LowerValue)
	}
	if k2.HaveUpper {
		if k1.UpperOpen != k2.UpperOpen {
			return fmt.Errorf("Kinds in ranges must all match")
		}
		k1.End.AddArgs(k2.UpperValue)
	}
	return nil
}

func (a *AwareKeySet) addKeyFromValExpr(valExpr sqlparser.ValExpr) (*Key, error) {
	col, ok := valExpr.(*sqlparser.ColName)
	if !ok {
		return nil, fmt.Errorf("not a valid column name")
	}
	if len(col.Qualifier) != 0 {
		return nil, fmt.Errorf("qualifiers not allowed")
	}
	keyName := string(col.Name[:])
	if a.Keys[keyName] == nil {
		a.KeyOrder = append(a.KeyOrder, keyName)
		a.Keys[keyName] = &Key{Name: keyName}
	}
	return a.Keys[keyName], nil
}

func (a *AwareKeySet) walkBoolExpr(boolExpr sqlparser.BoolExpr) error {
	switch expr := boolExpr.(type) {
	case *sqlparser.AndExpr:
		logrus.Debugf("AndExpr %#v\n", expr)
		err := a.walkBoolExpr(expr.Left)
		if err != nil {
			return err
		}
		err = a.walkBoolExpr(expr.Right)
		if err != nil {
			return err
		}
		return nil
	case *sqlparser.OrExpr:
		logrus.Debugf("OrExpr %#v\n", expr)
		return fmt.Errorf("Or Expressions are not currently supported")
	case *sqlparser.ParenBoolExpr:
		logrus.Debugf("ParenBoolExpr %#v\n", expr)
	case *sqlparser.ComparisonExpr:
		logrus.Debugf("ComparisonExpr %#v\n", expr)
		myKey, err := a.addKeyFromValExpr(expr.Left)
		if err != nil {
			return err
		}
		val, err := a.Args.ParseValExpr(expr.Right)
		if err != nil {
			return err
		}
		logrus.Debugf("OPERTATOR %#v\n", expr.Operator)
		switch expr.Operator {
		case "=":
			myKey.LowerValue = val
			myKey.UpperValue = val
			myKey.LowerOpen = false
			myKey.UpperOpen = false
			myKey.HaveUpper = true
			myKey.HaveLower = true
			return nil
		case ">":
			myKey.LowerValue = val
			myKey.LowerOpen = true
			myKey.HaveLower = true
			return nil
		case "<":
			myKey.UpperValue = val
			myKey.UpperOpen = true
			myKey.HaveUpper = true
			return nil
		case ">=":
			myKey.LowerValue = val
			myKey.LowerOpen = false
			myKey.HaveLower = true
			return nil
		case "<=":
			myKey.UpperValue = val
			myKey.UpperOpen = false
			myKey.HaveUpper = true
			return nil
		case "!=":
			return fmt.Errorf("!= comparisons are not supported")
		case "not in", "in":
			return fmt.Errorf("in, and not in  comparisons are not supported")
		default:
			return fmt.Errorf("%#v  is not a supported operator", expr.Operator)
		}
	case *sqlparser.RangeCond:
		logrus.Debugf("RangeCond %#v\n", expr)
		myKey, err := a.addKeyFromValExpr(expr.Left)
		if err != nil {
			return err
		}
		from, err := a.Args.ParseValExpr(expr.From)
		if err != nil {
			return err
		}
		to, err := a.Args.ParseValExpr(expr.To)
		if err != nil {
			return err
		}
		switch expr.Operator {
		case "between":
			myKey.LowerValue = from
			myKey.LowerOpen = true
			myKey.UpperValue = to
			myKey.UpperOpen = true
		case "not between":
			return fmt.Errorf("not between operator is not supported")
		}
	case *sqlparser.ExistsExpr:
		logrus.Debugf("ExistsExpr %#v\n", expr)
		return fmt.Errorf("Exists Expressions are not supported")
	}

	return fmt.Errorf("not a boolexpr %#v\n", boolExpr)
}
<commit_msg>removed dead code<commit_after>//
// Copyright 2017, TCN Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of TCN Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package generator

import (
	"fmt"
	"github.com/Sirupsen/logrus"
	"github.com/xwb1989/sqlparser"
)

// because spanner has multiple primary keys support,  EVERY field
// found in the query is assumed to be a primary key.  It will build the spanner.Key with the fields in the
// query in the order they are discovered. For Example if i have two queries:
//    q1 = "DELETE FROM test_table WHERE id = 1 AND simple_string="test_string"
//    q2 = "DELETE FROM test_table WHERE simple_string="test_string" AND id = 1
//    q1 would produce key: { 1, "test_string" }
//    q2 would produce key: { "test_string", 2 }
// Try to construct your queries with ANDs  instead of ORs. Because different fields are
// interpreted as primary keys,  it gets too difficult to parse what is meant by queries like:
//    q1 = "DELETE FROM test_table WHERE (id < 1 OR id >= 10) AND simple_string = "test_string"
// it might be possible in the future to parse the meaning of statements like this,  but for now it was
// easier to just drop support of statements for OR expressions
//   Other Rules:
// - NOT expressions are not supported, It is not possible to tell a spanner key what "not"  means.
// - currently only one key range, per primary key is permitted.  Just use two queries. ex.
//    not permitted: DELETE FROM test_table WHERE id > 1 AND id < 10 AND id > 20 AND id < 100
// - Does not support cross table queries
func extractSpannerKeyFromDelete(del *sqlparser.Delete) (*MergableKeyRange, error) {
	where := del.Where
	if where == nil {
		return nil, fmt.Errorf("Must include a where clause that contain primary keys in delete statement")
	}
	myArgs := &Args{}
	logrus.Debugf("where type: %+v\n", where.Type)
	aKeySet := &AwareKeySet{
		Args:     myArgs,
		Keys:     make(map[string]*Key),
		KeyOrder: make([]string, 0),
	}
	err := aKeySet.walkBoolExpr(where.Expr)
	if err != nil {
		return nil, err
	}
	return aKeySet.packKeyRange()
}

type Key struct {
	Name       string
	LowerValue interface{}
	UpperValue interface{}
	LowerOpen  bool
	UpperOpen  bool
	HaveLower  bool
	HaveUpper  bool
}

type AwareKeySet struct {
	Keys     map[string]*Key
	KeyOrder []string
	Args     *Args
}

type MergableKeyRange struct {
	Start     *partialArgSlice
	End       *partialArgSlice
	LowerOpen bool
	UpperOpen bool
	HaveLower bool
	HaveUpper bool
}

// all lower bounds are turned into a key together.
// all upper bounds are turned into a key together.
// it is expected that all fields in a query belong together
func (a *AwareKeySet) packKeyRange() (*MergableKeyRange, error) {
	var prev *MergableKeyRange
	//makes sure all we dont have holes in our key ranges,  that is undefined behaviour
	for i := len(a.KeyOrder) - 1; i > 0; i-- { // dont check before the first elem
		me := a.Keys[a.KeyOrder[i]]
		keyBeforeMe := a.Keys[a.KeyOrder[i-1]]
		if me.HaveLower {
			if !keyBeforeMe.HaveLower {
				return nil, fmt.Errorf("cannot have a lower bound on a key range without defining all higher priority lower bounds")
			}
		}
		if me.HaveUpper {
			if !keyBeforeMe.HaveUpper {
				return nil, fmt.Errorf("cannot have a upper bound on a key range without defining all higher priority upper bounds")
			}
		}
	}
	for _, k := range a.KeyOrder {
		key := a.Keys[k]
		if prev == nil {
			prev = &MergableKeyRange{Start: newPartialArgSlice(), End: newPartialArgSlice()}
			prev.fromKey(key)
		} else {
			logrus.Debugf("key that will populate prev: %#v\n\n", key)
			err := prev.mergeKey(key)
			logrus.Debugf("merged prev with m %#v\n\n", prev)
			if err != nil {
				return nil, err
			}
		}
	}
	return prev, nil
}

func (k1 *MergableKeyRange) fromKey(key *Key) {
	if key == nil {
		return
	}
	k1.LowerOpen = key.LowerOpen
	k1.UpperOpen = key.UpperOpen
	k1.HaveLower = key.HaveLower
	k1.HaveUpper = key.HaveUpper
	k1.Start.AddArgs(key.LowerValue)
	k1.End.AddArgs(key.UpperValue)
}

func (k1 *MergableKeyRange) mergeKey(k2 *Key) error {
	logrus.Debug("\nmerging into k1: %#v\n  k2: %#v\n\n", k1, k2)
	if k2.HaveLower {
		if k1.LowerOpen != k2.LowerOpen {
			return fmt.Errorf("Kinds in ranges must all match")
		}
		k1.Start.AddArgs(k2.LowerValue)
	}
	if k2.HaveUpper {
		if k1.UpperOpen != k2.UpperOpen {
			return fmt.Errorf("Kinds in ranges must all match")
		}
		k1.End.AddArgs(k2.UpperValue)
	}
	return nil
}

func (a *AwareKeySet) addKeyFromValExpr(valExpr sqlparser.ValExpr) (*Key, error) {
	col, ok := valExpr.(*sqlparser.ColName)
	if !ok {
		return nil, fmt.Errorf("not a valid column name")
	}
	if len(col.Qualifier) != 0 {
		return nil, fmt.Errorf("qualifiers not allowed")
	}
	keyName := string(col.Name[:])
	if a.Keys[keyName] == nil {
		a.KeyOrder = append(a.KeyOrder, keyName)
		a.Keys[keyName] = &Key{Name: keyName}
	}
	return a.Keys[keyName], nil
}

func (a *AwareKeySet) walkBoolExpr(boolExpr sqlparser.BoolExpr) error {
	switch expr := boolExpr.(type) {
	case *sqlparser.AndExpr:
		logrus.Debugf("AndExpr %#v\n", expr)
		err := a.walkBoolExpr(expr.Left)
		if err != nil {
			return err
		}
		err = a.walkBoolExpr(expr.Right)
		if err != nil {
			return err
		}
		return nil
	case *sqlparser.OrExpr:
		logrus.Debugf("OrExpr %#v\n", expr)
		return fmt.Errorf("Or Expressions are not currently supported")
	case *sqlparser.ParenBoolExpr:
		logrus.Debugf("ParenBoolExpr %#v\n", expr)
	case *sqlparser.ComparisonExpr:
		logrus.Debugf("ComparisonExpr %#v\n", expr)
		myKey, err := a.addKeyFromValExpr(expr.Left)
		if err != nil {
			return err
		}
		val, err := a.Args.ParseValExpr(expr.Right)
		if err != nil {
			return err
		}
		logrus.Debugf("OPERTATOR %#v\n", expr.Operator)
		switch expr.Operator {
		case "=":
			myKey.LowerValue = val
			myKey.UpperValue = val
			myKey.LowerOpen = false
			myKey.UpperOpen = false
			myKey.HaveUpper = true
			myKey.HaveLower = true
			return nil
		case ">":
			myKey.LowerValue = val
			myKey.LowerOpen = true
			myKey.HaveLower = true
			return nil
		case "<":
			myKey.UpperValue = val
			myKey.UpperOpen = true
			myKey.HaveUpper = true
			return nil
		case ">=":
			myKey.LowerValue = val
			myKey.LowerOpen = false
			myKey.HaveLower = true
			return nil
		case "<=":
			myKey.UpperValue = val
			myKey.UpperOpen = false
			myKey.HaveUpper = true
			return nil
		case "!=":
			return fmt.Errorf("!= comparisons are not supported")
		case "not in", "in":
			return fmt.Errorf("in, and not in  comparisons are not supported")
		default:
			return fmt.Errorf("%#v  is not a supported operator", expr.Operator)
		}
	case *sqlparser.RangeCond:
		logrus.Debugf("RangeCond %#v\n", expr)
		myKey, err := a.addKeyFromValExpr(expr.Left)
		if err != nil {
			return err
		}
		from, err := a.Args.ParseValExpr(expr.From)
		if err != nil {
			return err
		}
		to, err := a.Args.ParseValExpr(expr.To)
		if err != nil {
			return err
		}
		switch expr.Operator {
		case "between":
			myKey.LowerValue = from
			myKey.LowerOpen = true
			myKey.UpperValue = to
			myKey.UpperOpen = true
		case "not between":
			return fmt.Errorf("not between operator is not supported")
		}
	case *sqlparser.ExistsExpr:
		logrus.Debugf("ExistsExpr %#v\n", expr)
		return fmt.Errorf("Exists Expressions are not supported")
	}

	return fmt.Errorf("not a boolexpr %#v\n", boolExpr)
}
<|endoftext|>


<commit_before>package math

import "math"

// Vector - struct holding X Y Z values of a 3D vector
type Vector struct {
	X float64
	Y float64
	Z float64
}

// Add - adds two vectors together
func (a Vector) Add(b Vector) Vector {
	return Vector{
		X: a.X + b.X,
		Y: a.Y + b.Y,
		Z: a.Z + b.Z,
	}
}

// Sub - subtracts b Vector from a Vector
func (a Vector) Sub(b Vector) Vector {
	return Vector{
		X: a.X - b.X,
		Y: a.Y - b.Y,
		Z: a.Z - b.Z,
	}
}

// MultiplyByScalar - multiplies a Vector by s float64
func (a Vector) MultiplyByScalar(s float64) Vector {
	return Vector{
		X: a.X * s,
		Y: a.Y * s,
		Z: a.Z * s,
	}
}

// Length - calculates the length(magnitude) of the Vector
func (a Vector) Length() float64 {
	return math.Sqrt(a.Dot(a))
}

// Dot - calculates the dot product of two Vectors
func (a Vector) Dot(b Vector) float64 {
	return a.X*b.X + a.Y*b.Y + a.Z*b.Z
}

// Cross - calculates the cross product of two Vectors
func (a Vector) Cross(b Vector) Vector {
	return Vector{
		X: a.Y*b.Z - a.Z*b.Y,
		Y: a.Z*b.X - a.X*b.Z,
		Z: a.X*b.Y - a.Y*b.X,
	}
}

// ToVersor - returns a versor created from the given vector
func (a Vector) ToVersor() Vector {
	return a.MultiplyByScalar(1. / a.Length())
}
<commit_msg>Refactor Vector struct (nothing fancy)<commit_after>package math

import "math"

// Vector - struct holding X Y Z values of a 3D vector
type Vector struct {
	X, Y, Z float64
}

// Add - adds two vectors together
func (a Vector) Add(b Vector) Vector {
	return Vector{
		X: a.X + b.X,
		Y: a.Y + b.Y,
		Z: a.Z + b.Z,
	}
}

// Sub - subtracts b Vector from a Vector
func (a Vector) Sub(b Vector) Vector {
	return Vector{
		X: a.X - b.X,
		Y: a.Y - b.Y,
		Z: a.Z - b.Z,
	}
}

// MultiplyByScalar - multiplies a Vector by s float64
func (a Vector) MultiplyByScalar(s float64) Vector {
	return Vector{
		X: a.X * s,
		Y: a.Y * s,
		Z: a.Z * s,
	}
}

// Length - calculates the length(magnitude) of the Vector
func (a Vector) Length() float64 {
	return math.Sqrt(a.Dot(a))
}

// Dot - calculates the dot product of two Vectors
func (a Vector) Dot(b Vector) float64 {
	return a.X*b.X + a.Y*b.Y + a.Z*b.Z
}

// Cross - calculates the cross product of two Vectors
func (a Vector) Cross(b Vector) Vector {
	return Vector{
		X: a.Y*b.Z - a.Z*b.Y,
		Y: a.Z*b.X - a.X*b.Z,
		Z: a.X*b.Y - a.Y*b.X,
	}
}

// ToVersor - returns a versor created from the given vector
func (a Vector) ToVersor() Vector {
	return a.MultiplyByScalar(1. / a.Length())
}
<|endoftext|>


<commit_before>package image

import (
	"io"
	"os"

	"github.com/dnephin/dobi/tasks/context"
	docker "github.com/fsouza/go-dockerclient"
)

// RunPush pushes an image to the registry
func RunPush(ctx *context.ExecuteContext, t *Task, _ bool) (bool, error) {
	pushTag := func(tag string) error {
		return pushImage(ctx, t, tag)
	}
	if err := t.ForEachTag(ctx, pushTag); err != nil {
		return false, err
	}
	t.logger().Info("Pushed")
	return true, nil
}

func pushImage(ctx *context.ExecuteContext, t *Task, tag string) error {
	repo, err := parseAuthRepo(t.config.Image)
	if err != nil {
		return err
	}

	return Stream(os.Stdout, func(out io.Writer) error {
		return ctx.Client.PushImage(docker.PushImageOptions{
			Name:          tag,
			OutputStream:  out,
			RawJSONStream: true,
			// TODO: timeout
		}, ctx.GetAuthConfig(repo))
	})
}
<commit_msg>feat(image): push action authenticates against each tag<commit_after>package image

import (
	"io"
	"os"

	"github.com/dnephin/dobi/tasks/context"
	docker "github.com/fsouza/go-dockerclient"
)

// RunPush pushes an image to the registry
func RunPush(ctx *context.ExecuteContext, t *Task, _ bool) (bool, error) {
	pushTag := func(tag string) error {
		return pushImage(ctx, t, tag)
	}
	if err := t.ForEachTag(ctx, pushTag); err != nil {
		return false, err
	}
	t.logger().Info("Pushed")
	return true, nil
}

func pushImage(ctx *context.ExecuteContext, t *Task, tag string) error {
	repo, err := parseAuthRepo(tag)
	if err != nil {
		return err
	}

	return Stream(os.Stdout, func(out io.Writer) error {
		return ctx.Client.PushImage(docker.PushImageOptions{
			Name:          tag,
			OutputStream:  out,
			RawJSONStream: true,
			// TODO: timeout
		}, ctx.GetAuthConfig(repo))
	})
}
<|endoftext|>


<commit_before>package client

import (
	"crypto/tls"
	"errors"
	"net/http"
)

var errTLSConfigUnavailable = errors.New("TLSConfig unavailable")

// transportFunc allows us to inject a mock transport for testing. We define it
// here so we can detect the tlsconfig and return nil for only this type.
type transportFunc func(*http.Request) (*http.Response, error)

func (tf transportFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return tf(req)
}

// resolveTLSConfig attempts to resolve the tls configuration from the
// RoundTripper.
func resolveTLSConfig(transport http.RoundTripper) *tls.Config {
	switch tr := transport.(type) {
	case *http.Transport:
		return tr.TLSClientConfig
	default:
		return nil
	}
}
<commit_msg>Remove unused var 'errTLSConfigUnavailable' (#29626)<commit_after>package client

import (
	"crypto/tls"
	"net/http"
)

// transportFunc allows us to inject a mock transport for testing. We define it
// here so we can detect the tlsconfig and return nil for only this type.
type transportFunc func(*http.Request) (*http.Response, error)

func (tf transportFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return tf(req)
}

// resolveTLSConfig attempts to resolve the tls configuration from the
// RoundTripper.
func resolveTLSConfig(transport http.RoundTripper) *tls.Config {
	switch tr := transport.(type) {
	case *http.Transport:
		return tr.TLSClientConfig
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package velox

import (
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/donovanhide/eventsource"
	"github.com/gorilla/websocket"
)

type transport interface {
	connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error
	send(upd *update) error
	close() error
}

//=========================

var defaultUpgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type wsTransport struct {
	conn *websocket.Conn
}

func (ws *wsTransport) connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error {
	conn, err := defaultUpgrader.Upgrade(w, r, nil)
	if err != nil {
		connectingCh <- err
		return fmt.Errorf("cannot upgrade connection: %s", err)
	}
	ws.conn = conn
	connectingCh <- nil
	//block on connection
	for {
		//msgType, msgBytes, err
		if _, _, err := conn.ReadMessage(); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
	}
	return nil
}

func (ws *wsTransport) send(upd *update) error {
	return ws.conn.WriteJSON(upd)
}

func (ws *wsTransport) close() error {
	return ws.conn.Close()
}

//=========================

type evtSrcTransport struct {
	s *eventsource.Server
}

//evtSrcWriter is a hack to
//watch when connection is ready
type evtSrcWriter struct {
	isFlushed    bool
	connectingCh chan error
	http.ResponseWriter
}

func (ew *evtSrcWriter) CloseNotify() <-chan bool {
	return ew.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

//HACK: uses eventsource's call to flush as a signal that its connected
func (ew *evtSrcWriter) Flush() {
	if !ew.isFlushed {
		ew.connectingCh <- nil
	}
	ew.isFlushed = true
	f, ok := ew.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

func (es *evtSrcTransport) connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error {
	es.s = eventsource.NewServer()
	if !strings.Contains(w.Header().Get("Content-Encoding"), "gzip") {
		es.s.Gzip = true
	}
	ew := &evtSrcWriter{false, connectingCh, w}
	es.s.Handler("events").ServeHTTP(ew, r)
	return nil
}

func (es *evtSrcTransport) send(upd *update) error {
	es.s.Publish([]string{"events"}, upd)
	return nil
}

func (es *evtSrcTransport) close() error {
	if es.s != nil {
		es.s.Close()
	}
	return nil
}
<commit_msg>set read/write deadlines on websocket connections<commit_after>package velox

import (
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/donovanhide/eventsource"
	"github.com/gorilla/websocket"
)

type transport interface {
	connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error
	send(upd *update) error
	close() error
}

//=========================

var defaultUpgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type wsTransport struct {
	conn *websocket.Conn
}

func (ws *wsTransport) connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error {
	conn, err := defaultUpgrader.Upgrade(w, r, nil)
	if err != nil {
		connectingCh <- err
		return fmt.Errorf("cannot upgrade connection: %s", err)
	}
	ws.conn = conn
	connectingCh <- nil
	//block on connection
	for {
		//msgType, msgBytes, err
		conn.SetReadDeadline(time.Now().Add(35 * time.Second))
		//should receive pings at an interval
		if _, _, err := conn.ReadMessage(); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
	}
	return nil
}

func (ws *wsTransport) send(upd *update) error {
	ws.conn.SetWriteDeadline(time.Now().Add(35 * time.Second))
	return ws.conn.WriteJSON(upd)
}

func (ws *wsTransport) close() error {
	return ws.conn.Close()
}

//=========================

type evtSrcTransport struct {
	s *eventsource.Server
}

//evtSrcWriter is a hack to
//watch when connection is ready
type evtSrcWriter struct {
	isFlushed    bool
	connectingCh chan error
	http.ResponseWriter
}

func (ew *evtSrcWriter) CloseNotify() <-chan bool {
	return ew.ResponseWriter.(http.CloseNotifier).CloseNotify()
}

//HACK: uses eventsource's call to flush as a signal that its connected
func (ew *evtSrcWriter) Flush() {
	if !ew.isFlushed {
		ew.connectingCh <- nil
	}
	ew.isFlushed = true
	f, ok := ew.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

func (es *evtSrcTransport) connect(w http.ResponseWriter, r *http.Request, connectingCh chan error) error {
	es.s = eventsource.NewServer()
	if !strings.Contains(w.Header().Get("Content-Encoding"), "gzip") {
		es.s.Gzip = true
	}
	ew := &evtSrcWriter{false, connectingCh, w}
	es.s.Handler("events").ServeHTTP(ew, r)
	return nil
}

func (es *evtSrcTransport) send(upd *update) error {
	es.s.Publish([]string{"events"}, upd)
	return nil
}

func (es *evtSrcTransport) close() error {
	if es.s != nil {
		es.s.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package minion

import (
	"bytes"
	"encoding/json"
	"log"
	"math/rand"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dnaeon/backoff"
	"github.com/dnaeon/gru/classifier"
	"github.com/dnaeon/gru/task"
	"github.com/dnaeon/gru/utils"

	etcdclient "github.com/coreos/etcd/client"
	"github.com/pborman/uuid"
	"golang.org/x/net/context"
)

// Minions keyspace in etcd
const EtcdMinionSpace = "/gru/minion"

// Etcd Minion
type etcdMinion struct {
	// Name of the minion
	name string

	// Minion root directory in etcd
	rootDir string

	// Minion queue directory in etcd
	queueDir string

	// Log directory of previously executed tasks
	logDir string

	// Classifier directory in etcd
	classifierDir string

	// Minion unique identifier
	id uuid.UUID

	// KeysAPI client to etcd
	kapi etcdclient.KeysAPI

	// Channel over which tasks are sent for processing
	taskQueue chan *task.Task

	// Channel used to signal shutdown time
	done chan struct{}
}

// Creates a new etcd minion
func NewEtcdMinion(name string, cfg etcdclient.Config) Minion {
	c, err := etcdclient.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	kapi := etcdclient.NewKeysAPI(c)
	id := utils.GenerateUUID(name)
	rootDir := filepath.Join(EtcdMinionSpace, id.String())
	queueDir := filepath.Join(rootDir, "queue")
	classifierDir := filepath.Join(rootDir, "classifier")
	logDir := filepath.Join(rootDir, "log")
	taskQueue := make(chan *task.Task)
	done := make(chan struct{})

	m := &etcdMinion{
		name:          name,
		rootDir:       rootDir,
		queueDir:      queueDir,
		classifierDir: classifierDir,
		logDir:        logDir,
		id:            id,
		kapi:          kapi,
		taskQueue:     taskQueue,
		done:          done,
	}

	return m
}

// Checks for any pending tasks and sends them
// for processing if there are any
func (m *etcdMinion) checkQueue() error {
	opts := &etcdclient.GetOptions{
		Recursive: true,
		Sort:      true,
	}

	// Get backlog tasks if any
	// If the directory key in etcd is missing that is okay, since
	// it means there are no pending tasks for processing
	resp, err := m.kapi.Get(context.Background(), m.queueDir, opts)
	if err != nil {
		if eerr, ok := err.(etcdclient.Error); !ok || eerr.Code == etcdclient.ErrorCodeKeyNotFound {
			return err
		}
	}

	backlog := resp.Node.Nodes
	if len(backlog) == 0 {
		// No backlog tasks found
		return nil
	}

	log.Printf("Found %d pending tasks in queue", len(backlog))
	for _, node := range backlog {
		t, err := EtcdUnmarshalTask(node)
		m.kapi.Delete(context.Background(), node.Key, nil)

		if err != nil {
			continue
		}

		m.taskQueue <- t
	}

	return nil
}

// Runs periodic jobs such as refreshing classifiers and
// updating the lastseen time every five minutes
func (m *etcdMinion) periodicRunner() {
	schedule := time.Minute * 5
	ticker := time.NewTicker(schedule)
	log.Printf("Periodic scheduler set to run every %s\n", schedule)

	for {
		select {
		case <-m.done:
			break
		case now := <-ticker.C:
			// Run periodic jobs
			m.classify()
			m.SetLastseen(now.Unix())
		}
	}
}

// Processes new tasks
func (m *etcdMinion) processTask(t *task.Task) error {
	var buf bytes.Buffer

	// Update state of task to indicate that we are now processing it
	t.State = task.TaskStateProcessing
	m.SaveTaskResult(t)

	cmd := exec.Command(t.Command, t.Args...)
	cmd.Stdout = &buf
	cmd.Stderr = &buf

	log.Printf("Processing task %s\n", t.TaskID)

	cmdError := cmd.Run()
	t.TimeProcessed = time.Now().Unix()
	t.Result = buf.String()

	if cmdError != nil {
		log.Printf("Failed to process task %s\n", t.TaskID)
		t.Error = cmdError.Error()
		t.State = task.TaskStateFailed
	} else {
		log.Printf("Finished processing task %s\n", t.TaskID)
		t.State = task.TaskStateSuccess
	}

	m.SaveTaskResult(t)

	return cmdError
}

// Classifies the minion
func (m *etcdMinion) classify() error {
	for key, _ := range classifier.Registry {
		klassifier, err := classifier.Get(key)

		if err != nil {
			log.Printf("Failed to get classifier %s: %s\n", key, err)
			continue
		}

		m.SetClassifier(klassifier)
	}

	return nil
}

// Unmarshals task from etcd
func EtcdUnmarshalTask(node *etcdclient.Node) (*task.Task, error) {
	task := new(task.Task)
	err := json.Unmarshal([]byte(node.Value), &task)

	return task, err
}

// Returns the minion unique identifier
func (m *etcdMinion) ID() uuid.UUID {
	return m.id
}

// Set the human-readable name of the minion in etcd
func (m *etcdMinion) SetName(name string) error {
	nameKey := filepath.Join(m.rootDir, "name")
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err := m.kapi.Set(context.Background(), nameKey, name, opts)
	if err != nil {
		log.Printf("Failed to set name of minion: %s\n", err)
	}

	return err
}

// Set the time the minion was last seen in seconds since the Epoch
func (m *etcdMinion) SetLastseen(s int64) error {
	lastseenKey := filepath.Join(m.rootDir, "lastseen")
	lastseenValue := strconv.FormatInt(s, 10)
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err := m.kapi.Set(context.Background(), lastseenKey, lastseenValue, opts)
	if err != nil {
		log.Printf("Failed to set lastseen time: %s\n", err)
	}

	return err
}

// Classifies the minion
func (m *etcdMinion) SetClassifier(c *classifier.Classifier) error {
	// Classifiers in etcd expire after an hour
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
		TTL:       time.Hour,
	}

	// Serialize classifier to JSON and save it in etcd
	data, err := json.Marshal(c)
	if err != nil {
		log.Printf("Failed to serialize classifier %s: %s\n", c.Key, err)
		return err
	}

	// Classifier key in etcd
	klassifierKey := filepath.Join(m.classifierDir, c.Key)
	_, err = m.kapi.Set(context.Background(), klassifierKey, string(data), opts)

	if err != nil {
		log.Printf("Failed to set classifier %s: %s\n", c.Key, err)
		return err
	}

	return nil
}

// Monitors etcd for new tasks
func (m *etcdMinion) TaskListener(c chan<- *task.Task) error {
	log.Printf("Task listener is watching %s\n", m.queueDir)

	rand.Seed(time.Now().UTC().UnixNano())
	b := backoff.Backoff{
		Min:    1 * time.Second,
		Max:    10 * time.Minute,
		Factor: 2.0,
		Jitter: true,
	}

	watcherOpts := &etcdclient.WatcherOptions{
		Recursive: true,
	}
	watcher := m.kapi.Watcher(m.queueDir, watcherOpts)

	for {
		resp, err := watcher.Next(context.Background())
		if err != nil {
			// Use a backoff and retry later again
			duration := b.Duration()
			log.Printf("%s, retrying in %s\n", err, duration)
			time.Sleep(duration)
			continue
		}

		// Ignore "delete" events when removing a task from the queue
		action := strings.ToLower(resp.Action)
		if strings.EqualFold(action, "delete") {
			continue
		}

		// Unmarshal and remove task from the queue
		t, err := EtcdUnmarshalTask(resp.Node)
		m.kapi.Delete(context.Background(), resp.Node.Key, nil)

		if err != nil {
			log.Printf("Received invalid task %s: %s\n", resp.Node.Key, err)
			continue
		}

		log.Printf("Received task %s\n", t.TaskID)
		c <- t
	}

	return nil
}

// Processes new tasks
func (m *etcdMinion) TaskRunner(c <-chan *task.Task) error {
	log.Println("Starting task runner")

	for {
		select {
		case <-m.done:
			break
		case t := <-c:
			t.State = task.TaskStateQueued
			t.TimeReceived = time.Now().Unix()
			m.SaveTaskResult(t)

			if t.IsConcurrent {
				go m.processTask(t)
			} else {
				m.processTask(t)
			}
		}
	}

	return nil
}

// Saves a task in etcd
func (m *etcdMinion) SaveTaskResult(t *task.Task) error {
	taskKey := filepath.Join(m.logDir, t.TaskID.String())

	data, err := json.Marshal(t)
	if err != nil {
		log.Printf("Failed to serialize task %s: %s\n", t.TaskID, err)
		return err
	}

	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err = m.kapi.Set(context.Background(), taskKey, string(data), opts)
	if err != nil {
		log.Printf("Failed to save task %s: %s\n", t.TaskID, err)
	}

	return err
}

// Main entry point of the minion
func (m *etcdMinion) Serve() error {
	err := m.SetName(m.name)
	if err != nil {
		return err
	}

	now := time.Now().Unix()
	err = m.SetLastseen(now)
	if err != nil {
		return err
	}

	// Start minion services
	go m.classify()
	go m.periodicRunner()
	go m.checkQueue()
	go m.TaskRunner(m.taskQueue)
	go m.TaskListener(m.taskQueue)

	log.Printf("Minion %s is ready to serve", m.ID())

	return nil
}

// Stops the minion and performs any cleanup tasks
func (m *etcdMinion) Stop() error {
	log.Println("Minion is shutting down")

	close(m.taskQueue)
	close(m.done)

	return nil
}
<commit_msg>Periodically check for backlog tasks<commit_after>package minion

import (
	"bytes"
	"encoding/json"
	"log"
	"math/rand"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dnaeon/backoff"
	"github.com/dnaeon/gru/classifier"
	"github.com/dnaeon/gru/task"
	"github.com/dnaeon/gru/utils"

	etcdclient "github.com/coreos/etcd/client"
	"github.com/pborman/uuid"
	"golang.org/x/net/context"
)

// Minions keyspace in etcd
const EtcdMinionSpace = "/gru/minion"

// Etcd Minion
type etcdMinion struct {
	// Name of the minion
	name string

	// Minion root directory in etcd
	rootDir string

	// Minion queue directory in etcd
	queueDir string

	// Log directory of previously executed tasks
	logDir string

	// Classifier directory in etcd
	classifierDir string

	// Minion unique identifier
	id uuid.UUID

	// KeysAPI client to etcd
	kapi etcdclient.KeysAPI

	// Channel over which tasks are sent for processing
	taskQueue chan *task.Task

	// Channel used to signal shutdown time
	done chan struct{}
}

// Creates a new etcd minion
func NewEtcdMinion(name string, cfg etcdclient.Config) Minion {
	c, err := etcdclient.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	kapi := etcdclient.NewKeysAPI(c)
	id := utils.GenerateUUID(name)
	rootDir := filepath.Join(EtcdMinionSpace, id.String())
	queueDir := filepath.Join(rootDir, "queue")
	classifierDir := filepath.Join(rootDir, "classifier")
	logDir := filepath.Join(rootDir, "log")
	taskQueue := make(chan *task.Task)
	done := make(chan struct{})

	m := &etcdMinion{
		name:          name,
		rootDir:       rootDir,
		queueDir:      queueDir,
		classifierDir: classifierDir,
		logDir:        logDir,
		id:            id,
		kapi:          kapi,
		taskQueue:     taskQueue,
		done:          done,
	}

	return m
}

// Checks for any pending tasks and sends them
// for processing if there are any
func (m *etcdMinion) checkQueue() error {
	opts := &etcdclient.GetOptions{
		Recursive: true,
		Sort:      true,
	}

	// Get backlog tasks if any
	// If the directory key in etcd is missing that is okay, since
	// it means there are no pending tasks for processing
	resp, err := m.kapi.Get(context.Background(), m.queueDir, opts)
	if err != nil {
		if eerr, ok := err.(etcdclient.Error); !ok || eerr.Code == etcdclient.ErrorCodeKeyNotFound {
			return err
		}
	}

	backlog := resp.Node.Nodes
	if len(backlog) == 0 {
		// No backlog tasks found
		return nil
	}

	log.Printf("Found %d pending tasks in queue", len(backlog))
	for _, node := range backlog {
		t, err := EtcdUnmarshalTask(node)
		m.kapi.Delete(context.Background(), node.Key, nil)

		if err != nil {
			continue
		}

		m.taskQueue <- t
	}

	return nil
}

// Runs periodic jobs such as refreshing classifiers and
// updating the lastseen time every five minutes
func (m *etcdMinion) periodicRunner() {
	schedule := time.Minute * 5
	ticker := time.NewTicker(schedule)
	log.Printf("Periodic scheduler set to run every %s\n", schedule)

	for {
		select {
		case <-m.done:
			break
		case now := <-ticker.C:
			// Run periodic jobs
			m.classify()
			m.checkQueue()
			m.SetLastseen(now.Unix())
		}
	}
}

// Processes new tasks
func (m *etcdMinion) processTask(t *task.Task) error {
	var buf bytes.Buffer

	// Update state of task to indicate that we are now processing it
	t.State = task.TaskStateProcessing
	m.SaveTaskResult(t)

	cmd := exec.Command(t.Command, t.Args...)
	cmd.Stdout = &buf
	cmd.Stderr = &buf

	log.Printf("Processing task %s\n", t.TaskID)

	cmdError := cmd.Run()
	t.TimeProcessed = time.Now().Unix()
	t.Result = buf.String()

	if cmdError != nil {
		log.Printf("Failed to process task %s\n", t.TaskID)
		t.Error = cmdError.Error()
		t.State = task.TaskStateFailed
	} else {
		log.Printf("Finished processing task %s\n", t.TaskID)
		t.State = task.TaskStateSuccess
	}

	m.SaveTaskResult(t)

	return cmdError
}

// Classifies the minion
func (m *etcdMinion) classify() error {
	for key, _ := range classifier.Registry {
		klassifier, err := classifier.Get(key)

		if err != nil {
			log.Printf("Failed to get classifier %s: %s\n", key, err)
			continue
		}

		m.SetClassifier(klassifier)
	}

	return nil
}

// Unmarshals task from etcd
func EtcdUnmarshalTask(node *etcdclient.Node) (*task.Task, error) {
	task := new(task.Task)
	err := json.Unmarshal([]byte(node.Value), &task)

	return task, err
}

// Returns the minion unique identifier
func (m *etcdMinion) ID() uuid.UUID {
	return m.id
}

// Set the human-readable name of the minion in etcd
func (m *etcdMinion) SetName(name string) error {
	nameKey := filepath.Join(m.rootDir, "name")
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err := m.kapi.Set(context.Background(), nameKey, name, opts)
	if err != nil {
		log.Printf("Failed to set name of minion: %s\n", err)
	}

	return err
}

// Set the time the minion was last seen in seconds since the Epoch
func (m *etcdMinion) SetLastseen(s int64) error {
	lastseenKey := filepath.Join(m.rootDir, "lastseen")
	lastseenValue := strconv.FormatInt(s, 10)
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err := m.kapi.Set(context.Background(), lastseenKey, lastseenValue, opts)
	if err != nil {
		log.Printf("Failed to set lastseen time: %s\n", err)
	}

	return err
}

// Classifies the minion
func (m *etcdMinion) SetClassifier(c *classifier.Classifier) error {
	// Classifiers in etcd expire after an hour
	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
		TTL:       time.Hour,
	}

	// Serialize classifier to JSON and save it in etcd
	data, err := json.Marshal(c)
	if err != nil {
		log.Printf("Failed to serialize classifier %s: %s\n", c.Key, err)
		return err
	}

	// Classifier key in etcd
	klassifierKey := filepath.Join(m.classifierDir, c.Key)
	_, err = m.kapi.Set(context.Background(), klassifierKey, string(data), opts)

	if err != nil {
		log.Printf("Failed to set classifier %s: %s\n", c.Key, err)
		return err
	}

	return nil
}

// Monitors etcd for new tasks
func (m *etcdMinion) TaskListener(c chan<- *task.Task) error {
	log.Printf("Task listener is watching %s\n", m.queueDir)

	rand.Seed(time.Now().UTC().UnixNano())
	b := backoff.Backoff{
		Min:    1 * time.Second,
		Max:    10 * time.Minute,
		Factor: 2.0,
		Jitter: true,
	}

	watcherOpts := &etcdclient.WatcherOptions{
		Recursive: true,
	}
	watcher := m.kapi.Watcher(m.queueDir, watcherOpts)

	for {
		resp, err := watcher.Next(context.Background())
		if err != nil {
			// Use a backoff and retry later again
			duration := b.Duration()
			log.Printf("%s, retrying in %s\n", err, duration)
			time.Sleep(duration)
			continue
		}

		// Ignore "delete" events when removing a task from the queue
		action := strings.ToLower(resp.Action)
		if strings.EqualFold(action, "delete") {
			continue
		}

		// Unmarshal and remove task from the queue
		t, err := EtcdUnmarshalTask(resp.Node)
		m.kapi.Delete(context.Background(), resp.Node.Key, nil)

		if err != nil {
			log.Printf("Received invalid task %s: %s\n", resp.Node.Key, err)
			continue
		}

		log.Printf("Received task %s\n", t.TaskID)
		c <- t
	}

	return nil
}

// Processes new tasks
func (m *etcdMinion) TaskRunner(c <-chan *task.Task) error {
	log.Println("Starting task runner")

	for {
		select {
		case <-m.done:
			break
		case t := <-c:
			t.State = task.TaskStateQueued
			t.TimeReceived = time.Now().Unix()
			m.SaveTaskResult(t)

			if t.IsConcurrent {
				go m.processTask(t)
			} else {
				m.processTask(t)
			}
		}
	}

	return nil
}

// Saves a task in etcd
func (m *etcdMinion) SaveTaskResult(t *task.Task) error {
	taskKey := filepath.Join(m.logDir, t.TaskID.String())

	data, err := json.Marshal(t)
	if err != nil {
		log.Printf("Failed to serialize task %s: %s\n", t.TaskID, err)
		return err
	}

	opts := &etcdclient.SetOptions{
		PrevExist: etcdclient.PrevIgnore,
	}

	_, err = m.kapi.Set(context.Background(), taskKey, string(data), opts)
	if err != nil {
		log.Printf("Failed to save task %s: %s\n", t.TaskID, err)
	}

	return err
}

// Main entry point of the minion
func (m *etcdMinion) Serve() error {
	err := m.SetName(m.name)
	if err != nil {
		return err
	}

	now := time.Now().Unix()
	err = m.SetLastseen(now)
	if err != nil {
		return err
	}

	// Start minion services
	go m.classify()
	go m.checkQueue()
	go m.periodicRunner()
	go m.TaskRunner(m.taskQueue)
	go m.TaskListener(m.taskQueue)

	log.Printf("Minion %s is ready to serve", m.ID())

	return nil
}

// Stops the minion and performs any cleanup tasks
func (m *etcdMinion) Stop() error {
	log.Println("Minion is shutting down")

	close(m.taskQueue)
	close(m.done)

	return nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"strings"
)

func resourceAwsAppautoscalingTarget() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAppautoscalingTargetPut,
		Read:   resourceAwsAppautoscalingTargetRead,
		Update: resourceAwsAppautoscalingTargetPut,
		Delete: resourceAwsAppautoscalingTargetDelete,
		Importer: &schema.ResourceImporter{
			State: resourceAwsAppautoscalingTargetImport,
		},

		Schema: map[string]*schema.Schema{
			"max_capacity": {
				Type:     schema.TypeInt,
				Required: true,
			},
			"min_capacity": {
				Type:     schema.TypeInt,
				Required: true,
			},
			"resource_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"scalable_dimension": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"service_namespace": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsAppautoscalingTargetPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).appautoscalingconn

	var targetOpts applicationautoscaling.RegisterScalableTargetInput

	targetOpts.MaxCapacity = aws.Int64(int64(d.Get("max_capacity").(int)))
	targetOpts.MinCapacity = aws.Int64(int64(d.Get("min_capacity").(int)))
	targetOpts.ResourceId = aws.String(d.Get("resource_id").(string))
	targetOpts.ScalableDimension = aws.String(d.Get("scalable_dimension").(string))
	targetOpts.ServiceNamespace = aws.String(d.Get("service_namespace").(string))

	if roleArn, exists := d.GetOk("role_arn"); exists {
		targetOpts.RoleARN = aws.String(roleArn.(string))
	}

	log.Printf("[DEBUG] Application autoscaling target create configuration %s", targetOpts)
	var err error
	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
		_, err = conn.RegisterScalableTarget(&targetOpts)

		if err != nil {
			if isAWSErr(err, "ValidationException", "Unable to assume IAM role") {
				log.Printf("[DEBUG] Retrying creation of Application Autoscaling Scalable Target due to possible issues with IAM: %s", err)
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}

		return nil
	})
	if isResourceTimeoutError(err) {
		_, err = conn.RegisterScalableTarget(&targetOpts)
	}

	if err != nil {
		return fmt.Errorf("Error creating application autoscaling target: %s", err)
	}

	d.SetId(d.Get("resource_id").(string))
	log.Printf("[INFO] Application AutoScaling Target ID: %s", d.Id())

	return resourceAwsAppautoscalingTargetRead(d, meta)
}

func resourceAwsAppautoscalingTargetRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).appautoscalingconn

	namespace := d.Get("service_namespace").(string)
	dimension := d.Get("scalable_dimension").(string)
	t, err := getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn)
	if err != nil {
		return err
	}
	if t == nil {
		log.Printf("[WARN] Application AutoScaling Target (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	d.Set("max_capacity", t.MaxCapacity)
	d.Set("min_capacity", t.MinCapacity)
	d.Set("resource_id", t.ResourceId)
	d.Set("role_arn", t.RoleARN)
	d.Set("scalable_dimension", t.ScalableDimension)
	d.Set("service_namespace", t.ServiceNamespace)

	return nil
}

func resourceAwsAppautoscalingTargetDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).appautoscalingconn

	namespace := d.Get("service_namespace").(string)
	dimension := d.Get("scalable_dimension").(string)

	t, err := getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn)
	if err != nil {
		return err
	}
	if t == nil {
		log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
		return nil
	}

	log.Printf("[DEBUG] Application AutoScaling Target destroy: %#v", d.Id())
	deleteOpts := applicationautoscaling.DeregisterScalableTargetInput{
		ResourceId:        aws.String(d.Get("resource_id").(string)),
		ServiceNamespace:  aws.String(d.Get("service_namespace").(string)),
		ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
	}

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		if _, err := conn.DeregisterScalableTarget(&deleteOpts); err != nil {
			if awserr, ok := err.(awserr.Error); ok {
				// @TODO: We should do stuff here depending on the actual error returned
				return resource.RetryableError(awserr)
			}
			// Non recognized error, no retry.
			return resource.NonRetryableError(err)
		}
		// Successful delete
		return nil
	})
	if err != nil {
		return err
	}

	return resource.Retry(5*time.Minute, func() *resource.RetryError {
		if t, _ = getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn); t != nil {
			return resource.RetryableError(
				fmt.Errorf("Application AutoScaling Target still exists"))
		}
		return nil
	})
}

func getAwsAppautoscalingTarget(resourceId, namespace, dimension string,
	conn *applicationautoscaling.ApplicationAutoScaling) (*applicationautoscaling.ScalableTarget, error) {

	describeOpts := applicationautoscaling.DescribeScalableTargetsInput{
		ResourceIds:      []*string{aws.String(resourceId)},
		ServiceNamespace: aws.String(namespace),
	}

	log.Printf("[DEBUG] Application AutoScaling Target describe configuration: %#v", describeOpts)
	describeTargets, err := conn.DescribeScalableTargets(&describeOpts)
	if err != nil {
		// @TODO: We should probably send something else back if we're trying to access an unknown Resource ID
		// targetserr, ok := err.(awserr.Error)
		// if ok && targetserr.Code() == ""
		return nil, fmt.Errorf("Error retrieving Application AutoScaling Target: %s", err)
	}

	for idx, tgt := range describeTargets.ScalableTargets {
		if *tgt.ResourceId == resourceId && *tgt.ScalableDimension == dimension {
			return describeTargets.ScalableTargets[idx], nil
		}
	}

	return nil, nil
}

func resourceAwsAppautoscalingTargetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	idParts := strings.Split(d.Id(), "/")

	if len(idParts) < 3 {
		return nil, fmt.Errorf("unexpected format (%q), expected <service-namespace>/<resource-id>/<scalable-dimension>", d.Id())
	}

	serviceNamespace := idParts[0]
	resourceId := strings.Join(idParts[1:len(idParts)-1], "/")
	scalableDimension := idParts[len(idParts)-1]

	if serviceNamespace == "" || resourceId == "" || scalableDimension == "" {
		return nil, fmt.Errorf("unexpected format (%q), expected <service-namespace>/<resource-id>/<scalable-dimension>", d.Id())
	}

	d.Set("service_namespace", serviceNamespace)
	d.Set("resource_id", resourceId)
	d.Set("scalable_dimension", scalableDimension)
	d.SetId(resourceId)

	return []*schema.ResourceData{d}, nil
}
<commit_msg>resource/aws_appautoscaling_target: Prevent state removal at creation (#11819)<commit_after>package aws

import (
	"fmt"
	"log"
	"time"

	"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
	"strings"
)

func resourceAwsAppautoscalingTarget() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAppautoscalingTargetPut,
		Read:   resourceAwsAppautoscalingTargetRead,
		Update: resourceAwsAppautoscalingTargetPut,
		Delete: resourceAwsAppautoscalingTargetDelete,
		Importer: &schema.ResourceImporter{
			State: resourceAwsAppautoscalingTargetImport,
		},

		Schema: map[string]*schema.Schema{
			"max_capacity": {
				Type:     schema.TypeInt,
				Required: true,
			},
			"min_capacity": {
				Type:     schema.TypeInt,
				Required: true,
			},
			"resource_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"role_arn": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"scalable_dimension": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"service_namespace": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsAppautoscalingTargetPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).appautoscalingconn

	var targetOpts applicationautoscaling.RegisterScalableTargetInput

	targetOpts.MaxCapacity = aws.Int64(int64(d.Get("max_capacity").(int)))
	targetOpts.MinCapacity = aws.Int64(int64(d.Get("min_capacity").(int)))
	targetOpts.ResourceId = aws.String(d.Get("resource_id").(string))
	targetOpts.ScalableDimension = aws.String(d.Get("scalable_dimension").(string))
	targetOpts.ServiceNamespace = aws.String(d.Get("service_namespace").(string))

	if roleArn, exists := d.GetOk("role_arn"); exists {
		targetOpts.RoleARN = aws.String(roleArn.(string))
	}

	log.Printf("[DEBUG] Application autoscaling target create configuration %s", targetOpts)
	var err error
	err = resource.Retry(1*time.Minute, func() *resource.RetryError {
		_, err = conn.RegisterScalableTarget(&targetOpts)

		if err != nil {
			if isAWSErr(err, "ValidationException", "Unable to assume IAM role") {
				log.Printf("[DEBUG] Retrying creation of Application Autoscaling Scalable Target due to possible issues with IAM: %s", err)
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}

		return nil
	})
	if isResourceTimeoutError(err) {
		_, err = conn.RegisterScalableTarget(&targetOpts)
	}

	if err != nil {
		return fmt.Errorf("Error creating application autoscaling target: %s", err)
	}

	d.SetId(d.Get("resource_id").(string))
	log.Printf("[INFO] Application AutoScaling Target ID: %s", d.Id())

	return resourceAwsAppautoscalingTargetRead(d, meta)
}

func resourceAwsAppautoscalingTargetRead(d *schema.ResourceData, meta interface{}) error {
	var t *applicationautoscaling.ScalableTarget

	conn := meta.(*AWSClient).appautoscalingconn

	namespace := d.Get("service_namespace").(string)
	dimension := d.Get("scalable_dimension").(string)

	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		var err error
		t, err = getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn)
		if err != nil {
			return resource.NonRetryableError(err)
		}
		if d.IsNewResource() && t == nil {
			return resource.RetryableError(&resource.NotFoundError{})
		}
		return nil
	})
	if isResourceTimeoutError(err) {
		t, err = getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn)
	}

	if err != nil {
		return err
	}
	if t == nil {
		log.Printf("[WARN] Application AutoScaling Target (%s) not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	d.Set("max_capacity", t.MaxCapacity)
	d.Set("min_capacity", t.MinCapacity)
	d.Set("resource_id", t.ResourceId)
	d.Set("role_arn", t.RoleARN)
	d.Set("scalable_dimension", t.ScalableDimension)
	d.Set("service_namespace", t.ServiceNamespace)

	return nil
}

func resourceAwsAppautoscalingTargetDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).appautoscalingconn

	namespace := d.Get("service_namespace").(string)
	dimension := d.Get("scalable_dimension").(string)

	t, err := getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn)
	if err != nil {
		return err
	}
	if t == nil {
		log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
		return nil
	}

	log.Printf("[DEBUG] Application AutoScaling Target destroy: %#v", d.Id())
	deleteOpts := applicationautoscaling.DeregisterScalableTargetInput{
		ResourceId:        aws.String(d.Get("resource_id").(string)),
		ServiceNamespace:  aws.String(d.Get("service_namespace").(string)),
		ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
	}

	err = resource.Retry(5*time.Minute, func() *resource.RetryError {
		if _, err := conn.DeregisterScalableTarget(&deleteOpts); err != nil {
			if awserr, ok := err.(awserr.Error); ok {
				// @TODO: We should do stuff here depending on the actual error returned
				return resource.RetryableError(awserr)
			}
			// Non recognized error, no retry.
			return resource.NonRetryableError(err)
		}
		// Successful delete
		return nil
	})
	if err != nil {
		return err
	}

	return resource.Retry(5*time.Minute, func() *resource.RetryError {
		if t, _ = getAwsAppautoscalingTarget(d.Id(), namespace, dimension, conn); t != nil {
			return resource.RetryableError(
				fmt.Errorf("Application AutoScaling Target still exists"))
		}
		return nil
	})
}

func getAwsAppautoscalingTarget(resourceId, namespace, dimension string,
	conn *applicationautoscaling.ApplicationAutoScaling) (*applicationautoscaling.ScalableTarget, error) {

	describeOpts := applicationautoscaling.DescribeScalableTargetsInput{
		ResourceIds:      []*string{aws.String(resourceId)},
		ServiceNamespace: aws.String(namespace),
	}

	log.Printf("[DEBUG] Application AutoScaling Target describe configuration: %#v", describeOpts)
	describeTargets, err := conn.DescribeScalableTargets(&describeOpts)
	if err != nil {
		// @TODO: We should probably send something else back if we're trying to access an unknown Resource ID
		// targetserr, ok := err.(awserr.Error)
		// if ok && targetserr.Code() == ""
		return nil, fmt.Errorf("Error retrieving Application AutoScaling Target: %s", err)
	}

	for idx, tgt := range describeTargets.ScalableTargets {
		if *tgt.ResourceId == resourceId && *tgt.ScalableDimension == dimension {
			return describeTargets.ScalableTargets[idx], nil
		}
	}

	return nil, nil
}

func resourceAwsAppautoscalingTargetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	idParts := strings.Split(d.Id(), "/")

	if len(idParts) < 3 {
		return nil, fmt.Errorf("unexpected format (%q), expected <service-namespace>/<resource-id>/<scalable-dimension>", d.Id())
	}

	serviceNamespace := idParts[0]
	resourceId := strings.Join(idParts[1:len(idParts)-1], "/")
	scalableDimension := idParts[len(idParts)-1]

	if serviceNamespace == "" || resourceId == "" || scalableDimension == "" {
		return nil, fmt.Errorf("unexpected format (%q), expected <service-namespace>/<resource-id>/<scalable-dimension>", d.Id())
	}

	d.Set("service_namespace", serviceNamespace)
	d.Set("resource_id", resourceId)
	d.Set("scalable_dimension", scalableDimension)
	d.SetId(resourceId)

	return []*schema.ResourceData{d}, nil
}
<|endoftext|>


<commit_before>// A web app for Google App Engine that proxies HTTP requests and responses to a
// Tor relay running meek-server.
package reflect

import (
	"io"
	"net/http"
	"net/url"
	"time"

	"appengine"
	"appengine/urlfetch"
)

const (
	forwardURL = "http://meek.bamsoftware.com:7002/"
	// A timeout of 0 means to use the App Engine default (5 seconds).
	urlFetchTimeout = 10 * time.Second
)

var context appengine.Context

// Join two URL paths.
func pathJoin(a, b string) string {
	if len(a) > 0 && a[len(a)-1] == '/' {
		a = a[:len(a)-1]
	}
	if len(b) == 0 || b[0] != '/' {
		b = "/" + b
	}
	return a + b
}

// We reflect only a whitelisted set of header fields. In requests, the full
// list includes things like User-Agent and X-Appengine-Country that the Tor
// bridge doesn't need to know. In responses, there may be things like
// Transfer-Encoding that interfere with App Engine's own hop-by-hop headers.
var reflectedHeaderFields = []string{
	"Content-Type",
	"X-Session-Id",
}

// Make a copy of r, with the URL being changed to be relative to forwardURL,
// and including only the headers in reflectedHeaderFields.
func copyRequest(r *http.Request) (*http.Request, error) {
	u, err := url.Parse(forwardURL)
	if err != nil {
		return nil, err
	}
	// Append the requested path to the path in forwardURL, so that
	// forwardURL can be something like "http://example.com/reflect".
	u.Path = pathJoin(u.Path, r.URL.Path)
	c, err := http.NewRequest(r.Method, u.String(), r.Body)
	if err != nil {
		return nil, err
	}
	for _, key := range reflectedHeaderFields {
		value := r.Header.Get(key)
		if value != "" {
			c.Header.Add(key, value)
		}
	}
	return c, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	context = appengine.NewContext(r)
	fr, err := copyRequest(r)
	if err != nil {
		context.Errorf("copyRequest: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// Use urlfetch.Transport directly instead of urlfetch.Client because we
	// want only a single HTTP transaction, not following redirects.
	transport := urlfetch.Transport{
		Context: context,
		// Despite the name, Transport.Deadline is really a timeout and
		// not an absolute deadline as used in the net package. In
		// other words it is a time.Duration, not a time.Time.
		Deadline: urlFetchTimeout,
	}
	resp, err := transport.RoundTrip(fr)
	if err != nil {
		context.Errorf("RoundTrip: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	for _, key := range reflectedHeaderFields {
		value := resp.Header.Get(key)
		if value != "" {
			w.Header().Add(key, value)
		}
	}
	w.WriteHeader(resp.StatusCode)
	n, err := io.Copy(w, resp.Body)
	if err != nil {
		context.Errorf("io.Copy after %d bytes: %s", n, err)
	}
}

func init() {
	http.HandleFunc("/", handler)
}
<commit_msg>Increase the urlfetch timeout from 10 s to 20 s.<commit_after>// A web app for Google App Engine that proxies HTTP requests and responses to a
// Tor relay running meek-server.
package reflect

import (
	"io"
	"net/http"
	"net/url"
	"time"

	"appengine"
	"appengine/urlfetch"
)

const (
	forwardURL = "http://meek.bamsoftware.com:7002/"
	// A timeout of 0 means to use the App Engine default (5 seconds).
	urlFetchTimeout = 20 * time.Second
)

var context appengine.Context

// Join two URL paths.
func pathJoin(a, b string) string {
	if len(a) > 0 && a[len(a)-1] == '/' {
		a = a[:len(a)-1]
	}
	if len(b) == 0 || b[0] != '/' {
		b = "/" + b
	}
	return a + b
}

// We reflect only a whitelisted set of header fields. In requests, the full
// list includes things like User-Agent and X-Appengine-Country that the Tor
// bridge doesn't need to know. In responses, there may be things like
// Transfer-Encoding that interfere with App Engine's own hop-by-hop headers.
var reflectedHeaderFields = []string{
	"Content-Type",
	"X-Session-Id",
}

// Make a copy of r, with the URL being changed to be relative to forwardURL,
// and including only the headers in reflectedHeaderFields.
func copyRequest(r *http.Request) (*http.Request, error) {
	u, err := url.Parse(forwardURL)
	if err != nil {
		return nil, err
	}
	// Append the requested path to the path in forwardURL, so that
	// forwardURL can be something like "http://example.com/reflect".
	u.Path = pathJoin(u.Path, r.URL.Path)
	c, err := http.NewRequest(r.Method, u.String(), r.Body)
	if err != nil {
		return nil, err
	}
	for _, key := range reflectedHeaderFields {
		value := r.Header.Get(key)
		if value != "" {
			c.Header.Add(key, value)
		}
	}
	return c, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
	context = appengine.NewContext(r)
	fr, err := copyRequest(r)
	if err != nil {
		context.Errorf("copyRequest: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// Use urlfetch.Transport directly instead of urlfetch.Client because we
	// want only a single HTTP transaction, not following redirects.
	transport := urlfetch.Transport{
		Context: context,
		// Despite the name, Transport.Deadline is really a timeout and
		// not an absolute deadline as used in the net package. In
		// other words it is a time.Duration, not a time.Time.
		Deadline: urlFetchTimeout,
	}
	resp, err := transport.RoundTrip(fr)
	if err != nil {
		context.Errorf("RoundTrip: %s", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()
	for _, key := range reflectedHeaderFields {
		value := resp.Header.Get(key)
		if value != "" {
			w.Header().Add(key, value)
		}
	}
	w.WriteHeader(resp.StatusCode)
	n, err := io.Copy(w, resp.Body)
	if err != nil {
		context.Errorf("io.Copy after %d bytes: %s", n, err)
	}
}

func init() {
	http.HandleFunc("/", handler)
}
<|endoftext|>