<commit_before>package simulation

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"image"
	"log"
)

const MaxCharge = 6

type Circuit struct {
	wires       []*Wire
	transistors []*Transistor
}

func (c *Circuit) Wires() []*Wire {
	return c.wires
}

type WireState struct {
	charge uint8
	wire   *Wire
}

func (w WireState) Charge() uint8 {
	return w.charge
}

func (w WireState) Wire() *Wire {
	return w.wire
}

type Simulation struct {
	circuit *Circuit
	states  []WireState
}

func (s *Simulation) Circuit() *Circuit {
	return s.circuit
}

func (s *Simulation) State(wire *Wire) WireState {
	return s.states[wire.index]
}

func New(img *image.Paletted) *Simulation {
	size := img.Bounds().Size()
	groups := make(map[*group]struct{}, 0)
	matrix := newBucketMatrix(size.X, size.Y)
	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			charge := img.ColorIndexAt(x, y) - 1
			if charge > MaxCharge {
				continue
			}
			topLeftBucket := matrix.get(x-1, y-1)
			topBucket := matrix.get(x, y-1)
			leftBucket := matrix.get(x-1, y)
			var currentBucket *bucket
			switch {
			case nil == topBucket && nil == leftBucket:
				currentBucket = newBucket()
				groups[currentBucket.group] = struct{}{}
			case nil == topBucket && nil != leftBucket:
				currentBucket = leftBucket
			case (nil != topBucket && nil == leftBucket) ||
				topBucket == leftBucket ||
				topBucket.group == leftBucket.group:
				currentBucket = topBucket
			default:
				currentBucket = topBucket
				delete(groups, topBucket.group)
				topBucket.group.moveContentTo(leftBucket.group)
			}
			if nil != topLeftBucket && nil != topBucket && nil != leftBucket {
				currentBucket.group.wire.isPowerSource = true
			}
			matrix.set(x, y, currentBucket)
			if charge > currentBucket.group.wireState.charge {
				currentBucket.group.wireState.charge = charge
			}
			currentBucket.addPixel(image.Point{x, y})
		}
	}

	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			if nil != matrix.get(x, y) {
				continue
			}
			topBucket := matrix.get(x, y-1)
			topRightBucket := matrix.get(x+1, y-1)
			rightBucket := matrix.get(x+1, y)
			bottomRightBucket := matrix.get(x+1, y+1)
			bottomBucket := matrix.get(x, y+1)
			bottomLeftBucket := matrix.get(x-1, y+1)
			leftBucket := matrix.get(x-1, y)
			topLeftBucket := matrix.get(x-1, y-1)
			if nil == topLeftBucket && nil == topRightBucket && nil == bottomLeftBucket && nil == bottomRightBucket &&
				nil != topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket {
				if topBucket.group != bottomBucket.group {
					delete(groups, topBucket.group)
					topBucket.group.moveContentTo(bottomBucket.group)
				}
				if rightBucket.group != leftBucket.group {
					delete(groups, rightBucket.group)
					rightBucket.group.moveContentTo(leftBucket.group)
				}
			}
		}
	}

	transistors := make([]*Transistor, 0)
	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			if nil != matrix.get(x, y) {
				continue
			}
			topBucket := matrix.get(x, y-1)
			topRightBucket := matrix.get(x+1, y-1)
			rightBucket := matrix.get(x+1, y)
			bottomRightBucket := matrix.get(x+1, y+1)
			bottomBucket := matrix.get(x, y+1)
			bottomLeftBucket := matrix.get(x-1, y+1)
			leftBucket := matrix.get(x-1, y)
			topLeftBucket := matrix.get(x-1, y-1)

			switch {
			case nil == bottomLeftBucket && nil == bottomRightBucket &&
				nil == topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, bottomBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire))
			case nil == bottomLeftBucket && nil == topLeftBucket &&
				nil != topBucket && nil == rightBucket && nil != bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, leftBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire))
			case nil == topLeftBucket && nil == topRightBucket &&
				nil != topBucket && nil != rightBucket && nil == bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, topBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire))
			case nil == bottomRightBucket && nil == topRightBucket &&
				nil != topBucket && nil != rightBucket && nil != bottomBucket && nil == leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, rightBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire))
			}
		}
	}

	wires := make([]*Wire, len(groups))
	wireStates := make([]WireState, len(groups))
	i := 0
	for k := range groups {
		k.wire.index = i
		wires[i] = k.wire
		wireStates[i] = k.wireState
		i++
	}

	return &Simulation{&Circuit{wires: wires, transistors: transistors}, wireStates}
}

func (s *Simulation) Step() *Simulation {
	newWireState := make([]WireState, len(s.states))
	for i, state := range s.states {
		charge := state.charge
		if state.wire.isPowerSource {
			if state.charge < MaxCharge {
				charge = state.charge + 1
			}
		} else {
			source := s.tracePowerSource(state)
			if source.charge > state.charge+1 {
				charge = state.charge + 1
			} else if source.charge <= state.charge && state.charge > 0 {
				charge = state.charge - 1
			}
		}
		newWireState[i] = WireState{charge, state.wire}
	}
	return &Simulation{s.circuit, newWireState}
}

func (s *Simulation) tracePowerSource(origin WireState) WireState {
	result := origin
	for _, transistor := range origin.wire.transistors {
		if nil != transistor.base && s.states[transistor.base.index].charge > 0 {
			continue
		}
		if origin.wire == transistor.inputA {
			inputBState := s.states[transistor.inputB.index]
			if inputBState.charge == MaxCharge {
				return inputBState
			}
			if inputBState.charge > result.charge {
				result = inputBState
				continue
			}
		} else if origin.wire == transistor.inputB {
			inputAState := s.states[transistor.inputA.index]
			if inputAState.charge == MaxCharge {
				return inputAState
			}
			if inputAState.charge > result.charge {
				result = inputAState
				continue
			}
		}
	}
	return result
}

func (s *Simulation) DiffDraw(previousSimulation *Simulation, img *image.Paletted) {
	for i, state := range s.states {
		if previousSimulation.states[i].charge == state.charge {
			continue
		}
		state.wire.draw(img, state.charge+1)
	}
}

func (s *Simulation) Draw(img *image.Paletted) {
	for _, state := range s.states {
		state.wire.draw(img, state.charge+1)
	}
	for _, transistor := range s.circuit.transistors {
		transistor.draw(img, MaxCharge+2)
	}
}

func (s *Simulation) DrawAll(initialImage *image.Paletted, frameCount int) []*image.Paletted {
	bounds := initialImage.Bounds()
	images := make([]*image.Paletted, frameCount)
	s.Draw(initialImage)
	images[0] = initialImage
	for f := 1; f < frameCount; f++ {
		newSimulation := s.Step()
		img := image.NewPaletted(bounds, initialImage.Palette)
		newSimulation.DiffDraw(s, img)
		images[f] = img
		s = newSimulation
	}
	return images
}

func (s *Simulation) FindLooping() (*Simulation, int) {
	hashs := make(map[[sha1.Size]byte]int, 0)
	frame := 0
	for {
		s = s.Step()
		var hash [sha1.Size]byte
		copy(hash[:], s.Hash())
		if f, ok := hashs[hash]; ok {
			return s, frame - f
		}
		hashs[hash] = frame
		frame++
	}
}

func (s *Simulation) Hash() []byte {
	hash := sha1.New()

	for index, state := range s.states {
		buf := new(bytes.Buffer)

		err := binary.Write(buf, binary.LittleEndian, uint32(index))
		if err != nil {
			log.Fatal(err)
		}
		err = binary.Write(buf, binary.LittleEndian, state.charge)
		if err != nil {
			log.Fatal(err)
		}
		_, err = hash.Write(buf.Bytes())
		if err != nil {
			log.Fatal(err)
		}
	}
	return hash.Sum(nil)
}

type Transistor struct {
	position image.Point
	base     *Wire
	inputA   *Wire
	inputB   *Wire
}

func (t *Transistor) Position() image.Point {
	return t.position
}

func (t *Transistor) Base() *Wire {
	return t.base
}

func (t *Transistor) InputA() *Wire {
	return t.inputA
}

func (t *Transistor) InputB() *Wire {
	return t.inputB
}

func newTransistor(position image.Point, base, inputA, inputB *Wire) *Transistor {
	transistor := &Transistor{
		position: position,
		base:     base,
		inputA:   inputA,
		inputB:   inputB,
	}
	inputA.transistors = append(inputA.transistors, transistor)
	inputB.transistors = append(inputB.transistors, transistor)
	return transistor
}

func (t *Transistor) draw(img *image.Paletted, colorIndex uint8) {
	img.SetColorIndex(t.position.X, t.position.Y, colorIndex)
}

type Wire struct {
	index         int
	pixels        []image.Point
	bounds        image.Rectangle
	transistors   []*Transistor
	isPowerSource bool
}

func (w *Wire) Pixels() []image.Point {
	return w.pixels
}

func (w *Wire) Bounds() image.Rectangle {
	return w.bounds
}

func (w *Wire) Transistors() []*Transistor {
	return w.transistors
}

func (w *Wire) IsPowerSource() bool {
	return w.isPowerSource
}

func newWire() *Wire {
	return &Wire{
		index:         -1,
		pixels:        make([]image.Point, 0),
		bounds:        image.Rectangle{image.Pt(0, 0), image.Pt(0, 0)},
		transistors:   make([]*Transistor, 0),
		isPowerSource: false,
	}
}

func (w *Wire) draw(img *image.Paletted, colorIndex uint8) {
	for _, pixel := range w.pixels {
		img.SetColorIndex(pixel.X, pixel.Y, colorIndex)
	}
}

type bucketMatrix struct {
	buckets [][]*bucket
	width   int
	height  int
}

func newBucketMatrix(width int, height int) *bucketMatrix {
	m := &bucketMatrix{make([][]*bucket, height), width, height}
	for y := 0; y < height; y++ {
		m.buckets[y] = make([]*bucket, width)
	}
	return m
}

func (m *bucketMatrix) get(x int, y int) *bucket {
	if x < 0 || y < 0 || x >= m.width || y >= m.height {
		return nil
	}
	return m.buckets[y][x]
}

func (m *bucketMatrix) set(x int, y int, bucket *bucket) {
	m.buckets[y][x] = bucket
}

type bucket struct {
	group *group
}

func newBucket() *bucket {

	newBucket := &bucket{nil}
	newGroup := &group{
		buckets: []*bucket{newBucket},
		wire:    newWire(),
	}
	newGroup.wireState = WireState{wire: newGroup.wire, charge: 0}
	newBucket.group = newGroup
	return newBucket
}

func (b *bucket) addPixel(pixel image.Point) {
	b.group.wire.pixels = append(b.group.wire.pixels, pixel)
	b.group.wire.bounds = b.group.wire.bounds.Union(
		image.Rectangle{
			pixel,
			pixel.Add(image.Point{1, 1})})
}

type group struct {
	buckets   []*bucket
	wire      *Wire
	wireState WireState
}

func (g *group) moveContentTo(other *group) {
	if g == other {
		log.Fatal("A group can not be moved to itself.")
	}
	for _, bucket := range g.buckets {
		bucket.group = other
		other.buckets = append(other.buckets, bucket)
	}
	if g.wire.isPowerSource {
		other.wire.isPowerSource = true
	}
	if g.wireState.charge > other.wireState.charge {
		other.wireState.charge = g.wireState.charge
	}
	other.wire.bounds = other.wire.bounds.Union(g.wire.bounds)
	other.wire.pixels = append(other.wire.pixels, g.wire.pixels...)
}
<commit_msg>removed transistor debug draw<commit_after>package simulation

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"image"
	"log"
)

const MaxCharge = 6

type Circuit struct {
	wires       []*Wire
	transistors []*Transistor
}

func (c *Circuit) Wires() []*Wire {
	return c.wires
}

type WireState struct {
	charge uint8
	wire   *Wire
}

func (w WireState) Charge() uint8 {
	return w.charge
}

func (w WireState) Wire() *Wire {
	return w.wire
}

