<commit_before>package main

import "github.com/dynport/urknall"

type Postgres struct {
	Version string `urknall:"required=true"` // 9.3.4
	DataDir string `urknall:"default=/data/postgres"`
	User    string `urknall:"default=postgres"`

	InitDb bool
}

func (pkg *Postgres) Render(r urknall.Package) {
	r.AddCommands("packages",
		InstallPackages("build-essential", "openssl", "libssl-dev", "flex", "zlib1g-dev", "libxslt1-dev", "libxml2-dev", "python-dev", "libreadline-dev", "bison"),
	)
	r.AddCommands("download", DownloadAndExtract("{{ .Url }}", "/opt/src/"))
	r.AddCommands("user", AddUser("{{ .User }}", true))
	r.AddCommands("build",
		And(
			"cd /opt/src/postgresql-{{ .Version }}",
			"./configure --prefix={{ .InstallDir }}",
			"make",
			"make install",
		),
	)
	r.AddCommands("upstart",
		WriteFile("/etc/init/postgres.conf", postgresUpstart, "root", 0644),
	)
	if pkg.InitDb {
		r.AddCommands("init_db",
			Mkdir(pkg.DataDir, "{{ .User }}", 0755),
			AsUser("{{ .User }}", "{{ .InstallDir }}/bin/initdb -D {{ .DataDir }} -E utf8 --auth-local=trust"),
		)
	}
}

func (p *Postgres) InstallDir() string {
	if p.Version == "" {
		panic("Version must be set")
	}
	return "/opt/postgresql-" + p.Version
}

// some helpers for e.g. database creation
type PostgresDatabase struct {
	Name  string
	Owner string
}

func (db *PostgresDatabase) CreateCommand() string {
	cmd := "CREATE DATABASE " + db.Name
	if db.Owner != "" {
		cmd += " OWNER=" + db.Owner
	}
	return cmd
}

type PostgresUser struct {
	Name     string
	Password string
}

func (user *PostgresUser) CreateCommand() string {
	cmd := "CREATE USER " + user.Name
	if user.Password != "" {
		cmd += " WITH PASSWORD '" + user.Password + "'"
	}
	return cmd
}

func (pkg *Postgres) CreateDatabaseCommand(db *PostgresDatabase) string {
	return pkg.InstallDir() + "/bin/" + `psql -U postgres -c "` + db.CreateCommand() + `"`
}

func (pkg *Postgres) CreateUserCommand(user *PostgresUser) string {
	return pkg.InstallDir() + "/bin/" + `psql -U postgres -c "` + user.CreateCommand() + `"`
}

const postgresUpstart = `
start on runlevel [2345]
stop on runlevel [!2345]
setuid {{ .User }}
exec {{ .InstallDir }}/bin/postgres -D {{ .DataDir }}
`

func (pkg *Postgres) Url() string {
	return "http://ftp.postgresql.org/pub/source/v{{ .Version }}/postgresql-{{ .Version }}.tar.gz"
}
<commit_msg>fix postgres example<commit_after>package main

import "github.com/dynport/urknall"

type Postgres struct {
	Version string `urknall:"required=true"` // 9.3.4
	DataDir string `urknall:"default=/data/postgres"`
	User    string `urknall:"default=postgres"`

	InitDb bool
}

func (pkg *Postgres) Render(r urknall.Package) {
	r.AddCommands("packages",
		InstallPackages("build-essential", "openssl", "libssl-dev", "flex", "zlib1g-dev", "libxslt1-dev", "libxml2-dev", "python-dev", "libreadline-dev", "bison"),
	)
	r.AddCommands("download", DownloadAndExtract("{{ .Url }}", "/opt/src/"))
	r.AddCommands("user", AddUser("{{ .User }}", true))
	r.AddCommands("build",
		And(
			"cd /opt/src/postgresql-{{ .Version }}",
			"./configure --prefix={{ .InstallDir }}",
			"make",
			"make install",
		),
	)
	r.AddCommands("upstart",
		WriteFile("/etc/init/postgres.conf", postgresUpstart, "root", 0644),
	)
	if pkg.InitDb {
		user := pkg.User
		if user == "" {
			user = "postgres"
		}
		r.AddCommands("init_db",
			Mkdir(pkg.DataDir, "{{ .User }}", 0755),
			AsUser(user, "{{ .InstallDir }}/bin/initdb -D {{ .DataDir }} -E utf8 --auth-local=trust"),
		)
	}
}

func (p *Postgres) InstallDir() string {
	if p.Version == "" {
		panic("Version must be set")
	}
	return "/opt/postgresql-" + p.Version
}

// some helpers for e.g. database creation
type PostgresDatabase struct {
	Name  string
	Owner string
}

func (db *PostgresDatabase) CreateCommand() string {
	cmd := "CREATE DATABASE " + db.Name
	if db.Owner != "" {
		cmd += " OWNER=" + db.Owner
	}
	return cmd
}

type PostgresUser struct {
	Name     string
	Password string
}

func (user *PostgresUser) CreateCommand() string {
	cmd := "CREATE USER " + user.Name
	if user.Password != "" {
		cmd += " WITH PASSWORD '" + user.Password + "'"
	}
	return cmd
}

func (pkg *Postgres) CreateDatabaseCommand(db *PostgresDatabase) string {
	return pkg.InstallDir() + "/bin/" + `psql -U postgres -c "` + db.CreateCommand() + `"`
}

func (pkg *Postgres) CreateUserCommand(user *PostgresUser) string {
	return pkg.InstallDir() + "/bin/" + `psql -U postgres -c "` + user.CreateCommand() + `"`
}

const postgresUpstart = `
start on runlevel [2345]
stop on runlevel [!2345]
setuid {{ .User }}
exec {{ .InstallDir }}/bin/postgres -D {{ .DataDir }}
`

func (pkg *Postgres) Url() string {
	return "http://ftp.postgresql.org/pub/source/v{{ .Version }}/postgresql-{{ .Version }}.tar.gz"
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"net/url"
	id3 "github.com/mikkyang/id3-go"
)

const (
	Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [dir]\n", os.Args[0])
}

type Rss struct {
	XMLName   xml.Name `xml:"rss"`
	Channel Channel `xml:"channel"`
	Version string  `xml:"version,attr"`
	NS 		string		`xml:"xmlns:itunes,attr"`
}

type Channel struct {
	Title       string `xml:"title"`
	Link        string `xml:"link"`
	Description string `xml:"description"`
	Items       []Item `xml:"item"`
}

type Item struct {
	Title       string `xml:"title"`
	Description string `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string          `xml:"guid"`
	Subtitle	string  `xml:"itunes:subtitle"`
}

type Enclosure struct {
	Url         string `xml:"url,attr"`
	Length      int64  `xml:"length,attr"`
	Type        string `xml:"type,attr"`
}

func fileUrl(f os.FileInfo, baseUrl string) ( string, error) {
	Url, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	Url.Path += f.Name()
	return Url.String(), nil
}

func title(path string, f os.FileInfo, item *Item) string {
	fd, err := id3.Open(path)
	name := ""
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title();
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author;
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
	}
	return name
}

func visitFiles(channel *Channel, publicUrl string) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			fmt.Println(err) // can't walk here,
			return nil
		}

		if !!f.IsDir() {
			return nil // not a file. ignore
		}

		matched, err := filepath.Match("*.mp3", f.Name())
		if err != nil {
			fmt.Println(err) // malformed pattern
			return err
		}

		if matched {
			url, err := fileUrl(f, publicUrl)
			if err != nil {
				return err
			}

			enclosure := Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url:url}
			item := Item{Enclosure: enclosure}
			title(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

func main() {
	flag.Parse()

	if flag.NArg() > 1 {
		usage()
		os.Exit(-1)
	}

	var workDir = os.Args[0]
	if flag.NArg() == 1 {
		workDir = flag.Arg(0)
	}

	publicUrl := "http://localhost:8080"
	channel := &Channel{Title: "RSS FEED"}
	err := filepath.Walk(workDir, visitFiles(channel, publicUrl))

	if err != nil {
		fmt.Printf("error: %v\n", err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS:"http://www.itunes.com/dtds/podcast-1.0.dtd"}, " ", "	")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<commit_msg>rename func for adding meta to item, change publicUrl<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"net/url"
	id3 "github.com/mikkyang/id3-go"
)

const (
	Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
)

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [dir]\n", os.Args[0])
}

type Rss struct {
	XMLName   xml.Name `xml:"rss"`
	Channel Channel `xml:"channel"`
	Version string  `xml:"version,attr"`
	NS 		string		`xml:"xmlns:itunes,attr"`
}

type Channel struct {
	Title       string `xml:"title"`
	Link        string `xml:"link"`
	Description string `xml:"description"`
	Items       []Item `xml:"item"`
}

type Item struct {
	Title       string `xml:"title"`
	Description string `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string          `xml:"guid"`
	Subtitle	string  `xml:"itunes:subtitle"`
}

type Enclosure struct {
	Url         string `xml:"url,attr"`
	Length      int64  `xml:"length,attr"`
	Type        string `xml:"type,attr"`
}

func fileUrl(f os.FileInfo, baseUrl string) ( string, error) {
	Url, err := url.Parse(baseUrl)
	if err != nil {
		return "", err
	}
	Url.Path += f.Name()
	return Url.String(), nil
}

func addMeta(path string, f os.FileInfo, item *Item) string {
	fd, err := id3.Open(path)
	name := ""
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title();
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author;
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
	}
	return name
}

func visitFiles(channel *Channel, publicUrl string) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			fmt.Println(err) // can't walk here,
			return nil
		}

		if !!f.IsDir() {
			return nil // not a file. ignore
		}

		matched, err := filepath.Match("*.mp3", f.Name())
		if err != nil {
			fmt.Println(err) // malformed pattern
			return err
		}

		if matched {
			url, err := fileUrl(f, publicUrl)
			if err != nil {
				return err
			}

			enclosure := Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url:url}
			item := Item{Enclosure: enclosure, Guid : enclosure.Url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

func main() {
	flag.Parse()

	if flag.NArg() > 1 {
		usage()
		os.Exit(-1)
	}

	var workDir = os.Args[0]
	if flag.NArg() == 1 {
		workDir = flag.Arg(0)
	}

	publicUrl := "http://kirst.local:8080"
	channel := &Channel{Title: "RSS FEED", Link: publicUrl}
	err := filepath.Walk(workDir, visitFiles(channel, publicUrl))

	if err != nil {
		fmt.Printf("error: %v\n", err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS:"http://www.itunes.com/dtds/podcast-1.0.dtd"}, " ", "	")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	kingpin "gopkg.in/alecthomas/kingpin.v1"
	"net/url"
	"os"
	"path/filepath"
	"time"
)

const (
	Header   = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
	iTunesNs = "http://www.itunes.com/dtds/podcast-1.0.dtd"
)

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	Title       string  `xml:"title,omitempty"`
	Link        string  `xml:"link,omitempty"`
	Description string  `xml:"description,omitempty"`
	Language    string  `xml:"language,omitempty"`
	Images      []Image `xml:"image,omitempty"`
	Items       []Item  `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle"`
	PubDate     string    `xml:"pubDate,omitempty"`
}

