<commit_before>package main

import (
	"fmt"
	"io"

	"github.com/chlunde/syncr"
)

const (
	ClearAndMoveHome = "\033[2J\033[2H"
	CyanBold         = "\033[36;1m"
	RedBold          = "\033[31;1m"
	GreenBold        = "\033[32;1m"
	ResetColor       = "\033[0m"
)

func Display(buf io.Writer, syncrs []*syncr.Syncr) bool {
	fmt.Fprint(buf, ClearAndMoveHome)

	var anyAlive bool
	for _, s := range syncrs {
		s.Lock.Lock()
		fmt.Fprintln(buf, CyanBold, s.Description)

		var statusColor string
		switch {
		case s.Error != nil:
			statusColor = RedBold
		case s.Dead:
			statusColor = GreenBold
		default:
			statusColor = ResetColor
		}

		fmt.Fprintf(buf, statusColor)

		if s.Error != nil {
			fmt.Fprintf(buf, "Failed: %v\n", s.Error)
		}

		fmt.Fprintln(buf, s.Status[0])

		// Skip last line if it's blank we're not getting any more lines
		// prevents the status from jumping up and down
		if !s.Dead || len(s.Status[1]) > 0 {
			fmt.Fprintln(buf, s.Status[1])
		}

		fmt.Fprint(buf, ResetColor)

		if !s.Dead {
			anyAlive = true
		}
		s.Lock.Unlock()
	}

	return anyAlive
}
<commit_msg>Compact output<commit_after>package main

import (
	"fmt"
	"io"

	"github.com/chlunde/syncr"
)

const (
	ClearAndMoveHome = "\033[2J\033[2H"
	CyanBold         = "\033[36;1m"
	RedBold          = "\033[31;1m"
	GreenBold        = "\033[32;1m"
	ResetColor       = "\033[0m"
)

func Display(buf io.Writer, syncrs []*syncr.Syncr) bool {
	fmt.Fprint(buf, ClearAndMoveHome)

	var anyAlive bool
	for _, s := range syncrs {
		s.Lock.Lock()
		fmt.Fprint(buf, CyanBold, s.Description)

		var statusColor string
		var sep string
		switch {
		case s.Error != nil:
			statusColor = RedBold
			sep = "\n"
		case s.Dead:
			statusColor = GreenBold
			sep = " "
		default:
			statusColor = ResetColor
			sep = "\n"
		}

		fmt.Fprint(buf, statusColor, sep)

		if s.Error != nil {
			fmt.Fprintf(buf, "Failed: %v\n", s.Error)
		}

		fmt.Fprintln(buf, s.Status[0])

		// Skip last line if it's blank we're not getting any more lines
		// prevents the status from jumping up and down
		if !s.Dead || len(s.Status[1]) > 0 {
			fmt.Fprintln(buf, s.Status[1])
		}

		fmt.Fprint(buf, ResetColor)

		if !s.Dead {
			anyAlive = true
		}
		s.Lock.Unlock()
	}

	return anyAlive
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"

	"os"
	"os/signal"
	"syscall"

	"github.com/antihax/evedata/internal/redigohelper"
	"github.com/antihax/evedata/internal/sqlhelper"
	"github.com/antihax/evedata/services/vanguard"
	"github.com/antihax/evedata/services/vanguard/models"
	_ "github.com/antihax/evedata/services/vanguard/views"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/context"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	log.SetPrefix("evedata vanguard: ")

	r := redigohelper.ConnectRedisProdPool()
	db := sqlhelper.NewDatabase()

	// Make a new service and send it into the background.
	vanguard := vanguard.NewVanguard(r, db,
		os.Getenv("ESI_REFRESHKEY"),
		os.Getenv("ESI_CLIENTID_TOKENSTORE"),
		os.Getenv("ESI_SECRET_TOKENSTORE"),
		os.Getenv("ESI_CLIENTID_SSO"),
		os.Getenv("ESI_SECRET_SSO"),
		os.Getenv("COOKIE_SECRET"),
		os.Getenv("DOMAIN"),
	)
	log.Printf("Setup Router\n")
	rtr := vanguard.NewRouter()
	defer vanguard.Close()

	// Handle command line arguments
	if len(os.Args) > 1 {
		if os.Args[1] == "dumpdb" {
			// Dump the database to sql file.
			log.Printf("Dumping Database to evedata.sql\n")
			err := models.DumpDatabase("./sql/evedata.sql", "evedata")
			if err != nil {
				log.Fatalln(err)
			}
		} else if os.Args[1] == "flushcache" {
			// Erase http cache in redis
			log.Printf("Flushing Redis\n")
			conn := r.Get()
			defer conn.Close()
			keys, err := redis.Strings(conn.Do("KEYS", "*rediscache*"))
			if err != nil {
				log.Println(err)
			} else {
				for _, key := range keys {
					conn.Do("DEL", key)
					log.Printf("Deleting %s\n", key)
				}
			}

		} else if os.Args[1] == "flushredis" {
			// Erase everything in redis for modified deployments
			log.Printf("Flushing Redis\n")
			conn := r.Get()
			defer conn.Close()
			conn.Do("FLUSHALL")
		}
	}

	log.Printf("Start Listening\n")
	go log.Fatalln(http.ListenAndServe(":3000", context.ClearHandler(rtr)))

	log.Printf("In production\n")
	// Handle SIGINT and SIGTERM.
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)
}
<commit_msg>Add ability to clear structure failures<commit_after>package main

import (
	"log"
	"net/http"

	"os"
	"os/signal"
	"syscall"

	"github.com/antihax/evedata/internal/redigohelper"
	"github.com/antihax/evedata/internal/sqlhelper"
	"github.com/antihax/evedata/services/vanguard"
	"github.com/antihax/evedata/services/vanguard/models"
	_ "github.com/antihax/evedata/services/vanguard/views"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/context"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	log.SetPrefix("evedata vanguard: ")

	r := redigohelper.ConnectRedisProdPool()
	db := sqlhelper.NewDatabase()

	// Make a new service and send it into the background.
	vanguard := vanguard.NewVanguard(r, db,
		os.Getenv("ESI_REFRESHKEY"),
		os.Getenv("ESI_CLIENTID_TOKENSTORE"),
		os.Getenv("ESI_SECRET_TOKENSTORE"),
		os.Getenv("ESI_CLIENTID_SSO"),
		os.Getenv("ESI_SECRET_SSO"),
		os.Getenv("COOKIE_SECRET"),
		os.Getenv("DOMAIN"),
	)
	log.Printf("Setup Router\n")
	rtr := vanguard.NewRouter()
	defer vanguard.Close()

	// Handle command line arguments
	if len(os.Args) > 1 {
		if os.Args[1] == "dumpdb" {
			// Dump the database to sql file.
			log.Printf("Dumping Database to evedata.sql\n")
			err := models.DumpDatabase("./sql/evedata.sql", "evedata")
			if err != nil {
				log.Fatalln(err)
			}
		} else if os.Args[1] == "flushcache" {
			// Erase http cache in redis
			log.Printf("Flushing Redis\n")
			conn := r.Get()
			defer conn.Close()
			keys, err := redis.Strings(conn.Do("KEYS", "*rediscache*"))
			if err != nil {
				log.Println(err)
			} else {
				for _, key := range keys {
					conn.Do("DEL", key)
					log.Printf("Deleting %s\n", key)
				}
			}
		} else if os.Args[1] == "flushstructures" {
			// Erase http cache in redis
			log.Printf("Flushing structures\n")
			conn := r.Get()
			defer conn.Close()
			keys, err := redis.Strings(conn.Do("KEYS", "*structure_failure*"))
			if err != nil {
				log.Println(err)
			} else {
				for _, key := range keys {
					conn.Do("DEL", key)
					log.Printf("Deleting %s\n", key)
				}
			}

		} else if os.Args[1] == "flushredis" {
			// Erase everything in redis for modified deployments
			log.Printf("Flushing Redis\n")
			conn := r.Get()
			defer conn.Close()
			conn.Do("FLUSHALL")
		}
	}

	log.Printf("Start Listening\n")
	go log.Fatalln(http.ListenAndServe(":3000", context.ClearHandler(rtr)))

	log.Printf("In production\n")
	// Handle SIGINT and SIGTERM.
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	log.Println(<-ch)
}
<|endoftext|>


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

// yoda2rio converts YODA files containing hbook-like values (H1D, H2D, P1D, ...)
// into rio files.
//
// Example:
//
//  $> yoda2rio rivet.yoda >| rivet.rio
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"reflect"
	"strings"

	"go-hep.org/x/hep/hbook"
	"go-hep.org/x/hep/rio"
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("yoda2rio: ")
	log.SetOutput(os.Stderr)

	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			`Usage: yoda2rio [options] <file1.yoda> [<file2.yoda> [...]]

ex:
 $ yoda2rio rivet.yoda >| rivet.rio
`)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		log.Printf("missing input file name")
		flag.Usage()
		flag.PrintDefaults()
	}

	o, err := rio.NewWriter(os.Stdout)
	if err != nil {
		log.Fatal(err)
	}
	defer o.Close()

	for _, fname := range flag.Args() {
		convert(o, fname)
	}
}

func convert(w *rio.Writer, fname string) {
	r, err := os.Open(fname)
	if err != nil {
		log.Fatalf("error opening file [%s]: %v\n", fname, err)
	}
	defer r.Close()

	vs, err := yodaSlice(r)
	if err != nil {
		log.Fatalf("error decoding YODA file [%s]: %v\n", fname, err)
	}

	for _, v := range vs {
		err = w.WriteValue(v.Name(), v.Value())
		if err != nil {
			log.Fatalf("error writing %q from YODA file [%s]: %v\n", v.Name(), fname, err)
		}
	}
}

var (
	yodaHeader = []byte("BEGIN YODA_")
	yodaFooter = []byte("END YODA_")
)

func yodaSlice(r io.Reader) ([]Yoda, error) {
	var (
		err   error
		o     []Yoda
		block = make([]byte, 0, 1024)
		rt    reflect.Type
		name  string
	)
	scan := bufio.NewScanner(r)
	for scan.Scan() {
		raw := scan.Bytes()
		switch {
		case bytes.HasPrefix(raw, yodaHeader):
			rt, name, err = splitHeader(raw)
			if err != nil {
				log.Fatalf("error parsing YODA header: %v", err)
			}
			block = block[:0]
			block = append(block, raw...)
			block = append(block, '\n')

		default:
			block = append(block, raw...)
			block = append(block, '\n')

		case bytes.HasPrefix(raw, yodaFooter):
			block = append(block, raw...)
			block = append(block, '\n')

			v := reflect.New(rt).Elem()
			err = v.Addr().Interface().(unmarshalYoda).UnmarshalYODA(block)
			if err != nil {
				log.Fatalf("error unmarshaling YODA %q (type=%v): %v\n%v\n===\n", name, rt.Name(), err, string(block))
			}
			o = append(o, &yoda{name: name, ptr: v.Addr().Interface()})
		}
	}
	err = scan.Err()
	if err != nil {
		return nil, err
	}
	return o, nil
}