type Simulation struct {
	circuit *Circuit
	states  []WireState
}

func (s *Simulation) Circuit() *Circuit {
	return s.circuit
}

func (s *Simulation) State(wire *Wire) WireState {
	return s.states[wire.index]
}

func New(img *image.Paletted) *Simulation {
	size := img.Bounds().Size()
	groups := make(map[*group]struct{}, 0)
	matrix := newBucketMatrix(size.X, size.Y)
	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			charge := img.ColorIndexAt(x, y) - 1
			if charge > MaxCharge {
				continue
			}
			topLeftBucket := matrix.get(x-1, y-1)
			topBucket := matrix.get(x, y-1)
			leftBucket := matrix.get(x-1, y)
			var currentBucket *bucket
			switch {
			case nil == topBucket && nil == leftBucket:
				currentBucket = newBucket()
				groups[currentBucket.group] = struct{}{}
			case nil == topBucket && nil != leftBucket:
				currentBucket = leftBucket
			case (nil != topBucket && nil == leftBucket) ||
				topBucket == leftBucket ||
				topBucket.group == leftBucket.group:
				currentBucket = topBucket
			default:
				currentBucket = topBucket
				delete(groups, topBucket.group)
				topBucket.group.moveContentTo(leftBucket.group)
			}
			if nil != topLeftBucket && nil != topBucket && nil != leftBucket {
				currentBucket.group.wire.isPowerSource = true
			}
			matrix.set(x, y, currentBucket)
			if charge > currentBucket.group.wireState.charge {
				currentBucket.group.wireState.charge = charge
			}
			currentBucket.addPixel(image.Point{x, y})
		}
	}

	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			if nil != matrix.get(x, y) {
				continue
			}
			topBucket := matrix.get(x, y-1)
			topRightBucket := matrix.get(x+1, y-1)
			rightBucket := matrix.get(x+1, y)
			bottomRightBucket := matrix.get(x+1, y+1)
			bottomBucket := matrix.get(x, y+1)
			bottomLeftBucket := matrix.get(x-1, y+1)
			leftBucket := matrix.get(x-1, y)
			topLeftBucket := matrix.get(x-1, y-1)
			if nil == topLeftBucket && nil == topRightBucket && nil == bottomLeftBucket && nil == bottomRightBucket &&
				nil != topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket {
				if topBucket.group != bottomBucket.group {
					delete(groups, topBucket.group)
					topBucket.group.moveContentTo(bottomBucket.group)
				}
				if rightBucket.group != leftBucket.group {
					delete(groups, rightBucket.group)
					rightBucket.group.moveContentTo(leftBucket.group)
				}
			}
		}
	}

	transistors := make([]*Transistor, 0)
	for y := 0; y < size.Y; y++ {
		for x := 0; x < size.X; x++ {
			if nil != matrix.get(x, y) {
				continue
			}
			topBucket := matrix.get(x, y-1)
			topRightBucket := matrix.get(x+1, y-1)
			rightBucket := matrix.get(x+1, y)
			bottomRightBucket := matrix.get(x+1, y+1)
			bottomBucket := matrix.get(x, y+1)
			bottomLeftBucket := matrix.get(x-1, y+1)
			leftBucket := matrix.get(x-1, y)
			topLeftBucket := matrix.get(x-1, y-1)

			switch {
			case nil == bottomLeftBucket && nil == bottomRightBucket &&
				nil == topBucket && nil != rightBucket && nil != bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, bottomBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire))
			case nil == bottomLeftBucket && nil == topLeftBucket &&
				nil != topBucket && nil == rightBucket && nil != bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, leftBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire))
			case nil == topLeftBucket && nil == topRightBucket &&
				nil != topBucket && nil != rightBucket && nil == bottomBucket && nil != leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, topBucket.group.wire, rightBucket.group.wire, leftBucket.group.wire))
			case nil == bottomRightBucket && nil == topRightBucket &&
				nil != topBucket && nil != rightBucket && nil != bottomBucket && nil == leftBucket:
				transistors = append(transistors,
					newTransistor(image.Point{x, y}, rightBucket.group.wire, topBucket.group.wire, bottomBucket.group.wire))
			}
		}
	}

	wires := make([]*Wire, len(groups))
	wireStates := make([]WireState, len(groups))
	i := 0
	for k := range groups {
		k.wire.index = i
		wires[i] = k.wire
		wireStates[i] = k.wireState
		i++
	}

	return &Simulation{&Circuit{wires: wires, transistors: transistors}, wireStates}
}

func (s *Simulation) Step() *Simulation {
	newWireState := make([]WireState, len(s.states))
	for i, state := range s.states {
		charge := state.charge
		if state.wire.isPowerSource {
			if state.charge < MaxCharge {
				charge = state.charge + 1
			}
		} else {
			source := s.tracePowerSource(state)
			if source.charge > state.charge+1 {
				charge = state.charge + 1
			} else if source.charge <= state.charge && state.charge > 0 {
				charge = state.charge - 1
			}
		}
		newWireState[i] = WireState{charge, state.wire}
	}
	return &Simulation{s.circuit, newWireState}
}

func (s *Simulation) tracePowerSource(origin WireState) WireState {
	result := origin
	for _, transistor := range origin.wire.transistors {
		if nil != transistor.base && s.states[transistor.base.index].charge > 0 {
			continue
		}
		if origin.wire == transistor.inputA {
			inputBState := s.states[transistor.inputB.index]
			if inputBState.charge == MaxCharge {
				return inputBState
			}
			if inputBState.charge > result.charge {
				result = inputBState
				continue
			}
		} else if origin.wire == transistor.inputB {
			inputAState := s.states[transistor.inputA.index]
			if inputAState.charge == MaxCharge {
				return inputAState
			}
			if inputAState.charge > result.charge {
				result = inputAState
				continue
			}
		}
	}
	return result
}

func (s *Simulation) DiffDraw(previousSimulation *Simulation, img *image.Paletted) {
	for i, state := range s.states {
		if previousSimulation.states[i].charge == state.charge {
			continue
		}
		state.wire.draw(img, state.charge+1)
	}
}

func (s *Simulation) Draw(img *image.Paletted) {
	for _, state := range s.states {
		state.wire.draw(img, state.charge+1)
	}
}

func (s *Simulation) DrawAll(initialImage *image.Paletted, frameCount int) []*image.Paletted {
	bounds := initialImage.Bounds()
	images := make([]*image.Paletted, frameCount)
	s.Draw(initialImage)
	images[0] = initialImage
	for f := 1; f < frameCount; f++ {
		newSimulation := s.Step()
		img := image.NewPaletted(bounds, initialImage.Palette)
		newSimulation.DiffDraw(s, img)
		images[f] = img
		s = newSimulation
	}
	return images
}

func (s *Simulation) FindLooping() (*Simulation, int) {
	hashs := make(map[[sha1.Size]byte]int, 0)
	frame := 0
	for {
		s = s.Step()
		var hash [sha1.Size]byte
		copy(hash[:], s.Hash())
		if f, ok := hashs[hash]; ok {
			return s, frame - f
		}
		hashs[hash] = frame
		frame++
	}
}

func (s *Simulation) Hash() []byte {
	hash := sha1.New()

	for index, state := range s.states {
		buf := new(bytes.Buffer)

		err := binary.Write(buf, binary.LittleEndian, uint32(index))
		if err != nil {
			log.Fatal(err)
		}
		err = binary.Write(buf, binary.LittleEndian, state.charge)
		if err != nil {
			log.Fatal(err)
		}
		_, err = hash.Write(buf.Bytes())
		if err != nil {
			log.Fatal(err)
		}
	}
	return hash.Sum(nil)
}

type Transistor struct {
	position image.Point
	base     *Wire
	inputA   *Wire
	inputB   *Wire
}

func (t *Transistor) Position() image.Point {
	return t.position
}

func (t *Transistor) Base() *Wire {
	return t.base
}

func (t *Transistor) InputA() *Wire {
	return t.inputA
}

func (t *Transistor) InputB() *Wire {
	return t.inputB
}

func newTransistor(position image.Point, base, inputA, inputB *Wire) *Transistor {
	transistor := &Transistor{
		position: position,
		base:     base,
		inputA:   inputA,
		inputB:   inputB,
	}
	inputA.transistors = append(inputA.transistors, transistor)
	inputB.transistors = append(inputB.transistors, transistor)
	return transistor
}

type Wire struct {
	index         int
	pixels        []image.Point
	bounds        image.Rectangle
	transistors   []*Transistor
	isPowerSource bool
}

func (w *Wire) Pixels() []image.Point {
	return w.pixels
}

func (w *Wire) Bounds() image.Rectangle {
	return w.bounds
}

func (w *Wire) Transistors() []*Transistor {
	return w.transistors
}

func (w *Wire) IsPowerSource() bool {
	return w.isPowerSource
}

func newWire() *Wire {
	return &Wire{
		index:         -1,
		pixels:        make([]image.Point, 0),
		bounds:        image.Rectangle{image.Pt(0, 0), image.Pt(0, 0)},
		transistors:   make([]*Transistor, 0),
		isPowerSource: false,
	}
}

func (w *Wire) draw(img *image.Paletted, colorIndex uint8) {
	for _, pixel := range w.pixels {
		img.SetColorIndex(pixel.X, pixel.Y, colorIndex)
	}
}

type bucketMatrix struct {
	buckets [][]*bucket
	width   int
	height  int
}

func newBucketMatrix(width int, height int) *bucketMatrix {
	m := &bucketMatrix{make([][]*bucket, height), width, height}
	for y := 0; y < height; y++ {
		m.buckets[y] = make([]*bucket, width)
	}
	return m
}

func (m *bucketMatrix) get(x int, y int) *bucket {
	if x < 0 || y < 0 || x >= m.width || y >= m.height {
		return nil
	}
	return m.buckets[y][x]
}

func (m *bucketMatrix) set(x int, y int, bucket *bucket) {
	m.buckets[y][x] = bucket
}

type bucket struct {
	group *group
}

func newBucket() *bucket {

	newBucket := &bucket{nil}
	newGroup := &group{
		buckets: []*bucket{newBucket},
		wire:    newWire(),
	}
	newGroup.wireState = WireState{wire: newGroup.wire, charge: 0}
	newBucket.group = newGroup
	return newBucket
}

func (b *bucket) addPixel(pixel image.Point) {
	b.group.wire.pixels = append(b.group.wire.pixels, pixel)
	b.group.wire.bounds = b.group.wire.bounds.Union(
		image.Rectangle{
			pixel,
			pixel.Add(image.Point{1, 1})})
}

type group struct {
	buckets   []*bucket
	wire      *Wire
	wireState WireState
}

