<commit_before>package driver

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"gopkg.in/mgo.v2/bson"

	"github.com/Gujarats/nearest/util"
	"github.com/Gujarats/receiver"

	"github.com/Gujarats/nearest/model/city/interface"
	driverInterface "github.com/Gujarats/nearest/model/driver/interface"

	driverModel "github.com/Gujarats/nearest/model/driver"

	"github.com/Gujarats/nearest/model/global"
)

// create logger to print error in the console
var logger *log.Logger

func init() {
	logger = log.New(os.Stderr,
		"Controller Driver :: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

// find specific driver with their ID or name.
// if the desired data didn't exist then insert new data
func UpdateDriver(m *sync.Mutex, driver driverInterface.DriverInterfacce, cityInterface cityInterface.CityInterfacce) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		//start time for lenght of the process
		startTimer := time.Now()

		w.Header().Set("Access-Control-Allow-Methods", "POST")

		id := r.FormValue("id")
		name := r.FormValue("name")
		lat := r.FormValue("latitude")
		lon := r.FormValue("longitude")
		status := r.FormValue("status")
		city := r.FormValue("city")

		isAllExist := util.CheckValue(id, name, lat, lon, status, city)
		if !isAllExist {
			logger.Println("Required Params Empty")

			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Required Params Empty")
			return
		}

		// check id for validation format
		ok := bson.IsObjectIdHex(id)
		if !ok {
			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Invalid Id format")
			return
		}

		// convert string to bool
		statusBool, err := strconv.ParseBool(status)
		if err != nil {
			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Parse Boolean Error")
			return
		}

		// convert string to float64
		convertedFloat, err := util.ConvertToFloat64(lat, lon)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Failed to conver float value")
			return
		}

		latFloat := convertedFloat[0]
		lonFloat := convertedFloat[1]

		// check driver last location which district they were before
		lastDistrict := driver.GetLastDistrict(id)

		// checks drivers location which district they are now
		// NOTE : getting the nearest district must not null or fail. so we need to repeat the function if we got null.
		// but there is one approach solutions we give more distance value so that we can find the district event it is far.
		district, err := cityInterface.GetNearestDistrict(city, latFloat, lonFloat, 500)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to get nearest district")
			return

		}

		// checking if we got the current district data
		if district.Name == "" {
			w.WriteHeader(http.StatusOK)
			global.SetResponse(w, "Success", "No nearest district found!")
			return

		}

		// check if the location is the same or not, if not then remove the data from the lastdistrict.
		// format current district to meet the last district format
		if lastDistrict != "" {
			// lastDistrict is not empty from redis check if it is the same as current location
			if district.Name+"_district_"+district.Id.Hex() != lastDistrict {
				// remove the driver data in the last district
				// lastDistrict must formatted like collectionKey for district collections
				driver.Remove(id, lastDistrict)
			}

		}

		driverData := driverModel.DriverData{Id: bson.ObjectIdHex(id), Name: name, Status: statusBool, Location: driverModel.GeoJson{Type: "Point", Coordinates: []float64{lonFloat, latFloat}}}
		err = driver.Update(district.Name, district.Id.Hex(), driverData)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to update the driver")
			return
		}

		//return succes response
		elpasedTime := time.Since(startTimer).Seconds()
		w.WriteHeader(http.StatusOK)
		global.SetResponseTime(w, "Succes", "Driver Updated", elpasedTime)
		return
	})

}

// TODO :  to receive all values from request
// getting the value using the tag value
type DriverRequest struct {
	Lat      float64 `request:"latitude,required"`
	Lon      float64 `request:"longitude,required"`
	City     string  `request:"city,required"`
	Distance int64   `request:"distance,required"`
}

func FindDriver(driver driverInterface.DriverInterfacce, cityInterface cityInterface.CityInterfacce) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		//start time for lenght of the process
		startTimer := time.Now()
		var dr DriverRequest
		err := receiver.SetData(&dr, r)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", err.Error())
			return
		}

		// determined which quadran the input locations

		// NOTE : send the driver which locations and its distance from input location
		// 1.  getting all centers location map[int][4]locations
		// 2. determined which quadran is it for inputLocation
		// 3. get nearest marked locations from that quadran
		// 4. get driver from the collections of nearest locations(this is must be array)
		// 5. send the driver
		// 6. if the driver is not found then go to next nearest locations. currentIndex +1. until the last index.
		// 7. if not found find the driver on the next quadran (currentquadran +1) until reach 4
		// 8. if not found find the driver on the next level. repeat this until you found the driver

		// get all district from redis and calculate it
		// calculate nearest location district with given location and city from mongodb
		district, err := cityInterface.GetNearestDistrict(dr.City, dr.Lat, dr.Lon, dr.Distance)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to get nearest district")
			return
		}

		// checking district result from mongodb
		if district.Name == "" {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "No district found")
			return
		}

		//response variable for getting the drivers
		var driverResponse driverModel.DriverData

		// checks drivers int the district from the redis
		drivers := driver.DriversRedis(district.Name, district.Id.Hex())
		if len(drivers) > 0 {
			// get the first index drvier from redis and save it again to redis
			driverResponse = drivers[0]

			driver.SaveLastDistrict(drivers[0].Id.Hex(), district.Name, district.Id.Hex())

			// update the driver's status to unavailable in mongodb
			// Latitude is 1 in the index and Longitude is 0. Rules from mongodb
			drivers[0].Status = false
			err := driver.Update(district.Name, district.Id.Hex(), drivers[0])
			if err != nil {

			}

			// update redis data by removing the first index
			drivers = drivers[1:]
			// save the drivers to redis replacing previous data
			driver.SaveDriversRedis(drivers, district.Name, district.Id.Hex())

		} else {
			// get drivers from mongodb
			drivers = driver.GetAvailableDriver(district.Name, district.Id.Hex())
			if len(drivers) > 0 {
				driverResponse = drivers[0]

				drivers[0].Status = false
				err := driver.Update(district.Name, district.Id.Hex(), drivers[0])
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					global.SetResponse(w, "Failed", "Failed to update ther driver")
					return
				}

				// update redis data by removing the first index
				drivers = drivers[1:]
				// save the drivers to redis replacing previous data
				driver.SaveDriversRedis(drivers, district.Name, district.Id.Hex())
			} else {
				// we could not find any data in redis and mongo
				w.WriteHeader(http.StatusOK)
				global.SetResponse(w, "Success", "We couldn't find any driver")
				return
			}
		}

		//return succes response
		w.WriteHeader(http.StatusOK)
		elapsedTime := time.Since(startTimer).Seconds()
		response := global.Response{Status: "Success", Message: "Data found", Latency: elapsedTime, Data: driverResponse}
		json.NewEncoder(w).Encode(response)
		return

	})
}

func InsertDriver(driver driverInterface.DriverInterfacce) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		//start time for lenght of the process
		startTimer := time.Now()
		w.Header().Set("Access-Control-Allow-Methods", "POST")

		// getting the parameters
		name := r.FormValue("name")
		lat := r.FormValue("latitude")
		lon := r.FormValue("longitude")
		status := r.FormValue("status")

		isAllExist := util.CheckValue(name, lat, lon, status)
		if !isAllExist {
			logger.Println("Required Params Empty")

			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Required Params Empty")
			return
		}

		// convert string to bool
		statusBool, err := strconv.ParseBool(status)
		if err != nil {
			//return Bad response
			logger.Println("Failed to Parse Boolean")
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Parse Boolean Erro")
			return
		}

		// convert string to float64
		convertedFloat, err := util.ConvertToFloat64(lat, lon)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Failed to convert float value")
			return
		}
		latFloat := convertedFloat[0]
		lonFloat := convertedFloat[1]

		// insert driver t
		// TODO :  error handler here what happens if process of insertion data fails
		driver.Insert(name, name, latFloat, lonFloat, statusBool)

		//return succes response
		w.WriteHeader(http.StatusOK)
		elapsedTime := time.Since(startTimer).Seconds()
		response := global.Response{Status: "Success", Message: "Data Inserted", Latency: elapsedTime}
		json.NewEncoder(w).Encode(response)
		return
	})
}
<commit_msg>refactor update request driver using receiver<commit_after>package driver

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"gopkg.in/mgo.v2/bson"

	"github.com/Gujarats/nearest/util"
	"github.com/Gujarats/receiver"

	"github.com/Gujarats/nearest/model/city/interface"
	driverInterface "github.com/Gujarats/nearest/model/driver/interface"

	driverModel "github.com/Gujarats/nearest/model/driver"

	"github.com/Gujarats/nearest/model/global"
)

// create logger to print error in the console
var logger *log.Logger

func init() {
	logger = log.New(os.Stderr,
		"Controller Driver :: ",
		log.Ldate|log.Ltime|log.Lshortfile)
}

// type for the update driver request
type DriverUpdateRequest struct {
	Id     string  `request:"id,required"`
	Name   string  `request:"name,required"`
	Lat    float64 `request:"latitude,required"`
	Lon    float64 `request:"longitude,required"`
	Status bool    `request:"status,required"`
	City   string  `request:"city,required"`
}

// find specific driver with their ID or name.
// if the desired data didn't exist then insert new data
func UpdateDriver(m *sync.Mutex, driver driverInterface.DriverInterfacce, cityInterface cityInterface.CityInterfacce) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		//start time for lenght of the process
		startTimer := time.Now()

		w.Header().Set("Access-Control-Allow-Methods", "POST")

		// receive request value to type
		var dr DriverUpdateRequest
		err := receiver.SetData(&dr, r)
		if err != nil {
			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", err.Error())
			return
		}

		// check id for validation format
		ok := bson.IsObjectIdHex(dr.Id)
		if !ok {
			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Invalid Id format")
			return
		}

		// check driver last location which district they were before
		lastDistrict := driver.GetLastDistrict(dr.Id)

		// checks drivers location which district they are now
		// NOTE : getting the nearest district must not null or fail. so we need to repeat the function if we got null.
		// but there is one approach solutions we give more distance value so that we can find the district event it is far.
		district, err := cityInterface.GetNearestDistrict(dr.City, dr.Lat, dr.Lon, 500)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to get nearest district")
			return

		}

		// checking if we got the current district data
		if district.Name == "" {
			w.WriteHeader(http.StatusOK)
			global.SetResponse(w, "Success", "No nearest district found!")
			return

		}

		// check if the location is the same or not, if not then remove the data from the lastdistrict.
		// format current district to meet the last district format
		if lastDistrict != "" {
			// lastDistrict is not empty from redis check if it is the same as current location
			if district.Name+"_district_"+district.Id.Hex() != lastDistrict {
				// remove the driver data in the last district
				// lastDistrict must formatted like collectionKey for district collections
				driver.Remove(dr.Id, lastDistrict)
			}

		}

		// Update driver for given dr / driver data
		driverData := driverModel.DriverData{
			Id:   bson.ObjectIdHex(dr.Id),
			Name: dr.Name, Status: dr.Status,
			Location: driverModel.GeoJson{
				Type: "Point", Coordinates: []float64{dr.Lon, dr.Lat},
			},
		}
		err = driver.Update(district.Name, district.Id.Hex(), driverData)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to update the driver")
			return
		}

		//return succes response
		elpasedTime := time.Since(startTimer).Seconds()
		w.WriteHeader(http.StatusOK)
		global.SetResponseTime(w, "Succes", "Driver Updated", elpasedTime)
		return
	})

}

