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

// This program demonstrates the use of a MeshBuffer.
package main

import (
	"github.com/go-gl/gl"
	"github.com/go-gl/glfw"
	"github.com/go-gl/glh"
	"github.com/go-gl/glu"
	"github.com/andrebq/assimp"
	"github.com/andrebq/assimp/conv"
	"github.com/andrebq/gas"
	"log"
)

var (
	scene *assimp.Scene
)

func main() {

	loadMeshInfo()

	err := initGL()
	if err != nil {
		log.Printf("InitGL: %v", err)
		return
	}

	program := createSampleProgram()
	_ = program

	defer glfw.Terminate()

	mb := createBuffer()
	defer mb.Release()

	// Perform the rendering.
	var angle float32
	for glfw.WindowParam(glfw.Opened) > 0 {
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		gl.LoadIdentity()
		gl.Translatef(0, 0, -20)
		gl.Rotatef(angle, 1, 1, 1)
		//program.Use()

		// Render a solid cube at half the scale.
		//gl.Scalef(0.2, 0.2, 0.2)
		gl.Enable(gl.COLOR_MATERIAL)
		gl.Enable(gl.POLYGON_OFFSET_FILL)
		gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
		mb.Render(gl.TRIANGLES)
		/*

		// Render wireframe cubes, with incremental size.
		gl.Disable(gl.COLOR_MATERIAL)
		gl.Disable(gl.POLYGON_OFFSET_FILL)
		gl.PolygonMode(gl.FRONT_AND_BACK, gl.LINE)

		for i := 0; i < 50; i++ {
			scale := 0.004*float32(i) + 1.0
			gl.Scalef(scale, scale, scale)
			mb.Render(gl.QUADS)
		}
		*/

		angle += 0.5
		glfw.SwapBuffers()
	}
}

func createBuffer() *glh.MeshBuffer {

	assimp.RandomColor(scene.Mesh[0])
	fmesh := assimp.NewFlatMesh(scene.Mesh[0])

	// Create a mesh buffer with the given attributes.
	mb := glh.NewMeshBuffer(
		glh.RenderBuffered,

		// Indices.
		glh.NewIndexAttr(1, gl.UNSIGNED_BYTE, gl.STATIC_DRAW),

		// Vertex positions have 3 components (x, y, z).
		glh.NewPositionAttr(3, gl.FLOAT, gl.STATIC_DRAW),

		// Colors have 4 components (r, g, b, a).
		glh.NewColorAttr(4, gl.FLOAT, gl.STATIC_DRAW),
	)

	// Add the mesh to the buffer.
	mb.Add(fmesh.ByteIndex, fmesh.Vertex, fmesh.Color)
	return mb
}

// initGL initializes GLFW and OpenGL.
func initGL() error {
	err := glfw.Init()
	if err != nil {
		return err
	}

	glfw.OpenWindowHint(glfw.FsaaSamples, 4)

	err = glfw.OpenWindow(512, 512, 8, 8, 8, 8, 32, 0, glfw.Windowed)
	if err != nil {
		glfw.Terminate()
		return err
	}

	glfw.SetWindowTitle("Meshbuffer 3D example")
	glfw.SetSwapInterval(1)
	glfw.SetWindowSizeCallback(onResize)
	glfw.SetKeyCallback(onKey)

	gl.Init()
	if err = glh.CheckGLError(); err != nil {
		return err
	}

	gl.Enable(gl.DEPTH_TEST)
	gl.Enable(gl.MULTISAMPLE)
	gl.Disable(gl.LIGHTING)

	gl.ClearColor(0.2, 0.2, 0.23, 1.0)
	gl.ShadeModel(gl.SMOOTH)
	gl.LineWidth(2)
	gl.ClearDepth(1)
	gl.DepthFunc(gl.LEQUAL)
	gl.Hint(gl.PERSPECTIVE_CORRECTION_HINT, gl.NICEST)
	gl.ColorMaterial(gl.FRONT_AND_BACK, gl.AMBIENT_AND_DIFFUSE)
	return nil
}

// onKey handles key events.
func onKey(key, state int) {
	if key == glfw.KeyEsc {
		glfw.CloseWindow()
	}
}

// onResize handles window resize events.
func onResize(w, h int) {
	if w < 1 {
		w = 1
	}

	if h < 1 {
		h = 1
	}

	gl.Viewport(0, 0, w, h)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	glu.Perspective(45.0, float64(w)/float64(h), 0.1, 200.0)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()
}

// Create a vertex/fragment shader program
func createSampleProgram() gl.Program {
	vs := `
#version 120
// Input vertex data, different for all executions of this shader.
// attribute vec3 vertexPosition_modelspace;
void main(){

	gl_Position = gl_Vertex;
}
	`
	fs := `
#version 120

void main()
{

	// Output color = red
	gl_FragColor = vec4(1,0,0,1);

}
	`
	vshader := gl.CreateShader(gl.VERTEX_SHADER)
	vshader.Source(vs)
	vshader.Compile()
	if vshader.Get(gl.COMPILE_STATUS) != gl.TRUE {
		panic("Unable to compile vertex shader. " + vshader.GetInfoLog())
	}

	fshader := gl.CreateShader(gl.FRAGMENT_SHADER)
	fshader.Source(fs)
	fshader.Compile()
	if fshader.Get(gl.COMPILE_STATUS) != gl.TRUE {
		panic("Unable to compile fragment shader. " + fshader.GetInfoLog())
	}

	program := gl.CreateProgram()
	program.AttachShader(vshader)
	program.AttachShader(fshader)
	program.Link()

	if program.Get(gl.LINK_STATUS) != gl.TRUE {
		panic("Unable to link program. " + fshader.GetInfoLog())
	}

	//program.Use()
	return program

}

func loadMeshInfo() {
	path, err := gas.Abs("github.com/andrebq/assimp/data/cube.dae")
	if err != nil { panic(err) }

	scene, err = conv.LoadAsset(path)
}<commit_msg>Loading other resources (not only cube.dae)<commit_after>// Copyright 2012 The go-gl Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This program demonstrates the use of a MeshBuffer.
package main

import (
	"flag"
	"github.com/andrebq/assimp"
	"github.com/andrebq/assimp/conv"
	"github.com/andrebq/gas"
	"github.com/go-gl/gl"
	"github.com/go-gl/glfw"
	"github.com/go-gl/glh"
	"github.com/go-gl/glu"
	"log"
)

var (
	scene    *assimp.Scene
	meshFile = flag.String("if", "", "Sample cube")
)

func main() {
	flag.Parse()

	loadMeshInfo()

	err := initGL()
	if err != nil {
		log.Printf("InitGL: %v", err)
		return
	}

	program := createSampleProgram()
	_ = program

	defer glfw.Terminate()

	mb := createBuffer()
	defer mb.Release()

	// Perform the rendering.
	var angle float32
	for glfw.WindowParam(glfw.Opened) > 0 {
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		gl.LoadIdentity()
		gl.Translatef(0, 0, -20)
		gl.Rotatef(angle, 1, 1, 1)
		//program.Use()

		// Render a solid cube at half the scale.
		//gl.Scalef(0.2, 0.2, 0.2)
		gl.Enable(gl.COLOR_MATERIAL)
		gl.Enable(gl.POLYGON_OFFSET_FILL)
		gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
		mb.Render(gl.TRIANGLES)
		/*

			// Render wireframe cubes, with incremental size.
			gl.Disable(gl.COLOR_MATERIAL)
			gl.Disable(gl.POLYGON_OFFSET_FILL)
			gl.PolygonMode(gl.FRONT_AND_BACK, gl.LINE)

			for i := 0; i < 50; i++ {
				scale := 0.004*float32(i) + 1.0
				gl.Scalef(scale, scale, scale)
				mb.Render(gl.QUADS)
			}
		*/

		angle += 0.5
		glfw.SwapBuffers()
	}
}

func createBuffer() *glh.MeshBuffer {

	assimp.RandomColor(scene.Mesh[0])
	fmesh := assimp.NewFlatMesh(scene.Mesh[0])

	// Create a mesh buffer with the given attributes.
	mb := glh.NewMeshBuffer(
		glh.RenderBuffered,

		// Indices.
		glh.NewIndexAttr(1, gl.UNSIGNED_BYTE, gl.STATIC_DRAW),

		// Vertex positions have 3 components (x, y, z).
		glh.NewPositionAttr(3, gl.FLOAT, gl.STATIC_DRAW),

		// Colors have 4 components (r, g, b, a).
		glh.NewColorAttr(4, gl.FLOAT, gl.STATIC_DRAW),
	)

	// Add the mesh to the buffer.
	mb.Add(fmesh.ByteIndex, fmesh.Vertex, fmesh.Color)
	return mb
}

// initGL initializes GLFW and OpenGL.
func initGL() error {
	err := glfw.Init()
	if err != nil {
		return err
	}

	glfw.OpenWindowHint(glfw.FsaaSamples, 4)

	err = glfw.OpenWindow(512, 512, 8, 8, 8, 8, 32, 0, glfw.Windowed)
	if err != nil {
		glfw.Terminate()
		return err
	}

	glfw.SetWindowTitle("Meshbuffer 3D example")
	glfw.SetSwapInterval(1)
	glfw.SetWindowSizeCallback(onResize)
	glfw.SetKeyCallback(onKey)

	gl.Init()
	if err = glh.CheckGLError(); err != nil {
		return err
	}

	gl.Enable(gl.DEPTH_TEST)
	gl.Enable(gl.MULTISAMPLE)
	gl.Disable(gl.LIGHTING)

	gl.ClearColor(0.2, 0.2, 0.23, 1.0)
	gl.ShadeModel(gl.SMOOTH)
	gl.LineWidth(2)
	gl.ClearDepth(1)
	gl.DepthFunc(gl.LEQUAL)
	gl.Hint(gl.PERSPECTIVE_CORRECTION_HINT, gl.NICEST)
	gl.ColorMaterial(gl.FRONT_AND_BACK, gl.AMBIENT_AND_DIFFUSE)
	return nil
}

// onKey handles key events.
func onKey(key, state int) {
	if key == glfw.KeyEsc {
		glfw.CloseWindow()
	}
}

// onResize handles window resize events.
func onResize(w, h int) {
	if w < 1 {
		w = 1
	}

	if h < 1 {
		h = 1
	}

	gl.Viewport(0, 0, w, h)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	glu.Perspective(45.0, float64(w)/float64(h), 0.1, 200.0)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()
}

// Create a vertex/fragment shader program
func createSampleProgram() gl.Program {
	vs := `
#version 120
// Input vertex data, different for all executions of this shader.
// attribute vec3 vertexPosition_modelspace;
void main(){

	gl_Position = gl_Vertex;
}
	`
	fs := `
#version 120

void main()
{

	// Output color = red
	gl_FragColor = vec4(1,0,0,1);

}
	`
	vshader := gl.CreateShader(gl.VERTEX_SHADER)
	vshader.Source(vs)
	vshader.Compile()
	if vshader.Get(gl.COMPILE_STATUS) != gl.TRUE {
		panic("Unable to compile vertex shader. " + vshader.GetInfoLog())
	}

	fshader := gl.CreateShader(gl.FRAGMENT_SHADER)
	fshader.Source(fs)
	fshader.Compile()
	if fshader.Get(gl.COMPILE_STATUS) != gl.TRUE {
		panic("Unable to compile fragment shader. " + fshader.GetInfoLog())
	}

	program := gl.CreateProgram()
	program.AttachShader(vshader)
	program.AttachShader(fshader)
	program.Link()

	if program.Get(gl.LINK_STATUS) != gl.TRUE {
		panic("Unable to link program. " + fshader.GetInfoLog())
	}

	//program.Use()
	return program

}

func loadMeshInfo() {

	println(*meshFile)
	if *meshFile == "" {
		path, err := gas.Abs("github.com/andrebq/assimp/data/cube.dae")
		if err != nil {
			panic(err)
		}
		*meshFile = path
	}
	println(*meshFile)

	var err error
	scene, err = conv.LoadAsset(*meshFile)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/therealbill/airbrake-go"
	"github.com/therealbill/libredis/client"
	"github.com/therealbill/redskull/common"
	"github.com/zenazn/goji/web"
)