func (g *group) moveContentTo(other *group) {
	if g == other {
		log.Fatal("A group can not be moved to itself.")
	}
	for _, bucket := range g.buckets {
		bucket.group = other
		other.buckets = append(other.buckets, bucket)
	}
	if g.wire.isPowerSource {
		other.wire.isPowerSource = true
	}
	if g.wireState.charge > other.wireState.charge {
		other.wireState.charge = g.wireState.charge
	}
	other.wire.bounds = other.wire.bounds.Union(g.wire.bounds)
	other.wire.pixels = append(other.wire.pixels, g.wire.pixels...)
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

//Handlers for V1

package handlers

import (
	"encoding/json"
	"github.com/aws/amazon-ecs-agent/agent/api"
	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	"github.com/aws/amazon-ecs-agent/agent/engine/dockerstate"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"net/http"
	"strconv"
	"time"
)

var log = logger.ForModule("Handlers")

const statusBadRequest = 400
const statusNotImplemented = 501
const statusOK = 200

type RootResponse struct {
	AvailableCommands []string
}

func MetadataV1RequestHandlerMaker(containerInstanceArn *string, cfg *config.Config) func(http.ResponseWriter, *http.Request) {
	resp := &MetadataResponse{
		ClusterArn:           cfg.ClusterArn,
		ContainerInstanceArn: containerInstanceArn,
	}
	responseJSON, _ := json.Marshal(resp)

	return func(w http.ResponseWriter, r *http.Request) {
		w.Write(responseJSON)
	}
}

func NewTaskResponse(task *api.Task, containerMap map[string]*api.DockerContainer) *TaskResponse {
	containers := make([]ContainerResponse, len(containerMap))
	ndx := 0
	for containerName, container := range containerMap {
		containers[ndx] = ContainerResponse{container.DockerId, container.DockerName, containerName}
		ndx++
	}

	return &TaskResponse{
		Arn:           task.Arn,
		DesiredStatus: task.DesiredStatus.String(),
		KnownStatus:   task.KnownStatus.String(),
		Family:        task.Family,
		Version:       task.Version,
		Containers:    containers,
	}
}

func NewTasksResponse(state *dockerstate.DockerTaskEngineState) *TasksResponse {
	allTasks := state.AllTasks()
	taskResponses := make([]*TaskResponse, len(allTasks))
	for ndx, task := range allTasks {
		containerMap, _ := state.ContainerMapByArn(task.Arn)
		taskResponses[ndx] = NewTaskResponse(task, containerMap)
	}

	return &TasksResponse{Tasks: taskResponses}
}

func TasksV1RequestHandlerMaker(taskEngine engine.TaskEngine) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var tasksResponse *TasksResponse
		dockerTaskEngine, ok := taskEngine.(*engine.DockerTaskEngine)
		if ok != true {
			log.Warn("Unable to get DockerTaskEngine from TaskEngine")
			tasksResponse = &TasksResponse{}
		} else {
			tasksResponse = NewTasksResponse(dockerTaskEngine.State())
		}
		responseJSON, _ := json.Marshal(tasksResponse)
		w.Write(responseJSON)
	}
}

func ServeHttp(containerInstanceArn *string, taskEngine engine.TaskEngine, cfg *config.Config) {
	serverFunctions := map[string]func(w http.ResponseWriter, r *http.Request){
		"/v1/metadata": MetadataV1RequestHandlerMaker(containerInstanceArn, cfg),
		"/v1/tasks":    TasksV1RequestHandlerMaker(taskEngine),
	}

	paths := make([]string, 0, len(serverFunctions))
	for path := range serverFunctions {
		paths = append(paths, path)
	}
	availableCommands := &RootResponse{paths}
	// Autogenerated list of the above serverFunctions paths
	availableCommandResponse, _ := json.Marshal(&availableCommands)

	defaultHandler := func(w http.ResponseWriter, r *http.Request) {
		w.Write(availableCommandResponse)
	}

	serverMux := http.NewServeMux()
	serverMux.HandleFunc("/", defaultHandler)
	for key, fn := range serverFunctions {
		serverMux.HandleFunc(key, fn)
	}

	// Log all requests and then pass through to serverMux
	loggingServeMux := http.NewServeMux()
	loggingServeMux.Handle("/", LoggingHandler{serverMux})

	server := http.Server{
		Addr:         ":" + strconv.Itoa(config.AGENT_INTROSPECTION_PORT),
		Handler:      loggingServeMux,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}

	for {
		err := server.ListenAndServe()
		log.Error("Error running http api", "err", err)
	}
}
<commit_msg>Add error code to response for /tasks api.<commit_after>// Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

//Handlers for V1

package handlers

import (
	"encoding/json"
	"github.com/aws/amazon-ecs-agent/agent/api"
	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	"github.com/aws/amazon-ecs-agent/agent/engine/dockerstate"
	"github.com/aws/amazon-ecs-agent/agent/logger"
	"net/http"
	"strconv"
	"time"
)

var log = logger.ForModule("Handlers")

const statusBadRequest = 400
const statusNotImplemented = 501
const statusOK = 200
const statusInternalServerError = 500

type RootResponse struct {
	AvailableCommands []string
}

func MetadataV1RequestHandlerMaker(containerInstanceArn *string, cfg *config.Config) func(http.ResponseWriter, *http.Request) {
	resp := &MetadataResponse{
		ClusterArn:           cfg.ClusterArn,
		ContainerInstanceArn: containerInstanceArn,
	}
	responseJSON, _ := json.Marshal(resp)

	return func(w http.ResponseWriter, r *http.Request) {
		w.Write(responseJSON)
	}
}

func NewTaskResponse(task *api.Task, containerMap map[string]*api.DockerContainer) *TaskResponse {
	containers := make([]ContainerResponse, len(containerMap))
	ndx := 0
	for containerName, container := range containerMap {
		containers[ndx] = ContainerResponse{container.DockerId, container.DockerName, containerName}
		ndx++
	}

	return &TaskResponse{
		Arn:           task.Arn,
		DesiredStatus: task.DesiredStatus.String(),
		KnownStatus:   task.KnownStatus.String(),
		Family:        task.Family,
		Version:       task.Version,
		Containers:    containers,
	}
}

func NewTasksResponse(state *dockerstate.DockerTaskEngineState) *TasksResponse {
	allTasks := state.AllTasks()
	taskResponses := make([]*TaskResponse, len(allTasks))
	for ndx, task := range allTasks {
		containerMap, _ := state.ContainerMapByArn(task.Arn)
		taskResponses[ndx] = NewTaskResponse(task, containerMap)
	}

	return &TasksResponse{Tasks: taskResponses}
}

func TasksV1RequestHandlerMaker(taskEngine engine.TaskEngine) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		var tasksResponse *TasksResponse
		dockerTaskEngine, ok := taskEngine.(*engine.DockerTaskEngine)
		if ok {
			tasksResponse = NewTasksResponse(dockerTaskEngine.State())
		} else {
			log.Warn("Unable to get DockerTaskEngine from TaskEngine")
			w.WriteHeader(statusInternalServerError)
			tasksResponse = &TasksResponse{}
		}
		responseJSON, _ := json.Marshal(tasksResponse)
		w.Write(responseJSON)
	}
}

func ServeHttp(containerInstanceArn *string, taskEngine engine.TaskEngine, cfg *config.Config) {
	serverFunctions := map[string]func(w http.ResponseWriter, r *http.Request){
		"/v1/metadata": MetadataV1RequestHandlerMaker(containerInstanceArn, cfg),
		"/v1/tasks":    TasksV1RequestHandlerMaker(taskEngine),
	}

	paths := make([]string, 0, len(serverFunctions))
	for path := range serverFunctions {
		paths = append(paths, path)
	}
	availableCommands := &RootResponse{paths}
	// Autogenerated list of the above serverFunctions paths
	availableCommandResponse, _ := json.Marshal(&availableCommands)

	defaultHandler := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(statusNotImplemented)
		w.Write(availableCommandResponse)
	}

	serverMux := http.NewServeMux()
	serverMux.HandleFunc("/", defaultHandler)
	for key, fn := range serverFunctions {
		serverMux.HandleFunc(key, fn)
	}

	// Log all requests and then pass through to serverMux
	loggingServeMux := http.NewServeMux()
	loggingServeMux.Handle("/", LoggingHandler{serverMux})

	server := http.Server{
		Addr:         ":" + strconv.Itoa(config.AGENT_INTROSPECTION_PORT),
		Handler:      loggingServeMux,
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}

	for {
		err := server.ListenAndServe()
		log.Error("Error running http api", "err", err)
	}
}
<|endoftext|>


<commit_before>package geom

import (
	"math"
)

type LineString struct {
	layout     Layout
	stride     int
	flatCoords []float64
}

var _ T = &LineString{}

func NewLineString(layout Layout) *LineString {
	return &LineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: nil,
	}
}

func NewLineStringFlat(layout Layout, flatCoords []float64) *LineString {
	return &LineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: flatCoords,
	}
}

func (ls *LineString) Clone() *LineString {
	flatCoords := make([]float64, len(ls.flatCoords))
	copy(flatCoords, ls.flatCoords)
	return &LineString{
		layout:     ls.layout,
		stride:     ls.stride,
		flatCoords: flatCoords,
	}
}

func (ls *LineString) Coord(i int) []float64 {
	return ls.flatCoords[i*ls.stride : (i+1)*ls.stride]
}

func (ls *LineString) Coords() [][]float64 {
	return inflate1(ls.flatCoords, 0, len(ls.flatCoords), ls.stride)
}

func (ls *LineString) Ends() []int {
	return nil
}

func (ls *LineString) Endss() [][]int {
	return nil
}

func (ls *LineString) Envelope() *Envelope {
	return NewEnvelope().extendFlatCoords(ls.flatCoords, 0, len(ls.flatCoords), ls.stride)
}

func (ls *LineString) FlatCoords() []float64 {
	return ls.flatCoords
}

func (ls *LineString) Layout() Layout {
	return ls.layout
}

func (ls *LineString) Length() float64 {
	length := 0.0
	for i := ls.stride; i < len(ls.flatCoords); i += ls.stride {
		dx := ls.flatCoords[i] - ls.flatCoords[i-ls.stride]
		dy := ls.flatCoords[i+1] - ls.flatCoords[i+1-ls.stride]
		length += math.Sqrt(dx*dx + dy*dy)
	}
	return length
}

func (ls *LineString) NumCoords() int {
	return len(ls.flatCoords) / ls.stride
}

func (ls *LineString) Push(coord0 []float64) error {
	if len(coord0) != ls.stride {
		return ErrStrideMismatch{Got: len(coord0), Want: ls.stride}
	}
	ls.flatCoords = append(ls.flatCoords, coord0...)
	return nil
}

func (ls *LineString) SetCoords(coords1 [][]float64) error {
	var err error
	if ls.flatCoords, err = deflate1(nil, coords1, ls.stride); err != nil {
		return err
	}
	return nil
}

func (ls *LineString) Stride() int {
	return ls.stride
}
<commit_msg>Add LineString.SubLineString<commit_after>package geom

import (
	"math"
)

type LineString struct {
	layout     Layout
	stride     int
	flatCoords []float64
}

var _ T = &LineString{}

func NewLineString(layout Layout) *LineString {
	return &LineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: nil,
	}
}

func NewLineStringFlat(layout Layout, flatCoords []float64) *LineString {
	return &LineString{
		layout:     layout,
		stride:     layout.Stride(),
		flatCoords: flatCoords,
	}
}

func (ls *LineString) Clone() *LineString {
	flatCoords := make([]float64, len(ls.flatCoords))
	copy(flatCoords, ls.flatCoords)
	return &LineString{
		layout:     ls.layout,
		stride:     ls.stride,
		flatCoords: flatCoords,
	}
}

func (ls *LineString) Coord(i int) []float64 {
	return ls.flatCoords[i*ls.stride : (i+1)*ls.stride]
}

func (ls *LineString) Coords() [][]float64 {
	return inflate1(ls.flatCoords, 0, len(ls.flatCoords), ls.stride)
}

func (ls *LineString) Ends() []int {
	return nil
}

func (ls *LineString) Endss() [][]int {
	return nil
}

func (ls *LineString) Envelope() *Envelope {
	return NewEnvelope().extendFlatCoords(ls.flatCoords, 0, len(ls.flatCoords), ls.stride)
}

func (ls *LineString) FlatCoords() []float64 {
	return ls.flatCoords
}

func (ls *LineString) Layout() Layout {
	return ls.layout
}

func (ls *LineString) Length() float64 {
	length := 0.0
	for i := ls.stride; i < len(ls.flatCoords); i += ls.stride {
		dx := ls.flatCoords[i] - ls.flatCoords[i-ls.stride]
		dy := ls.flatCoords[i+1] - ls.flatCoords[i+1-ls.stride]
		length += math.Sqrt(dx*dx + dy*dy)
	}
	return length
}

func (ls *LineString) NumCoords() int {
	return len(ls.flatCoords) / ls.stride
}

func (ls *LineString) Push(coord0 []float64) error {
	if len(coord0) != ls.stride {
		return ErrStrideMismatch{Got: len(coord0), Want: ls.stride}
	}
	ls.flatCoords = append(ls.flatCoords, coord0...)
	return nil
}

