<commit_before>package mocks

import (
	"bytemark.co.uk/client/cmds/util"
	mock "github.com/maraino/go-mock"
)

// mock CommandSet

type Commands struct {
	mock.Mock
}

func (cmds *Commands) AddKey(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Console(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Debug(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) Delete(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) DeleteKey(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) DeleteServer(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) DeleteGroup(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) DeleteDisc(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Help(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Config(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) CreateDiscs(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) CreateGroup(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) CreateServer(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Distributions(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) HardwareProfiles(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ListAccounts(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ListDiscs(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) ListKeys(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ListGroups(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ListServers(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Reimage(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ResetServer(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ResizeDisc(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Restart(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))

}
func (cmds *Commands) Shutdown(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) Stop(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) Start(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ShowAccount(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ShowGroup(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ShowServer(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) ShowUser(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) StorageGrades(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Undelete(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) UndeleteServer(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) LockHWProfile(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) UnlockHWProfile(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) SetCores(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) SetHWProfile(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) SetMemory(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) Zones(args []string) util.ExitCode {
	r := cmds.Called(args)
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) EnsureAuth() error {
	r := cmds.Called()
	return r.Error(0)
}
func (cmds *Commands) HelpForAdd() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForConfig() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) HelpForConsole() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForCreate() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForDebug() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) HelpForDelete() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForHelp() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForList() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForLocks() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForPower() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForResize() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForSet() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}

func (cmds *Commands) HelpForShow() util.ExitCode {
	r := cmds.Called()
	return util.ExitCode(r.Int(0))
}
func (cmds *Commands) HelpForTopic(t string) util.ExitCode {
	r := cmds.Called(t)
	return util.ExitCode(r.Int(0))
}
<commit_msg>Remove obsoleted mocks.Commands<commit_after><|endoftext|>


<commit_before>package main

import (
	"log"

	"github.com/peterstace/grayt/tracer"
	"github.com/peterstace/grayt/vect"
)

func main() {

	mov := tracer.Movie{
		FrameCount: 512,
		CameraFactory: func(t float64) tracer.Camera {
			return tracer.NewRectilinearCamera(
				tracer.CameraConfig{
					Location:      vect.New(0, 10, 5),
					ViewDirection: vect.New(0, -1, -2),
					UpDirection:   vect.New(0, 1, 0),
					FieldOfView:   1.5,
					FocalLength:   10.0,
					FocalRatio:    1000.0,
				},
			)
		},
		GeometriesFactory: func(t float64) []tracer.Geometry {
			return []tracer.Geometry{
				tracer.NewPlane(vect.New(0, 0, 0), vect.New(0, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0.2, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(-0.2, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0, 1, 0.2)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0, 1, -0.2)),
				tracer.NewSphere(vect.New(0, 1, -10), 1),
			}
		},
		LightsFactory: func(t float64) []tracer.Light {
			return []tracer.Light{
				tracer.Light{Location: vect.New(0, 10, -10), Radius: 0.5, Intensity: 100},
			}
		},
	}

	if err := tracer.TraceMovie(mov, "out"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Make the sample animation change sphere size<commit_after>package main

import (
	"log"

	"github.com/peterstace/grayt/tracer"
	"github.com/peterstace/grayt/vect"
)

func main() {

	mov := tracer.Movie{
		FrameCount: 512,
		CameraFactory: func(t float64) tracer.Camera {
			return tracer.NewRectilinearCamera(
				tracer.CameraConfig{
					Location:      vect.New(0, 10, 5),
					ViewDirection: vect.New(0, -1, -2),
					UpDirection:   vect.New(0, 1, 0),
					FieldOfView:   1.5,
					FocalLength:   10.0,
					FocalRatio:    1000.0,
				},
			)
		},
		GeometriesFactory: func(t float64) []tracer.Geometry {
			return []tracer.Geometry{
				tracer.NewPlane(vect.New(0, 0, 0), vect.New(0, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0.2, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(-0.2, 1, 0)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0, 1, 0.2)),
				tracer.NewPlane(vect.New(0, -0.5, -10), vect.New(0, 1, -0.2)),
				tracer.NewSphere(vect.New(0, 1, -10), t*2),
			}
		},
		LightsFactory: func(t float64) []tracer.Light {
			return []tracer.Light{
				tracer.Light{Location: vect.New(0, 10, -10), Radius: 0.5, Intensity: 100},
			}
		},
	}

	if err := tracer.TraceMovie(mov, "out"); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package dokugen

import (
	"strings"
	"testing"
)

//This grid is #27 from Totally Pocket Sudoku. It's included for testing purposes only.

const TEST_GRID = `6|1|2|.|.|.|4|.|3
.|3|.|4|9|.|.|7|2
.|.|7|.|.|.|.|6|5
.|.|.|.|6|1|.|8|.
1|.|3|.|4|.|2|.|6
.|6|.|5|2|.|.|.|.
.|9|.|.|.|.|5|.|.
7|2|.|.|8|5|.|3|.
5|.|1|.|.|.|9|4|7`

func TestGridCreation(t *testing.T) {

	blockUpperLeftRow := make([]int, DIM)
	blockUpperLeftCol := make([]int, DIM)

	row := 0
	col := -1 * BLOCK_DIM

	for i := 0; i < DIM; i++ {
		col += BLOCK_DIM
		if col >= DIM {
			col = 0
			row += BLOCK_DIM
		}
		blockUpperLeftRow[i] = row
		blockUpperLeftCol[i] = col
	}

	cellData := "1"
	rowData := strings.Join(nCopies(cellData, DIM), COL_SEP)
	data := strings.Join(nCopies(rowData, DIM), ROW_SEP)
	grid := LoadGrid(data)
	if len(grid.cells) != DIM*DIM {
		t.Log("Didn't generate enough cells")
		t.Fail()
	}
	if grid.DataString() != data {
		t.Log("The grid round-tripped with different result than data in")
		t.Fail()
	}
	if grid.cells[10].Number() != 1 {
		t.Log("A random spot check of a cell had the wrong number: %s", grid.cells[10])
		t.Fail()
	}

	for count := 0; count < DIM; count++ {
		col := grid.Col(count)
		if num := len(col); num != DIM {
			t.Log("We got back a column but it had the wrong amount of items: ", num, "\n")
			t.Fail()
		}
		for i, cell := range col {
			if cell.Col != count {
				t.Log("One of the cells we got back when asking for column ", count, " was not in the right column.")
				t.Fail()
			}
			if cell.Row != i {
				t.Log("One of the cells we got back when asking for column ", count, " was not in the right row.")
				t.Fail()
			}
		}

		row := grid.Row(count)
		if len(row) != DIM {
			t.Log("We got back a row but it had the wrong number of items.")
			t.Fail()
		}
		for i, cell := range row {
			if cell.Row != count {
				t.Log("One of the cells we got back when asking for row ", count, " was not in the right rows.")
				t.Fail()
			}
			if cell.Col != i {
				t.Log("One of the cells we got back from row ", count, " was not in the right column.")
				t.Fail()
			}
		}

		block := grid.Block(count)
		if len(block) != DIM {
			t.Log("We got back a block but it had the wrong number of items.")
			t.Fail()
		}

		for _, cell := range block {
			if cell.Block != count {
				t.Log("We got a cell back in a block with the wrong block number")
				t.Fail()
			}
		}

		if block[0].Row != blockUpperLeftRow[count] || block[0].Col != blockUpperLeftCol[count] {
			t.Log("We got back the wrong first cell from block ", count, ": ", block[0])
			t.Fail()
		}

		if block[DIM-1].Row != blockUpperLeftRow[count]+BLOCK_DIM-1 || block[DIM-1].Col != blockUpperLeftCol[count]+BLOCK_DIM-1 {
			t.Log("We got back the wrong last cell from block ", count, ": ", block[0])
			t.Fail()
		}

	}

	cell := grid.Cell(2, 2)

	if cell.Row != 2 || cell.Col != 2 {
		t.Log("We grabbed a cell but what we got back was the wrong row and col.")
		t.Fail()
	}

	neighbors := cell.Neighbors()

	if len(neighbors) != (DIM-1)*3-(BLOCK_DIM-1)*2 {
		t.Log("We got a different number of neighbors than what we were expecting: ", len(neighbors))
		t.Fail()
	}
	neighborsMap := make(map[*Cell]bool)
	for _, neighbor := range neighbors {
		if neighbor == nil {
			t.Log("We found a nil neighbor")
			t.Fail()
		}
		if neighbor.Row != cell.Row && neighbor.Col != cell.Col && neighbor.Block != cell.Block {
			t.Log("We found a neighbor in ourselves that doesn't appear to be related: Neighbor: ", neighbor, " Cell: ", cell)
			t.Fail()
		}
		if _, ok := neighborsMap[neighbor]; ok {
			t.Log("We found a duplicate in the neighbors list")
			t.Fail()
		} else {
			neighborsMap[cell] = true
		}
	}
}

func TestGridLoad(t *testing.T) {
	grid := LoadGrid(TEST_GRID)

	cell := grid.Cell(0, 0)

	if cell.Number() != 6 {
		t.Log("The loaded grid did not have a 6 in the upper left corner")
		t.Fail()
	}

	cell = grid.Cell(DIM-1, DIM-1)

	if cell.Number() != 7 {
		t.Log("The loaded grid did not have a 7 in the bottom right corner")
		t.Fail()
	}

	if grid.DataString() != TEST_GRID {
		t.Log("The real test grid did not survive a round trip via DataString: \n", grid.DataString(), "\n\n", TEST_GRID)
		t.Fail()
	}
}

func nCopies(in string, copies int) (result []string) {
	for i := 0; i < copies; i++ {
		result = append(result, in)
	}
	return
}
<commit_msg>Added a test for whole-grid diagram printing.<commit_after>package dokugen

import (
	"strings"
	"testing"
)

//This grid is #27 from Totally Pocket Sudoku. It's included for testing purposes only.

const TEST_GRID = `6|1|2|.|.|.|4|.|3
.|3|.|4|9|.|.|7|2
.|.|7|.|.|.|.|6|5
.|.|.|.|6|1|.|8|.
1|.|3|.|4|.|2|.|6
.|6|.|5|2|.|.|.|.
.|9|.|.|.|.|5|.|.
7|2|.|.|8|5|.|3|.
5|.|1|.|.|.|9|4|7`

const TEST_GRID_DIAGRAM = `•••|•••|•••||   |   |   ||•••|   |•••
•6•|•1•|•2•||   | 5 |   ||•4•|   |•3•
•••|•••|•••||78 |7  |78 ||•••|  9|•••
---+---+---||---+---+---||---+---+---
   |•••|   ||•••|•••|   ||1  |•••|•••
   |•3•| 5 ||•4•|•9•|  6||   |•7•|•2•
 8 |•••| 8 ||•••|•••| 8 || 8 |•••|•••
---+---+---||---+---+---||---+---+---
   |   |•••||123|1 3| 23||1  |•••|•••
4  |4  |•7•||   |   |   ||   |•6•|•5•
 89| 8 |•••|| 8 |   | 8 || 8 |•••|•••
-----------++-----------++-----------
-----------++-----------++-----------
 2 |   |   ||  3|•••|•••||  3|•••|
4  |45 |45 ||   |•6•|•1•||   |•8•|4
  9|7  |  9||7 9|•••|•••||7  |•••|  9
---+---+---||---+---+---||---+---+---
•••|   |•••||   |•••|   ||•••|   |•••
•1•| 5 |•3•||   |•4•|   ||•2•| 5 |•6•
•••|78 |•••||789|•••|789||•••|  9|•••
---+---+---||---+---+---||---+---+---
   |•••|   ||•••|•••|  3||1 3|1  |1
4  |•6•|4  ||•5•|•2•|   ||   |   |4
 89|•••| 89||•••|•••|789||7  |  9|  9
-----------++-----------++-----------
-----------++-----------++-----------
  3|•••|   ||123|1 3| 23||•••|12 |1
4  |•9•|4 6||  6|   |4 6||•5•|   |
 8 |•••| 8 ||7  |7  |7  ||•••|   | 8
---+---+---||---+---+---||---+---+---
•••|•••|   ||1  |•••|•••||1  |•••|1
•7•|•2•|4 6||  6|•8•|•5•||  6|•3•|
•••|•••|   ||  9|•••|•••||   |•••|
---+---+---||---+---+---||---+---+---
•••|   |•••|| 23|  3| 23||•••|•••|•••
•5•|   |•1•||  6|   |  6||•9•|•4•|•7•
•••| 8 |•••||   |   |   ||•••|•••|•••`

func TestGridCreation(t *testing.T) {

	blockUpperLeftRow := make([]int, DIM)
	blockUpperLeftCol := make([]int, DIM)

	row := 0
	col := -1 * BLOCK_DIM

	for i := 0; i < DIM; i++ {
		col += BLOCK_DIM
		if col >= DIM {
			col = 0
			row += BLOCK_DIM
		}
		blockUpperLeftRow[i] = row
		blockUpperLeftCol[i] = col
	}

	cellData := "1"
	rowData := strings.Join(nCopies(cellData, DIM), COL_SEP)
	data := strings.Join(nCopies(rowData, DIM), ROW_SEP)
	grid := LoadGrid(data)
	if len(grid.cells) != DIM*DIM {
		t.Log("Didn't generate enough cells")
		t.Fail()
	}
	if grid.DataString() != data {
		t.Log("The grid round-tripped with different result than data in")
		t.Fail()
	}
	if grid.cells[10].Number() != 1 {
		t.Log("A random spot check of a cell had the wrong number: %s", grid.cells[10])
		t.Fail()
	}

	for count := 0; count < DIM; count++ {
		col := grid.Col(count)
		if num := len(col); num != DIM {
			t.Log("We got back a column but it had the wrong amount of items: ", num, "\n")
			t.Fail()
		}
		for i, cell := range col {
			if cell.Col != count {
				t.Log("One of the cells we got back when asking for column ", count, " was not in the right column.")
				t.Fail()
			}
			if cell.Row != i {
				t.Log("One of the cells we got back when asking for column ", count, " was not in the right row.")
				t.Fail()
			}
		}

		row := grid.Row(count)
		if len(row) != DIM {
			t.Log("We got back a row but it had the wrong number of items.")
			t.Fail()
		}
		for i, cell := range row {
			if cell.Row != count {
				t.Log("One of the cells we got back when asking for row ", count, " was not in the right rows.")
				t.Fail()
			}
			if cell.Col != i {
				t.Log("One of the cells we got back from row ", count, " was not in the right column.")
				t.Fail()
			}
		}

		block := grid.Block(count)
		if len(block) != DIM {
			t.Log("We got back a block but it had the wrong number of items.")
			t.Fail()
		}

		for _, cell := range block {
			if cell.Block != count {
				t.Log("We got a cell back in a block with the wrong block number")
				t.Fail()
			}
		}

		if block[0].Row != blockUpperLeftRow[count] || block[0].Col != blockUpperLeftCol[count] {
			t.Log("We got back the wrong first cell from block ", count, ": ", block[0])
			t.Fail()
		}

		if block[DIM-1].Row != blockUpperLeftRow[count]+BLOCK_DIM-1 || block[DIM-1].Col != blockUpperLeftCol[count]+BLOCK_DIM-1 {
			t.Log("We got back the wrong last cell from block ", count, ": ", block[0])
			t.Fail()
		}

	}

	cell := grid.Cell(2, 2)

	if cell.Row != 2 || cell.Col != 2 {
		t.Log("We grabbed a cell but what we got back was the wrong row and col.")
		t.Fail()
	}

	neighbors := cell.Neighbors()

	if len(neighbors) != (DIM-1)*3-(BLOCK_DIM-1)*2 {
		t.Log("We got a different number of neighbors than what we were expecting: ", len(neighbors))
		t.Fail()
	}
	neighborsMap := make(map[*Cell]bool)
	for _, neighbor := range neighbors {
		if neighbor == nil {
			t.Log("We found a nil neighbor")
			t.Fail()
		}
		if neighbor.Row != cell.Row && neighbor.Col != cell.Col && neighbor.Block != cell.Block {
			t.Log("We found a neighbor in ourselves that doesn't appear to be related: Neighbor: ", neighbor, " Cell: ", cell)
			t.Fail()
		}
		if _, ok := neighborsMap[neighbor]; ok {
			t.Log("We found a duplicate in the neighbors list")
			t.Fail()
		} else {
			neighborsMap[cell] = true
		}
	}
}

func TestGridLoad(t *testing.T) {
	grid := LoadGrid(TEST_GRID)

	cell := grid.Cell(0, 0)

	if cell.Number() != 6 {
		t.Log("The loaded grid did not have a 6 in the upper left corner")
		t.Fail()
	}

	cell = grid.Cell(DIM-1, DIM-1)

	if cell.Number() != 7 {
		t.Log("The loaded grid did not have a 7 in the bottom right corner")
		t.Fail()
	}

	if grid.DataString() != TEST_GRID {
		t.Log("The real test grid did not survive a round trip via DataString: \n", grid.DataString(), "\n\n", TEST_GRID)
		t.Fail()
	}

	if grid.Diagram() != TEST_GRID_DIAGRAM {
		t.Log("The grid did not match the expected diagram: \n", grid.Diagram())
		t.Fail()
	}
}

func nCopies(in string, copies int) (result []string) {
	for i := 0; i < copies; i++ {
		result = append(result, in)
	}
	return
}
<|endoftext|>


<commit_before>package view

import (
	"html/template"
	"net/http"
)

var ()

// extend safely reads the extend list.
func (c *Info) extend() template.FuncMap {
	c.extendMutex.RLock()
	list := c.extendList
	c.extendMutex.RUnlock()

	return list
}

// modify safely reads the modify list.
func (c *Info) modify() []ModifyFunc {
	// Get the setter collection
	c.modifyMutex.RLock()
	list := c.modifyList
	c.modifyMutex.RUnlock()

	return list
}

// SetTemplates will set the root and child templates.
func (c *Info) SetTemplates(rootTemp string, childTemps []string) {
	c.mutex.Lock()
	c.templateCollection = make(map[string]*template.Template)
	c.mutex.Unlock()

	c.rootTemplate = rootTemp
	c.childTemplates = childTemps
}

// ModifyFunc can modify the view before rendering.
type ModifyFunc func(http.ResponseWriter, *http.Request, *Info)

// SetModifiers will set the modifiers for the View that run
// before rendering.
func (c *Info) SetModifiers(fn ...ModifyFunc) {
	c.modifyMutex.Lock()
	c.modifyList = fn
	c.modifyMutex.Unlock()
}

// SetFuncMaps will combine all template.FuncMaps into one map and then set the
// them for each template.
// If a func already exists, it is rewritten without a warning.
func (c *Info) SetFuncMaps(fms ...template.FuncMap) {
	// Final FuncMap
	fm := make(template.FuncMap)

	// Loop through the maps
	for _, m := range fms {
		// Loop through each key and value
		for k, v := range m {
			fm[k] = v
		}
	}

	// Load the plugins
	c.extendMutex.Lock()
	c.extendList = fm
	c.extendMutex.Unlock()
}
<commit_msg>Fix plugin lint<commit_after>package view

import (
	"html/template"
	"net/http"
)

// extend safely reads the extend list.
func (c *Info) extend() template.FuncMap {
	c.extendMutex.RLock()
	list := c.extendList
	c.extendMutex.RUnlock()

	return list
}

// modify safely reads the modify list.
func (c *Info) modify() []ModifyFunc {
	// Get the setter collection
	c.modifyMutex.RLock()
	list := c.modifyList
	c.modifyMutex.RUnlock()

	return list
}

// SetTemplates will set the root and child templates.
func (c *Info) SetTemplates(rootTemp string, childTemps []string) {
	c.mutex.Lock()
	c.templateCollection = make(map[string]*template.Template)
	c.mutex.Unlock()

	c.rootTemplate = rootTemp
	c.childTemplates = childTemps
}

// ModifyFunc can modify the view before rendering.
type ModifyFunc func(http.ResponseWriter, *http.Request, *Info)

// SetModifiers will set the modifiers for the View that run
// before rendering.
func (c *Info) SetModifiers(fn ...ModifyFunc) {
	c.modifyMutex.Lock()
	c.modifyList = fn
	c.modifyMutex.Unlock()
}

// SetFuncMaps will combine all template.FuncMaps into one map and then set the
// them for each template.
// If a func already exists, it is rewritten without a warning.
func (c *Info) SetFuncMaps(fms ...template.FuncMap) {
	// Final FuncMap
	fm := make(template.FuncMap)

	// Loop through the maps
	for _, m := range fms {
		// Loop through each key and value
		for k, v := range m {
			fm[k] = v
		}
	}

	// Load the plugins
	c.extendMutex.Lock()
	c.extendList = fm
	c.extendMutex.Unlock()
}
<|endoftext|>


<commit_before>//  Copyright 2017 Walter Schulze
//
//  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 equal contains the implementation of the equal plugin, which generates the deriveEqual function.
//
// The deriveEqual function is a faster alternative to reflect.DeepEqual.
//
// When goderive walks over your code it is looking for a function that:
//  - was not implemented (or was previously derived) and
//  - has a prefix predefined prefix.
//
// In the following code the deriveEqual function will be found, because
// it was not implemented and it has a prefix deriveEqual.
// This prefix is configurable.
//
//	package main
//
//	type MyStruct struct {
// 		Int64     int64
//		StringPtr *string
//	}
//
//	func (this *MyStruct) Equal(that *MyStruct) bool {
// 		return deriveEqual(this, that)
//	}
//
// goderive will then generate the following code in a derived.gen.go file in the same package:
//
//	func deriveEqual(this, that *MyStruct) bool {
//		return (this == nil && that == nil) ||
//			this != nil && that != nil &&
//			this.Int64 == that.Int64 &&
//			((this.StringPtr == nil && that.StringPtr == nil) ||
//				(this.StringPtr != nil && that.StringPtr != nil && *(this.StringPtr) == *(that.StringPtr)))
//	}
//
// Supported types:
//	- basic types
//	- named structs
//	- slices
//	- maps
//	- pointers to these types
//	- private fields of structs in external packages (using reflect and unsafe)
//	- and many more
// Unsupported types:
//	- chan
//	- interface
//	- function
//	- unnamed structs, which are not comparable with the == operator
//
// This plugin has been tested thoroughly.
package equal

import (
	"fmt"
	"go/types"
	"strings"

	"github.com/awalterschulze/goderive/derive"
)

// NewPlugin creates a new equal plugin.
// This function returns the plugin name, default prefix and a constructor for the equal code generator.
func NewPlugin() derive.Plugin {
	return derive.NewPlugin("equal", "deriveEqual", New)
}

// New is a constructor for the equal code generator.
// This generator should be reconstructed for each package.
func New(typesMap derive.TypesMap, p derive.Printer, deps map[string]derive.Dependency) derive.Generator {
	return &gen{
		TypesMap:   typesMap,
		printer:    p,
		bytesPkg:   p.NewImport("bytes"),
		reflectPkg: p.NewImport("reflect"),
		unsafePkg:  p.NewImport("unsafe"),
	}
}

type gen struct {
	derive.TypesMap
	printer    derive.Printer
	bytesPkg   derive.Import
	reflectPkg derive.Import
	unsafePkg  derive.Import
}

func (this *gen) Add(name string, typs []types.Type) (string, error) {
	if len(typs) != 2 {
		return "", fmt.Errorf("%s does not have two arguments", name)
	}
	if !types.Identical(typs[0], typs[1]) {
		return "", fmt.Errorf("%s has two arguments, but they are of different types %s != %s",
			name, this.TypeString(typs[0]), this.TypeString(typs[1]))
	}
	return this.SetFuncName(name, typs[0])
}

func (this *gen) Generate(typs []types.Type) error {
	return this.genFunc(typs[0])
}

func (g *gen) genFunc(typ types.Type) error {
	p := g.printer
	g.Generating(typ)
	typeStr := g.TypeString(typ)
	p.P("")
	p.P("func %s(this, that %s) bool {", g.GetFuncName(typ), typeStr)
	p.In()
	if err := g.genStatement(typ, "this", "that"); err != nil {
		return nil
	}
	p.Out()
	p.P("}")
	return nil
}

func (g *gen) genStatement(typ types.Type, this, that string) error {
	p := g.printer
	switch ttyp := typ.Underlying().(type) {
	case *types.Basic:
		fieldStr, err := g.field(this, that, typ)
		if err != nil {
			return err
		}
		p.P("return " + fieldStr)
		return nil
	case *types.Pointer:
		thisref, thatref := "*"+this, "*"+that
		reftyp := ttyp.Elem()
		_, isNamed := reftyp.(*types.Named)
		strct, isStruct := reftyp.Underlying().(*types.Struct)
		if !isStruct {
			p.P("if %s == nil && %s == nil {", this, that)
			p.In()
			p.P("return true")
			p.Out()
			p.P("}")
			p.P("if %s != nil && %s != nil {", this, that)
			p.In()
			if err := g.genStatement(reftyp, thisref, thatref); err != nil {
				return err
			}
			p.Out()
			p.P("}")
			p.P("return false")
			return nil
		}
		if isNamed {
			fields := derive.Fields(g.TypesMap, strct)
			if len(fields.Fields) == 0 {
				p.P("return (%s == nil && %s == nil) || (%s != nil) && (%s != nil)", this, that, this, that)
				return nil
			}
			if fields.Reflect {
				p.P(`thisv := `+g.reflectPkg()+`.Indirect(`+g.reflectPkg()+`.ValueOf(%s))`, this)
				p.P(`thatv := `+g.reflectPkg()+`.Indirect(`+g.reflectPkg()+`.ValueOf(%s))`, that)
			}
			p.P("return (%s == nil && %s == nil) ||", this, that)
			p.In()
			p.P("%s != nil && %s != nil &&", this, that)
			for i, field := range fields.Fields {
				fieldType := field.Type
				var thisField, thatField string
				if !field.Private() {
					thisField, thatField = field.Name(this, nil), field.Name(that, nil)
				} else {
					thisField, thatField = field.Name("thisv", g.unsafePkg), field.Name("thatv", g.unsafePkg)
				}
				fieldStr, err := g.field(thisField, thatField, fieldType)
				if err != nil {
					return err
				}
				if (i + 1) != len(fields.Fields) {
					fieldStr += " &&"
				}
				if i == 0 {
					p.In()
				}
				p.P(fieldStr)
			}
			p.Out()
			p.Out()
			return nil
		}
	case *types.Struct:
		if canEqual(ttyp) {
			p.P("return %s == %s", this, that)
			return nil
		}
		if _, isNamed := typ.(*types.Named); isNamed {
			fieldStr, err := g.field("&"+this, "&"+that, types.NewPointer(ttyp))
			if err != nil {
				return err
			}
			p.P("return " + fieldStr)
			return nil
		}
	case *types.Slice:
		p.P("if %s == nil || %s == nil {", this, that)
		p.In()
		p.P("return %s == nil && %s == nil", this, that)
		p.Out()
		p.P("}")
		p.P("if len(%s) != len(%s) {", this, that)
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.P("for i := 0; i < len(%s); i++ {", this)
		p.In()
		thisElem, thatElem := wrap(this)+"[i]", wrap(that)+"[i]"
		eqStr, err := g.field(thisElem, thatElem, ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	case *types.Array:
		p.P("for i := 0; i < len(%s); i++ {", this)
		p.In()
		thisElem, thatElem := wrap(this)+"[i]", wrap(that)+"[i]"
		eqStr, err := g.field(thisElem, thatElem, ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	case *types.Map:
		p.P("if %s == nil || %s == nil {", this, that)
		p.In()
		p.P("return %s == nil && %s == nil", this, that)
		p.Out()
		p.P("}")
		p.P("if len(%s) != len(%s) {", this, that)
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.P("for k, v := range %s {", this)
		p.In()
		p.P("thatv, ok := %s[k]", wrap(that))
		p.P("if !ok {")
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		eqStr, err := g.field("v", "thatv", ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	}
	return fmt.Errorf("unsupported type: %#v", typ)
}

func not(s string) string {
	if strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		return "!" + s
	}
	return "!(" + s + ")"
}

func wrap(value string) string {
	if strings.HasPrefix(value, "*") || strings.HasPrefix(value, "&") {
		return "(" + value + ")"
	}
	return value
}

func canEqual(tt types.Type) bool {
	t := tt.Underlying()
	switch typ := t.(type) {
	case *types.Basic:
		return typ.Kind() != types.UntypedNil
	case *types.Struct:
		for i := 0; i < typ.NumFields(); i++ {
			f := typ.Field(i)
			ft := f.Type()
			if !canEqual(ft) {
				return false
			}
		}
		return true
	case *types.Array:
		return canEqual(typ.Elem())
	}
	return false
}

func equalMethodInputParam(typ *types.Named) *types.Type {
	for i := 0; i < typ.NumMethods(); i++ {
		meth := typ.Method(i)
		if meth.Name() != "Equal" {
			continue
		}
		sig, ok := meth.Type().(*types.Signature)
		if !ok {
			// impossible, but lets check anyway
			continue
		}
		if sig.Params().Len() != 1 {
			continue
		}
		res := sig.Results()
		if res.Len() != 1 {
			continue
		}
		b, ok := res.At(0).Type().(*types.Basic)
		if !ok {
			continue
		}
		if b.Kind() != types.Bool {
			continue
		}
		inputType := sig.Params().At(0).Type()
		return &inputType
	}
	return nil
}

func (this *gen) field(thisField, thatField string, fieldType types.Type) (string, error) {
	if canEqual(fieldType) {
		return fmt.Sprintf("%s == %s", thisField, thatField), nil
	}
	switch typ := fieldType.Underlying().(type) {
	case *types.Pointer:
		ref := typ.Elem()
		if named, ok := ref.(*types.Named); ok {
			inputType := equalMethodInputParam(named)
			if inputType != nil {
				ityp := *inputType
				if _, ok := ityp.(*types.Pointer); ok {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				} else if _, ok := ityp.(*types.Interface); ok {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				} else {
					// fall through to deferencing of pointers
				}
			} else {
				return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
			}
		}
		eqStr, err := this.field("*("+thisField+")", "*("+thatField+")", ref)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("((%[1]s == nil && %[2]s == nil) || (%[1]s != nil && %[2]s != nil && %[3]s))", thisField, thatField, eqStr), nil
	case *types.Array:
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Slice:
		if b, ok := typ.Elem().(*types.Basic); ok && b.Kind() == types.Byte {
			return fmt.Sprintf("%s.Equal(%s, %s)", this.bytesPkg(), thisField, thatField), nil
		}
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Map:
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Struct:
		if named, isNamed := fieldType.(*types.Named); isNamed {
			inputType := equalMethodInputParam(named)
			if inputType != nil {
				ityp := *inputType
				if _, ok := ityp.(*types.Pointer); ok {
					return fmt.Sprintf("%s.Equal(&%s)", wrap(thisField), thatField), nil
				} else if _, ok := ityp.(*types.Interface); ok {
					return fmt.Sprintf("%s.Equal(&%s)", wrap(thisField), thatField), nil
				} else {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				}
			}
		}
		return this.field("&"+thisField, "&"+thatField, types.NewPointer(fieldType))
	default: // *Chan, *Tuple, *Signature, *Interface, *types.Basic.Kind() == types.UntypedNil, *Struct
		return "", fmt.Errorf("unsupported type %#v", fieldType)
	}
}
<commit_msg>Update equal.go<commit_after>//  Copyright 2017 Walter Schulze
//
//  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 equal contains the implementation of the equal plugin, which generates the deriveEqual function.
//
// The deriveEqual function is a faster alternative to reflect.DeepEqual.
//
// When goderive walks over your code it is looking for a function that:
//  - was not implemented (or was previously derived) and
//  - has a predefined prefix.
//
// In the following code the deriveEqual function will be found, because
// it was not implemented and it has a prefix deriveEqual.
// This prefix is configurable.
//
//	package main
//
//	type MyStruct struct {
// 		Int64     int64
//		StringPtr *string
//	}
//
//	func (this *MyStruct) Equal(that *MyStruct) bool {
// 		return deriveEqual(this, that)
//	}
//
// goderive will then generate the following code in a derived.gen.go file in the same package:
//
//	func deriveEqual(this, that *MyStruct) bool {
//		return (this == nil && that == nil) ||
//			this != nil && that != nil &&
//			this.Int64 == that.Int64 &&
//			((this.StringPtr == nil && that.StringPtr == nil) ||
//				(this.StringPtr != nil && that.StringPtr != nil && *(this.StringPtr) == *(that.StringPtr)))
//	}
//
// Supported types:
//	- basic types
//	- named structs
//	- slices
//	- maps
//	- pointers to these types
//	- private fields of structs in external packages (using reflect and unsafe)
//	- and many more
// Unsupported types:
//	- chan
//	- interface
//	- function
//	- unnamed structs, which are not comparable with the == operator
//
// This plugin has been tested thoroughly.
package equal

import (
	"fmt"
	"go/types"
	"strings"

	"github.com/awalterschulze/goderive/derive"
)

// NewPlugin creates a new equal plugin.
// This function returns the plugin name, default prefix and a constructor for the equal code generator.
func NewPlugin() derive.Plugin {
	return derive.NewPlugin("equal", "deriveEqual", New)
}

// New is a constructor for the equal code generator.
// This generator should be reconstructed for each package.
func New(typesMap derive.TypesMap, p derive.Printer, deps map[string]derive.Dependency) derive.Generator {
	return &gen{
		TypesMap:   typesMap,
		printer:    p,
		bytesPkg:   p.NewImport("bytes"),
		reflectPkg: p.NewImport("reflect"),
		unsafePkg:  p.NewImport("unsafe"),
	}
}

type gen struct {
	derive.TypesMap
	printer    derive.Printer
	bytesPkg   derive.Import
	reflectPkg derive.Import
	unsafePkg  derive.Import
}

func (this *gen) Add(name string, typs []types.Type) (string, error) {
	if len(typs) != 2 {
		return "", fmt.Errorf("%s does not have two arguments", name)
	}
	if !types.Identical(typs[0], typs[1]) {
		return "", fmt.Errorf("%s has two arguments, but they are of different types %s != %s",
			name, this.TypeString(typs[0]), this.TypeString(typs[1]))
	}
	return this.SetFuncName(name, typs[0])
}

func (this *gen) Generate(typs []types.Type) error {
	return this.genFunc(typs[0])
}

func (g *gen) genFunc(typ types.Type) error {
	p := g.printer
	g.Generating(typ)
	typeStr := g.TypeString(typ)
	p.P("")
	p.P("func %s(this, that %s) bool {", g.GetFuncName(typ), typeStr)
	p.In()
	if err := g.genStatement(typ, "this", "that"); err != nil {
		return nil
	}
	p.Out()
	p.P("}")
	return nil
}

func (g *gen) genStatement(typ types.Type, this, that string) error {
	p := g.printer
	switch ttyp := typ.Underlying().(type) {
	case *types.Basic:
		fieldStr, err := g.field(this, that, typ)
		if err != nil {
			return err
		}
		p.P("return " + fieldStr)
		return nil
	case *types.Pointer:
		thisref, thatref := "*"+this, "*"+that
		reftyp := ttyp.Elem()
		_, isNamed := reftyp.(*types.Named)
		strct, isStruct := reftyp.Underlying().(*types.Struct)
		if !isStruct {
			p.P("if %s == nil && %s == nil {", this, that)
			p.In()
			p.P("return true")
			p.Out()
			p.P("}")
			p.P("if %s != nil && %s != nil {", this, that)
			p.In()
			if err := g.genStatement(reftyp, thisref, thatref); err != nil {
				return err
			}
			p.Out()
			p.P("}")
			p.P("return false")
			return nil
		}
		if isNamed {
			fields := derive.Fields(g.TypesMap, strct)
			if len(fields.Fields) == 0 {
				p.P("return (%s == nil && %s == nil) || (%s != nil) && (%s != nil)", this, that, this, that)
				return nil
			}
			if fields.Reflect {
				p.P(`thisv := `+g.reflectPkg()+`.Indirect(`+g.reflectPkg()+`.ValueOf(%s))`, this)
				p.P(`thatv := `+g.reflectPkg()+`.Indirect(`+g.reflectPkg()+`.ValueOf(%s))`, that)
			}
			p.P("return (%s == nil && %s == nil) ||", this, that)
			p.In()
			p.P("%s != nil && %s != nil &&", this, that)
			for i, field := range fields.Fields {
				fieldType := field.Type
				var thisField, thatField string
				if !field.Private() {
					thisField, thatField = field.Name(this, nil), field.Name(that, nil)
				} else {
					thisField, thatField = field.Name("thisv", g.unsafePkg), field.Name("thatv", g.unsafePkg)
				}
				fieldStr, err := g.field(thisField, thatField, fieldType)
				if err != nil {
					return err
				}
				if (i + 1) != len(fields.Fields) {
					fieldStr += " &&"
				}
				if i == 0 {
					p.In()
				}
				p.P(fieldStr)
			}
			p.Out()
			p.Out()
			return nil
		}
	case *types.Struct:
		if canEqual(ttyp) {
			p.P("return %s == %s", this, that)
			return nil
		}
		if _, isNamed := typ.(*types.Named); isNamed {
			fieldStr, err := g.field("&"+this, "&"+that, types.NewPointer(ttyp))
			if err != nil {
				return err
			}
			p.P("return " + fieldStr)
			return nil
		}
	case *types.Slice:
		p.P("if %s == nil || %s == nil {", this, that)
		p.In()
		p.P("return %s == nil && %s == nil", this, that)
		p.Out()
		p.P("}")
		p.P("if len(%s) != len(%s) {", this, that)
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.P("for i := 0; i < len(%s); i++ {", this)
		p.In()
		thisElem, thatElem := wrap(this)+"[i]", wrap(that)+"[i]"
		eqStr, err := g.field(thisElem, thatElem, ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	case *types.Array:
		p.P("for i := 0; i < len(%s); i++ {", this)
		p.In()
		thisElem, thatElem := wrap(this)+"[i]", wrap(that)+"[i]"
		eqStr, err := g.field(thisElem, thatElem, ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	case *types.Map:
		p.P("if %s == nil || %s == nil {", this, that)
		p.In()
		p.P("return %s == nil && %s == nil", this, that)
		p.Out()
		p.P("}")
		p.P("if len(%s) != len(%s) {", this, that)
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.P("for k, v := range %s {", this)
		p.In()
		p.P("thatv, ok := %s[k]", wrap(that))
		p.P("if !ok {")
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		eqStr, err := g.field("v", "thatv", ttyp.Elem())
		if err != nil {
			return err
		}
		p.P("if %s {", not(eqStr))
		p.In()
		p.P("return false")
		p.Out()
		p.P("}")
		p.Out()
		p.P("}")
		p.P("return true")
		return nil
	}
	return fmt.Errorf("unsupported type: %#v", typ)
}

func not(s string) string {
	if strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")") {
		return "!" + s
	}
	return "!(" + s + ")"
}

func wrap(value string) string {
	if strings.HasPrefix(value, "*") || strings.HasPrefix(value, "&") {
		return "(" + value + ")"
	}
	return value
}

func canEqual(tt types.Type) bool {
	t := tt.Underlying()
	switch typ := t.(type) {
	case *types.Basic:
		return typ.Kind() != types.UntypedNil
	case *types.Struct:
		for i := 0; i < typ.NumFields(); i++ {
			f := typ.Field(i)
			ft := f.Type()
			if !canEqual(ft) {
				return false
			}
		}
		return true
	case *types.Array:
		return canEqual(typ.Elem())
	}
	return false
}

func equalMethodInputParam(typ *types.Named) *types.Type {
	for i := 0; i < typ.NumMethods(); i++ {
		meth := typ.Method(i)
		if meth.Name() != "Equal" {
			continue
		}
		sig, ok := meth.Type().(*types.Signature)
		if !ok {
			// impossible, but lets check anyway
			continue
		}
		if sig.Params().Len() != 1 {
			continue
		}
		res := sig.Results()
		if res.Len() != 1 {
			continue
		}
		b, ok := res.At(0).Type().(*types.Basic)
		if !ok {
			continue
		}
		if b.Kind() != types.Bool {
			continue
		}
		inputType := sig.Params().At(0).Type()
		return &inputType
	}
	return nil
}

func (this *gen) field(thisField, thatField string, fieldType types.Type) (string, error) {
	if canEqual(fieldType) {
		return fmt.Sprintf("%s == %s", thisField, thatField), nil
	}
	switch typ := fieldType.Underlying().(type) {
	case *types.Pointer:
		ref := typ.Elem()
		if named, ok := ref.(*types.Named); ok {
			inputType := equalMethodInputParam(named)
			if inputType != nil {
				ityp := *inputType
				if _, ok := ityp.(*types.Pointer); ok {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				} else if _, ok := ityp.(*types.Interface); ok {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				} else {
					// fall through to deferencing of pointers
				}
			} else {
				return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
			}
		}
		eqStr, err := this.field("*("+thisField+")", "*("+thatField+")", ref)
		if err != nil {
			return "", err
		}
		return fmt.Sprintf("((%[1]s == nil && %[2]s == nil) || (%[1]s != nil && %[2]s != nil && %[3]s))", thisField, thatField, eqStr), nil
	case *types.Array:
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Slice:
		if b, ok := typ.Elem().(*types.Basic); ok && b.Kind() == types.Byte {
			return fmt.Sprintf("%s.Equal(%s, %s)", this.bytesPkg(), thisField, thatField), nil
		}
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Map:
		return fmt.Sprintf("%s(%s, %s)", this.GetFuncName(typ), thisField, thatField), nil
	case *types.Struct:
		if named, isNamed := fieldType.(*types.Named); isNamed {
			inputType := equalMethodInputParam(named)
			if inputType != nil {
				ityp := *inputType
				if _, ok := ityp.(*types.Pointer); ok {
					return fmt.Sprintf("%s.Equal(&%s)", wrap(thisField), thatField), nil
				} else if _, ok := ityp.(*types.Interface); ok {
					return fmt.Sprintf("%s.Equal(&%s)", wrap(thisField), thatField), nil
				} else {
					return fmt.Sprintf("%s.Equal(%s)", wrap(thisField), thatField), nil
				}
			}
		}
		return this.field("&"+thisField, "&"+thatField, types.NewPointer(fieldType))
	default: // *Chan, *Tuple, *Signature, *Interface, *types.Basic.Kind() == types.UntypedNil, *Struct
		return "", fmt.Errorf("unsupported type %#v", fieldType)
	}
}
<|endoftext|>


<commit_before>package doh

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"

	"github.com/miekg/dns"
)

// MimeType is the DoH mimetype that should be used.
const MimeType = "application/dns-message"

// Path is the URL path that should be used.
const Path = "/dns-query"

// NewRequest returns a new DoH request given a method, URL (without any paths, so exclude /dns-query) and dns.Msg.
func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) {
	buf, err := m.Pack()
	if err != nil {
		return nil, err
	}

	switch method {
	case http.MethodGet:
		b64 := base64.RawURLEncoding.EncodeToString(buf)

		req, err := http.NewRequest(http.MethodGet, "https://"+url+Path+"?dns="+b64, nil)
		if err != nil {
			return req, err
		}

		req.Header.Set("content-type", MimeType)
		req.Header.Set("accept", MimeType)
		return req, nil

	case http.MethodPost:
		req, err := http.NewRequest(http.MethodPost, "https://"+url+Path+"?bla=foo:443", bytes.NewReader(buf))
		if err != nil {
			return req, err
		}

		req.Header.Set("content-type", MimeType)
		req.Header.Set("accept", MimeType)
		return req, nil

	default:
		return nil, fmt.Errorf("method not allowed: %s", method)
	}
}

// ResponseToMsg converts a http.Response to a dns message.
func ResponseToMsg(resp *http.Response) (*dns.Msg, error) {
	defer resp.Body.Close()

	return toMsg(resp.Body)
}

// RequestToMsg converts a http.Request to a dns message.
func RequestToMsg(req *http.Request) (*dns.Msg, error) {
	switch req.Method {
	case http.MethodGet:
		return requestToMsgGet(req)

	case http.MethodPost:
		return requestToMsgPost(req)

	default:
		return nil, fmt.Errorf("method not allowed: %s", req.Method)
	}
}

// requestToMsgPost extracts the dns message from the request body.
func requestToMsgPost(req *http.Request) (*dns.Msg, error) {
	defer req.Body.Close()
	return toMsg(req.Body)
}

// requestToMsgGet extract the dns message from the GET request.
func requestToMsgGet(req *http.Request) (*dns.Msg, error) {
	values := req.URL.Query()
	b64, ok := values["dns"]
	if !ok {
		return nil, fmt.Errorf("no 'dns' query parameter found")
	}
	if len(b64) != 1 {
		return nil, fmt.Errorf("multiple 'dns' query values found")
	}
	return base64ToMsg(b64[0])
}

func toMsg(r io.ReadCloser) (*dns.Msg, error) {
	buf, err := io.ReadAll(io.LimitReader(r, 65536))
	if err != nil {
		return nil, err
	}
	m := new(dns.Msg)
	err = m.Unpack(buf)
	return m, err
}

func base64ToMsg(b64 string) (*dns.Msg, error) {
	buf, err := b64Enc.DecodeString(b64)
	if err != nil {
		return nil, err
	}

	m := new(dns.Msg)
	err = m.Unpack(buf)

	return m, err
}

var b64Enc = base64.RawURLEncoding
<commit_msg>Replace io.LimitReader with http.MaxBytesReader (#5241)<commit_after>package doh

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"

	"github.com/miekg/dns"
)

// MimeType is the DoH mimetype that should be used.
const MimeType = "application/dns-message"

// Path is the URL path that should be used.
const Path = "/dns-query"

// NewRequest returns a new DoH request given a method, URL (without any paths, so exclude /dns-query) and dns.Msg.
func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) {
	buf, err := m.Pack()
	if err != nil {
		return nil, err
	}

	switch method {
	case http.MethodGet:
		b64 := base64.RawURLEncoding.EncodeToString(buf)

		req, err := http.NewRequest(http.MethodGet, "https://"+url+Path+"?dns="+b64, nil)
		if err != nil {
			return req, err
		}

		req.Header.Set("content-type", MimeType)
		req.Header.Set("accept", MimeType)
		return req, nil

	case http.MethodPost:
		req, err := http.NewRequest(http.MethodPost, "https://"+url+Path+"?bla=foo:443", bytes.NewReader(buf))
		if err != nil {
			return req, err
		}

		req.Header.Set("content-type", MimeType)
		req.Header.Set("accept", MimeType)
		return req, nil

	default:
		return nil, fmt.Errorf("method not allowed: %s", method)
	}
}

// ResponseToMsg converts a http.Response to a dns message.
func ResponseToMsg(resp *http.Response) (*dns.Msg, error) {
	defer resp.Body.Close()

	return toMsg(resp.Body)
}

// RequestToMsg converts a http.Request to a dns message.
func RequestToMsg(req *http.Request) (*dns.Msg, error) {
	switch req.Method {
	case http.MethodGet:
		return requestToMsgGet(req)

	case http.MethodPost:
		return requestToMsgPost(req)

	default:
		return nil, fmt.Errorf("method not allowed: %s", req.Method)
	}
}

// requestToMsgPost extracts the dns message from the request body.
func requestToMsgPost(req *http.Request) (*dns.Msg, error) {
	defer req.Body.Close()
	return toMsg(req.Body)
}

// requestToMsgGet extract the dns message from the GET request.
func requestToMsgGet(req *http.Request) (*dns.Msg, error) {
	values := req.URL.Query()
	b64, ok := values["dns"]
	if !ok {
		return nil, fmt.Errorf("no 'dns' query parameter found")
	}
	if len(b64) != 1 {
		return nil, fmt.Errorf("multiple 'dns' query values found")
	}
	return base64ToMsg(b64[0])
}

func toMsg(r io.ReadCloser) (*dns.Msg, error) {
	buf, err := io.ReadAll(http.MaxBytesReader(nil, r, 65536))
	if err != nil {
		return nil, err
	}
	m := new(dns.Msg)
	err = m.Unpack(buf)
	return m, err
}

func base64ToMsg(b64 string) (*dns.Msg, error) {
	buf, err := b64Enc.DecodeString(b64)
	if err != nil {
		return nil, err
	}

	m := new(dns.Msg)
	err = m.Unpack(buf)

	return m, err
}

var b64Enc = base64.RawURLEncoding
<|endoftext|>


<commit_before>package store

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

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/docker/pkg/plugingetter"
	"github.com/docker/docker/pkg/plugins"
	"github.com/docker/docker/plugin/v2"
	"github.com/docker/docker/reference"
)

/* allowV1PluginsFallback determines daemon's support for V1 plugins.
 * When the time comes to remove support for V1 plugins, flipping
 * this bool is all that will be needed.
 */
const allowV1PluginsFallback bool = true

/* defaultAPIVersion is the version of the plugin API for volume, network,
   IPAM and authz. This is a very stable API. When we update this API, then
   pluginType should include a version. eg "networkdriver/2.0".
*/
const defaultAPIVersion string = "1.0"

// ErrNotFound indicates that a plugin was not found locally.
type ErrNotFound string

func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }

// GetByName retreives a plugin by name.
func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
	ps.RLock()
	defer ps.RUnlock()

	id, nameOk := ps.nameToID[name]
	if !nameOk {
		return nil, ErrNotFound(name)
	}

	p, idOk := ps.plugins[id]
	if !idOk {
		return nil, ErrNotFound(id)
	}
	return p, nil
}

// GetByID retreives a plugin by ID.
func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
	ps.RLock()
	defer ps.RUnlock()

	p, idOk := ps.plugins[id]
	if !idOk {
		return nil, ErrNotFound(id)
	}
	return p, nil
}

// GetAll retreives all plugins.
func (ps *Store) GetAll() map[string]*v2.Plugin {
	ps.RLock()
	defer ps.RUnlock()
	return ps.plugins
}

// SetAll initialized plugins during daemon restore.
func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
	ps.Lock()
	defer ps.Unlock()
	ps.plugins = plugins
}

func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
	ps.RLock()
	defer ps.RUnlock()

	result := make([]plugingetter.CompatPlugin, 0, 1)
	for _, p := range ps.plugins {
		if _, err := p.FilterByCap(capability); err == nil {
			result = append(result, p)
		}
	}
	return result
}

// SetState sets the active state of the plugin and updates plugindb.
func (ps *Store) SetState(p *v2.Plugin, state bool) {
	ps.Lock()
	defer ps.Unlock()

	p.PluginObj.Enabled = state
	ps.updatePluginDB()
}

// Add adds a plugin to memory and plugindb.
func (ps *Store) Add(p *v2.Plugin) {
	ps.Lock()
	ps.plugins[p.GetID()] = p
	ps.nameToID[p.Name()] = p.GetID()
	ps.updatePluginDB()
	ps.Unlock()
}

// Remove removes a plugin from memory and plugindb.
func (ps *Store) Remove(p *v2.Plugin) {
	ps.Lock()
	delete(ps.plugins, p.GetID())
	delete(ps.nameToID, p.Name())
	ps.updatePluginDB()
	ps.Unlock()
}

// Callers are expected to hold the store lock.
func (ps *Store) updatePluginDB() error {
	jsonData, err := json.Marshal(ps.plugins)
	if err != nil {
		logrus.Debugf("Error in json.Marshal: %v", err)
		return err
	}
	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
	return nil
}

// Get returns a plugin matching the given name and capability.
func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
	var (
		p   *v2.Plugin
		err error
	)

	// Lookup using new model.
	if ps != nil {
		fullName := name
		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
			if reference.IsNameOnly(named) {
				named = reference.WithDefaultTag(named)
			}
			ref, ok := named.(reference.NamedTagged)
			if !ok {
				return nil, fmt.Errorf("invalid name: %s", named.String())
			}
			fullName = ref.String()
		}
		p, err = ps.GetByName(fullName)
		if err == nil {
			p.Lock()
			p.RefCount += mode
			p.Unlock()
			return p.FilterByCap(capability)
		}
		if _, ok := err.(ErrNotFound); !ok {
			return nil, err
		}
	}

	// Lookup using legacy model.
	if allowV1PluginsFallback {
		p, err := plugins.Get(name, capability)
		if err != nil {
			return nil, fmt.Errorf("legacy plugin: %v", err)
		}
		return p, nil
	}

	return nil, err
}

// GetAllByCap returns a list of plugins matching the given capability.
func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
	result := make([]plugingetter.CompatPlugin, 0, 1)

	/* Daemon start always calls plugin.Init thereby initializing a store.
	 * So store on experimental builds can never be nil, even while
	 * handling legacy plugins. However, there are legacy plugin unit
	 * tests where the volume subsystem directly talks with the plugin,
	 * bypassing the daemon. For such tests, this check is necessary.
	 */
	if ps != nil {
		ps.RLock()
		result = ps.getAllByCap(capability)
		ps.RUnlock()
	}

	// Lookup with legacy model
	if allowV1PluginsFallback {
		pl, err := plugins.GetAll(capability)
		if err != nil {
			return nil, fmt.Errorf("legacy plugin: %v", err)
		}
		for _, p := range pl {
			result = append(result, p)
		}
	}
	return result, nil
}

// Handle sets a callback for a given capability. It is only used by network
// and ipam drivers during plugin registration. The callback registers the
// driver with the subsystem (network, ipam).
func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
	pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)

	// Register callback with new plugin model.
	ps.Lock()
	handlers, ok := ps.handlers[pluginType]
	if !ok {
		handlers = []func(string, *plugins.Client){}
	}
	handlers = append(handlers, callback)
	ps.handlers[pluginType] = handlers
	ps.Unlock()

	// Register callback with legacy plugin model.
	if allowV1PluginsFallback {
		plugins.Handle(capability, callback)
	}
}

// CallHandler calls the registered callback. It is invoked during plugin enable.
func (ps *Store) CallHandler(p *v2.Plugin) {
	for _, typ := range p.GetTypes() {
		for _, handler := range ps.handlers[typ.String()] {
			handler(p.Name(), p.Client())
		}
	}
}
<commit_msg>prevent plugin when volume plugin is disabled<commit_after>package store

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

	"github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/ioutils"
	"github.com/docker/docker/pkg/plugingetter"
	"github.com/docker/docker/pkg/plugins"
	"github.com/docker/docker/plugin/v2"
	"github.com/docker/docker/reference"
)

/* allowV1PluginsFallback determines daemon's support for V1 plugins.
 * When the time comes to remove support for V1 plugins, flipping
 * this bool is all that will be needed.
 */
const allowV1PluginsFallback bool = true

/* defaultAPIVersion is the version of the plugin API for volume, network,
   IPAM and authz. This is a very stable API. When we update this API, then
   pluginType should include a version. eg "networkdriver/2.0".
*/
const defaultAPIVersion string = "1.0"

// ErrNotFound indicates that a plugin was not found locally.
type ErrNotFound string

func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }

// GetByName retreives a plugin by name.
func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
	ps.RLock()
	defer ps.RUnlock()

	id, nameOk := ps.nameToID[name]
	if !nameOk {
		return nil, ErrNotFound(name)
	}

	p, idOk := ps.plugins[id]
	if !idOk {
		return nil, ErrNotFound(id)
	}
	return p, nil
}

// GetByID retreives a plugin by ID.
func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
	ps.RLock()
	defer ps.RUnlock()

	p, idOk := ps.plugins[id]
	if !idOk {
		return nil, ErrNotFound(id)
	}
	return p, nil
}