// getting the value using the tag value
type DriverRequest struct {
	Lat      float64 `request:"latitude,required"`
	Lon      float64 `request:"longitude,required"`
	City     string  `request:"city,required"`
	Distance int64   `request:"distance,required"`
}

func FindDriver(driver driverInterface.DriverInterfacce, cityInterface cityInterface.CityInterfacce) http.Handler {

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		//start time for lenght of the process
		startTimer := time.Now()
		var dr DriverRequest
		err := receiver.SetData(&dr, r)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", err.Error())
			return
		}

		// determined which quadran the input locations

		// NOTE : send the driver which locations and its distance from input location
		// 1.  getting all centers location map[int][4]locations
		// 2. determined which quadran is it for inputLocation
		// 3. get nearest marked locations from that quadran
		// 4. get driver from the collections of nearest locations(this is must be array)
		// 5. send the driver
		// 6. if the driver is not found then go to next nearest locations. currentIndex +1. until the last index.
		// 7. if not found find the driver on the next quadran (currentquadran +1) until reach 4
		// 8. if not found find the driver on the next level. repeat this until you found the driver

		// get all district from redis and calculate it
		// calculate nearest location district with given location and city from mongodb
		district, err := cityInterface.GetNearestDistrict(dr.City, dr.Lat, dr.Lon, dr.Distance)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "Failed to get nearest district")
			return
		}

		// checking district result from mongodb
		if district.Name == "" {
			w.WriteHeader(http.StatusInternalServerError)
			global.SetResponse(w, "Failed", "No district found")
			return
		}

		//response variable for getting the drivers
		var driverResponse driverModel.DriverData

		// checks drivers int the district from the redis
		drivers := driver.DriversRedis(district.Name, district.Id.Hex())
		if len(drivers) > 0 {
			// get the first index drvier from redis and save it again to redis
			driverResponse = drivers[0]

			driver.SaveLastDistrict(drivers[0].Id.Hex(), district.Name, district.Id.Hex())

			// update the driver's status to unavailable in mongodb
			// Latitude is 1 in the index and Longitude is 0. Rules from mongodb
			drivers[0].Status = false
			err := driver.Update(district.Name, district.Id.Hex(), drivers[0])
			if err != nil {

			}

			// update redis data by removing the first index
			drivers = drivers[1:]
			// save the drivers to redis replacing previous data
			driver.SaveDriversRedis(drivers, district.Name, district.Id.Hex())

		} else {
			// get drivers from mongodb
			drivers = driver.GetAvailableDriver(district.Name, district.Id.Hex())
			if len(drivers) > 0 {
				driverResponse = drivers[0]

				drivers[0].Status = false
				err := driver.Update(district.Name, district.Id.Hex(), drivers[0])
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					global.SetResponse(w, "Failed", "Failed to update ther driver")
					return
				}

				// update redis data by removing the first index
				drivers = drivers[1:]
				// save the drivers to redis replacing previous data
				driver.SaveDriversRedis(drivers, district.Name, district.Id.Hex())
			} else {
				// we could not find any data in redis and mongo
				w.WriteHeader(http.StatusOK)
				global.SetResponse(w, "Success", "We couldn't find any driver")
				return
			}
		}

		//return succes response
		w.WriteHeader(http.StatusOK)
		elapsedTime := time.Since(startTimer).Seconds()
		response := global.Response{Status: "Success", Message: "Data found", Latency: elapsedTime, Data: driverResponse}
		json.NewEncoder(w).Encode(response)
		return

	})
}

func InsertDriver(driver driverInterface.DriverInterfacce) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		//start time for lenght of the process
		startTimer := time.Now()
		w.Header().Set("Access-Control-Allow-Methods", "POST")

		// getting the parameters
		name := r.FormValue("name")
		lat := r.FormValue("latitude")
		lon := r.FormValue("longitude")
		status := r.FormValue("status")

		isAllExist := util.CheckValue(name, lat, lon, status)
		if !isAllExist {
			logger.Println("Required Params Empty")

			//return Bad response
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Required Params Empty")
			return
		}

		// convert string to bool
		statusBool, err := strconv.ParseBool(status)
		if err != nil {
			//return Bad response
			logger.Println("Failed to Parse Boolean")
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Parse Boolean Erro")
			return
		}

		// convert string to float64
		convertedFloat, err := util.ConvertToFloat64(lat, lon)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			global.SetResponse(w, "Failed", "Failed to convert float value")
			return
		}
		latFloat := convertedFloat[0]
		lonFloat := convertedFloat[1]

		// insert driver t
		// TODO :  error handler here what happens if process of insertion data fails
		driver.Insert(name, name, latFloat, lonFloat, statusBool)

		//return succes response
		w.WriteHeader(http.StatusOK)
		elapsedTime := time.Since(startTimer).Seconds()
		response := global.Response{Status: "Success", Message: "Data Inserted", Latency: elapsedTime}
		json.NewEncoder(w).Encode(response)
		return
	})
}
<|endoftext|>


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

// +build !cmd_go_bootstrap

// This code is compiled into the real 'go' binary, but it is not
// compiled into the binary that is built during all.bash, so as
// to avoid needing to build net (and thus use cgo) during the
// bootstrap process.

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

// httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient
var impatientHTTPClient = &http.Client{
	Timeout: time.Duration(5 * time.Second),
}

type httpError struct {
	status     string
	statusCode int
	url        string
}

func (e *httpError) Error() string {
	return fmt.Sprintf("%s: %s", e.url, e.status)
}

// httpGET returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) {
	resp, err := httpClient.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err := &httpError{status: resp.Status, statusCode: resp.StatusCode, url: url}

		return nil, err
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("%s: %v", url, err)
	}
	return b, nil
}

// httpsOrHTTP returns the body of either the importPath's
// https resource or, if unavailable, the http resource.
func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body io.ReadCloser, err error) {
	fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
		u, err := url.Parse(scheme + "://" + importPath)
		if err != nil {
			return "", nil, err
		}
		u.RawQuery = "go-get=1"
		urlStr = u.String()
		if buildV {
			log.Printf("Fetching %s", urlStr)
		}
		if security == insecure && scheme == "https" { // fail earlier
			res, err = impatientHTTPClient.Get(urlStr)
		} else {
			res, err = httpClient.Get(urlStr)
		}
		return
	}
	closeBody := func(res *http.Response) {
		if res != nil {
			res.Body.Close()
		}
	}
	urlStr, res, err := fetch("https")
	if err != nil {
		if buildV {
			log.Printf("https fetch failed: %v", err)
		}
		if security == insecure {
			closeBody(res)
			urlStr, res, err = fetch("http")
		}
	}
	if err != nil {
		closeBody(res)
		return "", nil, err
	}
	// Note: accepting a non-200 OK here, so people can serve a
	// meta import in their http 404 page.
	if buildV {
		log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
	}
	return urlStr, res.Body, nil
}
<commit_msg>cmd/go: for go get -insecure, skip TLS certificate checking<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !cmd_go_bootstrap

// This code is compiled into the real 'go' binary, but it is not
// compiled into the binary that is built during all.bash, so as
// to avoid needing to build net (and thus use cgo) during the
// bootstrap process.

package main

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"time"
)

// httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient

// impatientInsecureHTTPClient is used in -insecure mode,
// when we're connecting to https servers that might not be there
// or might be using self-signed certificates.
var impatientInsecureHTTPClient = &http.Client{
	Timeout: time.Duration(5 * time.Second),
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	},
}

type httpError struct {
	status     string
	statusCode int
	url        string
}

func (e *httpError) Error() string {
	return fmt.Sprintf("%s: %s", e.url, e.status)
}

// httpGET returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) {
	resp, err := httpClient.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		err := &httpError{status: resp.Status, statusCode: resp.StatusCode, url: url}

		return nil, err
	}
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("%s: %v", url, err)
	}
	return b, nil
}

// httpsOrHTTP returns the body of either the importPath's
// https resource or, if unavailable, the http resource.
func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body io.ReadCloser, err error) {
	fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
		u, err := url.Parse(scheme + "://" + importPath)
		if err != nil {
			return "", nil, err
		}
		u.RawQuery = "go-get=1"
		urlStr = u.String()
		if buildV {
			log.Printf("Fetching %s", urlStr)
		}
		if security == insecure && scheme == "https" { // fail earlier
			res, err = impatientInsecureHTTPClient.Get(urlStr)
		} else {
			res, err = httpClient.Get(urlStr)
		}
		return
	}
	closeBody := func(res *http.Response) {
		if res != nil {
			res.Body.Close()
		}
	}
	urlStr, res, err := fetch("https")
	if err != nil {
		if buildV {
			log.Printf("https fetch failed: %v", err)
		}
		if security == insecure {
			closeBody(res)
			urlStr, res, err = fetch("http")
		}
	}
	if err != nil {
		closeBody(res)
		return "", nil, err
	}
	// Note: accepting a non-200 OK here, so people can serve a
	// meta import in their http 404 page.
	if buildV {
		log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
	}
	return urlStr, res.Body, nil
}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"io/ioutil"
	"testing"
)

func TestGetter(t *testing.T) {
	fgp := NewBufferFileGetPutter()
	files := map[string]map[string][]byte{
		"file1.txt": {"foo": []byte{60, 60, 48, 48, 0, 0, 0, 0}},
		"file2.txt": {"bar": []byte{45, 196, 22, 240, 0, 0, 0, 0}},
	}
	for n, b := range files {
		for body, sum := range b {
			_, csum, err := fgp.Put(n, bytes.NewBufferString(body))
			if err != nil {
				t.Error(err)
			}
			if !bytes.Equal(csum, sum) {
				t.Errorf("checksum: expected 0x%x; got 0x%x", sum, csum)
			}
		}
	}
	for n, b := range files {
		for body := range b {
			r, err := fgp.Get(n)
			if err != nil {
				t.Error(err)
			}
			buf, err := ioutil.ReadAll(r)
			if err != nil {
				t.Error(err)
			}
			if body != string(buf) {
				t.Errorf("expected %q, got %q", body, string(buf))
			}
		}
	}
}
func TestPutter(t *testing.T) {
	fp := NewDiscardFilePutter()
	// map[filename]map[body]crc64sum
	files := map[string]map[string][]byte{
		"file1.txt": {"foo": []byte{60, 60, 48, 48, 0, 0, 0, 0}},
		"file2.txt": {"bar": []byte{45, 196, 22, 240, 0, 0, 0, 0}},
		"file3.txt": {"baz": []byte{32, 68, 22, 240, 0, 0, 0, 0}},
		"file4.txt": {"bif": []byte{48, 9, 150, 240, 0, 0, 0, 0}},
	}
	for n, b := range files {
		for body, sum := range b {
			_, csum, err := fp.Put(n, bytes.NewBufferString(body))
			if err != nil {
				t.Error(err)
			}
			if !bytes.Equal(csum, sum) {
				t.Errorf("checksum on %q: expected %v; got %v", n, sum, csum)
			}
		}
	}
}
<commit_msg>tar/storage: Benchmark for bufferFileGetPutter.Put<commit_after>package storage

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strings"
	"testing"
)