// WEB UI CALL
func RebalanceHTML(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[RebalanceHTML]", err)
	}
	context.Title = "Rebalance Attempt Complete"
	context.ViewTemplate = "rebalance_complete"
	context.Constellation.Balance()
	context.Refresh = true
	context.RefreshTime = 30
	context.RefreshURL = "/constellation/"
	render(w, context)
}

func ConstellationInfoHTML(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[ConstellationInfoHTML]", err)
	}
	subtitle := context.Constellation.Name
	context.Title = "Constellation Information"
	context.SubTitle = subtitle
	context.ViewTemplate = "show_constellation"
	context.Data = context.Constellation
	render(w, context)

}

// API Calls

func RebalanceJSON(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[RebalanceJSON]", err)
	}
	context.Constellation.Balance()
	response := InfoResponse{Status: "COMPLETE", StatusMessage: "Rebalance attempt completed", Data: context.Constellation.IsBalanced()}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func ConstellationInfoJSON(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[ConstellationInfoJSON]", err)
	}
	packed, _ := json.Marshal(context.Constellation)
	w.Write(packed)
}

func DoFailoverJSON(ctx web.C, w http.ResponseWriter, r *http.Request) (err error) {
	podname := ctx.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[DoFailoverJSON]", err)
	}
	didFailover, err := context.Constellation.Failover(podname)
	if err != nil {
		retcode, emsg := handleFailoverError(podname, r, err)
		log.Printf("%d: '%s'", retcode, emsg)
		http.Error(w, emsg, retcode)
		return
	}
	if !didFailover {
		retcode, emsg := handleFailoverError(podname, r, err)
		log.Printf("%d: '%s'", retcode, emsg)
		http.Error(w, emsg, retcode)
		return
	}
	return nil
}

func APIFailover(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
		reqdata  common.FailoverRequest
	)
	body, err := ioutil.ReadAll(r.Body)
	err = json.Unmarshal(body, &reqdata)
	if err != nil {
		retcode, em := throwJSONParseError(r)
		log.Println(body)
		if retcode >= 400 {
			http.Error(w, em, retcode)
			return
		}
	}
	reqdata.Podname = c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIFailover]", err)
	}
	ok, err := context.Constellation.Failover(reqdata.Podname)
	if err != nil {
		em := err.Error()
		em = strings.TrimSpace(em)
		response.Status = "ERROR"
		switch em {
		case "NOGOODSLAVE No suitable slave to promote":
			response.Status = "NOGOODSLAVE"
			response.StatusMessage = "No suitable slave to promote"
		default:
			log.Printf("'%s'", em)
			response.StatusMessage = err.Error()
		}
		packed, _ := json.Marshal(response)
		w.Write(packed)
		return
	}
	if !ok {
		response.Status = "NOTOK"
		response.StatusMessage = "Unknown issue during failover request submission"
		packed, _ := json.Marshal(response)
		w.Write(packed)
	}
	response.Status = "SUCCESS"
	response.StatusMessage = "Failover command accepted"
	if reqdata.ReturnNew {
		newmaster, err := context.Constellation.GetMaster(reqdata.Podname)
		if err != nil {
			response.Status = "ERROR"
			response.StatusMessage = err.Error()
		}
		response.Data = newmaster
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIGetSlaves(c web.C, w http.ResponseWriter, r *http.Request) {
	var response InfoResponse
	podName := c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetSlaves]", err)
	}
	slaves, err := context.Constellation.GetSlaves(podName)
	response.Data = slaves
	if err != nil {
		response.Status = "ERROR"
		response.StatusMessage = err.Error()
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIGetMaster(c web.C, w http.ResponseWriter, r *http.Request) {
	var response InfoResponse
	podName := c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetMaster]", err)
	}
	master, err := context.Constellation.GetMaster(podName)
	if err != nil {
		em := fmt.Errorf("Sentinel command error '%s'", err)
		e := airbrake.ExtendedNotification{ErrorClass: "Sentinel.Command", Error: em}
		err = airbrake.ExtendedError(e, r)
		if err != nil {
			log.Print("airbrake error:", err)
		}
		response.Status = "COMMANDERROR"
		response.StatusMessage = err.Error()
	} else {
		var addr client.MasterAddress
		addr = master
		response.Data = addr
		if len(addr.Host) == 0 {
			response.StatusMessage = "No master found"
			response.Status = "NOTFOUND"
			w.WriteHeader(404)
			return
		} else {
			response.Status = "SUCCESS"
		}
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIMonitorPod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
		reqdata  common.MonitorRequest
	)
	podName := c.URLParams["podName"]
	body, err := ioutil.ReadAll(r.Body)
	err = json.Unmarshal(body, &reqdata)
	if err != nil {
		retcode, em := throwJSONParseError(r)
		log.Print(em)
		http.Error(w, em, retcode)
	}
	reqdata.Podname = podName
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIMonitorPod]", err)
	}
	ok, err := context.Constellation.MonitorPod(podName, reqdata.MasterAddress, reqdata.MasterPort, reqdata.Quorum, reqdata.AuthToken)
	if !ok {
		response.StatusMessage = fmt.Sprintf("Pod '%s' failed to reach sentinel quorum.", reqdata.Podname)
		response.Status = "INCOMPLETE"
		em := fmt.Errorf("MONITOR pod '%s' failed to reach quorum", reqdata.Podname)
		e := airbrake.ExtendedNotification{ErrorClass: "Pod.Quorum", Error: em}
		err = airbrake.ExtendedError(e, r)
		if err != nil {
			log.Print("airbrake error:", err)
		}
	} else {
		response.Status = "COMPLETE"
	}

	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIRemovePod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	podName := c.URLParams["podName"]
	log.Print("Removing pod:", podName)

	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIRemovePod]", err)
	}
	_, err = context.Constellation.RemovePod(podName)
	if err != nil {
		response.Status = "COMMANDERROR"
		response.StatusMessage = err.Error()
	} else {
		response.Status = "COMPLETE"
		response.StatusMessage = fmt.Sprintf("Not monitoring pod '%s'", podName)
	}
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPodMap(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetPodMap]", err)
	}
	pods, _ := context.Constellation.GetPodMap()
	response.Status = "COMPLETE"
	response.Data = pods
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPods(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetPods]", err)
	}
	pods := context.Constellation.GetPods()
	response.Status = "COMPLETE"
	response.Data = pods
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	podname := c.URLParams["podName"]
	log.Print("pulling API for pod " + podname)
	if podname == "" {
		err := fmt.Errorf("API:GP called w/o a pod??")
		log.Print("API:GP Error:", err)
		response.Status = "ERROR"
		response.StatusMessage = err.Error()
	} else {
		context, err := NewPageContext()
		if err != nil {
			log.Fatal("[APIGetPod]", err)
		}
		pod, err := context.Constellation.GetPod(podname)
		if pod.Name > "" {
			response.Status = "COMPLETE"
			response.Data = pod
		} else {
			log.Print("API:GP Error:", err)
			response.Status = "ERROR"
			response.StatusMessage = err.Error()
			response.Data = pod
		}
	}
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	log.Print(packed)
	w.Write(packed)
}
<commit_msg>removing bad log entry<commit_after>package handlers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/therealbill/airbrake-go"
	"github.com/therealbill/libredis/client"
	"github.com/therealbill/redskull/common"
	"github.com/zenazn/goji/web"
)

// WEB UI CALL
func RebalanceHTML(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[RebalanceHTML]", err)
	}
	context.Title = "Rebalance Attempt Complete"
	context.ViewTemplate = "rebalance_complete"
	context.Constellation.Balance()
	context.Refresh = true
	context.RefreshTime = 30
	context.RefreshURL = "/constellation/"
	render(w, context)
}

func ConstellationInfoHTML(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[ConstellationInfoHTML]", err)
	}
	subtitle := context.Constellation.Name
	context.Title = "Constellation Information"
	context.SubTitle = subtitle
	context.ViewTemplate = "show_constellation"
	context.Data = context.Constellation
	render(w, context)

}

// API Calls

func RebalanceJSON(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[RebalanceJSON]", err)
	}
	context.Constellation.Balance()
	response := InfoResponse{Status: "COMPLETE", StatusMessage: "Rebalance attempt completed", Data: context.Constellation.IsBalanced()}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func ConstellationInfoJSON(c web.C, w http.ResponseWriter, r *http.Request) {
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[ConstellationInfoJSON]", err)
	}
	packed, _ := json.Marshal(context.Constellation)
	w.Write(packed)
}

func DoFailoverJSON(ctx web.C, w http.ResponseWriter, r *http.Request) (err error) {
	podname := ctx.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[DoFailoverJSON]", err)
	}
	didFailover, err := context.Constellation.Failover(podname)
	if err != nil {
		retcode, emsg := handleFailoverError(podname, r, err)
		log.Printf("%d: '%s'", retcode, emsg)
		http.Error(w, emsg, retcode)
		return
	}
	if !didFailover {
		retcode, emsg := handleFailoverError(podname, r, err)
		log.Printf("%d: '%s'", retcode, emsg)
		http.Error(w, emsg, retcode)
		return
	}
	return nil
}

func APIFailover(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
		reqdata  common.FailoverRequest
	)
	body, err := ioutil.ReadAll(r.Body)
	err = json.Unmarshal(body, &reqdata)
	if err != nil {
		retcode, em := throwJSONParseError(r)
		log.Println(body)
		if retcode >= 400 {
			http.Error(w, em, retcode)
			return
		}
	}
	reqdata.Podname = c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIFailover]", err)
	}
	ok, err := context.Constellation.Failover(reqdata.Podname)
	if err != nil {
		em := err.Error()
		em = strings.TrimSpace(em)
		response.Status = "ERROR"
		switch em {
		case "NOGOODSLAVE No suitable slave to promote":
			response.Status = "NOGOODSLAVE"
			response.StatusMessage = "No suitable slave to promote"
		default:
			log.Printf("'%s'", em)
			response.StatusMessage = err.Error()
		}
		packed, _ := json.Marshal(response)
		w.Write(packed)
		return
	}
	if !ok {
		response.Status = "NOTOK"
		response.StatusMessage = "Unknown issue during failover request submission"
		packed, _ := json.Marshal(response)
		w.Write(packed)
	}
	response.Status = "SUCCESS"
	response.StatusMessage = "Failover command accepted"
	if reqdata.ReturnNew {
		newmaster, err := context.Constellation.GetMaster(reqdata.Podname)
		if err != nil {
			response.Status = "ERROR"
			response.StatusMessage = err.Error()
		}
		response.Data = newmaster
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIGetSlaves(c web.C, w http.ResponseWriter, r *http.Request) {
	var response InfoResponse
	podName := c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetSlaves]", err)
	}
	slaves, err := context.Constellation.GetSlaves(podName)
	response.Data = slaves
	if err != nil {
		response.Status = "ERROR"
		response.StatusMessage = err.Error()
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIGetMaster(c web.C, w http.ResponseWriter, r *http.Request) {
	var response InfoResponse
	podName := c.URLParams["podName"]
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetMaster]", err)
	}
	master, err := context.Constellation.GetMaster(podName)
	if err != nil {
		em := fmt.Errorf("Sentinel command error '%s'", err)
		e := airbrake.ExtendedNotification{ErrorClass: "Sentinel.Command", Error: em}
		err = airbrake.ExtendedError(e, r)
		if err != nil {
			log.Print("airbrake error:", err)
		}
		response.Status = "COMMANDERROR"
		response.StatusMessage = err.Error()
	} else {
		var addr client.MasterAddress
		addr = master
		response.Data = addr
		if len(addr.Host) == 0 {
			response.StatusMessage = "No master found"
			response.Status = "NOTFOUND"
			w.WriteHeader(404)
			return
		} else {
			response.Status = "SUCCESS"
		}
	}
	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIMonitorPod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
		reqdata  common.MonitorRequest
	)
	podName := c.URLParams["podName"]
	body, err := ioutil.ReadAll(r.Body)
	err = json.Unmarshal(body, &reqdata)
	if err != nil {
		retcode, em := throwJSONParseError(r)
		log.Print(em)
		http.Error(w, em, retcode)
	}
	reqdata.Podname = podName
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIMonitorPod]", err)
	}
	ok, err := context.Constellation.MonitorPod(podName, reqdata.MasterAddress, reqdata.MasterPort, reqdata.Quorum, reqdata.AuthToken)
	if !ok {
		response.StatusMessage = fmt.Sprintf("Pod '%s' failed to reach sentinel quorum.", reqdata.Podname)
		response.Status = "INCOMPLETE"
		em := fmt.Errorf("MONITOR pod '%s' failed to reach quorum", reqdata.Podname)
		e := airbrake.ExtendedNotification{ErrorClass: "Pod.Quorum", Error: em}
		err = airbrake.ExtendedError(e, r)
		if err != nil {
			log.Print("airbrake error:", err)
		}
	} else {
		response.Status = "COMPLETE"
	}

	packed, _ := json.Marshal(response)
	w.Write(packed)
}

