<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/cagedtornado/centralconfig/datastores"
)

func ShowUI(rw http.ResponseWriter, req *http.Request) {
	http.Redirect(rw, req, "/ui/", 301)
}

//	Gets a specfic config item based on application and config item name
func GetConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	response, err := ds.Get(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	configItems := []datastores.ConfigItem{}
	if response.Name != "" {
		configItems = append(configItems, response)
		sendDataResponse(rw, "Config item found", configItems)
		return
	}

	sendDataResponse(rw, "No config item found with that application and name", configItems)
}

//	Set a specific config item
func SetConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	response, err := ds.Set(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
	} else {
		configItems := append([]datastores.ConfigItem{}, response)
		sendDataResponse(rw, "Config item updated", configItems)
	}
}

//	Removes a specific config item
func RemoveConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	err = ds.Remove(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
	} else {
		configItems := append([]datastores.ConfigItem{}, *request)
		sendDataResponse(rw, "Config item removed", configItems)
	}
}

//	Gets all config information for a given application
func GetAllConfigForApp(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	configItems, err := ds.GetAllForApplication(request.Application)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(configItems) > 0 {
		sendDataResponse(rw, "Config items found", configItems)
		return
	}

	sendDataResponse(rw, "No config items found with that application", configItems)
}

//	Gets all config information
func GetAllConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	configItems, err := ds.GetAll()
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(configItems) > 0 {
		sendDataResponse(rw, "Config items found", configItems)
		return
	}

	sendDataResponse(rw, "No config items found", configItems)
}

//	Gets all applications
func GetAllApplications(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	applications, err := ds.GetAllApplications()
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(applications) > 0 {
		sendDataResponse(rw, "Applications found", applications)
		return
	}

	sendDataResponse(rw, "No config items found", applications)
}

//	Initializes a store
func InitStore(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(rw, "InitStore method")
}

//	Used to send back an error:
func sendErrorResponse(rw http.ResponseWriter, err error, code int) {
	//	Our return value
	response := datastores.ConfigResponse{
		Status:  code,
		Message: "Error: " + err.Error()}

	//	Serialize to JSON & return the response:
	rw.WriteHeader(code)
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	json.NewEncoder(rw).Encode(response)
}

//	Used to send back a response with data
func sendDataResponse(rw http.ResponseWriter, message string, dataItems interface{}) {
	//	Our return value
	response := datastores.ConfigResponse{
		Status:  http.StatusOK,
		Message: message,
		Data:    dataItems}

	//	Serialize to JSON & return the response:
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	json.NewEncoder(rw).Encode(response)
}
<commit_msg>API: returns single config item for config/get<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/cagedtornado/centralconfig/datastores"
)

func ShowUI(rw http.ResponseWriter, req *http.Request) {
	http.Redirect(rw, req, "/ui/", 301)
}

//	Gets a specfic config item based on application and config item name
func GetConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	response, err := ds.Get(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty item):
	configItem := datastores.ConfigItem{}
	if response.Name != "" {
		configItem = response
		sendDataResponse(rw, "Config item found", configItem)
		return
	}

	sendDataResponse(rw, "No config item found with that application and name", configItem)
}

//	Set a specific config item
func SetConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	response, err := ds.Set(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
	} else {
		configItems := append([]datastores.ConfigItem{}, response)
		sendDataResponse(rw, "Config item updated", configItems)
	}
}

//	Removes a specific config item
func RemoveConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	err = ds.Remove(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
	} else {
		configItems := append([]datastores.ConfigItem{}, *request)
		sendDataResponse(rw, "Config item removed", configItems)
	}
}

//	Gets all config information for a given application
func GetAllConfigForApp(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Decode the request:
	request := &datastores.ConfigItem{}
	err := json.NewDecoder(req.Body).Decode(request)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusBadRequest)
		return
	}

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	configItems, err := ds.GetAllForApplication(request.Application)
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(configItems) > 0 {
		sendDataResponse(rw, "Config items found", configItems)
		return
	}

	sendDataResponse(rw, "No config items found with that application", configItems)
}

//	Gets all config information
func GetAllConfig(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	configItems, err := ds.GetAll()
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(configItems) > 0 {
		sendDataResponse(rw, "Config items found", configItems)
		return
	}

	sendDataResponse(rw, "No config items found", configItems)
}

//	Gets all applications
func GetAllApplications(rw http.ResponseWriter, req *http.Request) {
	//	req.Body is a ReadCloser -- we need to remember to close it:
	defer req.Body.Close()

	//	Get the current datastore:
	ds := datastores.GetConfigDatastore()

	//	Send the request to the datastore and get a response:
	applications, err := ds.GetAllApplications()
	if err != nil {
		sendErrorResponse(rw, err, http.StatusInternalServerError)
		return
	}

	//	If we found an item, return it (otherwise, return an empty array):
	if len(applications) > 0 {
		sendDataResponse(rw, "Applications found", applications)
		return
	}

	sendDataResponse(rw, "No config items found", applications)
}

//	Initializes a store
func InitStore(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintln(rw, "InitStore method")
}

//	Used to send back an error:
func sendErrorResponse(rw http.ResponseWriter, err error, code int) {
	//	Our return value
	response := datastores.ConfigResponse{
		Status:  code,
		Message: "Error: " + err.Error()}

	//	Serialize to JSON & return the response:
	rw.WriteHeader(code)
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	json.NewEncoder(rw).Encode(response)
}

//	Used to send back a response with data
func sendDataResponse(rw http.ResponseWriter, message string, dataItems interface{}) {
	//	Our return value
	response := datastores.ConfigResponse{
		Status:  http.StatusOK,
		Message: message,
		Data:    dataItems}

	//	Serialize to JSON & return the response:
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	json.NewEncoder(rw).Encode(response)
}
<|endoftext|>


<commit_before>package tests

import (
	"context"
	"fmt"
	"io/ioutil"

	. "github.com/smartystreets/goconvey/convey"

	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/testutil"
	"go.polydawn.net/timeless-api"
	"go.polydawn.net/timeless-api/rio"
)

func CheckRoundTrip(pack rio.PackFunc, unpack rio.UnpackFunc, warehouseAddr api.WarehouseAddr) {
	Convey("SPEC: Round-trip pack and unpack of fileset should work...", func() {
		for _, fixture := range AllFixtures {
			Convey(fmt.Sprintf("- Fixture %q", fixture.Name), func() {
				testutil.WithTmpdir(func(tmpDir fs.AbsolutePath) {
					fixturePath := tmpDir.Join(fs.MustRelPath("fixture"))
					// Set up fixture.
					PlaceFixture(osfs.New(fixturePath), fixture.Files)
					// Pack up into our warehouseaddr.
					wareID, err := pack(
						context.Background(),
						fixturePath.String(),
						api.FilesetFilters{},
						warehouseAddr,
						rio.Monitor{},
					)
					So(err, ShouldBeNil)
					// Unpack to a new path.
					unpackPath := tmpDir.Join(fs.MustRelPath("unpack"))
					wareID2, err := unpack(
						context.Background(),
						wareID,
						unpackPath.String(),
						api.FilesetFilters{},
						[]api.WarehouseAddr{warehouseAddr},
						rio.Monitor{},
					)
					Convey("...and agree on hash and content", FailureContinues, func() {
						So(err, ShouldBeNil)
						// Should be same hash reported by unpack hashing process.
						So(wareID, ShouldResemble, wareID2)
						// Each file in the fixture should exist and match rescanning.
						afs := osfs.New(unpackPath)
						for _, file := range fixture.Files {
							fmeta, reader, err := fsOp.ScanFile(afs, file.Metadata.Name)
							So(err, ShouldBeNil)
							fmeta.Mtime = fmeta.Mtime.UTC()
							So(*fmeta, ShouldResemble, file.Metadata)
							if file.Metadata.Type == fs.Type_File {
								body, _ := ioutil.ReadAll(reader)
								So(string(body), ShouldResemble, string(file.Body))
							}
						}
					})
				})
			})
		}
	})
}
<commit_msg>transmat: oowee, tests now need to specify filters, since we have active defaults now!<commit_after>package tests

import (
	"context"
	"fmt"
	"io/ioutil"

	. "github.com/smartystreets/goconvey/convey"

	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/testutil"
	"go.polydawn.net/timeless-api"
	"go.polydawn.net/timeless-api/rio"
)

