<commit_before>// +build linux

package mysql

import (
	"database/sql"
	"encoding/json"
	"os"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/jmoiron/sqlx"
	mysqltest "github.com/lestrrat-go/test-mysqld"
	"github.com/stretchr/testify/assert"

	"github.com/ajvb/kala/job"
)

func NewTestDb() (*DB, sqlmock.Sqlmock) {
	connection, m, _ := sqlmock.New()

	var db = &DB{
		conn: sqlx.NewDb(connection, "sqlmock"),
	}
	return db, m
}

func TestSaveAndGetJob(t *testing.T) {
	db, m := NewTestDb()

	cache := job.NewLockFreeJobCache(db)
	defer db.Close()

	genericMockJob := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJob.Init(cache)

	j, err := json.Marshal(genericMockJob)
	if assert.NoError(t, err) {
		m.ExpectBegin()
		m.ExpectPrepare("replace .*").
			ExpectExec().
			WithArgs(genericMockJob.Id, string(j)).
			WillReturnResult(sqlmock.NewResult(1, 1))
		m.ExpectCommit()
		err := db.Save(genericMockJob)
		if assert.NoError(t, err) {
			m.ExpectQuery("select .*").
				WithArgs(genericMockJob.Id).
				WillReturnRows(sqlmock.NewRows([]string{"job"}).AddRow(j))
			j2, err := db.Get(genericMockJob.Id)
			if assert.Nil(t, err) {
				assert.WithinDuration(t, j2.NextRunAt, genericMockJob.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, j2.Name, genericMockJob.Name)
				assert.Equal(t, j2.Id, genericMockJob.Id)
				assert.Equal(t, j2.Command, genericMockJob.Command)
				assert.Equal(t, j2.Schedule, genericMockJob.Schedule)
				assert.Equal(t, j2.Owner, genericMockJob.Owner)
				assert.Equal(t, j2.Metadata.SuccessCount, genericMockJob.Metadata.SuccessCount)
			}
		}
	}
}

func TestDeleteJob(t *testing.T) {
	db, m := NewTestDb()

	cache := job.NewLockFreeJobCache(db)

	genericMockJob := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJob.Init(cache)

	j, err := json.Marshal(genericMockJob)
	if assert.NoError(t, err) {

		m.ExpectBegin()
		m.ExpectPrepare("replace .*").
			ExpectExec().
			WithArgs(genericMockJob.Id, string(j)).
			WillReturnResult(sqlmock.NewResult(1, 1))
		m.ExpectCommit()

		err := db.Save(genericMockJob)
		if assert.NoError(t, err) {

			// Delete it
			m.ExpectExec("delete .*").
				WithArgs(genericMockJob.Id).
				WillReturnResult(sqlmock.NewResult(1, 1))

			err = db.Delete(genericMockJob.Id)
			assert.Nil(t, err)

			// Verify deletion
			m.ExpectQuery("select .*").
				WithArgs(genericMockJob.Id).
				WillReturnError(sql.ErrNoRows)

			k, err := db.Get(genericMockJob.Id)
			assert.Error(t, err)
			assert.Nil(t, k)
		}
	}

}

func TestSaveAndGetAllJobs(t *testing.T) {
	db, m := NewTestDb()

	genericMockJobOne := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJobTwo := job.GetMockJobWithGenericSchedule(time.Now())

	jobOne, err := json.Marshal(genericMockJobOne)
	if assert.NoError(t, err) {
		jobTwo, err := json.Marshal(genericMockJobTwo)
		if assert.NoError(t, err) {

			m.ExpectQuery(".*").WillReturnRows(sqlmock.NewRows([]string{"jobs"}).AddRow(jobOne).AddRow(jobTwo))

			jobs, err := db.GetAll()
			assert.Nil(t, err)
			assert.Equal(t, 2, len(jobs))
		}
	}

}

func TestRealDb(t *testing.T) {

	dsn := os.Getenv("MYSQL_DSN")
	if dsn == "" {
		mysqld, err := mysqltest.NewMysqld(nil)
		if assert.NoError(t, err) {
			dsn = mysqld.Datasource("test", "", "", 0)
			defer mysqld.Stop()
		} else {
			t.FailNow()
		}
	}

	db := New(dsn, nil)
	cache := job.NewLockFreeJobCache(db)

	genericMockJobOne := job.GetMockJobWithGenericSchedule(time.Now().Add(time.Hour))
	genericMockJobTwo := job.GetMockJobWithGenericSchedule(time.Now().Add(time.Hour))

	genericMockJobOne.Init(cache)
	genericMockJobTwo.Init(cache)

	t.Logf("Mock job one: %s", genericMockJobOne.Id)
	t.Logf("Mock job two: %s", genericMockJobTwo.Id)

	err := db.Save(genericMockJobOne)
	if assert.NoError(t, err) {

		err := db.Save(genericMockJobTwo)
		if assert.NoError(t, err) {

			jobs, err := db.GetAll()
			if assert.NoError(t, err) {

				assert.Equal(t, 2, len(jobs))

				if jobs[0].Id == genericMockJobTwo.Id {
					jobs[0], jobs[1] = jobs[1], jobs[0]
				}

				assert.WithinDuration(t, jobs[0].NextRunAt, genericMockJobOne.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, jobs[0].Name, genericMockJobOne.Name)
				assert.Equal(t, jobs[0].Id, genericMockJobOne.Id)
				assert.Equal(t, jobs[0].Command, genericMockJobOne.Command)
				assert.Equal(t, jobs[0].Schedule, genericMockJobOne.Schedule)
				assert.Equal(t, jobs[0].Owner, genericMockJobOne.Owner)
				assert.Equal(t, jobs[0].Metadata.SuccessCount, genericMockJobOne.Metadata.SuccessCount)

				assert.WithinDuration(t, jobs[1].NextRunAt, genericMockJobTwo.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, jobs[1].Name, genericMockJobTwo.Name)
				assert.Equal(t, jobs[1].Id, genericMockJobTwo.Id)
				assert.Equal(t, jobs[1].Command, genericMockJobTwo.Command)
				assert.Equal(t, jobs[1].Schedule, genericMockJobTwo.Schedule)
				assert.Equal(t, jobs[1].Owner, genericMockJobTwo.Owner)
				assert.Equal(t, jobs[1].Metadata.SuccessCount, genericMockJobTwo.Metadata.SuccessCount)

				job2, err := db.Get(genericMockJobTwo.Id)
				if assert.NoError(t, err) {

					assert.WithinDuration(t, job2.NextRunAt, genericMockJobTwo.NextRunAt, 400*time.Microsecond)
					assert.Equal(t, job2.Name, genericMockJobTwo.Name)
					assert.Equal(t, job2.Id, genericMockJobTwo.Id)
					assert.Equal(t, job2.Command, genericMockJobTwo.Command)
					assert.Equal(t, job2.Schedule, genericMockJobTwo.Schedule)
					assert.Equal(t, job2.Owner, genericMockJobTwo.Owner)
					assert.Equal(t, job2.Metadata.SuccessCount, genericMockJobTwo.Metadata.SuccessCount)

					t.Logf("Deleting job with id %s", job2.Id)

					err := db.Delete(job2.Id)
					if assert.NoError(t, err) {

						jobs, err := db.GetAll()
						if assert.NoError(t, err) {

							assert.Equal(t, 1, len(jobs))

							assert.WithinDuration(t, jobs[0].NextRunAt, genericMockJobOne.NextRunAt, 400*time.Microsecond)
							assert.Equal(t, jobs[0].Name, genericMockJobOne.Name)
							assert.Equal(t, jobs[0].Id, genericMockJobOne.Id)
							assert.Equal(t, jobs[0].Command, genericMockJobOne.Command)
							assert.Equal(t, jobs[0].Schedule, genericMockJobOne.Schedule)
							assert.Equal(t, jobs[0].Owner, genericMockJobOne.Owner)
							assert.Equal(t, jobs[0].Metadata.SuccessCount, genericMockJobOne.Metadata.SuccessCount)

						}
					}
				}
			}
		}
	}
}

func TestPersistEpsilon(t *testing.T) {

	dsn := os.Getenv("MYSQL_DSN")
	if dsn == "" {
		mysqld, err := mysqltest.NewMysqld(nil)
		if assert.NoError(t, err) {
			dsn = mysqld.Datasource("test", "", "", 0)
			defer mysqld.Stop()
		} else {
			t.FailNow()
		}
	}

	db := New(dsn, nil)
	defer db.Close()

	cache := job.NewMemoryJobCache(db)

	mockJob := job.GetMockRecurringJobWithSchedule(time.Now().Add(time.Second*1), "PT1H")
	mockJob.Epsilon = "PT1H"
	mockJob.Command = "asdf"
	mockJob.Retries = 2

	err := mockJob.Init(cache)
	if assert.NoError(t, err) {

		err := db.Save(mockJob)
		if assert.NoError(t, err) {

			retrieved, err := db.GetAll()
			if assert.NoError(t, err) {

				retrieved[0].Run(cache)

			}
		}
	}
}
<commit_msg>lint code<commit_after>//go:build linux
// +build linux

package mysql

import (
	"database/sql"
	"encoding/json"
	"os"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/jmoiron/sqlx"
	mysqltest "github.com/lestrrat-go/test-mysqld"
	"github.com/stretchr/testify/assert"

	"github.com/ajvb/kala/job"
)

func NewTestDb() (*DB, sqlmock.Sqlmock) {
	connection, m, _ := sqlmock.New()

	var db = &DB{
		conn: sqlx.NewDb(connection, "sqlmock"),
	}
	return db, m
}

func TestSaveAndGetJob(t *testing.T) {
	db, m := NewTestDb()

	cache := job.NewLockFreeJobCache(db)
	defer db.Close()

	genericMockJob := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJob.Init(cache)

	j, err := json.Marshal(genericMockJob)
	if assert.NoError(t, err) {
		m.ExpectBegin()
		m.ExpectPrepare("replace .*").
			ExpectExec().
			WithArgs(genericMockJob.Id, string(j)).
			WillReturnResult(sqlmock.NewResult(1, 1))
		m.ExpectCommit()
		err := db.Save(genericMockJob)
		if assert.NoError(t, err) {
			m.ExpectQuery("select .*").
				WithArgs(genericMockJob.Id).
				WillReturnRows(sqlmock.NewRows([]string{"job"}).AddRow(j))
			j2, err := db.Get(genericMockJob.Id)
			if assert.Nil(t, err) {
				assert.WithinDuration(t, j2.NextRunAt, genericMockJob.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, j2.Name, genericMockJob.Name)
				assert.Equal(t, j2.Id, genericMockJob.Id)
				assert.Equal(t, j2.Command, genericMockJob.Command)
				assert.Equal(t, j2.Schedule, genericMockJob.Schedule)
				assert.Equal(t, j2.Owner, genericMockJob.Owner)
				assert.Equal(t, j2.Metadata.SuccessCount, genericMockJob.Metadata.SuccessCount)
			}
		}
	}
}

func TestDeleteJob(t *testing.T) {
	db, m := NewTestDb()

	cache := job.NewLockFreeJobCache(db)

	genericMockJob := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJob.Init(cache)

	j, err := json.Marshal(genericMockJob)
	if assert.NoError(t, err) {

		m.ExpectBegin()
		m.ExpectPrepare("replace .*").
			ExpectExec().
			WithArgs(genericMockJob.Id, string(j)).
			WillReturnResult(sqlmock.NewResult(1, 1))
		m.ExpectCommit()

		err := db.Save(genericMockJob)
		if assert.NoError(t, err) {

			// Delete it
			m.ExpectExec("delete .*").
				WithArgs(genericMockJob.Id).
				WillReturnResult(sqlmock.NewResult(1, 1))

			err = db.Delete(genericMockJob.Id)
			assert.Nil(t, err)

			// Verify deletion
			m.ExpectQuery("select .*").
				WithArgs(genericMockJob.Id).
				WillReturnError(sql.ErrNoRows)

			k, err := db.Get(genericMockJob.Id)
			assert.Error(t, err)
			assert.Nil(t, k)
		}
	}

}

func TestSaveAndGetAllJobs(t *testing.T) {
	db, m := NewTestDb()

	genericMockJobOne := job.GetMockJobWithGenericSchedule(time.Now())
	genericMockJobTwo := job.GetMockJobWithGenericSchedule(time.Now())

	jobOne, err := json.Marshal(genericMockJobOne)
	if assert.NoError(t, err) {
		jobTwo, err := json.Marshal(genericMockJobTwo)
		if assert.NoError(t, err) {

			m.ExpectQuery(".*").WillReturnRows(sqlmock.NewRows([]string{"jobs"}).AddRow(jobOne).AddRow(jobTwo))

			jobs, err := db.GetAll()
			assert.Nil(t, err)
			assert.Equal(t, 2, len(jobs))
		}
	}

}