func TestGetter(t *testing.T) {
	fgp := NewBufferFileGetPutter()
	files := map[string]map[string][]byte{
		"file1.txt": {"foo": []byte{60, 60, 48, 48, 0, 0, 0, 0}},
		"file2.txt": {"bar": []byte{45, 196, 22, 240, 0, 0, 0, 0}},
	}
	for n, b := range files {
		for body, sum := range b {
			_, csum, err := fgp.Put(n, bytes.NewBufferString(body))
			if err != nil {
				t.Error(err)
			}
			if !bytes.Equal(csum, sum) {
				t.Errorf("checksum: expected 0x%x; got 0x%x", sum, csum)
			}
		}
	}
	for n, b := range files {
		for body := range b {
			r, err := fgp.Get(n)
			if err != nil {
				t.Error(err)
			}
			buf, err := ioutil.ReadAll(r)
			if err != nil {
				t.Error(err)
			}
			if body != string(buf) {
				t.Errorf("expected %q, got %q", body, string(buf))
			}
		}
	}
}

func TestPutter(t *testing.T) {
	fp := NewDiscardFilePutter()
	// map[filename]map[body]crc64sum
	files := map[string]map[string][]byte{
		"file1.txt": {"foo": []byte{60, 60, 48, 48, 0, 0, 0, 0}},
		"file2.txt": {"bar": []byte{45, 196, 22, 240, 0, 0, 0, 0}},
		"file3.txt": {"baz": []byte{32, 68, 22, 240, 0, 0, 0, 0}},
		"file4.txt": {"bif": []byte{48, 9, 150, 240, 0, 0, 0, 0}},
	}
	for n, b := range files {
		for body, sum := range b {
			_, csum, err := fp.Put(n, bytes.NewBufferString(body))
			if err != nil {
				t.Error(err)
			}
			if !bytes.Equal(csum, sum) {
				t.Errorf("checksum on %q: expected %v; got %v", n, sum, csum)
			}
		}
	}
}