func splitHeader(raw []byte) (reflect.Type, string, error) {
	raw = raw[len(yodaHeader):]
	i := bytes.Index(raw, []byte(" "))
	if i == -1 || i >= len(raw) {
		return nil, "", fmt.Errorf("invalid YODA header (missing space)")
	}

	var rt reflect.Type

	switch string(raw[:i]) {
	case "HISTO1D":
		rt = reflect.TypeOf((*hbook.H1D)(nil)).Elem()
	case "HISTO2D":
		rt = reflect.TypeOf((*hbook.H2D)(nil)).Elem()
	case "PROFILE1D":
		rt = reflect.TypeOf((*hbook.P1D)(nil)).Elem()
	case "SCATTER2D":
		rt = reflect.TypeOf((*hbook.S2D)(nil)).Elem()
	default:
		log.Fatalf("unhandled YODA object type %q", string(raw[:i]))
	}

	name := raw[i+2:] // +2 to also remove the leading '/'
	return rt, strings.TrimSpace(string(name)), nil
}

type Yoda interface {
	Name() string
	Value() interface{}
}

type unmarshalYoda interface {
	UnmarshalYODA([]byte) error
}

type yoda struct {
	name string
	ptr  interface{}
}

func (y *yoda) Name() string {
	return y.name
}

func (y *yoda) Value() interface{} {
	return y.ptr
}
<commit_msg>cmd/yoda2rio: refactor to use hbook/yodacnv<commit_after>// Copyright 2017 The go-hep Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// yoda2rio converts YODA files containing hbook-like values (H1D, H2D, P1D, ...)
// into rio files.
//
// Example:
//
//  $> yoda2rio rivet.yoda >| rivet.rio
package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"go-hep.org/x/hep/hbook/yodacnv"
	"go-hep.org/x/hep/rio"
)

func main() {
	log.SetFlags(0)
	log.SetPrefix("yoda2rio: ")
	log.SetOutput(os.Stderr)

	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			`Usage: yoda2rio [options] <file1.yoda> [<file2.yoda> [...]]

ex:
 $ yoda2rio rivet.yoda >| rivet.rio
`)
	}

	flag.Parse()

	if flag.NArg() < 1 {
		log.Printf("missing input file name")
		flag.Usage()
		flag.PrintDefaults()
	}

	o, err := rio.NewWriter(os.Stdout)
	if err != nil {
		log.Fatal(err)
	}
	defer o.Close()

	for _, fname := range flag.Args() {
		convert(o, fname)
	}
}

func convert(w *rio.Writer, fname string) {
	r, err := os.Open(fname)
	if err != nil {
		log.Fatalf("error opening file [%s]: %v\n", fname, err)
	}
	defer r.Close()

	vs, err := yodacnv.Read(r)
	if err != nil {
		log.Fatalf("error decoding YODA file [%s]: %v\n", fname, err)
	}

	for _, v := range vs {
		err = w.WriteValue(v.Name(), v)
		if err != nil {
			log.Fatalf("error writing %q from YODA file [%s]: %v\n", v.Name(), fname, err)
		}
	}
}
<|endoftext|>


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

package main

import (
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/openconfig/models-ci/commonci"
)

// Fake LabelPoster for testing.
type postLabelRecorder struct {
	labels []string
}

func (p *postLabelRecorder) PostLabel(labelName, labelColor, owner, repo string, prNumber int) error {
	p.labels = append(p.labels, labelName)
	return nil
}

func TestGenOpenConfigValidatorScript(t *testing.T) {
	prNumber = 1
	basicModelMap, err := commonci.ParseOCModels("testdata")
	if err != nil {
		t.Fatalf("TestGenOpenConfigLinterScript: Failed to parse models for testing: %v", err)
	}

	tests := []struct {
		name                 string
		inValidatorName      string
		inModelMap           commonci.OpenConfigModelMap
		inDisabledModelPaths map[string]bool
		wantCmd              string
		wantSkipLabels       []string
		wantErr              bool
	}{{
		name:            "basic pyang",
		inModelMap:      basicModelMap,
		inValidatorName: "pyang",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:                 "basic pyang with model to be skipped",
		inModelMap:           basicModelMap,
		inValidatorName:      "pyang",
		inDisabledModelPaths: map[string]bool{"acl": true, "dne": true},
		wantSkipLabels:       []string{"skipped: acl"},
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic oc-pyang",
		inModelMap:      basicModelMap,
		inValidatorName: "oc-pyang",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/oc-pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
  --openconfig
  --ignore-error=OC_RELATIVE_PATH
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic pyangbind",
		inModelMap:      basicModelMap,
		inValidatorName: "pyangbind",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyangbind
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
  -f pybind
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  local options=( -o "$1"."$2".binding.py "${options[@]}" )
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic goyang-ygot",
		inModelMap:      basicModelMap,
		inValidatorName: "goyang-ygot",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/goyang-ygot
mkdir -p "$workdir"
cmd="/go/bin/generator"
options=(
  -path=testdata,/workspace/third_party/ietf
  -package_name=exampleoc -generate_fakeroot -fakeroot_name=device -compress_paths=true
  -shorten_enum_leaf_names -trim_enum_openconfig_prefix -typedef_enum_with_defmod -enum_suffix_for_simple_union_enums
  -exclude_modules=ietf-interfaces -generate_rename -generate_append -generate_getters
  -generate_leaf_getters -generate_delete -annotations
  -list_builder_key_threshold=3
)
script_options=(
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  outdir=/go/src/"$1"."$2"/
  mkdir "$outdir"
  local options=( -output_file="$outdir"/oc.go "${options[@]}" )
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  status=0
  $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass) || status=1
  $(cd "$outdir" && go get && go build > ${prefix}pass) || status=1
  if [[ $status -eq "1" ]]; then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic yanglint",
		inModelMap:      basicModelMap,
		inValidatorName: "yanglint",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/yanglint
mkdir -p "$workdir"
cmd="yanglint"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic confd",
		inModelMap:      basicModelMap,
		inValidatorName: "confd",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/confd
mkdir -p "$workdir"
status=0
$1 -c --yangpath $2 testdata/acl/openconfig-acl.yang &>> /workspace/results/confd/acl==openconfig-acl==pass || status=1
$1 -c --yangpath $2 testdata/acl/openconfig-acl-evil-twin.yang &>> /workspace/results/confd/acl==openconfig-acl==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/acl==openconfig-acl==pass /workspace/results/confd/acl==openconfig-acl==fail
fi
status=0
$1 -c --yangpath $2 testdata/optical-transport/openconfig-optical-amplifier.yang &>> /workspace/results/confd/optical-transport==openconfig-optical-amplifier==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/optical-transport==openconfig-optical-amplifier==pass /workspace/results/confd/optical-transport==openconfig-optical-amplifier==fail
fi
status=0
$1 -c --yangpath $2 testdata/optical-transport/openconfig-transport-line-protection.yang &>> /workspace/results/confd/optical-transport==openconfig-transport-line-protection==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/optical-transport==openconfig-transport-line-protection==pass /workspace/results/confd/optical-transport==openconfig-transport-line-protection==fail
fi
wait
`,
	}, {
		name:            "basic misc-checks",
		inModelMap:      basicModelMap,
		inValidatorName: "misc-checks",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/misc-checks
mkdir -p "$workdir"
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang > /workspace/results/misc-checks/acl.openconfig-acl.pr-file-parse-log; then
  >&2 echo "parse of acl.openconfig-acl reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-optical-amplifier.yang > /workspace/results/misc-checks/optical-transport.openconfig-optical-amplifier.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-optical-amplifier reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-transport-line-connectivity.yang testdata/optical-transport/openconfig-wavelength-router.yang > /workspace/results/misc-checks/optical-transport.openconfig-wavelength-router.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-wavelength-router reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-transport-line-protection.yang > /workspace/results/misc-checks/optical-transport.openconfig-transport-line-protection.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-transport-line-protection reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-optical-attenuator.yang > /workspace/results/misc-checks/optical-transport.openconfig-optical-attenuator.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-optical-attenuator reported non-zero status."
fi
wait
`,
	}, {
		name:            "unrecognized validatorID",
		inModelMap:      basicModelMap,
		inValidatorName: "foo",
		wantErr:         true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			labelRecorder := &postLabelRecorder{}
			disabledModelPaths = tt.inDisabledModelPaths

			got, err := genOpenConfigValidatorScript(labelRecorder, tt.inValidatorName, "", tt.inModelMap)
			if got := err != nil; got != tt.wantErr {
				t.Fatalf("got error %v,	wantErr: %v", err, tt.wantErr)
			}
			if diff := cmp.Diff(strings.Split(tt.wantCmd, "\n"), strings.Split(got, "\n")); diff != "" {
				t.Errorf("(-want, +got):\n%s", diff)
			}

			if diff := cmp.Diff(tt.wantSkipLabels, labelRecorder.labels); diff != "" {
				t.Errorf("skipped models (-want, +got):\n%s", diff)
			}
		})
	}
}
<commit_msg>debug<commit_after>// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/openconfig/models-ci/commonci"
)

// Fake LabelPoster for testing.
type postLabelRecorder struct {
	labels []string
}

func (p *postLabelRecorder) PostLabel(labelName, labelColor, owner, repo string, prNumber int) error {
	p.labels = append(p.labels, labelName)
	return nil
}

