<commit_before>package orders

import (
	"log"
	"strconv"

	. "../utilities"
)

const debugOrders = true

func AddCabOrder(button ElevatorButton, localIP string) {
	ElevatorStatus[localIP].CabOrders[button.Floor] = true
	printOrders("Added CabOrder for " + localIP + " at floor " + strconv.Itoa(button.Floor+1))
}

func ShouldStop(floor, direction int, localIP string) bool {
	// cabOrders are checked first, do not depend on direction
	if ElevatorStatus[localIP].CabOrders[floor] == true {
		printOrders("There is a cabOrder at floor " + strconv.Itoa(floor+1) + " for " + localIP)
		return true
	}

	// Check both hallOrders, and for special cases (top floor, bottom floor, no more orders in direction)
	switch direction {
	case MotorStop:
		for k := ButtonCallUp; k <= ButtonCallDown; k++ {
			if HallOrderMatrix[floor][k].AssignedTo == localIP && HallOrderMatrix[floor][k].Status == Awaiting {
				printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[k] + " (direction: " + MotorStatus[direction] + ")")
				return true
			}
		}
	case MotorUp:
		if HallOrderMatrix[floor][ButtonCallUp].AssignedTo == localIP && HallOrderMatrix[floor][ButtonCallUp].Status == Awaiting {
			printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[ButtonCallUp] + " (direction: " + MotorStatus[direction] + ")")
			return true
		}
		return floor == NumFloors-1 || !anyRequestsAbove(floor, localIP)
	case MotorDown:
		if HallOrderMatrix[floor][ButtonCallDown].AssignedTo == localIP && HallOrderMatrix[floor][ButtonCallDown].Status == Awaiting {
			printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[ButtonCallDown] + " (direction: " + MotorStatus[direction] + ")")
			return true
		}
		return floor == 0 || !anyRequestsBelow(floor, localIP)
	default:
		// Insert error handling here
	}
	return false
}

func ChooseDirection(floor, direction int, localIP string) int {
	switch direction {
	case MotorStop:
		// For MotorStop, a "closestOrderedFloor" could possibly be used for further optimization
		if anyRequestsAbove(floor, localIP) && floor < NumFloors-1 {
			return MotorUp
		} else if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else {
			return MotorStop
		}
	case MotorUp:
		if anyRequestsAbove(floor, localIP) {
			return MotorUp
		} else if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else {
			return MotorStop
		}
	case MotorDown:
		if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else if anyRequestsAbove(floor, localIP) {
			return MotorUp
		} else {
			return MotorStop
		}
	default:
		// Insert error handling here
		return MotorStop
	}
	// Might be stuck in a loop (between say 2 floors), depends on implementation in eventManager.go
}