func (ls *LineString) SetCoords(coords1 [][]float64) error {
	var err error
	if ls.flatCoords, err = deflate1(nil, coords1, ls.stride); err != nil {
		return err
	}
	return nil
}

func (ls *LineString) Stride() int {
	return ls.stride
}

func (ls *LineString) SubLineString(start, stop int) *LineString {
	return &LineString{
		layout:     ls.layout,
		stride:     ls.stride,
		flatCoords: ls.flatCoords[start*ls.stride : stop*ls.stride],
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package controller

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/golang/glog"

	api "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"

	"k8s.io/ingress/core/pkg/ingress"
	"k8s.io/ingress/core/pkg/net/ssl"
)

// syncSecret keeps in sync Secrets used by Ingress rules with the files on
// disk to allow copy of the content of the secret to disk to be used
// by external processes.
func (ic *GenericController) syncSecret() {
	glog.V(3).Infof("starting syncing of secrets")

	var cert *ingress.SSLCert
	var err error

	for _, k := range ic.secretTracker.List() {
		key := k.(string)
		cert, err = ic.getPemCertificate(key)
		if err != nil {
			glog.Warningf("error obtaining PEM from secret %v: %v", key, err)
			continue
		}

		// create certificates and add or update the item in the store
		cur, exists := ic.sslCertTracker.Get(key)
		if exists {
			s := cur.(*ingress.SSLCert)
			if reflect.DeepEqual(s, cert) {
				// no need to update
				continue
			}
			glog.Infof("updating secret %v in the local store", key)
			ic.sslCertTracker.Update(key, cert)
			ic.reloadRequired = true
			continue
		}

		glog.Infof("adding secret %v to the local store", key)
		ic.sslCertTracker.Add(key, cert)
		ic.reloadRequired = true
	}
}

// getPemCertificate receives a secret, and creates a ingress.SSLCert as return.
// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only.
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
	secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
	if err != nil {
		return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err)
	}
	if !exists {
		return nil, fmt.Errorf("secret named %v does not exist", secretName)
	}

	secret := secretInterface.(*api.Secret)
	cert, okcert := secret.Data[api.TLSCertKey]
	key, okkey := secret.Data[api.TLSPrivateKeyKey]

	ca := secret.Data["ca.crt"]

	nsSecName := strings.Replace(secretName, "/", "-", -1)

	var s *ingress.SSLCert
	if okcert && okkey {
		s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
		glog.V(3).Infof("found certificate and private key, configuring %v as a TLS Secret (CN: %v)", secretName, s.CN)
	} else if ca != nil {
		glog.V(3).Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName)
		s, err = ssl.AddCertAuth(nsSecName, ca)
	} else {
		return nil, fmt.Errorf("no keypair or CA cert could be found in %v", secretName)
	}

	if err != nil {
		return nil, err
	}

	s.Name = secret.Name
	s.Namespace = secret.Namespace
	return s, nil
}

// sslCertTracker holds a store of referenced Secrets in Ingress rules
type sslCertTracker struct {
	cache.ThreadSafeStore
}

func newSSLCertTracker() *sslCertTracker {
	return &sslCertTracker{
		cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
	}
}

// secretTracker holds a store of Secrets
type secretTracker struct {
	cache.ThreadSafeStore
}

func newSecretTracker() *secretTracker {
	return &secretTracker{
		cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
	}
}
<commit_msg><commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package controller

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/golang/glog"

	api "k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/tools/cache"

	"k8s.io/ingress/core/pkg/ingress"
	"k8s.io/ingress/core/pkg/net/ssl"
)

// syncSecret keeps in sync Secrets used by Ingress rules with the files on
// disk to allow copy of the content of the secret to disk to be used
// by external processes.
func (ic *GenericController) syncSecret() {
	glog.V(3).Infof("starting syncing of secrets")

	var cert *ingress.SSLCert
	var err error

	for _, k := range ic.secretTracker.List() {
		key := k.(string)
		cert, err = ic.getPemCertificate(key)
		if err != nil {
			glog.Warningf("error obtaining PEM from secret %v: %v", key, err)
			continue
		}

		// create certificates and add or update the item in the store
		cur, exists := ic.sslCertTracker.Get(key)
		if exists {
			s := cur.(*ingress.SSLCert)
			if reflect.DeepEqual(s, cert) {
				// no need to update
				continue
			}
			glog.Infof("updating secret %v in the local store", key)
			ic.sslCertTracker.Update(key, cert)
			ic.reloadRequired = true
			continue
		}

		glog.Infof("adding secret %v to the local store", key)
		ic.sslCertTracker.Add(key, cert)
		ic.reloadRequired = true
	}
}

// getPemCertificate receives a secret, and creates a ingress.SSLCert as return.
// It parses the secret and verifies if it's a keypair, or a 'ca.crt' secret only.
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
	secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
	if err != nil {
		return nil, fmt.Errorf("error retrieving secret %v: %v", secretName, err)
	}
	if !exists {
		return nil, fmt.Errorf("secret named %v does not exist", secretName)
	}

	secret := secretInterface.(*api.Secret)
	cert, okcert := secret.Data[api.TLSCertKey]
	key, okkey := secret.Data[api.TLSPrivateKeyKey]

	ca := secret.Data["ca.crt"]

	nsSecName := strings.Replace(secretName, "/", "-", -1)

	var s *ingress.SSLCert
	if okcert && okkey {
		s, err = ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
		if err != nil {
			return nil, fmt.Errorf("unexpected error creating pem file %v", err)
		}
		glog.V(3).Infof("found certificate and private key, configuring %v as a TLS Secret (CN: %v)", secretName, s.CN)
	} else if ca != nil {
		glog.V(3).Infof("found only ca.crt, configuring %v as an Certificate Authentication secret", secretName)
		s, err = ssl.AddCertAuth(nsSecName, ca)
		if err != nil {
			return nil, fmt.Errorf("unexpected error creating pem file %v", err)
		}
	} else {
		return nil, fmt.Errorf("no keypair or CA cert could be found in %v", secretName)
	}

	if err != nil {
		return nil, err
	}

	s.Name = secret.Name
	s.Namespace = secret.Namespace
	return s, nil
}

// sslCertTracker holds a store of referenced Secrets in Ingress rules
type sslCertTracker struct {
	cache.ThreadSafeStore
}

func newSSLCertTracker() *sslCertTracker {
	return &sslCertTracker{
		cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
	}
}

// secretTracker holds a store of Secrets
type secretTracker struct {
	cache.ThreadSafeStore
}

func newSecretTracker() *secretTracker {
	return &secretTracker{
		cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
	}
}
<|endoftext|>


<commit_before>package hostaccess

import (
	"fmt"

	"github.com/gorilla/websocket"
	"github.com/rancher/go-rancher/client"
)

type RancherWebsocketClient client.RancherClient

func (c *RancherWebsocketClient) GetHostAccess(resource client.Resource, action string, input interface{}) (*websocket.Conn, error) {
	var resp client.HostAccess
	url := resource.Actions[action]
	if url == "" {
		return nil, fmt.Errorf("Failed to find action: %s", action)
	}

	err := c.Post(url, input, &resp)
	if err != nil {
		return nil, err
	}

	url = fmt.Sprintf("%s?token=%s", resp.Url, resp.Token)

	conn, _, err := c.Websocket(url, nil)

	return conn, err
}
<commit_msg>Switch host access to v2-beta<commit_after>package hostaccess

import (
	"fmt"

	"github.com/gorilla/websocket"
	"github.com/rancher/go-rancher/v2"
)

type RancherWebsocketClient client.RancherClient

func (c *RancherWebsocketClient) GetHostAccess(resource client.Resource, action string, input interface{}) (*websocket.Conn, error) {
	var resp client.HostAccess
	url := resource.Actions[action]
	if url == "" {
		return nil, fmt.Errorf("Failed to find action: %s", action)
	}

	err := c.Post(url, input, &resp)
	if err != nil {
		return nil, err
	}

	url = fmt.Sprintf("%s?token=%s", resp.Url, resp.Token)

	conn, _, err := c.Websocket(url, nil)

	return conn, err
}
<|endoftext|>


<commit_before>package brands

// Brand structure used by API
type Brand struct {
	UUID           string `json:"uuid"`
	PrefLabel      string `json:"prefLabel"`
	Description    string `json:"description"`
	ParentUUID     string `json:"parentUUID"`
	Strapline      string `json:"strapline"`
	DescriptionXML string `json:"descriptionXML"`
	ImageURL       string `json:"_imageUrl"` // TODO this is a temporary thing - needs to be integrated into images properly
}
<commit_msg>Set omitempty on ParentUUID<commit_after>package brands

// Brand structure used by API
type Brand struct {
	UUID           string `json:"uuid"`
	PrefLabel      string `json:"prefLabel"`
	Description    string `json:"description"`
	ParentUUID     string `json:"parentUUID,omitempty"`
	Strapline      string `json:"strapline"`
	DescriptionXML string `json:"descriptionXML"`
	ImageURL       string `json:"_imageUrl"` // TODO this is a temporary thing - needs to be integrated into images properly
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package config

import (
	"fmt"
	"time"

	"github.com/gocql/gocql"

	"github.com/uber/jaeger/pkg/cassandra"
	gocqlw "github.com/uber/jaeger/pkg/cassandra/gocql"
)

// Configuration describes the configuration properties needed to connect to a Cassandra cluster
type Configuration struct {
	Servers            []string      `validate:"nonzero"`
	Keyspace           string        `validate:"nonzero"`
	ConnectionsPerHost int           `validate:"min=1" yaml:"connections_per_host"`
	Timeout            time.Duration `validate:"min=500"`
	SocketKeepAlive    time.Duration `validate:"min=0" yaml:"socket_keep_alive"`
	MaxRetryAttempts   int           `validate:"min=0" yaml:"max_retry_attempt"`
	ProtoVersion       int           `yaml:"proto_version"`
	Consistency        string        `yaml:"consistency"`
	Port               int           `yaml:"port"`
}

// ApplyDefaults copies settings from source unless its own value is non-zero.
func (c *Configuration) ApplyDefaults(source *Configuration) {
	if c.ConnectionsPerHost == 0 {
		c.ConnectionsPerHost = source.ConnectionsPerHost
	}
	if c.MaxRetryAttempts == 0 {
		c.MaxRetryAttempts = source.MaxRetryAttempts
	}
	if c.Timeout == 0 {
		c.Timeout = source.Timeout
	}
	if c.Port == 0 {
		c.Port = source.Port
	}
	if c.Keyspace == "" {
		c.Keyspace = source.Keyspace
	}
	if c.ProtoVersion == 0 {
		c.ProtoVersion = source.ProtoVersion
	}
	if c.SocketKeepAlive == 0 {
		c.SocketKeepAlive = source.SocketKeepAlive
	}
}

// SessionBuilder creates new cassandra.Session
type SessionBuilder interface {
	NewSession() (cassandra.Session, error)
}

// NewSession creates a new Cassandra session
func (c *Configuration) NewSession() (cassandra.Session, error) {
	c.ProtoVersion = 4
	cluster := c.NewCluster()
	session, err := cluster.CreateSession()
	if err != nil {
		return nil, err
	}
	return gocqlw.WrapCQLSession(session), nil
}

// NewCluster creates a new gocql cluster from the configuration
func (c *Configuration) NewCluster() *gocql.ClusterConfig {
	cluster := gocql.NewCluster(c.Servers...)
	cluster.Keyspace = c.Keyspace
	cluster.NumConns = c.ConnectionsPerHost
	cluster.Timeout = c.Timeout
	cluster.SocketKeepalive = c.SocketKeepAlive
	if c.ProtoVersion > 0 {
		cluster.ProtoVersion = c.ProtoVersion
	}
	if c.MaxRetryAttempts > 1 {
		cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: c.MaxRetryAttempts - 1}
	}
	if c.Port != 0 {
		cluster.Port = c.Port
	}
	cluster.Compressor = gocql.SnappyCompressor{}
	if c.Consistency == "" {
		cluster.Consistency = gocql.LocalOne
	} else {
		cluster.Consistency = gocql.ParseConsistency(c.Consistency)
	}
	cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy())
	return cluster
}