func TestRealDb(t *testing.T) {

	dsn := os.Getenv("MYSQL_DSN")
	if dsn == "" {
		mysqld, err := mysqltest.NewMysqld(nil)
		if assert.NoError(t, err) {
			dsn = mysqld.Datasource("test", "", "", 0)
			defer mysqld.Stop()
		} else {
			t.FailNow()
		}
	}

	db := New(dsn, nil)
	cache := job.NewLockFreeJobCache(db)

	genericMockJobOne := job.GetMockJobWithGenericSchedule(time.Now().Add(time.Hour))
	genericMockJobTwo := job.GetMockJobWithGenericSchedule(time.Now().Add(time.Hour))

	genericMockJobOne.Init(cache)
	genericMockJobTwo.Init(cache)

	t.Logf("Mock job one: %s", genericMockJobOne.Id)
	t.Logf("Mock job two: %s", genericMockJobTwo.Id)

	err := db.Save(genericMockJobOne)
	if assert.NoError(t, err) {

		err := db.Save(genericMockJobTwo)
		if assert.NoError(t, err) {

			jobs, err := db.GetAll()
			if assert.NoError(t, err) {

				assert.Equal(t, 2, len(jobs))

				if jobs[0].Id == genericMockJobTwo.Id {
					jobs[0], jobs[1] = jobs[1], jobs[0]
				}

				assert.WithinDuration(t, jobs[0].NextRunAt, genericMockJobOne.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, jobs[0].Name, genericMockJobOne.Name)
				assert.Equal(t, jobs[0].Id, genericMockJobOne.Id)
				assert.Equal(t, jobs[0].Command, genericMockJobOne.Command)
				assert.Equal(t, jobs[0].Schedule, genericMockJobOne.Schedule)
				assert.Equal(t, jobs[0].Owner, genericMockJobOne.Owner)
				assert.Equal(t, jobs[0].Metadata.SuccessCount, genericMockJobOne.Metadata.SuccessCount)

				assert.WithinDuration(t, jobs[1].NextRunAt, genericMockJobTwo.NextRunAt, 400*time.Microsecond)
				assert.Equal(t, jobs[1].Name, genericMockJobTwo.Name)
				assert.Equal(t, jobs[1].Id, genericMockJobTwo.Id)
				assert.Equal(t, jobs[1].Command, genericMockJobTwo.Command)
				assert.Equal(t, jobs[1].Schedule, genericMockJobTwo.Schedule)
				assert.Equal(t, jobs[1].Owner, genericMockJobTwo.Owner)
				assert.Equal(t, jobs[1].Metadata.SuccessCount, genericMockJobTwo.Metadata.SuccessCount)

				job2, err := db.Get(genericMockJobTwo.Id)
				if assert.NoError(t, err) {

					assert.WithinDuration(t, job2.NextRunAt, genericMockJobTwo.NextRunAt, 400*time.Microsecond)
					assert.Equal(t, job2.Name, genericMockJobTwo.Name)
					assert.Equal(t, job2.Id, genericMockJobTwo.Id)
					assert.Equal(t, job2.Command, genericMockJobTwo.Command)
					assert.Equal(t, job2.Schedule, genericMockJobTwo.Schedule)
					assert.Equal(t, job2.Owner, genericMockJobTwo.Owner)
					assert.Equal(t, job2.Metadata.SuccessCount, genericMockJobTwo.Metadata.SuccessCount)

					t.Logf("Deleting job with id %s", job2.Id)

					err := db.Delete(job2.Id)
					if assert.NoError(t, err) {

						jobs, err := db.GetAll()
						if assert.NoError(t, err) {

							assert.Equal(t, 1, len(jobs))

							assert.WithinDuration(t, jobs[0].NextRunAt, genericMockJobOne.NextRunAt, 400*time.Microsecond)
							assert.Equal(t, jobs[0].Name, genericMockJobOne.Name)
							assert.Equal(t, jobs[0].Id, genericMockJobOne.Id)
							assert.Equal(t, jobs[0].Command, genericMockJobOne.Command)
							assert.Equal(t, jobs[0].Schedule, genericMockJobOne.Schedule)
							assert.Equal(t, jobs[0].Owner, genericMockJobOne.Owner)
							assert.Equal(t, jobs[0].Metadata.SuccessCount, genericMockJobOne.Metadata.SuccessCount)

						}
					}
				}
			}
		}
	}
}

func TestPersistEpsilon(t *testing.T) {

	dsn := os.Getenv("MYSQL_DSN")
	if dsn == "" {
		mysqld, err := mysqltest.NewMysqld(nil)
		if assert.NoError(t, err) {
			dsn = mysqld.Datasource("test", "", "", 0)
			defer mysqld.Stop()
		} else {
			t.FailNow()
		}
	}

	db := New(dsn, nil)
	defer db.Close()

	cache := job.NewMemoryJobCache(db)

	mockJob := job.GetMockRecurringJobWithSchedule(time.Now().Add(time.Second*1), "PT1H")
	mockJob.Epsilon = "PT1H"
	mockJob.Command = "asdf"
	mockJob.Retries = 2

	err := mockJob.Init(cache)
	if assert.NoError(t, err) {

		err := db.Save(mockJob)
		if assert.NoError(t, err) {

			retrieved, err := db.GetAll()
			if assert.NoError(t, err) {

				retrieved[0].Run(cache)

			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.Section()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.0.2"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy local files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<commit_msg>Version 1.0.3<commit_after>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.Section()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.0.3"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy local files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<|endoftext|>


<commit_before>package archiver

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strconv"

	"github.com/go-errors/errors"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/state"
)

func ExtractZip(readerAt io.ReaderAt, size int64, dir string, settings ExtractSettings) (*ExtractResult, error) {
	dirCount := 0
	regCount := 0
	symlinkCount := 0

	reader, err := zip.NewReader(readerAt, size)
	if err != nil {
		return nil, errors.Wrap(err, 1)
	}

	var totalSize int64
	for _, file := range reader.File {
		totalSize += int64(file.UncompressedSize64)
	}

	var doneSize uint64
	var lastDoneIndex int = -1

	func() {
		if settings.ResumeFrom == "" {
			return
		}

		resBytes, resErr := ioutil.ReadFile(settings.ResumeFrom)
		if resErr != nil {
			if !errors.Is(resErr, os.ErrNotExist) {
				settings.Consumer.Warnf("Couldn't read resume file: %s", resErr.Error())
			}
			return
		}

		lastDone64, resErr := strconv.ParseInt(string(resBytes), 10, 64)
		if resErr != nil {
			settings.Consumer.Warnf("Couldn't parse resume file: %s", resErr.Error())
			return
		}

		lastDoneIndex = int(lastDone64)
		settings.Consumer.Infof("Resuming from file %d", lastDoneIndex)
	}()

	warnedAboutWrite := false

	writeProgress := func(fileIndex int) {
		if settings.ResumeFrom == "" {
			return
		}

		payload := fmt.Sprintf("%d", fileIndex)

		wErr := ioutil.WriteFile(settings.ResumeFrom, []byte(payload), 0644)
		if wErr != nil {
			if !warnedAboutWrite {
				warnedAboutWrite = true
				settings.Consumer.Warnf("Couldn't save resume file: %s", wErr.Error())
			}
			return
		}
	}

	defer func() {
		if settings.ResumeFrom == "" {
			return
		}

		rErr := os.Remove(settings.ResumeFrom)
		if rErr != nil {
			settings.Consumer.Warnf("Couldn't remove resume file: %s", rErr.Error())
		}
	}()

	if settings.OnUncompressedSizeKnown != nil {
		settings.OnUncompressedSizeKnown(totalSize)
	}

	for fileIndex, file := range reader.File {
		if fileIndex <= lastDoneIndex {
			settings.Consumer.Debugf("Skipping file %d")
			doneSize += file.UncompressedSize64
			settings.Consumer.Progress(float64(doneSize) / float64(totalSize))
			continue
		}

		err = func() error {
			rel := file.Name
			filename := path.Join(dir, filepath.FromSlash(rel))

			info := file.FileInfo()
			mode := info.Mode()

			if info.IsDir() {
				err = Mkdir(filename)
				if err != nil {
					return errors.Wrap(err, 1)
				}
				dirCount++
			} else if mode&os.ModeSymlink > 0 {
				fileReader, fErr := file.Open()
				if fErr != nil {
					return errors.Wrap(fErr, 1)
				}
				defer fileReader.Close()

				linkname, lErr := ioutil.ReadAll(fileReader)
				lErr = Symlink(string(linkname), filename, settings.Consumer)
				if lErr != nil {
					return errors.Wrap(lErr, 1)
				}
				symlinkCount++
			} else {
				regCount++

				fileReader, fErr := file.Open()
				if fErr != nil {
					return errors.Wrap(fErr, 1)
				}
				defer fileReader.Close()

				settings.Consumer.Debugf("extract %s", filename)
				countingReader := counter.NewReaderCallback(func(offset int64) {
					currentSize := int64(doneSize) + offset
					settings.Consumer.Progress(float64(currentSize) / float64(totalSize))
				}, fileReader)

				err = CopyFile(filename, os.FileMode(mode&LuckyMode|ModeMask), countingReader)
				if err != nil {
					return errors.Wrap(err, 1)
				}
			}

			return nil
		}()
		if err != nil {
			return nil, errors.Wrap(err, 1)
		}

		doneSize += file.UncompressedSize64
		settings.Consumer.Progress(float64(doneSize) / float64(totalSize))
		writeProgress(fileIndex)
	}

	return &ExtractResult{
		Dirs:     dirCount,
		Files:    regCount,
		Symlinks: symlinkCount,
	}, nil
}

func CompressZip(archiveWriter io.Writer, dir string, consumer *state.Consumer) (*CompressResult, error) {
	var err error
	var uncompressedSize int64
	var compressedSize int64

	archiveCounter := counter.NewWriter(archiveWriter)

	zipWriter := zip.NewWriter(archiveCounter)
	defer zipWriter.Close()
	defer func() {
		if zipWriter != nil {
			if zErr := zipWriter.Close(); err == nil && zErr != nil {
				err = errors.Wrap(zErr, 1)
			}
		}
	}()

	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		name, wErr := filepath.Rel(dir, path)
		if wErr != nil {
			return wErr
		}

		if name == "." {
			// don't add '.' to zip
			return nil
		}

		name = filepath.ToSlash(name)

		fh, wErr := zip.FileInfoHeader(info)
		if wErr != nil {
			return wErr
		}

		fh.Name = name

		writer, wErr := zipWriter.CreateHeader(fh)
		if wErr != nil {
			return wErr
		}

		if info.IsDir() {
			// good!
		} else if info.Mode()&os.ModeSymlink > 0 {
			dest, wErr := os.Readlink(path)
			if wErr != nil {
				return wErr
			}

			_, wErr = writer.Write([]byte(dest))
			if wErr != nil {
				return wErr
			}
		} else if info.Mode().IsRegular() {
			reader, wErr := os.Open(path)
			if wErr != nil {
				return wErr
			}
			defer reader.Close()

			copiedBytes, wErr := io.Copy(writer, reader)
			if wErr != nil {
				return wErr
			}

			uncompressedSize += copiedBytes
		}

		return nil
	})

	err = zipWriter.Close()
	if err != nil {
		return nil, errors.Wrap(err, 1)
	}
	zipWriter = nil

	compressedSize = archiveCounter.Count()

	return &CompressResult{
		UncompressedSize: uncompressedSize,
		CompressedSize:   compressedSize,
	}, err
}
<commit_msg>archiver/zip: Don't attempt symlinks on windows<commit_after>package archiver

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strconv"

	"github.com/go-errors/errors"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/state"
)