// Does this function also need to send a message on the broadcastOrderChannel to notify that it has removed an order?
func RemoveFloorOrders(floor, direction int, localIP string) {
	if ElevatorStatus[localIP].CabOrders[floor] == true {
		printOrders("Removed CabOrder at floor " + strconv.Itoa(floor+1) + " for " + localIP)
	}
	ElevatorStatus[localIP].CabOrders[floor] = false

	switch direction {
	case MotorStop:
		for k := ButtonCallUp; k <= ButtonCallDown; k++ {
			if HallOrderMatrix[floor][k].AssignedTo == localIP {
				HallOrderMatrix[floor][k].Status = NotActive
				printOrders("Removed HallOrder at floor " + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip " + localIP)
			}
		}
	case MotorUp:
		if HallOrderMatrix[floor][ButtonCallUp].AssignedTo == localIP {
			HallOrderMatrix[floor][ButtonCallUp].Status = NotActive
		}
		printOrders("Removed HallOrder at floor " + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip " + localIP)
		// send order done
		/*
			broadcastOrderChannel <- ElevatorOrderMessage{
				Floor: floor,
				ButtonType: ButtonCallUp,
				//OriginIP: ,
				Event: EventOrderCompleted,
			}
		*/

	case MotorDown:
		if HallOrderMatrix[floor][ButtonCallDown].AssignedTo == localIP {
			HallOrderMatrix[floor][ButtonCallDown].Status = NotActive
		}
		printOrders("Removed HallOrder at floor" + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip" + localIP)

	default:
		log.Println("ERROR [order]: Undefined direction for RemoveFloorOrders")
	}
}

// --- //

func anyRequestsAbove(floor int, localIP string) bool {
	for f := floor + 1; f < NumFloors; f++ { // floor+1 : check floor above
		if ElevatorStatus[localIP].CabOrders[f] {
			printOrders("There is a cabOrder above floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
			return true
		}
		for k := 0; k < NumButtons-1; k++ { // -1 to remove Cab buttons
			if HallOrderMatrix[f][k].AssignedTo == localIP && HallOrderMatrix[f][k].Status == Awaiting {
				printOrders("There is a hallOrder above floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
				return true
			}
		}
	}
	return false
}

func anyRequestsBelow(floor int, localIP string) bool {
	for f := 0; f < floor; f++ {
		if ElevatorStatus[localIP].CabOrders[f] {
			printOrders("There is a cabOrder below floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
			return true
		}
		for k := 0; k < NumButtons-1; k++ {
			if HallOrderMatrix[f][k].AssignedTo == localIP && HallOrderMatrix[f][k].Status == Awaiting {
				printOrders("There is a hallOrder below floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
				return true
			}
		}
	}
	return false
}

func printOrders(s string) {
	if debugOrders {
		log.Println("[orders]\t\t", s)
	}
}
<commit_msg>Add edge cases in removeFloorOrders<commit_after>package orders

import (
	"log"
	"strconv"

	. "../utilities"
)

const debugOrders = true

func AddCabOrder(button ElevatorButton, localIP string) {
	ElevatorStatus[localIP].CabOrders[button.Floor] = true
	printOrders("Added CabOrder for " + localIP + " at floor " + strconv.Itoa(button.Floor+1))
}

func ShouldStop(floor, direction int, localIP string) bool {
	// cabOrders are checked first, do not depend on direction
	if ElevatorStatus[localIP].CabOrders[floor] == true {
		printOrders("There is a cabOrder at floor " + strconv.Itoa(floor+1) + " for " + localIP)
		return true
	}

	// Check both hallOrders, and for special cases (top floor, bottom floor, no more orders in direction)
	switch direction {
	case MotorStop:
		for k := ButtonCallUp; k <= ButtonCallDown; k++ {
			if HallOrderMatrix[floor][k].AssignedTo == localIP && HallOrderMatrix[floor][k].Status == Awaiting {
				printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[k] + " (direction: " + MotorStatus[direction] + ")")
				return true
			}
		}
	case MotorUp:
		if HallOrderMatrix[floor][ButtonCallUp].AssignedTo == localIP && HallOrderMatrix[floor][ButtonCallUp].Status == Awaiting {
			printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[ButtonCallUp] + " (direction: " + MotorStatus[direction] + ")")
			return true
		}
		return floor == NumFloors-1 || !anyRequestsAbove(floor, localIP)
	case MotorDown:
		if HallOrderMatrix[floor][ButtonCallDown].AssignedTo == localIP && HallOrderMatrix[floor][ButtonCallDown].Status == Awaiting {
			printOrders("Found order at floor " + strconv.Itoa(floor) + " of type " + ButtonType[ButtonCallDown] + " (direction: " + MotorStatus[direction] + ")")
			return true
		}
		return floor == 0 || !anyRequestsBelow(floor, localIP)
	default:
		// Insert error handling here
	}
	return false
}

func ChooseDirection(floor, direction int, localIP string) int {
	switch direction {
	case MotorStop:
		// For MotorStop, a "closestOrderedFloor" could possibly be used for further optimization
		if anyRequestsAbove(floor, localIP) && floor < NumFloors-1 {
			return MotorUp
		} else if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else {
			return MotorStop
		}
	case MotorUp:
		if anyRequestsAbove(floor, localIP) {
			return MotorUp
		} else if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else {
			return MotorStop
		}
	case MotorDown:
		if anyRequestsBelow(floor, localIP) {
			return MotorDown
		} else if anyRequestsAbove(floor, localIP) {
			return MotorUp
		} else {
			return MotorStop
		}
	default:
		// Insert error handling here
		return MotorStop
	}
	// Might be stuck in a loop (between say 2 floors), depends on implementation in eventManager.go
}

// Does this function also need to send a message on the broadcastOrderChannel to notify that it has removed an order?
func RemoveFloorOrders(floor, direction int, localIP string) {
	if ElevatorStatus[localIP].CabOrders[floor] == true {
		printOrders("Removed CabOrder at floor " + strconv.Itoa(floor+1) + " for " + localIP)
	}
	ElevatorStatus[localIP].CabOrders[floor] = false

	switch direction {
	case MotorStop:
		for k := ButtonCallUp; k <= ButtonCallDown; k++ {
			if HallOrderMatrix[floor][k].AssignedTo == localIP {
				HallOrderMatrix[floor][k].Status = NotActive
				printOrders("Removed HallOrder at floor " + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip " + localIP)
			}
		}
	case MotorUp:
		if HallOrderMatrix[floor][ButtonCallUp].AssignedTo == localIP {
			HallOrderMatrix[floor][ButtonCallUp].Status = NotActive
		}
		if floor == NumFloors-1 {
			HallOrderMatrix[floor][ButtonCallDown].Status = NotActive
		}
		printOrders("Removed HallOrder at floor " + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip " + localIP)
		// send order done
		/*
			broadcastOrderChannel <- ElevatorOrderMessage{
				Floor: floor,
				ButtonType: ButtonCallUp,
				//OriginIP: ,
				Event: EventOrderCompleted,
			}
		*/

	case MotorDown:
		if HallOrderMatrix[floor][ButtonCallDown].AssignedTo == localIP {
			HallOrderMatrix[floor][ButtonCallDown].Status = NotActive
		}
		if floor == Floor1 {
			HallOrderMatrix[floor][ButtonCallUp].Status = NotActive
		}
		printOrders("Removed HallOrder at floor" + strconv.Itoa(floor+1) + " for direction " + MotorStatus[direction] + ". Ip" + localIP)

	default:
		log.Println("ERROR [order]: Undefined direction for RemoveFloorOrders")
	}
}

// --- //

func anyRequestsAbove(floor int, localIP string) bool {
	for f := floor + 1; f < NumFloors; f++ { // floor+1 : check floor above
		if ElevatorStatus[localIP].CabOrders[f] {
			printOrders("There is a cabOrder above floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
			return true
		}
		for k := 0; k < NumButtons-1; k++ { // -1 to remove Cab buttons
			if HallOrderMatrix[f][k].AssignedTo == localIP && HallOrderMatrix[f][k].Status == Awaiting {
				printOrders("There is a hallOrder above floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
				return true
			}
		}
	}
	return false
}

func anyRequestsBelow(floor int, localIP string) bool {
	for f := 0; f < floor; f++ {
		if ElevatorStatus[localIP].CabOrders[f] {
			printOrders("There is a cabOrder below floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
			return true
		}
		for k := 0; k < NumButtons-1; k++ {
			if HallOrderMatrix[f][k].AssignedTo == localIP && HallOrderMatrix[f][k].Status == Awaiting {
				printOrders("There is a hallOrder below floor " + strconv.Itoa(floor+1) + " at floor " + strconv.Itoa(f+1) + " for elevator " + localIP)
				return true
			}
		}
	}
	return false
}

func printOrders(s string) {
	if debugOrders {
		log.Println("[orders]\t\t", s)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestGetVideoThumbResponse(t *testing.T) {
	resp, err := GetVideoThumbResponse("sm1")
	if err != nil {
		t.Fail()
	}
	errorInfo := resp.ErrorInfo
	assert.Equal(t, errorInfo.Code, "DELETED")
	assert.Equal(t, errorInfo.Description, "deleted")

	resp, err = GetVideoThumbResponse("sm9")
	if err != nil {
		t.Fail()
	}
	videoInfo := resp.VideoInfo
	assert.Equal(t, videoInfo.VideoId, "sm9")
	assert.Equal(t, videoInfo.MovieType, "flv")
}

func TestGetVideoTitle(t *testing.T) {
	title := GetVideoTitle("sm1")
	assert.Empty(t, title)

	title = GetVideoTitle("sm9")
	assert.Contains(t, title, "陰陽師")
}

func TestGetAllKindsOfVideoTags(t *testing.T) {
	allTags, lockedTags, unlockedTags := GetAllKindsOfVideoTags("sm1")
	assert.Nil(t, allTags)
	assert.Nil(t, lockedTags)
	assert.Nil(t, unlockedTags)

	allTags, lockedTags, unlockedTags = GetAllKindsOfVideoTags("sm9")
	assert.Equal(t, allTags[0], "陰陽師")
	assert.Equal(t, lockedTags[0], "陰陽師")
}
<commit_msg>Add BDD style test<commit_after>package main

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestGetVideoThumbResponse(t *testing.T) {
	resp, err := GetVideoThumbResponse("sm1")
	if err != nil {
		t.Fail()
	}
	errorInfo := resp.ErrorInfo
	assert.Equal(t, errorInfo.Code, "DELETED")
	assert.Equal(t, errorInfo.Description, "deleted")

	resp, err = GetVideoThumbResponse("sm9")
	if err != nil {
		t.Fail()
	}
	videoInfo := resp.VideoInfo
	assert.Equal(t, videoInfo.VideoId, "sm9")
	assert.Equal(t, videoInfo.MovieType, "flv")
}

func TestGetVideoTitle(t *testing.T) {
	title := GetVideoTitle("sm1")
	assert.Empty(t, title)

	title = GetVideoTitle("sm9")
	assert.Contains(t, title, "陰陽師")
}

func TestGetAllKindsOfVideoTags(t *testing.T) {
	allTags, lockedTags, unlockedTags := GetAllKindsOfVideoTags("sm1")
	assert.Nil(t, allTags)
	assert.Nil(t, lockedTags)
	assert.Nil(t, unlockedTags)

	allTags, lockedTags, unlockedTags = GetAllKindsOfVideoTags("sm9")
	assert.Equal(t, allTags[0], "陰陽師")
	assert.Equal(t, lockedTags[0], "陰陽師")
}

var _ = Describe("gonico core", func() {
	var (
		resp NicoVideoThumbResponse
		err  error
	)

	BeforeEach(func() {
		resp, err = GetVideoThumbResponse("sm1")
	})

	It("error loading", func() {
		errorInfo := resp.ErrorInfo
		Expect(errorInfo.Code).To(Equal("DELETED"))
	})
})
<|endoftext|>


<commit_before>package arn

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStreamAnime(t *testing.T) {
	assert.Nil(t, AniList.Authorize())

	count := 0
	stream := AniList.StreamAnime()

	for anime := range stream {
		assert.NotNil(t, anime)
		assert.NotEmpty(t, anime.TitleRomaji)
		count++

		if count >= 80 {
			break
		}
	}
}
<commit_msg>Minor changes<commit_after>package arn

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStreamAnime(t *testing.T) {
	assert.NoError(t, AniList.Authorize())

	count := 0
	stream := AniList.StreamAnime()

	for anime := range stream {
		assert.NotNil(t, anime)
		assert.NotEmpty(t, anime.TitleRomaji)
		count++

		if count >= 80 {
			break
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/csv"
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
	"time"
	// "fmt"
	// "io/ioutil"
	"github.com/paulmach/go.geojson"
	"github.com/pkg/errors"
)

func StudentAqisHandler(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()
	to, from, err := parseTimeInput(values)
	if err != nil {
		http.Error(w, "Could not parse time: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var within string
	var area string
	var plotMap string
	var plotChart string

	if len(values["within"]) > 0 {
		within = values["within"][0]
	}
	if len(values["area"]) > 0 {
		area = values["area"][0]
	}
	if len(values["plotmap"]) > 0 {
		plotMap = values["plotmap"][0]
	}
	if len(values["plotchart"]) > 0 {
		plotChart = values["plotchart"][0]
	}

	filter := StudentFilter{
		ToTime:    to,
		FromTime:  from,
		Within:    within,
		Area:      area,
		PlotMap:   plotMap,
		PlotChart: plotChart,
	}

	data, err := getStudentData(filter)
	if err != nil {
		http.Error(w, "Could not parse student data: "+err.Error(), http.StatusInternalServerError)
		return
	}

	fc := geojson.NewFeatureCollection()

	for _, measurement := range data.Data {
		geom := geojson.NewPointGeometry([]float64{measurement.Attributes.Longitude, measurement.Attributes.Latitude})
		f := geojson.NewFeature(geom)
		f.SetProperty("date", measurement.Attributes.Date)
		f.SetProperty("pmTen", measurement.Attributes.PmTen)
		f.SetProperty("pmTwoFive", measurement.Attributes.PmTwoFive)
		f.SetProperty("humidity", measurement.Attributes.Humidity)
		f.SetProperty("temperature", measurement.Attributes.Temperature)
		f.SetProperty("color", "6ee86e")
		f.SetProperty("weight", 10)
		fc = fc.AddFeature(f)
	}

	b, err := fc.MarshalJSON()
	if err != nil {
		http.Error(w, "Could not marshal geojson"+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(b)
	return
}

type StudentFilter struct {
	Area      string
	Within    string
	FromTime  time.Time
	ToTime    time.Time
	PlotMap   string
	PlotChart string
}

func StudentHandler(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()
	to, from, err := parseTimeInput(values)

	if err != nil {
		http.Error(w, "Could not parse time: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var within string
	var area string
	var plotMap string
	var plotChart string

	if len(values["within"]) > 0 {
		within = values["within"][0]
	}
	if len(values["area"]) > 0 {
		area = values["area"][0]
	}
	if len(values["plotmap"]) > 0 {
		plotMap = values["plotmap"][0]
	}
	if len(values["plotchart"]) > 0 {
		plotChart = values["plotchart"][0]
	}

	filter := StudentFilter{
		ToTime:    to,
		FromTime:  from,
		Within:    within,
		Area:      area,
		PlotMap:   plotMap,
		PlotChart: plotChart,
	}

	data, err := getStudentData(filter)
	if err != nil {
		http.Error(w, "Could not parse student data: "+err.Error(), http.StatusInternalServerError)
		return
	}

	records := [][]string{}
	header := []string{"timestamp", "latitude", "longitude", "pmTen", "pmTwoFive", "humidity", "temperature"}
	records = append(records, header)

	for _, measurement := range data.Data {
		var latitude float64
		var longitude float64
		var valuePmTen float64
		var valuePmTwoFive float64
		var valueHumidity float64
		var valueTemperature float64

		latitude = measurement.Attributes.Latitude
		longitude = measurement.Attributes.Longitude
		valuePmTen = measurement.Attributes.PmTen
		valuePmTwoFive = measurement.Attributes.PmTwoFive
		valueHumidity = measurement.Attributes.Humidity
		valueTemperature = measurement.Attributes.Temperature

		formattedLatitude := strconv.FormatFloat(latitude, 'f', -1, 64)
		formattedLongitude := strconv.FormatFloat(longitude, 'f', -1, 64)
		formattedPmTenValue := strconv.FormatFloat(valuePmTen, 'f', -1, 64)
		formattedPmTwoFiveValue := strconv.FormatFloat(valuePmTwoFive, 'f', -1, 64)
		formattedHumidityValue := strconv.FormatFloat(valueHumidity, 'f', -1, 64)
		formattedTemperatureValue := strconv.FormatFloat(valueTemperature, 'f', -1, 64)

		timestamp := measurement.Attributes.Date

		// fmt.Println(time.Parse(studentResponseTimeLayouttimestamp.Format("2006-01-02 15:04:05"))

		record := []string{
			timestamp,
			formattedLatitude,
			formattedLongitude,
			formattedPmTenValue,
			formattedPmTwoFiveValue,
			formattedHumidityValue,
			formattedTemperatureValue,
		}
		records = append(records, record)
	}

	writer := csv.NewWriter(w)

	filename := "studentdata.csv"
	w.Header().Set("Content-Disposition", "attachment; filename="+filename)

	err = writer.WriteAll(records)
	if err != nil {
		http.Error(w, "Could not write csv", http.StatusInternalServerError)
		return
	}
}

var studentTimeLayout = "2006-01-02T15:04:05"
var studentResponseTimeLayout = "2006-01-02 15:04:05 -0700"

type Result struct {
	Data []Record `json:"data"`
}

type Record struct {
	Id string `json:"id"`
	Type string `json:"type"`
	Attributes Measurement `json:"attributes"`
}

type Measurement struct {
	Latitude    float64
	Longitude   float64
	PmTen       float64
	PmTwoFive   float64
	Humidity    float64
	Temperature float64
	Date        string `json:"Timestamp"`
}

// Fetches and parses the student collected data
func getStudentData(filter StudentFilter) (Result, error) {

	fromDate := filter.FromTime.Format(studentTimeLayout)
	toDate := filter.ToTime.Format(studentTimeLayout)
	within := filter.Within
	area := filter.Area
	plotMap := filter.PlotMap
	plotChart := filter.PlotChart

	var u string
	// if len(within) > 0 {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&within=" + within
	// }	else if len(area) > 0 {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&area=" + url.QueryEscape(area)
	// }	else {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate
	// }
	if len(within) > 0 {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&within=" + within
	} else if len(area) > 0 {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&area=" + url.QueryEscape(area)
	} else {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate
	}

	if len(plotMap) > 0 {
		u += "&plotmap=" + plotMap
	} else if len(plotChart) > 0 {
		u += "&plotchart=" + plotChart
	}

	resp, err := http.Get(u)
	if err != nil {
		return Result{}, errors.Wrap(err, "Could not download data from luftprosjekttromso")
	}

	var data Result
	err = json.NewDecoder(resp.Body).Decode(&data)
	return data, nil
}
<commit_msg>Remove comments<commit_after>package controllers

import (
	"encoding/csv"
	"encoding/json"
	"net/http"
	"net/url"
	"strconv"
	"time"
	"github.com/paulmach/go.geojson"
	"github.com/pkg/errors"
)

func StudentAqisHandler(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()
	to, from, err := parseTimeInput(values)
	if err != nil {
		http.Error(w, "Could not parse time: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var within string
	var area string
	var plotMap string
	var plotChart string

	if len(values["within"]) > 0 {
		within = values["within"][0]
	}
	if len(values["area"]) > 0 {
		area = values["area"][0]
	}
	if len(values["plotmap"]) > 0 {
		plotMap = values["plotmap"][0]
	}
	if len(values["plotchart"]) > 0 {
		plotChart = values["plotchart"][0]
	}

	filter := StudentFilter{
		ToTime:    to,
		FromTime:  from,
		Within:    within,
		Area:      area,
		PlotMap:   plotMap,
		PlotChart: plotChart,
	}

	data, err := getStudentData(filter)
	if err != nil {
		http.Error(w, "Could not parse student data: "+err.Error(), http.StatusInternalServerError)
		return
	}

	fc := geojson.NewFeatureCollection()

	for _, measurement := range data.Data {
		geom := geojson.NewPointGeometry([]float64{measurement.Attributes.Longitude, measurement.Attributes.Latitude})
		f := geojson.NewFeature(geom)
		f.SetProperty("date", measurement.Attributes.Date)
		f.SetProperty("pmTen", measurement.Attributes.PmTen)
		f.SetProperty("pmTwoFive", measurement.Attributes.PmTwoFive)
		f.SetProperty("humidity", measurement.Attributes.Humidity)
		f.SetProperty("temperature", measurement.Attributes.Temperature)
		f.SetProperty("color", "6ee86e")
		f.SetProperty("weight", 10)
		fc = fc.AddFeature(f)
	}

	b, err := fc.MarshalJSON()
	if err != nil {
		http.Error(w, "Could not marshal geojson"+err.Error(), http.StatusInternalServerError)
		return
	}

	w.Write(b)
	return
}

type StudentFilter struct {
	Area      string
	Within    string
	FromTime  time.Time
	ToTime    time.Time
	PlotMap   string
	PlotChart string
}

func StudentHandler(w http.ResponseWriter, r *http.Request) {
	values := r.URL.Query()
	to, from, err := parseTimeInput(values)

	if err != nil {
		http.Error(w, "Could not parse time: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var within string
	var area string
	var plotMap string
	var plotChart string

	if len(values["within"]) > 0 {
		within = values["within"][0]
	}
	if len(values["area"]) > 0 {
		area = values["area"][0]
	}
	if len(values["plotmap"]) > 0 {
		plotMap = values["plotmap"][0]
	}
	if len(values["plotchart"]) > 0 {
		plotChart = values["plotchart"][0]
	}

	filter := StudentFilter{
		ToTime:    to,
		FromTime:  from,
		Within:    within,
		Area:      area,
		PlotMap:   plotMap,
		PlotChart: plotChart,
	}

	data, err := getStudentData(filter)
	if err != nil {
		http.Error(w, "Could not parse student data: "+err.Error(), http.StatusInternalServerError)
		return
	}

	records := [][]string{}
	header := []string{"timestamp", "latitude", "longitude", "pmTen", "pmTwoFive", "humidity", "temperature"}
	records = append(records, header)

	for _, measurement := range data.Data {
		var latitude float64
		var longitude float64
		var valuePmTen float64
		var valuePmTwoFive float64
		var valueHumidity float64
		var valueTemperature float64

		latitude = measurement.Attributes.Latitude
		longitude = measurement.Attributes.Longitude
		valuePmTen = measurement.Attributes.PmTen
		valuePmTwoFive = measurement.Attributes.PmTwoFive
		valueHumidity = measurement.Attributes.Humidity
		valueTemperature = measurement.Attributes.Temperature

		formattedLatitude := strconv.FormatFloat(latitude, 'f', -1, 64)
		formattedLongitude := strconv.FormatFloat(longitude, 'f', -1, 64)
		formattedPmTenValue := strconv.FormatFloat(valuePmTen, 'f', -1, 64)
		formattedPmTwoFiveValue := strconv.FormatFloat(valuePmTwoFive, 'f', -1, 64)
		formattedHumidityValue := strconv.FormatFloat(valueHumidity, 'f', -1, 64)
		formattedTemperatureValue := strconv.FormatFloat(valueTemperature, 'f', -1, 64)

		timestamp := measurement.Attributes.Date

		// fmt.Println(time.Parse(studentResponseTimeLayouttimestamp.Format("2006-01-02 15:04:05"))

		record := []string{
			timestamp,
			formattedLatitude,
			formattedLongitude,
			formattedPmTenValue,
			formattedPmTwoFiveValue,
			formattedHumidityValue,
			formattedTemperatureValue,
		}
		records = append(records, record)
	}

	writer := csv.NewWriter(w)

	filename := "studentdata.csv"
	w.Header().Set("Content-Disposition", "attachment; filename="+filename)

	err = writer.WriteAll(records)
	if err != nil {
		http.Error(w, "Could not write csv", http.StatusInternalServerError)
		return
	}
}

var studentTimeLayout = "2006-01-02T15:04:05"
var studentResponseTimeLayout = "2006-01-02 15:04:05 -0700"

type Result struct {
	Data []Record `json:"data"`
}

type Record struct {
	Id string `json:"id"`
	Type string `json:"type"`
	Attributes Measurement `json:"attributes"`
}

type Measurement struct {
	Latitude    float64
	Longitude   float64
	PmTen       float64
	PmTwoFive   float64
	Humidity    float64
	Temperature float64
	Date        string `json:"Timestamp"`
}

// Fetches and parses the student collected data
func getStudentData(filter StudentFilter) (Result, error) {

	fromDate := filter.FromTime.Format(studentTimeLayout)
	toDate := filter.ToTime.Format(studentTimeLayout)
	within := filter.Within
	area := filter.Area
	plotMap := filter.PlotMap
	plotChart := filter.PlotChart

	var u string
	// if len(within) > 0 {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&within=" + within
	// }	else if len(area) > 0 {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&area=" + url.QueryEscape(area)
	// }	else {
	// 	u = "http://localhost:8080/api/data?totime=" + toDate + "&fromtime=" + fromDate
	// }
	if len(within) > 0 {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&within=" + within
	} else if len(area) > 0 {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate + "&area=" + url.QueryEscape(area)
	} else {
		u = "https://luft-184208.appspot.com/api/data?totime=" + toDate + "&fromtime=" + fromDate
	}

	if len(plotMap) > 0 {
		u += "&plotmap=" + plotMap
	} else if len(plotChart) > 0 {
		u += "&plotchart=" + plotChart
	}

	resp, err := http.Get(u)
	if err != nil {
		return Result{}, errors.Wrap(err, "Could not download data from luftprosjekttromso")
	}

	var data Result
	err = json.NewDecoder(resp.Body).Decode(&data)
	return data, nil
}
<|endoftext|>


<commit_before>package proto

type Capability struct {
	Id           string                  `json:"id,omitempty"`
	Name         string                  `json:"name,omitempty"`
	MonitoringId string                  `json:"monitoringId,omitempty"`
	Metric       string                  `json:"metric,omitempty"`
	View         string                  `json:"view,omitempty"`
	Thresholds   uint64                  `json:"thresholds,omitempty"`
	Demux        *[]string               `json:"demux,omitempty"`
	Constraints  *[]CapabilityConstraint `json:"constraints,omitempty"`
	Details      *CapabilityDetails      `json:"details,omitempty"`
}

type CapabilityFilter struct {
	MonitoringId   string `json:"monitoringId,omitempty"`
	MonitoringName string `json:"monitoringName,omitempty"`
	Metric         string `json:"metric,omitempty"`
	View           string `json:"view,omitempty"`
}

type CapabilityConstraint struct {
	Type  string
	Name  string
	Value string
}

type CapabilityDetails struct {
	DetailsCreation
}

func (c *Capability) DeepCompare(a *Capability) bool {
	if c.Id != a.Id {
		return false
	}
	if c.Name != a.Name {
		return false
	}
	if c.MonitoringId != a.MonitoringId {
		return false
	}
	if c.Metric != a.Metric {
		return false
	}
	if c.View != a.View {
		return false
	}
	if c.Thresholds != a.Thresholds {
		return false
	}
	if c.Demux != nil {
	demuxloop:
		for _, str := range *c.Demux {
			if c.DeepCompareSlice(str, a.Demux) {
				continue demuxloop
			}
			return false
		}
	}
	if a.Demux != nil {
	revdemuxloop:
		for _, str := range *a.Demux {
			if c.DeepCompareSlice(str, c.Demux) {
				continue revdemuxloop
			}
			return false
		}
	}
	if c.Constraints != nil {
	constraintloop:
		for _, cstr := range *c.Constraints {
			if cstr.DeepCompareSlice(a.Constraints) {
				continue constraintloop
			}
			return false
		}
	}
	if a.Constraints != nil {
	revconstraintloop:
		for _, cstr := range *a.Constraints {
			if cstr.DeepCompareSlice(c.Constraints) {
				continue revconstraintloop
			}
			return false
		}
	}
	return true
}

func (c *Capability) DeepCompareSlice(s string, a *[]string) bool {
	if a == nil || *a == nil {
		return false
	}

	for _, str := range *a {
		if s == str {
			return true
		}
	}
	return false
}

func (c *CapabilityConstraint) DeepCompare(a *CapabilityConstraint) bool {
	if a == nil {
		return false
	}

	if c.Type != a.Type || c.Name != a.Type || c.Value != a.Value {
		return false
	}
	return true
}

func (c *CapabilityConstraint) DeepCompareSlice(a *[]CapabilityConstraint) bool {
	if a == nil || *a == nil {
		return false
	}

	for _, cstr := range *a {
		if c.DeepCompare(&cstr) {
			return true
		}
	}
	return false
}

func NewCapabilityRequest() Request {
	return Request{
		Flags: &Flags{},
		Capability: &Capability{
			Demux:       &[]string{},
			Constraints: &[]CapabilityConstraint{},
		},
	}
}

func NewCapabilityFilter() Request {
	return Request{
		Filter: &Filter{
			Capability: &CapabilityFilter{},
		},
	}
}

func NewCapabilityResult() Result {
	return Result{
		Errors:       &[]string{},
		Capabilities: &[]Capability{},
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Capability.DeepCompare: ignore unused<commit_after>package proto

type Capability struct {
	Id           string                  `json:"id,omitempty"`
	Name         string                  `json:"name,omitempty"`
	MonitoringId string                  `json:"monitoringId,omitempty"`
	Metric       string                  `json:"metric,omitempty"`
	View         string                  `json:"view,omitempty"`
	Thresholds   uint64                  `json:"thresholds,omitempty"`
	Demux        *[]string               `json:"demux,omitempty"`
	Constraints  *[]CapabilityConstraint `json:"constraints,omitempty"`
	Details      *CapabilityDetails      `json:"details,omitempty"`
}

type CapabilityFilter struct {
	MonitoringId   string `json:"monitoringId,omitempty"`
	MonitoringName string `json:"monitoringName,omitempty"`
	Metric         string `json:"metric,omitempty"`
	View           string `json:"view,omitempty"`
}

type CapabilityConstraint struct {
	Type  string
	Name  string
	Value string
}

type CapabilityDetails struct {
	DetailsCreation
}

func (c *Capability) DeepCompare(a *Capability) bool {
	if c.Id != a.Id {
		return false
	}
	if c.Name != a.Name {
		return false
	}
	if c.MonitoringId != a.MonitoringId {
		return false
	}
	if c.Metric != a.Metric {
		return false
	}
	if c.View != a.View {
		return false
	}
	if c.Thresholds != a.Thresholds {
		return false
	}
	/*
		if c.Demux != nil {
		demuxloop:
			for _, str := range *c.Demux {
				if c.DeepCompareSlice(str, a.Demux) {
					continue demuxloop
				}
				return false
			}
		}
		if a.Demux != nil {
		revdemuxloop:
			for _, str := range *a.Demux {
				if c.DeepCompareSlice(str, c.Demux) {
					continue revdemuxloop
				}
				return false
			}
		}
		if c.Constraints != nil {
		constraintloop:
			for _, cstr := range *c.Constraints {
				if cstr.DeepCompareSlice(a.Constraints) {
					continue constraintloop
				}
				return false
			}
		}
		if a.Constraints != nil {
		revconstraintloop:
			for _, cstr := range *a.Constraints {
				if cstr.DeepCompareSlice(c.Constraints) {
					continue revconstraintloop
				}
				return false
			}
		}
	*/
	return true
}

func (c *Capability) DeepCompareSlice(s string, a *[]string) bool {
	if a == nil || *a == nil {
		return false
	}

	for _, str := range *a {
		if s == str {
			return true
		}
	}
	return false
}

func (c *CapabilityConstraint) DeepCompare(a *CapabilityConstraint) bool {
	if a == nil {
		return false
	}

	if c.Type != a.Type || c.Name != a.Type || c.Value != a.Value {
		return false
	}
	return true
}

func (c *CapabilityConstraint) DeepCompareSlice(a *[]CapabilityConstraint) bool {
	if a == nil || *a == nil {
		return false
	}

	for _, cstr := range *a {
		if c.DeepCompare(&cstr) {
			return true
		}
	}
	return false
}

func NewCapabilityRequest() Request {
	return Request{
		Flags: &Flags{},
		Capability: &Capability{
			Demux:       &[]string{},
			Constraints: &[]CapabilityConstraint{},
		},
	}
}

func NewCapabilityFilter() Request {
	return Request{
		Filter: &Filter{
			Capability: &CapabilityFilter{},
		},
	}
}

func NewCapabilityResult() Result {
	return Result{
		Errors:       &[]string{},
		Capabilities: &[]Capability{},
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// 24 february 2014

package ui

import (
	"fmt"
	"unsafe"
)

type sysSizeData struct {
	cSysSizeData

	// for size calculations
	baseX	int
	baseY	int

	// for the actual resizing
	// possibly the HDWP
}

const (
	marginDialogUnits = 7
	paddingDialogUnits = 4
)

func (s *sysData) beginResize() (d *sysSizeData) {
	d = new(sysSizeData)

	dc := getTextDC(s.hwnd)
	defer releaseTextDC(s.hwnd, dc)

	var tm _TEXTMETRICS

	r1, _, err := _getTextMetrics.Call(
		uintptr(dc),
		uintptr(unsafe.Pointer(&tm)))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error getting text metrics for preferred size calculations: %v", err))
	}
	d.baseX = int(tm.tmAveCharWidth) // TODO not optimal; third reference has better way
	d.baseY = int(tm.tmHeight)

	if s.spaced {
		d.xmargin = muldiv(marginDialogUnits, d.baseX, 4)
		d.ymargin = muldiv(marginDialogUnits, d.baseY, 8)
		d.xpadding = muldiv(paddingDialogUnits, d.baseX, 4)
		d.xpadding = muldiv(paddingDialogUnits, d.baseY, 8)
	}

	return d
}

func (s *sysData) endResize(d *sysSizeData) {
	// redraw
}

func (s *sysData) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
	// no translation needed on windows
}

func (s *sysData) commitResize(c *allocation, d *sysSizeData) {
	yoff := stdDlgSizes[s.ctype].yoff
	if s.alternate {
		yoff = stdDlgSizes[s.ctype].yoffalt
	}
	if yoff != 0 {
		yoff = muldiv(yoff, d.baseY, 8)
	}
	c.y += yoff
	// TODO move this here
	s.setRect(c.x, c.y, c.width, c.height, 0)
}

func (s *sysData) getAuxResizeInfo(d *sysSizeData) {
	// do nothing
}

// For Windows, Microsoft just hands you a list of preferred control sizes as part of the MSDN documentation and tells you to roll with it.
// These sizes are given in "dialog units", which are independent of the font in use.
// We need to convert these into standard pixels, which requires we get the device context of the OS window.
// References:
// - http://msdn.microsoft.com/en-us/library/windows/desktop/aa511279.aspx#controlsizing for control sizes
// - http://msdn.microsoft.com/en-us/library/ms645502%28VS.85%29.aspx - the calculation needed
// - http://support.microsoft.com/kb/125681 - to get the base X and Y
// (thanks to http://stackoverflow.com/questions/58620/default-button-size)
// For push buttons, date/time pickers, links (which we don't use), toolbars, and rebars (another type of toolbar), Common Controls version 6 provides convenient methods to use instead, falling back to the old way if it fails.

// As we are left with incomplete data, an arbitrary size will be chosen
const (
	defaultWidth = 100 // 2 * preferred width of buttons
)

type dlgunits struct {
	width   int
	height  int
	longest bool // TODO actually use this
	getsize uintptr
	area    bool // use area sizes instead
	yoff		int
	yoffalt	int
}

var stdDlgSizes = [nctypes]dlgunits{
	c_button: dlgunits{
		width:   50,
		height:  14,
		getsize: _BCM_GETIDEALSIZE,
	},
	c_checkbox: dlgunits{
		// widtdh is not defined here so assume longest
		longest: true,
		height:  10,
	},
	c_combobox: dlgunits{
		// technically the height of a combobox has to include the drop-down list (this is a historical accident: originally comboboxes weren't drop-down)
		// but since we're forcing Common Controls version 6, we can take advantage of one of its mechanisms to automatically fix this mistake (bad practice but whatever)
		// see also: http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx
		// note that the Microsoft guidelines pages don't take the list size into account
		longest: true,
		height:  12, // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx; the page linked above says 14
	},
	c_lineedit: dlgunits{
		longest: true,
		height:  14,
	},
	c_label: dlgunits{
		longest: true,
		height:  8,
		yoff:		3,
		yoffalt:	0,
	},
	c_listbox: dlgunits{
		longest: true,
		// height is not clearly defined here ("an integral number of items (3 items minimum)") so just use a three-line edit control
		height: 14 + 10 + 10,
	},
	c_progressbar: dlgunits{
		width:  237, // the first reference says 107 also works; TODO decide which to use
		height: 8,
	},
	c_area: dlgunits{
		area: true,
	},
}

var (
	_selectObject         = gdi32.NewProc("SelectObject")
	_getDC                = user32.NewProc("GetDC")
	_getTextExtentPoint32 = gdi32.NewProc("GetTextExtentPoint32W")
	_getTextMetrics       = gdi32.NewProc("GetTextMetricsW")
	_releaseDC            = user32.NewProc("ReleaseDC")
)

func getTextDC(hwnd _HWND) (dc _HANDLE) {
	r1, _, err := _getDC.Call(uintptr(hwnd))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error getting DC for preferred size calculations: %v", err))
	}
	dc = _HANDLE(r1)
	r1, _, err = _selectObject.Call(
		uintptr(dc),
		uintptr(controlFont))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error loading control font into device context for preferred size calculation: %v", err))
	}
	return dc
}

func releaseTextDC(hwnd _HWND, dc _HANDLE) {
	r1, _, err := _releaseDC.Call(
		uintptr(hwnd),
		uintptr(dc))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error releasing DC for preferred size calculations: %v", err))
	}
}

// This function runs on uitask; call the functions directly.
func (s *sysData) preferredSize(d *sysSizeData) (width int, height int) {
	// the preferred size of an Area is its size
	if stdDlgSizes[s.ctype].area {
		return s.areawidth, s.areaheight
	}

	if msg := stdDlgSizes[s.ctype].getsize; msg != 0 {
		var size _SIZE

		r1, _, _ := _sendMessage.Call(
			uintptr(s.hwnd),
			msg,
			uintptr(0),
			uintptr(unsafe.Pointer(&size)))
		if r1 != uintptr(_FALSE) { // success
			return int(size.cx), int(size.cy)
		}
		// otherwise the message approach failed, so fall back to the regular approach
		println("message failed; falling back")
	}

	width = stdDlgSizes[s.ctype].width
	if width == 0 {
		width = defaultWidth
	}
	height = stdDlgSizes[s.ctype].height
	width = muldiv(width, d.baseX, 4)   // equivalent to right of rect
	height = muldiv(height, d.baseY, 8) // equivalent to bottom of rect

	return width, height
}

var (
	_mulDiv = kernel32.NewProc("MulDiv")
)

func muldiv(ma int, mb int, div int) int {
	// div will not be 0 in the usages above
	// we also ignore overflow; that isn't likely to happen for our use case anytime soon
	r1, _, _ := _mulDiv.Call(
		uintptr(int32(ma)),
		uintptr(int32(mb)),
		uintptr(int32(div)))
	return int(int32(r1))
}

type _SIZE struct {
	cx int32 // originally LONG
	cy int32
}

type _TEXTMETRICS struct {
	tmHeight           int32
	tmAscent           int32
	tmDescent          int32
	tmInternalLeading  int32
	tmExternalLeading  int32
	tmAveCharWidth     int32
	tmMaxCharWidth     int32
	tmWeight           int32
	tmOverhang         int32
	tmDigitizedAspectX int32
	tmDigitizedAspectY int32
	tmFirstChar        uint16
	tmLastChar         uint16
	tmDefaultChar      uint16
	tmBreakChar        uint16
	tmItalic           byte
	tmUnderlined       byte
	tmStruckOut        byte
	tmPitchAndFamily   byte
	tmCharSet          byte
}
<commit_msg>Fixed a typo in controlsize_windows.go.<commit_after>// 24 february 2014

package ui

import (
	"fmt"
	"unsafe"
)

type sysSizeData struct {
	cSysSizeData

	// for size calculations
	baseX	int
	baseY	int

	// for the actual resizing
	// possibly the HDWP
}

const (
	marginDialogUnits = 7
	paddingDialogUnits = 4
)

func (s *sysData) beginResize() (d *sysSizeData) {
	d = new(sysSizeData)

	dc := getTextDC(s.hwnd)
	defer releaseTextDC(s.hwnd, dc)

	var tm _TEXTMETRICS

	r1, _, err := _getTextMetrics.Call(
		uintptr(dc),
		uintptr(unsafe.Pointer(&tm)))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error getting text metrics for preferred size calculations: %v", err))
	}
	d.baseX = int(tm.tmAveCharWidth) // TODO not optimal; third reference has better way
	d.baseY = int(tm.tmHeight)

	if s.spaced {
		d.xmargin = muldiv(marginDialogUnits, d.baseX, 4)
		d.ymargin = muldiv(marginDialogUnits, d.baseY, 8)
		d.xpadding = muldiv(paddingDialogUnits, d.baseX, 4)
		d.ypadding = muldiv(paddingDialogUnits, d.baseY, 8)
	}

	return d
}

func (s *sysData) endResize(d *sysSizeData) {
	// redraw
}

func (s *sysData) translateAllocationCoords(allocations []*allocation, winwidth, winheight int) {
	// no translation needed on windows
}

func (s *sysData) commitResize(c *allocation, d *sysSizeData) {
	yoff := stdDlgSizes[s.ctype].yoff
	if s.alternate {
		yoff = stdDlgSizes[s.ctype].yoffalt
	}
	if yoff != 0 {
		yoff = muldiv(yoff, d.baseY, 8)
	}
	c.y += yoff
	// TODO move this here
	s.setRect(c.x, c.y, c.width, c.height, 0)
}

func (s *sysData) getAuxResizeInfo(d *sysSizeData) {
	// do nothing
}

// For Windows, Microsoft just hands you a list of preferred control sizes as part of the MSDN documentation and tells you to roll with it.
// These sizes are given in "dialog units", which are independent of the font in use.
// We need to convert these into standard pixels, which requires we get the device context of the OS window.
// References:
// - http://msdn.microsoft.com/en-us/library/windows/desktop/aa511279.aspx#controlsizing for control sizes
// - http://msdn.microsoft.com/en-us/library/ms645502%28VS.85%29.aspx - the calculation needed
// - http://support.microsoft.com/kb/125681 - to get the base X and Y
// (thanks to http://stackoverflow.com/questions/58620/default-button-size)
// For push buttons, date/time pickers, links (which we don't use), toolbars, and rebars (another type of toolbar), Common Controls version 6 provides convenient methods to use instead, falling back to the old way if it fails.

// As we are left with incomplete data, an arbitrary size will be chosen
const (
	defaultWidth = 100 // 2 * preferred width of buttons
)

type dlgunits struct {
	width   int
	height  int
	longest bool // TODO actually use this
	getsize uintptr
	area    bool // use area sizes instead
	yoff		int
	yoffalt	int
}

var stdDlgSizes = [nctypes]dlgunits{
	c_button: dlgunits{
		width:   50,
		height:  14,
		getsize: _BCM_GETIDEALSIZE,
	},
	c_checkbox: dlgunits{
		// widtdh is not defined here so assume longest
		longest: true,
		height:  10,
	},
	c_combobox: dlgunits{
		// technically the height of a combobox has to include the drop-down list (this is a historical accident: originally comboboxes weren't drop-down)
		// but since we're forcing Common Controls version 6, we can take advantage of one of its mechanisms to automatically fix this mistake (bad practice but whatever)
		// see also: http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx
		// note that the Microsoft guidelines pages don't take the list size into account
		longest: true,
		height:  12, // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx; the page linked above says 14
	},
	c_lineedit: dlgunits{
		longest: true,
		height:  14,
	},
	c_label: dlgunits{
		longest: true,
		height:  8,
		yoff:		3,
		yoffalt:	0,
	},
	c_listbox: dlgunits{
		longest: true,
		// height is not clearly defined here ("an integral number of items (3 items minimum)") so just use a three-line edit control
		height: 14 + 10 + 10,
	},
	c_progressbar: dlgunits{
		width:  237, // the first reference says 107 also works; TODO decide which to use
		height: 8,
	},
	c_area: dlgunits{
		area: true,
	},
}

var (
	_selectObject         = gdi32.NewProc("SelectObject")
	_getDC                = user32.NewProc("GetDC")
	_getTextExtentPoint32 = gdi32.NewProc("GetTextExtentPoint32W")
	_getTextMetrics       = gdi32.NewProc("GetTextMetricsW")
	_releaseDC            = user32.NewProc("ReleaseDC")
)

func getTextDC(hwnd _HWND) (dc _HANDLE) {
	r1, _, err := _getDC.Call(uintptr(hwnd))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error getting DC for preferred size calculations: %v", err))
	}
	dc = _HANDLE(r1)
	r1, _, err = _selectObject.Call(
		uintptr(dc),
		uintptr(controlFont))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error loading control font into device context for preferred size calculation: %v", err))
	}
	return dc
}

func releaseTextDC(hwnd _HWND, dc _HANDLE) {
	r1, _, err := _releaseDC.Call(
		uintptr(hwnd),
		uintptr(dc))
	if r1 == 0 { // failure
		panic(fmt.Errorf("error releasing DC for preferred size calculations: %v", err))
	}
}

// This function runs on uitask; call the functions directly.
func (s *sysData) preferredSize(d *sysSizeData) (width int, height int) {
	// the preferred size of an Area is its size
	if stdDlgSizes[s.ctype].area {
		return s.areawidth, s.areaheight
	}

	if msg := stdDlgSizes[s.ctype].getsize; msg != 0 {
		var size _SIZE

		r1, _, _ := _sendMessage.Call(
			uintptr(s.hwnd),
			msg,
			uintptr(0),
			uintptr(unsafe.Pointer(&size)))
		if r1 != uintptr(_FALSE) { // success
			return int(size.cx), int(size.cy)
		}
		// otherwise the message approach failed, so fall back to the regular approach
		println("message failed; falling back")
	}

	width = stdDlgSizes[s.ctype].width
	if width == 0 {
		width = defaultWidth
	}
	height = stdDlgSizes[s.ctype].height
	width = muldiv(width, d.baseX, 4)   // equivalent to right of rect
	height = muldiv(height, d.baseY, 8) // equivalent to bottom of rect

	return width, height
}

var (
	_mulDiv = kernel32.NewProc("MulDiv")
)

func muldiv(ma int, mb int, div int) int {
	// div will not be 0 in the usages above
	// we also ignore overflow; that isn't likely to happen for our use case anytime soon
	r1, _, _ := _mulDiv.Call(
		uintptr(int32(ma)),
		uintptr(int32(mb)),
		uintptr(int32(div)))
	return int(int32(r1))
}

type _SIZE struct {
	cx int32 // originally LONG
	cy int32
}

type _TEXTMETRICS struct {
	tmHeight           int32
	tmAscent           int32
	tmDescent          int32
	tmInternalLeading  int32
	tmExternalLeading  int32
	tmAveCharWidth     int32
	tmMaxCharWidth     int32
	tmWeight           int32
	tmOverhang         int32
	tmDigitizedAspectX int32
	tmDigitizedAspectY int32
	tmFirstChar        uint16
	tmLastChar         uint16
	tmDefaultChar      uint16
	tmBreakChar        uint16
	tmItalic           byte
	tmUnderlined       byte
	tmStruckOut        byte
	tmPitchAndFamily   byte
	tmCharSet          byte
}
<|endoftext|>


<commit_before>package main

import (
	_ "."
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"
)

func testHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func writeRules(rules []*Rule) string {
	f, err := ioutil.TempFile("", "sf-rules")
	if err != nil {
		log.Fatalln(err)
		return ""
	}
	defer f.Close()
	json.NewEncoder(f).Encode(rules)
	return f.Name()
}

func TestServer(t *testing.T) {
	dummy := httptest.NewServer(http.HandlerFunc(testHandler))
	defer dummy.Close()

	ruleFile := writeRules([]*Rule{
		{Host: "example.com", Forward: dummy.Listener.Addr().String()},
		{Host: "example.org", Serve: "testdata"},
	})
	defer os.Remove(ruleFile)

	s, err := NewServer(ruleFile, time.Hour)
	if err != nil {
		t.Fatal(err)
	}

	var tests = []struct {
		url  string
		code int
		body string
	}{
		{"http://example.com/", 200, "OK"},
		{"http://foo.example.com/", 200, "OK"},
		{"http://example.org/", 200, "contents of index.html\n"},
		{"http://example.net/", 404, "Not found.\n"},
		{"http://fooexample.com/", 404, "Not found.\n"},
	}

	for _, test := range tests {
		req, _ := http.NewRequest("GET", test.url, nil)
		rw := httptest.NewRecorder()
		rw.Body = new(bytes.Buffer)
		s.ServeHTTP(rw, req)
		if g, w := rw.Code, test.code; g != w {
			t.Errorf("%s: code = %d, want %d", test.url, g, w)
		}
		if g, w := rw.Body.String(), test.body; g != w {
			t.Errorf("%s: body = %q, want %q", test.url, g, w)
		}
	}
}
<commit_msg>Fix test code<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"
)

func TestServer(t *testing.T) {
	dummy := httptest.NewServer(http.HandlerFunc(testHandler))
	defer dummy.Close()

	ruleFile := writeRules([]*Rule{
		{Host: "example.com", Forward: dummy.Listener.Addr().String()},
		{Host: "example.org", Serve: "testdata"},
	})
	defer os.Remove(ruleFile)

	s, err := NewServer(ruleFile, time.Hour)
	if err != nil {
		t.Fatal(err)
	}

	var tests = []struct {
		url  string
		code int
		body string
	}{
		{"http://example.com/", 200, "OK"},
		{"http://foo.example.com/", 200, "OK"},
		{"http://example.org/", 200, "contents of index.html\n"},
		{"http://example.net/", 404, "Not found.\n"},
		{"http://fooexample.com/", 404, "Not found.\n"},
	}

	for _, test := range tests {
		req, _ := http.NewRequest("GET", test.url, nil)
		rw := httptest.NewRecorder()
		rw.Body = new(bytes.Buffer)
		s.ServeHTTP(rw, req)
		if g, w := rw.Code, test.code; g != w {
			t.Errorf("%s: code = %d, want %d", test.url, g, w)
		}
		if g, w := rw.Body.String(), test.body; g != w {
			t.Errorf("%s: body = %q, want %q", test.url, g, w)
		}
	}
}

func testHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func writeRules(rules []*Rule) string {
	f, err := ioutil.TempFile("", "sf-rules")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	err = json.NewEncoder(f).Encode(rules)
	if err != nil {
		panic(err)
	}
	return f.Name()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Vitess 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 cluster

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path"
	"time"

	"vitess.io/vitess/go/vt/log"
)

// DefaultCell : If no cell name is passed, then use following
const DefaultCell = "zone1"

var (
	keepData = flag.Bool("keep-data", false, "don't delete the per-test VTDATAROOT subfolders")
)

// LocalProcessCluster Testcases need to use this to iniate a cluster
type LocalProcessCluster struct {
	Keyspaces          []Keyspace
	Cell               string
	BaseTabletUID      int
	Hostname           string
	TopoPort           int
	TmpDirectory       string
	OriginalVTDATAROOT string
	CurrentVTDATAROOT  string

	VtgateMySQLPort int
	VtgateGrpcPort  int
	VtctldHTTPPort  int

	// standalone executable
	VtctlclientProcess VtctlClientProcess
	VtctlProcess       VtctlProcess

	// background executable processes
	topoProcess   EtcdProcess
	vtctldProcess VtctldProcess
	VtgateProcess VtgateProcess

	nextPortForProcess int

	//Extra arguments for vtTablet
	VtTabletExtraArgs []string

	//Extra arguments for vtGate
	VtGateExtraArgs []string
}

// Keyspace : Cluster accepts keyspace to launch it
type Keyspace struct {
	Name      string
	SchemaSQL string
	VSchema   string
	Shards    []Shard
}

// Shard with associated vttablets
type Shard struct {
	Name      string
	Vttablets []Vttablet
}

// Vttablet stores the properties needed to start a vttablet process
type Vttablet struct {
	Type      string
	TabletUID int
	HTTPPort  int
	GrpcPort  int
	MySQLPort int

	// background executable processes
	mysqlctlProcess MysqlctlProcess
	vttabletProcess VttabletProcess
}

// StartTopo starts topology server
func (cluster *LocalProcessCluster) StartTopo() (err error) {
	if cluster.Cell == "" {
		cluster.Cell = DefaultCell
	}
	cluster.TopoPort = cluster.GetAndReservePort()
	cluster.TmpDirectory = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/tmp_%d", cluster.GetAndReservePort()))
	cluster.topoProcess = *EtcdProcessInstance(cluster.TopoPort, cluster.GetAndReservePort(), cluster.Hostname, "global")
	log.Info(fmt.Sprintf("Starting etcd server on port : %d", cluster.TopoPort))
	if err = cluster.topoProcess.Setup(); err != nil {
		log.Error(err.Error())
		return
	}

	log.Info("Creating topo dirs")
	if err = cluster.topoProcess.ManageTopoDir("mkdir", "/vitess/global"); err != nil {
		log.Error(err.Error())
		return
	}

	if err = cluster.topoProcess.ManageTopoDir("mkdir", "/vitess/"+cluster.Cell); err != nil {
		log.Error(err.Error())
		return
	}

	log.Info("Adding cell info")
	cluster.VtctlProcess = *VtctlProcessInstance(cluster.topoProcess.Port, cluster.Hostname)
	if err = cluster.VtctlProcess.AddCellInfo(cluster.Cell); err != nil {
		log.Error(err)
		return
	}

	cluster.vtctldProcess = *VtctldProcessInstance(cluster.GetAndReservePort(), cluster.GetAndReservePort(), cluster.topoProcess.Port, cluster.Hostname, cluster.TmpDirectory)
	log.Info(fmt.Sprintf("Starting vtctld server on port : %d", cluster.vtctldProcess.Port))
	cluster.VtctldHTTPPort = cluster.vtctldProcess.Port
	if err = cluster.vtctldProcess.Setup(cluster.Cell); err != nil {
		log.Error(err.Error())
		return
	}

	cluster.VtctlclientProcess = *VtctlClientProcessInstance("localhost", cluster.vtctldProcess.GrpcPort, cluster.TmpDirectory)
	return
}

// StartUnshardedKeyspace starts unshared keyspace with shard name as "0"
func (cluster *LocalProcessCluster) StartUnshardedKeyspace(keyspace Keyspace, replicaCount int, rdonly bool) error {
	return cluster.StartKeyspace(keyspace, []string{"0"}, replicaCount, rdonly)
}

// StartKeyspace starts required number of shard and the corresponding tablets
// keyspace : struct containing keyspace name, Sqlschema to apply, VSchema to apply
// shardName : list of shard names
// replicaCount: total number of replicas excluding master and rdonly
// rdonly: whether readonly tablets needed
func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool) (err error) {
	totalTabletsRequired := replicaCount + 1 // + 1 is for master
	if rdonly {
		totalTabletsRequired = totalTabletsRequired + 1 // + 1 for rdonly
	}
	shards := make([]Shard, 0)
	log.Info("Starting keyspace : " + keyspace.Name)
	_ = cluster.VtctlProcess.CreateKeyspace(keyspace.Name)
	for _, shardName := range shardNames {
		shard := &Shard{
			Name: shardName,
		}
		log.Info("Starting shard : " + shardName)
		for i := 0; i < totalTabletsRequired; i++ {
			// instantiate vttable object with reserved ports
			tablet := &Vttablet{
				TabletUID: cluster.GetAndReserveTabletUID(),
				HTTPPort:  cluster.GetAndReservePort(),
				GrpcPort:  cluster.GetAndReservePort(),
				MySQLPort: cluster.GetAndReservePort(),
			}
			if i == 0 { // Make the first one as master
				tablet.Type = "master"
			} else if i == totalTabletsRequired-1 && rdonly { // Make the last one as rdonly if rdonly flag is passed
				tablet.Type = "rdonly"
			}
			// Start Mysqlctl process
			log.Info(fmt.Sprintf("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort))
			tablet.mysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory)
			if err = tablet.mysqlctlProcess.Start(); err != nil {
				log.Error(err.Error())
				return
			}

			// start vttablet process
			tablet.vttabletProcess = *VttabletProcessInstance(tablet.HTTPPort,
				tablet.GrpcPort,
				tablet.TabletUID,
				cluster.Cell,
				shardName,
				keyspace.Name,
				cluster.vtctldProcess.Port,
				tablet.Type,
				cluster.topoProcess.Port,
				cluster.Hostname,
				cluster.TmpDirectory,
				cluster.VtTabletExtraArgs)
			log.Info(fmt.Sprintf("Starting vttablet for tablet uid %d, grpc port %d", tablet.TabletUID, tablet.GrpcPort))

			if err = tablet.vttabletProcess.Setup(); err != nil {
				log.Error(err.Error())
				return
			}

			shard.Vttablets = append(shard.Vttablets, *tablet)
		}

		// Make first tablet as master
		if err = cluster.VtctlclientProcess.InitShardMaster(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil {
			log.Error(err.Error())
			return
		}

		shards = append(shards, *shard)
	}
	keyspace.Shards = shards
	cluster.Keyspaces = append(cluster.Keyspaces, keyspace)

	// Apply Schema SQL
	if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil {
		log.Error(err.Error())
		return
	}

	//Apply VSchema
	if keyspace.VSchema != "" {
		if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil {
			log.Error(err.Error())
			return
		}
	}

	log.Info("Done creating keyspace : " + keyspace.Name)
	return
}

// StartVtgate starts vtgate
func (cluster *LocalProcessCluster) StartVtgate() (err error) {
	vtgateHTTPPort := cluster.GetAndReservePort()
	vtgateGrpcPort := cluster.GetAndReservePort()
	cluster.VtgateMySQLPort = cluster.GetAndReservePort()
	log.Info(fmt.Sprintf("Starting vtgate on port %d", vtgateHTTPPort))
	cluster.VtgateProcess = *VtgateProcessInstance(
		vtgateHTTPPort,
		vtgateGrpcPort,
		cluster.VtgateMySQLPort,
		cluster.Cell,
		cluster.Cell,
		cluster.Hostname,
		"MASTER,REPLICA",
		cluster.topoProcess.Port,
		cluster.TmpDirectory,
		cluster.VtGateExtraArgs)

	log.Info(fmt.Sprintf("Vtgate started, connect to mysql using : mysql -h 127.0.0.1 -P %d", cluster.VtgateMySQLPort))
	if err = cluster.VtgateProcess.Setup(); err != nil {
		return err
	}
	if err = cluster.WaitForTabletsToHealthyInVtgate(); err != nil {
		return err
	}
	return nil
}

// NewCluster instantiates a new cluster
func NewCluster(cell string, hostname string) *LocalProcessCluster {
	cluster := &LocalProcessCluster{Cell: cell, Hostname: hostname}
	cluster.OriginalVTDATAROOT = os.Getenv("VTDATAROOT")
	cluster.CurrentVTDATAROOT = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("vtroot_%d", cluster.GetAndReservePort()))
	_ = createDirectory(cluster.CurrentVTDATAROOT, 0700)
	_ = os.Setenv("VTDATAROOT", cluster.CurrentVTDATAROOT)
	rand.Seed(time.Now().UTC().UnixNano())
	return cluster
}

// ReStartVtgate starts vtgate with updated configs
func (cluster *LocalProcessCluster) ReStartVtgate() (err error) {
	err = cluster.VtgateProcess.TearDown()
	if err != nil {
		log.Error(err.Error())
		return
	}
	err = cluster.StartVtgate()
	if err != nil {
		log.Error(err.Error())
		return
	}
	return err
}

// WaitForTabletsToHealthyInVtgate waits for all tablets in all shards to be healthy as per vtgate
func (cluster *LocalProcessCluster) WaitForTabletsToHealthyInVtgate() (err error) {
	var isRdOnlyPresent bool
	for _, keyspace := range cluster.Keyspaces {
		for _, shard := range keyspace.Shards {
			isRdOnlyPresent = false
			if err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspace.Name, shard.Name)); err != nil {
				return err
			}
			if err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace.Name, shard.Name)); err != nil {
				return err
			}
			for _, tablet := range shard.Vttablets {
				if tablet.Type == "rdonly" {
					isRdOnlyPresent = true
				}
			}
			if isRdOnlyPresent {
				err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspace.Name, shard.Name))
			}
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// Teardown brings down the cluster by invoking teardown for individual processes
func (cluster *LocalProcessCluster) Teardown() (err error) {
	if err = cluster.VtgateProcess.TearDown(); err != nil {
		log.Error(err.Error())
		return
	}

	for _, keyspace := range cluster.Keyspaces {
		for _, shard := range keyspace.Shards {
			for _, tablet := range shard.Vttablets {
				if err = tablet.mysqlctlProcess.Stop(); err != nil {
					log.Error(err.Error())
					return
				}

				if err = tablet.vttabletProcess.TearDown(); err != nil {
					log.Error(err.Error())
					return
				}
			}
		}
	}

	if err = cluster.vtctldProcess.TearDown(); err != nil {
		log.Error(err.Error())
		return
	}

	if err = cluster.topoProcess.TearDown(cluster.Cell, cluster.OriginalVTDATAROOT, cluster.CurrentVTDATAROOT, *keepData); err != nil {
		log.Error(err.Error())
		return
	}
	return err
}

// GetAndReservePort gives port for required process
func (cluster *LocalProcessCluster) GetAndReservePort() int {
	if cluster.nextPortForProcess == 0 {
		cluster.nextPortForProcess = getRandomNumber(20000, 15000)
	}
	cluster.nextPortForProcess = cluster.nextPortForProcess + 1
	return cluster.nextPortForProcess
}

// GetAndReserveTabletUID gives tablet uid
func (cluster *LocalProcessCluster) GetAndReserveTabletUID() int {
	if cluster.BaseTabletUID == 0 {
		cluster.BaseTabletUID = getRandomNumber(10000, 0)
	}
	cluster.BaseTabletUID = cluster.BaseTabletUID + 1
	return cluster.BaseTabletUID
}

func getRandomNumber(maxNumber int32, baseNumber int) int {
	return int(rand.Int31n(maxNumber)) + baseNumber
}<commit_msg>vreplication: keyspace_id() support<commit_after>/*
Copyright 2019 The Vitess 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 cluster

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path"
	"time"

	"vitess.io/vitess/go/vt/log"
)

// DefaultCell : If no cell name is passed, then use following
const DefaultCell = "zone1"

var (
	keepData = flag.Bool("keep-data", false, "don't delete the per-test VTDATAROOT subfolders")
)

// LocalProcessCluster Testcases need to use this to iniate a cluster
type LocalProcessCluster struct {
	Keyspaces          []Keyspace
	Cell               string
	BaseTabletUID      int
	Hostname           string
	TopoPort           int
	TmpDirectory       string
	OriginalVTDATAROOT string
	CurrentVTDATAROOT  string

	VtgateMySQLPort int
	VtgateGrpcPort  int
	VtctldHTTPPort  int

	// standalone executable
	VtctlclientProcess VtctlClientProcess
	VtctlProcess       VtctlProcess

	// background executable processes
	topoProcess   EtcdProcess
	vtctldProcess VtctldProcess
	VtgateProcess VtgateProcess

	nextPortForProcess int

	//Extra arguments for vtTablet
	VtTabletExtraArgs []string

	//Extra arguments for vtGate
	VtGateExtraArgs []string
}

// Keyspace : Cluster accepts keyspace to launch it
type Keyspace struct {
	Name      string
	SchemaSQL string
	VSchema   string
	Shards    []Shard
}

// Shard with associated vttablets
type Shard struct {
	Name      string
	Vttablets []Vttablet
}

// Vttablet stores the properties needed to start a vttablet process
type Vttablet struct {
	Type      string
	TabletUID int
	HTTPPort  int
	GrpcPort  int
	MySQLPort int

	// background executable processes
	mysqlctlProcess MysqlctlProcess
	vttabletProcess VttabletProcess
}

// StartTopo starts topology server
func (cluster *LocalProcessCluster) StartTopo() (err error) {
	if cluster.Cell == "" {
		cluster.Cell = DefaultCell
	}
	cluster.TopoPort = cluster.GetAndReservePort()
	cluster.TmpDirectory = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("/tmp_%d", cluster.GetAndReservePort()))
	cluster.topoProcess = *EtcdProcessInstance(cluster.TopoPort, cluster.GetAndReservePort(), cluster.Hostname, "global")
	log.Info(fmt.Sprintf("Starting etcd server on port : %d", cluster.TopoPort))
	if err = cluster.topoProcess.Setup(); err != nil {
		log.Error(err.Error())
		return
	}

	log.Info("Creating topo dirs")
	if err = cluster.topoProcess.ManageTopoDir("mkdir", "/vitess/global"); err != nil {
		log.Error(err.Error())
		return
	}

	if err = cluster.topoProcess.ManageTopoDir("mkdir", "/vitess/"+cluster.Cell); err != nil {
		log.Error(err.Error())
		return
	}

	log.Info("Adding cell info")
	cluster.VtctlProcess = *VtctlProcessInstance(cluster.topoProcess.Port, cluster.Hostname)
	if err = cluster.VtctlProcess.AddCellInfo(cluster.Cell); err != nil {
		log.Error(err)
		return
	}

	cluster.vtctldProcess = *VtctldProcessInstance(cluster.GetAndReservePort(), cluster.GetAndReservePort(), cluster.topoProcess.Port, cluster.Hostname, cluster.TmpDirectory)
	log.Info(fmt.Sprintf("Starting vtctld server on port : %d", cluster.vtctldProcess.Port))
	cluster.VtctldHTTPPort = cluster.vtctldProcess.Port
	if err = cluster.vtctldProcess.Setup(cluster.Cell); err != nil {
		log.Error(err.Error())
		return
	}

	cluster.VtctlclientProcess = *VtctlClientProcessInstance("localhost", cluster.vtctldProcess.GrpcPort, cluster.TmpDirectory)
	return
}

// StartUnshardedKeyspace starts unshared keyspace with shard name as "0"
func (cluster *LocalProcessCluster) StartUnshardedKeyspace(keyspace Keyspace, replicaCount int, rdonly bool) error {
	return cluster.StartKeyspace(keyspace, []string{"0"}, replicaCount, rdonly)
}

// StartKeyspace starts required number of shard and the corresponding tablets
// keyspace : struct containing keyspace name, Sqlschema to apply, VSchema to apply
// shardName : list of shard names
// replicaCount: total number of replicas excluding master and rdonly
// rdonly: whether readonly tablets needed
func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool) (err error) {
	totalTabletsRequired := replicaCount + 1 // + 1 is for master
	if rdonly {
		totalTabletsRequired = totalTabletsRequired + 1 // + 1 for rdonly
	}
	shards := make([]Shard, 0)
	log.Info("Starting keyspace : " + keyspace.Name)
	_ = cluster.VtctlProcess.CreateKeyspace(keyspace.Name)
	for _, shardName := range shardNames {
		shard := &Shard{
			Name: shardName,
		}
		log.Info("Starting shard : " + shardName)
		for i := 0; i < totalTabletsRequired; i++ {
			// instantiate vttable object with reserved ports
			tablet := &Vttablet{
				TabletUID: cluster.GetAndReserveTabletUID(),
				HTTPPort:  cluster.GetAndReservePort(),
				GrpcPort:  cluster.GetAndReservePort(),
				MySQLPort: cluster.GetAndReservePort(),
			}
			if i == 0 { // Make the first one as master
				tablet.Type = "master"
			} else if i == totalTabletsRequired-1 && rdonly { // Make the last one as rdonly if rdonly flag is passed
				tablet.Type = "rdonly"
			}
			// Start Mysqlctl process
			log.Info(fmt.Sprintf("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort))
			tablet.mysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory)
			if err = tablet.mysqlctlProcess.Start(); err != nil {
				log.Error(err.Error())
				return
			}

			// start vttablet process
			tablet.vttabletProcess = *VttabletProcessInstance(tablet.HTTPPort,
				tablet.GrpcPort,
				tablet.TabletUID,
				cluster.Cell,
				shardName,
				keyspace.Name,
				cluster.vtctldProcess.Port,
				tablet.Type,
				cluster.topoProcess.Port,
				cluster.Hostname,
				cluster.TmpDirectory,
				cluster.VtTabletExtraArgs)
			log.Info(fmt.Sprintf("Starting vttablet for tablet uid %d, grpc port %d", tablet.TabletUID, tablet.GrpcPort))

			if err = tablet.vttabletProcess.Setup(); err != nil {
				log.Error(err.Error())
				return
			}

			shard.Vttablets = append(shard.Vttablets, *tablet)
		}

		// Make first tablet as master
		if err = cluster.VtctlclientProcess.InitShardMaster(keyspace.Name, shardName, cluster.Cell, shard.Vttablets[0].TabletUID); err != nil {
			log.Error(err.Error())
			return
		}

		shards = append(shards, *shard)
	}
	keyspace.Shards = shards
	cluster.Keyspaces = append(cluster.Keyspaces, keyspace)

	// Apply Schema SQL
	if err = cluster.VtctlclientProcess.ApplySchema(keyspace.Name, keyspace.SchemaSQL); err != nil {
		log.Error(err.Error())
		return
	}

	//Apply VSchema
	if keyspace.VSchema != "" {
		if err = cluster.VtctlclientProcess.ApplyVSchema(keyspace.Name, keyspace.VSchema); err != nil {
			log.Error(err.Error())
			return
		}
	}

	log.Info("Done creating keyspace : " + keyspace.Name)
	return
}

// StartVtgate starts vtgate
func (cluster *LocalProcessCluster) StartVtgate() (err error) {
	vtgateHTTPPort := cluster.GetAndReservePort()
	vtgateGrpcPort := cluster.GetAndReservePort()
	cluster.VtgateMySQLPort = cluster.GetAndReservePort()
	log.Info(fmt.Sprintf("Starting vtgate on port %d", vtgateHTTPPort))
	cluster.VtgateProcess = *VtgateProcessInstance(
		vtgateHTTPPort,
		vtgateGrpcPort,
		cluster.VtgateMySQLPort,
		cluster.Cell,
		cluster.Cell,
		cluster.Hostname,
		"MASTER,REPLICA",
		cluster.topoProcess.Port,
		cluster.TmpDirectory,
		cluster.VtGateExtraArgs)

	log.Info(fmt.Sprintf("Vtgate started, connect to mysql using : mysql -h 127.0.0.1 -P %d", cluster.VtgateMySQLPort))
	if err = cluster.VtgateProcess.Setup(); err != nil {
		return err
	}
	if err = cluster.WaitForTabletsToHealthyInVtgate(); err != nil {
		return err
	}
	return nil
}

// NewCluster instantiates a new cluster
func NewCluster(cell string, hostname string) *LocalProcessCluster {
	cluster := &LocalProcessCluster{Cell: cell, Hostname: hostname}
	cluster.OriginalVTDATAROOT = os.Getenv("VTDATAROOT")
	cluster.CurrentVTDATAROOT = path.Join(os.Getenv("VTDATAROOT"), fmt.Sprintf("vtroot_%d", cluster.GetAndReservePort()))
	_ = createDirectory(cluster.CurrentVTDATAROOT, 0700)
	_ = os.Setenv("VTDATAROOT", cluster.CurrentVTDATAROOT)
	rand.Seed(time.Now().UTC().UnixNano())
	return cluster
}

// ReStartVtgate starts vtgate with updated configs
func (cluster *LocalProcessCluster) ReStartVtgate() (err error) {
	err = cluster.VtgateProcess.TearDown()
	if err != nil {
		log.Error(err.Error())
		return
	}
	err = cluster.StartVtgate()
	if err != nil {
		log.Error(err.Error())
		return
	}
	return err
}

// WaitForTabletsToHealthyInVtgate waits for all tablets in all shards to be healthy as per vtgate
func (cluster *LocalProcessCluster) WaitForTabletsToHealthyInVtgate() (err error) {
	var isRdOnlyPresent bool
	for _, keyspace := range cluster.Keyspaces {
		for _, shard := range keyspace.Shards {
			isRdOnlyPresent = false
			if err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.master", keyspace.Name, shard.Name)); err != nil {
				return err
			}
			if err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.replica", keyspace.Name, shard.Name)); err != nil {
				return err
			}
			for _, tablet := range shard.Vttablets {
				if tablet.Type == "rdonly" {
					isRdOnlyPresent = true
				}
			}
			if isRdOnlyPresent {
				err = cluster.VtgateProcess.WaitForStatusOfTabletInShard(fmt.Sprintf("%s.%s.rdonly", keyspace.Name, shard.Name))
			}
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// Teardown brings down the cluster by invoking teardown for individual processes
func (cluster *LocalProcessCluster) Teardown() (err error) {
	if err = cluster.VtgateProcess.TearDown(); err != nil {
		log.Error(err.Error())
		return
	}

	for _, keyspace := range cluster.Keyspaces {
		for _, shard := range keyspace.Shards {
			for _, tablet := range shard.Vttablets {
				if err = tablet.mysqlctlProcess.Stop(); err != nil {
					log.Error(err.Error())
					return
				}

				if err = tablet.vttabletProcess.TearDown(); err != nil {
					log.Error(err.Error())
					return
				}
			}
		}
	}

	if err = cluster.vtctldProcess.TearDown(); err != nil {
		log.Error(err.Error())
		return
	}

	if err = cluster.topoProcess.TearDown(cluster.Cell, cluster.OriginalVTDATAROOT, cluster.CurrentVTDATAROOT, *keepData); err != nil {
		log.Error(err.Error())
		return
	}
	return err
}

// GetAndReservePort gives port for required process
func (cluster *LocalProcessCluster) GetAndReservePort() int {
	if cluster.nextPortForProcess == 0 {
		cluster.nextPortForProcess = getRandomNumber(20000, 15000)
	}
	cluster.nextPortForProcess = cluster.nextPortForProcess + 1
	return cluster.nextPortForProcess
}

// GetAndReserveTabletUID gives tablet uid
func (cluster *LocalProcessCluster) GetAndReserveTabletUID() int {
	if cluster.BaseTabletUID == 0 {
		cluster.BaseTabletUID = getRandomNumber(10000, 0)
	}
	cluster.BaseTabletUID = cluster.BaseTabletUID + 1
	return cluster.BaseTabletUID
}

func getRandomNumber(maxNumber int32, baseNumber int) int {
	return int(rand.Int31n(maxNumber)) + baseNumber
}
<|endoftext|>


<commit_before>package sshCmd

import (
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"

	"github.com/cloudfoundry-incubator/diego-ssh/helpers"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/ssh/options"
	"github.com/cloudfoundry/cli/cf/ssh/sigwinch"
	"github.com/cloudfoundry/cli/cf/ssh/terminal"
	"github.com/docker/docker/pkg/term"
)

//go:generate counterfeiter -o fakes/fake_secure_shell.go . SecureShell
type SecureShell interface {
	Connect(opts *options.SSHOptions) error
	InteractiveSession() error
	LocalPortForward() error
	Wait() error
	Close() error
}

//go:generate counterfeiter -o fakes/fake_secure_dialer.go . SecureDialer
type SecureDialer interface {
	Dial(network, address string, config *ssh.ClientConfig) (SecureClient, error)
}

//go:generate counterfeiter -o fakes/fake_secure_client.go . SecureClient
type SecureClient interface {
	NewSession() (SecureSession, error)
	Conn() ssh.Conn
	Dial(network, address string) (net.Conn, error)
	Wait() error
	Close() error
}

//go:generate counterfeiter -o fakes/fake_listener_factory.go . ListenerFactory
type ListenerFactory interface {
	Listen(network, address string) (net.Listener, error)
}

//go:generate counterfeiter -o fakes/fake_secure_session.go . SecureSession
type SecureSession interface {
	RequestPty(term string, height, width int, termModes ssh.TerminalModes) error
	SendRequest(name string, wantReply bool, payload []byte) (bool, error)
	StdinPipe() (io.WriteCloser, error)
	StdoutPipe() (io.Reader, error)
	StderrPipe() (io.Reader, error)
	Start(command string) error
	Shell() error
	Wait() error
	Close() error
}

type secureShell struct {
	secureDialer           SecureDialer
	terminalHelper         sshTerminal.TerminalHelper
	listenerFactory        ListenerFactory
	keepAliveInterval      time.Duration
	app                    models.Application
	sshEndpointFingerprint string
	sshEndpoint            string
	token                  string
	secureClient           SecureClient
	opts                   *options.SSHOptions

	localListeners []net.Listener
}

func NewSecureShell(
	secureDialer SecureDialer,
	terminalHelper sshTerminal.TerminalHelper,
	listenerFactory ListenerFactory,
	keepAliveInterval time.Duration,
	app models.Application,
	sshEndpointFingerprint string,
	sshEndpoint string,
	token string,
) SecureShell {
	return &secureShell{
		secureDialer:      secureDialer,
		terminalHelper:    terminalHelper,
		listenerFactory:   listenerFactory,
		keepAliveInterval: keepAliveInterval,
		app:               app,
		sshEndpointFingerprint: sshEndpointFingerprint,
		sshEndpoint:            sshEndpoint,
		token:                  token,
		localListeners:         []net.Listener{},
	}
}

func (c *secureShell) Connect(opts *options.SSHOptions) error {
	err := c.validateTarget(opts)
	if err != nil {
		return err
	}

	clientConfig := &ssh.ClientConfig{
		User: fmt.Sprintf("cf:%s/%d", c.app.Guid, opts.Index),
		Auth: []ssh.AuthMethod{
			ssh.Password(c.token),
		},
		HostKeyCallback: fingerprintCallback(opts, c.sshEndpointFingerprint),
	}

	secureClient, err := c.secureDialer.Dial("tcp", c.sshEndpoint, clientConfig)
	if err != nil {
		return err
	}

	c.secureClient = secureClient
	c.opts = opts
	return nil
}

func (c *secureShell) Close() error {
	for _, listener := range c.localListeners {
		listener.Close()
	}
	return c.secureClient.Close()
}

func (c *secureShell) LocalPortForward() error {
	for _, forwardSpec := range c.opts.ForwardSpecs {
		listener, err := c.listenerFactory.Listen("tcp", forwardSpec.ListenAddress)
		if err != nil {
			return err
		}
		c.localListeners = append(c.localListeners, listener)

		go c.localForwardAcceptLoop(listener, forwardSpec.ConnectAddress)
	}

	return nil
}

func (c *secureShell) localForwardAcceptLoop(listener net.Listener, addr string) {
	defer listener.Close()

	for {
		conn, err := listener.Accept()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				time.Sleep(100 * time.Millisecond)
				continue
			}
			return
		}

		go c.handleForwardConnection(conn, addr)
	}
}

func (c *secureShell) handleForwardConnection(conn net.Conn, targetAddr string) {
	defer conn.Close()

	target, err := c.secureClient.Dial("tcp", targetAddr)
	if err != nil {
		fmt.Printf("connect to %s failed: %s\n", targetAddr, err.Error())
		return
	}
	defer target.Close()

	wg := &sync.WaitGroup{}
	wg.Add(2)

	go copyAndClose(wg, conn, target)
	go copyAndClose(wg, target, conn)
	wg.Wait()
}

func copyAndClose(wg *sync.WaitGroup, dest io.WriteCloser, src io.Reader) {
	io.Copy(dest, src)
	dest.Close()
	if wg != nil {
		wg.Done()
	}
}

func (c *secureShell) InteractiveSession() error {
	var err error

	secureClient := c.secureClient
	opts := c.opts

	session, err := secureClient.NewSession()
	if err != nil {
		return fmt.Errorf("SSH session allocation failed: %s", err.Error())
	}
	defer session.Close()

	stdin, stdout, stderr := c.terminalHelper.StdStreams()

	inPipe, err := session.StdinPipe()
	if err != nil {
		return err
	}

	outPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}

	errPipe, err := session.StderrPipe()
	if err != nil {
		return err
	}

	stdinFd, stdinIsTerminal := c.terminalHelper.GetFdInfo(stdin)
	stdoutFd, stdoutIsTerminal := c.terminalHelper.GetFdInfo(stdout)

	if c.shouldAllocateTerminal(opts, stdinIsTerminal) {
		modes := ssh.TerminalModes{
			ssh.ECHO:          1,
			ssh.TTY_OP_ISPEED: 115200,
			ssh.TTY_OP_OSPEED: 115200,
		}

		width, height := c.getWindowDimensions(stdoutFd)

		err = session.RequestPty(c.terminalType(), height, width, modes)
		if err != nil {
			return err
		}

		var state *term.State
		state, err = c.terminalHelper.SetRawTerminal(stdinFd)
		if err == nil {
			defer c.terminalHelper.RestoreTerminal(stdinFd, state)
		}
	}

	if len(opts.Command) != 0 {
		cmd := strings.Join(opts.Command, " ")
		err = session.Start(cmd)
		if err != nil {
			return err
		}
	} else {
		err = session.Shell()
		if err != nil {
			return err
		}
	}

	go copyAndClose(nil, inPipe, stdin)
	go io.Copy(stdout, outPipe)
	go io.Copy(stderr, errPipe)

	if stdoutIsTerminal {
		resized := make(chan os.Signal, 16)

		if runtime.GOOS == "windows" {
			ticker := time.NewTicker(250 * time.Millisecond)
			defer ticker.Stop()

			go func() {
				for _ = range ticker.C {
					resized <- syscall.Signal(-1)
				}
				close(resized)
			}()
		} else {
			signal.Notify(resized, sigwinch.SIGWINCH())
			defer func() { signal.Stop(resized); close(resized) }()
		}

		go c.resize(resized, session, stdoutFd)
	}

	keepaliveStopCh := make(chan struct{})
	defer close(keepaliveStopCh)

	go keepalive(secureClient.Conn(), time.NewTicker(c.keepAliveInterval), keepaliveStopCh)

	return session.Wait()
}

func (c *secureShell) Wait() error {
	return c.secureClient.Wait()
}

func (c *secureShell) validateTarget(opts *options.SSHOptions) error {
	if strings.ToUpper(c.app.State) != "STARTED" {
		return fmt.Errorf("Application %q is not in the STARTED state", opts.AppName)
	}

	if !c.app.Diego {
		return fmt.Errorf("Application %q is not running on Diego", opts.AppName)
	}

	return nil
}

type hostKeyCallback func(hostname string, remote net.Addr, key ssh.PublicKey) error

func fingerprintCallback(opts *options.SSHOptions, expectedFingerprint string) hostKeyCallback {
	if opts.SkipHostValidation {
		return nil
	}

	return func(hostname string, remote net.Addr, key ssh.PublicKey) error {
		switch len(expectedFingerprint) {
		case helpers.SHA1_FINGERPRINT_LENGTH:
			fingerprint := helpers.SHA1Fingerprint(key)
			if fingerprint != expectedFingerprint {
				return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint)
			}
		case helpers.MD5_FINGERPRINT_LENGTH:
			fingerprint := helpers.MD5Fingerprint(key)
			if fingerprint != expectedFingerprint {
				return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint)
			}
		case 0:
			fingerprint := helpers.MD5Fingerprint(key)
			return fmt.Errorf("Unable to verify identity of host.\n\nThe fingerprint of the received key was %q.", fingerprint)
		default:
			return errors.New("Unsupported host key fingerprint format")
		}
		return nil
	}
}

func (c *secureShell) shouldAllocateTerminal(opts *options.SSHOptions, stdinIsTerminal bool) bool {
	switch opts.TerminalRequest {
	case options.REQUEST_TTY_FORCE:
		return true
	case options.REQUEST_TTY_NO:
		return false
	case options.REQUEST_TTY_YES:
		return stdinIsTerminal
	case options.REQUEST_TTY_AUTO:
		return len(opts.Command) == 0 && stdinIsTerminal
	default:
		return false
	}
}

func (c *secureShell) resize(resized <-chan os.Signal, session SecureSession, terminalFd uintptr) {
	type resizeMessage struct {
		Width       uint32
		Height      uint32
		PixelWidth  uint32
		PixelHeight uint32
	}

	var previousWidth, previousHeight int

	for _ = range resized {
		width, height := c.getWindowDimensions(terminalFd)

		if width == previousWidth && height == previousHeight {
			continue
		}

		message := resizeMessage{
			Width:  uint32(width),
			Height: uint32(height),
		}

		session.SendRequest("window-change", false, ssh.Marshal(message))

		previousWidth = width
		previousHeight = height
	}
}

func keepalive(conn ssh.Conn, ticker *time.Ticker, stopCh chan struct{}) {
	for {
		select {
		case <-ticker.C:
			conn.SendRequest("keepalive@cloudfoundry.org", true, nil)
		case <-stopCh:
			ticker.Stop()
			return
		}
	}
}

func (c *secureShell) terminalType() string {
	term := os.Getenv("TERM")
	if term == "" {
		term = "xterm"
	}
	return term
}

func (c *secureShell) getWindowDimensions(terminalFd uintptr) (width int, height int) {
	winSize, err := c.terminalHelper.GetWinsize(terminalFd)
	if err != nil {
		winSize = &term.Winsize{
			Width:  80,
			Height: 43,
		}
	}

	return int(winSize.Width), int(winSize.Height)
}

type secureDialer struct{}

func (d *secureDialer) Dial(network string, address string, config *ssh.ClientConfig) (SecureClient, error) {
	client, err := ssh.Dial(network, address, config)
	if err != nil {
		return nil, err
	}

	return &secureClient{client: client}, nil
}

func DefaultSecureDialer() SecureDialer {
	return &secureDialer{}
}

type secureClient struct{ client *ssh.Client }

func (sc *secureClient) Close() error   { return sc.client.Close() }
func (sc *secureClient) Conn() ssh.Conn { return sc.client.Conn }
func (sc *secureClient) Wait() error    { return sc.client.Wait() }
func (sc *secureClient) Dial(n, addr string) (net.Conn, error) {
	return sc.client.Dial(n, addr)
}
func (sc *secureClient) NewSession() (SecureSession, error) {
	return sc.client.NewSession()
}

type listenerFactory struct{}

func (lf *listenerFactory) Listen(network, address string) (net.Listener, error) {
	return net.Listen(network, address)
}

func DefaultListenerFactory() ListenerFactory {
	return &listenerFactory{}
}
<commit_msg>Remove diego-ssh/helpers from ssh command<commit_after>package sshCmd

import (
	"crypto/md5"
	"crypto/sha1"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"os/signal"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh"

	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/ssh/options"
	"github.com/cloudfoundry/cli/cf/ssh/sigwinch"
	"github.com/cloudfoundry/cli/cf/ssh/terminal"
	"github.com/docker/docker/pkg/term"
)

const (
	md5FingerprintLength  = 47 // inclusive of space between bytes
	sha1FingerprintLength = 59 // inclusive of space between bytes
)

//go:generate counterfeiter -o fakes/fake_secure_shell.go . SecureShell
type SecureShell interface {
	Connect(opts *options.SSHOptions) error
	InteractiveSession() error
	LocalPortForward() error
	Wait() error
	Close() error
}

//go:generate counterfeiter -o fakes/fake_secure_dialer.go . SecureDialer
type SecureDialer interface {
	Dial(network, address string, config *ssh.ClientConfig) (SecureClient, error)
}

//go:generate counterfeiter -o fakes/fake_secure_client.go . SecureClient
type SecureClient interface {
	NewSession() (SecureSession, error)
	Conn() ssh.Conn
	Dial(network, address string) (net.Conn, error)
	Wait() error
	Close() error
}

//go:generate counterfeiter -o fakes/fake_listener_factory.go . ListenerFactory
type ListenerFactory interface {
	Listen(network, address string) (net.Listener, error)
}

//go:generate counterfeiter -o fakes/fake_secure_session.go . SecureSession
type SecureSession interface {
	RequestPty(term string, height, width int, termModes ssh.TerminalModes) error
	SendRequest(name string, wantReply bool, payload []byte) (bool, error)
	StdinPipe() (io.WriteCloser, error)
	StdoutPipe() (io.Reader, error)
	StderrPipe() (io.Reader, error)
	Start(command string) error
	Shell() error
	Wait() error
	Close() error
}

type secureShell struct {
	secureDialer           SecureDialer
	terminalHelper         sshTerminal.TerminalHelper
	listenerFactory        ListenerFactory
	keepAliveInterval      time.Duration
	app                    models.Application
	sshEndpointFingerprint string
	sshEndpoint            string
	token                  string
	secureClient           SecureClient
	opts                   *options.SSHOptions

	localListeners []net.Listener
}

func NewSecureShell(
	secureDialer SecureDialer,
	terminalHelper sshTerminal.TerminalHelper,
	listenerFactory ListenerFactory,
	keepAliveInterval time.Duration,
	app models.Application,
	sshEndpointFingerprint string,
	sshEndpoint string,
	token string,
) SecureShell {
	return &secureShell{
		secureDialer:      secureDialer,
		terminalHelper:    terminalHelper,
		listenerFactory:   listenerFactory,
		keepAliveInterval: keepAliveInterval,
		app:               app,
		sshEndpointFingerprint: sshEndpointFingerprint,
		sshEndpoint:            sshEndpoint,
		token:                  token,
		localListeners:         []net.Listener{},
	}
}

func (c *secureShell) Connect(opts *options.SSHOptions) error {
	err := c.validateTarget(opts)
	if err != nil {
		return err
	}

	clientConfig := &ssh.ClientConfig{
		User: fmt.Sprintf("cf:%s/%d", c.app.Guid, opts.Index),
		Auth: []ssh.AuthMethod{
			ssh.Password(c.token),
		},
		HostKeyCallback: fingerprintCallback(opts, c.sshEndpointFingerprint),
	}

	secureClient, err := c.secureDialer.Dial("tcp", c.sshEndpoint, clientConfig)
	if err != nil {
		return err
	}

	c.secureClient = secureClient
	c.opts = opts
	return nil
}

func (c *secureShell) Close() error {
	for _, listener := range c.localListeners {
		listener.Close()
	}
	return c.secureClient.Close()
}

func (c *secureShell) LocalPortForward() error {
	for _, forwardSpec := range c.opts.ForwardSpecs {
		listener, err := c.listenerFactory.Listen("tcp", forwardSpec.ListenAddress)
		if err != nil {
			return err
		}
		c.localListeners = append(c.localListeners, listener)

		go c.localForwardAcceptLoop(listener, forwardSpec.ConnectAddress)
	}

	return nil
}

func (c *secureShell) localForwardAcceptLoop(listener net.Listener, addr string) {
	defer listener.Close()

	for {
		conn, err := listener.Accept()
		if err != nil {
			if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
				time.Sleep(100 * time.Millisecond)
				continue
			}
			return
		}

		go c.handleForwardConnection(conn, addr)
	}
}

func (c *secureShell) handleForwardConnection(conn net.Conn, targetAddr string) {
	defer conn.Close()

	target, err := c.secureClient.Dial("tcp", targetAddr)
	if err != nil {
		fmt.Printf("connect to %s failed: %s\n", targetAddr, err.Error())
		return
	}
	defer target.Close()

	wg := &sync.WaitGroup{}
	wg.Add(2)

	go copyAndClose(wg, conn, target)
	go copyAndClose(wg, target, conn)
	wg.Wait()
}

func copyAndClose(wg *sync.WaitGroup, dest io.WriteCloser, src io.Reader) {
	io.Copy(dest, src)
	dest.Close()
	if wg != nil {
		wg.Done()
	}
}

func (c *secureShell) InteractiveSession() error {
	var err error

	secureClient := c.secureClient
	opts := c.opts

	session, err := secureClient.NewSession()
	if err != nil {
		return fmt.Errorf("SSH session allocation failed: %s", err.Error())
	}
	defer session.Close()

	stdin, stdout, stderr := c.terminalHelper.StdStreams()

	inPipe, err := session.StdinPipe()
	if err != nil {
		return err
	}

	outPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}

	errPipe, err := session.StderrPipe()
	if err != nil {
		return err
	}

	stdinFd, stdinIsTerminal := c.terminalHelper.GetFdInfo(stdin)
	stdoutFd, stdoutIsTerminal := c.terminalHelper.GetFdInfo(stdout)

	if c.shouldAllocateTerminal(opts, stdinIsTerminal) {
		modes := ssh.TerminalModes{
			ssh.ECHO:          1,
			ssh.TTY_OP_ISPEED: 115200,
			ssh.TTY_OP_OSPEED: 115200,
		}

		width, height := c.getWindowDimensions(stdoutFd)

		err = session.RequestPty(c.terminalType(), height, width, modes)
		if err != nil {
			return err
		}

		var state *term.State
		state, err = c.terminalHelper.SetRawTerminal(stdinFd)
		if err == nil {
			defer c.terminalHelper.RestoreTerminal(stdinFd, state)
		}
	}

	if len(opts.Command) != 0 {
		cmd := strings.Join(opts.Command, " ")
		err = session.Start(cmd)
		if err != nil {
			return err
		}
	} else {
		err = session.Shell()
		if err != nil {
			return err
		}
	}

	go copyAndClose(nil, inPipe, stdin)
	go io.Copy(stdout, outPipe)
	go io.Copy(stderr, errPipe)

	if stdoutIsTerminal {
		resized := make(chan os.Signal, 16)

		if runtime.GOOS == "windows" {
			ticker := time.NewTicker(250 * time.Millisecond)
			defer ticker.Stop()

			go func() {
				for _ = range ticker.C {
					resized <- syscall.Signal(-1)
				}
				close(resized)
			}()
		} else {
			signal.Notify(resized, sigwinch.SIGWINCH())
			defer func() { signal.Stop(resized); close(resized) }()
		}

		go c.resize(resized, session, stdoutFd)
	}

	keepaliveStopCh := make(chan struct{})
	defer close(keepaliveStopCh)

	go keepalive(secureClient.Conn(), time.NewTicker(c.keepAliveInterval), keepaliveStopCh)

	return session.Wait()
}

func (c *secureShell) Wait() error {
	return c.secureClient.Wait()
}

func (c *secureShell) validateTarget(opts *options.SSHOptions) error {
	if strings.ToUpper(c.app.State) != "STARTED" {
		return fmt.Errorf("Application %q is not in the STARTED state", opts.AppName)
	}

	if !c.app.Diego {
		return fmt.Errorf("Application %q is not running on Diego", opts.AppName)
	}

	return nil
}

func md5Fingerprint(key ssh.PublicKey) string {
	sum := md5.Sum(key.Marshal())
	return strings.Replace(fmt.Sprintf("% x", sum), " ", ":", -1)
}

func sha1Fingerprint(key ssh.PublicKey) string {
	sum := sha1.Sum(key.Marshal())
	return strings.Replace(fmt.Sprintf("% x", sum), " ", ":", -1)
}

type hostKeyCallback func(hostname string, remote net.Addr, key ssh.PublicKey) error

func fingerprintCallback(opts *options.SSHOptions, expectedFingerprint string) hostKeyCallback {
	if opts.SkipHostValidation {
		return nil
	}

	return func(hostname string, remote net.Addr, key ssh.PublicKey) error {
		switch len(expectedFingerprint) {
		case sha1FingerprintLength:
			fingerprint := sha1Fingerprint(key)
			if fingerprint != expectedFingerprint {
				return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint)
			}
		case md5FingerprintLength:
			fingerprint := md5Fingerprint(key)
			if fingerprint != expectedFingerprint {
				return fmt.Errorf("Host key verification failed.\n\nThe fingerprint of the received key was %q.", fingerprint)
			}
		case 0:
			fingerprint := md5Fingerprint(key)
			return fmt.Errorf("Unable to verify identity of host.\n\nThe fingerprint of the received key was %q.", fingerprint)
		default:
			return errors.New("Unsupported host key fingerprint format")
		}
		return nil
	}
}

func (c *secureShell) shouldAllocateTerminal(opts *options.SSHOptions, stdinIsTerminal bool) bool {
	switch opts.TerminalRequest {
	case options.REQUEST_TTY_FORCE:
		return true
	case options.REQUEST_TTY_NO:
		return false
	case options.REQUEST_TTY_YES:
		return stdinIsTerminal
	case options.REQUEST_TTY_AUTO:
		return len(opts.Command) == 0 && stdinIsTerminal
	default:
		return false
	}
}

func (c *secureShell) resize(resized <-chan os.Signal, session SecureSession, terminalFd uintptr) {
	type resizeMessage struct {
		Width       uint32
		Height      uint32
		PixelWidth  uint32
		PixelHeight uint32
	}

	var previousWidth, previousHeight int

	for _ = range resized {
		width, height := c.getWindowDimensions(terminalFd)

		if width == previousWidth && height == previousHeight {
			continue
		}

		message := resizeMessage{
			Width:  uint32(width),
			Height: uint32(height),
		}

		session.SendRequest("window-change", false, ssh.Marshal(message))

		previousWidth = width
		previousHeight = height
	}
}

func keepalive(conn ssh.Conn, ticker *time.Ticker, stopCh chan struct{}) {
	for {
		select {
		case <-ticker.C:
			conn.SendRequest("keepalive@cloudfoundry.org", true, nil)
		case <-stopCh:
			ticker.Stop()
			return
		}
	}
}

func (c *secureShell) terminalType() string {
	term := os.Getenv("TERM")
	if term == "" {
		term = "xterm"
	}
	return term
}

func (c *secureShell) getWindowDimensions(terminalFd uintptr) (width int, height int) {
	winSize, err := c.terminalHelper.GetWinsize(terminalFd)
	if err != nil {
		winSize = &term.Winsize{
			Width:  80,
			Height: 43,
		}
	}

	return int(winSize.Width), int(winSize.Height)
}

type secureDialer struct{}

func (d *secureDialer) Dial(network string, address string, config *ssh.ClientConfig) (SecureClient, error) {
	client, err := ssh.Dial(network, address, config)
	if err != nil {
		return nil, err
	}

	return &secureClient{client: client}, nil
}

func DefaultSecureDialer() SecureDialer {
	return &secureDialer{}
}

type secureClient struct{ client *ssh.Client }

func (sc *secureClient) Close() error   { return sc.client.Close() }
func (sc *secureClient) Conn() ssh.Conn { return sc.client.Conn }
func (sc *secureClient) Wait() error    { return sc.client.Wait() }
func (sc *secureClient) Dial(n, addr string) (net.Conn, error) {
	return sc.client.Dial(n, addr)
}
func (sc *secureClient) NewSession() (SecureSession, error) {
	return sc.client.NewSession()
}

type listenerFactory struct{}

func (lf *listenerFactory) Listen(network, address string) (net.Listener, error) {
	return net.Listen(network, address)
}

func DefaultListenerFactory() ListenerFactory {
	return &listenerFactory{}
}
<|endoftext|>


<commit_before>package corehttp

// TODO: move to IPNS
const WebUIPath = "/ipfs/QmenEBWcAk3tN94fSKpKFtUMwty1qNwSYw3DMDFV6cPBXA"

// this is a list of all past webUI paths.
var WebUIPaths = []string{
	WebUIPath,
	"/ipfs/QmUnXcWZC5Ve21gUseouJsH5mLAyz5JPp8aHsg8qVUUK8e",
	"/ipfs/QmSDgpiHco5yXdyVTfhKxr3aiJ82ynz8V14QcGKicM3rVh",
	"/ipfs/QmRuvWJz1Fc8B9cTsAYANHTXqGmKR9DVfY5nvMD1uA2WQ8",
	"/ipfs/QmQLXHs7K98JNQdWrBB2cQLJahPhmupbDjRuH1b9ibmwVa",
	"/ipfs/QmXX7YRpU7nNBKfw75VG7Y1c3GwpSAGHRev67XVPgZFv9R",
	"/ipfs/QmXdu7HWdV6CUaUabd9q2ZeA4iHZLVyDRj3Gi4dsJsWjbr",
	"/ipfs/QmaaqrHyAQm7gALkRW8DcfGX3u8q9rWKnxEMmf7m9z515w",
	"/ipfs/QmSHDxWsMPuJQKWmVA1rB5a3NX2Eme5fPqNb63qwaqiqSp",
	"/ipfs/QmctngrQAt9fjpQUZr7Bx3BsXUcif52eZGTizWhvcShsjz",
	"/ipfs/QmS2HL9v5YeKgQkkWMvs1EMnFtUowTEdFfSSeMT4pos1e6",
	"/ipfs/QmR9MzChjp1MdFWik7NjEjqKQMzVmBkdK3dz14A6B5Cupm",
	"/ipfs/QmRyWyKWmphamkMRnJVjUTzSFSAAZowYP4rnbgnfMXC9Mr",
	"/ipfs/QmU3o9bvfenhTKhxUakbYrLDnZU7HezAVxPM6Ehjw9Xjqy",
	"/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ",
}

var WebUIOption = RedirectOption("webui", WebUIPath)
<commit_msg>chore: update to Web UI v2.3.3<commit_after>package corehttp

// TODO: move to IPNS
const WebUIPath = "/ipfs/QmXc9raDM1M5G5fpBnVyQ71vR4gbnskwnB9iMEzBuLgvoZ"

// this is a list of all past webUI paths.
var WebUIPaths = []string{
	WebUIPath,
	"/ipfs/QmenEBWcAk3tN94fSKpKFtUMwty1qNwSYw3DMDFV6cPBXA",
	"/ipfs/QmUnXcWZC5Ve21gUseouJsH5mLAyz5JPp8aHsg8qVUUK8e",
	"/ipfs/QmSDgpiHco5yXdyVTfhKxr3aiJ82ynz8V14QcGKicM3rVh",
	"/ipfs/QmRuvWJz1Fc8B9cTsAYANHTXqGmKR9DVfY5nvMD1uA2WQ8",
	"/ipfs/QmQLXHs7K98JNQdWrBB2cQLJahPhmupbDjRuH1b9ibmwVa",
	"/ipfs/QmXX7YRpU7nNBKfw75VG7Y1c3GwpSAGHRev67XVPgZFv9R",
	"/ipfs/QmXdu7HWdV6CUaUabd9q2ZeA4iHZLVyDRj3Gi4dsJsWjbr",
	"/ipfs/QmaaqrHyAQm7gALkRW8DcfGX3u8q9rWKnxEMmf7m9z515w",
	"/ipfs/QmSHDxWsMPuJQKWmVA1rB5a3NX2Eme5fPqNb63qwaqiqSp",
	"/ipfs/QmctngrQAt9fjpQUZr7Bx3BsXUcif52eZGTizWhvcShsjz",
	"/ipfs/QmS2HL9v5YeKgQkkWMvs1EMnFtUowTEdFfSSeMT4pos1e6",
	"/ipfs/QmR9MzChjp1MdFWik7NjEjqKQMzVmBkdK3dz14A6B5Cupm",
	"/ipfs/QmRyWyKWmphamkMRnJVjUTzSFSAAZowYP4rnbgnfMXC9Mr",
	"/ipfs/QmU3o9bvfenhTKhxUakbYrLDnZU7HezAVxPM6Ehjw9Xjqy",
	"/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ",
}

var WebUIOption = RedirectOption("webui", WebUIPath)
<|endoftext|>