func BenchmarkPutter(b *testing.B) {
	files := []string{
		strings.Repeat("foo", 1000),
		strings.Repeat("bar", 1000),
		strings.Repeat("baz", 1000),
		strings.Repeat("fooz", 1000),
		strings.Repeat("vbatts", 1000),
		strings.Repeat("systemd", 1000),
	}
	for i := 0; i < b.N; i++ {
		fgp := NewBufferFileGetPutter()
		for n, body := range files {
			if _, _, err := fgp.Put(fmt.Sprintf("%d", n), bytes.NewBufferString(body)); err != nil {
				b.Fatal(err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package mastodon

import (
    "errors"
    "fmt"
    "strconv"
    "strings"
)

const (
    BATTERY_DISCHARGING = iota
    BATTERY_CHARGING
    BATTERY_FULL
)

type BatteryInfo struct {
    PercentRemaining float64
    SecondsRemaining float64
    Consumption float64
    status int
}

func (batteryInfo *BatteryInfo) IsCharging() bool {
    return batteryInfo.status == BATTERY_CHARGING
}

func (batteryInfo *BatteryInfo) IsFull() bool {
    return batteryInfo.status == BATTERY_FULL
}

func readBatteryInfo(battery int) (*BatteryInfo, error) {
    rawInfo := make(map[string]string)
    batteryInfo := new(BatteryInfo)

    path := fmt.Sprintf("/sys/class/power_supply/BAT%d/uevent", battery)
    if !FileExists(path) {
        return batteryInfo, errors.New("Battery not found")
    }
    callback := func(line string) bool {
        data := strings.Split(string(line), "=")
        rawInfo[data[0]] = data[1]
        return true
    }
    ReadLines(path, callback)

    var remaining, presentRate, voltage, fullDesign float64
    var wattAsUnit bool
    batteryInfo.status = BATTERY_DISCHARGING

    if rawInfo["POWER_SUPPLY_STATUS"] == "Charging" {
        batteryInfo.status = BATTERY_CHARGING
    } else if rawInfo["POWER_SUPPLY_STATUS"] == "Full" {
        batteryInfo.status = BATTERY_FULL
    }

    /* Convert to float shorthand */
    pf := func(keys ...string) float64 {
        for _, key := range(keys) {
            if _, ok := rawInfo[key]; ok {
                f, _ := strconv.ParseFloat(rawInfo[key], 64)
                return f
            }
        }
        return 0.
    }

    /* Read values from file */
    remaining = pf("POWER_SUPPLY_ENERGY_NOW", "POWER_SUPPLY_CHARGE_NOW")
    presentRate = pf("POWER_SUPPLY_CURRENT_NOW", "POWER_SUPPLY_POWER_NOW")
    voltage = pf("POWER_SUPPLY_VOLTAGE_NOW")
    fullDesign = pf("POWER_SUPPLY_CHARGE_FULL_DESIGN", "POWER_SUPPLY_ENERGY_FULL_DESIGN")
    _, wattAsUnit = rawInfo["POWER_SUPPLY_ENERGY_NOW"]

    if (!wattAsUnit) {
        presentRate = (voltage / 1000.0) * (presentRate / 1000.0)
        remaining = (voltage / 1000.0) * (remaining / 1000.0)
        fullDesign = (voltage / 1000.0) * (fullDesign / 1000.0)
    }

    if (fullDesign == 0) {
        return batteryInfo, errors.New("Battery full design missing")
    }

    batteryInfo.PercentRemaining = (remaining / fullDesign) * 100

    var remainingTime float64
    if presentRate > 0 {
        if batteryInfo.status == BATTERY_CHARGING {
            remainingTime = (fullDesign - remaining) / presentRate
        } else if batteryInfo.status == BATTERY_DISCHARGING {
            remainingTime = remaining / presentRate
        }
        batteryInfo.SecondsRemaining = remainingTime * 3600
    }
    batteryInfo.Consumption = presentRate / 1000000.0

    return batteryInfo, nil
}

func Battery(c *Config) *StatusInfo {
    bi, err := readBatteryInfo(c.Battery)
    data := make(map[string]string)
    if err != nil {
        return NewStatus(c.Templates["battery"], data)
    }
    data["bar"] = MakeBar(bi.PercentRemaining, c.BarSize)
    data["prefix"] = "BAT"
    if bi.IsCharging() {
        data["prefix"] = "FULL"
    } else if bi.IsCharging() {
        data["prefix"] = "CHR"
    }
    data["remaining"] = HumanDuration(int64(bi.SecondsRemaining))
    data["wattage"] = fmt.Sprintf("%.1f", bi.Consumption)
    si := NewStatus(c.Templates["battery"], data)
    if bi.PercentRemaining < 15 {
        si.Status = STATUS_BAD
    }
    return si
}
<commit_msg>Fixing mixup in battery code.<commit_after>package mastodon

import (
    "errors"
    "fmt"
    "strconv"
    "strings"
)

const (
    BATTERY_DISCHARGING = iota
    BATTERY_CHARGING
    BATTERY_FULL
)

type BatteryInfo struct {
    PercentRemaining float64
    SecondsRemaining float64
    Consumption float64
    status int
}

func (batteryInfo *BatteryInfo) IsCharging() bool {
    return batteryInfo.status == BATTERY_CHARGING
}

func (batteryInfo *BatteryInfo) IsFull() bool {
    return batteryInfo.status == BATTERY_FULL
}

func readBatteryInfo(battery int) (*BatteryInfo, error) {
    rawInfo := make(map[string]string)
    batteryInfo := new(BatteryInfo)

    path := fmt.Sprintf("/sys/class/power_supply/BAT%d/uevent", battery)
    if !FileExists(path) {
        return batteryInfo, errors.New("Battery not found")
    }
    callback := func(line string) bool {
        data := strings.Split(string(line), "=")
        rawInfo[data[0]] = data[1]
        return true
    }
    ReadLines(path, callback)

    var remaining, presentRate, voltage, fullDesign float64
    var wattAsUnit bool
    batteryInfo.status = BATTERY_DISCHARGING

    if rawInfo["POWER_SUPPLY_STATUS"] == "Charging" {
        batteryInfo.status = BATTERY_CHARGING
    } else if rawInfo["POWER_SUPPLY_STATUS"] == "Full" {
        batteryInfo.status = BATTERY_FULL
    }

    /* Convert to float shorthand */
    pf := func(keys ...string) float64 {
        for _, key := range(keys) {
            if _, ok := rawInfo[key]; ok {
                f, _ := strconv.ParseFloat(rawInfo[key], 64)
                return f
            }
        }
        return 0.
    }

    /* Read values from file */
    remaining = pf("POWER_SUPPLY_ENERGY_NOW", "POWER_SUPPLY_CHARGE_NOW")
    presentRate = pf("POWER_SUPPLY_CURRENT_NOW", "POWER_SUPPLY_POWER_NOW")
    voltage = pf("POWER_SUPPLY_VOLTAGE_NOW")
    fullDesign = pf("POWER_SUPPLY_CHARGE_FULL_DESIGN", "POWER_SUPPLY_ENERGY_FULL_DESIGN")
    _, wattAsUnit = rawInfo["POWER_SUPPLY_ENERGY_NOW"]

    if (!wattAsUnit) {
        presentRate = (voltage / 1000.0) * (presentRate / 1000.0)
        remaining = (voltage / 1000.0) * (remaining / 1000.0)
        fullDesign = (voltage / 1000.0) * (fullDesign / 1000.0)
    }

    if (fullDesign == 0) {
        return batteryInfo, errors.New("Battery full design missing")
    }

    batteryInfo.PercentRemaining = (remaining / fullDesign) * 100

    var remainingTime float64
    if presentRate > 0 {
        if batteryInfo.status == BATTERY_CHARGING {
            remainingTime = (fullDesign - remaining) / presentRate
        } else if batteryInfo.status == BATTERY_DISCHARGING {
            remainingTime = remaining / presentRate
        }
        batteryInfo.SecondsRemaining = remainingTime * 3600
    }
    batteryInfo.Consumption = presentRate / 1000000.0

    return batteryInfo, nil
}

func Battery(c *Config) *StatusInfo {
    bi, err := readBatteryInfo(c.Battery)
    data := make(map[string]string)
    if err != nil {
        return NewStatus(c.Templates["battery"], data)
    }
    data["battery"] = "present"
    data["bar"] = MakeBar(bi.PercentRemaining, c.BarSize)
    data["prefix"] = "BAT"
    if bi.IsCharging() {
        data["prefix"] = "FULL"
    } else if bi.IsCharging() {
        data["prefix"] = "CHR"
    }
    data["remaining"] = HumanDuration(int64(bi.SecondsRemaining))
    data["wattage"] = fmt.Sprintf("%.1f", bi.Consumption)
    si := NewStatus(c.Templates["battery"], data)
    if bi.PercentRemaining < 15 {
        si.Status = STATUS_BAD
    }
    return si
}
<|endoftext|>


<commit_before>// Copyright 2016 Bryan Jeal <bryan@jeal.ca>

// 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 nonce

import (
	"os"
	"sync"
	"testing"
	"time"

	"github.com/jmoiron/sqlx"
	uuid "github.com/satori/go.uuid"
)

const sqlCreateNonceTable string = `
PRAGMA foreign_keys = OFF;
-- Schema: nonce
ATTACH "nonce.sdb" AS "nonce";
BEGIN;
CREATE TABLE "nonce"."nonce"(
  "id" BINARY(16) NOT NULL,
  "user_id" BINARY(16) NOT NULL,
  "token" CHAR(88) NOT NULL,
  "action" TEXT,
  "salt" CHAR(24) NOT NULL,
  "is_used" BOOL NOT NULL DEFAULT 0,
  "is_valid" BOOL NOT NULL DEFAULT 1,
  "created_at" INTEGER NOT NULL,
  "expires_at" DATETIME NOT NULL
);
COMMIT;`

// tNonce holds the testing data
type NonceTest struct {
	Action    string
	UserID    uuid.UUID
	ExpiresIn time.Duration
}

var tNonce = NonceTest{
	Action:    "test-action",
	UserID:    uuid.NewV4(),
	ExpiresIn: time.Minute,
}

// Service that all tests will use
var nonce Service

type testService interface {
	Service
	TestTeardown()
}

// Wraper for NewService to make it work with the testService interface
func newServiceTest(db *sqlx.DB) testService {
	s := &nonceService{
		db:   db,
		quit: make(chan struct{}),
	}
	go s.removeExpired()
	return s
}
func (s *nonceService) TestTeardown() {
	tx := s.db.MustBegin()
	tx.MustExec("DELETE FROM nonce;")
	tx.Commit()
}

// Wraper for NewInMemoryService to make it work with the testService interface
func newInMemoryServiceTest() testService {
	s := &nonceInMemoryService{
		store: &inMemStore{
			RWMutex:  &sync.RWMutex{},
			nonceMap: make(map[string]Nonce),
		},
		quit: make(chan struct{}),
	}

	go s.removeExpired()
	return s
}
func (s *nonceInMemoryService) TestTeardown() {
	s.store.Lock()
	s.store.nonceMap = make(map[string]Nonce)
	s.store.Unlock()
}

// TestServices contains all the tests to run
func TestServices(t *testing.T) {
	RemoveExpiredInterval = 50 * time.Millisecond

	dbFile := "nonce.sdb"
	// create database
	db := sqlx.MustConnect("sqlite3", dbFile)
	// create user table
	db.MustExec(sqlCreateNonceTable)

	services := []testService{
		newServiceTest(db),
		newInMemoryServiceTest(),
	}

	for _, nonce := range services {
		// Run tests
		t.Run("New", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			if len(n.Token) != 88 {
				t.Fatalf("Expected Token to be 88 characters long. Instead length is: %d", len(n.Token))
			}
			if n.Action != tNonce.Action {
				t.Fatalf("Expected Action to be: %s. Instead got: %s", tNonce.Action, n.Action)
			}
			expiresAt := (time.Unix(n.CreatedAt, 0)).Add(tNonce.ExpiresIn).Truncate(time.Second)
			if n.ExpiresAt != expiresAt {
				t.Fatalf("Expected ExpiresAt to be: %s. Instead got: %s", expiresAt.String(), n.ExpiresAt.String())
			}
			if n.IsUsed != false {
				t.Fatalf("Expected IsUsed to be false. Instead got: %t", n.IsUsed)
			}
			if n.IsValid != true {
				t.Fatalf("Expected IsValid to be true. Instead got: %t", n.IsValid)
			}
			if n.UserID != tNonce.UserID {
				t.Fatalf("Expected UserID to be: %s. Instead got: %s", tNonce.UserID.String(), n.UserID.String())
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Check", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected to nonce check to be valid. Instead got the error: %v", err)
			}
			err = nonce.Check("", tNonce.Action, tNonce.UserID)
			if err != ErrNoToken {
				t.Fatalf("Expected ErrNoToken. Instead got: %v", err)
			}
			err = nonce.Check("InvalidToken", tNonce.Action, tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}
			err = nonce.Check(n.Token, "wrong action", tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, uuid.NewV4())
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckExpired", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, time.Duration(0))
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenExpired {
				t.Fatalf("Expected ErrTokenExpired. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckInvalid", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckUsed", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be marked as used. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Consume", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			n2, err := nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be consumed. Instead got the error: %v", err)
			}

			if n2.IsUsed != true {
				t.Fatalf("Expected token to be marked as used.")
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("ConsumeDuplicate", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be marked as used. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckThenConsume", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			n2, err := nonce.CheckThenConsume(n.Token, tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected to nonce check to be valid. Instead got the error: %v", err)
			}

			if n2.IsUsed != true {
				t.Fatalf("Expected token to be marked as used.")
			}

			_, err = nonce.CheckThenConsume(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Get", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}

			getN, err := nonce.Get(tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected get Nonce from DB. Instead got the error: %v", err)
			}

			if n.ID != getN.ID {
				t.Fatalf("Expected Nonce we just got to be the same as the one just added. N: %s. getN: %s", n.ID.String(), getN.ID.String())

			}

			n2, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}

			getN2, err := nonce.Get(tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected get Nonce from DB. Instead got the error: %v", err)
			}

			if n2.ID != getN2.ID {
				t.Fatalf("Expected Nonce we just got to be the same as the one just added. N2: %s. getN2: %s", n2.ID.String(), getN2.ID.String())
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("RemoveExpired", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, time.Second)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			time.Sleep(1100 * time.Millisecond)
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenNotFound {
				t.Fatalf("Expected ErrTokenNotFound. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		nonce.Shutdown()
	}

	// Drop the Table(s) we created
	// Close the DB
	db.MustExec("drop table nonce;")
	db.Close()
	err := os.Remove(dbFile)
	if err != nil {
		t.Fatalf("Expected to remove dbFile: %s. Instead got the error: %v", dbFile, err)
	}
}
<commit_msg>Fixed broken test in Go TIP. Problem is from the addition of the Monotonic Clock.<commit_after>// Copyright 2016 Bryan Jeal <bryan@jeal.ca>

// 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 nonce

import (
	"os"
	"sync"
	"testing"
	"time"

	"github.com/jmoiron/sqlx"
	uuid "github.com/satori/go.uuid"
)

const sqlCreateNonceTable string = `
PRAGMA foreign_keys = OFF;
-- Schema: nonce
ATTACH "nonce.sdb" AS "nonce";
BEGIN;
CREATE TABLE "nonce"."nonce"(
  "id" BINARY(16) NOT NULL,
  "user_id" BINARY(16) NOT NULL,
  "token" CHAR(88) NOT NULL,
  "action" TEXT,
  "salt" CHAR(24) NOT NULL,
  "is_used" BOOL NOT NULL DEFAULT 0,
  "is_valid" BOOL NOT NULL DEFAULT 1,
  "created_at" INTEGER NOT NULL,
  "expires_at" DATETIME NOT NULL
);
COMMIT;`

// tNonce holds the testing data
type NonceTest struct {
	Action    string
	UserID    uuid.UUID
	ExpiresIn time.Duration
}

var tNonce = NonceTest{
	Action:    "test-action",
	UserID:    uuid.NewV4(),
	ExpiresIn: time.Minute,
}

// Service that all tests will use
var nonce Service

type testService interface {
	Service
	TestTeardown()
}

// Wraper for NewService to make it work with the testService interface
func newServiceTest(db *sqlx.DB) testService {
	s := &nonceService{
		db:   db,
		quit: make(chan struct{}),
	}
	go s.removeExpired()
	return s
}
func (s *nonceService) TestTeardown() {
	tx := s.db.MustBegin()
	tx.MustExec("DELETE FROM nonce;")
	tx.Commit()
}

// Wraper for NewInMemoryService to make it work with the testService interface
func newInMemoryServiceTest() testService {
	s := &nonceInMemoryService{
		store: &inMemStore{
			RWMutex:  &sync.RWMutex{},
			nonceMap: make(map[string]Nonce),
		},
		quit: make(chan struct{}),
	}

	go s.removeExpired()
	return s
}
func (s *nonceInMemoryService) TestTeardown() {
	s.store.Lock()
	s.store.nonceMap = make(map[string]Nonce)
	s.store.Unlock()
}

// TestServices contains all the tests to run
func TestServices(t *testing.T) {
	RemoveExpiredInterval = 50 * time.Millisecond

	dbFile := "nonce.sdb"
	// create database
	db := sqlx.MustConnect("sqlite3", dbFile)
	// create user table
	db.MustExec(sqlCreateNonceTable)

	services := []testService{
		newServiceTest(db),
		newInMemoryServiceTest(),
	}

	for _, nonce := range services {
		// Run tests
		t.Run("New", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			if len(n.Token) != 88 {
				t.Fatalf("Expected Token to be 88 characters long. Instead length is: %d", len(n.Token))
			}
			if n.Action != tNonce.Action {
				t.Fatalf("Expected Action to be: %s. Instead got: %s", tNonce.Action, n.Action)
			}
			expiresAt := (time.Unix(n.CreatedAt, 0)).Add(tNonce.ExpiresIn).Truncate(time.Second)
			if !n.ExpiresAt.Equal(expiresAt) {
				t.Fatalf("Expected ExpiresAt to be: %s. Instead got: %s", expiresAt.String(), n.ExpiresAt.String())
			}
			if n.IsUsed != false {
				t.Fatalf("Expected IsUsed to be false. Instead got: %t", n.IsUsed)
			}
			if n.IsValid != true {
				t.Fatalf("Expected IsValid to be true. Instead got: %t", n.IsValid)
			}
			if n.UserID != tNonce.UserID {
				t.Fatalf("Expected UserID to be: %s. Instead got: %s", tNonce.UserID.String(), n.UserID.String())
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Check", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected to nonce check to be valid. Instead got the error: %v", err)
			}
			err = nonce.Check("", tNonce.Action, tNonce.UserID)
			if err != ErrNoToken {
				t.Fatalf("Expected ErrNoToken. Instead got: %v", err)
			}
			err = nonce.Check("InvalidToken", tNonce.Action, tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}
			err = nonce.Check(n.Token, "wrong action", tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, uuid.NewV4())
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckExpired", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, time.Duration(0))
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenExpired {
				t.Fatalf("Expected ErrTokenExpired. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckInvalid", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrInvalidToken {
				t.Fatalf("Expected ErrInvalidToken. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckUsed", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be marked as used. Instead got the error: %v", err)
			}
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Consume", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			n2, err := nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be consumed. Instead got the error: %v", err)
			}

			if n2.IsUsed != true {
				t.Fatalf("Expected token to be marked as used.")
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("ConsumeDuplicate", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != nil {
				t.Fatalf("Expected token to be marked as used. Instead got the error: %v", err)
			}
			_, err = nonce.Consume(n.Token)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("CheckThenConsume", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			n2, err := nonce.CheckThenConsume(n.Token, tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected to nonce check to be valid. Instead got the error: %v", err)
			}

			if n2.IsUsed != true {
				t.Fatalf("Expected token to be marked as used.")
			}

			_, err = nonce.CheckThenConsume(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenUsed {
				t.Fatalf("Expected ErrTokenUsed. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("Get", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}

			getN, err := nonce.Get(tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected get Nonce from DB. Instead got the error: %v", err)
			}

			if n.ID != getN.ID {
				t.Fatalf("Expected Nonce we just got to be the same as the one just added. N: %s. getN: %s", n.ID.String(), getN.ID.String())

			}

			n2, err := nonce.New(tNonce.Action, tNonce.UserID, tNonce.ExpiresIn)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}

			getN2, err := nonce.Get(tNonce.Action, tNonce.UserID)
			if err != nil {
				t.Fatalf("Expected get Nonce from DB. Instead got the error: %v", err)
			}

			if n2.ID != getN2.ID {
				t.Fatalf("Expected Nonce we just got to be the same as the one just added. N2: %s. getN2: %s", n2.ID.String(), getN2.ID.String())
			}

			// Clean Up
			nonce.TestTeardown()
		})

		t.Run("RemoveExpired", func(t *testing.T) {
			n, err := nonce.New(tNonce.Action, tNonce.UserID, time.Second)
			if err != nil {
				t.Fatalf("Expected to add nonce to DB. Instead got the error: %v", err)
			}
			time.Sleep(1100 * time.Millisecond)
			err = nonce.Check(n.Token, tNonce.Action, tNonce.UserID)
			if err != ErrTokenNotFound {
				t.Fatalf("Expected ErrTokenNotFound. Instead got: %v", err)
			}

			// Clean Up
			nonce.TestTeardown()
		})

		nonce.Shutdown()
	}

	// Drop the Table(s) we created
	// Close the DB
	db.MustExec("drop table nonce;")
	db.Close()
	err := os.Remove(dbFile)
	if err != nil {
		t.Fatalf("Expected to remove dbFile: %s. Instead got the error: %v", dbFile, err)
	}
}
<|endoftext|>


<commit_before>// +build windows

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package azure_dd

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

	"k8s.io/klog"

	"k8s.io/kubernetes/pkg/util/mount"
)

func scsiHostRescan(io ioHandler, exec mount.Exec) {
	cmd := "Update-HostStorageCache"
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("Update-HostStorageCache failed in scsiHostRescan, error: %v, output: %q", err, string(output))
	}
}

// search Windows disk number by LUN
func findDiskByLun(lun int, iohandler ioHandler, exec mount.Exec) (string, error) {
	cmd := `Get-Disk | select number, location | ConvertTo-Json`
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("Get-Disk failed in findDiskByLun, error: %v, output: %q", err, string(output))
		return "", err
	}

	if len(output) < 10 {
		return "", fmt.Errorf("Get-Disk output is too short, output: %q", string(output))
	}

	var data []map[string]interface{}
	if err = json.Unmarshal(output, &data); err != nil {
		klog.Errorf("Get-Disk output is not a json array, output: %q", string(output))
		return "", err
	}

	for _, v := range data {
		if jsonLocation, ok := v["location"]; ok {
			if location, ok := jsonLocation.(string); ok {
				if !strings.Contains(location, " LUN ") {
					continue
				}

				arr := strings.Split(location, " ")
				arrLen := len(arr)
				if arrLen < 3 {
					klog.Warningf("unexpected json structure from Get-Disk, location: %q", jsonLocation)
					continue
				}

				klog.V(4).Infof("found a disk, locatin: %q, lun: %q", location, arr[arrLen-1])
				//last element of location field is LUN number, e.g.
				//		"location":  "Integrated : Adapter 3 : Port 0 : Target 0 : LUN 1"
				l, err := strconv.Atoi(arr[arrLen-1])
				if err != nil {
					klog.Warningf("cannot parse element from data structure, location: %q, element: %q", location, arr[arrLen-1])
					continue
				}

				if l == lun {
					klog.V(4).Infof("found a disk and lun, locatin: %q, lun: %d", location, lun)
					if d, ok := v["number"]; ok {
						if diskNum, ok := d.(float64); ok {
							klog.V(2).Infof("azureDisk Mount: got disk number(%d) by LUN(%d)", int(diskNum), lun)
							return fmt.Sprintf(winDiskNumFormat, int(diskNum)), nil
						}
						klog.Warningf("LUN(%d) found, but could not get disk number(%q), location: %q", lun, d, location)
					}
					return "", fmt.Errorf("LUN(%d) found, but could not get disk number, location: %q", lun, location)
				}
			}
		}
	}

	return "", nil
}

func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) {
	if err := mount.ValidateDiskNumber(disk); err != nil {
		klog.Errorf("azureDisk Mount: formatIfNotFormatted failed, err: %v\n", err)
		return
	}

	if len(fstype) == 0 {
		// Use 'NTFS' as the default
		fstype = "NTFS"
	}
	cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru", disk)
	cmd += fmt.Sprintf(" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", fstype)
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("azureDisk Mount: Get-Disk failed, error: %v, output: %q", err, string(output))
	} else {
		klog.Infof("azureDisk Mount: Disk successfully formatted, disk: %q, fstype: %q\n", disk, fstype)
	}
}
<commit_msg>cleanup: log message typo fix<commit_after>// +build windows

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package azure_dd

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

	"k8s.io/klog"

	"k8s.io/kubernetes/pkg/util/mount"
)

func scsiHostRescan(io ioHandler, exec mount.Exec) {
	cmd := "Update-HostStorageCache"
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("Update-HostStorageCache failed in scsiHostRescan, error: %v, output: %q", err, string(output))
	}
}

// search Windows disk number by LUN
func findDiskByLun(lun int, iohandler ioHandler, exec mount.Exec) (string, error) {
	cmd := `Get-Disk | select number, location | ConvertTo-Json`
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("Get-Disk failed in findDiskByLun, error: %v, output: %q", err, string(output))
		return "", err
	}

	if len(output) < 10 {
		return "", fmt.Errorf("Get-Disk output is too short, output: %q", string(output))
	}

	var data []map[string]interface{}
	if err = json.Unmarshal(output, &data); err != nil {
		klog.Errorf("Get-Disk output is not a json array, output: %q", string(output))
		return "", err
	}

	for _, v := range data {
		if jsonLocation, ok := v["location"]; ok {
			if location, ok := jsonLocation.(string); ok {
				if !strings.Contains(location, " LUN ") {
					continue
				}

				arr := strings.Split(location, " ")
				arrLen := len(arr)
				if arrLen < 3 {
					klog.Warningf("unexpected json structure from Get-Disk, location: %q", jsonLocation)
					continue
				}

				klog.V(4).Infof("found a disk, location: %q, lun: %q", location, arr[arrLen-1])
				//last element of location field is LUN number, e.g.
				//		"location":  "Integrated : Adapter 3 : Port 0 : Target 0 : LUN 1"
				l, err := strconv.Atoi(arr[arrLen-1])
				if err != nil {
					klog.Warningf("cannot parse element from data structure, location: %q, element: %q", location, arr[arrLen-1])
					continue
				}

				if l == lun {
					klog.V(4).Infof("found a disk and lun, location: %q, lun: %d", location, lun)
					if d, ok := v["number"]; ok {
						if diskNum, ok := d.(float64); ok {
							klog.V(2).Infof("azureDisk Mount: got disk number(%d) by LUN(%d)", int(diskNum), lun)
							return fmt.Sprintf(winDiskNumFormat, int(diskNum)), nil
						}
						klog.Warningf("LUN(%d) found, but could not get disk number(%q), location: %q", lun, d, location)
					}
					return "", fmt.Errorf("LUN(%d) found, but could not get disk number, location: %q", lun, location)
				}
			}
		}
	}

	return "", nil
}

func formatIfNotFormatted(disk string, fstype string, exec mount.Exec) {
	if err := mount.ValidateDiskNumber(disk); err != nil {
		klog.Errorf("azureDisk Mount: formatIfNotFormatted failed, err: %v\n", err)
		return
	}

	if len(fstype) == 0 {
		// Use 'NTFS' as the default
		fstype = "NTFS"
	}
	cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru", disk)
	cmd += fmt.Sprintf(" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", fstype)
	output, err := exec.Run("powershell", "/c", cmd)
	if err != nil {
		klog.Errorf("azureDisk Mount: Get-Disk failed, error: %v, output: %q", err, string(output))
	} else {
		klog.Infof("azureDisk Mount: Disk successfully formatted, disk: %q, fstype: %q\n", disk, fstype)
	}
}
<|endoftext|>


<commit_before>package fs

import (
	"bytes"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/fs"
	"time"

	"github.com/ungerik/go-fs/fsimpl"
)

// Ensure MemFile implements interfaces
var (
	_ FileReader       = new(MemFile)
	_ io.Writer        = new(MemFile)
	_ io.WriterTo      = new(MemFile)
	_ io.ReaderAt      = new(MemFile)
	_ json.Marshaler   = new(MemFile)
	_ json.Unmarshaler = new(MemFile)
	_ gob.GobEncoder   = new(MemFile)
	_ gob.GobDecoder   = new(MemFile)
	_ fmt.Stringer     = new(MemFile)
)

// MemFile implements FileReader with a filename and an in memory byte slice.
// It exposes FileName and FileData as exported struct fields to emphasize
// its simple nature as just an wrapper of a name and some bytes.
// Note that the ReadAll method returns FileData directly
// without copying it to optimized performance.
// So be careful when modifying the FileData bytes of a MemFile.
//
// MemFile implements the following interfaces:
//   - FileReader
//   - io.Writer
//   - io.WriterTo
//   - io.ReaderAt
//   - json.Marshaler
//   - json.Unmarshaler
//   - gob.GobEncoder
//   - gob.GobDecoder
//   - fmt.Stringer
type MemFile struct {
	FileName string
	FileData []byte
}

// NewMemFile returns a new MemFile
func NewMemFile(name string, data []byte) *MemFile {
	return &MemFile{FileName: name, FileData: data}
}

// ReadMemFile returns a new MemFile with name and data from fileReader.
// If the passed fileReader is a *MemFile then
// its FileData is used directly without copying it.
func ReadMemFile(fileReader FileReader) (*MemFile, error) {
	data, err := fileReader.ReadAll() // Does not copy in case of fileReader.(*MemFile)
	if err != nil {
		return nil, fmt.Errorf("ReadMemFile: error reading from FileReader: %w", err)
	}
	return &MemFile{FileName: fileReader.Name(), FileData: data}, nil
}

// ReadMemFileRename returns a new MemFile with the data from fileReader and the passed name.
// If the passed fileReader is a *MemFile then
// its FileData is used directly without copying it.
func ReadMemFileRename(fileReader FileReader, name string) (*MemFile, error) {
	data, err := fileReader.ReadAll() // Does not copy in case of fileReader.(*MemFile)
	if err != nil {
		return nil, fmt.Errorf("ReadMemFileRename: error reading from FileReader: %w", err)
	}
	return &MemFile{FileName: name, FileData: data}, nil
}

// ReadAllMemFile returns a new MemFile with the data
// from io.ReadAll(r) and the passed name.
func ReadAllMemFile(r io.Reader, name string) (*MemFile, error) {
	data, err := io.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("ReadAllMemFile: error reading from io.Reader: %w", err)
	}
	return &MemFile{FileName: name, FileData: data}, nil
}

// String returns the name and meta information for the FileReader.
// String implements the fmt.Stringer interface.
func (f *MemFile) String() string {
	return fmt.Sprintf("MemFile{name: %q, size: %d}", f.FileName, len(f.FileData))
}

// Name returns the name of the file
func (f *MemFile) Name() string {
	return f.FileName
}

// Ext returns the extension of file name including the point, or an empty string.
func (f *MemFile) Ext() string {
	return fsimpl.Ext(f.FileName, "")
}

// LocalPath always returns an empty string for a MemFile.
func (f *MemFile) LocalPath() string {
	return ""
}

// Size returns the size of the file
func (f *MemFile) Size() int64 {
	return int64(len(f.FileData))
}

// Exists returns true if the MemFile has non empty FileName.
// It's valid to call this method on a nil pointer,
// will return false in this case.
func (f *MemFile) Exists() bool {
	return f != nil && f.FileName != ""
}

// CheckExists return an ErrDoesNotExist error
// if the file does not exist.
func (f *MemFile) CheckExists() error {
	if !f.Exists() {
		return NewErrDoesNotExistFileReader(f)
	}
	return nil
}

// ContentHash returns a Dropbox compatible content hash for the file.
// See https://www.dropbox.com/developers/reference/content-hash
func (f *MemFile) ContentHash() (string, error) {
	return fsimpl.ContentHashBytes(f.FileData), nil
}

// Write appends the passed bytes to the FileData,
// implementing the io.Writer interface.
func (f *MemFile) Write(b []byte) (int, error) {
	f.FileData = append(f.FileData, b...)
	return len(b), nil
}

// ReadAll returns the FileData without copying it.
func (f *MemFile) ReadAll() (data []byte, err error) {
	return f.FileData, nil
}

// ReadAllContentHash returns the FileData without copying it
// together with a Dropbox compatible content hash.
// See https://www.dropbox.com/developers/reference/content-hash
func (f *MemFile) ReadAllContentHash() (data []byte, hash string, err error) {
	return f.FileData, fsimpl.ContentHashBytes(f.FileData), nil
}

// ReadAllString returns the FileData as string.
func (f *MemFile) ReadAllString() (string, error) {
	return string(f.FileData), nil
}

// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying input source. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// When ReadAt returns n < len(p), it returns a non-nil error
// explaining why more bytes were not returned. In this respect,
// ReadAt is stricter than Read.
//
// If the n = len(p) bytes returned by ReadAt are at the end of the
// input source, ReadAt returns err == nil.
//
// Clients of ReadAt can execute parallel ReadAt calls on the
// same input source.
//
// ReadAt implements the interface io.ReaderAt.
func (f *MemFile) ReadAt(p []byte, off int64) (n int, err error) {
	if off >= int64(len(f.FileData)) {
		return 0, io.EOF
	}
	n = copy(p, f.FileData[off:])
	if n < len(p) {
		return n, fmt.Errorf("could only read %d of %d requested bytes", n, len(p))
	}
	return n, nil
}

// WriteTo implements the io.WriterTo interface
func (f *MemFile) WriteTo(writer io.Writer) (n int64, err error) {
	i, err := writer.Write(f.FileData)
	return int64(i), err
}

// OpenReader opens the file and returns a os/fs.File that has be closed after reading
func (f *MemFile) OpenReader() (fs.File, error) {
	return fsimpl.NewReadonlyFileBuffer(f.FileData, memFileInfo{f}), nil
}

// OpenReadSeeker opens the file and returns a ReadSeekCloser.
// Use OpenReader if seeking is not necessary because implementations
// may need additional buffering to support seeking or not support it at all.
func (f *MemFile) OpenReadSeeker() (ReadSeekCloser, error) {
	return fsimpl.NewReadonlyFileBuffer(f.FileData, memFileInfo{f}), nil
}

// ReadJSON reads and unmarshalles the JSON content of the file to output.
func (f *MemFile) ReadJSON(output interface{}) error {
	return json.Unmarshal(f.FileData, output)
}

// ReadXML reads and unmarshalles the XML content of the file to output.
func (f *MemFile) ReadXML(output interface{}) error {
	return xml.Unmarshal(f.FileData, output)
}

// MarshalJSON implements the json.Marshaler interface
func (f *MemFile) MarshalJSON() ([]byte, error) {
	encodedData := base64.RawURLEncoding.EncodeToString(f.FileData)
	return json.Marshal(map[string]string{f.FileName: encodedData})
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (f *MemFile) UnmarshalJSON(j []byte) error {
	m := make(map[string]string, 1)
	err := json.Unmarshal(j, &m)
	if err != nil {
		return fmt.Errorf("can't unmarshal JSON as MemFile: %w", err)
	}
	if len(m) != 1 {
		return fmt.Errorf("can't unmarshal JSON as MemFile: %d object keys", len(m))
	}
	for fileName, encodedData := range m {
		fileData, err := base64.RawURLEncoding.DecodeString(encodedData)
		if err != nil {
			return fmt.Errorf("can't decode base64 JSON data of MemFile: %w", err)
		}
		f.FileName = fileName
		f.FileData = fileData
	}
	return nil
}

// GobEncode gob encodes the file name and content,
// implementing encoding/gob.GobEncoder.
func (f *MemFile) GobEncode() ([]byte, error) {
	buf := bytes.NewBuffer(make([]byte, 0, 16+len(f.FileName)+len(f.FileData)))
	enc := gob.NewEncoder(buf)
	err := enc.Encode(f.FileName)
	if err != nil {
		return nil, fmt.Errorf("MemFile.GobEncode: error encoding FileName: %w", err)
	}
	err = enc.Encode(f.FileData)
	if err != nil {
		return nil, fmt.Errorf("MemFile.GobEncode: error encoding FileData: %w", err)
	}
	return buf.Bytes(), nil
}

// GobDecode decodes gobBytes file name and content,
// implementing encoding/gob.GobDecoder.
func (f *MemFile) GobDecode(gobBytes []byte) error {
	dec := gob.NewDecoder(bytes.NewReader(gobBytes))
	err := dec.Decode(&f.FileName)
	if err != nil {
		return fmt.Errorf("MemFile.GobDecode: error decoding FileName: %w", err)
	}
	err = dec.Decode(&f.FileData)
	if err != nil {
		return fmt.Errorf("MemFile.GobDecode: error decoding FileData: %w", err)
	}
	return nil
}

// Stat returns a io/fs.FileInfo describing the MemFile.
func (f *MemFile) Stat() (fs.FileInfo, error) {
	return memFileInfo{f}, nil
}

type memFileInfo struct {
	*MemFile
}

func (i memFileInfo) Mode() fs.FileMode  { return 0666 }
func (i memFileInfo) ModTime() time.Time { return time.Now() }
func (i memFileInfo) IsDir() bool        { return false }
func (i memFileInfo) Sys() interface{}   { return nil }
<commit_msg>added MemFilesAsFileReaders<commit_after>package fs

import (
	"bytes"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/fs"
	"time"

	"github.com/ungerik/go-fs/fsimpl"
)

// Ensure MemFile implements interfaces
var (
	_ FileReader       = new(MemFile)
	_ io.Writer        = new(MemFile)
	_ io.WriterTo      = new(MemFile)
	_ io.ReaderAt      = new(MemFile)
	_ json.Marshaler   = new(MemFile)
	_ json.Unmarshaler = new(MemFile)
	_ gob.GobEncoder   = new(MemFile)
	_ gob.GobDecoder   = new(MemFile)
	_ fmt.Stringer     = new(MemFile)
)

// MemFile implements FileReader with a filename and an in memory byte slice.
// It exposes FileName and FileData as exported struct fields to emphasize
// its simple nature as just an wrapper of a name and some bytes.
// Note that the ReadAll method returns FileData directly
// without copying it to optimized performance.
// So be careful when modifying the FileData bytes of a MemFile.
//
// MemFile implements the following interfaces:
//   - FileReader
//   - io.Writer
//   - io.WriterTo
//   - io.ReaderAt
//   - json.Marshaler
//   - json.Unmarshaler
//   - gob.GobEncoder
//   - gob.GobDecoder
//   - fmt.Stringer
type MemFile struct {
	FileName string
	FileData []byte
}

// NewMemFile returns a new MemFile
func NewMemFile(name string, data []byte) *MemFile {
	return &MemFile{FileName: name, FileData: data}
}

// ReadMemFile returns a new MemFile with name and data from fileReader.
// If the passed fileReader is a *MemFile then
// its FileData is used directly without copying it.
func ReadMemFile(fileReader FileReader) (*MemFile, error) {
	data, err := fileReader.ReadAll() // Does not copy in case of fileReader.(*MemFile)
	if err != nil {
		return nil, fmt.Errorf("ReadMemFile: error reading from FileReader: %w", err)
	}
	return &MemFile{FileName: fileReader.Name(), FileData: data}, nil
}

// ReadMemFileRename returns a new MemFile with the data from fileReader and the passed name.
// If the passed fileReader is a *MemFile then
// its FileData is used directly without copying it.
func ReadMemFileRename(fileReader FileReader, name string) (*MemFile, error) {
	data, err := fileReader.ReadAll() // Does not copy in case of fileReader.(*MemFile)
	if err != nil {
		return nil, fmt.Errorf("ReadMemFileRename: error reading from FileReader: %w", err)
	}
	return &MemFile{FileName: name, FileData: data}, nil
}

// ReadAllMemFile returns a new MemFile with the data
// from io.ReadAll(r) and the passed name.
func ReadAllMemFile(r io.Reader, name string) (*MemFile, error) {
	data, err := io.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("ReadAllMemFile: error reading from io.Reader: %w", err)
	}
	return &MemFile{FileName: name, FileData: data}, nil
}

// MemFilesAsFileReaders converts []*MemFile to []FileReader
func MemFilesAsFileReaders(memFiles []*MemFile) []FileReader {
	if len(memFiles) == 0 {
		return nil
	}
	fileReaders := make([]FileReader, len(memFiles))
	for i, memFile := range memFiles {
		fileReaders[i] = memFile
	}
	return fileReaders
}

// String returns the name and meta information for the FileReader.
// String implements the fmt.Stringer interface.
func (f *MemFile) String() string {
	return fmt.Sprintf("MemFile{name: %q, size: %d}", f.FileName, len(f.FileData))
}

// Name returns the name of the file
func (f *MemFile) Name() string {
	return f.FileName
}

// Ext returns the extension of file name including the point, or an empty string.
func (f *MemFile) Ext() string {
	return fsimpl.Ext(f.FileName, "")
}

// LocalPath always returns an empty string for a MemFile.
func (f *MemFile) LocalPath() string {
	return ""
}

// Size returns the size of the file
func (f *MemFile) Size() int64 {
	return int64(len(f.FileData))
}

// Exists returns true if the MemFile has non empty FileName.
// It's valid to call this method on a nil pointer,
// will return false in this case.
func (f *MemFile) Exists() bool {
	return f != nil && f.FileName != ""
}

// CheckExists return an ErrDoesNotExist error
// if the file does not exist.
func (f *MemFile) CheckExists() error {
	if !f.Exists() {
		return NewErrDoesNotExistFileReader(f)
	}
	return nil
}

// ContentHash returns a Dropbox compatible content hash for the file.
// See https://www.dropbox.com/developers/reference/content-hash
func (f *MemFile) ContentHash() (string, error) {
	return fsimpl.ContentHashBytes(f.FileData), nil
}

// Write appends the passed bytes to the FileData,
// implementing the io.Writer interface.
func (f *MemFile) Write(b []byte) (int, error) {
	f.FileData = append(f.FileData, b...)
	return len(b), nil
}

// ReadAll returns the FileData without copying it.
func (f *MemFile) ReadAll() (data []byte, err error) {
	return f.FileData, nil
}

// ReadAllContentHash returns the FileData without copying it
// together with a Dropbox compatible content hash.
// See https://www.dropbox.com/developers/reference/content-hash
func (f *MemFile) ReadAllContentHash() (data []byte, hash string, err error) {
	return f.FileData, fsimpl.ContentHashBytes(f.FileData), nil
}

// ReadAllString returns the FileData as string.
func (f *MemFile) ReadAllString() (string, error) {
	return string(f.FileData), nil
}

// ReadAt reads len(p) bytes into p starting at offset off in the
// underlying input source. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered.
//
// When ReadAt returns n < len(p), it returns a non-nil error
// explaining why more bytes were not returned. In this respect,
// ReadAt is stricter than Read.
//
// If the n = len(p) bytes returned by ReadAt are at the end of the
// input source, ReadAt returns err == nil.
//
// Clients of ReadAt can execute parallel ReadAt calls on the
// same input source.
//
// ReadAt implements the interface io.ReaderAt.
func (f *MemFile) ReadAt(p []byte, off int64) (n int, err error) {
	if off >= int64(len(f.FileData)) {
		return 0, io.EOF
	}
	n = copy(p, f.FileData[off:])
	if n < len(p) {
		return n, fmt.Errorf("could only read %d of %d requested bytes", n, len(p))
	}
	return n, nil
}

// WriteTo implements the io.WriterTo interface
func (f *MemFile) WriteTo(writer io.Writer) (n int64, err error) {
	i, err := writer.Write(f.FileData)
	return int64(i), err
}

// OpenReader opens the file and returns a os/fs.File that has be closed after reading
func (f *MemFile) OpenReader() (fs.File, error) {
	return fsimpl.NewReadonlyFileBuffer(f.FileData, memFileInfo{f}), nil
}

// OpenReadSeeker opens the file and returns a ReadSeekCloser.
// Use OpenReader if seeking is not necessary because implementations
// may need additional buffering to support seeking or not support it at all.
func (f *MemFile) OpenReadSeeker() (ReadSeekCloser, error) {
	return fsimpl.NewReadonlyFileBuffer(f.FileData, memFileInfo{f}), nil
}

// ReadJSON reads and unmarshalles the JSON content of the file to output.
func (f *MemFile) ReadJSON(output interface{}) error {
	return json.Unmarshal(f.FileData, output)
}

// ReadXML reads and unmarshalles the XML content of the file to output.
func (f *MemFile) ReadXML(output interface{}) error {
	return xml.Unmarshal(f.FileData, output)
}

// MarshalJSON implements the json.Marshaler interface
func (f *MemFile) MarshalJSON() ([]byte, error) {
	encodedData := base64.RawURLEncoding.EncodeToString(f.FileData)
	return json.Marshal(map[string]string{f.FileName: encodedData})
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (f *MemFile) UnmarshalJSON(j []byte) error {
	m := make(map[string]string, 1)
	err := json.Unmarshal(j, &m)
	if err != nil {
		return fmt.Errorf("can't unmarshal JSON as MemFile: %w", err)
	}
	if len(m) != 1 {
		return fmt.Errorf("can't unmarshal JSON as MemFile: %d object keys", len(m))
	}
	for fileName, encodedData := range m {
		fileData, err := base64.RawURLEncoding.DecodeString(encodedData)
		if err != nil {
			return fmt.Errorf("can't decode base64 JSON data of MemFile: %w", err)
		}
		f.FileName = fileName
		f.FileData = fileData
	}
	return nil
}

// GobEncode gob encodes the file name and content,
// implementing encoding/gob.GobEncoder.
func (f *MemFile) GobEncode() ([]byte, error) {
	buf := bytes.NewBuffer(make([]byte, 0, 16+len(f.FileName)+len(f.FileData)))
	enc := gob.NewEncoder(buf)
	err := enc.Encode(f.FileName)
	if err != nil {
		return nil, fmt.Errorf("MemFile.GobEncode: error encoding FileName: %w", err)
	}
	err = enc.Encode(f.FileData)
	if err != nil {
		return nil, fmt.Errorf("MemFile.GobEncode: error encoding FileData: %w", err)
	}
	return buf.Bytes(), nil
}

// GobDecode decodes gobBytes file name and content,
// implementing encoding/gob.GobDecoder.
func (f *MemFile) GobDecode(gobBytes []byte) error {
	dec := gob.NewDecoder(bytes.NewReader(gobBytes))
	err := dec.Decode(&f.FileName)
	if err != nil {
		return fmt.Errorf("MemFile.GobDecode: error decoding FileName: %w", err)
	}
	err = dec.Decode(&f.FileData)
	if err != nil {
		return fmt.Errorf("MemFile.GobDecode: error decoding FileData: %w", err)
	}
	return nil
}

// Stat returns a io/fs.FileInfo describing the MemFile.
func (f *MemFile) Stat() (fs.FileInfo, error) {
	return memFileInfo{f}, nil
}

type memFileInfo struct {
	*MemFile
}

func (i memFileInfo) Mode() fs.FileMode  { return 0666 }
func (i memFileInfo) ModTime() time.Time { return time.Now() }
func (i memFileInfo) IsDir() bool        { return false }
func (i memFileInfo) Sys() interface{}   { return nil }
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
)

var addr = flag.String("addr", ":4089", "Server address")
var timeout = flag.Duration("timeout", 5*time.Second, "Timeout for HTTP GETs")
var conditionsInterval = flag.Duration("conditions_interval", 10*time.Minute, "Interval between updating conditions")
var siteMapUrl = flag.String("site_map_url", "http://magicseaweed.com/site-map.php", "URL of the site map")
var conditionsFilePath = flag.String("conditions_file", "conditions.json", "Path to file with conditions cache")
var conditionsPeriod = flag.Duration("conditions_period", 10*time.Second, "How often to save the conditions file")
var help = flag.Bool("h", false, "Show help message")

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		os.Exit(0)
	}
	loadConditionsFile()
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/errors", handleErrors)
	go keepConditionsUpdated()
	log.Printf("Listending on %s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var head = template.HTML(`

		<title>Waveguide</title>

`)

var errsTmpl = template.Must(template.New("errs").Parse(`

{{.Head}}

		Errors:

			{{range .Errs}}

				{{.Loc.Name}}
				{{.Err}}

			{{end}}

`))

var tmpl = template.Must(template.New("main").Parse(`

{{.Head}}

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.Name}}
					{{.Stars}}
					{{.Details}}

				{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

type Conditions struct {
	Loc     *Location
	Rating  int
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

func handleErrors(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	defer mu.Unlock()
	data := struct {
		Errs []*Error
		Head template.HTML
	}{
		Errs: errs,
		Head: head,
	}
	err := errsTmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	// Sort conditions by rating and name.
	mu.Lock()
	defer mu.Unlock()
	conds2 := make([]*Conditions, 0, len(conds))
	for _, c := range conds {
		conds2 = append(conds2, c)
	}
	sort.Sort(ByRating(conds2))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Head  template.HTML
	}{Conds: conds2, Head: head}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func updateConditionsAllLocations() {
	client := &http.Client{Timeout: *timeout}

	// Gather locations.
	log.Printf("Fetching %s", *siteMapUrl)
	resp, err := client.Get(*siteMapUrl)
	if err != nil {
		log.Printf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read response body of site map. %v", err)
	}
	reportMatches := reportRx.FindAllSubmatch(body, -1)
	for _, match := range reportMatches {
		path := strings.TrimSpace(string(match[1]))
		name := strings.TrimSpace(string(match[2]))
		_, ok := locations[name]
		if !ok {
			log.Printf("Got new location: %s: %s", name, path)
		}
		loc := &Location{
			Name:             name,
			MagicSeaweedPath: path,
		}
		locations[name] = loc
	}
	log.Printf("Found %d reports", len(reportMatches))
	log.Printf("New number of locations: %d", len(locations))

	// Gather conditions and errors.
	mu.Lock()
	errs = make([]*Error, 0, len(locations))
	mu.Unlock()
	for _, loc := range locations {
		cond, err := loc.GetConditions(client)
		if err != nil {
			mu.Lock()
			e := &Error{
				Loc: loc,
				Err: err,
			}
			errs = append(errs, e)
			mu.Unlock()
			continue
		}
		mu.Lock()
		conds[loc.Name] = cond
		mu.Unlock()
		saveConditionsFile()
	}
}

func loadConditionsFile() {
	f, err := os.Open(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s. %v. That's okay.", *conditionsFilePath, err)
		return
	}
	contents, err := ioutil.ReadAll(f)
	err = json.Unmarshal(contents, &conds)
	if err != nil {
		log.Printf("Failed to unmarshall the conditions map. %v", err)
		os.Exit(1)
	}
	log.Printf("Loaded conditions file %s", *conditionsFilePath)
}

func saveConditionsFile() {
	f, err := os.Create(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s for writing. %v", *conditionsFilePath)
		return
	}
	mu.Lock()
	defer mu.Unlock()
	contents, err := json.Marshal(conds)
	if err != nil {
		log.Printf("Failed to marshal conditions. %v", conds)
		return
	}
	f.Write(contents)
	f.Close()
	log.Printf("Saved conditions file %s", *conditionsFilePath)
}

var locations = make(map[string]*Location)
var mu sync.Mutex // for conds and errs
var conds = make(map[string]*Conditions)
var errs []*Error

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)
var reportRx = regexp.MustCompile(`<option value="(/[^"]+-Surf-Report/\d+/)">([^<]+)\s*</option>`)

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	foundStars := starRx.FindAll(body, -1)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	rating := len(foundStars)
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}

func (c *Conditions) Stars() string {
	runes := make([]rune, 0, 5)
	for i := 0; i < c.Rating; i++ {
		runes = append(runes, '★')
	}
	for i := 0; i < 5-c.Rating; i++ {
		runes = append(runes, '☆')
	}
	return string(runes)
}

func keepConditionsUpdated() {
	updateConditionsAllLocations()
	tick := time.Tick(*conditionsInterval)
	for {
		<-tick
		updateConditionsAllLocations()
	}
}
<commit_msg>Fix typo<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
)

var addr = flag.String("addr", ":4089", "Server address")
var timeout = flag.Duration("timeout", 5*time.Second, "Timeout for HTTP GETs")
var conditionsInterval = flag.Duration("conditions_interval", 10*time.Minute, "Interval between updating conditions")
var siteMapUrl = flag.String("site_map_url", "http://magicseaweed.com/site-map.php", "URL of the site map")
var conditionsFilePath = flag.String("conditions_file", "conditions.json", "Path to file with conditions cache")
var conditionsPeriod = flag.Duration("conditions_period", 10*time.Second, "How often to save the conditions file")
var help = flag.Bool("h", false, "Show help message")

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		os.Exit(0)
	}
	loadConditionsFile()
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/errors", handleErrors)
	go keepConditionsUpdated()
	log.Printf("Listening on %s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var head = template.HTML(`

		<title>Waveguide</title>

`)

var errsTmpl = template.Must(template.New("errs").Parse(`

{{.Head}}

		Errors:

			{{range .Errs}}

				{{.Loc.Name}}
				{{.Err}}

			{{end}}

`))

var tmpl = template.Must(template.New("main").Parse(`

{{.Head}}

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.Name}}
					{{.Stars}}
					{{.Details}}

				{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

type Conditions struct {
	Loc     *Location
	Rating  int
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

func handleErrors(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	defer mu.Unlock()
	data := struct {
		Errs []*Error
		Head template.HTML
	}{
		Errs: errs,
		Head: head,
	}
	err := errsTmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	// Sort conditions by rating and name.
	mu.Lock()
	defer mu.Unlock()
	conds2 := make([]*Conditions, 0, len(conds))
	for _, c := range conds {
		conds2 = append(conds2, c)
	}
	sort.Sort(ByRating(conds2))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Head  template.HTML
	}{Conds: conds2, Head: head}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func updateConditionsAllLocations() {
	client := &http.Client{Timeout: *timeout}

	// Gather locations.
	log.Printf("Fetching %s", *siteMapUrl)
	resp, err := client.Get(*siteMapUrl)
	if err != nil {
		log.Printf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read response body of site map. %v", err)
	}
	reportMatches := reportRx.FindAllSubmatch(body, -1)
	for _, match := range reportMatches {
		path := strings.TrimSpace(string(match[1]))
		name := strings.TrimSpace(string(match[2]))
		_, ok := locations[name]
		if !ok {
			log.Printf("Got new location: %s: %s", name, path)
		}
		loc := &Location{
			Name:             name,
			MagicSeaweedPath: path,
		}
		locations[name] = loc
	}
	log.Printf("Found %d reports", len(reportMatches))
	log.Printf("New number of locations: %d", len(locations))

	// Gather conditions and errors.
	mu.Lock()
	errs = make([]*Error, 0, len(locations))
	mu.Unlock()
	for _, loc := range locations {
		cond, err := loc.GetConditions(client)
		if err != nil {
			mu.Lock()
			e := &Error{
				Loc: loc,
				Err: err,
			}
			errs = append(errs, e)
			mu.Unlock()
			continue
		}
		mu.Lock()
		conds[loc.Name] = cond
		mu.Unlock()
		saveConditionsFile()
	}
}

func loadConditionsFile() {
	f, err := os.Open(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s. %v. That's okay.", *conditionsFilePath, err)
		return
	}
	contents, err := ioutil.ReadAll(f)
	err = json.Unmarshal(contents, &conds)
	if err != nil {
		log.Printf("Failed to unmarshall the conditions map. %v", err)
		os.Exit(1)
	}
	log.Printf("Loaded conditions file %s", *conditionsFilePath)
}

func saveConditionsFile() {
	f, err := os.Create(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s for writing. %v", *conditionsFilePath)
		return
	}
	mu.Lock()
	defer mu.Unlock()
	contents, err := json.Marshal(conds)
	if err != nil {
		log.Printf("Failed to marshal conditions. %v", conds)
		return
	}
	f.Write(contents)
	f.Close()
	log.Printf("Saved conditions file %s", *conditionsFilePath)
}

var locations = make(map[string]*Location)
var mu sync.Mutex // for conds and errs
var conds = make(map[string]*Conditions)
var errs []*Error

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)
var reportRx = regexp.MustCompile(`<option value="(/[^"]+-Surf-Report/\d+/)">([^<]+)\s*</option>`)

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	foundStars := starRx.FindAll(body, -1)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	rating := len(foundStars)
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}