func ExtractZip(readerAt io.ReaderAt, size int64, dir string, settings ExtractSettings) (*ExtractResult, error) {
	dirCount := 0
	regCount := 0
	symlinkCount := 0

	reader, err := zip.NewReader(readerAt, size)
	if err != nil {
		return nil, errors.Wrap(err, 1)
	}

	var totalSize int64
	for _, file := range reader.File {
		totalSize += int64(file.UncompressedSize64)
	}

	var doneSize uint64
	var lastDoneIndex int = -1

	func() {
		if settings.ResumeFrom == "" {
			return
		}

		resBytes, resErr := ioutil.ReadFile(settings.ResumeFrom)
		if resErr != nil {
			if !errors.Is(resErr, os.ErrNotExist) {
				settings.Consumer.Warnf("Couldn't read resume file: %s", resErr.Error())
			}
			return
		}

		lastDone64, resErr := strconv.ParseInt(string(resBytes), 10, 64)
		if resErr != nil {
			settings.Consumer.Warnf("Couldn't parse resume file: %s", resErr.Error())
			return
		}

		lastDoneIndex = int(lastDone64)
		settings.Consumer.Infof("Resuming from file %d", lastDoneIndex)
	}()

	warnedAboutWrite := false

	writeProgress := func(fileIndex int) {
		if settings.ResumeFrom == "" {
			return
		}

		payload := fmt.Sprintf("%d", fileIndex)

		wErr := ioutil.WriteFile(settings.ResumeFrom, []byte(payload), 0644)
		if wErr != nil {
			if !warnedAboutWrite {
				warnedAboutWrite = true
				settings.Consumer.Warnf("Couldn't save resume file: %s", wErr.Error())
			}
			return
		}
	}

	defer func() {
		if settings.ResumeFrom == "" {
			return
		}

		rErr := os.Remove(settings.ResumeFrom)
		if rErr != nil {
			settings.Consumer.Warnf("Couldn't remove resume file: %s", rErr.Error())
		}
	}()

	if settings.OnUncompressedSizeKnown != nil {
		settings.OnUncompressedSizeKnown(totalSize)
	}

	windows := runtime.GOOS == "windows"

	for fileIndex, file := range reader.File {
		if fileIndex <= lastDoneIndex {
			settings.Consumer.Debugf("Skipping file %d")
			doneSize += file.UncompressedSize64
			settings.Consumer.Progress(float64(doneSize) / float64(totalSize))
			continue
		}

		err = func() error {
			rel := file.Name
			filename := path.Join(dir, filepath.FromSlash(rel))

			info := file.FileInfo()
			mode := info.Mode()

			if info.IsDir() {
				err = Mkdir(filename)
				if err != nil {
					return errors.Wrap(err, 1)
				}
				dirCount++
			} else if mode&os.ModeSymlink > 0 && !windows {
				fileReader, fErr := file.Open()
				if fErr != nil {
					return errors.Wrap(fErr, 1)
				}
				defer fileReader.Close()

				linkname, lErr := ioutil.ReadAll(fileReader)
				lErr = Symlink(string(linkname), filename, settings.Consumer)
				if lErr != nil {
					return errors.Wrap(lErr, 1)
				}
				symlinkCount++
			} else {
				regCount++

				fileReader, fErr := file.Open()
				if fErr != nil {
					return errors.Wrap(fErr, 1)
				}
				defer fileReader.Close()

				settings.Consumer.Debugf("extract %s", filename)
				countingReader := counter.NewReaderCallback(func(offset int64) {
					currentSize := int64(doneSize) + offset
					settings.Consumer.Progress(float64(currentSize) / float64(totalSize))
				}, fileReader)

				err = CopyFile(filename, os.FileMode(mode&LuckyMode|ModeMask), countingReader)
				if err != nil {
					return errors.Wrap(err, 1)
				}
			}

			return nil
		}()
		if err != nil {
			return nil, errors.Wrap(err, 1)
		}

		doneSize += file.UncompressedSize64
		settings.Consumer.Progress(float64(doneSize) / float64(totalSize))
		writeProgress(fileIndex)
	}

	return &ExtractResult{
		Dirs:     dirCount,
		Files:    regCount,
		Symlinks: symlinkCount,
	}, nil
}

func CompressZip(archiveWriter io.Writer, dir string, consumer *state.Consumer) (*CompressResult, error) {
	var err error
	var uncompressedSize int64
	var compressedSize int64

	archiveCounter := counter.NewWriter(archiveWriter)

	zipWriter := zip.NewWriter(archiveCounter)
	defer zipWriter.Close()
	defer func() {
		if zipWriter != nil {
			if zErr := zipWriter.Close(); err == nil && zErr != nil {
				err = errors.Wrap(zErr, 1)
			}
		}
	}()

	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		name, wErr := filepath.Rel(dir, path)
		if wErr != nil {
			return wErr
		}

		if name == "." {
			// don't add '.' to zip
			return nil
		}

		name = filepath.ToSlash(name)

		fh, wErr := zip.FileInfoHeader(info)
		if wErr != nil {
			return wErr
		}

		fh.Name = name

		writer, wErr := zipWriter.CreateHeader(fh)
		if wErr != nil {
			return wErr
		}

		if info.IsDir() {
			// good!
		} else if info.Mode()&os.ModeSymlink > 0 {
			dest, wErr := os.Readlink(path)
			if wErr != nil {
				return wErr
			}

			_, wErr = writer.Write([]byte(dest))
			if wErr != nil {
				return wErr
			}
		} else if info.Mode().IsRegular() {
			reader, wErr := os.Open(path)
			if wErr != nil {
				return wErr
			}
			defer reader.Close()

			copiedBytes, wErr := io.Copy(writer, reader)
			if wErr != nil {
				return wErr
			}

			uncompressedSize += copiedBytes
		}

		return nil
	})

	err = zipWriter.Close()
	if err != nil {
		return nil, errors.Wrap(err, 1)
	}
	zipWriter = nil

	compressedSize = archiveCounter.Count()

	return &CompressResult{
		UncompressedSize: uncompressedSize,
		CompressedSize:   compressedSize,
	}, err
}
<|endoftext|>


<commit_before>package asgi

import (
	"fmt"
	"net/http"

	"github.com/gorilla/websocket"
)

// Message is a dict to send via the channel layer.
// The key has to be a string and the type of the value vary.
type Message map[string]interface{}

// SendMessenger is an type that can be used to send a message through the channel
// layer. Therefore it has the method to convert a structured message to a
// Message dict.
type SendMessenger interface {
	// Converts a Message to a Message dict, that can be send through a asgi channel.
	Raw() Message
}

// ReceiveMessenger is a type that can be used to receive a message through the
// channel layer. Therefore it has the method to convert a Message dict to a
// structured message type.
type ReceiveMessenger interface {
	// Sets the values of the variable with the values of a Message dict.
	Set(Message) error
}

// Messager is an type that can be used to send and receive messages.
type Messager interface {
	SendMessenger
	ReceiveMessenger
}

// RequestMessage is a structured message type, defined by the asgi specs which
// is used to forward an http request from a client to the channel layer.
// This differs from the specs that all fields are uppercase and CamelCase.
// Also the Headers field has the type http.Header and therefore is a dictonary.
// BodyChannel does not default to None but to an empty string.
// Client and Server are strings in the form "host:port". They default to an
// empty string.
type RequestMessage struct {
	ReplyChannel string
	HTTPVersion  string
	Method       string
	Scheme       string
	Path         string
	QueryString  []byte
	RootPath     string
	Headers      http.Header
	Body         []byte
	BodyChannel  string
	Client       string
	Server       string
}

// Raw converts a RequestMessage to a Message dict.
func (r *RequestMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = r.ReplyChannel
	m["http_version"] = r.HTTPVersion
	m["method"] = r.Method
	m["scheme"] = r.Scheme
	m["path"] = r.Path
	m["query_string"] = r.QueryString
	m["root_path"] = r.RootPath
	m["headers"] = convertHeader(r.Headers)
	m["body"] = r.Body
	m["body_channel"] = r.BodyChannel
	m["client"], _ = strToHost(r.Client)
	m["server"], _ = strToHost(r.Server)
	return m
}

// RequestBodyChunkMessage is a structured message type, defined by the asgi
// specs which is used to continue forwarding an http request from a client to
// the channel layer, if the request body was big.
// This differs from the specs that all fields are uppercase and CamelCase.
type RequestBodyChunkMessage struct {
	Content     []byte
	Closed      bool
	MoreContent bool
}

// Raw converts a RequestBodyChunkMessage to a Message dict.
func (r *RequestBodyChunkMessage) Raw() Message {
	m := make(Message)
	m["content"] = r.Content
	m["closed"] = r.Closed
	m["more_content"] = r.MoreContent
	return m
}

// ResponseChunkMessage is a structured message type, defined by the asgi specs.
// It is used to forward an response from the channel layer to the client.
// It has to follow a ResponseMessage.
// It differs from the specs that the fields are uppercase and CamelCase.
type ResponseChunkMessage struct {
	Content     []byte
	MoreContent bool
}

// Set fills the values of a ResponseChunkMessage with a the data of a message dict.
func (rm *ResponseChunkMessage) Set(m Message) (err error) {
	var ok bool

	switch t := m["content"].(type) {
	case []byte:
		rm.Content = t
	case nil:
		rm.Content = []byte{}
	default:
		return fmt.Errorf("message has wrong format. \"content\" has to be []byte or nil, not %T", m["content"])
	}

	rm.MoreContent, ok = m["more_content"].(bool)
	if ok == false {
		return fmt.Errorf("message has wrong format. \"more_content\" has to be bool not %T", m["more_content"])
	}
	return nil
}

// ResponseMessage is a structured message type, defined by the asgi specs.
// It is used to forward an response from the channel layer to the client.
// It differs from the specs that the fields are uppercase and CamelCase and that
// Headers is a dictonary and not a list of tuples.
type ResponseMessage struct {
	ResponseChunkMessage
	Status  int
	Headers http.Header
}

// Set fills the values of a ResponseMessage with a the data of a message dict.
func (rm *ResponseMessage) Set(m Message) (err error) {
	var ok bool

	status, ok := m["status"].(uint64)
	if !ok {
		return fmt.Errorf("message has wrong format. \"status\" has to be uint64 not %T", m["status"])
	}
	rm.Status = int(status)

	switch t := m["content"].(type) {
	case []byte:
		rm.Content = t
	case nil:
		rm.Content = []byte{}
	default:
		return fmt.Errorf("message has wrong format. \"content\" has to be []byte or nil, not %T", m["content"])
	}

	rm.MoreContent, ok = m["more_content"].(bool)
	if ok == false {
		return fmt.Errorf("message has wrong format. \"more_content\" has to be bool not %T", m["more_content"])
	}

	rm.Headers = make(http.Header)
	for _, value := range m["headers"].([]interface{}) {
		// value should be a slice of interface{}
		value := value.([]interface{})
		k := string(value[0].([]byte))
		v := string(value[1].([]byte))
		rm.Headers.Add(k, v)
	}
	return nil
}

// TODO: Impelement "Server Push" and "Disconnect"

// ConnectionMessage is a structured message defined by the asgi specs. It is used
// to forware an websocket connection request to the channel layer.
// It differs from the asgi specs that all fields are Uppercase and CamelCase,
// the field Headers is a dict and the fields Client and Server are strings in
// the form "host:port". It has no field order.
type ConnectionMessage struct {
	ReplyChannel string
	Scheme       string
	Path         string
	QueryString  []byte
	RootPath     string
	Headers      http.Header
	Client       string
	Server       string
}

// Raw converts a ConnectionMessage to a Message dict, that can be send through
// the channel layer
func (cm *ConnectionMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = cm.ReplyChannel
	m["scheme"] = cm.Scheme
	m["path"] = cm.Path
	m["query_string"] = cm.QueryString
	m["root_path"] = cm.RootPath
	m["headers"] = convertHeader(cm.Headers)
	m["client"], _ = strToHost(cm.Client)
	m["server"], _ = strToHost(cm.Server)
	m["order"] = 0
	return m
}

// ReceiveMessage is message specified by the asgi spec
type ReceiveMessage struct {
	ReplyChannel string
	Path         string
	Content      []byte
	Type         int // See websocket.TextMessage and websocket.BinaryMessage
	Order        int
}

// Raw Converts a ReceiveMessage to a Message
func (cm *ReceiveMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = cm.ReplyChannel
	m["path"] = cm.Path
	if cm.Type == websocket.TextMessage {
		m["bytes"] = nil
		m["text"] = string(cm.Content)
	} else if cm.Type == websocket.BinaryMessage {
		m["bytes"] = cm.Content
		m["text"] = nil
	}
	m["order"] = cm.Order
	return m
}

// DisconnectionMessage is a structured message defined by the asgi specs. It is
// send to the channel layer when the connection was closed for any reason.
// It differs from the asgi specs that all fields are Uppercase and CamelCase.
type DisconnectionMessage struct {
	ReplyChannel string
	Code         int
	Path         string
	Order        int
}