func TestGenOpenConfigValidatorScript(t *testing.T) {
	prNumber = 1
	basicModelMap, err := commonci.ParseOCModels("testdata")
	if err != nil {
		t.Fatalf("TestGenOpenConfigLinterScript: Failed to parse models for testing: %v", err)
	}

	tests := []struct {
		name                 string
		inValidatorName      string
		inModelMap           commonci.OpenConfigModelMap
		inDisabledModelPaths map[string]bool
		wantCmd              string
		wantSkipLabels       []string
		wantErr              bool
	}{{
		name:            "basic pyang",
		inModelMap:      basicModelMap,
		inValidatorName: "pyang",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:                 "basic pyang with model to be skipped",
		inModelMap:           basicModelMap,
		inValidatorName:      "pyang",
		inDisabledModelPaths: map[string]bool{"acl": true, "dne": true},
		wantSkipLabels:       []string{"skipped: acl"},
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic oc-pyang",
		inModelMap:      basicModelMap,
		inValidatorName: "oc-pyang",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/oc-pyang
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
  --openconfig
  --ignore-error=OC_RELATIVE_PATH
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic pyangbind",
		inModelMap:      basicModelMap,
		inValidatorName: "pyangbind",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/pyangbind
mkdir -p "$workdir"
PYANG_MSG_TEMPLATE='messages:{{path:"{file}" line:{line} code:"{code}" type:"{type}" level:{level} message:'"'{msg}'}}"
cmd="$@"
options=(
  -p testdata
  -p /workspace/third_party/ietf
  -f pybind
)
script_options=(
  --msg-template "$PYANG_MSG_TEMPLATE"
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  local options=( -o "$1"."$2".binding.py "${options[@]}" )
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic goyang-ygot",
		inModelMap:      basicModelMap,
		inValidatorName: "goyang-ygot",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/goyang-ygot
mkdir -p "$workdir"
cmd="/go/bin/generator"
options=(
  -path=testdata,/workspace/third_party/ietf
  -package_name=exampleoc -generate_fakeroot -fakeroot_name=device -compress_paths=true
  -shorten_enum_leaf_names -trim_enum_openconfig_prefix -typedef_enum_with_defmod -enum_suffix_for_simple_union_enums
  -exclude_modules=ietf-interfaces -generate_rename -generate_append -generate_getters
  -generate_leaf_getters -generate_delete -annotations
  -list_builder_key_threshold=3
)
script_options=(
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  outdir=/go/src/"$1"."$2"/
  mkdir "$outdir"
  local options=( -output_file="$outdir"/oc.go "${options[@]}" )
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  status=0
  $cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass || status=1
  cd "$outdir" && go get && go build || status=1
  if [[ $status -eq "1" ]]; then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic yanglint",
		inModelMap:      basicModelMap,
		inValidatorName: "yanglint",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/yanglint
mkdir -p "$workdir"
cmd="yanglint"
options=(
  -p testdata
  -p /workspace/third_party/ietf
)
script_options=(
)
function run-dir() {
  declare prefix="$workdir"/"$1"=="$2"==
  shift 2
  echo $cmd "${options[@]}" "$@" > ${prefix}cmd
  if ! $($cmd "${options[@]}" "${script_options[@]}" "$@" &> ${prefix}pass); then
    mv ${prefix}pass ${prefix}fail
  fi
}
run-dir "acl" "openconfig-acl" testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang &
run-dir "optical-transport" "openconfig-optical-amplifier" testdata/optical-transport/openconfig-optical-amplifier.yang &
run-dir "optical-transport" "openconfig-transport-line-protection" testdata/optical-transport/openconfig-transport-line-protection.yang &
wait
`,
	}, {
		name:            "basic confd",
		inModelMap:      basicModelMap,
		inValidatorName: "confd",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/confd
mkdir -p "$workdir"
status=0
$1 -c --yangpath $2 testdata/acl/openconfig-acl.yang &>> /workspace/results/confd/acl==openconfig-acl==pass || status=1
$1 -c --yangpath $2 testdata/acl/openconfig-acl-evil-twin.yang &>> /workspace/results/confd/acl==openconfig-acl==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/acl==openconfig-acl==pass /workspace/results/confd/acl==openconfig-acl==fail
fi
status=0
$1 -c --yangpath $2 testdata/optical-transport/openconfig-optical-amplifier.yang &>> /workspace/results/confd/optical-transport==openconfig-optical-amplifier==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/optical-transport==openconfig-optical-amplifier==pass /workspace/results/confd/optical-transport==openconfig-optical-amplifier==fail
fi
status=0
$1 -c --yangpath $2 testdata/optical-transport/openconfig-transport-line-protection.yang &>> /workspace/results/confd/optical-transport==openconfig-transport-line-protection==pass || status=1
if [[ $status -eq "1" ]]; then
  mv /workspace/results/confd/optical-transport==openconfig-transport-line-protection==pass /workspace/results/confd/optical-transport==openconfig-transport-line-protection==fail
fi
wait
`,
	}, {
		name:            "basic misc-checks",
		inModelMap:      basicModelMap,
		inValidatorName: "misc-checks",
		wantCmd: `#!/bin/bash
workdir=/workspace/results/misc-checks
mkdir -p "$workdir"
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/acl/openconfig-acl.yang testdata/acl/openconfig-acl-evil-twin.yang > /workspace/results/misc-checks/acl.openconfig-acl.pr-file-parse-log; then
  >&2 echo "parse of acl.openconfig-acl reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-optical-amplifier.yang > /workspace/results/misc-checks/optical-transport.openconfig-optical-amplifier.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-optical-amplifier reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-transport-line-connectivity.yang testdata/optical-transport/openconfig-wavelength-router.yang > /workspace/results/misc-checks/optical-transport.openconfig-wavelength-router.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-wavelength-router reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-transport-line-protection.yang > /workspace/results/misc-checks/optical-transport.openconfig-transport-line-protection.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-transport-line-protection reported non-zero status."
fi
if ! /go/bin/ocversion -p testdata,/workspace/third_party/ietf testdata/optical-transport/openconfig-optical-attenuator.yang > /workspace/results/misc-checks/optical-transport.openconfig-optical-attenuator.pr-file-parse-log; then
  >&2 echo "parse of optical-transport.openconfig-optical-attenuator reported non-zero status."
fi
wait
`,
	}, {
		name:            "unrecognized validatorID",
		inModelMap:      basicModelMap,
		inValidatorName: "foo",
		wantErr:         true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			labelRecorder := &postLabelRecorder{}
			disabledModelPaths = tt.inDisabledModelPaths

			got, err := genOpenConfigValidatorScript(labelRecorder, tt.inValidatorName, "", tt.inModelMap)
			if got := err != nil; got != tt.wantErr {
				t.Fatalf("got error %v,	wantErr: %v", err, tt.wantErr)
			}
			if diff := cmp.Diff(strings.Split(tt.wantCmd, "\n"), strings.Split(got, "\n")); diff != "" {
				t.Errorf("(-want, +got):\n%s", diff)
			}

			if diff := cmp.Diff(tt.wantSkipLabels, labelRecorder.labels); diff != "" {
				t.Errorf("skipped models (-want, +got):\n%s", diff)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright © 2015 Steve Francia <spf@spf13.com>.
//
// 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 cmd

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

var cmdDirs = [...]string{"cmd", "cmds", "command", "commands"}
var srcPaths []string

func init() {
	// Initialize srcPaths.
	envGoPath := os.Getenv("GOPATH")
	goPaths := filepath.SplitList(envGoPath)
	if len(goPaths) == 0 {
		er("$GOPATH is not set")
	}
	srcPaths = make([]string, 0, len(goPaths))
	for _, goPath := range goPaths {
		srcPaths = append(srcPaths, filepath.Join(goPath, "src"))
	}
}

func er(msg interface{}) {
	fmt.Println("Error:", msg)
	os.Exit(1)
}

// isEmpty checks if a given path is empty.
// Hidden files in path are ignored.
func isEmpty(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		er(err)
	}

	if !fi.IsDir() {
		return fi.Size() == 0
	}

	f, err := os.Open(path)
	if err != nil {
		er(err)
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil && err != io.EOF {
		er(err)
	}

	for _, name := range names {
		if len(name) > 0 && name[0] != '.' {
			return false
		}
	}
	return true
}

// exists checks if a file or directory exists.
func exists(path string) bool {
	if path == "" {
		return false
	}
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if !os.IsNotExist(err) {
		er(err)
	}
	return false
}

func executeTemplate(tmplStr string, data interface{}) (string, error) {
	tmpl, err := template.New("").Funcs(template.FuncMap{"comment": commentifyString}).Parse(tmplStr)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)
	err = tmpl.Execute(buf, data)
	return buf.String(), err
}

func writeStringToFile(path string, s string) error {
	return writeToFile(path, strings.NewReader(s))
}

// writeToFile writes r to file with path only
// if file/directory on given path doesn't exist.
// If file/directory exists on given path, then
// it terminates app and prints an appropriate error.
func writeToFile(path string, r io.Reader) error {
	if exists(path) {
		return fmt.Errorf("%v already exists", path)
	}

	dir := filepath.Dir(path)
	if dir != "" {
		if err := os.MkdirAll(dir, 0777); err != nil {
			return err
		}
	}

	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(file, r)
	return err
}

// commentfyString comments every line of in.
func commentifyString(in string) string {
	var newlines []string
	lines := strings.Split(in, "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "//") {
			newlines = append(newlines, line)
		} else {
			if line == "" {
				newlines = append(newlines, "//")
			} else {
				newlines = append(newlines, "// "+line)
			}
		}
	}
	return strings.Join(newlines, "\n")
}
<commit_msg>Support default value of $GOPATH (#532)<commit_after>// Copyright © 2015 Steve Francia <spf@spf13.com>.
//
// 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 cmd

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

var cmdDirs = [...]string{"cmd", "cmds", "command", "commands"}
var srcPaths []string

func init() {
	// Initialize srcPaths.
	envGoPath := os.Getenv("GOPATH")
	goPaths := filepath.SplitList(envGoPath)
	if len(goPaths) == 0 {
		// Adapted from https://github.com/Masterminds/glide/pull/798/files.
		// As of Go 1.8 the GOPATH is no longer required to be set. Instead there
		// is a default value. If there is no GOPATH check for the default value.
		// Note, checking the GOPATH first to avoid invoking the go toolchain if
		// possible.

		goExecutable := os.Getenv("COBRA_GO_EXECUTABLE")
		if len(goExecutable) <= 0 {
			goExecutable = "go"
		}

		out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
		if err != nil {
			er(err)
		}

		toolchainGoPath := strings.TrimSpace(string(out))
		goPaths = filepath.SplitList(toolchainGoPath)
		if len(goPaths) == 0 {
			er("$GOPATH is not set")
		}
	}
	srcPaths = make([]string, 0, len(goPaths))
	for _, goPath := range goPaths {
		srcPaths = append(srcPaths, filepath.Join(goPath, "src"))
	}
}

func er(msg interface{}) {
	fmt.Println("Error:", msg)
	os.Exit(1)
}

// isEmpty checks if a given path is empty.
// Hidden files in path are ignored.
func isEmpty(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		er(err)
	}

	if !fi.IsDir() {
		return fi.Size() == 0
	}

	f, err := os.Open(path)
	if err != nil {
		er(err)
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil && err != io.EOF {
		er(err)
	}

	for _, name := range names {
		if len(name) > 0 && name[0] != '.' {
			return false
		}
	}
	return true
}

// exists checks if a file or directory exists.
func exists(path string) bool {
	if path == "" {
		return false
	}
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if !os.IsNotExist(err) {
		er(err)
	}
	return false
}

func executeTemplate(tmplStr string, data interface{}) (string, error) {
	tmpl, err := template.New("").Funcs(template.FuncMap{"comment": commentifyString}).Parse(tmplStr)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)
	err = tmpl.Execute(buf, data)
	return buf.String(), err
}

func writeStringToFile(path string, s string) error {
	return writeToFile(path, strings.NewReader(s))
}

// writeToFile writes r to file with path only
// if file/directory on given path doesn't exist.
// If file/directory exists on given path, then
// it terminates app and prints an appropriate error.
func writeToFile(path string, r io.Reader) error {
	if exists(path) {
		return fmt.Errorf("%v already exists", path)
	}

	dir := filepath.Dir(path)
	if dir != "" {
		if err := os.MkdirAll(dir, 0777); err != nil {
			return err
		}
	}

	file, err := os.Create(path)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(file, r)
	return err
}

// commentfyString comments every line of in.
func commentifyString(in string) string {
	var newlines []string
	lines := strings.Split(in, "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "//") {
			newlines = append(newlines, line)
		} else {
			if line == "" {
				newlines = append(newlines, "//")
			} else {
				newlines = append(newlines, "// "+line)
			}
		}
	}
	return strings.Join(newlines, "\n")
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"flag"
	"encoding/json"
	"io/ioutil"
	"log"
	ph "github.com/advptr/reverse-proxy/proxyhandler"
	"crypto/x509"
)

// Main config
type Config struct {
	TrustFiles []string `json:"trust-files"`
	Routes []ph.Route `json:"routes""`
}

// Program  Options
type Options struct {
	ServerAddress string
	ConfigFile    string
}

// Main Bootstrap
func main() {
	args := args()
	config := config(args.ConfigFile)
	trustedCertPool := config.trustedCertPool()
	for _, r := range config.Routes {
		handler := ph.NewWSHandler(r, trustedCertPool)
		http.HandleFunc(handler.Path(), handler.Handle)
	}
	log.Printf("reverse-proxy on %v\n", args.ServerAddress)

	err := http.ListenAndServe(args.ServerAddress, nil)
	if err != nil {
		panic(err)
	}
}

// Parse command args
func args() Options {
	const (
		defaultServerAddress = ":80"
		serverAddressUsage   = "server address: ':80', '0.0.0.0:8080'..."
		defaultRouteConfig   = "config.json"
		routeConfigUsage     = "configuration file: 'config.json'"
	)
	address := flag.String("address", defaultServerAddress, serverAddressUsage)
	config := flag.String("config", defaultRouteConfig, routeConfigUsage)
	flag.Parse()

	return Options{*address, *config}
}

//
func (c *Config) trustedCertPool() (*x509.CertPool) {
	trustedCertPool := x509.NewCertPool()
	for _, file := range c.TrustFiles {
		trustedCert, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatal(err)
		}
		trustedCertPool.AppendCertsFromPEM(trustedCert);
	}

	return trustedCertPool
}

// Unmarshal routes from JSON configuration file
func config(configFile string) Config {
	file, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatal(err)
	}
	var config Config
	err = json.Unmarshal(file, &config)
	if err != nil {
		log.Fatal(err)
	}
	return config
}

<commit_msg>formatted<commit_after>package main

import (
	"crypto/x509"
	"encoding/json"
	"flag"
	ph "github.com/advptr/reverse-proxy/proxyhandler"
	"io/ioutil"
	"log"
	"net/http"
)

// Main config
type Config struct {
	TrustFiles []string   `json:"trust-files"`
	Routes     []ph.Route `json:"routes""`
}

// Program  Options
type Options struct {
	ServerAddress string
	ConfigFile    string
}

// Main Bootstrap
func main() {
	args := args()
	config := config(args.ConfigFile)
	trustedCertPool := config.trustedCertPool()
	for _, r := range config.Routes {
		handler := ph.NewWSHandler(r, trustedCertPool)
		http.HandleFunc(handler.Path(), handler.Handle)
	}
	log.Printf("reverse-proxy on %v\n", args.ServerAddress)

	err := http.ListenAndServe(args.ServerAddress, nil)
	if err != nil {
		panic(err)
	}
}

// Parse command args
func args() Options {
	const (
		defaultServerAddress = ":80"
		serverAddressUsage   = "server address: ':80', '0.0.0.0:8080'..."
		defaultRouteConfig   = "config.json"
		routeConfigUsage     = "configuration file: 'config.json'"
	)
	address := flag.String("address", defaultServerAddress, serverAddressUsage)
	config := flag.String("config", defaultRouteConfig, routeConfigUsage)
	flag.Parse()

	return Options{*address, *config}
}

//
func (c *Config) trustedCertPool() *x509.CertPool {
	trustedCertPool := x509.NewCertPool()
	for _, file := range c.TrustFiles {
		trustedCert, err := ioutil.ReadFile(file)
		if err != nil {
			log.Fatal(err)
		}
		trustedCertPool.AppendCertsFromPEM(trustedCert)
	}

	return trustedCertPool
}

// Unmarshal routes from JSON configuration file
func config(configFile string) Config {
	file, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatal(err)
	}
	var config Config
	err = json.Unmarshal(file, &config)
	if err != nil {
		log.Fatal(err)
	}
	return config
}
<|endoftext|>