func (c *Conditions) Stars() string {
	runes := make([]rune, 0, 5)
	for i := 0; i < c.Rating; i++ {
		runes = append(runes, '★')
	}
	for i := 0; i < 5-c.Rating; i++ {
		runes = append(runes, '☆')
	}
	return string(runes)
}

func keepConditionsUpdated() {
	updateConditionsAllLocations()
	tick := time.Tick(*conditionsInterval)
	for {
		<-tick
		updateConditionsAllLocations()
	}
}
<|endoftext|>


<commit_before>package eventmgr

import "testing"

func handler1(event string, info infomap) {
	t := info["test"].(*testing.T)

	if info["k"].(int) != 3 {
		t.Error(
			"For", event,
			"expected", 3,
			"got", info["k"],
		)
	}
}

func TestEncode(t *testing.T) {
	var manager EventManager

	manager.Attach("test1", handler1, 0)
	info := make(infomap)
	info["k"] = 3
	info["test"] = t
	manager.Dispatch("test1", info)
}
<commit_msg>eventmgr: Test dispatching and priority<commit_after>package eventmgr

import "testing"

var test2Tracker = 0

func handler1(event string, info infomap) {
	t := info["test"].(*testing.T)

	if info["k"].(int) != 3 {
		t.Error(
			"For", event,
			"expected", 3,
			"got", info["k"],
		)
	}
}