// GetAll retreives all plugins.
func (ps *Store) GetAll() map[string]*v2.Plugin {
	ps.RLock()
	defer ps.RUnlock()
	return ps.plugins
}

// SetAll initialized plugins during daemon restore.
func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
	ps.Lock()
	defer ps.Unlock()
	ps.plugins = plugins
}

func (ps *Store) getAllByCap(capability string) []plugingetter.CompatPlugin {
	ps.RLock()
	defer ps.RUnlock()

	result := make([]plugingetter.CompatPlugin, 0, 1)
	for _, p := range ps.plugins {
		if p.IsEnabled() {
			if _, err := p.FilterByCap(capability); err == nil {
				result = append(result, p)
			}
		}
	}
	return result
}

// SetState sets the active state of the plugin and updates plugindb.
func (ps *Store) SetState(p *v2.Plugin, state bool) {
	ps.Lock()
	defer ps.Unlock()

	p.PluginObj.Enabled = state
	ps.updatePluginDB()
}

// Add adds a plugin to memory and plugindb.
func (ps *Store) Add(p *v2.Plugin) {
	ps.Lock()
	ps.plugins[p.GetID()] = p
	ps.nameToID[p.Name()] = p.GetID()
	ps.updatePluginDB()
	ps.Unlock()
}