type Image struct {
	Link  string `xml:"link"`
	Title string `xml:"title"`
	Url   string `xml:"url"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(relativePath string, baseUrl string) string {
	Url, _ := url.Parse(baseUrl)
	Url.Path += relativePath
	return Url.String()
}

func formatYear(year string) string {
	if len(year) > 0 {
		t, err := time.Parse("20060102", year)
		if err != nil {
			t, err = time.Parse("20060102", year[0:len(year)-1])
			if err == nil {
				return t.String()
			}
			t, err = time.Parse("2006", year)
			if err == nil {
				return t.String()
			}
			t, err = time.Parse("20060201", year)
			if err != nil {
				return ""
			}
		}
		return t.String()
	}
	return year
}

func addMeta(path string, f os.FileInfo, item *Item) {
	fd, err := id3.Open(path)
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
		item.PubDate = formatYear(fd.Year())
	}
}

func visitFiles(workDir string, channel *Channel, publicUrl string, recursive bool) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if f.IsDir() && path != workDir && !recursive {
			return filepath.SkipDir
		}

		if !!f.IsDir() {
			return nil
		}

		matched, _ := filepath.Match("*.mp3", f.Name())
		if matched {
			url := fileUrl(path[len(workDir)-1:], publicUrl)
			item := Item{Enclosure: Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}, Guid: url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

var (
	baseUrl     = kingpin.Flag("server", "hostname (and path) to the root e.g. http://myserver.com/rss").Short('s').Default("http://localhost").URL()
	recursive   = kingpin.Flag("recursive", "how to handle the directory scan").Short('r').Bool()
	language    = kingpin.Flag("language", "the language of the RSS document, a ISO 639 value").Short('l').String()
	title       = kingpin.Flag("title", "RSS channel title").Short('t').Default("RSS FEED").String()
	description = kingpin.Flag("description", "RSS channel description").Short('d').String()
	imageUrl    = kingpin.Flag("image", "Image URL for the RSS channel image").Short('i').URL()
	path        = kingpin.Arg("directory", "directory to read files relative from").Required().ExistingDir()
)

func main() {

	kingpin.Version("0.0.1")
	kingpin.Parse()

	channel := &Channel{
		Title:       *title,
		Link:        (*baseUrl).String(),
		Description: *description,
		Language:    *language}

	err := filepath.Walk(*path, visitFiles(*path, channel, (*baseUrl).String(), *recursive))
	if *imageUrl != nil {
		channel.Images = append(channel.Images, Image{Title: channel.Title, Link: channel.Link, Url: (*imageUrl).String()})
	}

	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: iTunesNs}, "", "  ")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<commit_msg>add pubDate to the Channel element<commit_after>package main

import (
	"encoding/xml"
	"fmt"
	id3 "github.com/mikkyang/id3-go"
	kingpin "gopkg.in/alecthomas/kingpin.v1"
	"net/url"
	"os"
	"path/filepath"
	"time"
)

const (
	Header   = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
	iTunesNs = "http://www.itunes.com/dtds/podcast-1.0.dtd"
)

type Rss struct {
	XMLName xml.Name `xml:"rss"`
	Channel Channel  `xml:"channel"`
	Version string   `xml:"version,attr"`
	NS      string   `xml:"xmlns:itunes,attr"`
}

type Channel struct {
	PubDate     string  `xml:"pubDate,omitempty"`
	Title       string  `xml:"title,omitempty"`
	Link        string  `xml:"link,omitempty"`
	Description string  `xml:"description,omitempty"`
	Language    string  `xml:"language,omitempty"`
	Images      []Image `xml:"image,omitempty"`
	Items       []Item  `xml:"item"`
}

type Item struct {
	Title       string    `xml:"title"`
	Description string    `xml:"description"`
	Enclosure   Enclosure `xml:"enclosure"`
	Guid        string    `xml:"guid"`
	Subtitle    string    `xml:"itunes:subtitle"`
	PubDate     string    `xml:"pubDate,omitempty"`
}

type Image struct {
	Link  string `xml:"link"`
	Title string `xml:"title"`
	Url   string `xml:"url"`
}

type Enclosure struct {
	Url    string `xml:"url,attr"`
	Length int64  `xml:"length,attr"`
	Type   string `xml:"type,attr"`
}

func fileUrl(relativePath string, baseUrl string) string {
	Url, _ := url.Parse(baseUrl)
	Url.Path += relativePath
	return Url.String()
}

func formatYear(year string) string {
	if len(year) > 0 {
		t, err := time.Parse("20060102", year)
		if err != nil {
			t, err = time.Parse("20060102", year[0:len(year)-1])
			if err == nil {
				return t.String()
			}
			t, err = time.Parse("2006", year)
			if err == nil {
				return t.String()
			}
			t, err = time.Parse("20060201", year)
			if err != nil {
				return ""
			}
		}
		return t.String()
	}
	return year
}

func addMeta(path string, f os.FileInfo, item *Item) {
	fd, err := id3.Open(path)
	if err != nil {
		item.Title = f.Name()
	} else {
		title := fd.Title()
		author := fd.Artist()
		if len(title) > 0 {
			item.Title = title
		} else {
			item.Title = author
			if len(author) > 0 {
				item.Title += " - "
			}
			item.Title += f.Name()
		}
		item.Subtitle = author
		item.PubDate = formatYear(fd.Year())
	}
}

func visitFiles(workDir string, channel *Channel, publicUrl string, recursive bool) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if f.IsDir() && path != workDir && !recursive {
			return filepath.SkipDir
		}

		if !!f.IsDir() {
			return nil
		}

		matched, _ := filepath.Match("*.mp3", f.Name())
		if matched {
			url := fileUrl(path[len(workDir)-1:], publicUrl)
			item := Item{Enclosure: Enclosure{Length: f.Size(), Type: "audio/mpeg",
				Url: url}, Guid: url}
			addMeta(path, f, &item)
			channel.Items = append(channel.Items, item)
		}

		return nil

	}
}

var (
	baseUrl     = kingpin.Flag("server", "hostname (and path) to the root e.g. http://myserver.com/rss").Short('s').Default("http://localhost").URL()
	recursive   = kingpin.Flag("recursive", "how to handle the directory scan").Short('r').Bool()
	language    = kingpin.Flag("language", "the language of the RSS document, a ISO 639 value").Short('l').String()
	title       = kingpin.Flag("title", "RSS channel title").Short('t').Default("RSS FEED").String()
	description = kingpin.Flag("description", "RSS channel description").Short('d').String()
	imageUrl    = kingpin.Flag("image", "Image URL for the RSS channel image").Short('i').URL()
	path        = kingpin.Arg("directory", "directory to read files relative from").Required().ExistingDir()
)

func main() {

	kingpin.Version("0.0.1")
	kingpin.Parse()

	channel := &Channel{
		PubDate:     time.Now().String(),
		Title:       *title,
		Link:        (*baseUrl).String(),
		Description: *description,
		Language:    *language}

	err := filepath.Walk(*path, visitFiles(*path, channel, (*baseUrl).String(), *recursive))
	if *imageUrl != nil {
		channel.Images = append(channel.Images, Image{Title: channel.Title, Link: channel.Link, Url: (*imageUrl).String()})
	}

	if err != nil {
		fmt.Printf("%s: %v\n", os.Args[0], err)
	} else {
		output, err := xml.MarshalIndent(
			&Rss{Channel: *channel, Version: "2.0", NS: iTunesNs}, "", "  ")
		if err != nil {
			fmt.Printf("error: %v\n", err)
		} else {
			os.Stdout.WriteString(Header)
			os.Stdout.Write(output)
		}
	}

}
<|endoftext|>


<commit_before>package koding

import (
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/pkg/multierrors"
	"sync"
	"time"

	"golang.org/x/net/context"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

var (
	// Be cautious if your try to lower this. A build might really last more
	// than 10 minutes.
	CleanUpTimeout = time.Minute * 10
)

// RunCleaner runs the given cleaners for the given timeout duration. It cleans
// up/resets machine documents and VM leftovers.
func (p *Provider) RunCleaners(interval time.Duration) {
	cleaners := func() {
		// run for the first time
		if err := p.CleanLocks(CleanUpTimeout); err != nil {
			p.Log.Warning("Cleaning locks: %s", err)
		}

		if err := p.CleanDeletedVMs(); err != nil {
			p.Log.Warning("Cleaning deleted vms: %s", err)
		}

		if err := p.CleanStates(CleanUpTimeout); err != nil {
			p.Log.Warning("Cleaning states vms: %s", err)
		}
	}

	cleaners()
	for _ = range time.Tick(interval) {
		go cleaners() // do not block
	}
}

// CleanDeletedVMS deletes VMs that have userDeleted set to true. This happens
// when the user deletes their account but kloud couldn't delete/terminate
// their VM's. This can happen for example if the user deletes their VM during
// an ongoing (build, start, stop...) process. There will be a lock due Build
// which will prevent to delete it.
func (p *Provider) CleanDeletedVMs() error {
	machines := make([]Machine, 0)

	query := func(c *mgo.Collection) error {
		deletedMachines := bson.M{
			"userDeleted": true,
		}

		machine := Machine{}
		iter := c.Find(deletedMachines).Batch(50).Iter()
		for iter.Next(&machine) {
			machines = append(machines, machine)
		}

		return iter.Close()
	}

	if err := p.DB.Run("jMachines", query); err != nil {
		return err
	}

	deleteMachine := func(m Machine) error {
		ctx := context.Background()
		if err := p.attachSession(ctx, &m); err != nil {
			return err
		}

		if m.Meta.InstanceId == "" {
			// if there is no instance Id just remove the document
			return m.DeleteDocument()
		}

		m.Log.Info("cleaner: terminating user deleted machine. User %s", m.Username)
		return m.Destroy(context.Background())
	}

	for _, machine := range machines {
		go func(m Machine) {
			if err := deleteMachine(m); err != nil {
				m.Log.Error("couldn't terminate user deleted machine: %s", err.Error())
			}
		}(machine)
	}

	return nil
}

// CleanLocks resets documents that where locked by workers who died and left
// the documents untouched/unlocked. These documents are *ghost* documents,
// because they have an assignee that is not nil no worker will pick it up. The
// given timeout specificies to look up documents from now on.
func (p *Provider) CleanLocks(timeout time.Duration) error {
	query := func(c *mgo.Collection) error {
		// machines that can't be updated because they seems to be in progress
		ghostMachines := bson.M{
			"assignee.inProgress": true,
			"assignee.assignedAt": bson.M{"$lt": time.Now().UTC().Add(-timeout)},
		}

		cleanMachines := bson.M{
			"assignee.inProgress": false,
			"assignee.assignedAt": time.Now().UTC(),
		}

		// reset all machines
		info, err := c.UpdateAll(ghostMachines, bson.M{"$set": cleanMachines})
		if err != nil {
			return err
		}

		// only show if there is something, that will prevent spamming the
		// output with the same content over and over
		if info.Updated != 0 {
			p.Log.Info("[checker] cleaned up %d documents", info.Updated)
		}

		return nil
	}

	return p.DB.Run("jMachines", query)
}

// CleanStates resets documents that has machine states in progress mode
// (building, stopping, etc..) which weren't updated since 10 minutes. This
// could be caused because of Kloud restarts or panics.
func (p *Provider) CleanStates(timeout time.Duration) error {
	cleanstateFunc := func(badstate, goodstate string) error {
		return p.DB.Run("jMachines", func(c *mgo.Collection) error {
			// machines that can't be updated because they seems to be in progress
			badstateMachines := bson.M{
				"assignee.inProgress": false, // never update during a onging process :)
				"status.state":        badstate,
				"status.modifiedAt":   bson.M{"$lt": time.Now().UTC().Add(-timeout)},
			}

			cleanMachines := bson.M{
				"status.state":      goodstate,
				"status.modifiedAt": time.Now().UTC(),
			}

			// reset all machines
			info, err := c.UpdateAll(badstateMachines, bson.M{"$set": cleanMachines})
			if err != nil {
				return err
			}

			// only show if there is something, that will prevent spamming the
			// output with the same content over and over
			if info.Updated != 0 {
				p.Log.Info("[state cleaner] fixed %d documents from '%s' to '%s'",
					info.Updated, badstate, goodstate)
			}

			return nil
		})
	}

	progressModes := []struct {
		bad  string
		good string
	}{
		// our "build" method can continue with the leftover data that was
		// updated to MongoDB during the "build" process. So setting the state
		// to NotInitialized will make it to continue from where it was left.
		{machinestate.Building.String(), machinestate.NotInitialized.String()},
		{machinestate.Terminating.String(), machinestate.Terminated.String()},
		// once stopped, always stopped.
		{machinestate.Stopping.String(), machinestate.Stopped.String()},
		// the final state is  "stopped" because we don't know the if the
		// domains are updated or if the machien was really started. If the
		// machine is alrady started, the "start" method won't start it again,
		// so setting it to "stopped" will at least make it updating the
		// domains.
		{machinestate.Starting.String(), machinestate.Stopped.String()},
	}

	errs := multierrors.New()

	var wg sync.WaitGroup
	for _, state := range progressModes {
		wg.Add(1)
		go func(bad, good string) {
			defer wg.Done()
			err := cleanstateFunc(bad, good)
			errs.Add(err)
		}(state.bad, state.good)
	}

	wg.Wait()

	// if there is no errors just return a nil
	if errs.Len() == 0 {
		return nil
	}

	return errs
}
<commit_msg>kloud: do not ready to use logger<commit_after>package koding

import (
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/pkg/multierrors"
	"sync"
	"time"

	"golang.org/x/net/context"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

var (
	// Be cautious if your try to lower this. A build might really last more
	// than 10 minutes.
	CleanUpTimeout = time.Minute * 10
)

// RunCleaner runs the given cleaners for the given timeout duration. It cleans
// up/resets machine documents and VM leftovers.
func (p *Provider) RunCleaners(interval time.Duration) {
	cleaners := func() {
		// run for the first time
		if err := p.CleanLocks(CleanUpTimeout); err != nil {
			p.Log.Warning("Cleaning locks: %s", err)
		}

		if err := p.CleanDeletedVMs(); err != nil {
			p.Log.Warning("Cleaning deleted vms: %s", err)
		}

		if err := p.CleanStates(CleanUpTimeout); err != nil {
			p.Log.Warning("Cleaning states vms: %s", err)
		}
	}

	cleaners()
	for _ = range time.Tick(interval) {
		go cleaners() // do not block
	}
}

// CleanDeletedVMS deletes VMs that have userDeleted set to true. This happens
// when the user deletes their account but kloud couldn't delete/terminate
// their VM's. This can happen for example if the user deletes their VM during
// an ongoing (build, start, stop...) process. There will be a lock due Build
// which will prevent to delete it.
func (p *Provider) CleanDeletedVMs() error {
	machines := make([]Machine, 0)

	query := func(c *mgo.Collection) error {
		deletedMachines := bson.M{
			"userDeleted": true,
		}

		machine := Machine{}
		iter := c.Find(deletedMachines).Batch(50).Iter()
		for iter.Next(&machine) {
			machines = append(machines, machine)
		}

		return iter.Close()
	}

	if err := p.DB.Run("jMachines", query); err != nil {
		return err
	}

	deleteMachine := func(m Machine) error {
		ctx := context.Background()
		if err := p.attachSession(ctx, &m); err != nil {
			return err
		}

		if m.Meta.InstanceId == "" {
			// if there is no instance Id just remove the document
			return m.DeleteDocument()
		}

		m.Log.Info("cleaner: terminating user deleted machine. User %s", m.Username)
		return m.Destroy(context.Background())
	}

	for _, machine := range machines {
		go func(m Machine) {
			if err := deleteMachine(m); err != nil {
				p.Log.Error("[%s] couldn't terminate user deleted machine: %s", m.Id.Hex(), err.Error())
			}
		}(machine)
	}

	return nil
}

// CleanLocks resets documents that where locked by workers who died and left
// the documents untouched/unlocked. These documents are *ghost* documents,
// because they have an assignee that is not nil no worker will pick it up. The
// given timeout specificies to look up documents from now on.
func (p *Provider) CleanLocks(timeout time.Duration) error {
	query := func(c *mgo.Collection) error {
		// machines that can't be updated because they seems to be in progress
		ghostMachines := bson.M{
			"assignee.inProgress": true,
			"assignee.assignedAt": bson.M{"$lt": time.Now().UTC().Add(-timeout)},
		}

		cleanMachines := bson.M{
			"assignee.inProgress": false,
			"assignee.assignedAt": time.Now().UTC(),
		}

		// reset all machines
		info, err := c.UpdateAll(ghostMachines, bson.M{"$set": cleanMachines})
		if err != nil {
			return err
		}

		// only show if there is something, that will prevent spamming the
		// output with the same content over and over
		if info.Updated != 0 {
			p.Log.Info("[checker] cleaned up %d documents", info.Updated)
		}

		return nil
	}

	return p.DB.Run("jMachines", query)
}

// CleanStates resets documents that has machine states in progress mode
// (building, stopping, etc..) which weren't updated since 10 minutes. This
// could be caused because of Kloud restarts or panics.
func (p *Provider) CleanStates(timeout time.Duration) error {
	cleanstateFunc := func(badstate, goodstate string) error {
		return p.DB.Run("jMachines", func(c *mgo.Collection) error {
			// machines that can't be updated because they seems to be in progress
			badstateMachines := bson.M{
				"assignee.inProgress": false, // never update during a onging process :)
				"status.state":        badstate,
				"status.modifiedAt":   bson.M{"$lt": time.Now().UTC().Add(-timeout)},
			}

			cleanMachines := bson.M{
				"status.state":      goodstate,
				"status.modifiedAt": time.Now().UTC(),
			}

			// reset all machines
			info, err := c.UpdateAll(badstateMachines, bson.M{"$set": cleanMachines})
			if err != nil {
				return err
			}

			// only show if there is something, that will prevent spamming the
			// output with the same content over and over
			if info.Updated != 0 {
				p.Log.Info("[state cleaner] fixed %d documents from '%s' to '%s'",
					info.Updated, badstate, goodstate)
			}

			return nil
		})
	}

	progressModes := []struct {
		bad  string
		good string
	}{
		// our "build" method can continue with the leftover data that was
		// updated to MongoDB during the "build" process. So setting the state
		// to NotInitialized will make it to continue from where it was left.
		{machinestate.Building.String(), machinestate.NotInitialized.String()},
		{machinestate.Terminating.String(), machinestate.Terminated.String()},
		// once stopped, always stopped.
		{machinestate.Stopping.String(), machinestate.Stopped.String()},
		// the final state is  "stopped" because we don't know the if the
		// domains are updated or if the machien was really started. If the
		// machine is alrady started, the "start" method won't start it again,
		// so setting it to "stopped" will at least make it updating the
		// domains.
		{machinestate.Starting.String(), machinestate.Stopped.String()},
	}

	errs := multierrors.New()

	var wg sync.WaitGroup
	for _, state := range progressModes {
		wg.Add(1)
		go func(bad, good string) {
			defer wg.Done()
			err := cleanstateFunc(bad, good)
			errs.Add(err)
		}(state.bad, state.good)
	}

	wg.Wait()

	// if there is no errors just return a nil
	if errs.Len() == 0 {
		return nil
	}

	return errs
}
<|endoftext|>


<commit_before>package koding

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb"
	"koding/kites/kloud/api/amazon"
	"koding/kites/kloud/contexthelper/request"
	"koding/kites/kloud/contexthelper/session"
	"koding/kites/kloud/dnsstorage"
	"koding/kites/kloud/eventer"
	"koding/kites/kloud/kloud"
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/pkg/dnsclient"
	"koding/kites/kloud/pkg/multiec2"
	"koding/kites/kloud/plans"
	"koding/kites/kloud/userdata"
	"time"

	"github.com/fatih/structs"
	"github.com/koding/kite"
	"github.com/koding/logging"
	"golang.org/x/net/context"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type Provider struct {
	DB         *mongodb.MongoDB
	Log        logging.Logger
	Kite       *kite.Kite
	DNSClient  *dnsclient.Route53
	DNSStorage *dnsstorage.MongodbStorage
	EC2Clients *multiec2.Clients
	Userdata   *userdata.Userdata

	PaymentFetcher plans.PaymentFetcher
	CheckerFetcher plans.CheckerFetcher

	AuthorizedUsers map[string]string
}

type Credential struct {
	Id        bson.ObjectId `bson:"_id" json:"-"`
	PublicKey string        `bson:"publicKey"`
	Meta      bson.M        `bson:"meta"`
}

func (p *Provider) Machine(ctx context.Context, id string) (interface{}, error) {
	if !bson.IsObjectIdHex(id) {
		return nil, fmt.Errorf("Invalid machine id: %q", id)
	}

	// let's first check if the id exists, because we are going to use
	// findAndModify() and it would be difficult to distinguish if the id
	// really doesn't exist or if there is an assignee which is a different
	// thing. (Because findAndModify() also returns "not found" for the case
	// where the id exist but someone else is the assignee).
	machine := &Machine{}
	if err := p.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.FindId(bson.ObjectIdHex(id)).One(&machine)
	}); err == mgo.ErrNotFound {
		return nil, kloud.NewError(kloud.ErrMachineNotFound)
	}

	req, ok := request.FromContext(ctx)
	if !ok {
		return nil, errors.New("request context is not available")
	}

	if machine.Meta.Region == "" {
		machine.Meta.Region = "us-east-1"
		p.Log.Critical("[%s] region is not set in. Fallback to us-east-1.", machine.Id.Hex())
	} else {
		p.Log.Debug("[%s] using region: %s", machine.Id.Hex(), machine.Meta.Region)
	}

	if err := p.attachSession(ctx, machine); err != nil {
		return nil, err
	}

	// check for validation and permission
	if err := p.validate(machine, req); err != nil {
		return nil, err
	}

	return machine, nil
}

func (p *Provider) attachSession(ctx context.Context, machine *Machine) error {
	// get user model which contains user ssh keys or the list of users that
	// are allowed to use this machine
	if len(machine.Users) == 0 {
		return errors.New("permitted users list is empty")
	}

	// check if this is called via Kite call
	var requesterUsername string
	req, ok := request.FromContext(ctx)
	if ok {
		requesterUsername = req.Username
	}

	// get the user from the permitted list. If the list contains more than one
	// allowed person, fetch the one that is the same as requesterUsername, if
	// not pick up the first one.
	user, err := p.getOwner(requesterUsername, machine.Users)
	if err != nil {
		return err
	}

	client, err := p.EC2Clients.Region(machine.Meta.Region)
	if err != nil {
		return err
	}

	amazonClient, err := amazon.New(structs.Map(machine.Meta), client)
	if err != nil {
		return fmt.Errorf("koding-amazon err: %s", err)
	}

	// attach user specific log
	machine.Log = p.Log.New(machine.Id.Hex())

	sess := &session.Session{
		DB:         p.DB,
		Kite:       p.Kite,
		DNSClient:  p.DNSClient,
		DNSStorage: p.DNSStorage,
		Userdata:   p.Userdata,
		AWSClient:  amazonClient,
		AWSClients: p.EC2Clients, // used for fallback if something goes wrong
		Log:        machine.Log,
	}

	// we use session a lot of in Machine owned methods, so that's why we
	// assign it to a field for easy access
	machine.Session = sess

	// we pass it also to the context, so other packages, such as plans checker
	// can make use of it.
	ctx = session.NewContext(ctx, sess)

	payment, err := p.PaymentFetcher.Fetch(ctx, user.Name)
	if err != nil {
		machine.Log.Warning("username: %s could not fetch plan. Fallback to Free plan. err: '%s'",
			user.Name, err)
		payment = &plans.PaymentResponse{Plan: "Free"}
	}

	checker, err := p.CheckerFetcher.Fetch(ctx, payment.Plan)
	if err != nil {
		return err
	}

	machine.Payment = payment
	machine.Username = user.Name
	machine.User = user
	machine.cleanFuncs = make([]func(), 0)
	machine.Checker = checker

	ev, ok := eventer.FromContext(ctx)
	if ok {
		machine.Session.Eventer = ev
	}

	return nil
}

// getOwner returns the owner of the machine, if it's not found it returns an
// error. The requestName is optional, if it's not empty and the the users list
// has more than one valid allowed users, we return the one that matches the
// requesterName.
func (p *Provider) getOwner(requesterName string, users []models.Permissions) (*models.User, error) {
	allowedIds := make([]bson.ObjectId, 0)
	for _, perm := range users {
		// we only going to fetch users that are allowed
		if perm.Sudo && perm.Owner {
			allowedIds = append(allowedIds, perm.Id)
		}
	}

	// nothing found, just return
	if len(allowedIds) == 0 {
		return nil, errors.New("owner not found")
	}

	// if the list contains only one user or if the requesterName is empty,
	// just get the do a short lookup and return the first result.
	if len(allowedIds) == 1 || requesterName == "" {
		var user *models.User
		err := p.DB.Run("jUsers", func(c *mgo.Collection) error {
			return c.FindId(allowedIds[0]).One(&user)
		})

		if err == mgo.ErrNotFound {
			return nil, fmt.Errorf("User with Id not found: %s", allowedIds[0].Hex())
		}
		if err != nil {
			return nil, fmt.Errorf("username lookup error: %v", err)
		}

		return user, nil
	}

	// get the full list of users and return the one that matches the
	// requesterName, if not we return someone that is allowed. Note that we
	// don't do the validation here, this is only to fetch the user, don't put
	// any validation logic here.
	var allowedUsers []*models.User
	if err := p.DB.Run("jUsers", func(c *mgo.Collection) error {
		return c.Find(bson.M{"_id": bson.M{"$in": allowedIds}}).All(&allowedUsers)
	}); err != nil {
		return nil, fmt.Errorf("username lookup error: %v", err)
	}

	// now we have all allowed users, if we have someone that is in match with
	// the requesterName just return it.
	for _, u := range allowedUsers {
		if u.Name == requesterName {
			return u, nil
		}
	}

	// nothing found, just return the first one
	return allowedUsers[0], nil
}

func (p *Provider) validate(m *Machine, r *kite.Request) error {
	m.Log.Debug("validating for method '%s'", r.Method)

	// give access to authorized users immediately
	if _, authorized := p.AuthorizedUsers[r.Auth.Type]; authorized {
		return nil
	}

	if r.Username != m.User.Name {
		return errors.New("username is not permitted to make any action")
	}

	// check for user permissions
	if err := p.checkUser(m.User.ObjectId, m.Users); err != nil {
		return err
	}

	if m.User.Status != "confirmed" {
		return kloud.NewError(kloud.ErrUserNotConfirmed)
	}

	return nil
}

// checkUser checks whether the given username is available in the users list
// and has permission
func (p *Provider) checkUser(userId bson.ObjectId, users []models.Permissions) error {
	// check if the incoming user is in the list of permitted user list
	for _, u := range users {
		if userId == u.Id && u.Owner {
			return nil // ok he/she is good to go!
		}
	}

	return fmt.Errorf("permission denied. user not in the list of permitted users")
}

func (p *Provider) credential(publicKey string) (*Credential, error) {
	credential := &Credential{}
	// we neglect errors because credential is optional
	err := p.DB.Run("jCredentialDatas", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": publicKey}).One(credential)
	})
	if err != nil {
		return nil, err
	}

	return credential, nil
}

func (m *Machine) UpdateState(reason string, state machinestate.State) error {
	m.Log.Debug("Updating state to '%v'", state)
	err := m.Session.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.Update(
			bson.M{
				"_id": m.Id,
			},
			bson.M{
				"$set": bson.M{
					"status.state":      state.String(),
					"status.modifiedAt": time.Now().UTC(),
					"status.reason":     reason,
				},
			},
		)
	})

	if err != nil {
		return fmt.Errorf("Couldn't update state to '%s' for document: '%s' err: %s",
			state, m.Id.Hex(), err)
	}

	return nil
}
<commit_msg>kloud: check if auth is nil before checking for type<commit_after>package koding

import (
	"errors"
	"fmt"
	"koding/db/models"
	"koding/db/mongodb"
	"koding/kites/kloud/api/amazon"
	"koding/kites/kloud/contexthelper/request"
	"koding/kites/kloud/contexthelper/session"
	"koding/kites/kloud/dnsstorage"
	"koding/kites/kloud/eventer"
	"koding/kites/kloud/kloud"
	"koding/kites/kloud/machinestate"
	"koding/kites/kloud/pkg/dnsclient"
	"koding/kites/kloud/pkg/multiec2"
	"koding/kites/kloud/plans"
	"koding/kites/kloud/userdata"
	"time"

	"github.com/fatih/structs"
	"github.com/koding/kite"
	"github.com/koding/logging"
	"golang.org/x/net/context"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type Provider struct {
	DB         *mongodb.MongoDB
	Log        logging.Logger
	Kite       *kite.Kite
	DNSClient  *dnsclient.Route53
	DNSStorage *dnsstorage.MongodbStorage
	EC2Clients *multiec2.Clients
	Userdata   *userdata.Userdata

	PaymentFetcher plans.PaymentFetcher
	CheckerFetcher plans.CheckerFetcher

	AuthorizedUsers map[string]string
}

type Credential struct {
	Id        bson.ObjectId `bson:"_id" json:"-"`
	PublicKey string        `bson:"publicKey"`
	Meta      bson.M        `bson:"meta"`
}

func (p *Provider) Machine(ctx context.Context, id string) (interface{}, error) {
	if !bson.IsObjectIdHex(id) {
		return nil, fmt.Errorf("Invalid machine id: %q", id)
	}

	// let's first check if the id exists, because we are going to use
	// findAndModify() and it would be difficult to distinguish if the id
	// really doesn't exist or if there is an assignee which is a different
	// thing. (Because findAndModify() also returns "not found" for the case
	// where the id exist but someone else is the assignee).
	machine := &Machine{}
	if err := p.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.FindId(bson.ObjectIdHex(id)).One(&machine)
	}); err == mgo.ErrNotFound {
		return nil, kloud.NewError(kloud.ErrMachineNotFound)
	}

	req, ok := request.FromContext(ctx)
	if !ok {
		return nil, errors.New("request context is not available")
	}

	if machine.Meta.Region == "" {
		machine.Meta.Region = "us-east-1"
		p.Log.Critical("[%s] region is not set in. Fallback to us-east-1.", machine.Id.Hex())
	} else {
		p.Log.Debug("[%s] using region: %s", machine.Id.Hex(), machine.Meta.Region)
	}

	if err := p.attachSession(ctx, machine); err != nil {
		return nil, err
	}

	// check for validation and permission
	if err := p.validate(machine, req); err != nil {
		return nil, err
	}

	return machine, nil
}

func (p *Provider) attachSession(ctx context.Context, machine *Machine) error {
	// get user model which contains user ssh keys or the list of users that
	// are allowed to use this machine
	if len(machine.Users) == 0 {
		return errors.New("permitted users list is empty")
	}

	// check if this is called via Kite call
	var requesterUsername string
	req, ok := request.FromContext(ctx)
	if ok {
		requesterUsername = req.Username
	}

	// get the user from the permitted list. If the list contains more than one
	// allowed person, fetch the one that is the same as requesterUsername, if
	// not pick up the first one.
	user, err := p.getOwner(requesterUsername, machine.Users)
	if err != nil {
		return err
	}

	client, err := p.EC2Clients.Region(machine.Meta.Region)
	if err != nil {
		return err
	}

	amazonClient, err := amazon.New(structs.Map(machine.Meta), client)
	if err != nil {
		return fmt.Errorf("koding-amazon err: %s", err)
	}

	// attach user specific log
	machine.Log = p.Log.New(machine.Id.Hex())

	sess := &session.Session{
		DB:         p.DB,
		Kite:       p.Kite,
		DNSClient:  p.DNSClient,
		DNSStorage: p.DNSStorage,
		Userdata:   p.Userdata,
		AWSClient:  amazonClient,
		AWSClients: p.EC2Clients, // used for fallback if something goes wrong
		Log:        machine.Log,
	}

	// we use session a lot of in Machine owned methods, so that's why we
	// assign it to a field for easy access
	machine.Session = sess

	// we pass it also to the context, so other packages, such as plans checker
	// can make use of it.
	ctx = session.NewContext(ctx, sess)

	payment, err := p.PaymentFetcher.Fetch(ctx, user.Name)
	if err != nil {
		machine.Log.Warning("username: %s could not fetch plan. Fallback to Free plan. err: '%s'",
			user.Name, err)
		payment = &plans.PaymentResponse{Plan: "Free"}
	}

	checker, err := p.CheckerFetcher.Fetch(ctx, payment.Plan)
	if err != nil {
		return err
	}

	machine.Payment = payment
	machine.Username = user.Name
	machine.User = user
	machine.cleanFuncs = make([]func(), 0)
	machine.Checker = checker

	ev, ok := eventer.FromContext(ctx)
	if ok {
		machine.Session.Eventer = ev
	}

	return nil
}

// getOwner returns the owner of the machine, if it's not found it returns an
// error. The requestName is optional, if it's not empty and the the users list
// has more than one valid allowed users, we return the one that matches the
// requesterName.
func (p *Provider) getOwner(requesterName string, users []models.Permissions) (*models.User, error) {
	allowedIds := make([]bson.ObjectId, 0)
	for _, perm := range users {
		// we only going to fetch users that are allowed
		if perm.Sudo && perm.Owner {
			allowedIds = append(allowedIds, perm.Id)
		}
	}

	// nothing found, just return
	if len(allowedIds) == 0 {
		return nil, errors.New("owner not found")
	}

	// if the list contains only one user or if the requesterName is empty,
	// just get the do a short lookup and return the first result.
	if len(allowedIds) == 1 || requesterName == "" {
		var user *models.User
		err := p.DB.Run("jUsers", func(c *mgo.Collection) error {
			return c.FindId(allowedIds[0]).One(&user)
		})

		if err == mgo.ErrNotFound {
			return nil, fmt.Errorf("User with Id not found: %s", allowedIds[0].Hex())
		}
		if err != nil {
			return nil, fmt.Errorf("username lookup error: %v", err)
		}

		return user, nil
	}

	// get the full list of users and return the one that matches the
	// requesterName, if not we return someone that is allowed. Note that we
	// don't do the validation here, this is only to fetch the user, don't put
	// any validation logic here.
	var allowedUsers []*models.User
	if err := p.DB.Run("jUsers", func(c *mgo.Collection) error {
		return c.Find(bson.M{"_id": bson.M{"$in": allowedIds}}).All(&allowedUsers)
	}); err != nil {
		return nil, fmt.Errorf("username lookup error: %v", err)
	}

	// now we have all allowed users, if we have someone that is in match with
	// the requesterName just return it.
	for _, u := range allowedUsers {
		if u.Name == requesterName {
			return u, nil
		}
	}

	// nothing found, just return the first one
	return allowedUsers[0], nil
}

func (p *Provider) validate(m *Machine, r *kite.Request) error {
	m.Log.Debug("validating for method '%s'", r.Method)

	// give access to authorized users immediately
	if r.Auth != nil {
		if _, authorized := p.AuthorizedUsers[r.Auth.Type]; authorized {
			return nil
		}
	}

	if r.Username != m.User.Name {
		return errors.New("username is not permitted to make any action")
	}

	// check for user permissions
	if err := p.checkUser(m.User.ObjectId, m.Users); err != nil {
		return err
	}

	if m.User.Status != "confirmed" {
		return kloud.NewError(kloud.ErrUserNotConfirmed)
	}

	return nil
}

// checkUser checks whether the given username is available in the users list
// and has permission
func (p *Provider) checkUser(userId bson.ObjectId, users []models.Permissions) error {
	// check if the incoming user is in the list of permitted user list
	for _, u := range users {
		if userId == u.Id && u.Owner {
			return nil // ok he/she is good to go!
		}
	}

	return fmt.Errorf("permission denied. user not in the list of permitted users")
}

func (p *Provider) credential(publicKey string) (*Credential, error) {
	credential := &Credential{}
	// we neglect errors because credential is optional
	err := p.DB.Run("jCredentialDatas", func(c *mgo.Collection) error {
		return c.Find(bson.M{"publicKey": publicKey}).One(credential)
	})
	if err != nil {
		return nil, err
	}

	return credential, nil
}

func (m *Machine) UpdateState(reason string, state machinestate.State) error {
	m.Log.Debug("Updating state to '%v'", state)
	err := m.Session.DB.Run("jMachines", func(c *mgo.Collection) error {
		return c.Update(
			bson.M{
				"_id": m.Id,
			},
			bson.M{
				"$set": bson.M{
					"status.state":      state.String(),
					"status.modifiedAt": time.Now().UTC(),
					"status.reason":     reason,
				},
			},
		)
	})

	if err != nil {
		return fmt.Errorf("Couldn't update state to '%s' for document: '%s' err: %s",
			state, m.Id.Hex(), err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	"github.com/stianeikeland/go-rpio"
	"net/http"
	"os"
	"strings"
	"time"
)

var (
	mode = 0 // 0=go-rpio,1=embd

	// LEDs
	ledRedPin    = 4
	ledYellowPin = 17
	ledGreenPin  = 27
	ledToColor   = map[int]string{}
	ledMapEmbd = map[int]embd.DigitalPin{}
	ledMap     = map[int]rpio.Pin{}

	// Buttons
	buttonPin = 22
	buttonMap = map[int]embd.DigitalPin{}
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val, _ := pin.Read()
	if val == embd.High {
		return embd.Low
	} else {
		return embd.High
	}
}

func toggleLEDEmbd(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	toggledValue := getToggledValue(pin)
	fmt.Println("Val to write", toggledValue)
	embd.DigitalWrite(pin.N(), toggledValue)
}

func toggleLED(pin rpio.Pin, color string) {
	fmt.Println(getLEDString(color))
	fmt.Println("Current value:", pin.Read())
	pin.Toggle()
}

func initButtons() {
	buttonMap[0],_ = embd.NewDigitalPin(buttonPin)
	buttonMap[0].SetDirection(embd.In)
	buttonMap[0].ActiveLow(false)
	quit := make(chan interface{})
	err := buttonMap[0].Watch(embd.EdgeFalling, func(btn embd.DigitalPin) {
		fmt.Println("Pressed", btn)
		quit <- btn
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Button %v was pressed.\n", <-quit)
}

func initLEDs() {
	if mode == 1 {
		ledMapEmbd[0],_ = embd.NewDigitalPin(ledRedPin)
		ledMapEmbd[1],_ = embd.NewDigitalPin(ledYellowPin)
		ledMapEmbd[2],_ = embd.NewDigitalPin(ledGreenPin)
		for i := 0; i < len(ledMapEmbd); i++ {
			ledMapEmbd[i].SetDirection(embd.Out)
			embd.DigitalWrite(ledMapEmbd[i].N(), embd.Low)
		}
	} else {
		ledMap[0] = rpio.Pin(ledRedPin)
		ledMap[1] = rpio.Pin(ledYellowPin)
		ledMap[2] = rpio.Pin(ledGreenPin)
		for i := 0; i < len(ledMap); i++ {
			ledMap[i].Output()
			ledMap[i].Low()
		}
	}
	ledToColor[0] = "red"
	ledToColor[1] = "yellow"
	ledToColor[2] = "green"
}

func initGPIO() error {
	if mode == 1 {
		return embd.InitGPIO()
	} else {
		return rpio.Open()
	}
}

func doLedToggling(i int) {
	if mode == 1 {
		toggleLEDEmbd(ledMapEmbd[i%3], ledToColor[i%3])
	} else {
		toggleLED(ledMap[i%3], ledToColor[i%3])
	}
	time.Sleep(time.Second)
}

type Gpio struct {
	ID int
	Name string
	Value int
}

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

    m := Gpio{1, "GPIO2", 1}
    b, err := json.Marshal(m)

    if err != nil {
        panic(err)
    }

     w.Write(b)
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 3, "number of blinks")
	modeFromCli := flag.Int("mode", 0, "mode")
	button := flag.Bool("button", false, "button mode")
	flag.Parse()
	mode = *modeFromCli
	if mode == 1 {
		fmt.Println("Running using Embd.io")
	} else {
		fmt.Println("Running using go-rpio")
	}
	fmt.Println("Number of blinks:", *num)

	http.HandleFunc("/gpios", gpios)
    http.ListenAndServe(":8080", nil)

	var err = initGPIO()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	initLEDs()
	if mode == 1 && *button {
		initButtons()
	}

	if mode == 1 {
		defer embd.CloseGPIO()
	} else {
		defer rpio.Close()
	}

	if !*button {
		if *num == 0 {
			var counter = 0
			for {
				doLedToggling(counter)
				counter++
			}
		} else {
			for i := 0; i < *num; i++ {
				doLedToggling(i)
			}
		}
	}
}
<commit_msg>Run webserver as goroutine<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/kidoman/embd"
	_ "github.com/kidoman/embd/host/all"
	"github.com/stianeikeland/go-rpio"
	"net/http"
	"os"
	"strings"
	"time"
)

var (
	mode = 0 // 0=go-rpio,1=embd

	// LEDs
	ledRedPin    = 4
	ledYellowPin = 17
	ledGreenPin  = 27
	ledToColor   = map[int]string{}
	ledMapEmbd = map[int]embd.DigitalPin{}
	ledMap     = map[int]rpio.Pin{}

	// Buttons
	buttonPin = 22
	buttonMap = map[int]embd.DigitalPin{}
)

func getLEDString(color string) string {
	return "Toggle " + strings.ToUpper(color)
}

func getToggledValue(pin embd.DigitalPin) int {
	val, _ := pin.Read()
	if val == embd.High {
		return embd.Low
	} else {
		return embd.High
	}
}

func toggleLEDEmbd(pin embd.DigitalPin, color string) {
	fmt.Println(getLEDString(color))
	toggledValue := getToggledValue(pin)
	fmt.Println("Val to write", toggledValue)
	embd.DigitalWrite(pin.N(), toggledValue)
}

func toggleLED(pin rpio.Pin, color string) {
	fmt.Println(getLEDString(color))
	fmt.Println("Current value:", pin.Read())
	pin.Toggle()
}

func initButtons() {
	buttonMap[0],_ = embd.NewDigitalPin(buttonPin)
	buttonMap[0].SetDirection(embd.In)
	buttonMap[0].ActiveLow(false)
	quit := make(chan interface{})
	err := buttonMap[0].Watch(embd.EdgeFalling, func(btn embd.DigitalPin) {
		fmt.Println("Pressed", btn)
		quit <- btn
	})
	if err != nil {
		panic(err)
	}
	fmt.Printf("Button %v was pressed.\n", <-quit)
}

func initLEDs() {
	if mode == 1 {
		ledMapEmbd[0],_ = embd.NewDigitalPin(ledRedPin)
		ledMapEmbd[1],_ = embd.NewDigitalPin(ledYellowPin)
		ledMapEmbd[2],_ = embd.NewDigitalPin(ledGreenPin)
		for i := 0; i < len(ledMapEmbd); i++ {
			ledMapEmbd[i].SetDirection(embd.Out)
			embd.DigitalWrite(ledMapEmbd[i].N(), embd.Low)
		}
	} else {
		ledMap[0] = rpio.Pin(ledRedPin)
		ledMap[1] = rpio.Pin(ledYellowPin)
		ledMap[2] = rpio.Pin(ledGreenPin)
		for i := 0; i < len(ledMap); i++ {
			ledMap[i].Output()
			ledMap[i].Low()
		}
	}
	ledToColor[0] = "red"
	ledToColor[1] = "yellow"
	ledToColor[2] = "green"
}

func initGPIO() error {
	if mode == 1 {
		return embd.InitGPIO()
	} else {
		return rpio.Open()
	}
}

func doLedToggling(i int) {
	if mode == 1 {
		toggleLEDEmbd(ledMapEmbd[i%3], ledToColor[i%3])
	} else {
		toggleLED(ledMap[i%3], ledToColor[i%3])
	}
	time.Sleep(time.Second)
}

type Gpio struct {
	ID int
	Name string
	Value int
}

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

    m := Gpio{1, "GPIO2", 1}
    b, err := json.Marshal(m)

    if err != nil {
        panic(err)
    }

     w.Write(b)
}

func initWebServer() {
	http.HandleFunc("/gpios", gpios)
    http.ListenAndServe(":8080", nil)
}

func main() {
	fmt.Println("Parsing parameters")
	num := flag.Int("num", 3, "number of blinks")
	modeFromCli := flag.Int("mode", 0, "mode")
	button := flag.Bool("button", false, "button mode")
	flag.Parse()
	mode = *modeFromCli
	if mode == 1 {
		fmt.Println("Running using Embd.io")
	} else {
		fmt.Println("Running using go-rpio")
	}
	fmt.Println("Number of blinks:", *num)

	go initWebServer()

	var err = initGPIO()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	initLEDs()
	if mode == 1 && *button {
		initButtons()
	}

	if mode == 1 {
		defer embd.CloseGPIO()
	} else {
		defer rpio.Close()
	}

	if !*button {
		if *num == 0 {
			var counter = 0
			for {
				doLedToggling(counter)
				counter++
			}
		} else {
			for i := 0; i < *num; i++ {
				doLedToggling(i)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"time"

	"github.com/ian-kent/go-log/log"
	"github.com/kevintavog/findaphoto/common"
	"github.com/kevintavog/findaphoto/findaphotoserver/configuration"

	"gopkg.in/olivere/elastic.v5"
)

func runIndexer(devMode bool) {
	log.Info("Starting indexing")

	var args = []string{
		"-s", configuration.Current.ElasticSearchUrl,
		"-o", configuration.Current.OpenMapUrl,
		"-k", configuration.Current.OpenMapKey,
		"-c", configuration.Current.OpenMapUrl,
		"-r", configuration.Current.RedisUrl}

	if devMode {
		args = append(args, "-i")
		args = append(args, "dev-")
	}

	countPaths := 0
	common.VisitAllPaths(func(alias common.AliasDocument) {
		countPaths += 1
		timeAndRunIndexer(args, alias.Path)
	})

	// If the repository is empty and there's a default path, index the default path
	if countPaths == 0 {
		if len(configuration.Current.DefaultIndexPath) > 0 {
			log.Info("Run the indexer for the default path: '%v'", configuration.Current.DefaultIndexPath)
			timeAndRunIndexer(args, configuration.Current.DefaultIndexPath)
		} else {
			log.Warn("No paths to index")
		}
	}

	// Re-load the aliases - at least update the last indexed timestamp
	client, err := elastic.NewSimpleClient(
		elastic.SetURL(common.ElasticSearchServer),
		elastic.SetSniff(false))

	if err != nil {
		log.Fatal("Unable to connect to '%s': %s", common.ElasticSearchServer, err.Error())
	}
	common.InitializeAliases(client)
}

func timeAndRunIndexer(args []string, path string) {

	startTime := time.Now()

	pathAndArgs := append(args, "-p")
	pathAndArgs = append(pathAndArgs, path)
	err := exec.Command(common.IndexerPath, pathAndArgs...).Run()
	duration := time.Now().Sub(startTime).Seconds()
	log.Info("Finished indexing '%v' in %1.1f seconds", path, duration)

	if err != nil {
		log.Error("Failed executing indexer for '%s': %s", path, err.Error())
	}
}
<commit_msg>The info page now shows the correct last updated timestamp for an index<commit_after>package main

import (
	"os/exec"
	"time"

	"golang.org/x/net/context"

	"github.com/ian-kent/go-log/log"
	"github.com/kevintavog/findaphoto/common"
	"github.com/kevintavog/findaphoto/findaphotoserver/configuration"

	"gopkg.in/olivere/elastic.v5"
)

func runIndexer(devMode bool) {
	log.Info("Starting indexing")

	var args = []string{
		"-s", configuration.Current.ElasticSearchUrl,
		"-o", configuration.Current.OpenMapUrl,
		"-k", configuration.Current.OpenMapKey,
		"-c", configuration.Current.OpenMapUrl,
		"-r", configuration.Current.RedisUrl}

	if devMode {
		args = append(args, "-i")
		args = append(args, "dev-")
	}

	countPaths := 0
	common.VisitAllPaths(func(alias common.AliasDocument) {
		countPaths += 1
		timeAndRunIndexer(args, alias.Path)
	})

	// If the repository is empty and there's a default path, index the default path
	if countPaths == 0 {
		if len(configuration.Current.DefaultIndexPath) > 0 {
			log.Info("Run the indexer for the default path: '%v'", configuration.Current.DefaultIndexPath)
			timeAndRunIndexer(args, configuration.Current.DefaultIndexPath)
		} else {
			log.Warn("No paths to index")
		}
	}

	// Re-load the aliases - at least update the last indexed timestamp
	client, err := elastic.NewSimpleClient(
		elastic.SetURL(common.ElasticSearchServer),
		elastic.SetSniff(false))

	if err != nil {
		log.Fatal("Unable to connect to '%s': %s", common.ElasticSearchServer, err.Error())
	}
	_, err = client.Refresh(common.MediaIndexName).Do(context.TODO())
	if err != nil {
		log.Error("Failed refreshing '%s': %s", common.MediaIndexName, err)
	}
	common.InitializeAliases(client)
}

func timeAndRunIndexer(args []string, path string) {

	startTime := time.Now()

	pathAndArgs := append(args, "-p")
	pathAndArgs = append(pathAndArgs, path)
	err := exec.Command(common.IndexerPath, pathAndArgs...).Run()
	duration := time.Now().Sub(startTime).Seconds()
	log.Info("Finished indexing '%v' in %1.1f seconds", path, duration)

	if err != nil {
		log.Error("Failed executing indexer for '%s': %s", path, err.Error())
	}
}
<|endoftext|>


<commit_before>package telegram

import (
	"fmt"
	"math/rand"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	"github.com/jackc/pgx/pgtype"
	"github.com/kak-tus/irma_bot/storage"
)

func (o *InstanceObj) messageFromNewbie(msg *tgbotapi.Message) error {
	var ban bool

	if msg.Entities != nil {
		for _, e := range *msg.Entities {
			if e.Type == "url" || e.Type == "text_link" || e.Type == "mention" || e.Type == "email" {
				ban = true
				break
			}
		}
	}

	if msg.CaptionEntities != nil {
		for _, e := range *msg.CaptionEntities {
			if e.Type == "url" || e.Type == "text_link" || e.Type == "mention" || e.Type == "email" {
				ban = true
				break
			}
		}
	}

	if msg.ForwardFrom != nil || msg.ForwardFromChat != nil || msg.Sticker != nil || msg.Photo != nil {
		ban = true
	}

	if !ban {
		return o.stor.AddNewbieMessages(msg.Chat.ID, msg.From.ID)
	}

	o.log.Infof("Restricted message from user: %s", msg.From.FirstName)

	kick := tgbotapi.KickChatMemberConfig{
		ChatMemberConfig: tgbotapi.ChatMemberConfig{
			ChatID: msg.Chat.ID,
			UserID: msg.From.ID,
		},
		UntilDate: time.Now().In(time.UTC).AddDate(0, 0, 1).Unix(),
	}

	_, err := o.bot.KickChatMember(kick)
	if err != nil {
		return err
	}

	if err := o.deleteMessage(msg.Chat.ID, msg.MessageID); err != nil {
		return err
	}

	return nil
}

func (o *InstanceObj) newMembers(msg *tgbotapi.Message) error {
	isAdm, err := o.isAdmin(msg.Chat.ID, msg.From.ID)
	if err != nil {
		return err
	}

	if !isAdm {
		o.log.Debugw(
			"Newbie added by admin, it is normal",
			"Admin", msg.From.ID,
		)
		return nil
	}

	gr, err := o.sett.GetGroup(msg.Chat.ID)
	if err != nil {
		return err
	}

	if gr == nil || gr.BanURL.Bool {
		for _, m := range *msg.NewChatMembers {
			o.log.Infof("Newbie found, add messages: %s", m.FirstName)
			err := o.stor.AddNewbieMessages(msg.Chat.ID, m.ID)
			if err != nil {
				return err
			}
		}
	}

	if gr != nil && gr.BanQuestion.Status == pgtype.Present && gr.BanQuestion.Bool == false {
		return nil
	}

	quest := o.cnf.DefaultQuestions
	greet := o.cnf.DefaultGreeting

	if gr != nil && len(gr.Questions) != 0 {
		quest = gr.Questions
	}
	if gr != nil && gr.Greeting.String != "" {
		greet = gr.Greeting.String
	}

	for _, m := range *msg.NewChatMembers {
		o.log.Infof("Newbie found, send question: %s", m.FirstName)

		qID := rand.Intn(len(quest))

		var name string
		if m.UserName != "" {
			name = m.UserName
		} else {
			name = m.FirstName

			if m.LastName != "" {
				name += " " + m.LastName
			}
		}

		txt := fmt.Sprintf("@%s %s\n\n%s", name, greet, quest[qID].Text)

		resp := tgbotapi.NewMessage(msg.Chat.ID, txt)

		btns := make([][]tgbotapi.InlineKeyboardButton, len(quest[qID].Answers))

		for i, a := range quest[qID].Answers {
			id := fmt.Sprintf("%d_%d_%d_%d", m.ID, msg.Chat.ID, qID, i)
			btns[i] = []tgbotapi.InlineKeyboardButton{tgbotapi.NewInlineKeyboardButtonData(a.Text, id)}
		}

		resp.ReplyMarkup = tgbotapi.NewInlineKeyboardMarkup(btns...)

		res, err := o.bot.Send(resp)
		if err != nil {
			return err
		}

		act := storage.Action{
			ChatID:    res.Chat.ID,
			Type:      "del",
			MessageID: res.MessageID,
			UserID:    m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}

		err = o.stor.SetKicked(msg.Chat.ID, m.ID)
		if err != nil {
			return err
		}

		act = storage.Action{
			ChatID:    msg.Chat.ID,
			Type:      "del",
			MessageID: msg.MessageID,
			UserID:    m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}

		act = storage.Action{
			ChatID: msg.Chat.ID,
			Type:   "kick",
			UserID: m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Copypaste bugfix.<commit_after>package telegram

import (
	"fmt"
	"math/rand"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	"github.com/jackc/pgx/pgtype"
	"github.com/kak-tus/irma_bot/storage"
)

func (o *InstanceObj) messageFromNewbie(msg *tgbotapi.Message) error {
	var ban bool

	if msg.Entities != nil {
		for _, e := range *msg.Entities {
			if e.Type == "url" || e.Type == "text_link" || e.Type == "mention" || e.Type == "email" {
				ban = true
				break
			}
		}
	}

	if msg.CaptionEntities != nil {
		for _, e := range *msg.CaptionEntities {
			if e.Type == "url" || e.Type == "text_link" || e.Type == "mention" || e.Type == "email" {
				ban = true
				break
			}
		}
	}

	if msg.ForwardFrom != nil || msg.ForwardFromChat != nil || msg.Sticker != nil || msg.Photo != nil {
		ban = true
	}

	if !ban {
		return o.stor.AddNewbieMessages(msg.Chat.ID, msg.From.ID)
	}

	o.log.Infof("Restricted message from user: %s", msg.From.FirstName)

	kick := tgbotapi.KickChatMemberConfig{
		ChatMemberConfig: tgbotapi.ChatMemberConfig{
			ChatID: msg.Chat.ID,
			UserID: msg.From.ID,
		},
		UntilDate: time.Now().In(time.UTC).AddDate(0, 0, 1).Unix(),
	}

	_, err := o.bot.KickChatMember(kick)
	if err != nil {
		return err
	}

	if err := o.deleteMessage(msg.Chat.ID, msg.MessageID); err != nil {
		return err
	}

	return nil
}

func (o *InstanceObj) newMembers(msg *tgbotapi.Message) error {
	isAdm, err := o.isAdmin(msg.Chat.ID, msg.From.ID)
	if err != nil {
		return err
	}

	if isAdm {
		o.log.Debugw(
			"Newbie added by admin, it is normal",
			"Admin", msg.From.ID,
		)
		return nil
	}

	gr, err := o.sett.GetGroup(msg.Chat.ID)
	if err != nil {
		return err
	}

	if gr == nil || gr.BanURL.Bool {
		for _, m := range *msg.NewChatMembers {
			o.log.Infof("Newbie found, add messages: %s", m.FirstName)
			err := o.stor.AddNewbieMessages(msg.Chat.ID, m.ID)
			if err != nil {
				return err
			}
		}
	}

	if gr != nil && gr.BanQuestion.Status == pgtype.Present && gr.BanQuestion.Bool == false {
		return nil
	}

	quest := o.cnf.DefaultQuestions
	greet := o.cnf.DefaultGreeting

	if gr != nil && len(gr.Questions) != 0 {
		quest = gr.Questions
	}
	if gr != nil && gr.Greeting.String != "" {
		greet = gr.Greeting.String
	}

	for _, m := range *msg.NewChatMembers {
		o.log.Infof("Newbie found, send question: %s", m.FirstName)

		qID := rand.Intn(len(quest))

		var name string
		if m.UserName != "" {
			name = m.UserName
		} else {
			name = m.FirstName

			if m.LastName != "" {
				name += " " + m.LastName
			}
		}

		txt := fmt.Sprintf("@%s %s\n\n%s", name, greet, quest[qID].Text)

		resp := tgbotapi.NewMessage(msg.Chat.ID, txt)

		btns := make([][]tgbotapi.InlineKeyboardButton, len(quest[qID].Answers))

		for i, a := range quest[qID].Answers {
			id := fmt.Sprintf("%d_%d_%d_%d", m.ID, msg.Chat.ID, qID, i)
			btns[i] = []tgbotapi.InlineKeyboardButton{tgbotapi.NewInlineKeyboardButtonData(a.Text, id)}
		}

		resp.ReplyMarkup = tgbotapi.NewInlineKeyboardMarkup(btns...)

		res, err := o.bot.Send(resp)
		if err != nil {
			return err
		}

		act := storage.Action{
			ChatID:    res.Chat.ID,
			Type:      "del",
			MessageID: res.MessageID,
			UserID:    m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}

		err = o.stor.SetKicked(msg.Chat.ID, m.ID)
		if err != nil {
			return err
		}

		act = storage.Action{
			ChatID:    msg.Chat.ID,
			Type:      "del",
			MessageID: msg.MessageID,
			UserID:    m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}

		act = storage.Action{
			ChatID: msg.Chat.ID,
			Type:   "kick",
			UserID: m.ID,
		}
		if err := o.stor.AddToActionPool(act, time.Minute); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


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

// Package metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "google.golang.org/cloud/compute/metadata"

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"google.golang.org/cloud/internal"
)

// metadataIP is the documented metadata server IP address.
const metadataIP = "169.254.169.254"

type cachedValue struct {
	k    string
	trim bool
	mu   sync.Mutex
	v    string
}

var (
	projID  = &cachedValue{k: "project/project-id", trim: true}
	projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
	instID  = &cachedValue{k: "instance/id", trim: true}
)

var (
	metaClient = &http.Client{
		Transport: &internal.Transport{
			Base: &http.Transport{
				Dial: (&net.Dialer{
					Timeout:   2 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
				ResponseHeaderTimeout: 2 * time.Second,
			},
		},
	}
	subscribeClient = &http.Client{
		Transport: &internal.Transport{
			Base: &http.Transport{
				Dial: (&net.Dialer{
					Timeout:   2 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
			},
		},
	}
)

// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string

func (suffix NotDefinedError) Error() string {
	return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}

// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func Get(suffix string) (string, error) {
	val, _, err := getETag(metaClient, suffix)
	return val, err
}

// getETag returns a value from the metadata service as well as the associated
// ETag using the provided client. This func is otherwise equivalent to Get.
func getETag(client *http.Client, suffix string) (value, etag string, err error) {
	// Using a fixed IP makes it very difficult to spoof the metadata service in
	// a container, which is an important use-case for local testing of cloud
	// deployments. To enable spoofing of the metadata service, the environment
	// variable GCE_METADATA_HOST is first inspected to decide where metadata
	// requests shall go.
	host := os.Getenv("GCE_METADATA_HOST")
	if host == "" {
		// Using 169.254.169.254 instead of "metadata" here because Go
		// binaries built with the "netgo" tag and without cgo won't
		// know the search suffix for "metadata" is
		// ".google.internal", and this IP address is documented as
		// being stable anyway.
		host = metadataIP
	}
	url := "http://" + host + "/computeMetadata/v1/" + suffix
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Metadata-Flavor", "Google")
	res, err := client.Do(req)
	if err != nil {
		return "", "", err
	}
	defer res.Body.Close()
	if res.StatusCode == http.StatusNotFound {
		return "", "", NotDefinedError(suffix)
	}
	if res.StatusCode != 200 {
		return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
	}
	all, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", "", err
	}
	return string(all), res.Header.Get("Etag"), nil
}

func getTrimmed(suffix string) (s string, err error) {
	s, err = Get(suffix)
	s = strings.TrimSpace(s)
	return
}

func (c *cachedValue) get() (v string, err error) {
	defer c.mu.Unlock()
	c.mu.Lock()
	if c.v != "" {
		return c.v, nil
	}
	if c.trim {
		v, err = getTrimmed(c.k)
	} else {
		v, err = Get(c.k)
	}
	if err == nil {
		c.v = v
	}
	return
}

var onGCE struct {
	sync.Mutex
	set bool
	v   bool
}

// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
	defer onGCE.Unlock()
	onGCE.Lock()
	if onGCE.set {
		return onGCE.v
	}
	onGCE.set = true
	onGCE.v = testOnGCE()
	return onGCE.v
}

func testOnGCE() bool {
	cancel := make(chan struct{})
	defer close(cancel)

	resc := make(chan bool, 2)

	// Try two strategies in parallel.
	// See https://github.com/GoogleCloudPlatform/gcloud-golang/issues/194
	go func() {
		req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
		req.Cancel = cancel
		res, err := metaClient.Do(req)
		if err != nil {
			resc <- false
			return
		}
		defer res.Body.Close()
		resc <- res.Header.Get("Metadata-Flavor") == "Google"
	}()

	go func() {
		addrs, err := net.LookupHost("metadata.google.internal")
		if err != nil || len(addrs) == 0 {
			resc <- false
			return
		}
		resc <- strsContains(addrs, metadataIP)
	}()

	return <-resc
}

// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
	const failedSubscribeSleep = time.Second * 5

	// First check to see if the metadata value exists at all.
	val, lastETag, err := getETag(subscribeClient, suffix)
	if err != nil {
		return err
	}

	if err := fn(val, true); err != nil {
		return err
	}

	ok := true
	if strings.ContainsRune(suffix, '?') {
		suffix += "&wait_for_change=true&last_etag="
	} else {
		suffix += "?wait_for_change=true&last_etag="
	}
	for {
		val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
		if err != nil {
			if _, deleted := err.(NotDefinedError); !deleted {
				time.Sleep(failedSubscribeSleep)
				continue // Retry on other errors.
			}
			ok = false
		}
		lastETag = etag

		if err := fn(val, ok); err != nil || !ok {
			return err
		}
	}
}

// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return projID.get() }

// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return projNum.get() }

// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) {
	return getTrimmed("instance/network-interfaces/0/ip")
}

// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) {
	return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}

// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) {
	return getTrimmed("instance/hostname")
}

// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) {
	var s []string
	j, err := Get("instance/tags")
	if err != nil {
		return nil, err
	}
	if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
		return nil, err
	}
	return s, nil
}

// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) {
	return instID.get()
}

// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) {
	host, err := Hostname()
	if err != nil {
		return "", err
	}
	return strings.Split(host, ".")[0], nil
}

// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) {
	zone, err := getTrimmed("instance/zone")
	// zone is of the form "projects/<projNum>/zones/<zoneName>".
	if err != nil {
		return "", err
	}
	return zone[strings.LastIndex(zone, "/")+1:], nil
}

// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }

// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM.  The value of
// an attribute can be obtained with ProjectAttributeValue.
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }

func lines(suffix string) ([]string, error) {
	j, err := Get(suffix)
	if err != nil {
		return nil, err
	}
	s := strings.Split(strings.TrimSpace(j), "\n")
	for i := range s {
		s[i] = strings.TrimSpace(s[i])
	}
	return s, nil
}

// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func InstanceAttributeValue(attr string) (string, error) {
	return Get("instance/attributes/" + attr)
}

// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func ProjectAttributeValue(attr string) (string, error) {
	return Get("project/attributes/" + attr)
}

// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func Scopes(serviceAccount string) ([]string, error) {
	if serviceAccount == "" {
		serviceAccount = "default"
	}
	return lines("instance/service-accounts/" + serviceAccount + "/scopes")
}

func strsContains(ss []string, s string) bool {
	for _, v := range ss {
		if v == s {
			return true
		}
	}
	return false
}
<commit_msg>metadata: use ctxhttp to support cancelation in Go 1.4<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "google.golang.org/cloud/compute/metadata"

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"

	"google.golang.org/cloud/internal"
)

// metadataIP is the documented metadata server IP address.
const metadataIP = "169.254.169.254"

type cachedValue struct {
	k    string
	trim bool
	mu   sync.Mutex
	v    string
}

var (
	projID  = &cachedValue{k: "project/project-id", trim: true}
	projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
	instID  = &cachedValue{k: "instance/id", trim: true}
)

var (
	metaClient = &http.Client{
		Transport: &internal.Transport{
			Base: &http.Transport{
				Dial: (&net.Dialer{
					Timeout:   2 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
				ResponseHeaderTimeout: 2 * time.Second,
			},
		},
	}
	subscribeClient = &http.Client{
		Transport: &internal.Transport{
			Base: &http.Transport{
				Dial: (&net.Dialer{
					Timeout:   2 * time.Second,
					KeepAlive: 30 * time.Second,
				}).Dial,
			},
		},
	}
)

// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string

func (suffix NotDefinedError) Error() string {
	return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}

// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func Get(suffix string) (string, error) {
	val, _, err := getETag(metaClient, suffix)
	return val, err
}

// getETag returns a value from the metadata service as well as the associated
// ETag using the provided client. This func is otherwise equivalent to Get.
func getETag(client *http.Client, suffix string) (value, etag string, err error) {
	// Using a fixed IP makes it very difficult to spoof the metadata service in
	// a container, which is an important use-case for local testing of cloud
	// deployments. To enable spoofing of the metadata service, the environment
	// variable GCE_METADATA_HOST is first inspected to decide where metadata
	// requests shall go.
	host := os.Getenv("GCE_METADATA_HOST")
	if host == "" {
		// Using 169.254.169.254 instead of "metadata" here because Go
		// binaries built with the "netgo" tag and without cgo won't
		// know the search suffix for "metadata" is
		// ".google.internal", and this IP address is documented as
		// being stable anyway.
		host = metadataIP
	}
	url := "http://" + host + "/computeMetadata/v1/" + suffix
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Metadata-Flavor", "Google")
	res, err := client.Do(req)
	if err != nil {
		return "", "", err
	}
	defer res.Body.Close()
	if res.StatusCode == http.StatusNotFound {
		return "", "", NotDefinedError(suffix)
	}
	if res.StatusCode != 200 {
		return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
	}
	all, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return "", "", err
	}
	return string(all), res.Header.Get("Etag"), nil
}

func getTrimmed(suffix string) (s string, err error) {
	s, err = Get(suffix)
	s = strings.TrimSpace(s)
	return
}

func (c *cachedValue) get() (v string, err error) {
	defer c.mu.Unlock()
	c.mu.Lock()
	if c.v != "" {
		return c.v, nil
	}
	if c.trim {
		v, err = getTrimmed(c.k)
	} else {
		v, err = Get(c.k)
	}
	if err == nil {
		c.v = v
	}
	return
}

var onGCE struct {
	sync.Mutex
	set bool
	v   bool
}

// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
	defer onGCE.Unlock()
	onGCE.Lock()
	if onGCE.set {
		return onGCE.v
	}
	onGCE.set = true
	onGCE.v = testOnGCE()
	return onGCE.v
}

func testOnGCE() bool {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	resc := make(chan bool, 2)

	// Try two strategies in parallel.
	// See https://github.com/GoogleCloudPlatform/gcloud-golang/issues/194
	go func() {
		res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP)
		if err != nil {
			resc <- false
			return
		}
		defer res.Body.Close()
		resc <- res.Header.Get("Metadata-Flavor") == "Google"
	}()

	go func() {
		addrs, err := net.LookupHost("metadata.google.internal")
		if err != nil || len(addrs) == 0 {
			resc <- false
			return
		}
		resc <- strsContains(addrs, metadataIP)
	}()

	return <-resc
}

// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
	const failedSubscribeSleep = time.Second * 5

	// First check to see if the metadata value exists at all.
	val, lastETag, err := getETag(subscribeClient, suffix)
	if err != nil {
		return err
	}

	if err := fn(val, true); err != nil {
		return err
	}

	ok := true
	if strings.ContainsRune(suffix, '?') {
		suffix += "&wait_for_change=true&last_etag="
	} else {
		suffix += "?wait_for_change=true&last_etag="
	}
	for {
		val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
		if err != nil {
			if _, deleted := err.(NotDefinedError); !deleted {
				time.Sleep(failedSubscribeSleep)
				continue // Retry on other errors.
			}
			ok = false
		}
		lastETag = etag

		if err := fn(val, ok); err != nil || !ok {
			return err
		}
	}
}

// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return projID.get() }

// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return projNum.get() }

// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) {
	return getTrimmed("instance/network-interfaces/0/ip")
}

// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) {
	return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}

// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) {
	return getTrimmed("instance/hostname")
}

// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) {
	var s []string
	j, err := Get("instance/tags")
	if err != nil {
		return nil, err
	}
	if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
		return nil, err
	}
	return s, nil
}

// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) {
	return instID.get()
}

// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) {
	host, err := Hostname()
	if err != nil {
		return "", err
	}
	return strings.Split(host, ".")[0], nil
}

// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) {
	zone, err := getTrimmed("instance/zone")
	// zone is of the form "projects/<projNum>/zones/<zoneName>".
	if err != nil {
		return "", err
	}
	return zone[strings.LastIndex(zone, "/")+1:], nil
}

// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }

// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM.  The value of
// an attribute can be obtained with ProjectAttributeValue.
func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }

func lines(suffix string) ([]string, error) {
	j, err := Get(suffix)
	if err != nil {
		return nil, err
	}
	s := strings.Split(strings.TrimSpace(j), "\n")
	for i := range s {
		s[i] = strings.TrimSpace(s[i])
	}
	return s, nil
}

// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func InstanceAttributeValue(attr string) (string, error) {
	return Get("instance/attributes/" + attr)
}

// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func ProjectAttributeValue(attr string) (string, error) {
	return Get("project/attributes/" + attr)
}

// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func Scopes(serviceAccount string) ([]string, error) {
	if serviceAccount == "" {
		serviceAccount = "default"
	}
	return lines("instance/service-accounts/" + serviceAccount + "/scopes")
}

func strsContains(ss []string, s string) bool {
	for _, v := range ss {
		if v == s {
			return true
		}
	}
	return false
}
<|endoftext|>


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

// Command nvimgo is a Neovim remote plogin.
package main

import (
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/neovim-go/vim/plugin"

	_ "nvim-go/commands"
	_ "nvim-go/nvim"
)

func init() {
	// logrus instead of stdlib log
	// neovim-go hijacked log format
	if lf := os.Getenv("NEOVIM_GO_LOG_FILE"); lf != "" {
		f, err := os.OpenFile(lf, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
		if err != nil {
			log.Fatal(err)
		}

		log.SetOutput(f)
		log.SetFormatter(&RpluginFormatter{})
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	plugin.Main()
}
<commit_msg>Support net/http/pprof debugging<commit_after>// Copyright 2016 Koichi Shiraishi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command nvimgo is a Neovim remote plogin.
package main

import (
	"net/http"
	_ "net/http/pprof"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/garyburd/neovim-go/vim/plugin"

	_ "nvim-go/commands"
	_ "nvim-go/nvim"
)

func init() {
	// logrus instead of stdlib log
	// neovim-go hijacked log format
	if lf := os.Getenv("NEOVIM_GO_LOG_FILE"); lf != "" {
		f, err := os.OpenFile(lf, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
		if err != nil {
			log.Fatal(err)
		}

		log.SetOutput(f)
		log.SetFormatter(&RpluginFormatter{})
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	go func() {
		log.Println(http.ListenAndServe("0.0.0.0:6060", nil))
	}()

	plugin.Main()
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/engine/diff"
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strconv"
)

func (api *coreAPI) handlePolicyGet(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	gen := params.ByName("rev")

	if len(gen) == 0 {
		gen = strconv.Itoa(int(runtime.LastGen))
	}

	policyData, err := api.store.GetPolicyData(runtime.ParseGeneration(gen))
	if err != nil {
		panic(fmt.Sprintf("error while getting requested policy: %s", err))
	}

	api.contentType.Write(writer, request, policyData)
}

// PolicyUpdateResultObject is an informational data structure with Kind and Constructor for PolicyUpdateResult
var PolicyUpdateResultObject = &runtime.Info{
	Kind:        "policy-update-result",
	Constructor: func() runtime.Object { return &PolicyUpdateResult{} },
}

// PolicyUpdateResult represents results for the policy update request (estimated list of actions to be executed to
// update existing actual state to the desired state)
type PolicyUpdateResult struct {
	runtime.TypeKind `yaml:",inline"`
	Actions          []string
}

func (api *coreAPI) handlePolicyUpdate(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	objects := api.readLang(request)

	user := api.getUserRequired(request)

	// Verify ACL for updated objects
	policy, _, err := api.store.GetPolicy(runtime.LastGen)
	if err != nil {
		panic(fmt.Sprintf("Error while loading current policy: %s", err))
	}
	for _, obj := range objects {
		errAdd := policy.AddObject(obj)
		if errAdd != nil {
			panic(fmt.Sprintf("Error while adding updated object to policy: %s", errAdd))
		}
		errManage := policy.View(user).ManageObject(obj)
		if errManage != nil {
			panic(fmt.Sprintf("Error while adding updated object to policy: %s", errManage))
		}
	}

	err = policy.Validate()
	if err != nil {
		panic(fmt.Sprintf("Updated policy is invalid: %s", err))
	}

	// todo(slukjanov): handle deleted
	deleted := make([]runtime.Key, 0)
	changed, policyData, err := api.store.UpdatePolicy(objects, deleted)
	if err != nil {
		panic(fmt.Sprintf("Error while updating policy: %s", err))
	}

	if !changed {
		api.contentType.Write(writer, request, &PolicyUpdateResult{
			TypeKind: PolicyUpdateResultObject.GetTypeKind(),
			Actions:  nil,
		})

		return
	}

	desiredPolicyGen := policyData.GetGeneration()
	desiredPolicy, _, err := api.store.GetPolicy(desiredPolicyGen)
	if err != nil {
		panic(fmt.Sprintf("Error while getting desiredPolicy: %s", err))
	}
	if desiredPolicy == nil {
		panic(fmt.Sprintf("Can't read policy right after updating it"))
	}

	actualState, err := api.store.GetActualState()
	if err != nil {
		panic(fmt.Sprintf("Error while getting actual state: %s", err))
	}

	// todo we should resolve before saving policy => add Mutex for this method to make sure it's safe
	resolver := resolve.NewPolicyResolver(desiredPolicy, api.externalData)
	desiredState, eventLog, err := resolver.ResolveAllDependencies()

	// todo save to log with clear prefix
	eventLog.Save(&event.HookConsole{})

	if err != nil {
		panic(fmt.Sprintf("Cannot resolve desiredPolicy: %v %v %v", err, desiredState, actualState))
	}

	nextRevision, err := api.store.NewRevision(desiredPolicyGen)
	if err != nil {
		panic(fmt.Sprintf("Unable to get next revision: %s", err))
	}

	stateDiff := diff.NewPolicyResolutionDiff(desiredState, actualState, nextRevision.GetGeneration())

	actions := make([]string, len(stateDiff.Actions))
	for idx, action := range stateDiff.Actions {
		actions[idx] = action.GetName()
	}

	api.contentType.Write(writer, request, &PolicyUpdateResult{
		TypeKind: PolicyUpdateResultObject.GetTypeKind(),
		Actions:  actions,
	})
}
<commit_msg>Fix generation parameter name<commit_after>package api

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/engine/diff"
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"strconv"
)

func (api *coreAPI) handlePolicyGet(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	gen := params.ByName("gen")

	if len(gen) == 0 {
		gen = strconv.Itoa(int(runtime.LastGen))
	}

	policyData, err := api.store.GetPolicyData(runtime.ParseGeneration(gen))
	if err != nil {
		panic(fmt.Sprintf("error while getting requested policy: %s", err))
	}

	api.contentType.Write(writer, request, policyData)
}

// PolicyUpdateResultObject is an informational data structure with Kind and Constructor for PolicyUpdateResult
var PolicyUpdateResultObject = &runtime.Info{
	Kind:        "policy-update-result",
	Constructor: func() runtime.Object { return &PolicyUpdateResult{} },
}

// PolicyUpdateResult represents results for the policy update request (estimated list of actions to be executed to
// update existing actual state to the desired state)
type PolicyUpdateResult struct {
	runtime.TypeKind `yaml:",inline"`
	Actions          []string
}

func (api *coreAPI) handlePolicyUpdate(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	objects := api.readLang(request)

	user := api.getUserRequired(request)

	// Verify ACL for updated objects
	policy, _, err := api.store.GetPolicy(runtime.LastGen)
	if err != nil {
		panic(fmt.Sprintf("Error while loading current policy: %s", err))
	}
	for _, obj := range objects {
		errAdd := policy.AddObject(obj)
		if errAdd != nil {
			panic(fmt.Sprintf("Error while adding updated object to policy: %s", errAdd))
		}
		errManage := policy.View(user).ManageObject(obj)
		if errManage != nil {
			panic(fmt.Sprintf("Error while adding updated object to policy: %s", errManage))
		}
	}

	err = policy.Validate()
	if err != nil {
		panic(fmt.Sprintf("Updated policy is invalid: %s", err))
	}

	// todo(slukjanov): handle deleted
	deleted := make([]runtime.Key, 0)
	changed, policyData, err := api.store.UpdatePolicy(objects, deleted)
	if err != nil {
		panic(fmt.Sprintf("Error while updating policy: %s", err))
	}

	if !changed {
		api.contentType.Write(writer, request, &PolicyUpdateResult{
			TypeKind: PolicyUpdateResultObject.GetTypeKind(),
			Actions:  nil,
		})

		return
	}

	desiredPolicyGen := policyData.GetGeneration()
	desiredPolicy, _, err := api.store.GetPolicy(desiredPolicyGen)
	if err != nil {
		panic(fmt.Sprintf("Error while getting desiredPolicy: %s", err))
	}
	if desiredPolicy == nil {
		panic(fmt.Sprintf("Can't read policy right after updating it"))
	}

	actualState, err := api.store.GetActualState()
	if err != nil {
		panic(fmt.Sprintf("Error while getting actual state: %s", err))
	}

	// todo we should resolve before saving policy => add Mutex for this method to make sure it's safe
	resolver := resolve.NewPolicyResolver(desiredPolicy, api.externalData)
	desiredState, eventLog, err := resolver.ResolveAllDependencies()

	// todo save to log with clear prefix
	eventLog.Save(&event.HookConsole{})

	if err != nil {
		panic(fmt.Sprintf("Cannot resolve desiredPolicy: %v %v %v", err, desiredState, actualState))
	}

	nextRevision, err := api.store.NewRevision(desiredPolicyGen)
	if err != nil {
		panic(fmt.Sprintf("Unable to get next revision: %s", err))
	}

	stateDiff := diff.NewPolicyResolutionDiff(desiredState, actualState, nextRevision.GetGeneration())

	actions := make([]string, len(stateDiff.Actions))
	for idx, action := range stateDiff.Actions {
		actions[idx] = action.GetName()
	}

	api.contentType.Write(writer, request, &PolicyUpdateResult{
		TypeKind: PolicyUpdateResultObject.GetTypeKind(),
		Actions:  actions,
	})
}
<|endoftext|>