// Raw converts a DisconnectionMessage to a Message, that can be send through
// the channel layer.
func (dm *DisconnectionMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = dm.ReplyChannel
	m["code"] = dm.Code
	m["path"] = dm.Path
	m["order"] = dm.Order
	return m
}

// SendCloseAcceptMessage is a structured message defined by the asgi specs. It
// is used as answer from the channel layer after a websocket connection and to s
// end data to an open websocket connection.
// It differs from the asgi specs that all fields are Uppercase and CamelCase.
type SendCloseAcceptMessage struct {
	Bytes  []byte
	Text   string
	Close  int
	Accept bool
}

// Set fills the values of a SendCloseAcceptMessage with a the data of a message
// dict.
func (s *SendCloseAcceptMessage) Set(m Message) (err error) {
	switch t := m["bytes"].(type) {
	case []byte:
		s.Bytes = t
	case nil:
		s.Bytes = nil
	default:
		return fmt.Errorf("the field bytes has to be []byte or nil not %T", t)
	}

	switch t := m["text"].(type) {
	case string:
		s.Text = t
	case nil:
		s.Text = ""
	default:
		return fmt.Errorf("the field text has to be string or nil not %T", t)
	}

	if s.Bytes != nil && s.Text != "" {
		return fmt.Errorf("only one of the fields text and bytes can be set at once")
	}

	switch t := m["close"].(type) {
	case bool:
		if t {
			s.Close = 0
		} else {
			s.Close = 1000
		}
	case int:
		s.Close = t
	case nil:
		s.Close = 0
	default:
		return fmt.Errorf("the field \"close\" has to be bool, int or nil, not %T", m["close"])
	}

	switch t := m["accept"].(type) {
	case bool:
		s.Accept = t
	case nil:
		s.Accept = false
	default:
		return fmt.Errorf("the field \"accept\" has to be bool or nil, not %T", m["close"])
	}
	return nil
}
<commit_msg>Panic when the host value of a message can not be set<commit_after>package asgi

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

	"github.com/gorilla/websocket"
)

// Message is a dict to send via the channel layer.
// The key has to be a string and the type of the value vary.
type Message map[string]interface{}

// SendMessenger is an type that can be used to send a message through the channel
// layer. Therefore it has the method to convert a structured message to a
// Message dict.
type SendMessenger interface {
	// Converts a Message to a Message dict, that can be send through a asgi channel.
	Raw() Message
}

// ReceiveMessenger is a type that can be used to receive a message through the
// channel layer. Therefore it has the method to convert a Message dict to a
// structured message type.
type ReceiveMessenger interface {
	// Sets the values of the variable with the values of a Message dict.
	Set(Message) error
}

// Messager is an type that can be used to send and receive messages.
type Messager interface {
	SendMessenger
	ReceiveMessenger
}

// RequestMessage is a structured message type, defined by the asgi specs which
// is used to forward an http request from a client to the channel layer.
// This differs from the specs that all fields are uppercase and CamelCase.
// Also the Headers field has the type http.Header and therefore is a dictonary.
// BodyChannel does not default to None but to an empty string.
// Client and Server are strings in the form "host:port". They default to an
// empty string.
type RequestMessage struct {
	ReplyChannel string
	HTTPVersion  string
	Method       string
	Scheme       string
	Path         string
	QueryString  []byte
	RootPath     string
	Headers      http.Header
	Body         []byte
	BodyChannel  string
	Client       string
	Server       string
}

// Raw converts a RequestMessage to a Message dict.
func (r *RequestMessage) Raw() Message {
	var err error
	m := make(Message)
	m["reply_channel"] = r.ReplyChannel
	m["http_version"] = r.HTTPVersion
	m["method"] = r.Method
	m["scheme"] = r.Scheme
	m["path"] = r.Path
	m["query_string"] = r.QueryString
	m["root_path"] = r.RootPath
	m["headers"] = convertHeader(r.Headers)
	m["body"] = r.Body
	m["body_channel"] = r.BodyChannel
	m["client"], err = strToHost(r.Client)
	if err != nil {
		log.Panicf("Could not create the client value for a request message: %s", err)
	}
	m["server"], err = strToHost(r.Server)
	if err != nil {
		log.Panicf("Could not create the server value for a request message: %s", err)
	}
	return m
}

// RequestBodyChunkMessage is a structured message type, defined by the asgi
// specs which is used to continue forwarding an http request from a client to
// the channel layer, if the request body was big.
// This differs from the specs that all fields are uppercase and CamelCase.
type RequestBodyChunkMessage struct {
	Content     []byte
	Closed      bool
	MoreContent bool
}

// Raw converts a RequestBodyChunkMessage to a Message dict.
func (r *RequestBodyChunkMessage) Raw() Message {
	m := make(Message)
	m["content"] = r.Content
	m["closed"] = r.Closed
	m["more_content"] = r.MoreContent
	return m
}

// ResponseChunkMessage is a structured message type, defined by the asgi specs.
// It is used to forward an response from the channel layer to the client.
// It has to follow a ResponseMessage.
// It differs from the specs that the fields are uppercase and CamelCase.
type ResponseChunkMessage struct {
	Content     []byte
	MoreContent bool
}

// Set fills the values of a ResponseChunkMessage with a the data of a message dict.
func (rm *ResponseChunkMessage) Set(m Message) (err error) {
	var ok bool

	switch t := m["content"].(type) {
	case []byte:
		rm.Content = t
	case nil:
		rm.Content = []byte{}
	default:
		return fmt.Errorf("message has wrong format. \"content\" has to be []byte or nil, not %T", m["content"])
	}

	rm.MoreContent, ok = m["more_content"].(bool)
	if ok == false {
		return fmt.Errorf("message has wrong format. \"more_content\" has to be bool not %T", m["more_content"])
	}
	return nil
}

// ResponseMessage is a structured message type, defined by the asgi specs.
// It is used to forward an response from the channel layer to the client.
// It differs from the specs that the fields are uppercase and CamelCase and that
// Headers is a dictonary and not a list of tuples.
type ResponseMessage struct {
	ResponseChunkMessage
	Status  int
	Headers http.Header
}

// Set fills the values of a ResponseMessage with a the data of a message dict.
func (rm *ResponseMessage) Set(m Message) (err error) {
	var ok bool

	status, ok := m["status"].(uint64)
	if !ok {
		return fmt.Errorf("message has wrong format. \"status\" has to be uint64 not %T", m["status"])
	}
	rm.Status = int(status)

	switch t := m["content"].(type) {
	case []byte:
		rm.Content = t
	case nil:
		rm.Content = []byte{}
	default:
		return fmt.Errorf("message has wrong format. \"content\" has to be []byte or nil, not %T", m["content"])
	}

	rm.MoreContent, ok = m["more_content"].(bool)
	if ok == false {
		return fmt.Errorf("message has wrong format. \"more_content\" has to be bool not %T", m["more_content"])
	}

	rm.Headers = make(http.Header)
	for _, value := range m["headers"].([]interface{}) {
		// value should be a slice of interface{}
		value := value.([]interface{})
		k := string(value[0].([]byte))
		v := string(value[1].([]byte))
		rm.Headers.Add(k, v)
	}
	return nil
}

// TODO: Impelement "Server Push" and "Disconnect"

// ConnectionMessage is a structured message defined by the asgi specs. It is used
// to forware an websocket connection request to the channel layer.
// It differs from the asgi specs that all fields are Uppercase and CamelCase,
// the field Headers is a dict and the fields Client and Server are strings in
// the form "host:port". It has no field order.
type ConnectionMessage struct {
	ReplyChannel string
	Scheme       string
	Path         string
	QueryString  []byte
	RootPath     string
	Headers      http.Header
	Client       string
	Server       string
}

// Raw converts a ConnectionMessage to a Message dict, that can be send through
// the channel layer
func (cm *ConnectionMessage) Raw() Message {
	var err error
	m := make(Message)
	m["reply_channel"] = cm.ReplyChannel
	m["scheme"] = cm.Scheme
	m["path"] = cm.Path
	m["query_string"] = cm.QueryString
	m["root_path"] = cm.RootPath
	m["headers"] = convertHeader(cm.Headers)
	m["client"], err = strToHost(cm.Client)
	if err != nil {
		log.Panicf("Could not create the client value for a connection message: %s", err)
	}
	m["server"], err = strToHost(cm.Server)
	if err != nil {
		log.Panicf("Could not create the server value for a connection message: %s", err)
	}
	m["order"] = 0
	return m
}

// ReceiveMessage is message specified by the asgi spec
type ReceiveMessage struct {
	ReplyChannel string
	Path         string
	Content      []byte
	Type         int // See websocket.TextMessage and websocket.BinaryMessage
	Order        int
}

// Raw Converts a ReceiveMessage to a Message
func (cm *ReceiveMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = cm.ReplyChannel
	m["path"] = cm.Path
	if cm.Type == websocket.TextMessage {
		m["bytes"] = nil
		m["text"] = string(cm.Content)
	} else if cm.Type == websocket.BinaryMessage {
		m["bytes"] = cm.Content
		m["text"] = nil
	}
	m["order"] = cm.Order
	return m
}

// DisconnectionMessage is a structured message defined by the asgi specs. It is
// send to the channel layer when the connection was closed for any reason.
// It differs from the asgi specs that all fields are Uppercase and CamelCase.
type DisconnectionMessage struct {
	ReplyChannel string
	Code         int
	Path         string
	Order        int
}

// Raw converts a DisconnectionMessage to a Message, that can be send through
// the channel layer.
func (dm *DisconnectionMessage) Raw() Message {
	m := make(Message)
	m["reply_channel"] = dm.ReplyChannel
	m["code"] = dm.Code
	m["path"] = dm.Path
	m["order"] = dm.Order
	return m
}

// SendCloseAcceptMessage is a structured message defined by the asgi specs. It
// is used as answer from the channel layer after a websocket connection and to s
// end data to an open websocket connection.
// It differs from the asgi specs that all fields are Uppercase and CamelCase.
type SendCloseAcceptMessage struct {
	Bytes  []byte
	Text   string
	Close  int
	Accept bool
}