// Remove removes a plugin from memory and plugindb.
func (ps *Store) Remove(p *v2.Plugin) {
	ps.Lock()
	delete(ps.plugins, p.GetID())
	delete(ps.nameToID, p.Name())
	ps.updatePluginDB()
	ps.Unlock()
}

// Callers are expected to hold the store lock.
func (ps *Store) updatePluginDB() error {
	jsonData, err := json.Marshal(ps.plugins)
	if err != nil {
		logrus.Debugf("Error in json.Marshal: %v", err)
		return err
	}
	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
	return nil
}

// Get returns a plugin matching the given name and capability.
func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlugin, error) {
	var (
		p   *v2.Plugin
		err error
	)

	// Lookup using new model.
	if ps != nil {
		fullName := name
		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
			if reference.IsNameOnly(named) {
				named = reference.WithDefaultTag(named)
			}
			ref, ok := named.(reference.NamedTagged)
			if !ok {
				return nil, fmt.Errorf("invalid name: %s", named.String())
			}
			fullName = ref.String()
		}
		p, err = ps.GetByName(fullName)
		if err == nil {
			p.Lock()
			p.RefCount += mode
			p.Unlock()
			return p.FilterByCap(capability)
		}
		if _, ok := err.(ErrNotFound); !ok {
			return nil, err
		}
	}

	// Lookup using legacy model.
	if allowV1PluginsFallback {
		p, err := plugins.Get(name, capability)
		if err != nil {
			return nil, fmt.Errorf("legacy plugin: %v", err)
		}
		return p, nil
	}

	return nil, err
}