func handler2First(event string, info infomap) {
	t := info["test"].(*testing.T)

	if test2Tracker != 0 {
		t.Error(
			"For", event,
			"expected", 0,
			"got", test2Tracker,
		)
	} else {
		test2Tracker += 3
	}
}

func handler2Second(event string, info infomap) {
	t := info["test"].(*testing.T)

	if test2Tracker != 3 {
		t.Error(
			"For", event,
			"expected", 3,
			"got", test2Tracker,
		)
	} else {
		test2Tracker--
	}
}

func handler2Third(event string, info infomap) {
	t := info["test"].(*testing.T)

	if test2Tracker != 2 {
		t.Error(
			"For", event,
			"expected", 2,
			"got", test2Tracker,
		)
	} else {
		test2Tracker += 5
	}
}

func TestAttachDispatch(t *testing.T) {
	var manager EventManager
	var info infomap

	// test dispatching
	manager.Attach("test1", handler1, 0)
	info = make(infomap)
	info["k"] = 3
	info["test"] = t
	manager.Dispatch("test1", info)

	// test priority dispatching
	manager.Attach("test2", handler2Second, 8)
	manager.Attach("test2", handler2First, 3)
	manager.Attach("test2", handler2Third, 16)
	info = make(infomap)
	info["test"] = t
	manager.Dispatch("test2", info)

	if test2Tracker != 7 {
		t.Error(
			"For", "test2 end",
			"expected", 7,
			"got", test2Tracker,
		)
	}
}
<|endoftext|>