// Set fills the values of a SendCloseAcceptMessage with a the data of a message
// dict.
func (s *SendCloseAcceptMessage) Set(m Message) (err error) {
	switch t := m["bytes"].(type) {
	case []byte:
		s.Bytes = t
	case nil:
		s.Bytes = nil
	default:
		return fmt.Errorf("the field bytes has to be []byte or nil not %T", t)
	}

	switch t := m["text"].(type) {
	case string:
		s.Text = t
	case nil:
		s.Text = ""
	default:
		return fmt.Errorf("the field text has to be string or nil not %T", t)
	}

	if s.Bytes != nil && s.Text != "" {
		return fmt.Errorf("only one of the fields text and bytes can be set at once")
	}

	switch t := m["close"].(type) {
	case bool:
		if t {
			s.Close = 0
		} else {
			s.Close = 1000
		}
	case int:
		s.Close = t
	case nil:
		s.Close = 0
	default:
		return fmt.Errorf("the field \"close\" has to be bool, int or nil, not %T", m["close"])
	}

	switch t := m["accept"].(type) {
	case bool:
		s.Accept = t
	case nil:
		s.Accept = false
	default:
		return fmt.Errorf("the field \"accept\" has to be bool or nil, not %T", m["close"])
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"log"
	"net"
	// _ "net/http/pprof" // HTTP profiling
	"os"
	"strconv"
)

const (
	// BufferLimit specifies buffer size that is sufficient to handle full-size UDP datagram or TCP segment in one step
	BufferLimit = 2<<16 - 1
	// UDPDisconnectSequence is used to disconnect UDP sessions
	UDPDisconnectSequence = "~."
)

// Progress indicates transfer status
type Progress struct {
	remoteAddr net.Addr
	bytes      uint64
}

func (p *Progress) String() string {
	return "{" +
		"remoteAddr: " + p.remoteAddr.String() +
		", bytes: " + strconv.Itoa(int(p.bytes)) +
		"}"
}

// TransferStreams launches two read-write goroutines and waits for signal from them
func TransferStreams(con io.ReadWriteCloser) {
	c := make(chan Progress)
	done := make(chan bool)

	// Read from Reader and write to Writer until EOF
	copy := func(r io.ReadCloser, w io.WriteCloser) {
		defer func() {
			r.Close()
			w.Close()
			if <-done {
				close(c)
			}
		}()
		n, err := io.Copy(w, r)
		if err != nil {
			log.Printf("Read/write error: %s\n", err)
		}
		var addr net.Addr
		if con, ok := r.(net.Conn); ok {
			addr = con.RemoteAddr()
		}
		if con, ok := w.(net.Conn); ok {
			addr = con.RemoteAddr()
		}
		c <- Progress{bytes: uint64(n), remoteAddr: addr}
	}

	go copy(con, os.Stdout)
	go copy(os.Stdin, con)

	d := false
	for p := range c {
		log.Printf("One of goroutines has been finished: %s\n", p.String())
		done <- d
		d = !d
	}
}

// TransferPackets launches receive goroutine first, wait for address from it (if needed), launches send goroutine then
func TransferPackets(con net.Conn) {
	c := make(chan Progress)
	done := make(chan bool)

	// Read from Reader and write to Writer until EOF.
	// ra is an address to whom packets must be sent in UDP listen mode.
	copy := func(r io.ReadCloser, w io.WriteCloser, ra net.Addr) {
		defer func() {
			r.Close()
			w.Close()
			if _, ok := w.(*net.UDPConn); ok {
				log.Printf("Stop receiving flow from %v\n", ra)
			}
			if <-done {
				close(c)
			}
		}()

		buf := make([]byte, BufferLimit)
		bytes := uint64(0)
		var n int
		var err error
		var addr net.Addr

		for {
			// Read
			if con, ok := r.(*net.UDPConn); ok {
				n, addr, err = con.ReadFrom(buf)
				// Send remote address to caller function (for UDP in listen mode only)
				if con.RemoteAddr() == nil && ra == nil {
					ra = addr
					c <- Progress{remoteAddr: ra}
				}
			} else {
				n, err = r.Read(buf)
			}
			if err != nil {
				if err != io.EOF {
					log.Printf("Read error: %s\n", err)
				}
				break
			}
			if string(buf[0:n-1]) == UDPDisconnectSequence {
				break
			}

			// Write
			if con, ok := w.(*net.UDPConn); ok && con.RemoteAddr() == nil {
				// Special case for UDP in listen mode otherwise net.ErrWriteToConnected will be thrown
				n, err = con.WriteTo(buf[0:n], ra)
			} else {
				n, err = w.Write(buf[0:n])
			}
			if err != nil {
				log.Printf("Write error: %s\n", err)
				break
			}
			bytes += uint64(n)
		}
		c <- Progress{bytes: bytes, remoteAddr: ra}
	}

	go copy(con, os.Stdout, nil)
	ra := con.RemoteAddr()
	// If connection hasn't got remote address then wait for it from receiver goroutine
	if ra == nil {
		p := <-c
		ra = p.remoteAddr
		log.Printf("Connect from %v\n", ra)
	}
	go copy(os.Stdin, con, ra)

	d := false
	for p := range c {
		log.Printf("One of goroutines has been finished: %s\n", p.String())
		done <- d
		d = !d
	}
}

func main() {
	// go http.ListenAndServe(":6060", nil)
	var host, port, proto string
	var listen bool
	flag.StringVar(&host, "host", "", "Remote host to connect, i.e. 127.0.0.1")
	flag.StringVar(&proto, "proto", "tcp", "TCP/UDP mode")
	flag.BoolVar(&listen, "listen", false, "Listen mode")
	flag.StringVar(&port, "port", ":9999", "Port to listen on or connect to (prepended by colon), i.e. :9999")
	flag.Parse()

	if proto == "tcp" {
		if listen {
			ln, err := net.Listen(proto, port)
			if err != nil {
				log.Fatalln(err)
			}
			log.Println("Listening on", proto+port)
			con, err := ln.Accept()
			if err != nil {
				log.Fatalln(err)
			}
			log.Println("Connect from", con.RemoteAddr())
			TransferStreams(con)
		} else if host != "" {
			con, err := net.Dial(proto, host+port)
			if err != nil {
				log.Fatalln(err)
			}
			log.Println("Connected to", host+port)
			TransferStreams(con)
		} else {
			flag.Usage()
		}
	} else if proto == "udp" {
		if listen {
			addr, err := net.ResolveUDPAddr(proto, port)
			if err != nil {
				log.Fatalln(err)
			}
			con, err := net.ListenUDP(proto, addr)
			if err != nil {
				log.Fatalln(err)
			}
			log.Println("Listening on", proto+port)
			TransferPackets(con)
		} else if host != "" {
			addr, err := net.ResolveUDPAddr(proto, host+port)
			if err != nil {
				log.Fatalln(err)
			}
			con, err := net.DialUDP(proto, nil, addr)
			if err != nil {
				log.Fatalln(err)
			}
			TransferPackets(con)
		} else {
			flag.Usage()
		}
	}
}
<commit_msg>Use switch instead couple of if-else and move server/client code to separate functions<commit_after>package main

import (
	"flag"
	"io"
	"log"
	"net"
	// _ "net/http/pprof" // HTTP profiling
	"os"
	"strconv"
)

const (
	// BufferLimit specifies buffer size that is sufficient to handle full-size UDP datagram or TCP segment in one step
	BufferLimit = 2<<16 - 1
	// UDPDisconnectSequence is used to disconnect UDP sessions
	UDPDisconnectSequence = "~."
)

// Progress indicates transfer status
type Progress struct {
	remoteAddr net.Addr
	bytes      uint64
}

func (p *Progress) String() string {
	return "{" +
		"remoteAddr: " + p.remoteAddr.String() +
		", bytes: " + strconv.Itoa(int(p.bytes)) +
		"}"
}

// TransferStreams launches two read-write goroutines and waits for signal from them
func TransferStreams(con io.ReadWriteCloser) {
	c := make(chan Progress)
	done := make(chan bool)

	// Read from Reader and write to Writer until EOF
	copy := func(r io.ReadCloser, w io.WriteCloser) {
		defer func() {
			r.Close()
			w.Close()
			if <-done {
				close(c)
			}
		}()
		n, err := io.Copy(w, r)
		if err != nil {
			log.Printf("Read/write error: %s\n", err)
		}
		var addr net.Addr
		if con, ok := r.(net.Conn); ok {
			addr = con.RemoteAddr()
		}
		if con, ok := w.(net.Conn); ok {
			addr = con.RemoteAddr()
		}
		c <- Progress{bytes: uint64(n), remoteAddr: addr}
	}

	go copy(con, os.Stdout)
	go copy(os.Stdin, con)

	d := false
	for p := range c {
		log.Printf("One of goroutines has been finished: %s\n", p.String())
		done <- d
		d = !d
	}
}

// TransferPackets launches receive goroutine first, wait for address from it (if needed), launches send goroutine then
func TransferPackets(con net.Conn) {
	c := make(chan Progress)
	done := make(chan bool)

	// Read from Reader and write to Writer until EOF.
	// ra is an address to whom packets must be sent in UDP listen mode.
	copy := func(r io.ReadCloser, w io.WriteCloser, ra net.Addr) {
		defer func() {
			r.Close()
			w.Close()
			if _, ok := w.(*net.UDPConn); ok {
				log.Printf("Stop receiving flow from %v\n", ra)
			}
			if <-done {
				close(c)
			}
		}()

		buf := make([]byte, BufferLimit)
		bytes := uint64(0)
		var n int
		var err error
		var addr net.Addr

		for {
			// Read
			if con, ok := r.(*net.UDPConn); ok {
				n, addr, err = con.ReadFrom(buf)
				// Send remote address to caller function (for UDP in listen mode only)
				if con.RemoteAddr() == nil && ra == nil {
					ra = addr
					c <- Progress{remoteAddr: ra}
				}
			} else {
				n, err = r.Read(buf)
			}
			if err != nil {
				if err != io.EOF {
					log.Printf("Read error: %s\n", err)
				}
				break
			}
			if string(buf[0:n-1]) == UDPDisconnectSequence {
				break
			}

			// Write
			if con, ok := w.(*net.UDPConn); ok && con.RemoteAddr() == nil {
				// Special case for UDP in listen mode otherwise net.ErrWriteToConnected will be thrown
				n, err = con.WriteTo(buf[0:n], ra)
			} else {
				n, err = w.Write(buf[0:n])
			}
			if err != nil {
				log.Printf("Write error: %s\n", err)
				break
			}
			bytes += uint64(n)
		}
		c <- Progress{bytes: bytes, remoteAddr: ra}
	}

	go copy(con, os.Stdout, nil)
	ra := con.RemoteAddr()
	// If connection hasn't got remote address then wait for it from receiver goroutine
	if ra == nil {
		p := <-c
		ra = p.remoteAddr
		log.Printf("Connect from %v\n", ra)
	}
	go copy(os.Stdin, con, ra)

	d := false
	for p := range c {
		log.Printf("One of goroutines has been finished: %s\n", p.String())
		done <- d
		d = !d
	}
}

func main() {
	// go http.ListenAndServe(":6060", nil)
	var host, port, proto string
	var listen bool
	flag.StringVar(&host, "host", "", "Remote host to connect, i.e. 127.0.0.1")
	flag.StringVar(&proto, "proto", "tcp", "TCP/UDP mode")
	flag.BoolVar(&listen, "listen", false, "Listen mode")
	flag.StringVar(&port, "port", ":9999", "Port to listen on or connect to (prepended by colon), i.e. :9999")
	flag.Parse()

	startTCPServer := func() {
		ln, err := net.Listen(proto, port)
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("Listening on", proto+port)
		con, err := ln.Accept()
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("Connect from", con.RemoteAddr())
		TransferStreams(con)
	}

	startTCPClient := func() {
		con, err := net.Dial(proto, host+port)
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("Connected to", host+port)
		TransferStreams(con)
	}

	startUDPServer := func() {
		addr, err := net.ResolveUDPAddr(proto, port)
		if err != nil {
			log.Fatalln(err)
		}
		con, err := net.ListenUDP(proto, addr)
		if err != nil {
			log.Fatalln(err)
		}
		log.Println("Listening on", proto+port)
		TransferPackets(con)
	}

	startUDPClient := func() {
		addr, err := net.ResolveUDPAddr(proto, host+port)
		if err != nil {
			log.Fatalln(err)
		}
		con, err := net.DialUDP(proto, nil, addr)
		if err != nil {
			log.Fatalln(err)
		}
		TransferPackets(con)
	}

	switch proto {
	case "tcp":
		if listen {
			startTCPServer()
		} else if host != "" {
			startTCPClient()
		} else {
			flag.Usage()
		}
	case "udp":
		if listen {
			startUDPServer()
		} else if host != "" {
			startUDPClient()
		} else {
			flag.Usage()
		}
	default:
		flag.Usage()
	}
}
<|endoftext|>


<commit_before>package pipeline

import (
	"log"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/eliothedeman/bangarang/alarm"
	"github.com/eliothedeman/bangarang/config"
	"github.com/eliothedeman/bangarang/event"
	"github.com/eliothedeman/bangarang/provider"
)

const (
	KEEP_ALIVE_SERVICE_NAME = "KeepAlive"
)

type Pipeline struct {
	keepAliveAge       time.Duration
	keepAliveCheckTime time.Duration
	globalPolicy       *alarm.Policy
	escalations        *alarm.Collection
	policies           map[string]*alarm.Policy
	index              *event.Index
	providers          provider.EventProviderCollection
	encodingPool       *event.EncodingPool
	config             *config.AppConfig
	tracker            *Tracker
	pauseCache         map[*event.Event]struct{}
	unpauseChan        chan struct{}
	in                 chan *event.Event
}

func NewPipeline(conf *config.AppConfig) *Pipeline {
	p := &Pipeline{
		encodingPool:       event.NewEncodingPool(event.EncoderFactories[conf.Encoding], event.DecoderFactories[conf.Encoding], runtime.NumCPU()),
		keepAliveAge:       conf.KeepAliveAge,
		keepAliveCheckTime: 30 * time.Second,
		in:                 make(chan *event.Event),
		unpauseChan:        make(chan struct{}),
	}

	p.Refresh(conf)

	logrus.Debug("Starting expiration checker")
	go p.checkExpired()

	return p
}

// refresh load all config params that don't require a restart
func (p *Pipeline) Refresh(conf *config.AppConfig) {
	p.pause()

	// if the config has changed at all, refresh the index
	if p.config == nil || string(conf.Hash) != string(p.config.Hash) {
		p.index = event.NewIndex()
		p.tracker = NewTracker()
		go p.tracker.Start()
	}

	// update optional config options
	if conf.Escalations != nil {
		p.escalations = conf.Escalations
	}

	if conf.EventProviders != nil {
		p.providers = *conf.EventProviders
	}

	p.policies = conf.Policies
	p.keepAliveAge = conf.KeepAliveAge
	p.globalPolicy = conf.GlobalPolicy

	// update to the new config
	p.config = conf
	p.unpause()
	// start up all of the providers
	logrus.Infof("Starting %d providers", len(p.providers.Collection))
	for name, ep := range p.providers.Collection {
		logrus.Infof("Starting event provider %s", name)
		go ep.Start(p.in)
	}
}

// unpause resume processing jobs
func (p *Pipeline) unpause() {
	logrus.Info("Unpausing pipeline")
	p.unpauseChan <- struct{}{}
	<-p.unpauseChan
}

// pause stop processing events
func (p *Pipeline) pause() {
	logrus.Info("Pausing pipeline")

	// cache the old injest channel
	old := p.in

	// make a temporary channel to catch incomming events
	p.in = nil

	// make a channel to signal the end of the pause
	done := make(chan struct{})
	p.unpauseChan = done

	// start a new goroutine to catch the incomming events
	go func() {
		// make a map to cache the incomming events
		cache := make(map[*event.Event]struct{})
		var e *event.Event
		for {
			select {

			// start caching the events as they come in
			case e = <-old:
				logrus.Debugf("Caching event during pause %+v", *e)
				cache[e] = struct{}{}

			// when the pause is complete, revert to the old injestion channel
			case <-done:

				// set the cached event channel
				p.in = old

				// restart the pipeline
				p.Start()

				// empty the cache
				for e, _ = range cache {
					logrus.Debugf("Proccessing cached event after unpause %+v", *e)
					old <- e
				}

				// signal the unpause function that we are done with the unpause
				p.unpauseChan <- struct{}{}
				return
			}
		}
	}()
}

func (p *Pipeline) GetTracker() *Tracker {
	return p.tracker
}

func (p *Pipeline) GetConfig() *config.AppConfig {
	return p.config
}

func (p *Pipeline) checkExpired() {
	var events []*event.Event
	for {
		time.Sleep(p.keepAliveCheckTime)
		logrus.Info("Checking for expired events.")

		// get keepalive events for all known hosts
		events = createKeepAliveEvents(p.tracker.HostTimes())
		logrus.Infof("Found %d hosts with keepalives", len(events))

		// process every event as if it was an incomming event
		for _, e := range events {
			p.Process(e)
		}
	}
}

// create keep alive events for each hostname -> time pair
func createKeepAliveEvents(times map[string]time.Time) []*event.Event {
	n := time.Now()
	events := make([]*event.Event, len(times))
	x := 0
	for host, t := range times {
		events[x] = &event.Event{
			Host:    host,
			Metric:  n.Sub(t).Seconds(),
			Service: KEEP_ALIVE_SERVICE_NAME,
		}
		x += 1
	}

	return events
}

func (p *Pipeline) Start() {
	logrus.Info("Starting pipeline")

	// fan in all of the providers and process them
	go func() {
		var e *event.Event
		for {

			// if the injest channel is nil, stop
			if p.in == nil {
				logrus.Info("Pipeline is paused, stoping pipeline")
				return
			}

			// recieve the event
			e = <-p.in

			logrus.Debugf("Beginning processing %+v", e)
			// process the event
			p.Process(e)
			logrus.Debugf("Done processing %+v", e)
		}
	}()
}

// Run the given event though the pipeline
func (p *Pipeline) Process(e *event.Event) int {
	if p.globalPolicy != nil {
		if !p.globalPolicy.CheckMatch(e) || !p.globalPolicy.CheckNotMatch(e) {
			return event.OK
		}
	}

	// track stas for this event
	p.tracker.TrackEvent(e)

	for _, pol := range p.policies {
		if pol.Matches(e) {
			act := pol.Action(e)

			// if there is an action to be taken
			if act != "" {

				// create a new incident for this event
				in := p.NewIncident(pol.Name, e)

				// dedup the incident
				if p.Dedupe(in) {

					// update the incident in the index
					if in.Status != event.OK {
						p.index.PutIncident(in)
					} else {
						p.index.DeleteIncidentById(in.IndexName())
					}

					// fetch the escalation to take
					esc, ok := p.escalations.Collection()[act]
					if ok {

						// send to every alarm in the escalation
						for _, a := range esc {
							a.Send(in)
						}
					} else {
						log.Println("unknown escalation", act)
					}
				}
			}
		}
	}

	return e.Status
}

// returns true if this is a new incident, false if it is a duplicate
func (p *Pipeline) Dedupe(i *event.Incident) bool {
	old := p.index.GetIncident(i.IndexName())

	if old == nil {
		return i.Status != event.OK
	}

	return old.Status != i.Status
}

func (p *Pipeline) ListIncidents() []*event.Incident {
	return p.index.ListIncidents()
}

func (p *Pipeline) PutIncident(in *event.Incident) {
	if in.Id == 0 {
		in.Id = p.index.GetIncidentCounter()
		p.index.UpdateIncidentCounter(in.Id + 1)
	}
	p.index.PutIncident(in)
}

func (p *Pipeline) NewIncident(policy string, e *event.Event) *event.Incident {
	return event.NewIncident(policy, e)
}
<commit_msg>make sure the global policy is compiled<commit_after>package pipeline

import (
	"log"
	"runtime"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/eliothedeman/bangarang/alarm"
	"github.com/eliothedeman/bangarang/config"
	"github.com/eliothedeman/bangarang/event"
	"github.com/eliothedeman/bangarang/provider"
)

const (
	KEEP_ALIVE_SERVICE_NAME = "KeepAlive"
)

type Pipeline struct {
	keepAliveAge       time.Duration
	keepAliveCheckTime time.Duration
	globalPolicy       *alarm.Policy
	escalations        *alarm.Collection
	policies           map[string]*alarm.Policy
	index              *event.Index
	providers          provider.EventProviderCollection
	encodingPool       *event.EncodingPool
	config             *config.AppConfig
	tracker            *Tracker
	pauseCache         map[*event.Event]struct{}
	unpauseChan        chan struct{}
	in                 chan *event.Event
}

func NewPipeline(conf *config.AppConfig) *Pipeline {
	p := &Pipeline{
		encodingPool:       event.NewEncodingPool(event.EncoderFactories[conf.Encoding], event.DecoderFactories[conf.Encoding], runtime.NumCPU()),
		keepAliveAge:       conf.KeepAliveAge,
		keepAliveCheckTime: 30 * time.Second,
		in:                 make(chan *event.Event),
		unpauseChan:        make(chan struct{}),
	}

	p.Refresh(conf)

	logrus.Debug("Starting expiration checker")
	go p.checkExpired()

	return p
}

// refresh load all config params that don't require a restart
func (p *Pipeline) Refresh(conf *config.AppConfig) {
	p.pause()

	// if the config has changed at all, refresh the index
	if p.config == nil || string(conf.Hash) != string(p.config.Hash) {
		p.index = event.NewIndex()
		p.tracker = NewTracker()
		go p.tracker.Start()
	}

	// update optional config options
	if conf.Escalations != nil {
		p.escalations = conf.Escalations
	}

	if conf.EventProviders != nil {
		p.providers = *conf.EventProviders
	}

	p.policies = conf.Policies
	p.keepAliveAge = conf.KeepAliveAge
	p.globalPolicy = conf.GlobalPolicy

	if p.globalPolicy != nil {
		p.globalPolicy.Compile()
	}

	// update to the new config
	p.config = conf
	p.unpause()
	// start up all of the providers
	logrus.Infof("Starting %d providers", len(p.providers.Collection))
	for name, ep := range p.providers.Collection {
		logrus.Infof("Starting event provider %s", name)
		go ep.Start(p.in)
	}
}

// unpause resume processing jobs
func (p *Pipeline) unpause() {
	logrus.Info("Unpausing pipeline")
	p.unpauseChan <- struct{}{}
	<-p.unpauseChan
}

// pause stop processing events
func (p *Pipeline) pause() {
	logrus.Info("Pausing pipeline")

	// cache the old injest channel
	old := p.in

	// make a temporary channel to catch incomming events
	p.in = nil

	// make a channel to signal the end of the pause
	done := make(chan struct{})
	p.unpauseChan = done

	// start a new goroutine to catch the incomming events
	go func() {
		// make a map to cache the incomming events
		cache := make(map[*event.Event]struct{})
		var e *event.Event
		for {
			select {

			// start caching the events as they come in
			case e = <-old:
				logrus.Debugf("Caching event during pause %+v", *e)
				cache[e] = struct{}{}

			// when the pause is complete, revert to the old injestion channel
			case <-done:

				// set the cached event channel
				p.in = old

				// restart the pipeline
				p.Start()

				// empty the cache
				for e, _ = range cache {
					logrus.Debugf("Proccessing cached event after unpause %+v", *e)
					old <- e
				}

				// signal the unpause function that we are done with the unpause
				p.unpauseChan <- struct{}{}
				return
			}
		}
	}()
}

func (p *Pipeline) GetTracker() *Tracker {
	return p.tracker
}

func (p *Pipeline) GetConfig() *config.AppConfig {
	return p.config
}

func (p *Pipeline) checkExpired() {
	var events []*event.Event
	for {
		time.Sleep(p.keepAliveCheckTime)
		logrus.Info("Checking for expired events.")

		// get keepalive events for all known hosts
		events = createKeepAliveEvents(p.tracker.HostTimes())
		logrus.Infof("Found %d hosts with keepalives", len(events))

		// process every event as if it was an incomming event
		for _, e := range events {
			p.Process(e)
		}
	}
}

// create keep alive events for each hostname -> time pair
func createKeepAliveEvents(times map[string]time.Time) []*event.Event {
	n := time.Now()
	events := make([]*event.Event, len(times))
	x := 0
	for host, t := range times {
		events[x] = &event.Event{
			Host:    host,
			Metric:  n.Sub(t).Seconds(),
			Service: KEEP_ALIVE_SERVICE_NAME,
		}
		x += 1
	}

	return events
}

func (p *Pipeline) Start() {
	logrus.Info("Starting pipeline")

	// fan in all of the providers and process them
	go func() {
		var e *event.Event
		for {

			// if the injest channel is nil, stop
			if p.in == nil {
				logrus.Info("Pipeline is paused, stoping pipeline")
				return
			}

			// recieve the event
			e = <-p.in

			logrus.Debugf("Beginning processing %+v", e)
			// process the event
			p.Process(e)
			logrus.Debugf("Done processing %+v", e)
		}
	}()
}

// Run the given event though the pipeline
func (p *Pipeline) Process(e *event.Event) int {
	if p.globalPolicy != nil {
		if !p.globalPolicy.CheckMatch(e) || !p.globalPolicy.CheckNotMatch(e) {
			return event.OK
		}
	}

	// track stas for this event
	p.tracker.TrackEvent(e)

	for _, pol := range p.policies {
		if pol.Matches(e) {
			act := pol.Action(e)

			// if there is an action to be taken
			if act != "" {

				// create a new incident for this event
				in := p.NewIncident(pol.Name, e)

				// dedup the incident
				if p.Dedupe(in) {

					// update the incident in the index
					if in.Status != event.OK {
						p.index.PutIncident(in)
					} else {
						p.index.DeleteIncidentById(in.IndexName())
					}

					// fetch the escalation to take
					esc, ok := p.escalations.Collection()[act]
					if ok {

						// send to every alarm in the escalation
						for _, a := range esc {
							a.Send(in)
						}
					} else {
						log.Println("unknown escalation", act)
					}
				}
			}
		}
	}

	return e.Status
}

// returns true if this is a new incident, false if it is a duplicate
func (p *Pipeline) Dedupe(i *event.Incident) bool {
	old := p.index.GetIncident(i.IndexName())

	if old == nil {
		return i.Status != event.OK
	}

	return old.Status != i.Status
}

func (p *Pipeline) ListIncidents() []*event.Incident {
	return p.index.ListIncidents()
}

func (p *Pipeline) PutIncident(in *event.Incident) {
	if in.Id == 0 {
		in.Id = p.index.GetIncidentCounter()
		p.index.UpdateIncidentCounter(in.Id + 1)
	}
	p.index.PutIncident(in)
}

func (p *Pipeline) NewIncident(policy string, e *event.Event) *event.Incident {
	return event.NewIncident(policy, e)
}
<|endoftext|>


<commit_before>// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package file provides handlers that serve static files for the ozzo routing package.
package file

import (
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/go-ozzo/ozzo-routing"
)

// ServerOptions defines the possible options for the Server handler.
type ServerOptions struct {
	// The path that all files to be served should be located within. The path map passed to the Server method
	// are all relative to this path. This property can be specified as an absolute file path or a path relative
	// to the current working path. If not set, this property defaults to the current working path.
	RootPath string
	// The file (e.g. index.html) to be served when the current request corresponds to a directory.
	// If not set, the handler will return a 404 HTTP error when the request corresponds to a directory.
	// This should only be a file name without the directory part.
	IndexFile string
	// The file to be served when no file or directory matches the current request.
	// If not set, the handler will return a 404 HTTP error when no file/directory matches the request.
	// The path of this file is relative to RootPath
	CatchAllFile string
	// A function that checks if the requested file path is allowed. If allowed, the function
	// may do additional work such as setting Expires HTTP header.
	// The function should return a boolean indicating whether the file should be served or not.
	// If false, a 404 HTTP error will be returned by the handler.
	Allow func(*routing.Context, string) bool
}

// PathMap specifies the mapping between URL paths (keys) and file paths (keys).
// The file paths are relative to Options.RootPath
type PathMap map[string]string

// RootPath stores the current working path
var RootPath string

func init() {
	RootPath, _ = os.Getwd()
}

// Server returns a handler that serves the files as the response content.
// The files being served are determined using the current URL path and the specified path map.
// For example, if the path map is {"/css": "/www/css", "/js": "/www/js"} and the current URL path
// "/css/main.css", the file "<working dir>/www/css/main.css" will be served.
// If a URL path matches multiple prefixes in the path map, the most specific prefix will take precedence.
// For example, if the path map contains both "/css" and "/css/img", and the URL path is "/css/img/logo.gif",
// then the path mapped by "/css/img" will be used.
//
//     import (
//         "log"
//         "github.com/go-ozzo/ozzo-routing"
//         "github.com/go-ozzo/ozzo-routing/file"
//     )
//
//     r := routing.New()
//     r.Get("/*", file.Server(file.PathMap{
//          "/css": "/ui/dist/css",
//          "/js": "/ui/dist/js",
//     }))
func Server(pathMap PathMap, opts ...ServerOptions) routing.Handler {
	var options ServerOptions
	if len(opts) > 0 {
		options = opts[0]
	}
	if !filepath.IsAbs(options.RootPath) {
		options.RootPath = filepath.Join(RootPath, options.RootPath)
	}
	from, to := parsePathMap(pathMap)

	// security measure: limit the files within options.RootPath
	dir := http.Dir(options.RootPath)

	return func(c *routing.Context) error {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			return routing.NewHTTPError(http.StatusMethodNotAllowed)
		}
		path, found := matchPath(c.Request.URL.Path, from, to)
		if !found || options.Allow != nil && !options.Allow(c, path) {
			return routing.NewHTTPError(http.StatusNotFound)
		}

		var (
			file  http.File
			fstat os.FileInfo
			err   error
		)

		if file, err = dir.Open(path); err != nil {
			if options.CatchAllFile != "" {
				return serveFile(c, dir, options.CatchAllFile)
			}
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}
		defer file.Close()

		if fstat, err = file.Stat(); err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}

		if fstat.IsDir() {
			if options.IndexFile == "" {
				return routing.NewHTTPError(http.StatusNotFound)
			}
			return serveFile(c, dir, filepath.Join(path, options.IndexFile))
		}

		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
		return nil
	}
}