func (c *Configuration) String() string {
	return fmt.Sprintf("%+v", *c)
}
<commit_msg>Remove default ProtoVersion (#316)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package config

import (
	"fmt"
	"time"

	"github.com/gocql/gocql"

	"github.com/uber/jaeger/pkg/cassandra"
	gocqlw "github.com/uber/jaeger/pkg/cassandra/gocql"
)

// Configuration describes the configuration properties needed to connect to a Cassandra cluster
type Configuration struct {
	Servers            []string      `validate:"nonzero"`
	Keyspace           string        `validate:"nonzero"`
	ConnectionsPerHost int           `validate:"min=1" yaml:"connections_per_host"`
	Timeout            time.Duration `validate:"min=500"`
	SocketKeepAlive    time.Duration `validate:"min=0" yaml:"socket_keep_alive"`
	MaxRetryAttempts   int           `validate:"min=0" yaml:"max_retry_attempt"`
	ProtoVersion       int           `yaml:"proto_version"`
	Consistency        string        `yaml:"consistency"`
	Port               int           `yaml:"port"`
}

// ApplyDefaults copies settings from source unless its own value is non-zero.
func (c *Configuration) ApplyDefaults(source *Configuration) {
	if c.ConnectionsPerHost == 0 {
		c.ConnectionsPerHost = source.ConnectionsPerHost
	}
	if c.MaxRetryAttempts == 0 {
		c.MaxRetryAttempts = source.MaxRetryAttempts
	}
	if c.Timeout == 0 {
		c.Timeout = source.Timeout
	}
	if c.Port == 0 {
		c.Port = source.Port
	}
	if c.Keyspace == "" {
		c.Keyspace = source.Keyspace
	}
	if c.ProtoVersion == 0 {
		c.ProtoVersion = source.ProtoVersion
	}
	if c.SocketKeepAlive == 0 {
		c.SocketKeepAlive = source.SocketKeepAlive
	}
}

// SessionBuilder creates new cassandra.Session
type SessionBuilder interface {
	NewSession() (cassandra.Session, error)
}

// NewSession creates a new Cassandra session
func (c *Configuration) NewSession() (cassandra.Session, error) {
	cluster := c.NewCluster()
	session, err := cluster.CreateSession()
	if err != nil {
		return nil, err
	}
	return gocqlw.WrapCQLSession(session), nil
}

// NewCluster creates a new gocql cluster from the configuration
func (c *Configuration) NewCluster() *gocql.ClusterConfig {
	cluster := gocql.NewCluster(c.Servers...)
	cluster.Keyspace = c.Keyspace
	cluster.NumConns = c.ConnectionsPerHost
	cluster.Timeout = c.Timeout
	cluster.SocketKeepalive = c.SocketKeepAlive
	if c.ProtoVersion > 0 {
		cluster.ProtoVersion = c.ProtoVersion
	}
	if c.MaxRetryAttempts > 1 {
		cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: c.MaxRetryAttempts - 1}
	}
	if c.Port != 0 {
		cluster.Port = c.Port
	}
	cluster.Compressor = gocql.SnappyCompressor{}
	if c.Consistency == "" {
		cluster.Consistency = gocql.LocalOne
	} else {
		cluster.Consistency = gocql.ParseConsistency(c.Consistency)
	}
	cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RoundRobinHostPolicy())
	return cluster
}

func (c *Configuration) String() string {
	return fmt.Sprintf("%+v", *c)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package controller

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/utils"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress"
	"reflect"
	"sort"
)

// convert a list of ingress backends to a map with backend names as keys
func ingressBackendsRemap(backends []*ingress.Backend) (map[string]*ingress.Backend, []string) {
	backendsMap := map[string]*ingress.Backend{}
	keys := []string{}
	for _, ingBackend := range backends {
		backendsMap[ingBackend.Name] = ingBackend
		keys = append(keys, ingBackend.Name)
	}
	sort.Strings(keys)
	return backendsMap, keys
}

// convert a list of endpoints to a map with address:port as keys
func ingressEndpointsRemap(endpoints []ingress.Endpoint) map[string]*ingress.Endpoint {
	endpointMap := map[string]*ingress.Endpoint{}
	for i, e := range endpoints {
		endpointMap[fmt.Sprintf("%s:%s", e.Address, e.Port)] = &endpoints[i]
	}
	return endpointMap
}

// return elements in s1 but not in s2
func endpointsSubtract(s1, s2 map[string]*ingress.Endpoint) map[string]*ingress.Endpoint {
	s3 := map[string]*ingress.Endpoint{}
	for k, v := range s1 {
		_, ok := s2[k]
		if !ok {
			s3[k] = v
		}
	}
	return s3
}

// remove Ingress Endpoint from a backend by disabling a specific server slot
func removeEndpoint(statsSocket, backendName, backendServerName string) bool {
	err := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s state maint\n", backendName, backendServerName))
	if err != nil {
		glog.Warningln("failed socket command srv remove")
		return false
	}
	return true
}

// add Ingress Endpoint to a backend in a specific server slot
func addEndpoint(statsSocket, backendName, backendServerName, address, port string) bool {
	err1 := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s addr %s port %s\n", backendName, backendServerName, address, port))
	err2 := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s state ready\n", backendName, backendServerName))
	if err1 != nil || err2 != nil {
		glog.Warningln("failed socket command srv add")
		return false
	}
	return true
}

// populate template variables and optionally issue socket commands to reconfigure haproxy without reloading
func reconfigureBackends(currentConfig, updatedConfig *types.ControllerConfig) bool {
	reloadRequired := true
	reconfigureEmptySlots := false

	if !updatedConfig.Cfg.DynamicScaling {
		reconfigureEmptySlots = true
	} else if currentConfig != nil {
		// store backend lists for retrieval
		curBackends := currentConfig.Backends
		updBackends := updatedConfig.Backends
		// store BackendSlots for retrieval
		curBackendSlots := currentConfig.BackendSlots

		// exclude backend lists and slots from reflect.DeepEqual
		updatedConfig.Backends = []*ingress.Backend{}
		currentConfig.Backends = updatedConfig.Backends
		currentConfig.BackendSlots = updatedConfig.BackendSlots

		// check equality of everything but backends
		if !reflect.DeepEqual(updatedConfig, currentConfig) {
			reconfigureEmptySlots = true
		} else {
			// set up maps
			curBackendsMap, curKeys := ingressBackendsRemap(curBackends)
			updBackendsMap, updKeys := ingressBackendsRemap(updBackends)

			if !reflect.DeepEqual(curKeys, updKeys) {
				// backend names or number of backends is different
				reconfigureEmptySlots = true
			} else {
				// same names and nr of backends, we can modify existing HAProxyBackendSlots, should not need reloading
				reloadRequired = false
				updatedConfig.BackendSlots = curBackendSlots
				for _, backendName := range curKeys {
					updLen := len(updBackendsMap[backendName].Endpoints)
					totalSlots := len(curBackendSlots[backendName].EmptySlots) + len(curBackendSlots[backendName].FullSlots)
					if updLen > totalSlots || updLen < (totalSlots-updatedConfig.Cfg.BackendServerSlotsIncrement) {
						// need to resize number of empty slots by BackendServerSlotsIncrement amount
						reconfigureEmptySlots = true
					} else {
						// everything fits so reconfigure endpoints without reloading
						// do it with maps posing as sets
						curEndpoints := ingressEndpointsRemap(curBackendsMap[backendName].Endpoints)
						updEndpoints := ingressEndpointsRemap(updBackendsMap[backendName].Endpoints)

						toRemoveEndpoints := endpointsSubtract(curEndpoints, updEndpoints)
						toAddEndpoints := endpointsSubtract(updEndpoints, curEndpoints)

						// check for new/removed entries in this backend, issue socket commands
						backendSlots := updatedConfig.BackendSlots[backendName]
						// remove endpoints
						for k := range toRemoveEndpoints {
							reloadRequired = reloadRequired || !removeEndpoint(currentConfig.Cfg.StatsSocket, backendName, backendSlots.FullSlots[k].BackendServerName)
							backendSlots.EmptySlots = append(backendSlots.EmptySlots, backendSlots.FullSlots[k].BackendServerName)
							delete(backendSlots.FullSlots, k)
						}
						sort.Strings(backendSlots.EmptySlots)
						// add endpoints
						for k, endpoint := range toAddEndpoints {
							// rearrange slots
							backendSlots.FullSlots[k] = types.HAProxyBackendSlot{
								BackendServerName: backendSlots.EmptySlots[0],
								BackendEndpoint:   endpoint,
							}
							backendSlots.EmptySlots = backendSlots.EmptySlots[1:]
							reloadRequired = reloadRequired || !addEndpoint(currentConfig.Cfg.StatsSocket, backendName, backendSlots.FullSlots[k].BackendServerName, endpoint.Address, endpoint.Port)
						}
						updatedConfig.BackendSlots[backendName] = backendSlots
					}
				}
			}
		}
		// restore backend lists
		currentConfig.Backends = curBackends
		updatedConfig.Backends = updBackends
		// restore backend slots
		currentConfig.BackendSlots = curBackendSlots
	}

	if currentConfig == nil || reconfigureEmptySlots {
		fillBackendServerSlots(updatedConfig)
		reloadRequired = true
	}

	return reloadRequired
}

// fill-out backends with available endpoints, add empty slots if required
func fillBackendServerSlots(updatedConfig *types.ControllerConfig) {
	updBackendsMap, updKeys := ingressBackendsRemap(updatedConfig.Backends)
	updatedConfig.BackendSlots = map[string]types.HAProxyBackendSlots{}

	for _, backendName := range updKeys {
		newBackend := types.HAProxyBackendSlots{}
		newBackend.FullSlots = map[string]types.HAProxyBackendSlot{}
		if updatedConfig.Cfg.DynamicScaling {
			for i, endpoint := range updBackendsMap[backendName].Endpoints {
				newBackend.FullSlots[fmt.Sprintf("%s:%s", endpoint.Address, endpoint.Port)] = types.HAProxyBackendSlot{
					BackendServerName: fmt.Sprintf("server%04d", i),
					BackendEndpoint:   &endpoint,
				}
			}
			// add up to BackendServerSlotsIncrement empty slots
			fullSlotCnt := len(newBackend.FullSlots)
			extraSlotCnt := (int(fullSlotCnt/updatedConfig.Cfg.BackendServerSlotsIncrement)+1)*updatedConfig.Cfg.BackendServerSlotsIncrement - fullSlotCnt
			for i := 0; i < extraSlotCnt; i++ {
				newBackend.EmptySlots = append(newBackend.EmptySlots, fmt.Sprintf("server%04d", i+fullSlotCnt))
			}
		} else {
			// use addr:port as BackendServerName, don't generate empty slots
			for _, endpoint := range updBackendsMap[backendName].Endpoints {
				curEndpoint := endpoint
				target := fmt.Sprintf("%s:%s", endpoint.Address, endpoint.Port)
				newBackend.FullSlots[target] = types.HAProxyBackendSlot{
					BackendServerName: target,
					BackendEndpoint:   &curEndpoint,
				}
			}
		}
		updatedConfig.BackendSlots[backendName] = newBackend
	}
}
<commit_msg>Correctly copy Endpoints to FullSlots (DynamicScaling case)<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package controller

import (
	"fmt"
	"github.com/golang/glog"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/types"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/utils"
	"github.com/jcmoraisjr/haproxy-ingress/pkg/common/ingress"
	"reflect"
	"sort"
)

// convert a list of ingress backends to a map with backend names as keys
func ingressBackendsRemap(backends []*ingress.Backend) (map[string]*ingress.Backend, []string) {
	backendsMap := map[string]*ingress.Backend{}
	keys := []string{}
	for _, ingBackend := range backends {
		backendsMap[ingBackend.Name] = ingBackend
		keys = append(keys, ingBackend.Name)
	}
	sort.Strings(keys)
	return backendsMap, keys
}