// GetAllByCap returns a list of plugins matching the given capability.
func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
	result := make([]plugingetter.CompatPlugin, 0, 1)

	/* Daemon start always calls plugin.Init thereby initializing a store.
	 * So store on experimental builds can never be nil, even while
	 * handling legacy plugins. However, there are legacy plugin unit
	 * tests where the volume subsystem directly talks with the plugin,
	 * bypassing the daemon. For such tests, this check is necessary.
	 */
	if ps != nil {
		ps.RLock()
		result = ps.getAllByCap(capability)
		ps.RUnlock()
	}

	// Lookup with legacy model
	if allowV1PluginsFallback {
		pl, err := plugins.GetAll(capability)
		if err != nil {
			return nil, fmt.Errorf("legacy plugin: %v", err)
		}
		for _, p := range pl {
			result = append(result, p)
		}
	}
	return result, nil
}

// Handle sets a callback for a given capability. It is only used by network
// and ipam drivers during plugin registration. The callback registers the
// driver with the subsystem (network, ipam).
func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
	pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)

	// Register callback with new plugin model.
	ps.Lock()
	handlers, ok := ps.handlers[pluginType]
	if !ok {
		handlers = []func(string, *plugins.Client){}
	}
	handlers = append(handlers, callback)
	ps.handlers[pluginType] = handlers
	ps.Unlock()

	// Register callback with legacy plugin model.
	if allowV1PluginsFallback {
		plugins.Handle(capability, callback)
	}
}