<commit_before>// This file is an example of an S3-pulling plugin.  This is a real-world
// plugin that can actually be used in a production environment (compared to
// the more general but dangerous "external-images" plugin).  This requires you
// to put your AWS access key information into the environment per AWS's
// standard credential management: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
// You may also put access keys in $HOME/.aws/credentials (or
// docker/s3credentials if you're using the docker-compose example override
// setup).  See docker/s3credentials.example for an example credentials file.
//
// When a resource is requested, if its IIIF id begins with "s3:", we treat the
// rest of the id as an s3 id to be pulled from the configured zone and bucket.
// As zone and bucket are configured on the server end, attack vectors seen in
// the external images plugin are effectively nullified.
//
// We assume the asset is already a format RAIS can serve (preferably JP2), and
// we cache it locally with the same extension it has in S3.  The IDToPath
// return is the cached path so that RAIS can use the cached file immediately
// after download.  The JP2 cache is configurable via `S3Cache` in the RAIS
// toml file or by setting `RAIS_S3CACHE` in the environment, and defaults to
// `/var/cache/rais-s3`.
//
// Expiration of cached files must be managed externally (to avoid
// over-complicating this plugin).  A simple approach could be a cron job that
// wipes out all cached data if it hasn't been accessed in the past 24 hours:
//
//     find /var/cache/rais-s3 -type f -atime +1 -exec rm {} \;
//
// Depending how fast the cache grows, how much disk space you have available,
// and how much variety you have in S3, you may want to monitor the cache
// closely and tweak this cron job example as needed, or come up with something
// more sophisticated.

package main

import (
	"errors"
	"hash/fnv"
	"path/filepath"
	"rais/src/iiif"
	"rais/src/plugins"
	"strconv"
	"sync"
	"time"

	"github.com/spf13/viper"
	"github.com/uoregon-libraries/gopkg/fileutil"
	"github.com/uoregon-libraries/gopkg/logger"
)

var downloading = make(map[string]bool)
var m sync.RWMutex

var l *logger.Logger

var s3cache, s3zone, s3bucket string

// Disabled lets the plugin manager know not to add this plugin's functions to
// the global list unless sanity checks in Initialize() pass
var Disabled = true

// Initialize sets up package variables for the s3 pulls and verifies sanity of
// some of the configuration
func Initialize() {
	viper.SetDefault("S3Cache", "/var/local/rais-s3")
	s3cache = viper.GetString("S3Cache")
	s3zone = viper.GetString("S3Zone")
	s3bucket = viper.GetString("S3Bucket")

	if s3zone == "" {
		l.Infof("S3 plugin will not be enabled: S3Zone must be set in rais.toml or RAIS_S3ZONE must be set in the environment")
		return
	}

	if s3bucket == "" {
		l.Infof("S3 plugin will not be enabled: S3Bucket must be set in rais.toml or RAIS_S3BUCKET must be set in the environment")
		return
	}

	l.Debugf("Setting S3 cache location to %q", s3cache)
	l.Debugf("Setting S3 zone to %q", s3zone)
	l.Debugf("Setting S3 bucket to %q", s3bucket)
	Disabled = false

	if fileutil.IsDir(s3cache) {
		return
	}
	if !fileutil.MustNotExist(s3cache) {
		l.Fatalf("S3 plugin failure: %q must not exist or else must be a directory", s3cache)
	}
}

// SetLogger is called by the RAIS server's plugin manager to let plugins use
// the central logger
func SetLogger(raisLogger *logger.Logger) {
	l = raisLogger
}

func buckets(s3ID string) (string, string) {
	var h = fnv.New32()
	h.Write([]byte(s3ID))
	var val = int(h.Sum32() / 10000)
	return strconv.Itoa(val % 100), strconv.Itoa((val / 100) % 100)
}

// IDToPath implements the auto-download logic when a IIIF ID
// starts with "s3:"
func IDToPath(id iiif.ID) (path string, err error) {
	var ids = string(id)
	if len(ids) < 4 {
		return "", plugins.ErrSkipped
	}

	if ids[:3] != "s3:" {
		return "", plugins.ErrSkipped
	}

	// Check cache - don't re-download
	var s3ID = ids[3:]
	var bucket1, bucket2 = buckets(s3ID)
	path = filepath.Join(s3cache, bucket1, bucket2, s3ID)

	// See if this file is currently being downloaded; if so we need to wait
	var timeout = time.Now().Add(time.Second * 10)
	for isDownloading(s3ID) {
		time.Sleep(time.Millisecond * 250)
		if time.Now().After(timeout) {
			return "", errors.New("timed out waiting for s3 download")
		}
	}

	l.Debugf("s3-images plugin: Checking for cached file at %q", path)
	if fileutil.MustNotExist(path) {
		err = pullImage(s3ID, path)
	} else {
		l.Debugf("s3-images plugin: cached file found")
	}

	return path, err
}

func pullImage(s3ID, path string) error {
	setIsDownloading(s3ID)
	defer clearIsDownloading(s3ID)
	return s3download(s3ID, path)
}

func isDownloading(s3ID string) bool {
	m.RLock()
	defer m.RUnlock()
	return downloading[s3ID]
}

func setIsDownloading(s3ID string) {
	m.Lock()
	defer m.Unlock()
	downloading[s3ID] = true
}