func CheckRoundTrip(pack rio.PackFunc, unpack rio.UnpackFunc, warehouseAddr api.WarehouseAddr) {
	Convey("SPEC: Round-trip pack and unpack of fileset should work...", func() {
		for _, fixture := range AllFixtures {
			Convey(fmt.Sprintf("- Fixture %q", fixture.Name), func() {
				testutil.WithTmpdir(func(tmpDir fs.AbsolutePath) {
					fixturePath := tmpDir.Join(fs.MustRelPath("fixture"))
					// Set up fixture.
					PlaceFixture(osfs.New(fixturePath), fixture.Files)
					// Pack up into our warehouseaddr.
					wareID, err := pack(
						context.Background(),
						fixturePath.String(),
						api.FilesetFilters{
							Uid:   "keep",
							Gid:   "keep",
							Mtime: "keep",
						},
						warehouseAddr,
						rio.Monitor{},
					)
					So(err, ShouldBeNil)
					// Unpack to a new path.
					unpackPath := tmpDir.Join(fs.MustRelPath("unpack"))
					wareID2, err := unpack(
						context.Background(),
						wareID,
						unpackPath.String(),
						api.FilesetFilters{
							Sticky: true,
						},
						[]api.WarehouseAddr{warehouseAddr},
						rio.Monitor{},
					)
					Convey("...and agree on hash and content", FailureContinues, func() {
						So(err, ShouldBeNil)
						// Should be same hash reported by unpack hashing process.
						So(wareID, ShouldResemble, wareID2)
						// Each file in the fixture should exist and match rescanning.
						afs := osfs.New(unpackPath)
						for _, file := range fixture.Files {
							fmeta, reader, err := fsOp.ScanFile(afs, file.Metadata.Name)
							So(err, ShouldBeNil)
							fmeta.Mtime = fmeta.Mtime.UTC()
							So(*fmeta, ShouldResemble, file.Metadata)
							if file.Metadata.Type == fs.Type_File {
								body, _ := ioutil.ReadAll(reader)
								So(string(body), ShouldResemble, string(file.Body))
							}
						}
					})
				})
			})
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"bytes"
	"errors"
	"log"
	"net/http"
	"io/ioutil"
	"os"
	"strings"

	"code.google.com/p/vitess/go/cache"
)

const cacheCapacity = 246*1024*1024 // 256MB
const template = `<!doctype html>

    <meta charset="utf-8">
    <title>viewdocs.io</title>
    <link rel="stylesheet" type="text/css" href="//cloud.typography.com/678416/735422/css/fonts.css" />
    <link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:300,600' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="http://static.gist.io/css/screen.css">

{{NAME}}

            {{CONTENT}}

`

type CacheValue struct {
	Value string
}

func (cv *CacheValue) Size() int {
	return len(cv.Value)
}

func parseRequest(r *http.Request) (user, repo, doc string, err error) {
	hostname := strings.Split(r.Host, ".")
	if len(hostname) < 2 {
		return "", "", "", errors.New("Bad hostname")
	}
	user = hostname[0]
	path := strings.Split(r.RequestURI, "/")
	repo = path[1]
	if len(path) < 3 {
		doc = "index"
	} else {
		doc = strings.Join(path[2:], "/")
	}
	return
}

func fetchAndRenderDoc(user, repo, doc string) (string, error) {
	resp, err := http.Get("https://raw.github.com/"+user+"/"+repo+"/master/docs/"+doc+".md")
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}
	payload, err := json.Marshal(map[string]string{"text": string(body)})
	if err != nil {
		return "", err
	}
	resp, err = http.Post("https://api.github.com/markdown?access_token="+os.Getenv("ACCESS_TOKEN"), "application/json", bytes.NewBuffer(payload))
	if err != nil {
		return "", err
	}
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}
	output := strings.Replace(template, "{{CONTENT}}", string(body), 1)
	output = strings.Replace(output, "{{NAME}}", repo, -1)
	output = strings.Replace(output, "{{USER}}", user, -1)
	return output, nil
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8888"
	}

	lru := cache.NewLRUCache(cacheCapacity)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.RequestURI == "/" {
			http.Redirect(w, r, "http://progrium.viewdocs.io/viewdocs", 301)
			return
		}
		if r.RequestURI == "/favicon.ico" {
			return
		}
		switch r.Method {
		case "GET":
			user, repo, doc, err := parseRequest(r)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			key := user + ":" + repo + ":" + doc
			value, ok := lru.Get(key)
			var output string
			if !ok {
				log.Println("CACHE MISS: ", key)
				output, err = fetchAndRenderDoc(user, repo, doc)
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}
				lru.Set(key, &CacheValue{output})
				log.Println(lru.StatsJSON())
			} else {
				output = value.(*CacheValue).Value
			}
			w.Write([]byte(output))
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
	log.Println("Listening on port "+port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<commit_msg>conserving log output<commit_after>package main

import (
	"encoding/json"
	"bytes"
	"errors"
	"log"
	"net/http"
	"io/ioutil"
	"os"
	"strings"

	"code.google.com/p/vitess/go/cache"
)

const cacheCapacity = 246*1024*1024 // 256MB
const template = `<!doctype html>

    <meta charset="utf-8">
    <title>viewdocs.io</title>
    <link rel="stylesheet" type="text/css" href="//cloud.typography.com/678416/735422/css/fonts.css" />
    <link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:300,600' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="http://static.gist.io/css/screen.css">

{{NAME}}

            {{CONTENT}}

`

type CacheValue struct {
	Value string
}

func (cv *CacheValue) Size() int {
	return len(cv.Value)
}

func parseRequest(r *http.Request) (user, repo, doc string, err error) {
	hostname := strings.Split(r.Host, ".")
	if len(hostname) < 2 {
		return "", "", "", errors.New("Bad hostname")
	}
	user = hostname[0]
	path := strings.Split(r.RequestURI, "/")
	repo = path[1]
	if len(path) < 3 {
		doc = "index"
	} else {
		doc = strings.Join(path[2:], "/")
	}
	return
}

func fetchAndRenderDoc(user, repo, doc string) (string, error) {
	resp, err := http.Get("https://raw.github.com/"+user+"/"+repo+"/master/docs/"+doc+".md")
	if err != nil {
		return "", err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}
	payload, err := json.Marshal(map[string]string{"text": string(body)})
	if err != nil {
		return "", err
	}
	resp, err = http.Post("https://api.github.com/markdown?access_token="+os.Getenv("ACCESS_TOKEN"), "application/json", bytes.NewBuffer(payload))
	if err != nil {
		return "", err
	}
	body, err = ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return "", err
	}
	output := strings.Replace(template, "{{CONTENT}}", string(body), 1)
	output = strings.Replace(output, "{{NAME}}", repo, -1)
	output = strings.Replace(output, "{{USER}}", user, -1)
	return output, nil
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8888"
	}

	lru := cache.NewLRUCache(cacheCapacity)

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.RequestURI == "/" {
			http.Redirect(w, r, "http://progrium.viewdocs.io/viewdocs", 301)
			return
		}
		if r.RequestURI == "/favicon.ico" {
			return
		}
		switch r.Method {
		case "GET":
			user, repo, doc, err := parseRequest(r)
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				w.Write([]byte(err.Error()))
				return
			}
			key := user + ":" + repo + ":" + doc
			value, ok := lru.Get(key)
			var output string
			if !ok {
				output, err = fetchAndRenderDoc(user, repo, doc)
				if err != nil {
					w.WriteHeader(http.StatusInternalServerError)
					w.Write([]byte(err.Error()))
					return
				}
				lru.Set(key, &CacheValue{output})
				log.Println("CACHE MISS:", key, lru.StatsJSON())
			} else {
				output = value.(*CacheValue).Value
			}
			w.Write([]byte(output))
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})
	log.Println("Listening on port "+port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<|endoftext|>


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

// The ct_server binary runs the CT personality.
package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/etcd/clientv3"
	etcdnaming "github.com/coreos/etcd/clientv3/naming"
	"github.com/golang/glog"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/trillian/ctfe/configpb"
	"github.com/google/certificate-transparency-go/trillian/util"
	"github.com/google/trillian"
	"github.com/google/trillian/monitoring/opencensus"
	"github.com/google/trillian/monitoring/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/rs/cors"
	"github.com/tomasen/realip"
	"google.golang.org/grpc"
	"google.golang.org/grpc/balancer/roundrobin"
	"google.golang.org/grpc/naming"

	// Register PEMKeyFile, PrivateKey and PKCS11Config ProtoHandlers
	_ "github.com/google/trillian/crypto/keys/der/proto"
	_ "github.com/google/trillian/crypto/keys/pem/proto"
	_ "github.com/google/trillian/crypto/keys/pkcs11/proto"
)

// Global flags that affect all log instances.
var (
	httpEndpoint       = flag.String("http_endpoint", "localhost:6962", "Endpoint for HTTP (host:port)")
	metricsEndpoint    = flag.String("metrics_endpoint", "localhost:6963", "Endpoint for serving metrics; if left empty, metrics will be visible on --http_endpoint")
	rpcBackend         = flag.String("log_rpc_server", "localhost:8090", "Backend specification; comma-separated list or etcd service name (if --etcd_servers specified). If unset backends are specified in config (as a LogMultiConfig proto)")
	rpcDeadline        = flag.Duration("rpc_deadline", time.Second*10, "Deadline for backend RPC requests")
	getSTHInterval     = flag.Duration("get_sth_interval", time.Second*180, "Interval between internal get-sth operations (0 to disable)")
	logConfig          = flag.String("log_config", "", "File holding log config in text proto format")
	maxGetEntries      = flag.Int64("max_get_entries", 0, "Max number of entries we allow in a get-entries request (0=>use default 1000)")
	etcdServers        = flag.String("etcd_servers", "", "A comma-separated list of etcd servers")
	etcdHTTPService    = flag.String("etcd_http_service", "trillian-ctfe-http", "Service name to announce our HTTP endpoint under")
	etcdMetricsService = flag.String("etcd_metrics_service", "trillian-ctfe-metrics-http", "Service name to announce our HTTP metrics endpoint under")
	tracing            = flag.Bool("tracing", false, "If true opencensus Stackdriver tracing will be enabled. See https://opencensus.io/.")
	tracingProjectID   = flag.String("tracing_project_id", "", "project ID to pass to stackdriver. Can be empty for GCP, consult docs for other platforms.")
	tracingPercent     = flag.Int("tracing_percent", 0, "Percent of requests to be traced. Zero is a special case to use the DefaultSampler")
	quotaRemote        = flag.Bool("quota_remote", true, "Enable requesting of quota for IP address sending incoming requests")
	quotaIntermediate  = flag.Bool("quota_intermediate", true, "Enable requesting of quota for intermediate certificates in sumbmitted chains")
)