func serveFile(c *routing.Context, dir http.Dir, path string) error {
	file, err := dir.Open(path)
	if err != nil {
		return routing.NewHTTPError(http.StatusNotFound, err.Error())
	}
	defer file.Close()
	fstat, err := file.Stat()
	if err != nil {
		return routing.NewHTTPError(http.StatusNotFound, err.Error())
	} else if fstat.IsDir() {
		return routing.NewHTTPError(http.StatusNotFound)
	}
	http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
	return nil
}

// Content returns a handler that serves the content of the specified file as the response.
// The file to be served can be specified as an absolute file path or a path relative to RootPath (which
// defaults to the current working path).
// If the specified file does not exist, the handler will pass the control to the next available handler.
func Content(path string) routing.Handler {
	if !filepath.IsAbs(path) {
		path = filepath.Join(RootPath, path)
	}
	return func(c *routing.Context) error {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			return routing.NewHTTPError(http.StatusMethodNotAllowed)
		}
		file, err := os.Open(path)
		if err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}
		defer file.Close()
		fstat, err := file.Stat()
		if err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		} else if fstat.IsDir() {
			return routing.NewHTTPError(http.StatusNotFound)
		}
		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
		return nil
	}
}

func parsePathMap(pathMap PathMap) (from, to []string) {
	from = make([]string, len(pathMap))
	to = make([]string, len(pathMap))
	n := 0
	for i := range pathMap {
		from[n] = i
		n++
	}
	sort.Strings(from)
	for i, s := range from {
		to[i] = pathMap[s]
	}
	return
}