func APIRemovePod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	podName := c.URLParams["podName"]
	log.Print("Removing pod:", podName)

	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIRemovePod]", err)
	}
	_, err = context.Constellation.RemovePod(podName)
	if err != nil {
		response.Status = "COMMANDERROR"
		response.StatusMessage = err.Error()
	} else {
		response.Status = "COMPLETE"
		response.StatusMessage = fmt.Sprintf("Not monitoring pod '%s'", podName)
	}
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPodMap(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetPodMap]", err)
	}
	pods, _ := context.Constellation.GetPodMap()
	response.Status = "COMPLETE"
	response.Data = pods
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPods(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	context, err := NewPageContext()
	if err != nil {
		log.Fatal("[APIGetPods]", err)
	}
	pods := context.Constellation.GetPods()
	response.Status = "COMPLETE"
	response.Data = pods
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}

func APIGetPod(c web.C, w http.ResponseWriter, r *http.Request) {
	var (
		response InfoResponse
	)
	podname := c.URLParams["podName"]
	log.Print("pulling API for pod " + podname)
	if podname == "" {
		err := fmt.Errorf("API:GP called w/o a pod??")
		log.Print("API:GP Error:", err)
		response.Status = "ERROR"
		response.StatusMessage = err.Error()
	} else {
		context, err := NewPageContext()
		if err != nil {
			log.Fatal("[APIGetPod]", err)
		}
		pod, err := context.Constellation.GetPod(podname)
		if pod.Name > "" {
			response.Status = "COMPLETE"
			response.Data = pod
		} else {
			log.Print("API:GP Error:", err)
			response.Status = "ERROR"
			response.StatusMessage = err.Error()
			response.Data = pod
		}
	}
	packed, err := json.Marshal(response)
	if err != nil {
		log.Print("Unable to pack JSON, err:", err)
	}
	w.Write(packed)
}
<|endoftext|>


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

package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"

	qifcsv "bank/csv"
	"bank/qif"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func qifId(e *qif.Entry) string {
	h := sha1.New()
	io.WriteString(h, e.Number+"\n")
	io.WriteString(h, e.Date.Format("2006/01/02")+"\n")
	io.WriteString(h, strconv.Itoa(e.Amount)+"\n")
	io.WriteString(h, e.Payee+"\n")
	io.WriteString(h, e.Address+"\n")
	return fmt.Sprintf("%x", h.Sum(nil))
}

type QIFRead interface {
	ReadEntry() (*qif.Entry, error)
}

func parse(path string, entries []*qif.Entry) []*qif.Entry {
	f, err := os.Open(path)
	check(err)
	defer f.Close()

	var qr QIFRead

	ext := filepath.Ext(path)
	log.Printf("ext %v", ext)
	switch ext {
	case ".qif":
		r := qif.NewReader(f)
		ttype, err := r.ReadHeader()
		if err != nil {
			err = fmt.Errorf("reading %s: %s", path, err.Error())
			check(err)
		}
		log.Printf("%s: %q", path, ttype)
		qr = r
	case ".csv":
		r, err := qifcsv.NewCSVReader(f)
		if err != nil {
			err = fmt.Errorf("reading %s: %s", path, err.Error())
			check(err)
		}
		log.Printf("%s: csv", path)
		qr = r
	default:
		log.Printf("%s: unknown format", path)
	}

	for {
		entry, err := qr.ReadEntry()
		if err != nil {
			if err == io.EOF {
				break
			}
			check(err)
		}

		entries = append(entries, entry)
	}

	return entries
}

func main() {
	var tagsPath string
	flag.StringVar(&tagsPath, "tags", "", "path to read/write tag list")
	flag.Parse()

	if tagsPath == "" {
		fmt.Println("must specify tags path")
		flag.PrintDefaults()
		return
	}

	tags, err := LoadTags(tagsPath)
	check(err)

	var entries []*qif.Entry
	for _, arg := range flag.Args() {
		entries = parse(arg, entries)
	}

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

package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"

	qifcsv "bank/csv"
	"bank/qif"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func qifId(e *qif.Entry) string {
	h := sha1.New()
	io.WriteString(h, e.Number+"\n")
	io.WriteString(h, e.Date.Format("2006/01/02")+"\n")
	io.WriteString(h, strconv.Itoa(e.Amount)+"\n")
	io.WriteString(h, e.Payee+"\n")
	io.WriteString(h, e.Address+"\n")
	return fmt.Sprintf("%x", h.Sum(nil))
}

type QIFRead interface {
	ReadEntry() (*qif.Entry, error)
}

func parse(path string, entries []*qif.Entry) []*qif.Entry {
	f, err := os.Open(path)
	check(err)
	defer f.Close()

	var qr QIFRead

	ext := filepath.Ext(path)
	switch ext {
	case ".qif":
		r := qif.NewReader(f)
		ttype, err := r.ReadHeader()
		if err != nil {
			err = fmt.Errorf("reading %s: %s", path, err.Error())
			check(err)
		}
		log.Printf("%s: %q", path, ttype)
		qr = r
	case ".csv":
		r, err := qifcsv.NewCSVReader(f)
		if err != nil {
			err = fmt.Errorf("reading %s: %s", path, err.Error())
			check(err)
		}
		log.Printf("%s: csv", path)
		qr = r
	default:
		log.Printf("%s: unknown format", path)
	}

	for {
		entry, err := qr.ReadEntry()
		if err != nil {
			if err == io.EOF {
				break
			}
			check(err)
		}

		entries = append(entries, entry)
	}

	return entries
}

func main() {
	var tagsPath string
	flag.StringVar(&tagsPath, "tags", "", "path to read/write tag list")
	flag.Parse()

	if tagsPath == "" {
		fmt.Println("must specify tags path")
		flag.PrintDefaults()
		return
	}

	tags, err := LoadTags(tagsPath)
	check(err)

	var entries []*qif.Entry
	for _, arg := range flag.Args() {
		entries = parse(arg, entries)
	}

	w := web{
		entries:  entries,
		tags:     tags,
		tagsPath: tagsPath,
	}
	w.start()
}
<|endoftext|>


<commit_before>package git

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"golang.org/x/crypto/ssh"
	gogit "gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
)

const (
	DefaultCloneTimeout = 2 * time.Minute
	DefaultPullTimeout  = 2 * time.Minute
	privateKeyFileMode  = os.FileMode(0400)
	ChangesClone        = "sync_clone"
)

var (
	ErrNoChanges    = errors.New("no changes made in repo")
	ErrNoChartsDir  = errors.New("no Charts dir provided")
	ErrNoRepo       = errors.New("no repo provided")
	ErrNoRepoCloned = errors.New("no repo cloned")
)

type GitRemoteConfig struct {
	URL    string `json:"url"`
	Branch string `json:"branch"`
	Path   string `json:"path"`
}

// Checkout is a local clone of the remote repo.
type Checkout struct {
	Logger   log.Logger
	Config   GitRemoteConfig // remote repo info provided by the user
	auth     *gitssh.PublicKeys
	Dir      string            // directory where the repo was cloned (repo root)
	Repo     *gogit.Repository // cloned repo info
	worktree *gogit.Worktree
	sync.RWMutex
}

// NewGitRemoteConfig ... sets up git repo configuration.
func NewGitRemoteConfig(url, branch, path string) (GitRemoteConfig, error) {
	if len(url) == 0 {
		return GitRemoteConfig{}, errors.New("git repo URL must be provided")
	}
	if len(branch) == 0 {
		branch = "master"
	}
	if len(path) == 0 || (len(path) != 0 && path[0] == '/') {
		return GitRemoteConfig{}, errors.New("git subdirectory (--git-charts-path) must be probided and cannot have leading forward slash")
	}

	return GitRemoteConfig{
		URL:    url,
		Branch: branch,
		Path:   path,
	}, nil
}

// SetupRepo creates a new checkout and clones repo until ready
func RepoSetup(logger log.Logger, auth *gitssh.PublicKeys, config GitRemoteConfig, cloneSubdir string) *Checkout {
	checkout := &Checkout{
		Logger: logger,
		Config: config,
		auth:   auth,
	}
	// If cloning not immediately possible, we wait until it is -----------------------------
	var err error
	for {
		ctx, cancel := context.WithTimeout(context.Background(), DefaultCloneTimeout)
		err = checkout.Clone(ctx, cloneSubdir)
		cancel()
		if err == nil {
			break
		}
		logger.Log("error", fmt.Sprintf("Failed to clone git repo [%s, %s, %s]: %v", config.URL, config.Path, config.Branch, err))
		time.Sleep(10 * time.Second)
	}

	return checkout
}

// Clone creates a local clone of a remote repo and
// checks out the relevant branch
//		subdir reflects the purpose of the clone:
//																		* acting on Charts changes (syncing the cluster when there were only commits
//																		  in the Charts parts of the repo which did not trigger Custom Resource changes)
func (ch *Checkout) Clone(ctx context.Context, cloneSubdir string) error {
	ch.Lock()
	defer ch.Unlock()

	if ch.Config.URL == "" {
		return ErrNoRepo
	}

	repoDir, err := ioutil.TempDir(os.TempDir(), cloneSubdir)
	if err != nil {
		return err
	}
	ch.Dir = repoDir

	repo, err := gogit.PlainCloneContext(ctx, repoDir, false, &gogit.CloneOptions{
		URL:           ch.Config.URL,
		Auth:          ch.auth,
		ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", ch.Config.Branch)),
	})
	if err != nil {
		return err
	}

	wt, err := repo.Worktree()
	if err != nil {
		return err
	}

	ch.Repo = repo
	ch.worktree = wt

	ch.Logger.Log("debug", fmt.Sprintf("repo cloned in into %s", ch.Dir))

	return nil
}

// Cleanup ... removes the temp repo directory
func (ch *Checkout) Cleanup() {
	ch.Lock()
	defer ch.Unlock()

	if ch.Dir != "" {
		err := os.RemoveAll(ch.Dir)
		if err != nil {
			ch.Logger.Log("error", err.Error())
		}
	}
	ch.Dir = ""
	ch.Repo = nil
	ch.worktree = nil
}

// GetRepoAuth ... provides git repo authentication based on private ssh key
func GetRepoAuth(sshUsername, k8sSecretVolumeMountPath, k8sSecretDataKey string) (*gitssh.PublicKeys, error) {
	privateKeyPath := path.Join(k8sSecretVolumeMountPath, k8sSecretDataKey)
	fileInfo, err := os.Stat(privateKeyPath)
	switch {
	case os.IsNotExist(err):
		return &gitssh.PublicKeys{}, err
	case err != nil:
		return &gitssh.PublicKeys{}, err
	case fileInfo.Mode() != privateKeyFileMode:
		if err := os.Chmod(privateKeyPath, privateKeyFileMode); err != nil {
			return &gitssh.PublicKeys{}, err
		}
	default:
	}

	sshKey, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return nil, err
	}
	signer, err := ssh.ParsePrivateKey([]byte(sshKey))
	if err != nil {
		return nil, err
	}
	auth := &gitssh.PublicKeys{User: sshUsername, Signer: signer}

	return auth, nil
}

// Pull ... makes a git pull
func (ch *Checkout) Pull(ctx context.Context) error {
	ch.Lock()
	defer ch.Unlock()

	w := ch.worktree
	if w == nil {
		return ErrNoRepoCloned
	}
	err := w.Pull(&gogit.PullOptions{
		RemoteName:    "origin",
		Auth:          ch.auth,
		ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", ch.Config.Branch)),
	})
	if err != nil && err != gogit.NoErrAlreadyUpToDate {
		return err
	}
	return nil
}