func clearIsDownloading(s3ID string) {
	m.Lock()
	defer m.Unlock()
	delete(downloading, s3ID)
}
<commit_msg>plugins/s3-images: log S3 downloads<commit_after>// This file is an example of an S3-pulling plugin.  This is a real-world
// plugin that can actually be used in a production environment (compared to
// the more general but dangerous "external-images" plugin).  This requires you
// to put your AWS access key information into the environment per AWS's
// standard credential management: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
// You may also put access keys in $HOME/.aws/credentials (or
// docker/s3credentials if you're using the docker-compose example override
// setup).  See docker/s3credentials.example for an example credentials file.
//
// When a resource is requested, if its IIIF id begins with "s3:", we treat the
// rest of the id as an s3 id to be pulled from the configured zone and bucket.
// As zone and bucket are configured on the server end, attack vectors seen in
// the external images plugin are effectively nullified.
//
// We assume the asset is already a format RAIS can serve (preferably JP2), and
// we cache it locally with the same extension it has in S3.  The IDToPath
// return is the cached path so that RAIS can use the cached file immediately
// after download.  The JP2 cache is configurable via `S3Cache` in the RAIS
// toml file or by setting `RAIS_S3CACHE` in the environment, and defaults to
// `/var/cache/rais-s3`.
//
// Expiration of cached files must be managed externally (to avoid
// over-complicating this plugin).  A simple approach could be a cron job that
// wipes out all cached data if it hasn't been accessed in the past 24 hours:
//
//     find /var/cache/rais-s3 -type f -atime +1 -exec rm {} \;
//
// Depending how fast the cache grows, how much disk space you have available,
// and how much variety you have in S3, you may want to monitor the cache
// closely and tweak this cron job example as needed, or come up with something
// more sophisticated.

package main

import (
	"errors"
	"hash/fnv"
	"path/filepath"
	"rais/src/iiif"
	"rais/src/plugins"
	"strconv"
	"sync"
	"time"

	"github.com/spf13/viper"
	"github.com/uoregon-libraries/gopkg/fileutil"
	"github.com/uoregon-libraries/gopkg/logger"
)

var downloading = make(map[string]bool)
var m sync.RWMutex

var l *logger.Logger

var s3cache, s3zone, s3bucket string

// Disabled lets the plugin manager know not to add this plugin's functions to
// the global list unless sanity checks in Initialize() pass
var Disabled = true

// Initialize sets up package variables for the s3 pulls and verifies sanity of
// some of the configuration
func Initialize() {
	viper.SetDefault("S3Cache", "/var/local/rais-s3")
	s3cache = viper.GetString("S3Cache")
	s3zone = viper.GetString("S3Zone")
	s3bucket = viper.GetString("S3Bucket")

	if s3zone == "" {
		l.Infof("S3 plugin will not be enabled: S3Zone must be set in rais.toml or RAIS_S3ZONE must be set in the environment")
		return
	}

	if s3bucket == "" {
		l.Infof("S3 plugin will not be enabled: S3Bucket must be set in rais.toml or RAIS_S3BUCKET must be set in the environment")
		return
	}

	l.Debugf("Setting S3 cache location to %q", s3cache)
	l.Debugf("Setting S3 zone to %q", s3zone)
	l.Debugf("Setting S3 bucket to %q", s3bucket)
	Disabled = false

	if fileutil.IsDir(s3cache) {
		return
	}
	if !fileutil.MustNotExist(s3cache) {
		l.Fatalf("S3 plugin failure: %q must not exist or else must be a directory", s3cache)
	}
}

// SetLogger is called by the RAIS server's plugin manager to let plugins use
// the central logger
func SetLogger(raisLogger *logger.Logger) {
	l = raisLogger
}

func buckets(s3ID string) (string, string) {
	var h = fnv.New32()
	h.Write([]byte(s3ID))
	var val = int(h.Sum32() / 10000)
	return strconv.Itoa(val % 100), strconv.Itoa((val / 100) % 100)
}

// IDToPath implements the auto-download logic when a IIIF ID
// starts with "s3:"
func IDToPath(id iiif.ID) (path string, err error) {
	var ids = string(id)
	if len(ids) < 4 {
		return "", plugins.ErrSkipped
	}

	if ids[:3] != "s3:" {
		return "", plugins.ErrSkipped
	}

	// Check cache - don't re-download
	var s3ID = ids[3:]
	var bucket1, bucket2 = buckets(s3ID)
	path = filepath.Join(s3cache, bucket1, bucket2, s3ID)

	// See if this file is currently being downloaded; if so we need to wait
	var timeout = time.Now().Add(time.Second * 10)
	for isDownloading(s3ID) {
		time.Sleep(time.Millisecond * 250)
		if time.Now().After(timeout) {
			return "", errors.New("timed out waiting for s3 download")
		}
	}

	if fileutil.MustNotExist(path) {
		l.Debugf("s3-images plugin: no cached file at %q; downloading from S3", path)
		err = pullImage(s3ID, path)
	}

	return path, err
}

func pullImage(s3ID, path string) error {
	setIsDownloading(s3ID)
	defer clearIsDownloading(s3ID)
	return s3download(s3ID, path)
}

func isDownloading(s3ID string) bool {
	m.RLock()
	defer m.RUnlock()
	return downloading[s3ID]
}

func setIsDownloading(s3ID string) {
	m.Lock()
	defer m.Unlock()
	downloading[s3ID] = true
}

func clearIsDownloading(s3ID string) {
	m.Lock()
	defer m.Unlock()
	delete(downloading, s3ID)
}
<|endoftext|>


<commit_before>package flappy

import (
	neural "github.com/poseidon4o/go-neural/src/neural"
	util "github.com/poseidon4o/go-neural/src/util"
	"math"
	"sort"
)

type NeuronName int

const (
	diffY     NeuronName = iota
	diffX     NeuronName = iota
	velY      NeuronName = iota
	H1        NeuronName = iota
	H2        NeuronName = iota
	H3        NeuronName = iota
	H4        NeuronName = iota
	jump      NeuronName = iota
	NRN_COUNT int        = iota
)

func nrn(name NeuronName) int {
	return int(name)
}

type Flappy struct {
	birds    Flock
	lvl      Level
	drawCb   func(pos, size *util.Vector, color uint32)
	drawSize int
}

func (f *Flappy) Completed() float64 {
	return f.birds[0].bestX / f.lvl.size.X
}

func (f *Flappy) Done() bool {
	return f.birds[0].bestX > f.lvl.pylons[len(f.lvl.pylons)-1].X
}

func (f *Flappy) SetDrawRectCb(cb func(pos, size *util.Vector, color uint32)) {
	f.drawCb = cb
}

func (f *Flappy) LogicTick(dt float64) {
	f.lvl.Step(dt)
	f.checkFlock()
	f.mutateFlock()
	f.thnikFlock()
}

func (f *Flappy) DrawTick() {
	var (
		red   = uint32(0xffff0000)
		green = uint32(0xff00ff00)
		blue  = uint32(0xff0000ff)
	)

	var tl, size util.Vector
	size.X = float64(f.drawSize)
	size.Y = float64(f.drawSize)

	for c := range f.birds {
		f.drawCb(&f.birds[c].bird.Pos, &size, red)
	}

	hSize := float64(PylonHole) / 2.0
	for _, pylon := range f.lvl.pylons {
		// top part
		tl.X = pylon.X
		tl.Y = 0
		size.Y = pylon.Y - hSize
		f.drawCb(&tl, &size, green)

		// bottom part
		tl.Y = pylon.Y + hSize
		size.Y = f.lvl.size.Y - (pylon.Y + hSize)
		f.drawCb(&tl, &size, green)

		// middle point
		tl.Y = pylon.Y
		size.Y = float64(f.drawSize)
		f.drawCb(&tl, &size, blue)
	}
}

func NewFlappy(birdCount int, size *util.Vector) *Flappy {
	level := NewLevel(int(size.X), int(size.Y))

	nets := make([]*neural.Net, birdCount, birdCount)
	for c := range nets {
		nets[c] = neural.NewNet(NRN_COUNT)

		// diffY- to hidden
		*nets[c].Synapse(nrn(diffY), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H4)) = 0.0

		// diffX- to hidden
		*nets[c].Synapse(nrn(diffX), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H4)) = 0.0

		// velY - to hidden
		*nets[c].Synapse(nrn(velY), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H4)) = 0.0

		// hidden to output
		*nets[c].Synapse(nrn(H1), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H2), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H3), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H4), nrn(jump)) = 0.0

		nets[c].Randomize()
	}

	level.AddBirds(birdCount)
	flock := make(Flock, birdCount)
	for c := 0; c < birdCount; c++ {
		flock[c].bird = level.birds[c]
		flock[c].brain = nets[c]
		flock[c].bestX = 0
	}

	return &Flappy{
		birds:    flock,
		lvl:      *level,
		drawCb:   func(pos, size *util.Vector, color uint32) {},
		drawSize: 5,
	}
}

type FBird struct {
	bird  *Bird
	brain *neural.Net
	bestX float64
	dead  bool
}

type Flock []FBird

func (birds Flock) Len() int {
	return len(birds)
}

func (birds Flock) Less(c, r int) bool {
	return birds[c].bestX > birds[r].bestX
}

func (birds Flock) Swap(c, r int) {
	birds[c], birds[r] = birds[r], birds[c]
}

// will check if going from pos to next will collide
func (f *Flappy) checkFlock() {

	// just for readability
	collide := func(aX, bX, cX float64) bool {
		// c.X == d.X
		return aX-1 <= cX && bX+1 >= cX
	}

	hSize := float64(PylonHole / 2)

	for c := range f.birds {
		if f.birds[c].bird.Pos.Y >= f.lvl.size.Y || f.birds[c].bird.Pos.Y < 1 {
			// hit ceeling or floor
			f.birds[c].dead = true
			continue
		}

		pylon := f.lvl.ClosestPylon(&f.birds[c].bird.Pos)
		if f.birds[c].bird.Pos.Y >= pylon.Y-hSize && f.birds[c].bird.Pos.Y <= pylon.Y+hSize {
			// in the pylon hole
			continue
		}

		f.birds[c].dead = collide(f.birds[c].bird.Pos.X, f.birds[c].bird.NextPos.X, pylon.X)
	}

}