// CallHandler calls the registered callback. It is invoked during plugin enable.
func (ps *Store) CallHandler(p *v2.Plugin) {
	for _, typ := range p.GetTypes() {
		for _, handler := range ps.handlers[typ.String()] {
			handler(p.Name(), p.Client())
		}
	}
}
<|endoftext|>


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

// Test mutator availability and end-to-end latency of an RPC-like system.
package main

// TODO: This benchmark doesn't target a particular aspect of the GC
// like other gcbench benchmarks do. It would be nice if we just had
// composable sets of metrics that a benchmark could opt in to without
// needing a whole "framework", but I'm not sure how to do that with
// the gctrace on stderr thing. Maybe we could dup our own FD over
// stderr to read our own gctrace? That's dangerous if something goes
// wrong since you'll lose the panic. We could pipe it by way of an
// external program that could report if something goes wrong (e.g.,
// tee if you just want to see the whole log). This might also make it
// easier to ignore initialization and warm-up in the GC log.

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"sync"
	"sync/atomic"
	"time"

	"github.com/aclements/go-gcbench/gcbench"
	"golang.org/x/benchmarks/gcbench.newer/heapgen"
)

var (
	flagDuration   = flag.Duration("benchtime", 10*time.Second, "steady state duration")
	flagBallast    = gcbench.FlagBytes("ballast", 64*gcbench.MB, "retain `x` bytes of global data")
	flagReqsPerSec = flag.Float64("reqs-per-sec", 10000, "send `rate` requests per second")
	flagClient     = flag.String("client", "", "internal flag to act as RPC client")
)