// GetRevision returns string representation of the revision hash
func (ch *Checkout) GetRevision() (plumbing.Hash, error) {
	if ch.Repo == nil {
		return plumbing.Hash{}, ErrNoRepoCloned
	}
	ref, err := ch.Repo.Head()
	if err != nil {
		return plumbing.Hash{}, err
	}
	rev := ref.Hash()
	return rev, nil
}
<commit_msg>Fix typo in error message.<commit_after>package git

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"sync"
	"time"

	"github.com/go-kit/kit/log"
	"golang.org/x/crypto/ssh"
	gogit "gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
)

const (
	DefaultCloneTimeout = 2 * time.Minute
	DefaultPullTimeout  = 2 * time.Minute
	privateKeyFileMode  = os.FileMode(0400)
	ChangesClone        = "sync_clone"
)

var (
	ErrNoChanges    = errors.New("no changes made in repo")
	ErrNoChartsDir  = errors.New("no Charts dir provided")
	ErrNoRepo       = errors.New("no repo provided")
	ErrNoRepoCloned = errors.New("no repo cloned")
)

type GitRemoteConfig struct {
	URL    string `json:"url"`
	Branch string `json:"branch"`
	Path   string `json:"path"`
}

// Checkout is a local clone of the remote repo.
type Checkout struct {
	Logger   log.Logger
	Config   GitRemoteConfig // remote repo info provided by the user
	auth     *gitssh.PublicKeys
	Dir      string            // directory where the repo was cloned (repo root)
	Repo     *gogit.Repository // cloned repo info
	worktree *gogit.Worktree
	sync.RWMutex
}

// NewGitRemoteConfig ... sets up git repo configuration.
func NewGitRemoteConfig(url, branch, path string) (GitRemoteConfig, error) {
	if len(url) == 0 {
		return GitRemoteConfig{}, errors.New("git repo URL must be provided")
	}
	if len(branch) == 0 {
		branch = "master"
	}
	if len(path) == 0 || (len(path) != 0 && path[0] == '/') {
		return GitRemoteConfig{}, errors.New("git subdirectory (--git-charts-path) must be provided and cannot have leading forward slash")
	}

	return GitRemoteConfig{
		URL:    url,
		Branch: branch,
		Path:   path,
	}, nil
}

// SetupRepo creates a new checkout and clones repo until ready
func RepoSetup(logger log.Logger, auth *gitssh.PublicKeys, config GitRemoteConfig, cloneSubdir string) *Checkout {
	checkout := &Checkout{
		Logger: logger,
		Config: config,
		auth:   auth,
	}
	// If cloning not immediately possible, we wait until it is -----------------------------
	var err error
	for {
		ctx, cancel := context.WithTimeout(context.Background(), DefaultCloneTimeout)
		err = checkout.Clone(ctx, cloneSubdir)
		cancel()
		if err == nil {
			break
		}
		logger.Log("error", fmt.Sprintf("Failed to clone git repo [%s, %s, %s]: %v", config.URL, config.Path, config.Branch, err))
		time.Sleep(10 * time.Second)
	}

	return checkout
}

// Clone creates a local clone of a remote repo and
// checks out the relevant branch
//		subdir reflects the purpose of the clone:
//																		* acting on Charts changes (syncing the cluster when there were only commits
//																		  in the Charts parts of the repo which did not trigger Custom Resource changes)
func (ch *Checkout) Clone(ctx context.Context, cloneSubdir string) error {
	ch.Lock()
	defer ch.Unlock()

	if ch.Config.URL == "" {
		return ErrNoRepo
	}

	repoDir, err := ioutil.TempDir(os.TempDir(), cloneSubdir)
	if err != nil {
		return err
	}
	ch.Dir = repoDir

	repo, err := gogit.PlainCloneContext(ctx, repoDir, false, &gogit.CloneOptions{
		URL:           ch.Config.URL,
		Auth:          ch.auth,
		ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", ch.Config.Branch)),
	})
	if err != nil {
		return err
	}

	wt, err := repo.Worktree()
	if err != nil {
		return err
	}

	ch.Repo = repo
	ch.worktree = wt

	ch.Logger.Log("debug", fmt.Sprintf("repo cloned in into %s", ch.Dir))

	return nil
}

// Cleanup ... removes the temp repo directory
func (ch *Checkout) Cleanup() {
	ch.Lock()
	defer ch.Unlock()

	if ch.Dir != "" {
		err := os.RemoveAll(ch.Dir)
		if err != nil {
			ch.Logger.Log("error", err.Error())
		}
	}
	ch.Dir = ""
	ch.Repo = nil
	ch.worktree = nil
}

// GetRepoAuth ... provides git repo authentication based on private ssh key
func GetRepoAuth(sshUsername, k8sSecretVolumeMountPath, k8sSecretDataKey string) (*gitssh.PublicKeys, error) {
	privateKeyPath := path.Join(k8sSecretVolumeMountPath, k8sSecretDataKey)
	fileInfo, err := os.Stat(privateKeyPath)
	switch {
	case os.IsNotExist(err):
		return &gitssh.PublicKeys{}, err
	case err != nil:
		return &gitssh.PublicKeys{}, err
	case fileInfo.Mode() != privateKeyFileMode:
		if err := os.Chmod(privateKeyPath, privateKeyFileMode); err != nil {
			return &gitssh.PublicKeys{}, err
		}
	default:
	}

	sshKey, err := ioutil.ReadFile(privateKeyPath)
	if err != nil {
		return nil, err
	}
	signer, err := ssh.ParsePrivateKey([]byte(sshKey))
	if err != nil {
		return nil, err
	}
	auth := &gitssh.PublicKeys{User: sshUsername, Signer: signer}

	return auth, nil
}

// Pull ... makes a git pull
func (ch *Checkout) Pull(ctx context.Context) error {
	ch.Lock()
	defer ch.Unlock()

	w := ch.worktree
	if w == nil {
		return ErrNoRepoCloned
	}
	err := w.Pull(&gogit.PullOptions{
		RemoteName:    "origin",
		Auth:          ch.auth,
		ReferenceName: plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", ch.Config.Branch)),
	})
	if err != nil && err != gogit.NoErrAlreadyUpToDate {
		return err
	}
	return nil
}