func main() {
	flag.Parse()
	ctx := context.Background()

	if *maxGetEntries > 0 {
		ctfe.MaxGetEntriesAllowed = *maxGetEntries
	}

	var cfg *configpb.LogMultiConfig
	var err error
	// Get log config from file before we start. This is a different proto
	// type if we're using a multi backend configuration (no rpcBackend set
	// in flags). The single-backend config is converted to a multi config so
	// they can be treated the same.
	if len(*rpcBackend) > 0 {
		var cfgs []*configpb.LogConfig
		if cfgs, err = ctfe.LogConfigFromFile(*logConfig); err == nil {
			cfg = ctfe.ToMultiLogConfig(cfgs, *rpcBackend)
		}
	} else {
		cfg, err = ctfe.MultiLogConfigFromFile(*logConfig)
	}

	if err != nil {
		glog.Exitf("Failed to read config: %v", err)
	}

	beMap, err := ctfe.ValidateLogMultiConfig(cfg)
	if err != nil {
		glog.Exitf("Invalid config: %v", err)
	}

	glog.CopyStandardLogTo("WARNING")
	glog.Info("**** CT HTTP Server Starting ****")

	metricsAt := *metricsEndpoint
	if metricsAt == "" {
		metricsAt = *httpEndpoint
	}

	dialOpts := []grpc.DialOption{grpc.WithInsecure()}
	if len(*etcdServers) > 0 {
		// Use etcd to provide endpoint resolution.
		cfg := clientv3.Config{Endpoints: strings.Split(*etcdServers, ","), DialTimeout: 5 * time.Second}
		client, err := clientv3.New(cfg)
		if err != nil {
			glog.Exitf("Failed to connect to etcd at %v: %v", *etcdServers, err)
		}
		etcdRes := &etcdnaming.GRPCResolver{Client: client}
		dialOpts = append(dialOpts, grpc.WithBalancer(grpc.RoundRobin(etcdRes)))

		// Also announce ourselves.
		updateHTTP := naming.Update{Op: naming.Add, Addr: *httpEndpoint}
		updateMetrics := naming.Update{Op: naming.Add, Addr: metricsAt}
		glog.Infof("Announcing our presence in %v with %+v", *etcdHTTPService, updateHTTP)
		etcdRes.Update(ctx, *etcdHTTPService, updateHTTP)
		glog.Infof("Announcing our presence in %v with %+v", *etcdMetricsService, updateMetrics)
		etcdRes.Update(ctx, *etcdMetricsService, updateMetrics)

		byeHTTP := naming.Update{Op: naming.Delete, Addr: *httpEndpoint}
		byeMetrics := naming.Update{Op: naming.Delete, Addr: metricsAt}
		defer func() {
			glog.Infof("Removing our presence in %v with %+v", *etcdHTTPService, byeHTTP)
			etcdRes.Update(ctx, *etcdHTTPService, byeHTTP)
			glog.Infof("Removing our presence in %v with %+v", *etcdMetricsService, byeMetrics)
			etcdRes.Update(ctx, *etcdMetricsService, byeMetrics)
		}()
	} else if strings.Contains(*rpcBackend, ",") {
		glog.Infof("Using FixedBackendResolver")
		// Use a fixed endpoint resolution that just returns the addresses configured on the command line.
		res := util.FixedBackendResolver{}
		dialOpts = append(dialOpts, grpc.WithBalancer(grpc.RoundRobin(res)))
	} else {
		glog.Infof("Using regular DNS resolver")
		dialOpts = append(dialOpts, grpc.WithBalancerName(roundrobin.Name))
	}

	// Dial all our log backends.
	clientMap := make(map[string]trillian.TrillianLogClient)
	for _, be := range beMap {
		glog.Infof("Dialling backend: %v", be)
		if len(beMap) == 1 {
			// If there's only one of them we use the blocking option as we can't
			// serve anything until connected.
			dialOpts = append(dialOpts, grpc.WithBlock())
		}
		conn, err := grpc.Dial(be.BackendSpec, dialOpts...)
		if err != nil {
			glog.Exitf("Could not dial RPC server: %v: %v", be, err)
		}
		defer conn.Close()
		clientMap[be.Name] = trillian.NewTrillianLogClient(conn)
	}

	// Allow cross-origin requests to all handlers registered on corsMux.
	// This is safe for CT log handlers because the log is public and
	// unauthenticated so cross-site scripting attacks are not a concern.
	corsMux := http.NewServeMux()
	corsHandler := cors.AllowAll().Handler(corsMux)
	http.Handle("/", corsHandler)

	// Register handlers for all the configured logs using the correct RPC
	// client.
	for _, c := range cfg.LogConfigs.Config {
		if err := setupAndRegister(ctx, clientMap[c.LogBackendName], *rpcDeadline, c, corsMux); err != nil {
			glog.Exitf("Failed to set up log instance for %+v: %v", cfg, err)
		}
	}

	// Return a 200 on the root, for GCE default health checking :/
	corsMux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/" {
			resp.WriteHeader(http.StatusOK)
		} else {
			resp.WriteHeader(http.StatusNotFound)
		}
	})

	if metricsAt != *httpEndpoint {
		// Run a separate handler for metrics.
		go func() {
			mux := http.NewServeMux()
			mux.Handle("/metrics", promhttp.Handler())
			metricsServer := http.Server{Addr: metricsAt, Handler: mux}
			err := metricsServer.ListenAndServe()
			glog.Warningf("Metrics server exited: %v", err)
		}()
	} else {
		// Handle metrics on the DefaultServeMux.
		http.Handle("/metrics", promhttp.Handler())
	}

	if *getSTHInterval > 0 {
		// Regularly update the internal STH for each log so our metrics stay up-to-date with any tree head
		// changes that are not triggered by us.
		for _, c := range cfg.LogConfigs.Config {
			ticker := time.NewTicker(*getSTHInterval)
			go func(c *configpb.LogConfig) {
				glog.Infof("start internal get-sth operations on log %v (%d)", c.Prefix, c.LogId)
				for t := range ticker.C {
					glog.V(1).Infof("tick at %v: force internal get-sth for log %v (%d)", t, c.Prefix, c.LogId)
					if _, err := ctfe.GetTreeHead(ctx, clientMap[c.LogBackendName], c.LogId, c.Prefix, nil /*quota user*/); err != nil {
						glog.Warningf("failed to retrieve tree head for log %v (%d): %v", c.Prefix, c.LogId, err)
					}
				}
			}(c)
		}
	}

	// Bring up the HTTP server and serve until we get a signal not to.
	go awaitSignal(func() {
		os.Exit(1)
	})

	// If we're enabling tracing we need to use an instrumented http.Handler.
	var handler http.Handler
	if *tracing {
		handler, err = opencensus.EnableHTTPServerTracing(*tracingProjectID, *tracingPercent)
		if err != nil {
			glog.Exitf("Failed to initialize stackdriver / opencensus tracing: %v", err)
		}
	}

	err = http.ListenAndServe(*httpEndpoint, handler)
	glog.Warningf("Server exited: %v", err)
	glog.Flush()
}

// awaitSignal waits for standard termination signals, then runs the given
// function; it should be run as a separate goroutine.
func awaitSignal(doneFn func()) {
	// Arrange notification for the standard set of signals used to terminate a server
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// Now block main and wait for a signal
	sig := <-sigs
	glog.Warningf("Signal received: %v", sig)
	glog.Flush()

	doneFn()
}

func setupAndRegister(ctx context.Context, client trillian.TrillianLogClient, deadline time.Duration, cfg *configpb.LogConfig, mux *http.ServeMux) error {
	opts := ctfe.InstanceOptions{
		Deadline:      deadline,
		MetricFactory: prometheus.MetricFactory{},
		RequestLog:    new(ctfe.DefaultRequestLog),
	}
	if *quotaRemote {
		glog.Info("Enabling quota for requesting IP")
		opts.RemoteQuotaUser = realip.FromRequest
	}
	if *quotaIntermediate {
		glog.Info("Enabling quota for intermediate certificates")
		opts.CertificateQuotaUser = ctfe.QuotaUserForCert
	}
	handlers, err := ctfe.SetUpInstance(ctx, client, cfg, opts)
	if err != nil {
		return err
	}
	for path, handler := range *handlers {
		mux.Handle(path, handler)
	}
	return nil
}
<commit_msg>trillian/ctfe: graceful shutdown on signal (#245)<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// The ct_server binary runs the CT personality.
package main

import (
	"context"
	"flag"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/etcd/clientv3"
	etcdnaming "github.com/coreos/etcd/clientv3/naming"
	"github.com/golang/glog"
	"github.com/google/certificate-transparency-go/trillian/ctfe"
	"github.com/google/certificate-transparency-go/trillian/ctfe/configpb"
	"github.com/google/certificate-transparency-go/trillian/util"
	"github.com/google/trillian"
	"github.com/google/trillian/monitoring/opencensus"
	"github.com/google/trillian/monitoring/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/rs/cors"
	"github.com/tomasen/realip"
	"google.golang.org/grpc"
	"google.golang.org/grpc/balancer/roundrobin"
	"google.golang.org/grpc/naming"

	// Register PEMKeyFile, PrivateKey and PKCS11Config ProtoHandlers
	_ "github.com/google/trillian/crypto/keys/der/proto"
	_ "github.com/google/trillian/crypto/keys/pem/proto"
	_ "github.com/google/trillian/crypto/keys/pkcs11/proto"
)

// Global flags that affect all log instances.
var (
	httpEndpoint       = flag.String("http_endpoint", "localhost:6962", "Endpoint for HTTP (host:port)")
	metricsEndpoint    = flag.String("metrics_endpoint", "localhost:6963", "Endpoint for serving metrics; if left empty, metrics will be visible on --http_endpoint")
	rpcBackend         = flag.String("log_rpc_server", "localhost:8090", "Backend specification; comma-separated list or etcd service name (if --etcd_servers specified). If unset backends are specified in config (as a LogMultiConfig proto)")
	rpcDeadline        = flag.Duration("rpc_deadline", time.Second*10, "Deadline for backend RPC requests")
	getSTHInterval     = flag.Duration("get_sth_interval", time.Second*180, "Interval between internal get-sth operations (0 to disable)")
	logConfig          = flag.String("log_config", "", "File holding log config in text proto format")
	maxGetEntries      = flag.Int64("max_get_entries", 0, "Max number of entries we allow in a get-entries request (0=>use default 1000)")
	etcdServers        = flag.String("etcd_servers", "", "A comma-separated list of etcd servers")
	etcdHTTPService    = flag.String("etcd_http_service", "trillian-ctfe-http", "Service name to announce our HTTP endpoint under")
	etcdMetricsService = flag.String("etcd_metrics_service", "trillian-ctfe-metrics-http", "Service name to announce our HTTP metrics endpoint under")
	tracing            = flag.Bool("tracing", false, "If true opencensus Stackdriver tracing will be enabled. See https://opencensus.io/.")
	tracingProjectID   = flag.String("tracing_project_id", "", "project ID to pass to stackdriver. Can be empty for GCP, consult docs for other platforms.")
	tracingPercent     = flag.Int("tracing_percent", 0, "Percent of requests to be traced. Zero is a special case to use the DefaultSampler")
	quotaRemote        = flag.Bool("quota_remote", true, "Enable requesting of quota for IP address sending incoming requests")
	quotaIntermediate  = flag.Bool("quota_intermediate", true, "Enable requesting of quota for intermediate certificates in sumbmitted chains")
)

func main() {
	flag.Parse()
	ctx := context.Background()

	if *maxGetEntries > 0 {
		ctfe.MaxGetEntriesAllowed = *maxGetEntries
	}

	var cfg *configpb.LogMultiConfig
	var err error
	// Get log config from file before we start. This is a different proto
	// type if we're using a multi backend configuration (no rpcBackend set
	// in flags). The single-backend config is converted to a multi config so
	// they can be treated the same.
	if len(*rpcBackend) > 0 {
		var cfgs []*configpb.LogConfig
		if cfgs, err = ctfe.LogConfigFromFile(*logConfig); err == nil {
			cfg = ctfe.ToMultiLogConfig(cfgs, *rpcBackend)
		}
	} else {
		cfg, err = ctfe.MultiLogConfigFromFile(*logConfig)
	}

	if err != nil {
		glog.Exitf("Failed to read config: %v", err)
	}

	beMap, err := ctfe.ValidateLogMultiConfig(cfg)
	if err != nil {
		glog.Exitf("Invalid config: %v", err)
	}

	glog.CopyStandardLogTo("WARNING")
	glog.Info("**** CT HTTP Server Starting ****")

	metricsAt := *metricsEndpoint
	if metricsAt == "" {
		metricsAt = *httpEndpoint
	}

	dialOpts := []grpc.DialOption{grpc.WithInsecure()}
	if len(*etcdServers) > 0 {
		// Use etcd to provide endpoint resolution.
		cfg := clientv3.Config{Endpoints: strings.Split(*etcdServers, ","), DialTimeout: 5 * time.Second}
		client, err := clientv3.New(cfg)
		if err != nil {
			glog.Exitf("Failed to connect to etcd at %v: %v", *etcdServers, err)
		}
		etcdRes := &etcdnaming.GRPCResolver{Client: client}
		dialOpts = append(dialOpts, grpc.WithBalancer(grpc.RoundRobin(etcdRes)))

		// Also announce ourselves.
		updateHTTP := naming.Update{Op: naming.Add, Addr: *httpEndpoint}
		updateMetrics := naming.Update{Op: naming.Add, Addr: metricsAt}
		glog.Infof("Announcing our presence in %v with %+v", *etcdHTTPService, updateHTTP)
		etcdRes.Update(ctx, *etcdHTTPService, updateHTTP)
		glog.Infof("Announcing our presence in %v with %+v", *etcdMetricsService, updateMetrics)
		etcdRes.Update(ctx, *etcdMetricsService, updateMetrics)

		byeHTTP := naming.Update{Op: naming.Delete, Addr: *httpEndpoint}
		byeMetrics := naming.Update{Op: naming.Delete, Addr: metricsAt}
		defer func() {
			glog.Infof("Removing our presence in %v with %+v", *etcdHTTPService, byeHTTP)
			etcdRes.Update(ctx, *etcdHTTPService, byeHTTP)
			glog.Infof("Removing our presence in %v with %+v", *etcdMetricsService, byeMetrics)
			etcdRes.Update(ctx, *etcdMetricsService, byeMetrics)
		}()
	} else if strings.Contains(*rpcBackend, ",") {
		glog.Infof("Using FixedBackendResolver")
		// Use a fixed endpoint resolution that just returns the addresses configured on the command line.
		res := util.FixedBackendResolver{}
		dialOpts = append(dialOpts, grpc.WithBalancer(grpc.RoundRobin(res)))
	} else {
		glog.Infof("Using regular DNS resolver")
		dialOpts = append(dialOpts, grpc.WithBalancerName(roundrobin.Name))
	}

	// Dial all our log backends.
	clientMap := make(map[string]trillian.TrillianLogClient)
	for _, be := range beMap {
		glog.Infof("Dialling backend: %v", be)
		if len(beMap) == 1 {
			// If there's only one of them we use the blocking option as we can't
			// serve anything until connected.
			dialOpts = append(dialOpts, grpc.WithBlock())
		}
		conn, err := grpc.Dial(be.BackendSpec, dialOpts...)
		if err != nil {
			glog.Exitf("Could not dial RPC server: %v: %v", be, err)
		}
		defer conn.Close()
		clientMap[be.Name] = trillian.NewTrillianLogClient(conn)
	}

	// Allow cross-origin requests to all handlers registered on corsMux.
	// This is safe for CT log handlers because the log is public and
	// unauthenticated so cross-site scripting attacks are not a concern.
	corsMux := http.NewServeMux()
	corsHandler := cors.AllowAll().Handler(corsMux)
	http.Handle("/", corsHandler)

	// Register handlers for all the configured logs using the correct RPC
	// client.
	for _, c := range cfg.LogConfigs.Config {
		if err := setupAndRegister(ctx, clientMap[c.LogBackendName], *rpcDeadline, c, corsMux); err != nil {
			glog.Exitf("Failed to set up log instance for %+v: %v", cfg, err)
		}
	}

	// Return a 200 on the root, for GCE default health checking :/
	corsMux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/" {
			resp.WriteHeader(http.StatusOK)
		} else {
			resp.WriteHeader(http.StatusNotFound)
		}
	})

	if metricsAt != *httpEndpoint {
		// Run a separate handler for metrics.
		go func() {
			mux := http.NewServeMux()
			mux.Handle("/metrics", promhttp.Handler())
			metricsServer := http.Server{Addr: metricsAt, Handler: mux}
			err := metricsServer.ListenAndServe()
			glog.Warningf("Metrics server exited: %v", err)
		}()
	} else {
		// Handle metrics on the DefaultServeMux.
		http.Handle("/metrics", promhttp.Handler())
	}

	if *getSTHInterval > 0 {
		// Regularly update the internal STH for each log so our metrics stay up-to-date with any tree head
		// changes that are not triggered by us.
		for _, c := range cfg.LogConfigs.Config {
			ticker := time.NewTicker(*getSTHInterval)
			go func(c *configpb.LogConfig) {
				glog.Infof("start internal get-sth operations on log %v (%d)", c.Prefix, c.LogId)
				for t := range ticker.C {
					glog.V(1).Infof("tick at %v: force internal get-sth for log %v (%d)", t, c.Prefix, c.LogId)
					if _, err := ctfe.GetTreeHead(ctx, clientMap[c.LogBackendName], c.LogId, c.Prefix, nil /*quota user*/); err != nil {
						glog.Warningf("failed to retrieve tree head for log %v (%d): %v", c.Prefix, c.LogId, err)
					}
				}
			}(c)
		}
	}

	// If we're enabling tracing we need to use an instrumented http.Handler.
	var handler http.Handler
	if *tracing {
		handler, err = opencensus.EnableHTTPServerTracing(*tracingProjectID, *tracingPercent)
		if err != nil {
			glog.Exitf("Failed to initialize stackdriver / opencensus tracing: %v", err)
		}
	}

	// Bring up the HTTP server and serve until we get a signal not to.
	srv := http.Server{Addr: *httpEndpoint, Handler: handler}
	go awaitSignal(func() {
		// Allow 60s for any pending requests to finish then terminate any stragglers
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*60)
		defer cancel()
		glog.Info("Shutting down HTTP server...")
		srv.Shutdown(ctx)
		glog.Info("HTTP server shutdown")
	})

	err = srv.ListenAndServe()
	if err != http.ErrServerClosed {
		glog.Warningf("Server exited: %v", err)
	}
	glog.Flush()
}