func (f *Flappy) thnikFlock() {
	wg := make(chan struct{}, len(f.birds))

	thinkBird := func(c int) {
		next := f.lvl.FirstPylonAfter(&f.birds[c].bird.Pos)
		diffYval := next.Y - f.birds[c].bird.Pos.Y
		diffXval := next.X - f.birds[c].bird.Pos.X

		f.birds[c].brain.Stimulate(nrn(diffY), diffYval)
		f.birds[c].brain.Stimulate(nrn(diffX), diffXval)
		f.birds[c].brain.Stimulate(nrn(velY), f.birds[c].bird.Vel.Y)

		f.birds[c].brain.Step()
		if f.birds[c].brain.ValueOf(nrn(jump)) > 0.75 {
			f.birds[c].bird.Vel.Y = -500
		}

		f.birds[c].brain.Clear()
		wg <- struct{}{}
	}

	for c := 0; c < len(f.birds); c++ {
		go thinkBird(c)
	}

	for c := 0; c < len(f.birds); c++ {
		<-wg
	}
}

func (f *Flappy) mutateFlock() {
	sort.Sort(f.birds)

	randNet := func() *neural.Net {
		return f.birds[int(neural.RandMax(float64(len(f.birds))))].brain
	}

	best := f.birds[0].brain

	for c := range f.birds {
		if f.birds[c].dead {
			f.birds[c].dead = false
			f.birds[c].bird.Pos = *f.lvl.NewBirdPos()
			f.birds[c].bird.Vel = *util.NewVector(SCROLL_SPEED, 0)

			f.birds[c].brain = neural.Cross(best, randNet())

			if neural.Chance(0.1) {
				// penalize best achievement due to mutation
				f.birds[c].bestX *= 0.99
				f.birds[c].brain.Mutate(0.33)
			}
		} else {
			f.birds[c].bird.Pos = f.birds[c].bird.NextPos
			f.birds[c].bestX = math.Max(f.birds[c].bird.Pos.X, f.birds[c].bestX)
		}
	}

}
<commit_msg>Move declarations in the begining<commit_after>package flappy

import (
	neural "github.com/poseidon4o/go-neural/src/neural"
	util "github.com/poseidon4o/go-neural/src/util"
	"math"
	"sort"
)

type NeuronName int

const (
	diffY     NeuronName = iota
	diffX     NeuronName = iota
	velY      NeuronName = iota
	H1        NeuronName = iota
	H2        NeuronName = iota
	H3        NeuronName = iota
	H4        NeuronName = iota
	jump      NeuronName = iota
	NRN_COUNT int        = iota
)

func nrn(name NeuronName) int {
	return int(name)
}

type FBird struct {
	bird  *Bird
	brain *neural.Net
	bestX float64
	dead  bool
}

type Flock []FBird

func (birds Flock) Len() int {
	return len(birds)
}

func (birds Flock) Less(c, r int) bool {
	return birds[c].bestX > birds[r].bestX
}

func (birds Flock) Swap(c, r int) {
	birds[c], birds[r] = birds[r], birds[c]
}

type Flappy struct {
	birds    Flock
	lvl      Level
	drawCb   func(pos, size *util.Vector, color uint32)
	drawSize int
}

func (f *Flappy) Completed() float64 {
	return f.birds[0].bestX / f.lvl.size.X
}

func (f *Flappy) Done() bool {
	return f.birds[0].bestX > f.lvl.pylons[len(f.lvl.pylons)-1].X
}

func (f *Flappy) SetDrawRectCb(cb func(pos, size *util.Vector, color uint32)) {
	f.drawCb = cb
}

func (f *Flappy) LogicTick(dt float64) {
	f.lvl.Step(dt)
	f.checkFlock()
	f.mutateFlock()
	f.thnikFlock()
}

func (f *Flappy) DrawTick() {
	var (
		red   = uint32(0xffff0000)
		green = uint32(0xff00ff00)
		blue  = uint32(0xff0000ff)
	)

	var tl, size util.Vector
	size.X = float64(f.drawSize)
	size.Y = float64(f.drawSize)

	for c := range f.birds {
		f.drawCb(&f.birds[c].bird.Pos, &size, red)
	}

	hSize := float64(PylonHole) / 2.0
	for _, pylon := range f.lvl.pylons {
		// top part
		tl.X = pylon.X
		tl.Y = 0
		size.Y = pylon.Y - hSize
		f.drawCb(&tl, &size, green)

		// bottom part
		tl.Y = pylon.Y + hSize
		size.Y = f.lvl.size.Y - (pylon.Y + hSize)
		f.drawCb(&tl, &size, green)

		// middle point
		tl.Y = pylon.Y
		size.Y = float64(f.drawSize)
		f.drawCb(&tl, &size, blue)
	}
}

func NewFlappy(birdCount int, size *util.Vector) *Flappy {
	level := NewLevel(int(size.X), int(size.Y))

	nets := make([]*neural.Net, birdCount, birdCount)
	for c := range nets {
		nets[c] = neural.NewNet(NRN_COUNT)

		// diffY- to hidden
		*nets[c].Synapse(nrn(diffY), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(diffY), nrn(H4)) = 0.0

		// diffX- to hidden
		*nets[c].Synapse(nrn(diffX), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(diffX), nrn(H4)) = 0.0

		// velY - to hidden
		*nets[c].Synapse(nrn(velY), nrn(H1)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H2)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H3)) = 0.0
		*nets[c].Synapse(nrn(velY), nrn(H4)) = 0.0

		// hidden to output
		*nets[c].Synapse(nrn(H1), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H2), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H3), nrn(jump)) = 0.0
		*nets[c].Synapse(nrn(H4), nrn(jump)) = 0.0

		nets[c].Randomize()
	}

	level.AddBirds(birdCount)
	flock := make(Flock, birdCount)
	for c := 0; c < birdCount; c++ {
		flock[c].bird = level.birds[c]
		flock[c].brain = nets[c]
		flock[c].bestX = 0
	}

	return &Flappy{
		birds:    flock,
		lvl:      *level,
		drawCb:   func(pos, size *util.Vector, color uint32) {},
		drawSize: 5,
	}
}

// will check if going from pos to next will collide
func (f *Flappy) checkFlock() {

	// just for readability
	collide := func(aX, bX, cX float64) bool {
		// c.X == d.X
		return aX-1 <= cX && bX+1 >= cX
	}

	hSize := float64(PylonHole / 2)

	for c := range f.birds {
		if f.birds[c].bird.Pos.Y >= f.lvl.size.Y || f.birds[c].bird.Pos.Y < 1 {
			// hit ceeling or floor
			f.birds[c].dead = true
			continue
		}

		pylon := f.lvl.ClosestPylon(&f.birds[c].bird.Pos)
		if f.birds[c].bird.Pos.Y >= pylon.Y-hSize && f.birds[c].bird.Pos.Y <= pylon.Y+hSize {
			// in the pylon hole
			continue
		}

		f.birds[c].dead = collide(f.birds[c].bird.Pos.X, f.birds[c].bird.NextPos.X, pylon.X)
	}

}

func (f *Flappy) thnikFlock() {
	wg := make(chan struct{}, len(f.birds))

	thinkBird := func(c int) {
		next := f.lvl.FirstPylonAfter(&f.birds[c].bird.Pos)
		diffYval := next.Y - f.birds[c].bird.Pos.Y
		diffXval := next.X - f.birds[c].bird.Pos.X

		f.birds[c].brain.Stimulate(nrn(diffY), diffYval)
		f.birds[c].brain.Stimulate(nrn(diffX), diffXval)
		f.birds[c].brain.Stimulate(nrn(velY), f.birds[c].bird.Vel.Y)

		f.birds[c].brain.Step()
		if f.birds[c].brain.ValueOf(nrn(jump)) > 0.75 {
			f.birds[c].bird.Vel.Y = -500
		}

		f.birds[c].brain.Clear()
		wg <- struct{}{}
	}

	for c := 0; c < len(f.birds); c++ {
		go thinkBird(c)
	}

	for c := 0; c < len(f.birds); c++ {
		<-wg
	}
}

func (f *Flappy) mutateFlock() {
	sort.Sort(f.birds)

	randNet := func() *neural.Net {
		return f.birds[int(neural.RandMax(float64(len(f.birds))))].brain
	}

	best := f.birds[0].brain

	for c := range f.birds {
		if f.birds[c].dead {
			f.birds[c].dead = false
			f.birds[c].bird.Pos = *f.lvl.NewBirdPos()
			f.birds[c].bird.Vel = *util.NewVector(SCROLL_SPEED, 0)

			f.birds[c].brain = neural.Cross(best, randNet())

			if neural.Chance(0.1) {
				// penalize best achievement due to mutation
				f.birds[c].bestX *= 0.99
				f.birds[c].brain.Mutate(0.33)
			}
		} else {
			f.birds[c].bird.Pos = f.birds[c].bird.NextPos
			f.birds[c].bestX = math.Max(f.birds[c].bird.Pos.X, f.birds[c].bestX)
		}
	}

}
<|endoftext|>


<commit_before>package stamp

import (
	"net"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/dedis/prifi/coco"
	"github.com/dedis/prifi/coco/coconet"
	"github.com/dedis/prifi/coco/hashid"
	"github.com/dedis/prifi/coco/proof"
	"github.com/dedis/prifi/coco/sign"
	"github.com/dedis/prifi/coco/test/logutils"
)

type Server struct {
	coco.Signer
	name    string
	Clients map[string]coconet.Conn

	// for aggregating messages from clients
	mux        sync.Mutex
	Queue      [][]MustReplyMessage
	READING    int
	PROCESSING int

	// Leaves, Root and Proof for a round
	Leaves []hashid.HashId // can be removed after we verify protocol
	Root   hashid.HashId
	Proofs []proof.Proof

	rLock     sync.Mutex
	maxRounds int
	closeChan chan bool

	Logger   string
	Hostname string
	App      string
}

func NewServer(signer coco.Signer) *Server {
	s := &Server{}

	s.Clients = make(map[string]coconet.Conn)
	s.Queue = make([][]MustReplyMessage, 2)
	s.READING = 0
	s.PROCESSING = 1

	s.Signer = signer
	s.Signer.RegisterAnnounceFunc(s.OnAnnounce())
	s.Signer.RegisterDoneFunc(s.OnDone())
	s.rLock = sync.Mutex{}

	// listen for client requests at one port higher
	// than the signing node
	h, p, err := net.SplitHostPort(s.Signer.Name())
	if err == nil {
		i, err := strconv.Atoi(p)
		if err != nil {
			log.Fatal(err)
		}
		s.name = net.JoinHostPort(h, strconv.Itoa(i+1))
	}
	s.Queue[s.READING] = make([]MustReplyMessage, 0)
	s.Queue[s.PROCESSING] = make([]MustReplyMessage, 0)
	s.closeChan = make(chan bool, 5)
	return s
}

var clientNumber int = 0