// convert a list of endpoints to a map with address:port as keys
func ingressEndpointsRemap(endpoints []ingress.Endpoint) map[string]*ingress.Endpoint {
	endpointMap := map[string]*ingress.Endpoint{}
	for i, e := range endpoints {
		endpointMap[fmt.Sprintf("%s:%s", e.Address, e.Port)] = &endpoints[i]
	}
	return endpointMap
}

// return elements in s1 but not in s2
func endpointsSubtract(s1, s2 map[string]*ingress.Endpoint) map[string]*ingress.Endpoint {
	s3 := map[string]*ingress.Endpoint{}
	for k, v := range s1 {
		_, ok := s2[k]
		if !ok {
			s3[k] = v
		}
	}
	return s3
}

// remove Ingress Endpoint from a backend by disabling a specific server slot
func removeEndpoint(statsSocket, backendName, backendServerName string) bool {
	err := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s state maint\n", backendName, backendServerName))
	if err != nil {
		glog.Warningln("failed socket command srv remove")
		return false
	}
	return true
}

// add Ingress Endpoint to a backend in a specific server slot
func addEndpoint(statsSocket, backendName, backendServerName, address, port string) bool {
	err1 := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s addr %s port %s\n", backendName, backendServerName, address, port))
	err2 := utils.SendToSocket(statsSocket,
		fmt.Sprintf("set server %s/%s state ready\n", backendName, backendServerName))
	if err1 != nil || err2 != nil {
		glog.Warningln("failed socket command srv add")
		return false
	}
	return true
}

// populate template variables and optionally issue socket commands to reconfigure haproxy without reloading
func reconfigureBackends(currentConfig, updatedConfig *types.ControllerConfig) bool {
	reloadRequired := true
	reconfigureEmptySlots := false

	if !updatedConfig.Cfg.DynamicScaling {
		reconfigureEmptySlots = true
	} else if currentConfig != nil {
		// store backend lists for retrieval
		curBackends := currentConfig.Backends
		updBackends := updatedConfig.Backends
		// store BackendSlots for retrieval
		curBackendSlots := currentConfig.BackendSlots

		// exclude backend lists and slots from reflect.DeepEqual
		updatedConfig.Backends = []*ingress.Backend{}
		currentConfig.Backends = updatedConfig.Backends
		currentConfig.BackendSlots = updatedConfig.BackendSlots

		// check equality of everything but backends
		if !reflect.DeepEqual(updatedConfig, currentConfig) {
			reconfigureEmptySlots = true
		} else {
			// set up maps
			curBackendsMap, curKeys := ingressBackendsRemap(curBackends)
			updBackendsMap, updKeys := ingressBackendsRemap(updBackends)

			if !reflect.DeepEqual(curKeys, updKeys) {
				// backend names or number of backends is different
				reconfigureEmptySlots = true
			} else {
				// same names and nr of backends, we can modify existing HAProxyBackendSlots, should not need reloading
				reloadRequired = false
				updatedConfig.BackendSlots = curBackendSlots
				for _, backendName := range curKeys {
					updLen := len(updBackendsMap[backendName].Endpoints)
					totalSlots := len(curBackendSlots[backendName].EmptySlots) + len(curBackendSlots[backendName].FullSlots)
					if updLen > totalSlots || updLen < (totalSlots-updatedConfig.Cfg.BackendServerSlotsIncrement) {
						// need to resize number of empty slots by BackendServerSlotsIncrement amount
						reconfigureEmptySlots = true
					} else {
						// everything fits so reconfigure endpoints without reloading
						// do it with maps posing as sets
						curEndpoints := ingressEndpointsRemap(curBackendsMap[backendName].Endpoints)
						updEndpoints := ingressEndpointsRemap(updBackendsMap[backendName].Endpoints)

						toRemoveEndpoints := endpointsSubtract(curEndpoints, updEndpoints)
						toAddEndpoints := endpointsSubtract(updEndpoints, curEndpoints)

						// check for new/removed entries in this backend, issue socket commands
						backendSlots := updatedConfig.BackendSlots[backendName]
						// remove endpoints
						for k := range toRemoveEndpoints {
							reloadRequired = reloadRequired || !removeEndpoint(currentConfig.Cfg.StatsSocket, backendName, backendSlots.FullSlots[k].BackendServerName)
							backendSlots.EmptySlots = append(backendSlots.EmptySlots, backendSlots.FullSlots[k].BackendServerName)
							delete(backendSlots.FullSlots, k)
						}
						sort.Strings(backendSlots.EmptySlots)
						// add endpoints
						for k, endpoint := range toAddEndpoints {
							// rearrange slots
							backendSlots.FullSlots[k] = types.HAProxyBackendSlot{
								BackendServerName: backendSlots.EmptySlots[0],
								BackendEndpoint:   endpoint,
							}
							backendSlots.EmptySlots = backendSlots.EmptySlots[1:]
							reloadRequired = reloadRequired || !addEndpoint(currentConfig.Cfg.StatsSocket, backendName, backendSlots.FullSlots[k].BackendServerName, endpoint.Address, endpoint.Port)
						}
						updatedConfig.BackendSlots[backendName] = backendSlots
					}
				}
			}
		}
		// restore backend lists
		currentConfig.Backends = curBackends
		updatedConfig.Backends = updBackends
		// restore backend slots
		currentConfig.BackendSlots = curBackendSlots
	}

	if currentConfig == nil || reconfigureEmptySlots {
		fillBackendServerSlots(updatedConfig)
		reloadRequired = true
	}

	return reloadRequired
}

// fill-out backends with available endpoints, add empty slots if required
func fillBackendServerSlots(updatedConfig *types.ControllerConfig) {
	updBackendsMap, updKeys := ingressBackendsRemap(updatedConfig.Backends)
	updatedConfig.BackendSlots = map[string]types.HAProxyBackendSlots{}

	for _, backendName := range updKeys {
		newBackend := types.HAProxyBackendSlots{}
		newBackend.FullSlots = map[string]types.HAProxyBackendSlot{}
		if updatedConfig.Cfg.DynamicScaling {
			for i, endpoint := range updBackendsMap[backendName].Endpoints {
				curEndpoint := endpoint
				newBackend.FullSlots[fmt.Sprintf("%s:%s", endpoint.Address, endpoint.Port)] = types.HAProxyBackendSlot{
					BackendServerName: fmt.Sprintf("server%04d", i),
					BackendEndpoint:   &curEndpoint,
				}
			}
			// add up to BackendServerSlotsIncrement empty slots
			fullSlotCnt := len(newBackend.FullSlots)
			extraSlotCnt := (int(fullSlotCnt/updatedConfig.Cfg.BackendServerSlotsIncrement)+1)*updatedConfig.Cfg.BackendServerSlotsIncrement - fullSlotCnt
			for i := 0; i < extraSlotCnt; i++ {
				newBackend.EmptySlots = append(newBackend.EmptySlots, fmt.Sprintf("server%04d", i+fullSlotCnt))
			}
		} else {
			// use addr:port as BackendServerName, don't generate empty slots
			for _, endpoint := range updBackendsMap[backendName].Endpoints {
				curEndpoint := endpoint
				target := fmt.Sprintf("%s:%s", endpoint.Address, endpoint.Port)
				newBackend.FullSlots[target] = types.HAProxyBackendSlot{
					BackendServerName: target,
					BackendEndpoint:   &curEndpoint,
				}
			}
		}
		updatedConfig.BackendSlots[backendName] = newBackend
	}
}
<|endoftext|>


<commit_before>// Copyright 2016-2018 Authors of Cilium
//
// 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 endpointmanager

import (
	"context"
	"fmt"
	"sync"

	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/metrics"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/policy"

	"github.com/prometheus/client_golang/prometheus"
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "endpoint-manager")

	// mutex protects endpoints and endpointsAux
	mutex lock.RWMutex

	// endpoints is the global list of endpoints indexed by ID. mutex must
	// be held to read and write.
	endpoints    = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}

	// EndpointSynchronizer updates external resources (e.g., Kubernetes) with
	// up-to-date information about endpoints managed by the endpoint manager.
	EndpointSynchronizer EndpointResourceSynchronizer
)

// EndpointResourceSynchronizer is an interface which synchronizes CiliumEndpoint
// resources with Kubernetes.
type EndpointResourceSynchronizer interface {
	RunK8sCiliumEndpointSync(ep *endpoint.Endpoint)
}

func init() {
	// EndpointCount is a function used to collect this metric. We cannot
	// increment/decrement a gauge since we invoke Remove gratuitiously and that
	// would result in negative counts.
	// It must be thread-safe.
	metrics.EndpointCount = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Namespace: metrics.Namespace,
		Name:      "endpoint_count",
		Help:      "Number of endpoints managed by this agent",
	},
		func() float64 { return float64(len(GetEndpoints())) },
	)
	metrics.MustRegister(metrics.EndpointCount)
}

// Insert inserts the endpoint into the global maps.
func Insert(ep *endpoint.Endpoint) {
	// No need to check liveness as an endpoint can only be deleted via the
	// API after it has been inserted into the manager.
	ep.UnconditionalRLock()
	mutex.Lock()

	endpoints[ep.ID] = ep
	updateReferences(ep)

	mutex.Unlock()
	ep.RUnlock()

	if EndpointSynchronizer != nil {
		EndpointSynchronizer.RunK8sCiliumEndpointSync(ep)
	}
}

// Lookup looks up the endpoint by prefix id
func Lookup(id string) (*endpoint.Endpoint, error) {
	mutex.RLock()
	defer mutex.RUnlock()

	prefix, eid, err := endpointid.ParsePrefix(id)
	if err != nil {
		return nil, err
	}

	switch prefix {
	case endpointid.CiliumLocalIdPrefix:
		n, err := endpointid.ParseCiliumID(id)
		if err != nil {
			return nil, err
		}
		return lookupCiliumID(uint16(n)), nil

	case endpointid.CiliumGlobalIdPrefix:
		return nil, fmt.Errorf("Unsupported id format for now")

	case endpointid.ContainerIdPrefix:
		return lookupContainerID(eid), nil

	case endpointid.DockerEndpointPrefix:
		return lookupDockerEndpoint(eid), nil

	case endpointid.ContainerNamePrefix:
		return lookupDockerContainerName(eid), nil

	case endpointid.PodNamePrefix:
		return lookupPodNameLocked(eid), nil

	case endpointid.IPv4Prefix:
		return lookupIPv4(eid), nil

	default:
		return nil, fmt.Errorf("Unknown endpoint prefix %s", prefix)
	}
}

// LookupCiliumID looks up endpoint by endpoint ID
func LookupCiliumID(id uint16) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupCiliumID(id)
	mutex.RUnlock()
	return ep
}

// LookupContainerID looks up endpoint by Docker ID
func LookupContainerID(id string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupContainerID(id)
	mutex.RUnlock()
	return ep
}

// LookupIPv4 looks up endpoint by IPv4 address
func LookupIPv4(ipv4 string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupIPv4(ipv4)
	mutex.RUnlock()
	return ep
}

// LookupPodName looks up endpoint by namespace + pod name
func LookupPodName(name string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupPodNameLocked(name)
	mutex.RUnlock()
	return ep
}

// UpdateReferences makes an endpoint available by all possible reference
// fields as available for this endpoint (containerID, IPv4 address, ...)
// Must be called with ep.Mutex.RLock held.
func UpdateReferences(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	updateReferences(ep)
}

// Remove removes the endpoint from the global maps.
// Must be called with ep.Mutex.RLock held.
func Remove(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	delete(endpoints, ep.ID)

	if ep.ContainerID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerIdPrefix, ep.ContainerID))
	}

	if ep.DockerEndpointID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID))
	}

	if ep.IPv4.String() != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String()))
	}

	if ep.ContainerName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName))
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.PodNamePrefix, podName))
	}
}

// RemoveAll removes all endpoints from the global maps.
func RemoveAll() {
	mutex.Lock()
	defer mutex.Unlock()
	endpoints = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
}