// awaitSignal waits for standard termination signals, then runs the given
// function; it should be run as a separate goroutine.
func awaitSignal(doneFn func()) {
	// Arrange notification for the standard set of signals used to terminate a server
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// Now block main and wait for a signal
	sig := <-sigs
	glog.Warningf("Signal received: %v", sig)
	glog.Flush()

	doneFn()
}

func setupAndRegister(ctx context.Context, client trillian.TrillianLogClient, deadline time.Duration, cfg *configpb.LogConfig, mux *http.ServeMux) error {
	opts := ctfe.InstanceOptions{
		Deadline:      deadline,
		MetricFactory: prometheus.MetricFactory{},
		RequestLog:    new(ctfe.DefaultRequestLog),
	}
	if *quotaRemote {
		glog.Info("Enabling quota for requesting IP")
		opts.RemoteQuotaUser = realip.FromRequest
	}
	if *quotaIntermediate {
		glog.Info("Enabling quota for intermediate certificates")
		opts.CertificateQuotaUser = ctfe.QuotaUserForCert
	}
	handlers, err := ctfe.SetUpInstance(ctx, client, cfg, opts)
	if err != nil {
		return err
	}
	for path, handler := range *handlers {
		mux.Handle(path, handler)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/boombuler/led"
    "image/color"
    "time"
)

var RED = color.RGBA{0xFF, 0x00, 0x00, 0xFF}
var GREEN = color.RGBA{0xFF, 0xFF, 0x00, 0xFF}

func main() {
    for devInfo := range led.Devices() {
        dev, err := devInfo.Open()
        if err != nil {
            fmt.Println(err)
            continue
        }
        defer dev.Close()
        dev.SetColor(GREEN)

        time.Sleep(2 * time.Second)
    }

}
<commit_msg>implemented basic parameter<commit_after>package main

import (
    "flag"
    "fmt"
    "time"
    "github.com/boombuler/led"
    "image/color"
    "math/rand"
    "os"
)

var RED = color.RGBA{0xFF, 0x00, 0x00, 0xFF}
var GREEN = color.RGBA{0xFF, 0xFF, 0x00, 0xFF}

var flagSetColor string

func init() {
    flag.StringVar(&flagSetColor, "set-color", "", "Set color for device. The format must be \"#rrggbb\" or \"random\" where rr, gg, bb is any hexadecimal number from 00 to FF.")
}

func main() {

    flag.Parse()

    if (len(flagSetColor) < 1) {
        os.Exit(0)
    }

    for devInfo := range led.Devices() {
        dev, err := devInfo.Open()
        if err != nil {
            fmt.Println(err)
            continue
        }
        defer dev.Close()
        if (flagSetColor == "random") {
            rand.Seed(time.Now().UnixNano())
            dev.SetColor(color.RGBA{uint8(rand.Int()), uint8(rand.Int()), uint8(rand.Int()), 0xFF})
        } else {
            dev.SetColor(GREEN)
        }

        time.Sleep(2 * time.Second)
    }

}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 e2e

import (
	"fmt"
	"testing"
	"time"

	pkgTest "github.com/knative/pkg/test"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	serviceresourcenames "github.com/knative/serving/pkg/reconciler/v1alpha1/service/resources/names"
	"github.com/knative/serving/test"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	"github.com/knative/serving/pkg/pool"
	. "github.com/knative/serving/pkg/reconciler/v1alpha1/testing"
)

// Latencies is an interface for providing mechanisms for recording timings
// for the parts of the scale test.
type Latencies interface {
	// Add takes the name of this measurement and the time at which it began.
	// This should be called at the moment of completion, so that duration may
	// be computed with `time.Since(start)`.  We use this signature to that this
	// function is suitable for use in a `defer`.
	Add(name string, start time.Time)
}

func ScaleToWithin(t *testing.T, scale int, duration time.Duration, latencies Latencies) {
	clients := Setup(t)

	cleanupCh := make(chan test.ResourceNames, scale)
	defer close(cleanupCh)

	// These are the local (per-probe) and global (all probes) targets for the scale test.
	// 90 = 18/20, so allow two failures with the minimum number of probes, but expect
	// us to have 2.5 9s overall.
	//
	// TODO(#2850): After moving to Istio 1.1 we need to revisit these SLOs.
	const (
		localSLO  = 0.90
		globalSLO = 0.995
		minProbes = 20
	)
	pm := test.NewProberManager(t, clients, minProbes)

	timeoutCh := time.After(duration)

	t.Log("Creating new Services")
	wg := pool.NewWithCapacity(50 /* maximum in-flight creates */, scale /* capacity */)
	for i := 0; i < scale; i++ {
		// https://golang.org/doc/faq#closures_and_goroutines
		i := i

		wg.Go(func() error {
			names := test.ResourceNames{
				Service: test.SubServiceNameForTest(t, fmt.Sprintf("%d", i)),
				Image:   "helloworld",
			}

			options := &test.Options{
				// Give each request 10 seconds to respond.
				// This is mostly to work around #2897
				RevisionTimeoutSeconds: 10,
				ReadinessProbe: &corev1.Probe{
					Handler: corev1.Handler{
						HTTPGet: &corev1.HTTPGetAction{
							Path: "/",
						},
					},
				},
			}

			// Start the clock for various waypoints towards Service readiness.
			start := time.Now()
			// Record the overall completion time regardless of success/failure.
			defer latencies.Add("time-to-done", start)

			svc, err := test.CreateLatestService(t, clients, names, options,
				WithResourceRequirements(corev1.ResourceRequirements{
					Limits: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("50Mi"),
					},
					Requests: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("20Mi"),
					},
				}),
				// See #2946 for why we do this.
				// turns off auto scaling by setting max and min scale to 1
				WithConfigAnnotations(map[string]string{
					"autoscaling.knative.dev/minScale": "1",
					"autoscaling.knative.dev/maxScale": "1",
				}))

			if err != nil {
				t.Errorf("CreateLatestService() = %v", err)
				return nil
			}
			// Record the time it took to create the service.
			latencies.Add("time-to-create", start)
			names.Route = serviceresourcenames.Route(svc)
			names.Config = serviceresourcenames.Configuration(svc)

			// Send it to our cleanup logic (below)
			cleanupCh <- names

			t.Logf("Wait for %s to become ready.", names.Service)
			var domain string
			err = test.WaitForServiceState(clients.ServingClient, names.Service, func(s *v1alpha1.Service) (bool, error) {
				if s.Status.Domain == "" {
					return false, nil
				}
				domain = s.Status.Domain
				return test.IsServiceReady(s)
			}, "ServiceUpdatedWithDomain")
			if err != nil {
				t.Errorf("WaitForServiceState(w/ Domain) = %v", err)
				return nil
			}
			// Record the time it took to become ready.
			latencies.Add("time-to-ready", start)

			_, err = pkgTest.WaitForEndpointState(
				clients.KubeClient,
				t.Logf,
				domain,
				test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.EventuallyMatchesBody(helloWorldExpectedOutput))),
				"WaitForEndpointToServeText",
				test.ServingFlags.ResolvableDomain)
			if err != nil {
				t.Errorf("WaitForEndpointState(expected text) = %v", err)
				return nil
			}
			// Record the time it took to get back a 200 with the expected text.
			latencies.Add("time-to-200", start)

			// Start probing the domain until the test is complete.
			pm.Spawn(domain)

			t.Logf("%s is ready.", names.Service)
			return nil
		})
	}

	// Wait for all of the service creations to complete (possibly in failure),
	// and signal the done channel.
	doneCh := make(chan error)
	go func() {
		defer close(doneCh)
		if err := wg.Wait(); err != nil {
			doneCh <- err
		}
	}()

	for {
		// As services get created, add logic to clean them up.
		// When all of the creations have finished, then stop all of the active probers
		// and check our SLIs against our SLOs.
		// All of this has to finish within the configured timeout.
		select {
		case names := <-cleanupCh:
			t.Logf("Added %v to cleanup routine.", names)
			test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
			defer test.TearDown(clients, names)

		case err := <-doneCh:
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}

			// This ProberManager implementation waits for minProbes before actually stopping.
			if err := pm.Stop(); err != nil {
				t.Fatalf("Stop() = %v", err)
			}
			// Check each of the local SLOs
			pm.Foreach(func(domain string, p test.Prober) {
				if err := test.CheckSLO(localSLO, domain, p); err != nil {
					t.Errorf("CheckSLO() = %v", err)
				}
			})
			// Check the global SLO
			if err := test.CheckSLO(globalSLO, "aggregate", pm); err != nil {
				t.Errorf("CheckSLO() = %v", err)
			}
			return

		case <-timeoutCh:
			// If we don't do this first, then we'll see tons of 503s from the ongoing probes
			// as we tear down the things they are probing.
			defer pm.Stop()
			t.Fatalf("Timed out waiting for %d services to become ready", scale)
		}
	}
}
<commit_msg>Improve error reporting of ScaleToN tests. (#3404)<commit_after>/*
Copyright 2019 The Knative 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 e2e

import (
	"fmt"
	"testing"
	"time"

	pkgTest "github.com/knative/pkg/test"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	serviceresourcenames "github.com/knative/serving/pkg/reconciler/v1alpha1/service/resources/names"
	"github.com/knative/serving/test"
	"github.com/pkg/errors"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"

	"github.com/knative/serving/pkg/pool"
	. "github.com/knative/serving/pkg/reconciler/v1alpha1/testing"
)

// Latencies is an interface for providing mechanisms for recording timings
// for the parts of the scale test.
type Latencies interface {
	// Add takes the name of this measurement and the time at which it began.
	// This should be called at the moment of completion, so that duration may
	// be computed with `time.Since(start)`.  We use this signature to that this
	// function is suitable for use in a `defer`.
	Add(name string, start time.Time)
}

func ScaleToWithin(t *testing.T, scale int, duration time.Duration, latencies Latencies) {
	clients := Setup(t)

	cleanupCh := make(chan test.ResourceNames, scale)
	defer close(cleanupCh)

	// These are the local (per-probe) and global (all probes) targets for the scale test.
	// 90 = 18/20, so allow two failures with the minimum number of probes, but expect
	// us to have 2.5 9s overall.
	//
	// TODO(#2850): After moving to Istio 1.1 we need to revisit these SLOs.
	const (
		localSLO  = 0.90
		globalSLO = 0.995
		minProbes = 20
	)
	pm := test.NewProberManager(t, clients, minProbes)

	timeoutCh := time.After(duration)

	t.Log("Creating new Services")
	wg := pool.NewWithCapacity(50 /* maximum in-flight creates */, scale /* capacity */)
	for i := 0; i < scale; i++ {
		// https://golang.org/doc/faq#closures_and_goroutines
		i := i

		wg.Go(func() error {
			names := test.ResourceNames{
				Service: test.SubServiceNameForTest(t, fmt.Sprintf("%d", i)),
				Image:   "helloworld",
			}

			options := &test.Options{
				// Give each request 10 seconds to respond.
				// This is mostly to work around #2897
				RevisionTimeoutSeconds: 10,
				ReadinessProbe: &corev1.Probe{
					Handler: corev1.Handler{
						HTTPGet: &corev1.HTTPGetAction{
							Path: "/",
						},
					},
				},
			}

			// Start the clock for various waypoints towards Service readiness.
			start := time.Now()
			// Record the overall completion time regardless of success/failure.
			defer latencies.Add("time-to-done", start)

			svc, err := test.CreateLatestService(t, clients, names, options,
				WithResourceRequirements(corev1.ResourceRequirements{
					Limits: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("50Mi"),
					},
					Requests: corev1.ResourceList{
						corev1.ResourceCPU:    resource.MustParse("10m"),
						corev1.ResourceMemory: resource.MustParse("20Mi"),
					},
				}),
				// See #2946 for why we do this.
				// turns off auto scaling by setting max and min scale to 1
				WithConfigAnnotations(map[string]string{
					"autoscaling.knative.dev/minScale": "1",
					"autoscaling.knative.dev/maxScale": "1",
				}))

			if err != nil {
				t.Errorf("CreateLatestService() = %v", err)
				return errors.Wrap(err, "CreateLatestService() failed")
			}
			// Record the time it took to create the service.
			latencies.Add("time-to-create", start)
			names.Route = serviceresourcenames.Route(svc)
			names.Config = serviceresourcenames.Configuration(svc)

			// Send it to our cleanup logic (below)
			cleanupCh <- names

			t.Logf("Wait for %s to become ready.", names.Service)
			var domain string
			err = test.WaitForServiceState(clients.ServingClient, names.Service, func(s *v1alpha1.Service) (bool, error) {
				if s.Status.Domain == "" {
					return false, nil
				}
				domain = s.Status.Domain
				return test.IsServiceReady(s)
			}, "ServiceUpdatedWithDomain")
			if err != nil {
				t.Errorf("WaitForServiceState(w/ Domain) = %v", err)
				return errors.Wrap(err, "WaitForServiceState(w/ Domain) failed")
			}
			// Record the time it took to become ready.
			latencies.Add("time-to-ready", start)

			_, err = pkgTest.WaitForEndpointState(
				clients.KubeClient,
				t.Logf,
				domain,
				test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.MatchesBody(helloWorldExpectedOutput))),
				"WaitForEndpointToServeText",
				test.ServingFlags.ResolvableDomain)
			if err != nil {
				t.Errorf("WaitForEndpointState(expected text) = %v", err)
				return errors.Wrap(err, "WaitForEndpointState(expected text) failed")
			}
			// Record the time it took to get back a 200 with the expected text.
			latencies.Add("time-to-200", start)

			// Start probing the domain until the test is complete.
			pm.Spawn(domain)

			t.Logf("%s is ready.", names.Service)
			return nil
		})
	}

	// Wait for all of the service creations to complete (possibly in failure),
	// and signal the done channel.
	doneCh := make(chan error)
	go func() {
		defer close(doneCh)
		if err := wg.Wait(); err != nil {
			doneCh <- err
		}
	}()

	for {
		// As services get created, add logic to clean them up.
		// When all of the creations have finished, then stop all of the active probers
		// and check our SLIs against our SLOs.
		// All of this has to finish within the configured timeout.
		select {
		case names := <-cleanupCh:
			t.Logf("Added %v to cleanup routine.", names)
			test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
			defer test.TearDown(clients, names)

		case err := <-doneCh:
			if err != nil {
				t.Fatalf("Unexpected error: %v", err)
			}

			// This ProberManager implementation waits for minProbes before actually stopping.
			if err := pm.Stop(); err != nil {
				t.Fatalf("Stop() = %v", err)
			}
			// Check each of the local SLOs
			pm.Foreach(func(domain string, p test.Prober) {
				if err := test.CheckSLO(localSLO, domain, p); err != nil {
					t.Errorf("CheckSLO() = %v", err)
				}
			})
			// Check the global SLO
			if err := test.CheckSLO(globalSLO, "aggregate", pm); err != nil {
				t.Errorf("CheckSLO() = %v", err)
			}
			return

		case <-timeoutCh:
			// If we don't do this first, then we'll see tons of 503s from the ongoing probes
			// as we tear down the things they are probing.
			defer pm.Stop()
			t.Fatalf("Timed out waiting for %d services to become ready", scale)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"sort"
	"time"
)

type song struct {
	Title  string
	Artist string
	Album  string
	Track  int
	ID     string
	path   string
}

type byTitle []*song
type byArtist []*song
type byAlbum []*song
type byLeast []*song
type byFavorite []*song

func (songs byTitle) Len() int {
	return len(songs)
}

func (songs byArtist) Len() int {
	return len(songs)
}

func (songs byAlbum) Len() int {
	return len(songs)
}

func (songs byLeast) Len() int {
	return len(songs)
}

func (songs byFavorite) Len() int {
	return len(songs)
}

func (songs byTitle) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byArtist) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byAlbum) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byLeast) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byFavorite) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byTitle) Less(i, j int) bool {
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	return false
}