// GetRevision returns string representation of the revision hash
func (ch *Checkout) GetRevision() (plumbing.Hash, error) {
	if ch.Repo == nil {
		return plumbing.Hash{}, ErrNoRepoCloned
	}
	ref, err := ch.Repo.Head()
	if err != nil {
		return plumbing.Hash{}, err
	}
	rev := ref.Hash()
	return rev, nil
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018, 2020 Arm Limited.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 graph

import (
	"reflect"
	"strconv"
	"strings"
	"testing"

	"github.com/ARM-software/bob-build/internal/utils"
)

func TestShould_add_new_edge_When_edge_removed(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("3", "10")

	if testGraph.AddEdge("8", "9") {
		testGraph.SetEdgeColor("8", "9", "red")
	} else {
		t.Errorf("Edge should be new")
	}

	if testGraph.AddEdge("8", "9") {
		t.Errorf("Edge already added!")
	}

	testGraph.DeleteEdge("8", "9")

	if testGraph.AddEdge("8", "9") {
		testGraph.SetEdgeColor("8", "9", "blue")
	} else {
		t.Errorf("Edge should be new")
	}
}

func TestFun(t *testing.T) {
	////////////////////////////////////////////
	// The graph shown below has many valid topological sorts, including:

	//    5, 7, 3, 11, 8, 2, 9, 10 (visual left-to-right, top-to-bottom)
	//    3, 5, 7, 8, 11, 2, 9, 10 (smallest-numbered available vertex first)
	//    5, 7, 3, 8, 11, 10, 9, 2 (fewest edges first)
	//    7, 5, 11, 3, 10, 8, 9, 2 (largest-numbered available vertex first) <---------
	//    5, 7, 11, 2, 3, 8, 9, 10 (attempting top-to-bottom, left-to-right)
	//    3, 7, 8, 5, 11, 10, 2, 9 (arbitrary)
	testGraph := NewGraph("Test")
	testGraph.AddEdge("3", "10")
	testGraph.SetEdgeColor("3", "10", "blue")
	testGraph.AddEdge("11", "2")
	testGraph.AddEdge("11", "9")
	testGraph.AddEdge("11", "10")
	testGraph.AddEdge("7", "8")
	testGraph.AddEdge("3", "8")
	testGraph.AddEdge("5", "11")
	testGraph.AddEdge("7", "11")
	testGraph.AddEdge("8", "9")

	for i, sub := range GetSubgraphs(testGraph) {
		t.Log("Index: " + strconv.Itoa(i) + " - " + sub.GetName())
		t.Log(ToString(sub))
	}

	t.Log(testGraph.GetName())
	t.Log(ToString(testGraph))

	sorted, _ := TopologicalSort(testGraph)

	// Check that each condition is met
	if utils.Find(sorted, "3") > utils.Find(sorted, "10") {
		t.Errorf("3 and 10 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "2") {
		t.Errorf("11 and 2 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "9") {
		t.Errorf("11 and 9 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "10") {
		t.Errorf("11 and 10 not correctly sorted")
	}

	if utils.Find(sorted, "7") > utils.Find(sorted, "8") {
		t.Errorf("7 and 8 not correctly sorted")
	}

	if utils.Find(sorted, "3") > utils.Find(sorted, "8") {
		t.Errorf("13 and 8 not correctly sorted")
	}

	if utils.Find(sorted, "5") > utils.Find(sorted, "11") {
		t.Errorf("5 and 11 not correctly sorted")
	}

	if utils.Find(sorted, "7") > utils.Find(sorted, "11") {
		t.Errorf("7 and 11 not correctly sorted")
	}

	if utils.Find(sorted, "8") > utils.Find(sorted, "9") {
		t.Errorf("8 and 9 not correctly sorted")
	}
}

func TestShould_pass_connections_When_remove_proxy_node(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("B", "C")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyNode("B")

	if testGraph.HasNode("B") {
		t.Errorf("Node shouldn't be there")
	}

	if testGraph.AddEdge("A", "C") {
		t.Errorf("Edge should already exist")
	}

	if attributes, err := testGraph.GetEdgeAttributes("A", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge A -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

}

func TestShould_pass_connections_When_remove_proxy_node2(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("X", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyNode("B")

	if testGraph.HasNode("B") {
		t.Errorf("Node shouldn't be there")
	}

	if testGraph.AddEdge("A", "C") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("A", "D") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("A", "E") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("X", "C") {
		t.Errorf("Edge should already exist")
	}
	if attributes, err := testGraph.GetEdgeAttributes("X", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge X -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

	if testGraph.AddEdge("X", "D") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("X", "E") {
		t.Errorf("Edge should already exist")
	}
}

func TestShould_pass_connections_When_remove_proxy_edge(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("X", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyEdge("X", "B")

	if !testGraph.HasNode("B") {
		t.Errorf("Node should be there")
	}

	if testGraph.HasEdge("A", "C") {
		t.Errorf("Edge shouldn't exist")
	}
	if testGraph.HasEdge("A", "D") {
		t.Errorf("Edge shouldn't exist")
	}
	if testGraph.HasEdge("A", "E") {
		t.Errorf("Edge shouldn't exist")
	}

	if !testGraph.HasEdge("X", "C") {
		t.Errorf("Edge should exist")
	}
	if attributes, err := testGraph.GetEdgeAttributes("X", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge X -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

	if !testGraph.HasEdge("X", "D") {
		t.Errorf("Edge should exist")
	}
	if !testGraph.HasEdge("X", "E") {
		t.Errorf("Edge should exist")
	}
}

func TestShould_not_pass_connections_When_remove_proxy_edge_isnt_connected(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("C", "X")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")

	if testGraph.HasEdge("A", "X") {
		t.Errorf("Edge should exist")
	}

	testGraph.DeleteProxyEdge("A", "C")

	if testGraph.HasEdge("A", "X") {
		t.Log(ToString(testGraph))
		t.Errorf("Edge should exist")
	}
}

func Test_TopologicalSortMaintainsSubnodeOrder(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")
	testGraph.AddEdge("top", "c")
	testGraph.AddEdge("top", "d")
	testGraph.AddEdge("top", "e")
	testGraph.AddEdge("top", "f")

	// To ensure orderring, visit the top node first
	testGraph.SetNodePriority("top", -1)

	target := []string{"top", "a", "b", "c", "d", "e", "f"}

	sorted, _ := TopologicalSort(testGraph)
	if !reflect.DeepEqual(target, sorted) {
		t.Errorf("Mismatch!\n%s <------- result\n%s <------- correct",
			strings.Join(sorted, ", "),
			strings.Join(target, ", "))
	}

	// If we copy the graph we should still get the same sort result
	subGraph := GetSubgraph(testGraph, "top")

	sorted, _ = TopologicalSort(subGraph)
	if !reflect.DeepEqual(target, sorted) {
		t.Errorf("Mismatch after copy!\n%s <------- result\n%s <------- correct",
			strings.Join(sorted, ", "),
			strings.Join(target, ", "))
	}
}

func Test_SubnodeOrderAfterDeleteProxyEdges(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")
	testGraph.AddEdge("top", "c")
	testGraph.AddEdge("top", "d")

	testGraph.AddEdge("d", "df")
	testGraph.AddEdge("d", "de")
	testGraph.AddEdge("c", "cf")
	testGraph.AddEdge("c", "ce")
	testGraph.AddEdge("b", "bf")
	testGraph.AddEdge("b", "be")
	testGraph.AddEdge("a", "af")
	testGraph.AddEdge("a", "ae")

	testGraph.SetEdgeColor("top", "a", "red")
	testGraph.SetEdgeColor("top", "b", "red")
	testGraph.SetEdgeColor("top", "c", "red")
	testGraph.SetEdgeColor("top", "d", "red")

	testGraph.DeleteProxyEdges("red")

	// Remove unref'd nodes
	testGraph = GetSubgraph(testGraph, "top")

	// To ensure orderring, visit the top node first
	testGraph.SetNodePriority("top", -1)

	// ProxyEdges should be removed in subnode order, and their children added in order
	target := []string{"top", "af", "ae", "bf", "be", "cf", "ce", "df", "de"}

	sorted, _ := TopologicalSort(testGraph)
	if !reflect.DeepEqual(target, sorted) {
		t.Errorf("Mismatch!\n%s <------- result\n%s <------- correct",
			strings.Join(sorted, ", "),
			strings.Join(target, ", "))
	}
}

func Test_GetSubgraphNodeCount(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")

	testGraph.AddEdge("a", "a0")
	testGraph.AddEdge("a", "a1")
	testGraph.AddEdge("b", "b0")
	testGraph.AddEdge("b", "b1")

	nodes := []string{"top", "a", "a0", "a1", "b", "b0", "b1"}

	for _, node := range nodes {
		count1 := GetSubgraph(testGraph, node).GetNodeCount()
		count2 := GetSubgraphNodeCount(testGraph, node)
		if count1 != count2 {
			t.Errorf("Mismatch!\n%d <------- result\n%d <------- correct", count1, count2)
		}
	}
}

func Test_GetSubgraphHasNode(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")

	testGraph.AddEdge("a", "a0")
	testGraph.AddEdge("a", "a1")
	testGraph.AddEdge("b", "b0")
	testGraph.AddEdge("b", "b1")

	nodes := []string{"top", "a", "a0", "a1", "b", "b0", "b1"}

	for _, node1 := range nodes {
		for _, node2 := range nodes {
			res1 := GetSubgraphHasNode(testGraph, node1, node2)
			res2 := GetSubgraph(testGraph, node1).HasNode(node2)
			if res1 != res2 {
				t.Errorf("Mismatch!\n%v <------- result\n%v <------- correct", res1, res2)
			}
		}
	}
}
<commit_msg>Fix mismatch output in `graph_test.go`<commit_after>/*
 * Copyright 2018, 2020 Arm Limited.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 graph

import (
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/ARM-software/bob-build/internal/utils"
)

func TestShould_add_new_edge_When_edge_removed(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("3", "10")

	if testGraph.AddEdge("8", "9") {
		testGraph.SetEdgeColor("8", "9", "red")
	} else {
		t.Errorf("Edge should be new")
	}

	if testGraph.AddEdge("8", "9") {
		t.Errorf("Edge already added!")
	}

	testGraph.DeleteEdge("8", "9")

	if testGraph.AddEdge("8", "9") {
		testGraph.SetEdgeColor("8", "9", "blue")
	} else {
		t.Errorf("Edge should be new")
	}
}

func TestFun(t *testing.T) {
	////////////////////////////////////////////
	// The graph shown below has many valid topological sorts, including:

	//    5, 7, 3, 11, 8, 2, 9, 10 (visual left-to-right, top-to-bottom)
	//    3, 5, 7, 8, 11, 2, 9, 10 (smallest-numbered available vertex first)
	//    5, 7, 3, 8, 11, 10, 9, 2 (fewest edges first)
	//    7, 5, 11, 3, 10, 8, 9, 2 (largest-numbered available vertex first) <---------
	//    5, 7, 11, 2, 3, 8, 9, 10 (attempting top-to-bottom, left-to-right)
	//    3, 7, 8, 5, 11, 10, 2, 9 (arbitrary)
	testGraph := NewGraph("Test")
	testGraph.AddEdge("3", "10")
	testGraph.SetEdgeColor("3", "10", "blue")
	testGraph.AddEdge("11", "2")
	testGraph.AddEdge("11", "9")
	testGraph.AddEdge("11", "10")
	testGraph.AddEdge("7", "8")
	testGraph.AddEdge("3", "8")
	testGraph.AddEdge("5", "11")
	testGraph.AddEdge("7", "11")
	testGraph.AddEdge("8", "9")

	for i, sub := range GetSubgraphs(testGraph) {
		t.Log("Index: " + strconv.Itoa(i) + " - " + sub.GetName())
		t.Log(ToString(sub))
	}

	t.Log(testGraph.GetName())
	t.Log(ToString(testGraph))

	sorted, _ := TopologicalSort(testGraph)

	// Check that each condition is met
	if utils.Find(sorted, "3") > utils.Find(sorted, "10") {
		t.Errorf("3 and 10 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "2") {
		t.Errorf("11 and 2 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "9") {
		t.Errorf("11 and 9 not correctly sorted")
	}

	if utils.Find(sorted, "11") > utils.Find(sorted, "10") {
		t.Errorf("11 and 10 not correctly sorted")
	}

	if utils.Find(sorted, "7") > utils.Find(sorted, "8") {
		t.Errorf("7 and 8 not correctly sorted")
	}

	if utils.Find(sorted, "3") > utils.Find(sorted, "8") {
		t.Errorf("13 and 8 not correctly sorted")
	}

	if utils.Find(sorted, "5") > utils.Find(sorted, "11") {
		t.Errorf("5 and 11 not correctly sorted")
	}

	if utils.Find(sorted, "7") > utils.Find(sorted, "11") {
		t.Errorf("7 and 11 not correctly sorted")
	}

	if utils.Find(sorted, "8") > utils.Find(sorted, "9") {
		t.Errorf("8 and 9 not correctly sorted")
	}
}

func TestShould_pass_connections_When_remove_proxy_node(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("B", "C")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyNode("B")

	if testGraph.HasNode("B") {
		t.Errorf("Node shouldn't be there")
	}

	if testGraph.AddEdge("A", "C") {
		t.Errorf("Edge should already exist")
	}

	if attributes, err := testGraph.GetEdgeAttributes("A", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge A -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

}

func TestShould_pass_connections_When_remove_proxy_node2(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("X", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyNode("B")

	if testGraph.HasNode("B") {
		t.Errorf("Node shouldn't be there")
	}

	if testGraph.AddEdge("A", "C") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("A", "D") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("A", "E") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("X", "C") {
		t.Errorf("Edge should already exist")
	}
	if attributes, err := testGraph.GetEdgeAttributes("X", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge X -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

	if testGraph.AddEdge("X", "D") {
		t.Errorf("Edge should already exist")
	}
	if testGraph.AddEdge("X", "E") {
		t.Errorf("Edge should already exist")
	}
}

func TestShould_pass_connections_When_remove_proxy_edge(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("X", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")
	testGraph.SetEdgeColor("B", "C", "blue")

	testGraph.DeleteProxyEdge("X", "B")

	if !testGraph.HasNode("B") {
		t.Errorf("Node should be there")
	}

	if testGraph.HasEdge("A", "C") {
		t.Errorf("Edge shouldn't exist")
	}
	if testGraph.HasEdge("A", "D") {
		t.Errorf("Edge shouldn't exist")
	}
	if testGraph.HasEdge("A", "E") {
		t.Errorf("Edge shouldn't exist")
	}

	if !testGraph.HasEdge("X", "C") {
		t.Errorf("Edge should exist")
	}
	if attributes, err := testGraph.GetEdgeAttributes("X", "C"); err == nil {
		if attributes["color"] != "\"blue\"" {
			t.Logf("Edge color: %s", attributes["color"])
			t.Errorf("Edge X -> C should be blue")
		}
	} else {
		t.Errorf("Edge should already exist")
	}

	if !testGraph.HasEdge("X", "D") {
		t.Errorf("Edge should exist")
	}
	if !testGraph.HasEdge("X", "E") {
		t.Errorf("Edge should exist")
	}
}

func TestShould_not_pass_connections_When_remove_proxy_edge_isnt_connected(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("A", "B")
	testGraph.AddEdge("B", "C")
	testGraph.AddEdge("C", "X")
	testGraph.AddEdge("B", "D")
	testGraph.AddEdge("B", "E")

	if testGraph.HasEdge("A", "X") {
		t.Errorf("Edge should exist")
	}

	testGraph.DeleteProxyEdge("A", "C")

	if testGraph.HasEdge("A", "X") {
		t.Log(ToString(testGraph))
		t.Errorf("Edge should exist")
	}
}

func Test_TopologicalSortMaintainsSubnodeOrder(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")
	testGraph.AddEdge("top", "c")
	testGraph.AddEdge("top", "d")
	testGraph.AddEdge("top", "e")
	testGraph.AddEdge("top", "f")

	// To ensure orderring, visit the top node first
	testGraph.SetNodePriority("top", -1)

	target := []string{"top", "a", "b", "c", "d", "e", "f"}

	sorted, _ := TopologicalSort(testGraph)
	assert.Equal(t, target, sorted)

	// If we copy the graph we should still get the same sort result
	subGraph := GetSubgraph(testGraph, "top")

	sorted, _ = TopologicalSort(subGraph)
	assert.Equal(t, target, sorted)
}

func Test_SubnodeOrderAfterDeleteProxyEdges(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")
	testGraph.AddEdge("top", "c")
	testGraph.AddEdge("top", "d")

	testGraph.AddEdge("d", "df")
	testGraph.AddEdge("d", "de")
	testGraph.AddEdge("c", "cf")
	testGraph.AddEdge("c", "ce")
	testGraph.AddEdge("b", "bf")
	testGraph.AddEdge("b", "be")
	testGraph.AddEdge("a", "af")
	testGraph.AddEdge("a", "ae")

	testGraph.SetEdgeColor("top", "a", "red")
	testGraph.SetEdgeColor("top", "b", "red")
	testGraph.SetEdgeColor("top", "c", "red")
	testGraph.SetEdgeColor("top", "d", "red")

	testGraph.DeleteProxyEdges("red")

	// Remove unref'd nodes
	testGraph = GetSubgraph(testGraph, "top")

	// To ensure orderring, visit the top node first
	testGraph.SetNodePriority("top", -1)

	// ProxyEdges should be removed in subnode order, and their children added in order
	target := []string{"top", "af", "ae", "bf", "be", "cf", "ce", "df", "de"}

	sorted, _ := TopologicalSort(testGraph)
	assert.Equal(t, target, sorted)
}

func Test_GetSubgraphNodeCount(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")

	testGraph.AddEdge("a", "a0")
	testGraph.AddEdge("a", "a1")
	testGraph.AddEdge("b", "b0")
	testGraph.AddEdge("b", "b1")

	nodes := []string{"top", "a", "a0", "a1", "b", "b0", "b1"}

	for _, node := range nodes {
		expected := GetSubgraph(testGraph, node).GetNodeCount()
		result := GetSubgraphNodeCount(testGraph, node)
		assert.Equal(t, expected, result)
	}
}

func Test_GetSubgraphHasNode(t *testing.T) {
	testGraph := NewGraph("Test")
	testGraph.AddEdge("top", "a")
	testGraph.AddEdge("top", "b")

	testGraph.AddEdge("a", "a0")
	testGraph.AddEdge("a", "a1")
	testGraph.AddEdge("b", "b0")
	testGraph.AddEdge("b", "b1")

	nodes := []string{"top", "a", "a0", "a1", "b", "b0", "b1"}

	for _, node1 := range nodes {
		for _, node2 := range nodes {
			expected := GetSubgraph(testGraph, node1).HasNode(node2)
			result := GetSubgraphHasNode(testGraph, node1, node2)
			assert.Equal(t, expected, result)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"code.cloudfoundry.org/lager"
	flags "github.com/jessevdk/go-flags"
	"github.com/robdimsdale/honeylager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"

	"cred-alert/ccp/api"
	"cred-alert/ccp/web"
	"cred-alert/config"
	"cred-alert/revokpb"
)

const (
	// Required.
	portEnvKey = "PORT"

	// Passphrase for the client private key. Required if the key is encrypted.
	clientKeyPassphraseEnvKey = "CLIENT_KEY_PASSPHRASE"

	// Address for RPC server. Required.
	rpcServerAddressEnvKey = "RPC_SERVER_ADDRESS"

	// Port for RPC server. Required.
	rpcServerPortEnvKey = "RPC_SERVER_PORT"

	// Required.
	caCertEnvKey = "SERVER_CA_CERT"

	// Optional
	honeycombWriteKeyEnvKey = "HONEYCOMB_WRITE_KEY"
	honeycombDatasetEnvKey  = "HONEYCOMB_DATASET"
)

type Opts struct {
	ClientCertPath string `long:"client-cert-path" description:"Path to the client certificate" required:"true"`
	ClientKeyPath  string `long:"client-key-path" description:"Path to the client private key" required:"true"`
}

var (
	indexLayout        *template.Template
	organizationLayout *template.Template
	repositoryLayout   *template.Template
	logger             lager.Logger
)

func init() {
	bs, err := web.Asset("web/templates/index.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	indexLayout = template.Must(template.New("index.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/organization.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	organizationLayout = template.Must(template.New("organization.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/repository.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	repositoryLayout = template.Must(template.New("repository.html").Parse(string(bs)))

	logger = lager.NewLogger("credential-count-publisher")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.INFO))
}

func main() {
	var opts Opts

	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}

	honeycombWriteKey := os.Getenv(honeycombWriteKeyEnvKey)
	honeycombDataset := os.Getenv(honeycombDatasetEnvKey)
	if honeycombWriteKey != "" && honeycombDataset != "" {
		s := honeylager.NewSink(honeycombWriteKey, honeycombDataset, lager.DEBUG)
		defer s.Close()
		logger.RegisterSink(s)
	} else {
		logger.Info(fmt.Sprintf(
			"Honeycomb not configured - need %s and %s",
			honeycombWriteKeyEnvKey,
			honeycombDatasetEnvKey,
		))
	}

	logger.Info("starting")

	portStr := mustGetEnv(portEnvKey)
	port, err := strconv.Atoi(portStr)
	if err != nil {
		logger.Fatal("failed-to-parse-port", err)
	}

	rpcServerAddress := mustGetEnv(rpcServerAddressEnvKey)

	rpcServerPortStr := mustGetEnv(rpcServerPortEnvKey)
	rpcServerPort, err := strconv.Atoi(rpcServerPortStr)
	if err != nil {
		logger.Fatal("failed-to-parse-rpc-server-port", err)
	}

	serverAddr := fmt.Sprintf("%s:%d", rpcServerAddress, rpcServerPort)
	listenAddr := fmt.Sprintf(":%d", port)

	clientCert, err := config.LoadCertificate(
		opts.ClientCertPath,
		opts.ClientKeyPath,
		os.Getenv(clientKeyPassphraseEnvKey),
	)
	if err != nil {
		log.Fatalln(err)
	}

	caCert := mustGetEnv(caCertEnvKey)

	rootCertPool, err := config.LoadCertificatePool(caCert)
	if err != nil {
		log.Fatalln(err)
	}

	transportCreds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCertPool,
	})

	conn, err := grpc.Dial(
		serverAddr,
		grpc.WithTransportCredentials(transportCreds),
		grpc.WithDialer(keepAliveDial),
	)
	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}
	defer conn.Close()

	revokClient := revokpb.NewRevokAPIClient(conn)

	handler, err := rata.NewRouter(web.Routes, rata.Handlers{
		web.Index:        api.NewIndexHandler(logger, indexLayout, revokClient),
		web.Organization: api.NewOrganizationHandler(logger, organizationLayout, revokClient),
		web.Repository:   api.NewRepositoryHandler(logger, repositoryLayout, revokClient),
	})

	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}

	runner := sigmon.New(http_server.New(listenAddr, handler))

	err = <-ifrit.Invoke(runner).Wait()
	if err != nil {
		logger.Error("running-server-failed", err)
	}
}

func keepAliveDial(addr string, timeout time.Duration) (net.Conn, error) {
	d := net.Dialer{
		Timeout:   timeout,
		KeepAlive: 60 * time.Second,
	}
	return d.Dial("tcp", addr)
}

func mustGetEnv(key string) string {
	val := os.Getenv(key)
	err := fmt.Errorf("failed-to-get-env-key")
	if val == "" {
		logger.Fatal("failed-to-get-env-key", err, lager.Data{"missing-env-key": key})
	}

	return val
}
<commit_msg>Revert "Refactor mustGetEnv to use existing logger."<commit_after>package main

import (
	"crypto/tls"
	"fmt"
	"html/template"
	"log"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"code.cloudfoundry.org/lager"
	flags "github.com/jessevdk/go-flags"
	"github.com/robdimsdale/honeylager"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/http_server"
	"github.com/tedsuo/ifrit/sigmon"
	"github.com/tedsuo/rata"

	"cred-alert/ccp/api"
	"cred-alert/ccp/web"
	"cred-alert/config"
	"cred-alert/revokpb"
)

const (
	// Required.
	portEnvKey = "PORT"

	// Passphrase for the client private key. Required if the key is encrypted.
	clientKeyPassphraseEnvKey = "CLIENT_KEY_PASSPHRASE"

	// Address for RPC server. Required.
	rpcServerAddressEnvKey = "RPC_SERVER_ADDRESS"

	// Port for RPC server. Required.
	rpcServerPortEnvKey = "RPC_SERVER_PORT"

	// Required.
	caCertEnvKey = "SERVER_CA_CERT"

	// Optional
	honeycombWriteKeyEnvKey = "HONEYCOMB_WRITE_KEY"
	honeycombDatasetEnvKey  = "HONEYCOMB_DATASET"
)

type Opts struct {
	ClientCertPath string `long:"client-cert-path" description:"Path to the client certificate" required:"true"`
	ClientKeyPath  string `long:"client-key-path" description:"Path to the client private key" required:"true"`
}

var (
	indexLayout        *template.Template
	organizationLayout *template.Template
	repositoryLayout   *template.Template
	logger             lager.Logger
)

func init() {
	bs, err := web.Asset("web/templates/index.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	indexLayout = template.Must(template.New("index.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/organization.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	organizationLayout = template.Must(template.New("organization.html").Parse(string(bs)))

	bs, err = web.Asset("web/templates/repository.html")
	if err != nil {
		log.Fatalf("failed loading asset: %s", err.Error())
	}
	repositoryLayout = template.Must(template.New("repository.html").Parse(string(bs)))

	logger = lager.NewLogger("credential-count-publisher")
	logger.RegisterSink(lager.NewWriterSink(os.Stdout, lager.INFO))
}

func main() {
	var opts Opts

	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}

	honeycombWriteKey := os.Getenv(honeycombWriteKeyEnvKey)
	honeycombDataset := os.Getenv(honeycombDatasetEnvKey)
	if honeycombWriteKey != "" && honeycombDataset != "" {
		s := honeylager.NewSink(honeycombWriteKey, honeycombDataset, lager.DEBUG)
		defer s.Close()
		logger.RegisterSink(s)
	} else {
		logger.Info(fmt.Sprintf(
			"Honeycomb not configured - need %s and %s",
			honeycombWriteKeyEnvKey,
			honeycombDatasetEnvKey,
		))
	}

	logger.Info("starting")

	portStr := mustGetEnv(logger, portEnvKey)
	port, err := strconv.Atoi(portStr)
	if err != nil {
		logger.Fatal("failed-to-parse-port", err)
	}

	rpcServerAddress := mustGetEnv(logger, rpcServerAddressEnvKey)

	rpcServerPortStr := mustGetEnv(logger, rpcServerPortEnvKey)
	rpcServerPort, err := strconv.Atoi(rpcServerPortStr)
	if err != nil {
		logger.Fatal("failed-to-parse-rpc-server-port", err)
	}

	serverAddr := fmt.Sprintf("%s:%d", rpcServerAddress, rpcServerPort)
	listenAddr := fmt.Sprintf(":%d", port)

	clientCert, err := config.LoadCertificate(
		opts.ClientCertPath,
		opts.ClientKeyPath,
		os.Getenv(clientKeyPassphraseEnvKey),
	)
	if err != nil {
		log.Fatalln(err)
	}

	caCert := mustGetEnv(logger, caCertEnvKey)

	rootCertPool, err := config.LoadCertificatePool(caCert)
	if err != nil {
		log.Fatalln(err)
	}

	transportCreds := credentials.NewTLS(&tls.Config{
		Certificates: []tls.Certificate{clientCert},
		RootCAs:      rootCertPool,
	})

	conn, err := grpc.Dial(
		serverAddr,
		grpc.WithTransportCredentials(transportCreds),
		grpc.WithDialer(keepAliveDial),
	)
	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}
	defer conn.Close()

	revokClient := revokpb.NewRevokAPIClient(conn)

	handler, err := rata.NewRouter(web.Routes, rata.Handlers{
		web.Index:        api.NewIndexHandler(logger, indexLayout, revokClient),
		web.Organization: api.NewOrganizationHandler(logger, organizationLayout, revokClient),
		web.Repository:   api.NewRepositoryHandler(logger, repositoryLayout, revokClient),
	})

	if err != nil {
		log.Fatalf("failed to create handler: %s", err.Error())
	}

	runner := sigmon.New(http_server.New(listenAddr, handler))

	err = <-ifrit.Invoke(runner).Wait()
	if err != nil {
		logger.Error("running-server-failed", err)
	}
}

func keepAliveDial(addr string, timeout time.Duration) (net.Conn, error) {
	d := net.Dialer{
		Timeout:   timeout,
		KeepAlive: 60 * time.Second,
	}
	return d.Dial("tcp", addr)
}

func mustGetEnv(logger lager.Logger, key string) string {
	val := os.Getenv(key)
	err := fmt.Errorf("failed-to-get-env-key")
	if val == "" {
		logger.Fatal("failed-to-get-env-key", err, lager.Data{"missing-env-key": key})
	}

	return val
}
<|endoftext|>


<commit_before>package telegram

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	"github.com/scbizu/Astral/internal/plugin/hub"
	"github.com/scbizu/Astral/pkg/getcert"
	"github.com/scbizu/Astral/pkg/talker"
	"github.com/scbizu/Astral/pkg/talker/dce"
	"github.com/sirupsen/logrus"
)

type Bot struct {
	bot         *tgbotapi.BotAPI
	isDebugMode bool
}

func NewBot(isDebugMode bool) (*Bot, error) {
	bot := new(Bot)
	tgConn, err := ConnectTG()
	if err != nil {
		return nil, err
	}
	bot.bot = tgConn
	if isDebugMode {
		logrus.SetLevel(logrus.DebugLevel)
		bot.isDebugMode = true
		tgConn.Debug = true
		logrus.Infof("bot auth passed as %s", tgConn.Self.UserName)
	}
	return bot, nil
}

func listenWebhook() {
	port := fmt.Sprintf(":%s", os.Getenv("LISTENPORT"))
	http.ListenAndServe(port, nil)
}

func (b *Bot) ServeBotUpdateMessage() error {
	go listenWebhook()
	b.bot.RemoveWebhook()
	cert := getcert.NewDomainCert(tgAPIDomain)
	domainWithToken := fmt.Sprintf("%s/%s", cert.GetDomain(), token)
	if _, err := b.bot.SetWebhook(tgbotapi.NewWebhook(domainWithToken)); err != nil {
		logrus.Errorf("notify webhook failed:%s", err.Error())
		return err
	}
	if b.isDebugMode {
		logrus.SetLevel(logrus.DebugLevel)
		info, err := b.bot.GetWebhookInfo()
		if err != nil {
			return err
		}
		logrus.Debug(info.LastErrorMessage, info.LastErrorDate)
	}
	pattern := fmt.Sprintf("/tg/%s", token)
	updatesMsgChannel := b.bot.ListenForWebhook(pattern)

	logrus.Debugf("msg in channel:%d", len(updatesMsgChannel))
	for update := range updatesMsgChannel {
		logrus.Debugf("[raw msg]:%#v\n", update)

		if update.Message == nil {
			continue
		}
		pluginHub := hub.NewTGPluginHub(update.Message)
		msg := pluginHub.RegistTGEnabledPlugins(update.Message)
		if isMsgBadRequest(msg) {
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, "Astral服务酱表示不想理你")
		}

		mbNames, ok := isMsgNewMember(update)
		if ok {
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("撒花欢迎新基佬(%v)入群", mbNames))
		}

		_, ok = isMsgLeftMember(update)
		if ok {
			// drop the member left message
			continue
		}

		msg.ReplyToMessageID = update.Message.MessageID
		b.bot.Send(msg)
	}
	return nil
}

func (b *Bot) ServePushAstralServerMessage() {
	go healthCheck(b.bot)
	registerDCEServer(b.bot)
}

func isMsgNewMember(update tgbotapi.Update) ([]string, bool) {
	members := update.Message.NewChatMembers
	if members == nil {
		return nil, false
	}
	if len(*members) == 0 {
		return nil, false
	}
	var mbNames []string
	for _, m := range *members {
		mbNames = append(mbNames, m.String())
	}
	return mbNames, true
}

func isMsgLeftMember(update tgbotapi.Update) (string, bool) {
	if update.Message.LeftChatMember == nil {
		return "", false
	}
	mbName := update.Message.LeftChatMember.String()
	return mbName, true
}

func isMsgBadRequest(msg tgbotapi.MessageConfig) bool {
	if msg.Text == "" || msg.ChatID == 0 {
		return true
	}
	return false
}

func registerDCEServer(bot *tgbotapi.BotAPI) {
	dceListenHandler := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			r.ParseForm()
			body, err := ioutil.ReadAll(r.Body)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			defer r.Body.Close()
			dceObj, err := dce.NewDCEObj(string(body))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			noti := talker.NewNotifaction(dceObj.GetRepoName(),
				dceObj.GetStageMap(), dceObj.GetCommitMsg(),
				dceObj.GetBuildDuration())
			respMsg, err := bot.Send(noti.Notify())
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(err.Error()))
				return
			}
			w.WriteHeader(http.StatusOK)
			w.Write([]byte(respMsg.Text))
		} else {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Astral denied your request"))
		}
	}

	http.HandleFunc("/alert/dce", dceListenHandler)

	port := fmt.Sprintf(":%s", os.Getenv("LISTENPORT"))

	http.ListenAndServe(port, nil)
}
<commit_msg>internal/telegram: add error log for alert service<commit_after>package telegram

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	"github.com/scbizu/Astral/internal/plugin/hub"
	"github.com/scbizu/Astral/pkg/getcert"
	"github.com/scbizu/Astral/pkg/talker"
	"github.com/scbizu/Astral/pkg/talker/dce"
	"github.com/sirupsen/logrus"
)

type Bot struct {
	bot         *tgbotapi.BotAPI
	isDebugMode bool
}

func NewBot(isDebugMode bool) (*Bot, error) {
	bot := new(Bot)
	tgConn, err := ConnectTG()
	if err != nil {
		return nil, err
	}
	bot.bot = tgConn
	if isDebugMode {
		logrus.SetLevel(logrus.DebugLevel)
		bot.isDebugMode = true
		tgConn.Debug = true
		logrus.Infof("bot auth passed as %s", tgConn.Self.UserName)
	}
	return bot, nil
}

func listenWebhook() {
	port := fmt.Sprintf(":%s", os.Getenv("LISTENPORT"))
	http.ListenAndServe(port, nil)
}

func (b *Bot) ServeBotUpdateMessage() error {
	go listenWebhook()
	b.bot.RemoveWebhook()
	cert := getcert.NewDomainCert(tgAPIDomain)
	domainWithToken := fmt.Sprintf("%s/%s", cert.GetDomain(), token)
	if _, err := b.bot.SetWebhook(tgbotapi.NewWebhook(domainWithToken)); err != nil {
		logrus.Errorf("notify webhook failed:%s", err.Error())
		return err
	}
	if b.isDebugMode {
		logrus.SetLevel(logrus.DebugLevel)
		info, err := b.bot.GetWebhookInfo()
		if err != nil {
			return err
		}
		logrus.Debug(info.LastErrorMessage, info.LastErrorDate)
	}
	pattern := fmt.Sprintf("/tg/%s", token)
	updatesMsgChannel := b.bot.ListenForWebhook(pattern)

	logrus.Debugf("msg in channel:%d", len(updatesMsgChannel))
	for update := range updatesMsgChannel {
		logrus.Debugf("[raw msg]:%#v\n", update)

		if update.Message == nil {
			continue
		}
		pluginHub := hub.NewTGPluginHub(update.Message)
		msg := pluginHub.RegistTGEnabledPlugins(update.Message)
		if isMsgBadRequest(msg) {
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, "Astral服务酱表示不想理你")
		}

		mbNames, ok := isMsgNewMember(update)
		if ok {
			msg = tgbotapi.NewMessage(update.Message.Chat.ID, fmt.Sprintf("撒花欢迎新基佬(%v)入群", mbNames))
		}

		_, ok = isMsgLeftMember(update)
		if ok {
			// drop the member left message
			continue
		}

		msg.ReplyToMessageID = update.Message.MessageID
		b.bot.Send(msg)
	}
	return nil
}

func (b *Bot) ServePushAstralServerMessage() {
	go healthCheck(b.bot)
	registerDCEServer(b.bot)
}

func isMsgNewMember(update tgbotapi.Update) ([]string, bool) {
	members := update.Message.NewChatMembers
	if members == nil {
		return nil, false
	}
	if len(*members) == 0 {
		return nil, false
	}
	var mbNames []string
	for _, m := range *members {
		mbNames = append(mbNames, m.String())
	}
	return mbNames, true
}

func isMsgLeftMember(update tgbotapi.Update) (string, bool) {
	if update.Message.LeftChatMember == nil {
		return "", false
	}
	mbName := update.Message.LeftChatMember.String()
	return mbName, true
}

func isMsgBadRequest(msg tgbotapi.MessageConfig) bool {
	if msg.Text == "" || msg.ChatID == 0 {
		return true
	}
	return false
}

func registerDCEServer(bot *tgbotapi.BotAPI) {
	dceListenHandler := func(w http.ResponseWriter, r *http.Request) {
		if r.Method == http.MethodPost {
			r.ParseForm()
			body, err := ioutil.ReadAll(r.Body)
			if err != nil {
				logrus.Errorf("dce: read webhook msg failed: %q", err.Error())
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			defer r.Body.Close()
			dceObj, err := dce.NewDCEObj(string(body))
			if err != nil {
				logrus.Errorf("dce: read webhook msg failed: %q", err.Error())
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			// TODO: talker need to be REMOVED ASAP
			noti := talker.NewNotifaction(dceObj.GetRepoName(),
				dceObj.GetStageMap(), dceObj.GetCommitMsg(),
				dceObj.GetBuildDuration())

			respMsg, err := bot.Send(noti.Notify())
			if err != nil {
				logrus.Errorf("telegram bot: send server info failed: %q", err.Error())
				return
			}

			w.WriteHeader(http.StatusOK)
			w.Write([]byte(respMsg.Text))
		} else {
			w.WriteHeader(http.StatusForbidden)
			w.Write([]byte("Astral has denied your request"))
		}
	}

	http.HandleFunc("/alert/dce", dceListenHandler)

	port := fmt.Sprintf(":%s", os.Getenv("LISTENPORT"))

	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package globalflag

import (
	"flag"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/spf13/pflag"
	cliflag "k8s.io/component-base/cli/flag"
)

func TestAddGlobalFlags(t *testing.T) {
	namedFlagSets := &cliflag.NamedFlagSets{}
	nfs := namedFlagSets.FlagSet("global")
	AddGlobalFlags(nfs, "test-cmd")

	actualFlag := []string{}
	nfs.VisitAll(func(flag *pflag.Flag) {
		actualFlag = append(actualFlag, flag.Name)
	})

	// Get all flags from flags.CommandLine, except flag `test.*`.
	wantedFlag := []string{"help"}
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.VisitAll(func(flag *pflag.Flag) {
		if !strings.Contains(flag.Name, "test.") {
			wantedFlag = append(wantedFlag, normalize(flag.Name))
		}
	})
	sort.Strings(wantedFlag)

	if !reflect.DeepEqual(wantedFlag, actualFlag) {
		t.Errorf("[Default]: expected %+v, got %+v", wantedFlag, actualFlag)
	}

	tests := []struct {
		expectedFlag  []string
		matchExpected bool
	}{
		{
			// Happy case
			expectedFlag:  []string{"alsologtostderr", "help", "log-backtrace-at", "log-dir", "log-file", "log-file-max-size", "log-flush-frequency", "logtostderr", "skip-headers", "skip-log-headers", "stderrthreshold", "v", "vmodule"},
			matchExpected: false,
		},
		{
			// Missing flag
			expectedFlag:  []string{"logtostderr", "log-dir"},
			matchExpected: true,
		},
		{
			// Empty flag
			expectedFlag:  []string{},
			matchExpected: true,
		},
		{
			// Invalid flag
			expectedFlag:  []string{"foo"},
			matchExpected: true,
		},
	}

	for i, test := range tests {
		if reflect.DeepEqual(test.expectedFlag, actualFlag) == test.matchExpected {
			t.Errorf("[%d]: expected %+v, got %+v", i, test.expectedFlag, actualFlag)
		}
	}
}
<commit_msg>Adjust test case for a new command line parameter<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package globalflag

import (
	"flag"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/spf13/pflag"
	cliflag "k8s.io/component-base/cli/flag"
)

func TestAddGlobalFlags(t *testing.T) {
	namedFlagSets := &cliflag.NamedFlagSets{}
	nfs := namedFlagSets.FlagSet("global")
	AddGlobalFlags(nfs, "test-cmd")

	actualFlag := []string{}
	nfs.VisitAll(func(flag *pflag.Flag) {
		actualFlag = append(actualFlag, flag.Name)
	})

	// Get all flags from flags.CommandLine, except flag `test.*`.
	wantedFlag := []string{"help"}
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.VisitAll(func(flag *pflag.Flag) {
		if !strings.Contains(flag.Name, "test.") {
			wantedFlag = append(wantedFlag, normalize(flag.Name))
		}
	})
	sort.Strings(wantedFlag)

	if !reflect.DeepEqual(wantedFlag, actualFlag) {
		t.Errorf("[Default]: expected %+v, got %+v", wantedFlag, actualFlag)
	}

	tests := []struct {
		expectedFlag  []string
		matchExpected bool
	}{
		{
			// Happy case
			expectedFlag:  []string{"add-dir-header", "alsologtostderr", "help", "log-backtrace-at", "log-dir", "log-file", "log-file-max-size", "log-flush-frequency", "logtostderr", "skip-headers", "skip-log-headers", "stderrthreshold", "v", "vmodule"},
			matchExpected: false,
		},
		{
			// Missing flag
			expectedFlag:  []string{"logtostderr", "log-dir"},
			matchExpected: true,
		},
		{
			// Empty flag
			expectedFlag:  []string{},
			matchExpected: true,
		},
		{
			// Invalid flag
			expectedFlag:  []string{"foo"},
			matchExpected: true,
		},
	}

	for i, test := range tests {
		if reflect.DeepEqual(test.expectedFlag, actualFlag) == test.matchExpected {
			t.Errorf("[%d]: expected %+v, got %+v", i, test.expectedFlag, actualFlag)
		}
	}
}
<|endoftext|>


<commit_before>package network

import (
    "fmt"
    "net"
    "bufio"
    "encoding/binary"
    "time"

)

const (
    XML_REP = `<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>`

    LEN_HEAD    =   2
    LEN_URI     =   2
    MAX_LEN_HEAD   = 1024 * 4
)

type ClientConnection struct {
    conn        net.Conn
    reader      *bufio.Reader
    writer      *bufio.Writer
    sendchan    chan []byte
}

func NewClientConnection(c net.Conn) *ClientConnection {
    cliConn := new(ClientConnection)
    cliConn.conn = c
    cliConn.reader = bufio.NewReader(c)
    cliConn.writer = bufio.NewWriter(c)
    cliConn.sendchan = make(chan []byte, 64)
    return cliConn
}

func (this *ClientConnection) Send(buf []byte) {
    head := make([]byte, LEN_HEAD)
    binary.LittleEndian.PutUint16(head, uint16(len(buf)))
    buf = append(head, buf...)

    select {
        case this.sendchan <- buf:

        default:
            //fmt.Println("send chan overflow or closed")
    }
}

func (this *ClientConnection) sendall() bool {
    for more := true; more; {
        select {
            case b := <-this.sendchan:
                if _, err := this.writer.Write(b); err != nil {
                    fmt.Println("write err:", err)
                    return false
                }
            default:
                more = false
        }
    }

    if err := this.writer.Flush(); err != nil {
        fmt.Println("flush err:", err)
        return false
    }
    return true
}

func (this *ClientConnection) duplexRead(buff []byte) bool {
    var read_size int
    for {
        // write
        if !this.sendall() {
            break
        }

        // read
        this.conn.SetReadDeadline(time.Now().Add(1e8))
        n, err := this.reader.Read(buff[read_size:])
        if err != nil {
            if e, ok := err.(*net.OpError); ok && e.Temporary() {
                read_size = n
                continue
            } else {
                //fmt.Println("read err disconnect:", err)
                break
            }
        }

        if n == 0 { return true }
        if n < len(buff) {
            read_size += n
            continue
        }
        return true
    }
    this.Close()
    return false
}

func (this *ClientConnection) duplexReadBody() (ret []byte,  ok bool) {
    buff_head := make([]byte, LEN_HEAD)
    if !this.duplexRead(buff_head) {
        return
    }
    len_head := binary.LittleEndian.Uint16(buff_head)
    if len_head > MAX_LEN_HEAD {
        if len_head == 42556 {
            this.WriteFlashAuthRep()
        } else {
            fmt.Println("message len too long", len_head)
        }
        this.Close()
        return
    }
    ret = make([]byte, len_head)
    if !this.duplexRead(ret) {
        return
    }
    ok = true
    return
}

func (this *ClientConnection) Close() {
    this.sendchan = nil
    this.conn.Close()
}

func (this *ClientConnection) WriteFlashAuthRep() {
    this.writer.Write([]byte(XML_REP))
    this.writer.Write([]byte("\x00"))
    this.writer.Flush()
}

<commit_msg>flash magic-num<commit_after>package network

import (
    "fmt"
    "net"
    "bufio"
    "encoding/binary"
    "time"

)

const (
    XML_REP = `<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>`

    LEN_HEAD    =   2
    LEN_URI     =   2
    MAX_LEN_HEAD   = 1024 * 4
)

type ClientConnection struct {
    conn        net.Conn
    reader      *bufio.Reader
    writer      *bufio.Writer
    sendchan    chan []byte
}

func NewClientConnection(c net.Conn) *ClientConnection {
    cliConn := new(ClientConnection)
    cliConn.conn = c
    cliConn.reader = bufio.NewReader(c)
    cliConn.writer = bufio.NewWriter(c)
    cliConn.sendchan = make(chan []byte, 64)
    return cliConn
}

func (this *ClientConnection) Send(buf []byte) {
    head := make([]byte, LEN_HEAD)
    binary.LittleEndian.PutUint16(head, uint16(len(buf)))
    buf = append(head, buf...)

    select {
        case this.sendchan <- buf:

        default:
            //fmt.Println("send chan overflow or closed")
    }
}

func (this *ClientConnection) sendall() bool {
    for more := true; more; {
        select {
            case b := <-this.sendchan:
                if _, err := this.writer.Write(b); err != nil {
                    fmt.Println("write err:", err)
                    return false
                }
            default:
                more = false
        }
    }

    if err := this.writer.Flush(); err != nil {
        fmt.Println("flush err:", err)
        return false
    }
    return true
}

func (this *ClientConnection) duplexRead(buff []byte) bool {
    var read_size int
    for {
        // write
        if !this.sendall() {
            break
        }

        // read
        this.conn.SetReadDeadline(time.Now().Add(1e8))
        n, err := this.reader.Read(buff[read_size:])
        if err != nil {
            if e, ok := err.(*net.OpError); ok && e.Temporary() {
                read_size = n
                continue
            } else {
                //fmt.Println("read err disconnect:", err)
                break
            }
        }

        if n == 0 { return true }
        if n < len(buff) {
            read_size += n
            continue
        }
        return true
    }
    this.Close()
    return false
}

func (this *ClientConnection) duplexReadBody() (ret []byte,  ok bool) {
    buff_head := make([]byte, LEN_HEAD)
    if !this.duplexRead(buff_head) {
        return
    }
    len_head := binary.LittleEndian.Uint16(buff_head)
    if len_head > MAX_LEN_HEAD {
        if len_head == 28732 {
            this.WriteFlashAuthRep()
        } else {
            fmt.Println("message len too long", len_head)
        }
        this.Close()
        return
    }
    ret = make([]byte, len_head)
    if !this.duplexRead(ret) {
        return
    }
    ok = true
    return
}

func (this *ClientConnection) Close() {
    this.sendchan = nil
    this.conn.Close()
}

func (this *ClientConnection) WriteFlashAuthRep() {
    this.writer.Write([]byte(XML_REP))
    this.writer.Write([]byte("\x00"))
    this.writer.Flush()
}

<|endoftext|>


<commit_before>package network

import (
	"github.com/name5566/leaf/log"
	"net"
	"sync"
	"time"
)

type TCPServer struct {
	Addr            string
	MaxConnNum      int
	PendingWriteNum int
	NewAgent        func(*TCPConn) Agent
	ln              net.Listener
	conns           ConnSet
	mutexConns      sync.Mutex
	wgLn            sync.WaitGroup
	wgConns         sync.WaitGroup

	// msg parser
	LenMsgLen    int
	MinMsgLen    uint32
	MaxMsgLen    uint32
	LittleEndian bool
	msgParser    *MsgParser
}

func (server *TCPServer) Start() {
	server.init()
	go server.run()
}

func (server *TCPServer) init() {
	ln, err := net.Listen("tcp", server.Addr)
	if err != nil {
		log.Fatal("%v", err)
	}

	if server.MaxConnNum <= 0 {
		server.MaxConnNum = 100
		log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum)
	}
	if server.PendingWriteNum <= 0 {
		server.PendingWriteNum = 100
		log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum)
	}
	if server.NewAgent == nil {
		log.Fatal("NewAgent must not be nil")
	}

	server.ln = ln
	server.conns = make(ConnSet)

	// msg parser
	msgParser := NewMsgParser()
	msgParser.SetMsgLen(server.LenMsgLen, server.MinMsgLen, server.MaxMsgLen)
	msgParser.SetByteOrder(server.LittleEndian)
	server.msgParser = msgParser
}

func (server *TCPServer) run() {
	server.wgLn.Add(1)
	defer server.wgLn.Done()

	var tempDelay time.Duration
	for {
		conn, err := server.ln.Accept()
		if err != nil {
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Release("accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return
		}
		tempDelay = 0

		server.mutexConns.Lock()
		if len(server.conns) >= server.MaxConnNum {
			server.mutexConns.Unlock()
			conn.Close()
			log.Debug("too many connections")
			continue
		}
		server.conns[conn] = struct{}{}
		server.mutexConns.Unlock()

		server.wgConns.Add(1)

		tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser)
		agent := server.NewAgent(tcpConn)
		go func() {
			agent.Run()

			// cleanup
			tcpConn.Close()
			server.mutexConns.Lock()
			delete(server.conns, conn)
			server.mutexConns.Unlock()
			agent.OnClose()

			server.wgConns.Done()
		}()
	}
}

func (server *TCPServer) Close() {
	server.ln.Close()
	server.wgLn.Wait()

	server.mutexConns.Lock()
	for conn := range server.conns {
		conn.Close()
	}
	server.conns = nil
	server.mutexConns.Unlock()
	server.wgConns.Wait()
}
<commit_msg>bug fix on TCPServer Close<commit_after>package network

import (
	"github.com/name5566/leaf/log"
	"net"
	"sync"
	"time"
)

type TCPServer struct {
	Addr            string
	MaxConnNum      int
	PendingWriteNum int
	NewAgent        func(*TCPConn) Agent
	ln              net.Listener
	conns           ConnSet
	mutexConns      sync.Mutex
	wgLn            sync.WaitGroup
	wgConns         sync.WaitGroup

	// msg parser
	LenMsgLen    int
	MinMsgLen    uint32
	MaxMsgLen    uint32
	LittleEndian bool
	msgParser    *MsgParser
}

func (server *TCPServer) Start() {
	server.init()
	go server.run()
}

func (server *TCPServer) init() {
	ln, err := net.Listen("tcp", server.Addr)
	if err != nil {
		log.Fatal("%v", err)
	}

	if server.MaxConnNum <= 0 {
		server.MaxConnNum = 100
		log.Release("invalid MaxConnNum, reset to %v", server.MaxConnNum)
	}
	if server.PendingWriteNum <= 0 {
		server.PendingWriteNum = 100
		log.Release("invalid PendingWriteNum, reset to %v", server.PendingWriteNum)
	}
	if server.NewAgent == nil {
		log.Fatal("NewAgent must not be nil")
	}

	server.ln = ln
	server.conns = make(ConnSet)

	// msg parser
	msgParser := NewMsgParser()
	msgParser.SetMsgLen(server.LenMsgLen, server.MinMsgLen, server.MaxMsgLen)
	msgParser.SetByteOrder(server.LittleEndian)
	server.msgParser = msgParser
}

func (server *TCPServer) run() {
	server.wgLn.Add(1)
	defer server.wgLn.Done()

	var tempDelay time.Duration
	for {
		conn, err := server.ln.Accept()
		if err != nil {
			if ne, ok := err.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				log.Release("accept error: %v; retrying in %v", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return
		}
		tempDelay = 0

		server.mutexConns.Lock()
		if len(server.conns) >= server.MaxConnNum {
			server.mutexConns.Unlock()
			conn.Close()
			log.Debug("too many connections")
			continue
		}
		server.conns[conn] = struct{}{}
		server.mutexConns.Unlock()

		server.wgConns.Add(1)

		tcpConn := newTCPConn(conn, server.PendingWriteNum, server.msgParser)
		agent := server.NewAgent(tcpConn)
		go func() {
			agent.Run()

			// cleanup
			tcpConn.Close()
			server.mutexConns.Lock()
			delete(server.conns, conn)
			server.mutexConns.Unlock()
			agent.OnClose()

			server.wgConns.Done()
		}()
	}
}

func (server *TCPServer) Close() {
	server.ln.Close()
	server.wgLn.Wait()

	server.mutexConns.Lock()
	for conn := range server.conns {
		conn.Close()
	}
	server.conns = make(ConnSet)
	server.mutexConns.Unlock()
	server.wgConns.Wait()
}
<|endoftext|>