// lookupCiliumID looks up endpoint by endpoint ID
func lookupCiliumID(id uint16) *endpoint.Endpoint {
	if ep, ok := endpoints[id]; ok {
		return ep
	}
	return nil
}

func lookupDockerEndpoint(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, id)]; ok {
		return ep
	}
	return nil
}

func lookupPodNameLocked(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupDockerContainerName(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupIPv4(ipv4 string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ipv4)]; ok {
		return ep
	}
	return nil
}

func lookupContainerID(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, id)]; ok {
		return ep
	}
	return nil
}

func linkContainerID(ep *endpoint.Endpoint) {
	endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, ep.ContainerID)] = ep
}

// UpdateReferences updates the mappings of various values to their corresponding
// endpoints, such as ContainerID, Docker Container Name, Pod Name, etc.
func updateReferences(ep *endpoint.Endpoint) {
	if ep.ContainerID != "" {
		linkContainerID(ep)
	}

	if ep.DockerEndpointID != "" {
		endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID)] = ep
	}

	if ep.IPv4.String() != "" {
		endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String())] = ep
	}

	if ep.ContainerName != "" {
		endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName)] = ep
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, podName)] = ep
	}
}

// RegenerateAllEndpoints calls a SetStateLocked for each endpoint and
// regenerates if state transaction is valid. During this process, the endpoint
// list is locked and cannot be modified.
// The endpoint.RegenerationContext will be cloned to send a new context to
// each endpoint to avoid issue on endpoint regenerations statistics.
// Returns a waiting group that can be used to know when all the endpoints are
// regenerated.
func RegenerateAllEndpoints(owner endpoint.Owner, regenContext *endpoint.RegenerationContext) *sync.WaitGroup {
	var wg sync.WaitGroup

	eps := GetEndpoints()
	wg.Add(len(eps))

	log.Infof("regenerating all endpoints due to %s", regenContext.Reason)
	for _, ep := range eps {
		go func(ep *endpoint.Endpoint, wg *sync.WaitGroup) {
			if err := ep.LockAlive(); err != nil {
				log.WithError(err).Warnf("Endpoint disappeared while queued to be regenerated: %s", regenContext.Reason)
				ep.LogStatus(endpoint.Policy, endpoint.Failure, "Error while handling policy updates for endpoint: "+err.Error())
			} else {
				regen := ep.SetStateLocked(endpoint.StateWaitingToRegenerate, fmt.Sprintf("Triggering endpoint regeneration due to %s", regenContext.Reason))
				ep.Unlock()
				if regen {
					// Regenerate logs status according to the build success/failure
					// Create a new regenContext to not overwrite the spanStats
					// values on the endpoint regeneration.
					<-ep.Regenerate(owner, endpoint.NewRegenerationContext(regenContext.Reason))
				}
			}
			wg.Done()
		}(ep, &wg)
	}

	return &wg
}

// HasGlobalCT returns true if the endpoints have a global CT, false otherwise.
func HasGlobalCT() bool {
	eps := GetEndpoints()
	for _, e := range eps {
		if !e.Options.IsEnabled(option.ConntrackLocal) {
			return true
		}
	}
	return false
}

// GetEndpoints returns a slice of all endpoints present in endpoint manager.
func GetEndpoints() []*endpoint.Endpoint {
	mutex.RLock()
	eps := make([]*endpoint.Endpoint, 0, len(endpoints))
	for _, ep := range endpoints {
		eps = append(eps, ep)
	}
	mutex.RUnlock()
	return eps
}

// AddEndpoint takes the prepared endpoint object and starts managing it.
func AddEndpoint(owner endpoint.Owner, ep *endpoint.Endpoint, reason string) (err error) {
	alwaysEnforce := policy.GetPolicyEnabled() == option.AlwaysEnforce
	ep.SetIngressPolicyEnabled(alwaysEnforce)
	ep.SetEgressPolicyEnabled(alwaysEnforce)

	// Regenerate immediately if ready or waiting for identity
	if err := ep.LockAlive(); err != nil {
		return err
	}
	build := false
	state := ep.GetStateLocked()

	// We can only trigger regeneration of endpoints if the endpoint is in a
	// state where it can regenerate. See endpoint.SetStateLocked().
	if state == endpoint.StateReady {
		ep.SetStateLocked(endpoint.StateWaitingToRegenerate, reason)
		build = true
	}
	ep.Unlock()

	if build {
		if err := ep.RegenerateWait(owner, reason); err != nil {
			return err
		}
	}

	Insert(ep)
	ep.InsertEvent()

	return nil
}

// WaitForEndpointsAtPolicyRev waits for all endpoints which existed at the time
// this function is called to be at a given policy revision.
// New endpoints appearing while waiting are ignored.
func WaitForEndpointsAtPolicyRev(ctx context.Context, rev uint64) error {
	eps := GetEndpoints()
	for i := range eps {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-eps[i].WaitForPolicyRevision(ctx, rev):
			if ctx.Err() != nil {
				return ctx.Err()
			}
		}
	}
	return nil
}
<commit_msg>pkg/endpointmanager: make use of errors variables<commit_after>// Copyright 2016-2018 Authors of Cilium
//
// 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 endpointmanager

import (
	"context"
	"fmt"
	"sync"

	"github.com/cilium/cilium/pkg/endpoint"
	endpointid "github.com/cilium/cilium/pkg/endpoint/id"
	"github.com/cilium/cilium/pkg/lock"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/metrics"
	"github.com/cilium/cilium/pkg/option"
	"github.com/cilium/cilium/pkg/policy"

	"github.com/prometheus/client_golang/prometheus"
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "endpoint-manager")

	// mutex protects endpoints and endpointsAux
	mutex lock.RWMutex

	// endpoints is the global list of endpoints indexed by ID. mutex must
	// be held to read and write.
	endpoints    = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}

	// EndpointSynchronizer updates external resources (e.g., Kubernetes) with
	// up-to-date information about endpoints managed by the endpoint manager.
	EndpointSynchronizer EndpointResourceSynchronizer
)

// EndpointResourceSynchronizer is an interface which synchronizes CiliumEndpoint
// resources with Kubernetes.
type EndpointResourceSynchronizer interface {
	RunK8sCiliumEndpointSync(ep *endpoint.Endpoint)
}

func init() {
	// EndpointCount is a function used to collect this metric. We cannot
	// increment/decrement a gauge since we invoke Remove gratuitiously and that
	// would result in negative counts.
	// It must be thread-safe.
	metrics.EndpointCount = prometheus.NewGaugeFunc(prometheus.GaugeOpts{
		Namespace: metrics.Namespace,
		Name:      "endpoint_count",
		Help:      "Number of endpoints managed by this agent",
	},
		func() float64 { return float64(len(GetEndpoints())) },
	)
	metrics.MustRegister(metrics.EndpointCount)
}

// Insert inserts the endpoint into the global maps.
func Insert(ep *endpoint.Endpoint) {
	// No need to check liveness as an endpoint can only be deleted via the
	// API after it has been inserted into the manager.
	ep.UnconditionalRLock()
	mutex.Lock()

	endpoints[ep.ID] = ep
	updateReferences(ep)

	mutex.Unlock()
	ep.RUnlock()

	if EndpointSynchronizer != nil {
		EndpointSynchronizer.RunK8sCiliumEndpointSync(ep)
	}
}

// Lookup looks up the endpoint by prefix id
func Lookup(id string) (*endpoint.Endpoint, error) {
	mutex.RLock()
	defer mutex.RUnlock()

	prefix, eid, err := endpointid.ParsePrefix(id)
	if err != nil {
		return nil, err
	}

	switch prefix {
	case endpointid.CiliumLocalIdPrefix:
		n, err := endpointid.ParseCiliumID(id)
		if err != nil {
			return nil, err
		}
		return lookupCiliumID(uint16(n)), nil

	case endpointid.CiliumGlobalIdPrefix:
		return nil, ErrUnsupportedID

	case endpointid.ContainerIdPrefix:
		return lookupContainerID(eid), nil

	case endpointid.DockerEndpointPrefix:
		return lookupDockerEndpoint(eid), nil

	case endpointid.ContainerNamePrefix:
		return lookupDockerContainerName(eid), nil

	case endpointid.PodNamePrefix:
		return lookupPodNameLocked(eid), nil

	case endpointid.IPv4Prefix:
		return lookupIPv4(eid), nil

	default:
		return nil, ErrInvalidPrefix{InvalidPrefix: prefix.String()}
	}
}

// LookupCiliumID looks up endpoint by endpoint ID
func LookupCiliumID(id uint16) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupCiliumID(id)
	mutex.RUnlock()
	return ep
}

// LookupContainerID looks up endpoint by Docker ID
func LookupContainerID(id string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupContainerID(id)
	mutex.RUnlock()
	return ep
}

// LookupIPv4 looks up endpoint by IPv4 address
func LookupIPv4(ipv4 string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupIPv4(ipv4)
	mutex.RUnlock()
	return ep
}

// LookupPodName looks up endpoint by namespace + pod name
func LookupPodName(name string) *endpoint.Endpoint {
	mutex.RLock()
	ep := lookupPodNameLocked(name)
	mutex.RUnlock()
	return ep
}

// UpdateReferences makes an endpoint available by all possible reference
// fields as available for this endpoint (containerID, IPv4 address, ...)
// Must be called with ep.Mutex.RLock held.
func UpdateReferences(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	updateReferences(ep)
}

// Remove removes the endpoint from the global maps.
// Must be called with ep.Mutex.RLock held.
func Remove(ep *endpoint.Endpoint) {
	mutex.Lock()
	defer mutex.Unlock()
	delete(endpoints, ep.ID)

	if ep.ContainerID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerIdPrefix, ep.ContainerID))
	}

	if ep.DockerEndpointID != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID))
	}

	if ep.IPv4.String() != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String()))
	}

	if ep.ContainerName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName))
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		delete(endpointsAux, endpointid.NewID(endpointid.PodNamePrefix, podName))
	}
}

// RemoveAll removes all endpoints from the global maps.
func RemoveAll() {
	mutex.Lock()
	defer mutex.Unlock()
	endpoints = map[uint16]*endpoint.Endpoint{}
	endpointsAux = map[string]*endpoint.Endpoint{}
}

// lookupCiliumID looks up endpoint by endpoint ID
func lookupCiliumID(id uint16) *endpoint.Endpoint {
	if ep, ok := endpoints[id]; ok {
		return ep
	}
	return nil
}

func lookupDockerEndpoint(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, id)]; ok {
		return ep
	}
	return nil
}

func lookupPodNameLocked(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupDockerContainerName(name string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, name)]; ok {
		return ep
	}
	return nil
}

func lookupIPv4(ipv4 string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ipv4)]; ok {
		return ep
	}
	return nil
}

func lookupContainerID(id string) *endpoint.Endpoint {
	if ep, ok := endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, id)]; ok {
		return ep
	}
	return nil
}

func linkContainerID(ep *endpoint.Endpoint) {
	endpointsAux[endpointid.NewID(endpointid.ContainerIdPrefix, ep.ContainerID)] = ep
}

// UpdateReferences updates the mappings of various values to their corresponding
// endpoints, such as ContainerID, Docker Container Name, Pod Name, etc.
func updateReferences(ep *endpoint.Endpoint) {
	if ep.ContainerID != "" {
		linkContainerID(ep)
	}

	if ep.DockerEndpointID != "" {
		endpointsAux[endpointid.NewID(endpointid.DockerEndpointPrefix, ep.DockerEndpointID)] = ep
	}

	if ep.IPv4.String() != "" {
		endpointsAux[endpointid.NewID(endpointid.IPv4Prefix, ep.IPv4.String())] = ep
	}

	if ep.ContainerName != "" {
		endpointsAux[endpointid.NewID(endpointid.ContainerNamePrefix, ep.ContainerName)] = ep
	}

	if podName := ep.GetK8sNamespaceAndPodNameLocked(); podName != "" {
		endpointsAux[endpointid.NewID(endpointid.PodNamePrefix, podName)] = ep
	}
}