func (songs byArtist) Less(i, j int) bool {
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byAlbum) Less(i, j int) bool {
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byLeast) Less(i, j int) bool {
	var icount, jcount int64
	if v, ok := playcount[songs[i].ID]; ok {
		icount = v
	} else {
		icount = 0
	}
	if v, ok := playcount[songs[j].ID]; ok {
		jcount = v
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount < jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byFavorite) Less(i, j int) bool {
	var icount, jcount int
	if _, ok := liked[songs[i].ID]; ok {
		icount = 1
	} else {
		icount = 0
	}
	if _, ok := liked[songs[j].ID]; ok {
		jcount = 1
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount > jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func countPlay(playcount map[string]int64) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		apiMutex.Lock()
		playcount[id]++
		savePlayCount(playcount)
		apiMutex.Unlock()
	}
}

func likes(likes map[string]bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		apiMutex.Lock()
		defer apiMutex.Unlock()
		if id := r.URL.Query().Get("id"); id != "" {
			if v, ok := likes[id]; ok {
				json.NewEncoder(w).Encode(v)
			} else {
				json.NewEncoder(w).Encode(false)
			}
			return
		}
		if like := r.URL.Query().Get("like"); like != "" {
			if v, ok := likes[like]; ok {
				if v == true {
					delete(likes, like)
					json.NewEncoder(w).Encode(false)
				} else {
					likes[like] = true
					json.NewEncoder(w).Encode(true)
				}
			} else {
				likes[like] = true
				json.NewEncoder(w).Encode(true)
			}
		}
		err := likedFile.Truncate(0)
		if err != nil {
			srvlog.Crit("Could not truncate likes file", "error", err)
		}
		err = json.NewEncoder(likedFile).Encode(likes)
		if err != nil {
			srvlog.Crit("Could not encode likes json", "error", err)
		}
	}
}