func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		flag.Usage()
		os.Exit(2)
	}

	// TODO: Report end-to-end latency measured by the client as a
	// metric.

	if *flagClient != "" {
		doClient(*flagClient)
		return
	}

	name := "RPC"
	gcbench.NewBenchmark(name, benchMain).Config("reqs-per-sec", *flagReqsPerSec).Config("ballast", *flagBallast).Run()
}

var sink1 interface{}
var requestCount int64

func benchMain() {
	// Create the ballast.
	m := heapgen.Measure(heapgen.MakeAST)
	sink1 = heapgen.Generate(m.Gen, m.BytesRetained, int(*flagBallast))

	// Start the server.
	l := startServer()

	// Start the client.
	client := exec.Command(os.Args[0], "-client", l.Addr().String(), "-reqs-per-sec", fmt.Sprint(*flagReqsPerSec))
	client.Env = []string{}
	cin, err := client.StdinPipe()
	if err != nil {
		log.Fatal("creating client stdin pipe: ", err)
	}
	defer cin.Close()
	cout, err := client.StdoutPipe()
	if err != nil {
		log.Fatal("creating client stdout pipe: ", err)
	}
	defer cout.Close()
	client.Stderr = os.Stderr
	err = client.Start()
	if err != nil {
		log.Fatal("starting client: ", err)
	}

	// Run the server.
	done := make(chan bool)
	startTime := time.Now()
	time.AfterFunc(*flagDuration, func() {
		gcbench.ReportExtra("reqs/sec", float64(atomic.LoadInt64(&requestCount))/float64(time.Since(startTime))*float64(time.Second))

		// Shut down client.
		cin.Close()
		err := client.Wait()
		if err != nil {
			log.Fatal("client failed: ", err)
		}
		close(done)
	})
	go func() {
		for {
			c, err := l.Accept()
			if err != nil {
				log.Fatal("accept: ", err)
			}
			go handler(c)
		}
	}()
	<-done
}

func startServer() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
			log.Fatalf("failed to listen: %v", err)
		}
	}
	return l
}

func handler(c net.Conn) {
	var header [1]byte
	r, w := bufio.NewReader(c), bufio.NewWriter(c)
	d := json.NewDecoder(r)
	for {
		// Read the request.
		n, err := r.Read(header[:])
		if err == io.EOF {
			c.Close()
			return
		}
		if n != 1 || header[0] != 'x' {
			log.Fatal("bad header: ", n, err, header)
		}

		var data interface{}
		err = d.Decode(&data)
		if err != nil {
			log.Fatal("decoding request: ", err)
		}

		atomic.AddInt64(&requestCount, 1)

		// Send the response.
		json.NewEncoder(w).Encode(data)
		w.Flush()
	}
}

func doClient(addr string) {
	var wg sync.WaitGroup
	stop := make(chan bool)

	var lat gcbench.LatencyDist

	// Wait for stdin to close, then shut down cleanly.
	go func() {
		var buf [1024]byte
		for {
			n, err := os.Stdin.Read(buf[:])
			if n <= 0 || err != nil {
				break
			}
		}
		log.Print("cleaning up client")
		close(stop)
		wg.Wait()
		log.Print("closing ", len(conns.q), " connections")
		for _, c := range conns.q {
			c.Close()
		}
		lat.Fprint(os.Stderr)
		//log.Print("client exiting")
		os.Exit(0)
	}()

	// Generate a Poisson process by spacing events out by an
	// exponential distribution and using open-loop control. To
	// minimize the bottleneck in the coordinator, we shard this
	// process across several goroutines.
	const shards = 16
	reqsPerNSPerShard := *flagReqsPerSec / 1e9 / shards
	for i := 0; i < shards; i++ {
		wg.Add(1)
		go func() {
		loop:
			for {
				wait := rand.ExpFloat64() / reqsPerNSPerShard
				select {
				case <-stop:
					break loop
				case <-time.After(time.Duration(wait)):
				}
				wg.Add(1)
				go sendOneRequest(addr, &wg, &lat)
			}
			wg.Done()
		}()
	}
	select {}
}

var reqBytes = []byte(`x{
	"Dir": "src/runtime",
	"ImportPath": "runtime",
	"Name": "runtime",
	"Doc": "Package runtime contains operations that interact with Go's runtime system, such as functions to control goroutines.",
	"Target": "pkg/linux_amd64/runtime.a",
	"Goroot": true,
	"Standard": true,
	"Stale": true,
	"StaleReason": "stale dependency",
	"Root": "go.dev"
}`)

var conns struct {
	q []net.Conn
	sync.Mutex
}

func sendOneRequest(addr string, wg *sync.WaitGroup, lat *gcbench.LatencyDist) {
	defer wg.Done()

	var c net.Conn
	conns.Lock()
	if len(conns.q) > 0 {
		c = conns.q[len(conns.q)-1]
		conns.q = conns.q[:len(conns.q)-1]
	}
	conns.Unlock()
	if c == nil {
		var err error
		c, err = net.Dial("tcp", addr)
		if err != nil {
			// This could just mean the server is
			// overloaded. (TODO: Maybe it's this single
			// Accept that gets overloaded?)
			log.Print("dialing ", addr, ": ", err)
			return
		}
	}

	// Send the request.
	//log.Print("sending request", c)
	lt := lat.Start()
	n, err := c.Write(reqBytes)
	if err != nil {
		log.Fatal("writing request: ", err)
	}
	if n != len(reqBytes) {
		log.Fatal("short write")
	}
	//log.Print("sent request; reading response", c)

	// Read the response.
	var buf [1024]byte
	var done bool
	for !done {
		n, err := c.Read(buf[:])
		if bytes.IndexRune(buf[:n], '\n') >= 0 {
			done = true
		}
		if err == io.EOF {
			c.Close()
			return
		}
		if err != nil {
			log.Fatal("reading response: ", err)
		}
	}
	lt.Tick()
	//log.Print("read response", c)

	conns.Lock()
	conns.q = append(conns.q, c)
	conns.Unlock()
}
<commit_msg>gcbench/progs: print header for request latency<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test mutator availability and end-to-end latency of an RPC-like system.
package main