<commit_before>// +build freebsd linux darwin

package disk

import "syscall"

func Usage(path string) (*UsageStat, error) {
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path, &stat)
	if err != nil {
		return nil, err
	}
	bsize := stat.Bsize

	ret := &UsageStat{
		Path:        path,
		Fstype:      getFsType(stat),
		Total:       (uint64(stat.Blocks) * uint64(bsize)),
		Free:        (uint64(stat.Bavail) * uint64(bsize)),
		InodesTotal: (uint64(stat.Files)),
		InodesFree:  (uint64(stat.Ffree)),
	}

	// if could not get InodesTotal, return empty
	if ret.InodesTotal < ret.InodesFree {
		return ret, nil
	}
	ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
	ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
	ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize)
	ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0

	return ret, nil
}
<commit_msg>fix: division by zero possible<commit_after>// +build freebsd linux darwin

package disk

import "syscall"

func Usage(path string) (*UsageStat, error) {
	stat := syscall.Statfs_t{}
	err := syscall.Statfs(path, &stat)
	if err != nil {
		return nil, err
	}
	bsize := stat.Bsize

	ret := &UsageStat{
		Path:        path,
		Fstype:      getFsType(stat),
		Total:       (uint64(stat.Blocks) * uint64(bsize)),
		Free:        (uint64(stat.Bavail) * uint64(bsize)),
		InodesTotal: (uint64(stat.Files)),
		InodesFree:  (uint64(stat.Ffree)),
	}

	// if could not get InodesTotal, return empty
	if ret.InodesTotal < ret.InodesFree {
		return ret, nil
	}

	ret.InodesUsed = (ret.InodesTotal - ret.InodesFree)
	ret.Used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(bsize)

        if ret.InodesTotal == 0 {
		ret.InodesUsedPercent = 0
	} else {
		ret.InodesUsedPercent = (float64(ret.InodesUsed) / float64(ret.InodesTotal)) * 100.0
	}

	if ret.Total == 0 {
		ret.UsedPercent = 0
	} else {
		ret.UsedPercent = (float64(ret.Used) / float64(ret.Total)) * 100.0
	}

	return ret, nil
}
<|endoftext|>