// RegenerateAllEndpoints calls a SetStateLocked for each endpoint and
// regenerates if state transaction is valid. During this process, the endpoint
// list is locked and cannot be modified.
// The endpoint.RegenerationContext will be cloned to send a new context to
// each endpoint to avoid issue on endpoint regenerations statistics.
// Returns a waiting group that can be used to know when all the endpoints are
// regenerated.
func RegenerateAllEndpoints(owner endpoint.Owner, regenContext *endpoint.RegenerationContext) *sync.WaitGroup {
	var wg sync.WaitGroup

	eps := GetEndpoints()
	wg.Add(len(eps))

	log.Infof("regenerating all endpoints due to %s", regenContext.Reason)
	for _, ep := range eps {
		go func(ep *endpoint.Endpoint, wg *sync.WaitGroup) {
			if err := ep.LockAlive(); err != nil {
				log.WithError(err).Warnf("Endpoint disappeared while queued to be regenerated: %s", regenContext.Reason)
				ep.LogStatus(endpoint.Policy, endpoint.Failure, "Error while handling policy updates for endpoint: "+err.Error())
			} else {
				regen := ep.SetStateLocked(endpoint.StateWaitingToRegenerate, fmt.Sprintf("Triggering endpoint regeneration due to %s", regenContext.Reason))
				ep.Unlock()
				if regen {
					// Regenerate logs status according to the build success/failure
					// Create a new regenContext to not overwrite the spanStats
					// values on the endpoint regeneration.
					<-ep.Regenerate(owner, endpoint.NewRegenerationContext(regenContext.Reason))
				}
			}
			wg.Done()
		}(ep, &wg)
	}

	return &wg
}

// HasGlobalCT returns true if the endpoints have a global CT, false otherwise.
func HasGlobalCT() bool {
	eps := GetEndpoints()
	for _, e := range eps {
		if !e.Options.IsEnabled(option.ConntrackLocal) {
			return true
		}
	}
	return false
}

// GetEndpoints returns a slice of all endpoints present in endpoint manager.
func GetEndpoints() []*endpoint.Endpoint {
	mutex.RLock()
	eps := make([]*endpoint.Endpoint, 0, len(endpoints))
	for _, ep := range endpoints {
		eps = append(eps, ep)
	}
	mutex.RUnlock()
	return eps
}

// AddEndpoint takes the prepared endpoint object and starts managing it.
func AddEndpoint(owner endpoint.Owner, ep *endpoint.Endpoint, reason string) (err error) {
	alwaysEnforce := policy.GetPolicyEnabled() == option.AlwaysEnforce
	ep.SetIngressPolicyEnabled(alwaysEnforce)
	ep.SetEgressPolicyEnabled(alwaysEnforce)

	// Regenerate immediately if ready or waiting for identity
	if err := ep.LockAlive(); err != nil {
		return err
	}
	build := false
	state := ep.GetStateLocked()

	// We can only trigger regeneration of endpoints if the endpoint is in a
	// state where it can regenerate. See endpoint.SetStateLocked().
	if state == endpoint.StateReady {
		ep.SetStateLocked(endpoint.StateWaitingToRegenerate, reason)
		build = true
	}
	ep.Unlock()

	if build {
		if err := ep.RegenerateWait(owner, reason); err != nil {
			return err
		}
	}

	Insert(ep)
	ep.InsertEvent()

	return nil
}

// WaitForEndpointsAtPolicyRev waits for all endpoints which existed at the time
// this function is called to be at a given policy revision.
// New endpoints appearing while waiting are ignored.
func WaitForEndpointsAtPolicyRev(ctx context.Context, rev uint64) error {
	eps := GetEndpoints()
	for i := range eps {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-eps[i].WaitForPolicyRevision(ctx, rev):
			if ctx.Err() != nil {
				return ctx.Err()
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Immutable (i.e. persistent) data structures
package ps

import "bytes"
import "encoding/gob"

type Any interface{}

type Map map[string]Any

// NewMap allocates a new, persistent map from strings to any value
func NewMap() Map {
    m := make(map[string]Any)
    return m
}

// Set returns a new map similar to this one but with key and value
// associated.  If the key didn't exist, it's created; otherwise, the
// associated value is changed.
func (prev Map) Set(key string, value Any) Map {
    next := prev.Clone()
    next[key] = value
    return next
}

// Delete returns a new map with the association for key, if any, removed
func (prev Map) Delete(key string) Map {
    next := prev.Clone()
    delete(next, key)
    return next
}

// Lookup returns a pair of values.  The first is the value, the second is
// true if the value exists.
func (m Map) Lookup(key string) (Any, bool) {
    v, ok := m[key]
    return v, ok
}

// Size returns the number of key value pairs in the map
func (m Map) Size() int {
    return len(m)
}

// Keys returns a slice containing all keys in this map
func (m Map) Keys() []string {
    keys := make([]string, len(m))
    i := 0
    for key := range m {
        keys[i] = key
        i++
    }
    return keys
}

// Clone deep copies `src` into `dst`
func (prev Map) Clone() Map {
    next := NewMap()
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)
    dec := gob.NewDecoder(&buf)
    enc.Encode(prev)
    dec.Decode(&next)
    return next
}
<commit_msg>Rewrite Map using a hash-balanced tree<commit_after>// Immutable (i.e. persistent) data structures
package ps

import . "fmt"

import "hash/fnv"

type Any interface{}

type Map struct {
    count   int
    hash    uint64  // hash of the key (used for tree balancing)
    key     string
    value   Any
    left    *Map
    right   *Map
}

// NewMap allocates a new, persistent map from strings to any value
func NewMap() *Map {
    var m Map
    m.count = 0  // 0 count indicates an empty tree
    return &m
}

// IsNil returns true if the Map is empty
func (self *Map) IsNil() bool {
    return self.count == 0
}

// clone returns an exact duplicate of a tree node
func (self *Map) clone() *Map {
    var m Map
    m.count = self.count
    m.hash  = self.hash
    m.key   = self.key
    m.value = self.value
    m.left  = self.left
    m.right = self.right
    return &m
}

// hashKey returns a hash code for a given string
func hashKey(key string) uint64 {
    hasher := fnv.New64()
    Fprint(hasher, key)
    return hasher.Sum64()
}

// Set returns a new map similar to this one but with key and value
// associated.  If the key didn't exist, it's created; otherwise, the
// associated value is changed.
func (self *Map) Set(key string, value Any) *Map {
    hash := hashKey(key)
    return setLowLevel(self, hash, key, value)
}

func setLowLevel(self *Map, hash uint64, key string, value Any) *Map {
    if self == nil || self.IsNil() { // an empty tree is easy
        var m Map
        m.count = 1
        m.hash  = hash
        m.key   = key
        m.value = value
        return &m
    }

    if hash < self.hash { // insert into left subtree
        m := self.clone()
        m.left = setLowLevel(self.left, hash, key, value)
        recalculateCount(m)
        return m
    }
    if hash > self.hash { // insert into right subtree
        m := self.clone()
        m.right = setLowLevel(self.right, hash, key, value)
        recalculateCount(m)
        return m
    }

    // replacing a key's previous value
    m := self.clone()
    m.value = value
    return m
}

// modifies a map by recalculating its key count based on the counts
// of its subtrees
func recalculateCount(m *Map) {
    count := 0
    if m.left != nil {
        count += m.left.Size()
    }
    if m.right != nil {
        count += m.right.Size()
    }
    m.count = count + 1 // add one to count ourself
}

// Delete returns a new map with the association for key, if any, removed
func (m *Map) Delete(key string) *Map {
    hash := hashKey(key)
    newMap, _ := deleteLowLevel(m, hash)
    return newMap
}

func deleteLowLevel(self *Map, hash uint64) (*Map, bool) {
    // empty trees are easy
    if self == nil {
        return NewMap(), false
    }
    if self.IsNil() {
        return self, false
    }

    if hash < self.hash { // look in the left subtree
        newLeft, found := deleteLowLevel(self.left, hash)
        if !found {
            return self, false
        }
        newMap := self.clone()
        newMap.left = newLeft
        recalculateCount(newMap)
    }
    if hash > self.hash { // look in the right subtree
        newRight, found := deleteLowLevel(self.right, hash)
        if !found {
            return self, false
        }
        newMap := self.clone()
        newMap.right = newRight
        recalculateCount(newMap)
    }

    // we must delete our own node
    if self.isLeaf() {  // we have no children
        return NewMap(), true
    }
    if self.subtreeCount() == 1 { // only one subtree
        if self.hasLeft() {  // it's the left one
            return self.left, true
        }
        return self.right, true  // it's the right one
    }

    // find a node to replace us
    if self.left.Size() > self.right.Size() {  // make left side smaller
        replacement, newLeft := self.left.deleteRightmost()
        newMap := replacement.clone()
        newMap.left = newLeft
        newMap.right = self.right
        recalculateCount(newMap)
        return newMap, true
    }

    // make right side smaller
    replacement, newRight := self.right.deleteLeftmost()
    newMap := replacement.clone()
    newMap.right = newRight
    newMap.left = self.left
    recalculateCount(newMap)
    return newMap, true
}

// delete the left or rightmost node in a tree returning the node that
// was deleted and the tree left over after its deletion
func (m *Map) deleteRightmost() (*Map, *Map) {
    if m.isLeaf() {
        return m, NewMap()
    }
    if m.hasRight() {
        deleted, newRight := m.right.deleteRightmost()
        newMap := m.clone()
        newMap.right = newRight
        recalculateCount(newMap)
        return deleted, newMap
    }

    deleted := m.clone()
    deleted.left = nil
    return deleted, m.left
}
func (m *Map) deleteLeftmost() (*Map, *Map) {
    if m.isLeaf() {
        return m, NewMap()
    }
    if m.hasLeft() {
        deleted, newLeft := m.left.deleteLeftmost()
        newMap := m.clone()
        newMap.left = newLeft
        recalculateCount(newMap)
        return deleted, newMap
    }

    deleted := m.clone()
    deleted.count = 1
    deleted.right = nil
    return deleted, m.right
}

// hasLeft and hasRight return true if this tree has a left or right subtree
func (m *Map) hasLeft() bool {
    return m.left != nil && !m.left.IsNil()
}
func (m *Map) hasRight() bool {
    return m.right != nil && !m.right.IsNil()
}

// isLeaf returns true if this is a leaf node
func (m *Map) isLeaf() bool {
    return m.Size() == 1
}

// returns the number of child subtrees we have
func (m *Map) subtreeCount() int {
    count := 0
    if m.hasLeft() {
        count++
    }
    if m.hasRight() {
        count++
    }
    return count
}

// Lookup returns a pair of values.  The first is the value, the second is
// true if the value exists.
func (m *Map) Lookup(key string) (Any, bool) {
    hash := hashKey(key)
    return lookupLowLevel(m, hash)
}

func lookupLowLevel(self *Map, hash uint64) (Any, bool) {
    if self == nil || self.IsNil() { // an empty tree is easy
        return nil, false
    }

    if hash < self.hash { // look in the left subtree
        return lookupLowLevel(self.left, hash)
    }
    if hash > self.hash { // look in the right subtree
        return lookupLowLevel(self.right, hash)
    }

    // we found it
    return self.value, true
}

// Size returns the number of key value pairs in the map
func (m *Map) Size() int {
    return m.count
}

// ForEach executes a callback on each key value pair in the map
func (m *Map) ForEach(f func(key string, val Any)) {
    if m.IsNil() {
        return
    }

    // left branch
    if m.left != nil {
        m.left.ForEach(f)
    }

    // ourself
    f(m.key, m.value)

    // right branch
    if m.right != nil {
        m.right.ForEach(f)
    }
}

// Keys returns a slice containing all keys in this map
func (m *Map) Keys() []string {
    keys := make([]string, m.Size())
    i := 0
    m.ForEach( func (k string, v Any) {
        keys[i] = k
        i++
    })
    return keys
}
<|endoftext|>