// TODO: This benchmark doesn't target a particular aspect of the GC
// like other gcbench benchmarks do. It would be nice if we just had
// composable sets of metrics that a benchmark could opt in to without
// needing a whole "framework", but I'm not sure how to do that with
// the gctrace on stderr thing. Maybe we could dup our own FD over
// stderr to read our own gctrace? That's dangerous if something goes
// wrong since you'll lose the panic. We could pipe it by way of an
// external program that could report if something goes wrong (e.g.,
// tee if you just want to see the whole log). This might also make it
// easier to ignore initialization and warm-up in the GC log.

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"sync"
	"sync/atomic"
	"time"

	"github.com/aclements/go-gcbench/gcbench"
	"golang.org/x/benchmarks/gcbench.newer/heapgen"
)

var (
	flagDuration   = flag.Duration("benchtime", 10*time.Second, "steady state duration")
	flagBallast    = gcbench.FlagBytes("ballast", 64*gcbench.MB, "retain `x` bytes of global data")
	flagReqsPerSec = flag.Float64("reqs-per-sec", 10000, "send `rate` requests per second")
	flagClient     = flag.String("client", "", "internal flag to act as RPC client")
)

func main() {
	flag.Parse()
	if flag.NArg() > 0 {
		flag.Usage()
		os.Exit(2)
	}

	// TODO: Report end-to-end latency measured by the client as a
	// metric.

	if *flagClient != "" {
		doClient(*flagClient)
		return
	}

	name := "RPC"
	gcbench.NewBenchmark(name, benchMain).Config("reqs-per-sec", *flagReqsPerSec).Config("ballast", *flagBallast).Run()
}

var sink1 interface{}
var requestCount int64

func benchMain() {
	// Create the ballast.
	m := heapgen.Measure(heapgen.MakeAST)
	sink1 = heapgen.Generate(m.Gen, m.BytesRetained, int(*flagBallast))

	// Start the server.
	l := startServer()

	// Start the client.
	client := exec.Command(os.Args[0], "-client", l.Addr().String(), "-reqs-per-sec", fmt.Sprint(*flagReqsPerSec))
	client.Env = []string{}
	cin, err := client.StdinPipe()
	if err != nil {
		log.Fatal("creating client stdin pipe: ", err)
	}
	defer cin.Close()
	cout, err := client.StdoutPipe()
	if err != nil {
		log.Fatal("creating client stdout pipe: ", err)
	}
	defer cout.Close()
	client.Stderr = os.Stderr
	err = client.Start()
	if err != nil {
		log.Fatal("starting client: ", err)
	}

	// Run the server.
	done := make(chan bool)
	startTime := time.Now()
	time.AfterFunc(*flagDuration, func() {
		gcbench.ReportExtra("reqs/sec", float64(atomic.LoadInt64(&requestCount))/float64(time.Since(startTime))*float64(time.Second))

		// Shut down client.
		cin.Close()
		err := client.Wait()
		if err != nil {
			log.Fatal("client failed: ", err)
		}
		close(done)
	})
	go func() {
		for {
			c, err := l.Accept()
			if err != nil {
				log.Fatal("accept: ", err)
			}
			go handler(c)
		}
	}()
	<-done
}

func startServer() net.Listener {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
			log.Fatalf("failed to listen: %v", err)
		}
	}
	return l
}

func handler(c net.Conn) {
	var header [1]byte
	r, w := bufio.NewReader(c), bufio.NewWriter(c)
	d := json.NewDecoder(r)
	for {
		// Read the request.
		n, err := r.Read(header[:])
		if err == io.EOF {
			c.Close()
			return
		}
		if n != 1 || header[0] != 'x' {
			log.Fatal("bad header: ", n, err, header)
		}

		var data interface{}
		err = d.Decode(&data)
		if err != nil {
			log.Fatal("decoding request: ", err)
		}

		atomic.AddInt64(&requestCount, 1)

		// Send the response.
		json.NewEncoder(w).Encode(data)
		w.Flush()
	}
}

func doClient(addr string) {
	var wg sync.WaitGroup
	stop := make(chan bool)

	var lat gcbench.LatencyDist

	// Wait for stdin to close, then shut down cleanly.
	go func() {
		var buf [1024]byte
		for {
			n, err := os.Stdin.Read(buf[:])
			if n <= 0 || err != nil {
				break
			}
		}
		log.Print("cleaning up client")
		close(stop)
		wg.Wait()
		log.Print("closing ", len(conns.q), " connections")
		for _, c := range conns.q {
			c.Close()
		}
		fmt.Fprintln(os.Stderr, "client-measured request latency:")
		lat.Fprint(os.Stderr)
		//log.Print("client exiting")
		os.Exit(0)
	}()

	// Generate a Poisson process by spacing events out by an
	// exponential distribution and using open-loop control. To
	// minimize the bottleneck in the coordinator, we shard this
	// process across several goroutines.
	const shards = 16
	reqsPerNSPerShard := *flagReqsPerSec / 1e9 / shards
	for i := 0; i < shards; i++ {
		wg.Add(1)
		go func() {
		loop:
			for {
				wait := rand.ExpFloat64() / reqsPerNSPerShard
				select {
				case <-stop:
					break loop
				case <-time.After(time.Duration(wait)):
				}
				wg.Add(1)
				go sendOneRequest(addr, &wg, &lat)
			}
			wg.Done()
		}()
	}
	select {}
}

var reqBytes = []byte(`x{
	"Dir": "src/runtime",
	"ImportPath": "runtime",
	"Name": "runtime",
	"Doc": "Package runtime contains operations that interact with Go's runtime system, such as functions to control goroutines.",
	"Target": "pkg/linux_amd64/runtime.a",
	"Goroot": true,
	"Standard": true,
	"Stale": true,
	"StaleReason": "stale dependency",
	"Root": "go.dev"
}`)

var conns struct {
	q []net.Conn
	sync.Mutex
}

func sendOneRequest(addr string, wg *sync.WaitGroup, lat *gcbench.LatencyDist) {
	defer wg.Done()

	var c net.Conn
	conns.Lock()
	if len(conns.q) > 0 {
		c = conns.q[len(conns.q)-1]
		conns.q = conns.q[:len(conns.q)-1]
	}
	conns.Unlock()
	if c == nil {
		var err error
		c, err = net.Dial("tcp", addr)
		if err != nil {
			// This could just mean the server is
			// overloaded. (TODO: Maybe it's this single
			// Accept that gets overloaded?)
			log.Print("dialing ", addr, ": ", err)
			return
		}
	}

	// Send the request.
	//log.Print("sending request", c)
	lt := lat.Start()
	n, err := c.Write(reqBytes)
	if err != nil {
		log.Fatal("writing request: ", err)
	}
	if n != len(reqBytes) {
		log.Fatal("short write")
	}
	//log.Print("sent request; reading response", c)

	// Read the response.
	var buf [1024]byte
	var done bool
	for !done {
		n, err := c.Read(buf[:])
		if bytes.IndexRune(buf[:n], '\n') >= 0 {
			done = true
		}
		if err == io.EOF {
			c.Close()
			return
		}
		if err != nil {
			log.Fatal("reading response: ", err)
		}
	}
	lt.Tick()
	//log.Print("read response", c)

	conns.Lock()
	conns.q = append(conns.q, c)
	conns.Unlock()
}
<|endoftext|>


<commit_before>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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.
 */

// Architecture
// ============
//
// Policy plugin is split into multiple layers with the data moving
// in the direction from the up to the bottom. Each layer obtains policy-related
// data from the layer above and outputs them processed in some way into
// the layer below. With each layer the abstraction level decreases until it
// reaches the format of policies used by the target network stack.
//
// On the top there are K8s state data as reflected into ETCD by KSR.
// On the very bottom there is a virtual switch into which the policies
// are rendered. Layers in-between perform policy processing with the assistance
// of in-memory caches.
//
// Layers
// ------
//
//	1. Policy Plugin:
//     - implements the Plugin interface for CN-Infra
//     - initializes caches and all the layers, performs dependency injection
//     - watches ETCD for changes written by KSR
//     - propagates datasync events into the Policy Cache without any processing
//     - postpones RESYNC until the Contiv plugin has finalized its RESYNC
//
//  2. Policy Processor
//     - implements the PolicyCacheWatcher interface
//     - subscribes in the Policy Cache for watching changes and RESYNC events
//     - for each change, decides if the re-configuration should be postponed
//       until more data are available
//     - if a change carries enough information, processor determines the list
//       of pods with the outdated policy configuration (skipped for RESYNC)
//        - changes needed to be considered:
//           * new pod, policy, namespace
//           * removed pod, policy, namespace
//           * changed pod labels
//           * changed namespace labels
//           * changed/added/removed port names
//           * changed policy in any way
//           * pod migrated between hosts
//     - handles pod migration
//        - learns IP subnet assigned to the node from the Contiv plugin
//        - unlike Policy Configurator, the processor is aware of the
//          inter-host networking but not aware of the intra-host networking
//          details
//     - for each outdated pod, processor calculates the set of policies that
//       should be configured
//        - converts K8s Network Policies into less-abstract ContivPolicy type
//          used by the PolicyConfigurator
//           * evaluates Label Selectors
//           * translates port names into numbers
//           * expands namespaces into pods
//
//  3. Policy Configurator
//     - for a given pod, translates a set of Contiv Policies into ingress and
//       egress lists of Contiv Rules (n-tuples with the most basic policy rule
//       definition; order matters) and applies them into the target vswitch via
//       registered renderers
//     - allows to register multiple renderers for different network stacks
//     - uses the cache and the Contiv plugin to get the IP address and
//       the interface name associated with a pod, respectively
//        - i.e. unlike the processor, the configurator is aware of the
//          intra-host networking (but not aware of inter-host networking
//          details)
//     - for the best performance, creates a shortest possible sequence of rules
//       that implement a given policy
//     - for the sake of renderers that install rules into per-interface tables
//       (as opposed to one or more global tables), the configurator ensures
//       that the same set of policies always results in the same list of rules,
//       allowing renderers to group and share them across multiple interfaces
//       (if supported by the destination network stack)
//
//  4. Policy Renderer
//     - applies a list of Contiv Rules into the destination network stack
//
// Caches
// -------
//
// * Policy Cache
//    - stores K8s State data using idxmap-s
//    - processes datasync events generated when KSR reflects a change
//      in the K8s State into ETCD
//    - allows watching for changes
//    - changes are propagated via callbacks: watcher must implement
//      the PolicyCacheWatcher interface
//    - provides various lookup methods (e.g. by the label selector)
//
// * Renderer's own cache
//    - to accomplish the task of (efficient) rule rendering, renderers often
//      implement their own cache for rules (transparent for the layers above)
//    - the selection of the data structure for the cache depends on the policy
//      implementation in the destination network stack which limits
//      re-usability of caches between renderers
//
//
// Diagram
// -------
//
// +-----------------------------------+
// |                                   |
// |                                   |
// |              ETCD                 |
// |                                   |
// |                |                  |
// +----------------|------------------+
//                watch
// +----------------|------------------+  +------------------------+
// |                v                  |  |                        |
// |                                   |  |                        |
// |       1. Policy Plugin        --update-->                     |
// |                                   |  |                        |
// |                                   |  |                        |
// +-----------------------------------+  |                        |
//                                        |                        |
// +-----------------------------------+  |                        |
// |                                   |  |                        |
// |                                   |  |                        |
// |       2. Policy Processor      <--watch--                     |
// |                                 <--get--                      |
// |                 |                 |  |                        |
// +-----------------|-----------------+  |      Policy Cache      |
//       Configure Contiv Policies        |                        |
// +-----------------|-----------------+  |                        |
// |                 v                 |  |                        |
// |                                   |  |                        |
// |       3. Policy Configurator    <--get--                      |
// |                                   |  |                        |
// |      |                     |      |  |                        |
// +------|---------------------|------+  +------------------------+
//        | Render Contiv Rules |
// +------v--------+   +--------v------+
// |               |   |               |
// | 4. Policy     |   | 4. Policy     |
// |   Renderer    |   |   Renderer    |
// |     +-------+ |   |     +-------+ |
// |     | cache | |   |     | cache | |
// |     +-------+ |   |     +-------+ |
// +-------|-------+   +-------|-------+
//      Render (via e.g. local client)
// +-------|-------------------|-------+
// |       v                   v       |
// |                                   |
// |            VPP / OVS / ...        |
// |                                   |
// |                                   |
// +-----------------------------------+