func (s *Server) Close() {
	log.Printf("closing stampserver: %p", s)
	s.closeChan <- true
	s.Signer.Close()
}

// listen for clients connections
// this server needs to be running on a different port
// than the Signer that is beneath it
func (s *Server) Listen() error {
	// log.Println("Listening @ ", s.name)
	ln, err := net.Listen("tcp4", s.name)
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			log.Printf("LISTENING TO CLIENTS: %p", s)
			conn, err := ln.Accept()
			if err != nil {
				// handle error
				log.Errorln("failed to accept connection")
				continue
			}

			c := coconet.NewTCPConnFromNet(conn)
			// log.Println("CLIENT TCP CONNECTION SUCCESSFULLY ESTABLISHED:", c)

			if _, ok := s.Clients[c.Name()]; !ok {
				s.Clients[c.Name()] = c

				go func(c coconet.Conn) {
					for {
						tsm := TimeStampMessage{}
						err := c.Get(&tsm)
						if err != nil {
							log.Errorf("%p Failed to get from child:", s, err)
							s.Close()
							return
						}
						switch tsm.Type {
						default:
							log.Errorf("Message of unknown type: %v\n", tsm.Type)
						case StampRequestType:
							// log.Println("RECEIVED STAMP REQUEST")
							s.mux.Lock()
							READING := s.READING
							s.Queue[READING] = append(s.Queue[READING],
								MustReplyMessage{Tsm: tsm, To: c.Name()})
							s.mux.Unlock()
						}
					}
				}(c)
			}

		}
	}()

	return nil
}

// Used for goconns
// should only be used if clients are created in batch
func (s *Server) ListenToClients() {
	log.Printf("LISTENING TO CLIENTS: %p", s, s.Clients)
	for _, c := range s.Clients {
		go func(c coconet.Conn) {
			for {
				tsm := TimeStampMessage{}
				err := c.Get(&tsm)
				if err == coconet.ErrClosed {
					log.Errorf("%p Failed to get from client:", s, err)
					s.Close()
					return
				}
				if err != nil {
					log.WithFields(log.Fields{
						"file": logutils.File(),
					}).Errorf("%p failed To get message:", s, err)
				}
				switch tsm.Type {
				default:
					log.Errorln("Message of unknown type")
				case StampRequestType:
					// log.Println("STAMP REQUEST")
					s.mux.Lock()
					READING := s.READING
					s.Queue[READING] = append(s.Queue[READING],
						MustReplyMessage{Tsm: tsm, To: c.Name()})
					s.mux.Unlock()
				}
			}
		}(c)
	}
}

func (s *Server) ConnectToLogger() {
	return
	if s.Logger == "" || s.Hostname == "" || s.App == "" {
		log.Println("skipping connect to logger")
		return
	}
	log.Println("Connecting to Logger")
	lh, _ := logutils.NewLoggerHook(s.Logger, s.Hostname, s.App)
	log.Println("Connected to Logger")
	log.AddHook(lh)
}

func (s *Server) LogReRun(nextRole string, curRole string) {
	if nextRole == "root" {
		var messg = s.Name() + " became root"
		if curRole == "root" {
			messg = s.Name() + " remained root"
		}

		go s.ConnectToLogger()

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		log.Printf("role change: %p", s)

	} else {
		var messg = s.Name() + " remained regular"
		if curRole == "root" {
			messg = s.Name() + " became regular"
		}

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		log.Printf("role change: %p", s)

	}

}

func (s *Server) runAsRoot(nRounds int) string {
	// every 5 seconds start a new round
	ticker := time.Tick(ROUND_TIME)
	log.Infoln(s.Name(), "running as root", s.LastRound(), int64(nRounds))

	for {
		select {
		case nextRole := <-s.ViewChangeCh():
			return nextRole
			// s.reRunWith(nextRole, nRounds, true)
		case <-ticker:

			start := time.Now()
			log.Println(s.Name(), "is STAMP SERVER STARTING SIGNING ROUND FOR:", s.LastRound()+1, "of", nRounds)

			err := s.StartSigningRound()
			if err == sign.ChangingViewError {
				// report change in view, and continue with the select
				log.WithFields(log.Fields{
					"file": logutils.File(),
					"type": "view_change",
				}).Info("Tried to stary signing round on " + s.Name() + " but it reports view change in progress")
				// skip # of failed round
				// s.SetLastSeenRound(s.LastRound() + 1)
				break
			}

			if err != nil {
				log.Errorln(err)
				return ""
			}

			elapsed := time.Since(start)
			log.WithFields(log.Fields{
				"file":  logutils.File(),
				"type":  "root_round",
				"round": s.LastRound(),
				"time":  elapsed,
			}).Info("root round")

			if s.LastRound() >= int64(nRounds) {
				log.Errorln(s.Name(), "reports exceeded the max round: terminating", s.LastRound(), ">=", nRounds)
				return "close"
			}
		}
	}
}

func (s *Server) runAsRegular() string {
	select {
	case <-s.closeChan:
		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "close",
		}).Infoln("server" + s.Name() + "has closed")
		return ""

	case nextRole := <-s.ViewChangeCh():
		return nextRole
	}
}

// Listen on client connections. If role is root also send annoucement
// for all of the nRounds
func (s *Server) Run(role string, nRounds int) {
	// defer func() {
	// 	log.Infoln(s.Name(), "CLOSE AFTER RUN")
	// 	s.Close()
	// }()
	go func() { err := s.Signer.Listen(); s.Close(); log.Error(err) }()

	s.rLock.Lock()
	s.maxRounds = nRounds
	s.rLock.Unlock()

	var nextRole string // next role when view changes
	for {
		switch role {

		case "root":
			nextRole = s.runAsRoot(nRounds)
		case "regular":
			nextRole = s.runAsRegular()
		case "test":
			ticker := time.Tick(2000 * time.Millisecond)
			for _ = range ticker {
				s.AggregateCommits(0)
			}
		}

		log.Println(s.Name(), "nextRole: ", nextRole)
		if nextRole == "close" {
			s.Close()
			return
		}
		if nextRole == "" {
			return
		}
		s.LogReRun(nextRole, role)
		role = nextRole
	}

}

func (s *Server) OnAnnounce() coco.CommitFunc {
	return func(view int) []byte {
		//log.Println("Aggregating Commits")
		return s.AggregateCommits(view)
	}
}

func (s *Server) OnDone() coco.DoneFunc {
	return func(view int, SNRoot hashid.HashId, LogHash hashid.HashId, p proof.Proof) {
		s.mux.Lock()
		for i, msg := range s.Queue[s.PROCESSING] {
			// proof to get from s.Root to big root
			combProof := make(proof.Proof, len(p))
			copy(combProof, p)

			// add my proof to get from a leaf message to my root s.Root
			combProof = append(combProof, s.Proofs[i]...)

			// proof that i can get from a leaf message to the big root
			if coco.DEBUG == true {
				proof.CheckProof(s.Signer.(*sign.Node).Suite().Hash, SNRoot, s.Leaves[i], combProof)
			}

			respMessg := TimeStampMessage{
				Type:  StampReplyType,
				ReqNo: msg.Tsm.ReqNo,
				Srep:  &StampReply{Sig: SNRoot, Prf: combProof}}

			s.PutToClient(msg.To, respMessg)
		}
		s.mux.Unlock()
	}

}

func (s *Server) AggregateCommits(view int) []byte {
	//log.Println(s.Name(), "calling AggregateCommits")
	s.mux.Lock()
	// get data from s once to avoid refetching from structure
	Queue := s.Queue
	READING := s.READING
	PROCESSING := s.PROCESSING
	// messages read will now be processed
	READING, PROCESSING = PROCESSING, READING
	s.READING, s.PROCESSING = s.PROCESSING, s.READING
	s.Queue[READING] = s.Queue[READING][:0]

	// give up if nothing to process
	if len(Queue[PROCESSING]) == 0 {
		s.mux.Unlock()
		s.Root = make([]byte, hashid.Size)
		s.Proofs = make([]proof.Proof, 1)
		return s.Root
	}

	// pull out to be Merkle Tree leaves
	s.Leaves = make([]hashid.HashId, 0)
	for _, msg := range Queue[PROCESSING] {
		s.Leaves = append(s.Leaves, hashid.HashId(msg.Tsm.Sreq.Val))
	}
	s.mux.Unlock()

	// non root servers keep track of rounds here
	if !s.IsRoot(view) {
		s.rLock.Lock()
		lsr := s.LastRound()
		mr := s.maxRounds
		s.rLock.Unlock()
		// if this is our last round then close the connections
		if lsr >= int64(mr) && mr >= 0 {
			s.closeChan <- true
		}
	}

	// create Merkle tree for this round's messages and check corectness
	s.Root, s.Proofs = proof.ProofTree(s.Suite().Hash, s.Leaves)
	if coco.DEBUG == true {
		if proof.CheckLocalProofs(s.Suite().Hash, s.Root, s.Leaves, s.Proofs) == true {
			log.Println("Local Proofs of", s.Name(), "successful for round "+strconv.Itoa(int(s.LastRound())))
		} else {
			panic("Local Proofs" + s.Name() + " unsuccessful for round " + strconv.Itoa(int(s.LastRound())))
		}
	}

	return s.Root
}

// Send message to client given by name
func (s *Server) PutToClient(name string, data coconet.BinaryMarshaler) {
	err := s.Clients[name].Put(data)
	if err == coconet.ErrClosed {
		s.Close()
		return
	}
	if err != nil && err != coconet.ErrNotEstablished {
		log.Warnf("%p error putting to client: %v", s, err)
	}
}
<commit_msg>don't exit if stamp timed out<commit_after>package stamp

import (
	"net"
	"strconv"
	"sync"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/dedis/prifi/coco"
	"github.com/dedis/prifi/coco/coconet"
	"github.com/dedis/prifi/coco/hashid"
	"github.com/dedis/prifi/coco/proof"
	"github.com/dedis/prifi/coco/sign"
	"github.com/dedis/prifi/coco/test/logutils"
)

type Server struct {
	coco.Signer
	name    string
	Clients map[string]coconet.Conn

	// for aggregating messages from clients
	mux        sync.Mutex
	Queue      [][]MustReplyMessage
	READING    int
	PROCESSING int

	// Leaves, Root and Proof for a round
	Leaves []hashid.HashId // can be removed after we verify protocol
	Root   hashid.HashId
	Proofs []proof.Proof

	rLock     sync.Mutex
	maxRounds int
	closeChan chan bool

	Logger   string
	Hostname string
	App      string
}