func getStream(filemap map[string]string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if v, ok := filemap[id]; ok {
			_, err := os.Stat(v)
			if err != nil {
				srvlog.Crit("file missing", "file", v)
				w.WriteHeader(http.StatusNotFound)
				return
			}
			http.ServeFile(w, r, v)
		} else {
			w.WriteHeader(http.StatusNotFound)
			return
		}
	}
}

func getAPI(songs []*song) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		var err error
		switch r.URL.Query().Get("sort") {
		case "bytitle":
			if v, ok := sortcache["bytitle"]; !ok {
				apiMutex.Lock()
				sort.Sort(byTitle(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["bytitle"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by title failed", "error", err)
					delete(sortcache, "bytitle")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byartist":
			if v, ok := sortcache["byartist"]; !ok {
				apiMutex.Lock()
				sort.Sort(byArtist(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byartist"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by artist failed", "error", err)
					delete(sortcache, "byartist")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byalbum":
			if v, ok := sortcache["byalbum"]; !ok {
				apiMutex.Lock()
				sort.Sort(byAlbum(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byalbum"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by album failed", "error", err)
					delete(sortcache, "byalbum")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byshuffle":
			apiMutex.Lock()
			rand.Seed(time.Now().UnixNano())
			for i := len(songs) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byleast":
			apiMutex.Lock()
			sort.Sort(byLeast(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "bymost":
			apiMutex.Lock()
			sort.Sort(sort.Reverse(byLeast(songs)))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byfavorite":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:len(liked)])
			apiMutex.Unlock()
		case "byfavshuffle":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			rand.Seed(time.Now().UnixNano())
			for i := len(songs[0:len(liked)]) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:len(liked)])
			apiMutex.Unlock()
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

func artwork(w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	reg := regexp.MustCompile("^\\w+$")
	if !reg.MatchString(id) {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file := fmt.Sprintf("./artcache/%s", id)
	_, err := os.Stat(file)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	http.ServeFile(w, r, file)
}

func savePlayCount(playcount map[string]int64) {
	playcountFile.Truncate(0)
	enc := json.NewEncoder(playcountFile)
	enc.SetIndent("", " ")
	err := enc.Encode(playcount)
	if err != nil {
		srvlog.Crit("could not encode playcount json", "error", err)
	}
}
<commit_msg>set intend for like json<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"sort"
	"time"
)

type song struct {
	Title  string
	Artist string
	Album  string
	Track  int
	ID     string
	path   string
}

type byTitle []*song
type byArtist []*song
type byAlbum []*song
type byLeast []*song
type byFavorite []*song

func (songs byTitle) Len() int {
	return len(songs)
}

func (songs byArtist) Len() int {
	return len(songs)
}

func (songs byAlbum) Len() int {
	return len(songs)
}

func (songs byLeast) Len() int {
	return len(songs)
}

func (songs byFavorite) Len() int {
	return len(songs)
}

func (songs byTitle) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byArtist) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byAlbum) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byLeast) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byFavorite) Swap(i, j int) {
	songs[i], songs[j] = songs[j], songs[i]
}

func (songs byTitle) Less(i, j int) bool {
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	return false
}

func (songs byArtist) Less(i, j int) bool {
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byAlbum) Less(i, j int) bool {
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byLeast) Less(i, j int) bool {
	var icount, jcount int64
	if v, ok := playcount[songs[i].ID]; ok {
		icount = v
	} else {
		icount = 0
	}
	if v, ok := playcount[songs[j].ID]; ok {
		jcount = v
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount < jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func (songs byFavorite) Less(i, j int) bool {
	var icount, jcount int
	if _, ok := liked[songs[i].ID]; ok {
		icount = 1
	} else {
		icount = 0
	}
	if _, ok := liked[songs[j].ID]; ok {
		jcount = 1
	} else {
		jcount = 0
	}
	if icount != jcount {
		return icount > jcount
	}
	if songs[i].Artist != songs[j].Artist {
		return songs[i].Artist < songs[j].Artist
	}
	if songs[i].Album != songs[j].Album {
		return songs[i].Album < songs[j].Album
	}
	if songs[i].Track != songs[j].Track {
		return songs[i].Track < songs[j].Track
	}
	if songs[i].Title != songs[j].Title {
		return songs[i].Title < songs[j].Title
	}
	return false
}

func countPlay(playcount map[string]int64) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		apiMutex.Lock()
		playcount[id]++
		savePlayCount(playcount)
		apiMutex.Unlock()
	}
}

func likes(likes map[string]bool) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		apiMutex.Lock()
		defer apiMutex.Unlock()
		if id := r.URL.Query().Get("id"); id != "" {
			if v, ok := likes[id]; ok {
				json.NewEncoder(w).Encode(v)
			} else {
				json.NewEncoder(w).Encode(false)
			}
			return
		}
		if like := r.URL.Query().Get("like"); like != "" {
			if v, ok := likes[like]; ok {
				if v == true {
					delete(likes, like)
					json.NewEncoder(w).Encode(false)
				} else {
					likes[like] = true
					json.NewEncoder(w).Encode(true)
				}
			} else {
				likes[like] = true
				json.NewEncoder(w).Encode(true)
			}
		}
		err := likedFile.Truncate(0)
		if err != nil {
			srvlog.Crit("Could not truncate likes file", "error", err)
		}
		enc := json.NewEncoder(likedFile)
		enc.SetIndent("", " ")
		err = enc.Encode(likes)
		if err != nil {
			srvlog.Crit("Could not encode likes json", "error", err)
		}
	}
}

func getStream(filemap map[string]string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		id := r.URL.Query().Get("id")
		if v, ok := filemap[id]; ok {
			_, err := os.Stat(v)
			if err != nil {
				srvlog.Crit("file missing", "file", v)
				w.WriteHeader(http.StatusNotFound)
				return
			}
			http.ServeFile(w, r, v)
		} else {
			w.WriteHeader(http.StatusNotFound)
			return
		}
	}
}

func getAPI(songs []*song) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		var err error
		switch r.URL.Query().Get("sort") {
		case "bytitle":
			if v, ok := sortcache["bytitle"]; !ok {
				apiMutex.Lock()
				sort.Sort(byTitle(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["bytitle"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by title failed", "error", err)
					delete(sortcache, "bytitle")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byartist":
			if v, ok := sortcache["byartist"]; !ok {
				apiMutex.Lock()
				sort.Sort(byArtist(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byartist"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by artist failed", "error", err)
					delete(sortcache, "byartist")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byalbum":
			if v, ok := sortcache["byalbum"]; !ok {
				apiMutex.Lock()
				sort.Sort(byAlbum(songs))
				enc := json.NewEncoder(w)
				enc.SetIndent("", " ")
				enc.Encode(songs)
				sortcache["byalbum"], err = json.MarshalIndent(songs, "", " ")
				apiMutex.Unlock()
				if err != nil {
					srvlog.Crit("marshaling cache by album failed", "error", err)
					delete(sortcache, "byalbum")
				}
			} else {
				io.Copy(w, bytes.NewReader(v))
			}
		case "byshuffle":
			apiMutex.Lock()
			rand.Seed(time.Now().UnixNano())
			for i := len(songs) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byleast":
			apiMutex.Lock()
			sort.Sort(byLeast(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "bymost":
			apiMutex.Lock()
			sort.Sort(sort.Reverse(byLeast(songs)))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs)
			apiMutex.Unlock()
		case "byfavorite":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:len(liked)])
			apiMutex.Unlock()
		case "byfavshuffle":
			apiMutex.Lock()
			sort.Sort(byFavorite(songs))
			rand.Seed(time.Now().UnixNano())
			for i := len(songs[0:len(liked)]) - 1; i > 0; i-- {
				j := rand.Intn(i + 1)
				songs[i], songs[j] = songs[j], songs[i]
			}
			enc := json.NewEncoder(w)
			enc.SetIndent("", " ")
			enc.Encode(songs[0:len(liked)])
			apiMutex.Unlock()
		default:
			w.WriteHeader(http.StatusNotFound)
		}
	}
}

func artwork(w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	reg := regexp.MustCompile("^\\w+$")
	if !reg.MatchString(id) {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	file := fmt.Sprintf("./artcache/%s", id)
	_, err := os.Stat(file)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	http.ServeFile(w, r, file)
}

func savePlayCount(playcount map[string]int64) {
	playcountFile.Truncate(0)
	enc := json.NewEncoder(playcountFile)
	enc.SetIndent("", " ")
	err := enc.Encode(playcount)
	if err != nil {
		srvlog.Crit("could not encode playcount json", "error", err)
	}
}
<|endoftext|>


<commit_before>package rootfs

import (
	"io"
	"io/ioutil"

	"github.com/docker/containerd"
	"github.com/docker/containerd/log"
	"github.com/docker/docker/pkg/archive"
	"github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/identity"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

type Snapshotter interface {
	Prepare(key, parent string) ([]containerd.Mount, error)
	Commit(name, key string) error
	Rollback(key string) error
	Exists(name string) bool
}

type Mounter interface {
	Mount(mounts ...containerd.Mount) error
	Unmount(mounts ...containerd.Mount) error
}

// ApplyLayer applies the layer to the provided parent. The resulting snapshot
// will be stored under its ChainID.
//
// The parent *must* be the chainID of the parent layer.
//
// The returned digest is the diffID for the applied layer.
func ApplyLayer(snapshots Snapshotter, mounter Mounter, rd io.Reader, parent digest.Digest) (digest.Digest, error) {
	digester := digest.Canonical.Digester() // used to calculate diffID.
	rd = io.TeeReader(rd, digester.Hash())

	// create a temporary directory to work from, needs to be on same
	// filesystem. Probably better if this shared but we'll use a tempdir, for
	// now.
	dir, err := ioutil.TempDir("", "unpack-")
	if err != nil {
		return "", errors.Wrapf(err, "creating temporary directory failed")
	}

	// TODO(stevvooe): Choose this key WAY more carefully. We should be able to
	// create collisions for concurrent, conflicting unpack processes but we
	// would need to have it be a function of the parent diffID and child
	// layerID (since we don't know the diffID until we are done!).
	key := dir

	mounts, err := snapshots.Prepare(key, parent.String())
	if err != nil {
		return "", err
	}

	if err := mounter.Mount(mounts...); err != nil {
		if err := snapshots.Rollback(key); err != nil {
			log.L.WithError(err).Error("snapshot rollback failed")
		}
		return "", err
	}
	defer mounter.Unmount(mounts...)

	if _, err := archive.ApplyLayer(key, rd); err != nil {
		return "", err
	}

	diffID := digester.Digest()

	chainID := diffID
	if parent != "" {
		chainID = identity.ChainID([]digest.Digest{parent, chainID})
	}

	return diffID, snapshots.Commit(chainID.String(), key)
}

// Prepare the root filesystem from the set of layers. Snapshots are created
// for each layer if they don't exist, keyed by their chain id. If the snapshot
// already exists, it will be skipped.
//
// If successful, the chainID for the top-level layer is returned. That
// identifier can be used to check out a snapshot.
func Prepare(snapshots Snapshotter, mounter Mounter, layers []ocispec.Descriptor,
	// TODO(stevvooe): The following functions are candidate for internal
	// object functions. We can use these to formulate the beginnings of a
	// rootfs Controller.
	//
	// Just pass them in for now.
	openBlob func(digest.Digest) (io.ReadCloser, error),
	resolveDiffID func(digest.Digest) digest.Digest,
	registerDiffID func(diffID, dgst digest.Digest) error) (digest.Digest, error) {
	var (
		parent digest.Digest
		chain  []digest.Digest
	)

	for _, layer := range layers {
		// TODO: layer.Digest should not be string
		// (https://github.com/opencontainers/image-spec/pull/514)
		layerDigest := digest.Digest(layer.Digest)
		// This will convert a possibly compressed layer hash to the
		// uncompressed hash, if we know about it. If we don't, we unpack and
		// calculate it. If we do have it, we then calculate the chain id for
		// the application and see if the snapshot is there.
		diffID := resolveDiffID(layerDigest)
		if diffID != "" {
			chainLocal := append(chain, diffID)
			chainID := identity.ChainID(chainLocal)

			if snapshots.Exists(chainID.String()) {
				continue
			}
		}

		rc, err := openBlob(layerDigest)
		if err != nil {
			return "", err
		}
		defer rc.Close() // pretty lazy!

		diffID, err = ApplyLayer(snapshots, mounter, rc, parent)
		if err != nil {
			return "", err
		}

		// Register the association between the diffID and the layer's digest.
		// For uncompressed layers, this will be the same. For compressed
		// layers, we can look up the diffID from the digest if we've already
		// unpacked it.
		if err := registerDiffID(diffID, layerDigest); err != nil {
			return "", err
		}

		chain = append(chain, diffID)
		parent = identity.ChainID(chain)
	}

	return parent, nil
}
<commit_msg>Update rootfs to use snapshot interface<commit_after>package rootfs

import (
	"io"
	"io/ioutil"

	"github.com/docker/containerd"
	"github.com/docker/containerd/log"
	"github.com/docker/containerd/snapshot"
	"github.com/docker/docker/pkg/archive"
	"github.com/opencontainers/go-digest"
	"github.com/opencontainers/image-spec/identity"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

type Mounter interface {
	Mount(mounts ...containerd.Mount) error
	Unmount(mounts ...containerd.Mount) error
}

// ApplyLayer applies the layer to the provided parent. The resulting snapshot
// will be stored under its ChainID.
//
// The parent *must* be the chainID of the parent layer.
//
// The returned digest is the diffID for the applied layer.
func ApplyLayer(snapshots snapshot.Snapshotter, mounter Mounter, rd io.Reader, parent digest.Digest) (digest.Digest, error) {
	digester := digest.Canonical.Digester() // used to calculate diffID.
	rd = io.TeeReader(rd, digester.Hash())

	// create a temporary directory to work from, needs to be on same
	// filesystem. Probably better if this shared but we'll use a tempdir, for
	// now.
	dir, err := ioutil.TempDir("", "unpack-")
	if err != nil {
		return "", errors.Wrapf(err, "creating temporary directory failed")
	}

	// TODO(stevvooe): Choose this key WAY more carefully. We should be able to
	// create collisions for concurrent, conflicting unpack processes but we
	// would need to have it be a function of the parent diffID and child
	// layerID (since we don't know the diffID until we are done!).
	key := dir

	mounts, err := snapshots.Prepare(key, parent.String())
	if err != nil {
		return "", err
	}

	if err := mounter.Mount(mounts...); err != nil {
		if err := snapshots.Remove(key); err != nil {
			log.L.WithError(err).Error("snapshot rollback failed")
		}
		return "", err
	}
	defer mounter.Unmount(mounts...)

	if _, err := archive.ApplyLayer(key, rd); err != nil {
		return "", err
	}

	diffID := digester.Digest()

	chainID := diffID
	if parent != "" {
		chainID = identity.ChainID([]digest.Digest{parent, chainID})
	}

	return diffID, snapshots.Commit(chainID.String(), key)
}

// Prepare the root filesystem from the set of layers. Snapshots are created
// for each layer if they don't exist, keyed by their chain id. If the snapshot
// already exists, it will be skipped.
//
// If successful, the chainID for the top-level layer is returned. That
// identifier can be used to check out a snapshot.
func Prepare(snapshots snapshot.Snapshotter, mounter Mounter, layers []ocispec.Descriptor,
	// TODO(stevvooe): The following functions are candidate for internal
	// object functions. We can use these to formulate the beginnings of a
	// rootfs Controller.
	//
	// Just pass them in for now.
	openBlob func(digest.Digest) (io.ReadCloser, error),
	resolveDiffID func(digest.Digest) digest.Digest,
	registerDiffID func(diffID, dgst digest.Digest) error) (digest.Digest, error) {
	var (
		parent digest.Digest
		chain  []digest.Digest
	)

	for _, layer := range layers {
		// TODO: layer.Digest should not be string
		// (https://github.com/opencontainers/image-spec/pull/514)
		layerDigest := digest.Digest(layer.Digest)
		// This will convert a possibly compressed layer hash to the
		// uncompressed hash, if we know about it. If we don't, we unpack and
		// calculate it. If we do have it, we then calculate the chain id for
		// the application and see if the snapshot is there.
		diffID := resolveDiffID(layerDigest)
		if diffID != "" {
			chainLocal := append(chain, diffID)
			chainID := identity.ChainID(chainLocal)

			if _, err := snapshots.Stat(chainID.String()); err == nil {
				continue
			}
		}

		rc, err := openBlob(layerDigest)
		if err != nil {
			return "", err
		}
		defer rc.Close() // pretty lazy!

		diffID, err = ApplyLayer(snapshots, mounter, rc, parent)
		if err != nil {
			return "", err
		}

		// Register the association between the diffID and the layer's digest.
		// For uncompressed layers, this will be the same. For compressed
		// layers, we can look up the diffID from the digest if we've already
		// unpacked it.
		if err := registerDiffID(diffID, layerDigest); err != nil {
			return "", err
		}

		chain = append(chain, diffID)
		parent = identity.ChainID(chain)
	}

	return parent, nil
}
<|endoftext|>


<commit_before>package leetcode

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

func Test_reverseBetween(t *testing.T) {
	assert := assert.New(t)

	head := &ListNode{Val: 1, Next: &ListNode{Val: 2, Next: &ListNode{Val: 3, Next: &ListNode{Val: 4, Next: &ListNode{Val: 5}}}}}
	expected := &ListNode{Val: 1, Next: &ListNode{Val: 4, Next: &ListNode{Val: 3, Next: &ListNode{Val: 2, Next: &ListNode{Val: 5}}}}}
	assert.Equal(expected, reverseBetween(head, 2, 4))
}
<commit_msg>add m==n test<commit_after>package leetcode

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

func Test_reverseBetween(t *testing.T) {
	assert := assert.New(t)

	head := &ListNode{Val: 1, Next: &ListNode{Val: 2, Next: &ListNode{Val: 3, Next: &ListNode{Val: 4, Next: &ListNode{Val: 5}}}}}
	expected := &ListNode{Val: 1, Next: &ListNode{Val: 4, Next: &ListNode{Val: 3, Next: &ListNode{Val: 2, Next: &ListNode{Val: 5}}}}}
	assert.Equal(expected, reverseBetween(head, 2, 4))
	assert.Equal(head, reverseBetween(head, 3, 3))
}
<|endoftext|>


<commit_before>package vm

import (
	"sync"
)

type stack struct {
	Data   []*Pointer
	thread *thread
	// Although every thread has its own stack, vm's main thread still can be accessed by other threads.
	// This is why we need a lock in stack
	// TODO: Find a way to fix this instead of put lock on every stack.
	*sync.RWMutex
}

func (s *stack) set(index int, pointer *Pointer) {
	s.Lock()
	s.Data[index] = pointer
	s.Unlock()
}

func (s *stack) push(v *Pointer) {
	s.Lock()
	defer s.Unlock()

	if len(s.Data) <= s.thread.sp {
		s.Data = append(s.Data, v)
	} else {
		s.Data[s.thread.sp] = v
	}

	if _, ok := v.Target.(*Error); ok {
		cf := s.thread.callFrameStack.top()
		cf.pc = len(cf.instructionSet.instructions)
	}

	s.thread.sp++
}

func (s *stack) pop() *Pointer {
	s.Lock()
	defer s.Unlock()

	if len(s.Data) < 1 {
		panic("Nothing to pop!")
	}

	if s.thread.sp < 0 {
		panic("SP is not normal!")
	}

	if s.thread.sp > 0 {
		s.thread.sp--
	}

	v := s.Data[s.thread.sp]
	s.Data[s.thread.sp] = nil
	return v
}

func (s *stack) top() *Pointer {
	s.RLock()
	defer s.RUnlock()

	if len(s.Data) == 0 {
		return nil
	}

	if s.thread.sp > 0 {
		return s.Data[s.thread.sp-1]
	}

	return s.Data[0]
}
<commit_msg>Print error message and exit program with status 1.<commit_after>package vm

import (
	"fmt"
	"os"
	"sync"
)

type stack struct {
	Data   []*Pointer
	thread *thread
	// Although every thread has its own stack, vm's main thread still can be accessed by other threads.
	// This is why we need a lock in stack
	// TODO: Find a way to fix this instead of put lock on every stack.
	*sync.RWMutex
}

func (s *stack) set(index int, pointer *Pointer) {
	s.Lock()
	s.Data[index] = pointer
	s.Unlock()
}

func (s *stack) push(v *Pointer) {
	s.Lock()
	defer s.Unlock()

	if len(s.Data) <= s.thread.sp {
		s.Data = append(s.Data, v)
	} else {
		s.Data[s.thread.sp] = v
	}

	if err, ok := v.Target.(*Error); ok {
		cf := s.thread.callFrameStack.top()
		cf.pc = len(cf.instructionSet.instructions)

		if s.thread.vm.mode == NormalMode {
			fmt.Println(err.Message)
			os.Exit(1)
		}
	}

	s.thread.sp++
}

func (s *stack) pop() *Pointer {
	s.Lock()
	defer s.Unlock()

	if len(s.Data) < 1 {
		panic("Nothing to pop!")
	}

	if s.thread.sp < 0 {
		panic("SP is not normal!")
	}

	if s.thread.sp > 0 {
		s.thread.sp--
	}

	v := s.Data[s.thread.sp]
	s.Data[s.thread.sp] = nil
	return v
}

func (s *stack) top() *Pointer {
	s.RLock()
	defer s.RUnlock()

	if len(s.Data) == 0 {
		return nil
	}

	if s.thread.sp > 0 {
		return s.Data[s.thread.sp-1]
	}

	return s.Data[0]
}
<|endoftext|>


<commit_before>package main

import (
	"sync/atomic"
	"testing"
	"time"
)

func TestCountingCompletionBarrier(t *testing.T) {
	callsCount := uint64(0)
	expectedCallsCount := uint64(10)
	b := newCountingCompletionBarrier(expectedCallsCount, func() {
		atomic.AddUint64(&callsCount, 1)
	})
	for b.grabWork() {
		b.jobDone()
	}
	if callsCount != expectedCallsCount {
		t.Errorf("Expected to get %v calls, but got %v instead",
			expectedCallsCount, callsCount)
	}
}

func TestCouintingCompletionBarrierWait(t *testing.T) {
	b := newCountingCompletionBarrier(100, func() {})
	go func() {
		for b.grabWork() {
			b.jobDone()
		}
	}()
	wc := make(chan struct{})
	go func() {
		b.wait()
		wc <- struct{}{}
	}()
	select {
	case <-wc:
		return
	case <-time.After(100 * time.Millisecond):
		t.Fail()
	}
}

func TestTimedCompletionBarrier(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}
	tickCount := uint64(0)
	parties := uint64(10)
	duration := 100 * time.Millisecond
	tickDuration := 10 * time.Millisecond
	b := newTimedCompletionBarrier(parties, tickDuration, duration, func() {
		atomic.AddUint64(&tickCount, 1)
	})
	callsCount := uint64(0)
	for i := uint64(0); i < parties; i++ {
		go func() {
			for b.grabWork() {
				atomic.AddUint64(&callsCount, 1)
				b.jobDone()
			}
		}()
	}
	b.wait()
	if callsCount == 0 {
		t.Errorf("Workers haven't done any work")
	}
	if tickCount == 0 {
		t.Errorf("Tick callback wasn't called even once")
	}
}

func TestTimedCompletionBarrierWait(t *testing.T) {
	parties := uint64(10)
	duration := 100 * time.Millisecond
	timeout := duration * 2
	err := 3 * time.Millisecond
	sleepDuration := 2 * time.Millisecond
	tickDuration := 5 * time.Millisecond
	b := newTimedCompletionBarrier(parties, tickDuration, duration, func() {})
	for i := uint64(0); i < parties; i++ {
		go func() {
			for b.grabWork() {
				b.jobDone()
				time.Sleep(sleepDuration)
			}
		}()
	}
	wc := make(chan time.Duration)
	go func() {
		start := time.Now()
		b.wait()
		wc <- time.Since(start)
	}()
	select {
	case actual := <-wc:
		if !approximatelyEqual(duration, actual, sleepDuration+err) {
			t.Errorf("Expected to run %v, but ran %v instead", duration, actual)
		}
	case <-time.After(timeout):
		t.Error("Barrier hanged")
	}
}

func approximatelyEqual(expected, actual, err time.Duration) bool {
	return expected-err < actual && actual < expected+err
}
<commit_msg>Increase error window in TimedCompletionBarrierWait test<commit_after>package main

import (
	"sync/atomic"
	"testing"
	"time"
)

func TestCountingCompletionBarrier(t *testing.T) {
	callsCount := uint64(0)
	expectedCallsCount := uint64(10)
	b := newCountingCompletionBarrier(expectedCallsCount, func() {
		atomic.AddUint64(&callsCount, 1)
	})
	for b.grabWork() {
		b.jobDone()
	}
	if callsCount != expectedCallsCount {
		t.Errorf("Expected to get %v calls, but got %v instead",
			expectedCallsCount, callsCount)
	}
}

func TestCouintingCompletionBarrierWait(t *testing.T) {
	b := newCountingCompletionBarrier(100, func() {})
	go func() {
		for b.grabWork() {
			b.jobDone()
		}
	}()
	wc := make(chan struct{})
	go func() {
		b.wait()
		wc <- struct{}{}
	}()
	select {
	case <-wc:
		return
	case <-time.After(100 * time.Millisecond):
		t.Fail()
	}
}

func TestTimedCompletionBarrier(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}
	tickCount := uint64(0)
	parties := uint64(10)
	duration := 100 * time.Millisecond
	tickDuration := 10 * time.Millisecond
	b := newTimedCompletionBarrier(parties, tickDuration, duration, func() {
		atomic.AddUint64(&tickCount, 1)
	})
	callsCount := uint64(0)
	for i := uint64(0); i < parties; i++ {
		go func() {
			for b.grabWork() {
				atomic.AddUint64(&callsCount, 1)
				b.jobDone()
			}
		}()
	}
	b.wait()
	if callsCount == 0 {
		t.Errorf("Workers haven't done any work")
	}
	if tickCount == 0 {
		t.Errorf("Tick callback wasn't called even once")
	}
}

func TestTimedCompletionBarrierWait(t *testing.T) {
	parties := uint64(10)
	duration := 100 * time.Millisecond
	timeout := duration * 2
	err := 15 * time.Millisecond
	sleepDuration := 2 * time.Millisecond
	tickDuration := 5 * time.Millisecond
	b := newTimedCompletionBarrier(parties, tickDuration, duration, func() {})
	for i := uint64(0); i < parties; i++ {
		go func() {
			for b.grabWork() {
				b.jobDone()
				time.Sleep(sleepDuration)
			}
		}()
	}
	wc := make(chan time.Duration)
	go func() {
		start := time.Now()
		b.wait()
		wc <- time.Since(start)
	}()
	select {
	case actual := <-wc:
		if !approximatelyEqual(duration, actual, sleepDuration+err) {
			t.Errorf("Expected to run %v, but ran %v instead", duration, actual)
		}
	case <-time.After(timeout):
		t.Error("Barrier hanged")
	}
}

func approximatelyEqual(expected, actual, err time.Duration) bool {
	return expected-err < actual && actual < expected+err
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"

	"github.com/alecthomas/kingpin"
	_ "github.com/go-sql-driver/mysql"
)

var (
	DB  *sql.DB
	err error

	httpHost = kingpin.Flag("http-host", "Connect to host").Default("localhost").Short('H').String()
	httpPort = kingpin.Flag("http-port", "Connect to host").Default("9199").Short('P').String()

	mysqlHost = kingpin.Flag("sql-host", "Connect to host").Default("localhost").Short('h').String()
	mysqlUser = kingpin.Flag("sql-user", "User for login").Default("root").Short('u').String()
	mysqlPass = kingpin.Flag("sql-password", "Password to use when connecting to server").Short('p').String()

	donorOk = kingpin.Flag("donor-ok", "treat donor as regular working node").Short('d').Bool()
)

func main() {
	kingpin.Parse()
	DB, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:3306)/mysql", *mysqlUser, *mysqlPass, *mysqlHost),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", statusHandler)
	http.ListenAndServe(*httpHost+":"+*httpPort, nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	err = DB.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = DB.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk == true:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<commit_msg>switch to initflags<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"
	"github.com/vharitonsky/iniflags"
)

var (
	DB  *sql.DB
	err error

	httpHost = flag.String("HTTP_HOST", "localhost", "Connect to host")
	httpPort = flag.String("HTTP_PORT", "9199", "Connect to host")

	sqlHost = flag.String("MYSQL_HOST", "localhost", "Connect to host")
	sqlPort = flag.String("MYSQL_PORT", "3306", "Connect to host")
	sqlUser = flag.String("MYSQL_USER", "root", "User for login to MySQL")
	sqlPass = flag.String("MYSQL_PASS", "", "Password for login to MySQL")

	donorOk = flag.Bool("DONOR_OK", false, "treat donor as regular working node")
)

func main() {
	iniflags.Parse()
	DB, err = sql.Open(
		"mysql",
		fmt.Sprintf("%s:%s@tcp(%s:%s)/mysql", *sqlUser, *sqlPass, *sqlHost, *sqlPort),
	)
	if err != nil {
		log.Fatal(err)
	}
	http.HandleFunc("/", statusHandler)
	http.ListenAndServe(*httpHost+":"+*httpPort, nil)
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	err = DB.Ping()
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	var key string
	var value int64
	err = DB.QueryRow("SHOW STATUS LIKE 'wsrep_local_state'").Scan(&key, &value)
	if err != nil {
		http.Error(w, "Galera Node is *down*. ("+err.Error()+")", 503)
		return
	}

	switch {
	case 4 == value:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	case 2 == value && *donorOk:
		fmt.Fprintf(w, "Galera Node is running.")
		return
	default:
		http.Error(w, "Galera Node is *down*. (State Mismatch)", 503)
		return
	}
}
<|endoftext|>