func matchPath(path string, from, to []string) (string, bool) {
	for i := len(from) - 1; i >= 0; i-- {
		prefix := from[i]
		if strings.HasPrefix(path, prefix) {
			return to[i] + path[len(prefix):], true
		}
	}
	return "", false
}
<commit_msg>fixed incorrect content-type when using file middleware<commit_after>// Copyright 2016 Qiang Xue. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package file provides handlers that serve static files for the ozzo routing package.
package file

import (
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/go-ozzo/ozzo-routing"
)

// ServerOptions defines the possible options for the Server handler.
type ServerOptions struct {
	// The path that all files to be served should be located within. The path map passed to the Server method
	// are all relative to this path. This property can be specified as an absolute file path or a path relative
	// to the current working path. If not set, this property defaults to the current working path.
	RootPath string
	// The file (e.g. index.html) to be served when the current request corresponds to a directory.
	// If not set, the handler will return a 404 HTTP error when the request corresponds to a directory.
	// This should only be a file name without the directory part.
	IndexFile string
	// The file to be served when no file or directory matches the current request.
	// If not set, the handler will return a 404 HTTP error when no file/directory matches the request.
	// The path of this file is relative to RootPath
	CatchAllFile string
	// A function that checks if the requested file path is allowed. If allowed, the function
	// may do additional work such as setting Expires HTTP header.
	// The function should return a boolean indicating whether the file should be served or not.
	// If false, a 404 HTTP error will be returned by the handler.
	Allow func(*routing.Context, string) bool
}

// PathMap specifies the mapping between URL paths (keys) and file paths (keys).
// The file paths are relative to Options.RootPath
type PathMap map[string]string

// RootPath stores the current working path
var RootPath string

func init() {
	RootPath, _ = os.Getwd()
}

// Server returns a handler that serves the files as the response content.
// The files being served are determined using the current URL path and the specified path map.
// For example, if the path map is {"/css": "/www/css", "/js": "/www/js"} and the current URL path
// "/css/main.css", the file "<working dir>/www/css/main.css" will be served.
// If a URL path matches multiple prefixes in the path map, the most specific prefix will take precedence.
// For example, if the path map contains both "/css" and "/css/img", and the URL path is "/css/img/logo.gif",
// then the path mapped by "/css/img" will be used.
//
//     import (
//         "log"
//         "github.com/go-ozzo/ozzo-routing"
//         "github.com/go-ozzo/ozzo-routing/file"
//     )
//
//     r := routing.New()
//     r.Get("/*", file.Server(file.PathMap{
//          "/css": "/ui/dist/css",
//          "/js": "/ui/dist/js",
//     }))
func Server(pathMap PathMap, opts ...ServerOptions) routing.Handler {
	var options ServerOptions
	if len(opts) > 0 {
		options = opts[0]
	}
	if !filepath.IsAbs(options.RootPath) {
		options.RootPath = filepath.Join(RootPath, options.RootPath)
	}
	from, to := parsePathMap(pathMap)

	// security measure: limit the files within options.RootPath
	dir := http.Dir(options.RootPath)

	return func(c *routing.Context) error {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			return routing.NewHTTPError(http.StatusMethodNotAllowed)
		}
		path, found := matchPath(c.Request.URL.Path, from, to)
		if !found || options.Allow != nil && !options.Allow(c, path) {
			return routing.NewHTTPError(http.StatusNotFound)
		}

		var (
			file  http.File
			fstat os.FileInfo
			err   error
		)

		if file, err = dir.Open(path); err != nil {
			if options.CatchAllFile != "" {
				return serveFile(c, dir, options.CatchAllFile)
			}
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}
		defer file.Close()

		if fstat, err = file.Stat(); err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}

		if fstat.IsDir() {
			if options.IndexFile == "" {
				return routing.NewHTTPError(http.StatusNotFound)
			}
			return serveFile(c, dir, filepath.Join(path, options.IndexFile))
		}

		c.Response.Header().Del("Content-Type")
		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
		return nil
	}
}

func serveFile(c *routing.Context, dir http.Dir, path string) error {
	file, err := dir.Open(path)
	if err != nil {
		return routing.NewHTTPError(http.StatusNotFound, err.Error())
	}
	defer file.Close()
	fstat, err := file.Stat()
	if err != nil {
		return routing.NewHTTPError(http.StatusNotFound, err.Error())
	} else if fstat.IsDir() {
		return routing.NewHTTPError(http.StatusNotFound)
	}
	c.Response.Header().Del("Content-Type")
	http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
	return nil
}

// Content returns a handler that serves the content of the specified file as the response.
// The file to be served can be specified as an absolute file path or a path relative to RootPath (which
// defaults to the current working path).
// If the specified file does not exist, the handler will pass the control to the next available handler.
func Content(path string) routing.Handler {
	if !filepath.IsAbs(path) {
		path = filepath.Join(RootPath, path)
	}
	return func(c *routing.Context) error {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			return routing.NewHTTPError(http.StatusMethodNotAllowed)
		}
		file, err := os.Open(path)
		if err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		}
		defer file.Close()
		fstat, err := file.Stat()
		if err != nil {
			return routing.NewHTTPError(http.StatusNotFound, err.Error())
		} else if fstat.IsDir() {
			return routing.NewHTTPError(http.StatusNotFound)
		}
		c.Response.Header().Del("Content-Type")
		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
		return nil
	}
}

func parsePathMap(pathMap PathMap) (from, to []string) {
	from = make([]string, len(pathMap))
	to = make([]string, len(pathMap))
	n := 0
	for i := range pathMap {
		from[n] = i
		n++
	}
	sort.Strings(from)
	for i, s := range from {
		to[i] = pathMap[s]
	}
	return
}

func matchPath(path string, from, to []string) (string, bool) {
	for i := len(from) - 1; i >= 0; i-- {
		prefix := from[i]
		if strings.HasPrefix(path, prefix) {
			return to[i] + path[len(prefix):], true
		}
	}
	return "", false
}
<|endoftext|>


<commit_before>package main

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

	"github.com/chris-ramon/graphql"
)

type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

var data map[string]user

/*
   Create User object type with fields "id" and "name" by using GraphQLObjectTypeConfig:
       - Name: name of object type
       - Fields: a map of fields by using GraphQLFieldConfigMap
   Setup type of field use GraphQLFieldConfig
*/
var userType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "User",
		Fields: graphql.FieldConfigMap{
			"id": &graphql.FieldConfig{
				Type: graphql.String,
			},
			"name": &graphql.FieldConfig{
				Type: graphql.String,
			},
		},
	},
)

/*
   Create Query object type with fields "user" has type [userType] by using GraphQLObjectTypeConfig:
       - Name: name of object type
       - Fields: a map of fields by using GraphQLFieldConfigMap
   Setup type of field use GraphQLFieldConfig to define:
       - Type: type of field
       - Args: arguments to query with current field
       - Resolve: function to query data using params from [Args] and return value with current type
*/
var queryType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Query",
		Fields: graphql.FieldConfigMap{
			"user": &graphql.FieldConfig{
				Type: userType,
				Args: graphql.FieldConfigArgument{
					"id": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
				},
				Resolve: func(p graphql.GQLFRParams) interface{} {
					idQuery, isOK := p.Args["id"].(string)
					if isOK {
						return data[idQuery]
					}
					return nil
				},
			},
		},
	})

var schema, _ = graphql.NewSchema(
	graphql.SchemaConfig{
		Query: queryType,
	},
)

func executeQuery(query string, schema graphql.Schema) *graphql.Result {
	params := graphql.Params{
		Schema:        schema,
		RequestString: query,
	}
	resultChannel := make(chan *graphql.Result)
	go graphql.Graphql(params, resultChannel)
	result := <-resultChannel
	if len(result.Errors) > 0 {
		fmt.Println("wrong result, unexpected errors: %v", result.Errors)
	}
	return result
}