func NewServer(signer coco.Signer) *Server {
	s := &Server{}

	s.Clients = make(map[string]coconet.Conn)
	s.Queue = make([][]MustReplyMessage, 2)
	s.READING = 0
	s.PROCESSING = 1

	s.Signer = signer
	s.Signer.RegisterAnnounceFunc(s.OnAnnounce())
	s.Signer.RegisterDoneFunc(s.OnDone())
	s.rLock = sync.Mutex{}

	// listen for client requests at one port higher
	// than the signing node
	h, p, err := net.SplitHostPort(s.Signer.Name())
	if err == nil {
		i, err := strconv.Atoi(p)
		if err != nil {
			log.Fatal(err)
		}
		s.name = net.JoinHostPort(h, strconv.Itoa(i+1))
	}
	s.Queue[s.READING] = make([]MustReplyMessage, 0)
	s.Queue[s.PROCESSING] = make([]MustReplyMessage, 0)
	s.closeChan = make(chan bool, 5)
	return s
}

var clientNumber int = 0

func (s *Server) Close() {
	log.Printf("closing stampserver: %p", s)
	s.closeChan <- true
	s.Signer.Close()
}

// listen for clients connections
// this server needs to be running on a different port
// than the Signer that is beneath it
func (s *Server) Listen() error {
	// log.Println("Listening @ ", s.name)
	ln, err := net.Listen("tcp4", s.name)
	if err != nil {
		panic(err)
	}

	go func() {
		for {
			log.Printf("LISTENING TO CLIENTS: %p", s)
			conn, err := ln.Accept()
			if err != nil {
				// handle error
				log.Errorln("failed to accept connection")
				continue
			}

			c := coconet.NewTCPConnFromNet(conn)
			// log.Println("CLIENT TCP CONNECTION SUCCESSFULLY ESTABLISHED:", c)

			if _, ok := s.Clients[c.Name()]; !ok {
				s.Clients[c.Name()] = c

				go func(c coconet.Conn) {
					for {
						tsm := TimeStampMessage{}
						err := c.Get(&tsm)
						if err != nil {
							log.Errorf("%p Failed to get from child:", s, err)
							s.Close()
							return
						}
						switch tsm.Type {
						default:
							log.Errorf("Message of unknown type: %v\n", tsm.Type)
						case StampRequestType:
							// log.Println("RECEIVED STAMP REQUEST")
							s.mux.Lock()
							READING := s.READING
							s.Queue[READING] = append(s.Queue[READING],
								MustReplyMessage{Tsm: tsm, To: c.Name()})
							s.mux.Unlock()
						}
					}
				}(c)
			}

		}
	}()

	return nil
}

// Used for goconns
// should only be used if clients are created in batch
func (s *Server) ListenToClients() {
	log.Printf("LISTENING TO CLIENTS: %p", s, s.Clients)
	for _, c := range s.Clients {
		go func(c coconet.Conn) {
			for {
				tsm := TimeStampMessage{}
				err := c.Get(&tsm)
				if err == coconet.ErrClosed {
					log.Errorf("%p Failed to get from client:", s, err)
					s.Close()
					return
				}
				if err != nil {
					log.WithFields(log.Fields{
						"file": logutils.File(),
					}).Errorf("%p failed To get message:", s, err)
				}
				switch tsm.Type {
				default:
					log.Errorln("Message of unknown type")
				case StampRequestType:
					// log.Println("STAMP REQUEST")
					s.mux.Lock()
					READING := s.READING
					s.Queue[READING] = append(s.Queue[READING],
						MustReplyMessage{Tsm: tsm, To: c.Name()})
					s.mux.Unlock()
				}
			}
		}(c)
	}
}

func (s *Server) ConnectToLogger() {
	return
	if s.Logger == "" || s.Hostname == "" || s.App == "" {
		log.Println("skipping connect to logger")
		return
	}
	log.Println("Connecting to Logger")
	lh, _ := logutils.NewLoggerHook(s.Logger, s.Hostname, s.App)
	log.Println("Connected to Logger")
	log.AddHook(lh)
}

func (s *Server) LogReRun(nextRole string, curRole string) {
	if nextRole == "root" {
		var messg = s.Name() + " became root"
		if curRole == "root" {
			messg = s.Name() + " remained root"
		}

		go s.ConnectToLogger()

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		log.Printf("role change: %p", s)

	} else {
		var messg = s.Name() + " remained regular"
		if curRole == "root" {
			messg = s.Name() + " became regular"
		}

		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "role_change",
		}).Infoln(messg)
		log.Printf("role change: %p", s)

	}

}

func (s *Server) runAsRoot(nRounds int) string {
	// every 5 seconds start a new round
	ticker := time.Tick(ROUND_TIME)
	log.Infoln(s.Name(), "running as root", s.LastRound(), int64(nRounds))

	for {
		select {
		case nextRole := <-s.ViewChangeCh():
			return nextRole
			// s.reRunWith(nextRole, nRounds, true)
		case <-ticker:

			start := time.Now()
			log.Println(s.Name(), "is STAMP SERVER STARTING SIGNING ROUND FOR:", s.LastRound()+1, "of", nRounds)

			err := s.StartSigningRound()
			if err == sign.ChangingViewError {
				// report change in view, and continue with the select
				log.WithFields(log.Fields{
					"file": logutils.File(),
					"type": "view_change",
				}).Info("Tried to stary signing round on " + s.Name() + " but it reports view change in progress")
				// skip # of failed round
				// s.SetLastSeenRound(s.LastRound() + 1)
				break
			}

			if err != nil {
				log.Errorln(err)
				break
			}

			elapsed := time.Since(start)
			log.WithFields(log.Fields{
				"file":  logutils.File(),
				"type":  "root_round",
				"round": s.LastRound(),
				"time":  elapsed,
			}).Info("root round")

			if s.LastRound() >= int64(nRounds) {
				log.Errorln(s.Name(), "reports exceeded the max round: terminating", s.LastRound(), ">=", nRounds)
				return "close"
			}
		}
	}
}

func (s *Server) runAsRegular() string {
	select {
	case <-s.closeChan:
		log.WithFields(log.Fields{
			"file": logutils.File(),
			"type": "close",
		}).Infoln("server" + s.Name() + "has closed")
		return ""

	case nextRole := <-s.ViewChangeCh():
		return nextRole
	}
}

// Listen on client connections. If role is root also send annoucement
// for all of the nRounds
func (s *Server) Run(role string, nRounds int) {
	// defer func() {
	// 	log.Infoln(s.Name(), "CLOSE AFTER RUN")
	// 	s.Close()
	// }()
	go func() { err := s.Signer.Listen(); s.Close(); log.Error(err) }()

	s.rLock.Lock()
	s.maxRounds = nRounds
	s.rLock.Unlock()

	var nextRole string // next role when view changes
	for {
		switch role {

		case "root":
			nextRole = s.runAsRoot(nRounds)
		case "regular":
			nextRole = s.runAsRegular()
		case "test":
			ticker := time.Tick(2000 * time.Millisecond)
			for _ = range ticker {
				s.AggregateCommits(0)
			}
		}

		log.Println(s.Name(), "nextRole: ", nextRole)
		if nextRole == "close" {
			s.Close()
			return
		}
		if nextRole == "" {
			return
		}
		s.LogReRun(nextRole, role)
		role = nextRole
	}

}

func (s *Server) OnAnnounce() coco.CommitFunc {
	return func(view int) []byte {
		//log.Println("Aggregating Commits")
		return s.AggregateCommits(view)
	}
}

func (s *Server) OnDone() coco.DoneFunc {
	return func(view int, SNRoot hashid.HashId, LogHash hashid.HashId, p proof.Proof) {
		s.mux.Lock()
		for i, msg := range s.Queue[s.PROCESSING] {
			// proof to get from s.Root to big root
			combProof := make(proof.Proof, len(p))
			copy(combProof, p)

			// add my proof to get from a leaf message to my root s.Root
			combProof = append(combProof, s.Proofs[i]...)

			// proof that i can get from a leaf message to the big root
			if coco.DEBUG == true {
				proof.CheckProof(s.Signer.(*sign.Node).Suite().Hash, SNRoot, s.Leaves[i], combProof)
			}

			respMessg := TimeStampMessage{
				Type:  StampReplyType,
				ReqNo: msg.Tsm.ReqNo,
				Srep:  &StampReply{Sig: SNRoot, Prf: combProof}}

			s.PutToClient(msg.To, respMessg)
		}
		s.mux.Unlock()
	}

}

func (s *Server) AggregateCommits(view int) []byte {
	//log.Println(s.Name(), "calling AggregateCommits")
	s.mux.Lock()
	// get data from s once to avoid refetching from structure
	Queue := s.Queue
	READING := s.READING
	PROCESSING := s.PROCESSING
	// messages read will now be processed
	READING, PROCESSING = PROCESSING, READING
	s.READING, s.PROCESSING = s.PROCESSING, s.READING
	s.Queue[READING] = s.Queue[READING][:0]

	// give up if nothing to process
	if len(Queue[PROCESSING]) == 0 {
		s.mux.Unlock()
		s.Root = make([]byte, hashid.Size)
		s.Proofs = make([]proof.Proof, 1)
		return s.Root
	}

	// pull out to be Merkle Tree leaves
	s.Leaves = make([]hashid.HashId, 0)
	for _, msg := range Queue[PROCESSING] {
		s.Leaves = append(s.Leaves, hashid.HashId(msg.Tsm.Sreq.Val))
	}
	s.mux.Unlock()

	// non root servers keep track of rounds here
	if !s.IsRoot(view) {
		s.rLock.Lock()
		lsr := s.LastRound()
		mr := s.maxRounds
		s.rLock.Unlock()
		// if this is our last round then close the connections
		if lsr >= int64(mr) && mr >= 0 {
			s.closeChan <- true
		}
	}

	// create Merkle tree for this round's messages and check corectness
	s.Root, s.Proofs = proof.ProofTree(s.Suite().Hash, s.Leaves)
	if coco.DEBUG == true {
		if proof.CheckLocalProofs(s.Suite().Hash, s.Root, s.Leaves, s.Proofs) == true {
			log.Println("Local Proofs of", s.Name(), "successful for round "+strconv.Itoa(int(s.LastRound())))
		} else {
			panic("Local Proofs" + s.Name() + " unsuccessful for round " + strconv.Itoa(int(s.LastRound())))
		}
	}

	return s.Root
}

// Send message to client given by name
func (s *Server) PutToClient(name string, data coconet.BinaryMarshaler) {
	err := s.Clients[name].Put(data)
	if err == coconet.ErrClosed {
		s.Close()
		return
	}
	if err != nil && err != coconet.ErrNotEstablished {
		log.Warnf("%p error putting to client: %v", s, err)
	}
}
<|endoftext|>