// Package policy implements plugin that processes and applies K8s Network
// policies into various destination network stacks. Support for a new network
// stack can be easily added into the plugin via so-called renderers.
package policy
<commit_msg>Add KSR to the diagram for policy plugin.<commit_after>/*
 * // Copyright (c) 2017 Cisco and/or its affiliates.
 * //
 * // 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.
 */

// Architecture
// ============
//
// Policy plugin is split into multiple layers with the data moving
// in the direction from the up to the bottom. Each layer obtains policy-related
// data from the layer above and outputs them processed in some way into
// the layer below. With each layer the abstraction level decreases until it
// reaches the format of policies used by the target network stack.
//
// On the top there are K8s state data as reflected into ETCD by KSR.
// On the very bottom there is a virtual switch into which the policies
// are rendered. Layers in-between perform policy processing with the assistance
// of in-memory caches.
//
// Layers
// ------
//
//	1. Policy Plugin:
//     - implements the Plugin interface for CN-Infra
//     - initializes caches and all the layers, performs dependency injection
//     - watches ETCD for changes written by KSR
//     - propagates datasync events into the Policy Cache without any processing
//     - postpones RESYNC until the Contiv plugin has finalized its RESYNC
//
//  2. Policy Processor
//     - implements the PolicyCacheWatcher interface
//     - subscribes in the Policy Cache for watching changes and RESYNC events
//     - for each change, decides if the re-configuration should be postponed
//       until more data are available
//     - if a change carries enough information, processor determines the list
//       of pods with the outdated policy configuration (skipped for RESYNC)
//        - changes needed to be considered:
//           * new pod, policy, namespace
//           * removed pod, policy, namespace
//           * changed pod labels
//           * changed namespace labels
//           * changed/added/removed port names
//           * changed policy in any way
//           * pod migrated between hosts
//     - handles pod migration
//        - learns IP subnet assigned to the node from the Contiv plugin
//        - unlike Policy Configurator, the processor is aware of the
//          inter-host networking but not aware of the intra-host networking
//          details
//     - for each outdated pod, processor calculates the set of policies that
//       should be configured
//        - converts K8s Network Policies into less-abstract ContivPolicy type
//          used by the PolicyConfigurator
//           * evaluates Label Selectors
//           * translates port names into numbers
//           * expands namespaces into pods
//
//  3. Policy Configurator
//     - for a given pod, translates a set of Contiv Policies into ingress and
//       egress lists of Contiv Rules (n-tuples with the most basic policy rule
//       definition; order matters) and applies them into the target vswitch via
//       registered renderers
//     - allows to register multiple renderers for different network stacks
//     - uses the cache and the Contiv plugin to get the IP address and
//       the interface name associated with a pod, respectively
//        - i.e. unlike the processor, the configurator is aware of the
//          intra-host networking (but not aware of inter-host networking
//          details)
//     - for the best performance, creates a shortest possible sequence of rules
//       that implement a given policy
//     - for the sake of renderers that install rules into per-interface tables
//       (as opposed to one or more global tables), the configurator ensures
//       that the same set of policies always results in the same list of rules,
//       allowing renderers to group and share them across multiple interfaces
//       (if supported by the destination network stack)
//
//  4. Policy Renderer
//     - applies a list of Contiv Rules into the destination network stack
//
// Caches
// -------
//
// * Policy Cache
//    - stores K8s State data using idxmap-s
//    - processes datasync events generated when KSR reflects a change
//      in the K8s State into ETCD
//    - allows watching for changes
//    - changes are propagated via callbacks: watcher must implement
//      the PolicyCacheWatcher interface
//    - provides various lookup methods (e.g. by the label selector)
//
// * Renderer's own cache
//    - to accomplish the task of (efficient) rule rendering, renderers often
//      implement their own cache for rules (transparent for the layers above)
//    - the selection of the data structure for the cache depends on the policy
//      implementation in the destination network stack which limits
//      re-usability of caches between renderers
//
//
// Diagram
// -------
//
// +-----------------------------------+
// |                                   |
// |                                   |
// |               K8s                 |
// |                                   |
// |                |                  |
// +----------------|------------------+
//           watch via K8s API
// +----------------|------------------+
// |                v                  |
// |                                   |
// |               KSR                 |
// |                                   |
// |                |                  |
// +----------------|------------------+
//                write
// +----------------|------------------+
// |                v                  |
// |                                   |
// |              ETCD                 |
// |                                   |
// |                |                  |
// +----------------|------------------+
//                watch
// +----------------|------------------+  +------------------------+
// |                v                  |  |                        |
// |                                   |  |                        |
// |       1. Policy Plugin        --update-->                     |
// |                                   |  |                        |
// |                                   |  |                        |
// +-----------------------------------+  |                        |
//                                        |                        |
// +-----------------------------------+  |                        |
// |                                   |  |                        |
// |                                   |  |                        |
// |       2. Policy Processor      <--watch--                     |
// |                                 <--get--                      |
// |                 |                 |  |                        |
// +-----------------|-----------------+  |      Policy Cache      |
//       Configure Contiv Policies        |                        |
// +-----------------|-----------------+  |                        |
// |                 v                 |  |                        |
// |                                   |  |                        |
// |       3. Policy Configurator    <--get--                      |
// |                                   |  |                        |
// |      |                     |      |  |                        |
// +------|---------------------|------+  +------------------------+
//        | Render Contiv Rules |
// +------v--------+   +--------v------+
// |               |   |               |
// | 4. Policy     |   | 4. Policy     |
// |   Renderer    |   |   Renderer    |
// |     +-------+ |   |     +-------+ |
// |     | cache | |   |     | cache | |
// |     +-------+ |   |     +-------+ |
// +-------|-------+   +-------|-------+
//      Render (via e.g. local client)
// +-------|-------------------|-------+
// |       v                   v       |
// |                                   |
// |            VPP / OVS / ...        |
// |                                   |
// |                                   |
// +-----------------------------------+

// Package policy implements plugin that processes and applies K8s Network
// policies into various destination network stacks. Support for a new network
// stack can be easily added into the plugin via so-called renderers.
package policy
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"time"

	"github.com/nanobox-io/nanobox/util/config"
)

// Env ...
type Env struct {
	ID        string
	Directory string
	Name      string

	// Remotes map a local app to multiple production apps, by an alias
	Remotes map[string]Remote
	// the boxfile from the most recent build
	BuiltBoxfile      string
	UserBoxfile       string
	BuiltID           string
	DeployedID        string
	LastBuild         time.Time
	LastCompile       time.Time
	BuildTriggers     map[string]string
	LastBuildProvider string
}

// Remote ...
type Remote struct {
	ID       string
	Name     string
	Endpoint string
}

// IsNew returns true if the Env hasn't been created yet
func (e *Env) IsNew() bool {
	return e.ID == ""
}

// Save persists the Env to the database
func (e *Env) Save() error {

	if err := put("envs", e.ID, e); err != nil {
		return fmt.Errorf("failed to save env: %s", err.Error())
	}

	return nil
}

// Delete deletes the app record from the database
func (e *Env) Delete() error {

	if err := destroy("envs", e.ID); err != nil {
		return fmt.Errorf("failed to delete env: %s", err.Error())
	}

	return nil
}

// Generate populates an Env from config data and persists the record
func (e *Env) Generate() error {

	// short-circuit if this record has already been generated
	if !e.IsNew() {
		return nil
	}

	// populate the data from the config package
	e.ID = config.EnvID()
	e.Directory = config.LocalDir()
	e.Name = config.LocalDirName()
	e.Remotes = map[string]Remote{}

	return e.Save()
}

// Apps get a list of the apps that belong to this
func (e *Env) Apps() ([]*App, error) {
	return AllAppsByEnv(e.ID)
}

// FindEnvByID finds an app by an ID
func FindEnvByID(ID string) (*Env, error) {

	env := &Env{}

	if err := get("envs", ID, &env); err != nil {
		return env, fmt.Errorf("failed to load env: %s", err.Error())
	}

	return env, nil
}

// AllEnvs loads all of the Envs in the database
func AllEnvs() ([]*Env, error) {
	// list of apps to return
	apps := []*Env{}

	return apps, getAll("envs", &apps)
}
<commit_msg>set the last build on generate to make the implode check work<commit_after>package models

import (
	"fmt"
	"time"

	"github.com/nanobox-io/nanobox/util/config"
)

// Env ...
type Env struct {
	ID        string
	Directory string
	Name      string

	// Remotes map a local app to multiple production apps, by an alias
	Remotes map[string]Remote
	// the boxfile from the most recent build
	BuiltBoxfile      string
	UserBoxfile       string
	BuiltID           string
	DeployedID        string
	LastBuild         time.Time
	LastCompile       time.Time
	BuildTriggers     map[string]string
	LastBuildProvider string
}

// Remote ...
type Remote struct {
	ID       string
	Name     string
	Endpoint string
}

// IsNew returns true if the Env hasn't been created yet
func (e *Env) IsNew() bool {
	return e.ID == ""
}

// Save persists the Env to the database
func (e *Env) Save() error {

	if err := put("envs", e.ID, e); err != nil {
		return fmt.Errorf("failed to save env: %s", err.Error())
	}

	return nil
}

// Delete deletes the app record from the database
func (e *Env) Delete() error {

	if err := destroy("envs", e.ID); err != nil {
		return fmt.Errorf("failed to delete env: %s", err.Error())
	}

	return nil
}

// Generate populates an Env from config data and persists the record
func (e *Env) Generate() error {

	// short-circuit if this record has already been generated
	if !e.IsNew() {
		return nil
	}

	// populate the data from the config package
	e.ID = config.EnvID()
	e.Directory = config.LocalDir()
	e.Name = config.LocalDirName()
	e.Remotes = map[string]Remote{}
	e.LastBuildProvider = config.Viper().GetString("provider")

	return e.Save()
}

// Apps get a list of the apps that belong to this
func (e *Env) Apps() ([]*App, error) {
	return AllAppsByEnv(e.ID)
}

// FindEnvByID finds an app by an ID
func FindEnvByID(ID string) (*Env, error) {

	env := &Env{}

	if err := get("envs", ID, &env); err != nil {
		return env, fmt.Errorf("failed to load env: %s", err.Error())
	}

	return env, nil
}

// AllEnvs loads all of the Envs in the database
func AllEnvs() ([]*Env, error) {
	// list of apps to return
	apps := []*Env{}

	return apps, getAll("envs", &apps)
}
<|endoftext|>