func main() {
	_ = importJSONDataFromFile("data.json", &data)

	http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
		result := executeQuery(r.URL.Query()["query"][0], schema)
		json.NewEncoder(w).Encode(result)
	})

	fmt.Println("Now server is running on port 8080")
	fmt.Println("Test with Get      : curl -g \"http://localhost:8080/graphql?query={user(id:%221%22){name}}\"")
	http.ListenAndServe(":8080", nil)
}

//Helper function to import json from file to map
func importJSONDataFromFile(fileName string, result interface{}) (isOK bool) {
	isOK = true
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		fmt.Print("Error:", err)
		isOK = false
	}
	err = json.Unmarshal(content, result)
	if err != nil {
		isOK = false
		fmt.Print("Error:", err)
	}
	return
}
<commit_msg>Fix http example<commit_after>package main

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

	"github.com/chris-ramon/graphql"
)

type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

var data map[string]user

/*
   Create User object type with fields "id" and "name" by using GraphQLObjectTypeConfig:
       - Name: name of object type
       - Fields: a map of fields by using GraphQLFieldConfigMap
   Setup type of field use GraphQLFieldConfig
*/
var userType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "User",
		Fields: graphql.FieldConfigMap{
			"id": &graphql.FieldConfig{
				Type: graphql.String,
			},
			"name": &graphql.FieldConfig{
				Type: graphql.String,
			},
		},
	},
)

/*
   Create Query object type with fields "user" has type [userType] by using GraphQLObjectTypeConfig:
       - Name: name of object type
       - Fields: a map of fields by using GraphQLFieldConfigMap
   Setup type of field use GraphQLFieldConfig to define:
       - Type: type of field
       - Args: arguments to query with current field
       - Resolve: function to query data using params from [Args] and return value with current type
*/
var queryType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Query",
		Fields: graphql.FieldConfigMap{
			"user": &graphql.FieldConfig{
				Type: userType,
				Args: graphql.FieldConfigArgument{
					"id": &graphql.ArgumentConfig{
						Type: graphql.String,
					},
				},
				Resolve: func(p graphql.GQLFRParams) interface{} {
					idQuery, isOK := p.Args["id"].(string)
					if isOK {
						return data[idQuery]
					}
					return nil
				},
			},
		},
	})

var schema, _ = graphql.NewSchema(
	graphql.SchemaConfig{
		Query: queryType,
	},
)

func executeQuery(query string, schema graphql.Schema) *graphql.Result {
	result := graphql.Graphql(graphql.Params{
		Schema:        schema,
		RequestString: query,
	})
	if len(result.Errors) > 0 {
		fmt.Println("wrong result, unexpected errors: %v", result.Errors)
	}
	return result
}

func main() {
	_ = importJSONDataFromFile("data.json", &data)

	http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
		result := executeQuery(r.URL.Query()["query"][0], schema)
		json.NewEncoder(w).Encode(result)
	})

	fmt.Println("Now server is running on port 8080")
	fmt.Println("Test with Get      : curl -g \"http://localhost:8080/graphql?query={user(id:%221%22){name}}\"")
	http.ListenAndServe(":8080", nil)
}

//Helper function to import json from file to map
func importJSONDataFromFile(fileName string, result interface{}) (isOK bool) {
	isOK = true
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		fmt.Print("Error:", err)
		isOK = false
	}
	err = json.Unmarshal(content, result)
	if err != nil {
		isOK = false
		fmt.Print("Error:", err)
	}
	return
}
<|endoftext|>


<commit_before>package k

import (
	"time"

	"github.com/altairsix/pkg/types"
	"github.com/opentracing/opentracing-go/log"
)

// ID logs general id
func ID(id types.Key) log.Field {
	return log.String("id", id.String())
}

// OrgID logs org id
func OrgID(orgID types.Key) log.Field {
	return log.String("org", orgID.String())
}

// Elapsed time measured in seconds
func Elapsed(d time.Duration) log.Field {
	return log.Int("elapsed", int(d/time.Second))
}

// Delay measured in seconds
func Delay(d time.Duration) log.Field {
	return log.Int("delay", int(d/time.Second))
}
<commit_msg>added more standard log targets<commit_after>package k

import (
	"fmt"
	"time"

	"github.com/altairsix/pkg/types"
	"github.com/opentracing/opentracing-go/log"
)

// ID logs general id
func ID(id types.Key) log.Field {
	return log.String("id", id.String())
}

// OrgID logs org id
func OrgID(orgID types.Key) log.Field {
	return log.String("org", orgID.String())
}

// Elapsed time measured in seconds
func Elapsed(d time.Duration) log.Field {
	return log.Int("elapsed", int(d/time.Second))
}

// Delay measured in seconds
func Delay(d time.Duration) log.Field {
	return log.Int("delay", int(d/time.Second))
}

// Name provide a name field
func Name(name string) log.Field {
	return log.String("name", name)
}

// Method provides an http method
func Method(method string) log.Field {
	return log.String("method", method)
}

// StatusCode provides an http status code
func StatusCode(sc int) log.Field {
	return log.Int("status-code", sc)
}

// URL provides an http url
func URL(url string) log.Field {
	return log.String("url", url)
}

// Err provides an error
func Err(err error) log.Field {
	if err == nil {
		return log.Noop()
	}
	return log.Error(err)
}

// Key provides a standard key
func Key(key string) log.Field {
	return log.String("key", key)
}

// Duration measured in seconds
func Duration(d time.Duration) log.Field {
	return log.Int("duration", int(d/time.Second))
}

// Value allows the logging of arbitrary values
func Value(in interface{}) log.Field {
	switch v := in.(type) {
	case string:
		return log.String("value", v)
	case int:
		return log.Int("value", v)
	case int32:
		return log.Int32("value", v)
	case int64:
		return log.Int64("value", v)
	case float32:
		return log.Float32("value", v)
	case float64:
		return log.Float64("value", v)
	default:
		panic(fmt.Sprintf("unhandled type, %v", in))
	}
}
<|endoftext|>


<commit_before>package invdapi

import (
	"github.com/Invoiced/invoiced-go/invdendpoint"
)

func (c *Connection) ListTransaction(id int64) (*invdendpoint.Transaction, *APIError) {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)

	transaction := new(invdendpoint.Transaction)

	_, apiErr := c.retrieveDataFromAPI(endPoint, transaction)

	if apiErr != nil {
		return nil, apiErr
	}

	return transaction, apiErr

}

func (c *Connection) ListAllTransactionAuto(filter *invdendpoint.Filter, sort *invdendpoint.Sort) (*invdendpoint.Transactions, *APIError) {
	endPoint := c.makeEndPointURL(invdendpoint.TransactionsEndPoint)
	endPoint = addFilterSortToEndPoint(endPoint, filter, sort)

	transactions := new(invdendpoint.Transactions)

NEXT:
	tmpTransactions := new(invdendpoint.Transactions)

	endPoint, apiErr := c.retrieveDataFromAPI(endPoint, tmpTransactions)

	if apiErr != nil {
		return nil, apiErr
	}

	*transactions = append(*transactions, *tmpTransactions...)

	if endPoint != "" {
		goto NEXT
	}

	return transactions, apiErr

}

func (c *Connection) DeleteTransaction(id int64) *APIError {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)

	apiErr := c.delete(endPoint)

	return apiErr

}

func (c *Connection) UpdateTransaction(id int64, transactionToUpdate *invdendpoint.Transaction) (*invdendpoint.Transaction, *APIError) {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)
	transactionCreated := new(invdendpoint.Transaction)

	apiErr := c.update(endPoint, transactionToUpdate, transactionCreated)

	return transactionCreated, apiErr

}

func (c *Connection) CreateTransaction(transactionToCreate *invdendpoint.Transaction) (*invdendpoint.Transaction, *APIError) {
	endPoint := c.makeEndPointURL(invdendpoint.TransactionsEndPoint)

	transactionCreated := new(invdendpoint.Transaction)

	apiErr := c.create(endPoint, transactionToCreate, transactionCreated)

	return transactionCreated, apiErr

}
<commit_msg>Fixed typo<commit_after>package invdapi

import (
	"github.com/Invoiced/invoiced-go/invdendpoint"
)

func (c *Connection) ListTransaction(id int64) (*invdendpoint.Transaction, *APIError) {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)

	transaction := new(invdendpoint.Transaction)

	_, apiErr := c.retrieveDataFromAPI(endPoint, transaction)

	if apiErr != nil {
		return nil, apiErr
	}

	return transaction, apiErr

}

func (c *Connection) ListAllTransactionsAuto(filter *invdendpoint.Filter, sort *invdendpoint.Sort) (*invdendpoint.Transactions, *APIError) {
	endPoint := c.makeEndPointURL(invdendpoint.TransactionsEndPoint)
	endPoint = addFilterSortToEndPoint(endPoint, filter, sort)

	transactions := new(invdendpoint.Transactions)

NEXT:
	tmpTransactions := new(invdendpoint.Transactions)

	endPoint, apiErr := c.retrieveDataFromAPI(endPoint, tmpTransactions)

	if apiErr != nil {
		return nil, apiErr
	}

	*transactions = append(*transactions, *tmpTransactions...)

	if endPoint != "" {
		goto NEXT
	}

	return transactions, apiErr

}

func (c *Connection) DeleteTransaction(id int64) *APIError {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)

	apiErr := c.delete(endPoint)

	return apiErr

}

func (c *Connection) UpdateTransaction(id int64, transactionToUpdate *invdendpoint.Transaction) (*invdendpoint.Transaction, *APIError) {
	endPoint := makeEndPointSingular(c.makeEndPointURL(invdendpoint.TransactionsEndPoint), id)
	transactionCreated := new(invdendpoint.Transaction)

	apiErr := c.update(endPoint, transactionToUpdate, transactionCreated)

	return transactionCreated, apiErr

}

func (c *Connection) CreateTransaction(transactionToCreate *invdendpoint.Transaction) (*invdendpoint.Transaction, *APIError) {
	endPoint := c.makeEndPointURL(invdendpoint.TransactionsEndPoint)

	transactionCreated := new(invdendpoint.Transaction)

	apiErr := c.create(endPoint, transactionToCreate, transactionCreated)

	return transactionCreated, apiErr

}
<|endoftext|>


<commit_before>package app

import (
	r "github.com/robfig/revel"
	"runtime"
)

func Init() {
	numCPU := runtime.NumCPU()
	gomaxprocs := runtime.GOMAXPROCS(numCPU)

	r.WARN.Printf("GOMAXPROCS was %v, Total CPU detected %v, set as new GOMAXPROCS", gomaxprocs, numCPU)
}

func init() {
	r.Filters = []r.Filter{
		r.PanicFilter,             // Recover from panics and display an error page instead.
		r.RouterFilter,            // Use the routing table to select the right Action
		r.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
		r.ParamsFilter,            // Parse parameters into Controller.Params.
		r.SessionFilter,           // Restore and write the session cookie.
		r.FlashFilter,             // Restore and write the flash cookie.
		r.ValidationFilter,        // Restore kept validation errors and save new ones from cookie.
		r.I18nFilter,              // Resolve the requested language
		r.InterceptorFilter,       // Run interceptors around the action.
		r.ActionInvoker,           // Invoke the action.
	}

	r.OnAppStart(Init)
}
<commit_msg>init.go: r. -> revel.<commit_after>package app

import (
	"github.com/robfig/revel"
	"runtime"
)

func Init() {
	numCPU := runtime.NumCPU()
	gomaxprocs := runtime.GOMAXPROCS(numCPU)

	revel.WARN.Printf("GOMAXPROCS was %v, Total CPU detected %v, set as new GOMAXPROCS", gomaxprocs, numCPU)
}

func init() {
	revel.Filters = []revel.Filter{
		revel.PanicFilter,             // Recover from panics and display an error page instead.
		revel.RouterFilter,            // Use the routing table to select the right Action
		revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
		revel.ParamsFilter,            // Parse parameters into Controller.Params.
		revel.SessionFilter,           // Restore and write the session cookie.
		revel.FlashFilter,             // Restore and write the flash cookie.
		revel.ValidationFilter,        // Restore kept validation errors and save new ones from cookie.
		revel.I18nFilter,              // Resolve the requested language
		revel.InterceptorFilter,       // Run interceptors around the action.
		revel.ActionInvoker,           // Invoke the action.
	}

	revel.OnAppStart(Init)
}
<|endoftext|>