<commit_before>package cmd

import (
	"fmt"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/cvmfs/docker-graphdriver/daemon/lib"
)

func init() {
	rootCmd.AddCommand(garbageCollectionCmd)
}

var garbageCollectionCmd = &cobra.Command{
	Use:     "garbage-collection",
	Short:   "Removes layers that are not necessary anymore",
	Aliases: []string{"gc"},
	Args:    cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {

		fmt.Println("Start")
		repo := args[0]
		llog := func(l *log.Entry) *log.Entry {
			return l.WithFields(log.Fields{"action": "garbage collect",
				"repo": repo,
			})
		}

		manifestToRemove, err := lib.FindImageToGarbageCollect(repo)
		if err != nil {
			llog(lib.LogE(err)).Warning(
				"Error in finding the image to remove from the scheduler, goin on...")
		}
		var images2layers map[string][]string

		for _, manifest := range manifestToRemove {
			digest := strings.Split(manifest.Config.Digest, ":")[1]
			for _, layerStruct := range manifest.Layers {
				layerName := strings.Split(layerStruct.Digest, ":")[1]
				images2layers[digest] = append(images2layers[digest], layerName)
			}
		}

		for image, layers := range images2layers {
			for _, layer := range layers {
				err = lib.GarbageCollectSingleLayer(repo, image, layer)
				if err != nil {
					llog(lib.LogE(err)).Warning(
						"Error in removing a single layer from the repository, going on...")
				}
			}
		}

		os.Exit(0)
	},
}
<commit_msg>initialize map<commit_after>package cmd

import (
	"fmt"
	"os"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/cvmfs/docker-graphdriver/daemon/lib"
)

func init() {
	rootCmd.AddCommand(garbageCollectionCmd)
}

var garbageCollectionCmd = &cobra.Command{
	Use:     "garbage-collection",
	Short:   "Removes layers that are not necessary anymore",
	Aliases: []string{"gc"},
	Args:    cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {

		fmt.Println("Start")
		repo := args[0]
		llog := func(l *log.Entry) *log.Entry {
			return l.WithFields(log.Fields{"action": "garbage collect",
				"repo": repo,
			})
		}

		manifestToRemove, err := lib.FindImageToGarbageCollect(repo)
		if err != nil {
			llog(lib.LogE(err)).Warning(
				"Error in finding the image to remove from the scheduler, goin on...")
		}
		images2layers := make(map[string][]string)

		for _, manifest := range manifestToRemove {
			digest := strings.Split(manifest.Config.Digest, ":")[1]
			for _, layerStruct := range manifest.Layers {
				layerName := strings.Split(layerStruct.Digest, ":")[1]
				images2layers[digest] = append(images2layers[digest], layerName)
			}
		}

		for image, layers := range images2layers {
			for _, layer := range layers {
				err = lib.GarbageCollectSingleLayer(repo, image, layer)
				if err != nil {
					llog(lib.LogE(err)).Warning(
						"Error in removing a single layer from the repository, going on...")
				}
			}
		}

		os.Exit(0)
	},
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
)

var (
	newInode chan uint64
)

func mount(point string) {

	// init inode generator
	newInode = make(chan uint64)
	go func() {
		var counter uint64 = 2 // start with 2, 1 is dir root
		for {
			newInode <- counter
			counter += 1
		}
	}()

	// startup mount
	c, err := fuse.Mount(point)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	log.Println("Mounted: ", point)
	err = fs.Serve(c, mgoFS{})
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	checkFatal(c.MountError)
}

// mgoFS implements my mgo fuse filesystem
type mgoFS struct{}

func (mgoFS) Root() (fs.Node, fuse.Error) {
	log.Println("returning root node")
	return Dir{"Root"}, nil
}

// Dir implements both Node and Handle for the root directory.
type Dir struct {
	name string
}

func (d Dir) Attr() fuse.Attr {
	log.Println("Dir.Attr() for ", d.name)
	return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555}
}

func (Dir) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Println("Dir.Lookup():", name)

	coll, ok := collections[name]
	if !ok {
		// might be..?
		return nil, fuse.ENOENT

	}

	return coll, nil
}

func (d Dir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Println("Dir.ReadDir():", d.name)

	ents := make([]fuse.Dirent, 0, len(collections))

	for name, _ := range collections {
		ents = append(ents, fuse.Dirent{Name: name, Type: fuse.DT_Dir})
	}
	return ents, nil
}
<commit_msg>removed unused inode generator<commit_after>package main

import (
	"log"
	"os"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
)

func mount(point string) {

	// startup mount
	c, err := fuse.Mount(point)
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	log.Println("Mounted: ", point)
	err = fs.Serve(c, mgoFS{})
	if err != nil {
		log.Fatal(err)
	}

	// check if the mount process has an error to report
	<-c.Ready
	checkFatal(c.MountError)
}

// mgoFS implements my mgo fuse filesystem
type mgoFS struct{}

func (mgoFS) Root() (fs.Node, fuse.Error) {
	log.Println("returning root node")
	return Dir{"Root"}, nil
}

// Dir implements both Node and Handle for the root directory.
type Dir struct {
	name string
}

func (d Dir) Attr() fuse.Attr {
	log.Println("Dir.Attr() for ", d.name)
	return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555}
}

func (Dir) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
	log.Println("Dir.Lookup():", name)

	coll, ok := collections[name]
	if !ok {
		// might be..?
		return nil, fuse.ENOENT

	}

	return coll, nil
}

func (d Dir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
	log.Println("Dir.ReadDir():", d.name)

	ents := make([]fuse.Dirent, 0, len(collections))

	for name, _ := range collections {
		ents = append(ents, fuse.Dirent{Name: name, Type: fuse.DT_Dir})
	}
	return ents, nil
}
<|endoftext|>


<commit_before>// +build gofuzz

package cryptdo

func Fuzz(data []byte) int {
	if _, err := Decrypt(data, "fuzzing"); err != nil {
		return 0
	}

	return 1
}
<commit_msg>remove fuzz example for now<commit_after><|endoftext|>


<commit_before>package main

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

type lead struct {
	Id                 bson.ObjectId `bson:"_id"                          json:"id"`
	Contact            *mgo.DBRef    `bson:"contact,omitempty"            json:"contact,omitempty"`
	Source             string        `bson:"source,omitempty"             json:"source,omitempty"`
	Owner              string        `bson:"owner,omitempty"              json:"owner,omitempty"`
	Status             string        `bson:"status,omitempty"             json:"status,omitempty"`
	TeamSize           float64       `bson:"teamsize,omitempty"           json:"teamsize,omitempty"`
	RatePerHour        float64       `bson:"rateperhour,omitempty"        json:"rateperhour,omitempty"`
	DurationInMonths   float64       `bson:"durationinmonths,omitempty"   json:"durationinmonths,omitempty"`
	EstimatedStartDate string        `bson:"estimatedstartdate,omitempty" json:"estimatedstartdate,omitempty"`
	Comments           []string      `bson:"comments,omitempty"           json:"comments,omitempty"`
}

// NewLead takes the fields of a lead, initializes a struct of lead type and returns
// the pointer to that struct.
// Also, It inserts the lead data into a mongoDB collection, which is passed as the first parameter.
func NewLead(c *mgo.Collection, r *mgo.DBRef, source, owner, status string,
	teamsize, rate, duration float64, start string, comments []string) (*lead, error) {

	doc := lead{
		Id:                 bson.NewObjectId(),
		Contact:            r,
		Source:             source,
		Owner:              owner,
		Status:             status,
		TeamSize:           teamsize,
		RatePerHour:        rate,
		DurationInMonths:   duration,
		EstimatedStartDate: start,
		Comments:           comments,
	}
	err := c.Insert(doc)
	if err != nil {
		return &lead{}, err
	}
	return &doc, nil
}

// TODO(Hari): Move session logic into a config file and a separate function
func GetLead(i bson.ObjectId) (*lead, error) {
	sess, err := mgo.Dial("localhost")
	if err != nil {
		return &lead{}, err
	}
	defer sess.Close()
	sess.SetMode(mgo.Monotonic, true)
	sess.SetSafe(&mgo.Safe{})
	collection := sess.DB("test").C("newlead")
	var l lead
	err = collection.FindId(i).One(&l)
	if err != nil {
		return &lead{}, err
	}
	return &l, nil
}

func DeleteLead(i bson.ObjectId) error {
	sess, err := mgo.Dial("localhost")
	if err != nil {
		return err
	}
	defer sess.Close()
	sess.SetMode(mgo.Monotonic, true)
	sess.SetSafe(&mgo.Safe{})
	collection := sess.DB("test").C("newlead")
	err = collection.RemoveId(i)
	return err
}
<commit_msg>Use config package for lead.go<commit_after>package main

import (
	"github.com/joshsoftware/curem/config"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

type lead struct {
	Id                 bson.ObjectId `bson:"_id"                          json:"id"`
	Contact            *mgo.DBRef    `bson:"contact,omitempty"            json:"contact,omitempty"`
	Source             string        `bson:"source,omitempty"             json:"source,omitempty"`
	Owner              string        `bson:"owner,omitempty"              json:"owner,omitempty"`
	Status             string        `bson:"status,omitempty"             json:"status,omitempty"`
	TeamSize           float64       `bson:"teamsize,omitempty"           json:"teamsize,omitempty"`
	RatePerHour        float64       `bson:"rateperhour,omitempty"        json:"rateperhour,omitempty"`
	DurationInMonths   float64       `bson:"durationinmonths,omitempty"   json:"durationinmonths,omitempty"`
	EstimatedStartDate string        `bson:"estimatedstartdate,omitempty" json:"estimatedstartdate,omitempty"`
	Comments           []string      `bson:"comments,omitempty"           json:"comments,omitempty"`
}

// NewLead takes the fields of a lead, initializes a struct of lead type and returns
// the pointer to that struct.
// Also, It inserts the lead data into a mongoDB collection, which is passed as the first parameter.
func NewLead(c *mgo.Collection, r *mgo.DBRef, source, owner, status string,
	teamsize, rate, duration float64, start string, comments []string) (*lead, error) {

	doc := lead{
		Id:                 bson.NewObjectId(),
		Contact:            r,
		Source:             source,
		Owner:              owner,
		Status:             status,
		TeamSize:           teamsize,
		RatePerHour:        rate,
		DurationInMonths:   duration,
		EstimatedStartDate: start,
		Comments:           comments,
	}
	err := c.Insert(doc)
	if err != nil {
		return &lead{}, err
	}
	return &doc, nil
}

func GetLead(i bson.ObjectId) (*lead, error) {
	collection := config.Db.C("newlead")
	var l lead
	err = collection.FindId(i).One(&l)
	if err != nil {
		return &lead{}, err
	}
	return &l, nil
}

func DeleteLead(i bson.ObjectId) error {
	collection := config.Db.C("newlead")
	err := collection.RemoveId(i)
	return err
}
<|endoftext|>


<commit_before>package lape

import (
        `bytes`
        `fmt`
	`math`
	`regexp`
)

const (
        MIN = float64(-math.MaxInt16)
        MAX = float64(math.MaxInt16)
        MATE = float64(math.MinInt32/10)
)

type Game struct {
	pieces	[64]Piece
	players	[2]*Player
        attacks *Attack
        current int
}

func (g *Game)Initialize() *Game {
        g.players[0] = new(Player).Initialize(g, WHITE)
        g.players[1] = new(Player).Initialize(g, BLACK)
        g.attacks = new(Attack).Initialize()
        g.current = WHITE

        return g
}

func (g *Game) Setup(white, black string) *Game {
	re := regexp.MustCompile(`\W+`)
	whitePieces, blackPieces := re.Split(white, -1), re.Split(black, -1)
	return g.SetupSide(whitePieces, 0).SetupSide(blackPieces, 1)
}

func (g *Game) SetupSide(moves []string, color int) *Game {
	re := regexp.MustCompile(`([KQRBN]?)([a-h])([1-8])`)

	for _, move := range moves {
		arr := re.FindStringSubmatch(move)
		if len(arr) == 0 {
			fmt.Printf("Invalid move %s for %c\n", move, C(color))
			return g
		}
		name, col, row := arr[1], arr[2][0]-'a', arr[3][0]-'1'

		var piece Piece
		switch name {
		case `K`:
			piece = King(color)
		case `Q`:
			piece = Queen(color)
		case `R`:
			piece = Rook(color)
		case `B`:
			piece = Bishop(color)
		case `N`:
			piece = Knight(color)
		default:
			piece = Pawn(color)
		}
		g.Set(int(row), int(col), piece)
	}
	return g
}

func (g *Game)Set(row, col int, piece Piece) *Game {
        g.pieces[Index(row, col)] = piece

        return g
}

func (g *Game)Get(row, col int) Piece {
        return g.pieces[Index(row, col)]
}

func (g *Game)SetInitialPosition() *Game {
        return g.Setup(`Kg1,Qh1,Bh8,g2`, `Kg8,Rf8,f7,g6,h7`)
        return g.Setup(`Kh1,Ra7,Rc7,Ba8`, `Kh8`)
        return g.Setup(`Kh1,h2,g2,Qh4,Bf6,g5,g4,d4`, `Kg8,Rf8,f7,g6,h7,c8`)
        return g.Setup(`Kh1,g2,h2,Nh6,Qe6`, `Kh8,Rf8,g7,h7`)
        return g.Setup(`Kh1,Ra6,Rb5`, `Kh7`)
        return g.Setup(`Kh1,Ra1`, `Kg8,f7,g7,h7`)

        return g.Setup(`Kg1,f2,g2,h2`, `Kg8,Ra1`)
        return g.Setup(`Kg1,f3,e2,e3`, `Kh3,Ra1`)
        return g.Setup(`d2,f3,g2,Rf2,Kg1`, `Kg3,Ra1`)
        return g.Setup(`Ra1,Nb1,Bc1,Qd1,Ke1,Bf1,Ng1,Rh1,a2,b2,c2,d2,e2,f2,g2,h2`, `Ra8,Nb8,Bc8,Qd8,Ke8,Bf8,Ng8,Rh8,a7,b7,c7,d7,e7,f7,g7,h7`)
        return g.Setup(`a3,Bb4,a5,c3,e7,Kh2`, `a7,a5,b6,Bc7,Kg8`)
        return g.Setup(`a2,Ra3,b3,a7,Kg1`, `d4,Rc4,c3,c5,Bb6,Kg8`)
}

func (g *Game)Search(depth int) (best *Move) {
        position := new(Position).Initialize(g, g.pieces, g.current)
        moves := position.Moves(g.current)
        estimate := MIN

        if len(moves) > 0 {
                for i, move := range moves {
                        score := -position.MakeMove(g, move).Score(depth*2-1, g.current, MIN, MAX)
                        fmt.Printf("  %d/%d: %s for %s, score is %.2f\n", i+1, len(moves), move, C(g.current), score)
                        if score >= estimate {
                                if -score == MATE { // Just pick the move if it mates.
                                        return move
                                }
                                estimate = score
                                best = move
                                fmt.Printf("  New best move for %s is %s\n\n", C(g.current), best)
                        }
                }
        } else if position.IsCheck(g.current) {
                fmt.Printf("Checkmate for %s\n", C(g.current))
        } else {
                fmt.Printf("Stalemate\n") // TODO
        }
	return
}

func (g *Game)String() string {
	buffer := bytes.NewBufferString("  a b c d e f g h\n")
	for row := 7;  row >= 0;  row-- {
		buffer.WriteByte('1' + byte(row))
		for col := 0;  col <= 7; col++ {
			index := Index(row, col)
			buffer.WriteByte(' ')
			if piece := g.pieces[index]; piece != 0 {
				buffer.WriteString(piece.String())
			} else {
				buffer.WriteString("\u22C5")
			}
		}
		buffer.WriteByte('\n')
	}
	return buffer.String()
}
<commit_msg>Solving mate in 2 problems<commit_after>package lape

import (
        `bytes`
        `fmt`
	`math`
	`regexp`
)

const (
        MIN = float64(-math.MaxInt16)
        MAX = float64(math.MaxInt16)
        MATE = float64(math.MinInt32/10)
)

type Game struct {
	pieces	[64]Piece
	players	[2]*Player
        attacks *Attack
        current int
}

func (g *Game)Initialize() *Game {
        g.players[0] = new(Player).Initialize(g, WHITE)
        g.players[1] = new(Player).Initialize(g, BLACK)
        g.attacks = new(Attack).Initialize()
        g.current = WHITE

        return g
}

func (g *Game) Setup(white, black string) *Game {
	re := regexp.MustCompile(`\W+`)
	whitePieces, blackPieces := re.Split(white, -1), re.Split(black, -1)
	return g.SetupSide(whitePieces, 0).SetupSide(blackPieces, 1)
}

func (g *Game) SetupSide(moves []string, color int) *Game {
	re := regexp.MustCompile(`([KQRBN]?)([a-h])([1-8])`)

	for _, move := range moves {
		arr := re.FindStringSubmatch(move)
		if len(arr) == 0 {
			fmt.Printf("Invalid move %s for %c\n", move, C(color))
			return g
		}
		name, col, row := arr[1], arr[2][0]-'a', arr[3][0]-'1'

		var piece Piece
		switch name {
		case `K`:
			piece = King(color)
		case `Q`:
			piece = Queen(color)
		case `R`:
			piece = Rook(color)
		case `B`:
			piece = Bishop(color)
		case `N`:
			piece = Knight(color)
		default:
			piece = Pawn(color)
		}
		g.Set(int(row), int(col), piece)
	}
	return g
}

func (g *Game)Set(row, col int, piece Piece) *Game {
        g.pieces[Index(row, col)] = piece

        return g
}

func (g *Game)Get(row, col int) Piece {
        return g.pieces[Index(row, col)]
}

func (g *Game)SetInitialPosition() *Game {
        // http://chessproblem.ru/index.php?kind=2&f_ot=0&f_do=8&lev=0
        return g.Setup(`Qc2,Nc5,Kf4`, `Kd4`) // Nb7
        return g.Setup(`Qf6,Kf8`, `Kh7,Nf5`) // Qg5
        return g.Setup(`Qf7,Nf3,Kf2`, `Kg4`) // Qf6
        return g.Setup(`Qc2,Kc3,Ra4`, `Kb5`) // Qg6
        return g.Setup(`Kb4,Nc1,Qg7`, `Kb1`) // TODO: Qc3 stalemate vs. Kc3+-
        return g.Setup(`Ke5,Qc1,Rf3,Bg2`, `Ke2,Nd5,Nb1`) // Rd3
        return g.Setup(`Qa8,Bf7,Ng2,Kf1`, `Kg4`) // Qb8
        return g.Setup(`Bb1,Rd3,Ke5`, `Kh7`) // Kf1

        return g.Setup(`Kg1,Qh1,Bh8,g2`, `Kg8,Rf8,f7,g6,h7`)
        return g.Setup(`Kh1,Ra7,Rc7,Ba8`, `Kh8`)
        return g.Setup(`Kh1,h2,g2,Qh4,Bf6,g5,g4,d4`, `Kg8,Rf8,f7,g6,h7,c8`)
        return g.Setup(`Kh1,g2,h2,Nh6,Qe6`, `Kh8,Rf8,g7,h7`)
        return g.Setup(`Kh1,Ra6,Rb5`, `Kh7`)
        return g.Setup(`Kh1,Ra1`, `Kg8,f7,g7,h7`)

        return g.Setup(`Kg1,f2,g2,h2`, `Kg8,Ra1`)
        return g.Setup(`Kg1,f3,e2,e3`, `Kh3,Ra1`)
        return g.Setup(`d2,f3,g2,Rf2,Kg1`, `Kg3,Ra1`)
        return g.Setup(`Ra1,Nb1,Bc1,Qd1,Ke1,Bf1,Ng1,Rh1,a2,b2,c2,d2,e2,f2,g2,h2`, `Ra8,Nb8,Bc8,Qd8,Ke8,Bf8,Ng8,Rh8,a7,b7,c7,d7,e7,f7,g7,h7`)
        return g.Setup(`a3,Bb4,a5,c3,e7,Kh2`, `a7,a5,b6,Bc7,Kg8`)
        return g.Setup(`a2,Ra3,b3,a7,Kg1`, `d4,Rc4,c3,c5,Bb6,Kg8`)
}

func (g *Game)Search(depth int) (best *Move) {
        position := new(Position).Initialize(g, g.pieces, g.current)
        moves := position.Moves(g.current)
        estimate := MIN

        if len(moves) > 0 {
                for i, move := range moves {
                        score := -position.MakeMove(g, move).Score(depth*2-1, g.current, MIN, MAX)
                        fmt.Printf("  %d/%d: %s for %s, score is %.2f\n", i+1, len(moves), move, C(g.current), score)
                        if score >= estimate {
                                if -score == MATE { // Just pick the move if it mates.
                                        return move
                                }
                                estimate = score
                                best = move
                                fmt.Printf("  New best move for %s is %s\n\n", C(g.current), best)
                        }
                }
        } else if position.IsCheck(g.current) {
                fmt.Printf("Checkmate for %s\n", C(g.current))
        } else {
                fmt.Printf("Stalemate\n") // TODO
        }
	return
}

func (g *Game)String() string {
	buffer := bytes.NewBufferString("  a b c d e f g h\n")
	for row := 7;  row >= 0;  row-- {
		buffer.WriteByte('1' + byte(row))
		for col := 0;  col <= 7; col++ {
			index := Index(row, col)
			buffer.WriteByte(' ')
			if piece := g.pieces[index]; piece != 0 {
				buffer.WriteString(piece.String())
			} else {
				buffer.WriteString("\u22C5")
			}
		}
		buffer.WriteByte('\n')
	}
	return buffer.String()
}
<|endoftext|>


<commit_before>package sharing

import (
	"fmt"
	"net/url"

	"github.com/cozy/cozy-stack/model/instance"
	"github.com/cozy/cozy-stack/model/job"
	"github.com/cozy/cozy-stack/model/vfs"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/mail"
	"github.com/cozy/cozy-stack/pkg/shortcut"
)

// SendInvitations sends invitation mails to the recipients that were in the
// mail-not-sent status (owner only)
func (s *Sharing) SendInvitations(inst *instance.Instance, codes map[string]string) error {
	if !s.Owner {
		return ErrInvalidSharing
	}
	if len(s.Members) != len(s.Credentials)+1 {
		return ErrInvalidSharing
	}
	sharer, desc := s.getSharerAndDescription(inst)

	for i, m := range s.Members {
		if i == 0 || m.Status != MemberStatusMailNotSent { // i == 0 is for the owner
			continue
		}
		link := m.InvitationLink(inst, s, s.Credentials[i-1].State, codes)
		if m.Instance != "" {
			if err := m.SendShortcut(inst, s, link); err == nil {
				continue
			}
		}
		if m.Email == "" {
			return ErrInvitationNotSent
		}
		if err := m.SendMail(inst, s, sharer, desc, link); err != nil {
			inst.Logger().WithField("nspace", "sharing").
				Errorf("Can't send email for %#v: %s", m.Email, err)
			return ErrInvitationNotSent
		}
		s.Members[i].Status = MemberStatusPendingInvitation
	}

	return couchdb.UpdateDoc(inst, s)
}

// SendInvitationsToMembers sends mails from a recipient (open_sharing) to
// their contacts to invite them
func (s *Sharing) SendInvitationsToMembers(inst *instance.Instance, members []Member, states map[string]string) error {
	sharer, desc := s.getSharerAndDescription(inst)

	for _, m := range members {
		key := m.Email
		if key == "" {
			key = m.Instance
		}
		link := m.InvitationLink(inst, s, states[key], nil)
		if m.Instance != "" {
			if err := m.SendShortcut(inst, s, link); err == nil {
				continue
			}
		}
		if m.Email == "" {
			return ErrInvitationNotSent
		}
		if err := m.SendMail(inst, s, sharer, desc, link); err != nil {
			inst.Logger().WithField("nspace", "sharing").
				Errorf("Can't send email for %#v: %s", m.Email, err)
			return ErrInvitationNotSent
		}
		for i, member := range s.Members {
			if i == 0 {
				continue // skip the owner
			}
			var found bool
			if m.Email == "" {
				found = m.Instance == member.Instance
			} else {
				found = m.Email == member.Email
			}
			if found && member.Status == MemberStatusMailNotSent {
				s.Members[i].Status = MemberStatusPendingInvitation
				break
			}
		}
	}
	return couchdb.UpdateDoc(inst, s)
}

func (s *Sharing) getSharerAndDescription(inst *instance.Instance) (string, string) {
	sharer, _ := inst.PublicName()
	if sharer == "" {
		sharer = inst.Translate("Sharing Empty name")
	}
	desc := s.Description
	if desc == "" {
		desc = inst.Translate("Sharing Empty description")
	}
	return sharer, desc
}

// InvitationLink generates an HTTP link where the recipient can start the
// process of accepting the sharing
func (m *Member) InvitationLink(inst *instance.Instance, s *Sharing, state string, codes map[string]string) string {
	if s.Owner && s.PreviewPath != "" && codes != nil {
		if code, ok := codes[m.Email]; ok {
			u := inst.SubDomain(s.AppSlug)
			u.Path = s.PreviewPath
			u.RawQuery = url.Values{"sharecode": {code}}.Encode()
			return u.String()
		}
	}

	query := url.Values{"state": {state}}
	path := fmt.Sprintf("/sharings/%s/discovery", s.SID)
	return inst.PageURL(path, query)
}

// SendMail sends an invitation mail to a recipient
func (m *Member) SendMail(inst *instance.Instance, s *Sharing, sharer, description, link string) error {
	addr := &mail.Address{
		Email: m.Email,
		Name:  m.PrimaryName(),
	}
	sharerMail, _ := inst.SettingsEMail()
	var action string
	if s.ReadOnlyRules() {
		action = inst.Translate("Mail Sharing Request Action Read")
	} else {
		action = inst.Translate("Mail Sharing Request Action Write")
	}
	docType := getDocumentType(inst, s)
	mailValues := map[string]interface{}{
		"SharerPublicName": sharer,
		"SharerEmail":      sharerMail,
		"Action":           action,
		"Description":      description,
		"DocType":          docType,
		"SharingLink":      link,
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "from",
		To:             []*mail.Address{addr},
		TemplateName:   "sharing_request",
		TemplateValues: mailValues,
		RecipientName:  addr.Name,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}

func getDocumentType(inst *instance.Instance, s *Sharing) string {
	rule := s.FirstFilesRule()
	if rule == nil {
		return inst.Translate("Notification Sharing Type Document")
	}
	_, err := inst.VFS().FileByID(rule.Values[0])
	if err != nil {
		return inst.Translate("Notification Sharing Type Directory")
	}
	return inst.Translate("Notification Sharing Type File")
}

// CreateShortcut is used to create a shortcut for a Cozy to Cozy sharing that
// has not yet been accepted.
func (s *Sharing) CreateShortcut(inst *instance.Instance, discoveryURL string) error {
	dir, err := EnsureSharedWithMeDir(inst)
	if err != nil {
		return err
	}

	body := shortcut.Generate(discoveryURL)
	cm := vfs.NewCozyMetadata(s.Members[0].Instance)
	fileDoc, err := vfs.NewFileDoc(
		s.Description+".url",
		dir.DocID,
		int64(len(body)),
		nil, // Let the VFS compute the md5sum
		consts.ShortcutMimeType,
		"shortcut",
		cm.UpdatedAt,
		false, // Not executable
		false, // Not trashed
		nil,   // No tags
	)
	if err != nil {
		return err
	}
	fileDoc.CozyMetadata = cm
	fileDoc.Metadata = vfs.Metadata{
		"sharing": map[string]interface{}{
			"status": "new",
		},
		"target": map[string]interface{}{
			"cozyMetadata": map[string]interface{}{
				"instance": s.Members[0].Instance,
			},
			"_type": s.Rules[0].DocType,
			"mime":  s.Rules[0].Mime,
		},
	}
	fileDoc.AddReferencedBy(couchdb.DocReference{
		ID:   s.SID,
		Type: consts.Sharings,
	})

	file, err := inst.VFS().CreateFile(fileDoc, nil)
	if err != nil {
		return err
	}
	_, err = file.Write(body)
	if cerr := file.Close(); cerr != nil && err == nil {
		err = cerr
	}
	if err != nil {
		return err
	}

	s.ShortcutID = fileDoc.DocID
	if err := couchdb.UpdateDoc(inst, s); err != nil {
		inst.Logger().Warnf("Cannot save shortcut id %s: %s", s.ShortcutID, err)
	}

	return s.SendShortcutMail(inst, fileDoc)
}

// SendShortcut sends the HTTP request to the cozy of the recipient for adding
// a shortcut on the recipient's instance.
func (m *Member) SendShortcut(inst *instance.Instance, s *Sharing, link string) error {
	u, err := url.Parse(m.Instance)
	if err != nil || u.Host == "" {
		return ErrInvalidURL
	}

	creds := s.FindCredentials(m)
	if creds == nil {
		return ErrInvalidSharing
	}

	v := url.Values{}
	v.Add("shortcut", "true")
	v.Add("url", link)
	u.RawQuery = v.Encode()
	return m.CreateSharingRequest(inst, s, creds, u)
}

// SendShortcutMail will send a notification mail after a shortcut for a
// sharing has been created.
func (s *Sharing) SendShortcutMail(inst *instance.Instance, fileDoc *vfs.FileDoc) error {
	sharerName := s.Members[0].PublicName
	if sharerName == "" {
		sharerName = inst.Translate("Sharing Empty name")
	}
	u := inst.SubDomain(consts.DriveSlug)
	u.Fragment = "/folder/" + fileDoc.DirID
	targetType := getTargetType(inst, fileDoc.Metadata)
	mailValues := map[string]interface{}{
		"SharerPublicName": sharerName,
		"TargetType":       targetType,
		"TargetName":       s.Description,
		"SharingsLink":     u.String(),
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "noreply",
		TemplateName:   "notifications_sharing",
		TemplateValues: mailValues,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}

func getTargetType(inst *instance.Instance, metadata map[string]interface{}) string {
	target, _ := metadata["target"].(map[string]interface{})
	if target["_type"] != consts.Files {
		return inst.Translate("Notification Sharing Type Document")
	}
	if target["mime"] == nil || target["mime"] == "" {
		return inst.Translate("Notification Sharing Type Directory")
	}
	return inst.Translate("Notification Sharing Type File")
}

// InviteMsg is the struct for the invite route
type InviteMsg struct {
	Sharer      string `json:"sharer_public_name"`
	Description string `json:"description"`
	Link        string `json:"sharing_link"`
}

// SendInviteMail will send an invitation email to the owner of this cozy.
func SendInviteMail(inst *instance.Instance, invite *InviteMsg) error {
	action := inst.Translate("Mail Sharing Request Action Read")
	docType := inst.Translate("Notification Sharing Type Directory")
	mailValues := map[string]interface{}{
		"SharerPublicName": invite.Sharer,
		"SharerEmail":      "",
		"Action":           action,
		"Description":      invite.Description,
		"DocType":          docType,
		"SharingLink":      invite.Link,
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "noreply",
		TemplateName:   "sharing_request",
		TemplateValues: mailValues,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<commit_msg>Fix the status of sharing members (#2630)<commit_after>package sharing

import (
	"fmt"
	"net/url"

	"github.com/cozy/cozy-stack/model/instance"
	"github.com/cozy/cozy-stack/model/job"
	"github.com/cozy/cozy-stack/model/vfs"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/mail"
	"github.com/cozy/cozy-stack/pkg/shortcut"
)

// SendInvitations sends invitation mails to the recipients that were in the
// mail-not-sent status (owner only)
func (s *Sharing) SendInvitations(inst *instance.Instance, codes map[string]string) error {
	if !s.Owner {
		return ErrInvalidSharing
	}
	if len(s.Members) != len(s.Credentials)+1 {
		return ErrInvalidSharing
	}
	sharer, desc := s.getSharerAndDescription(inst)

	for i, m := range s.Members {
		if i == 0 || m.Status != MemberStatusMailNotSent { // i == 0 is for the owner
			continue
		}
		link := m.InvitationLink(inst, s, s.Credentials[i-1].State, codes)
		if m.Instance != "" {
			if err := m.SendShortcut(inst, s, link); err == nil {
				s.Members[i].Status = MemberStatusPendingInvitation
				continue
			}
		}
		if m.Email == "" {
			return ErrInvitationNotSent
		}
		if err := m.SendMail(inst, s, sharer, desc, link); err != nil {
			inst.Logger().WithField("nspace", "sharing").
				Errorf("Can't send email for %#v: %s", m.Email, err)
			return ErrInvitationNotSent
		}
		s.Members[i].Status = MemberStatusPendingInvitation
	}

	return couchdb.UpdateDoc(inst, s)
}

// SendInvitationsToMembers sends mails from a recipient (open_sharing) to
// their contacts to invite them
func (s *Sharing) SendInvitationsToMembers(inst *instance.Instance, members []Member, states map[string]string) error {
	sharer, desc := s.getSharerAndDescription(inst)

	for _, m := range members {
		key := m.Email
		if key == "" {
			key = m.Instance
		}
		sent := false
		link := m.InvitationLink(inst, s, states[key], nil)
		if m.Instance != "" {
			if err := m.SendShortcut(inst, s, link); err != nil {
				sent = true
			}
		}
		if !sent {
			if m.Email == "" {
				return ErrInvitationNotSent
			}
			if err := m.SendMail(inst, s, sharer, desc, link); err != nil {
				inst.Logger().WithField("nspace", "sharing").
					Errorf("Can't send email for %#v: %s", m.Email, err)
				return ErrInvitationNotSent
			}
		}
		for i, member := range s.Members {
			if i == 0 {
				continue // skip the owner
			}
			var found bool
			if m.Email == "" {
				found = m.Instance == member.Instance
			} else {
				found = m.Email == member.Email
			}
			if found && member.Status == MemberStatusMailNotSent {
				s.Members[i].Status = MemberStatusPendingInvitation
				break
			}
		}
	}
	return couchdb.UpdateDoc(inst, s)
}

func (s *Sharing) getSharerAndDescription(inst *instance.Instance) (string, string) {
	sharer, _ := inst.PublicName()
	if sharer == "" {
		sharer = inst.Translate("Sharing Empty name")
	}
	desc := s.Description
	if desc == "" {
		desc = inst.Translate("Sharing Empty description")
	}
	return sharer, desc
}

// InvitationLink generates an HTTP link where the recipient can start the
// process of accepting the sharing
func (m *Member) InvitationLink(inst *instance.Instance, s *Sharing, state string, codes map[string]string) string {
	if s.Owner && s.PreviewPath != "" && codes != nil {
		if code, ok := codes[m.Email]; ok {
			u := inst.SubDomain(s.AppSlug)
			u.Path = s.PreviewPath
			u.RawQuery = url.Values{"sharecode": {code}}.Encode()
			return u.String()
		}
	}

	query := url.Values{"state": {state}}
	path := fmt.Sprintf("/sharings/%s/discovery", s.SID)
	return inst.PageURL(path, query)
}

// SendMail sends an invitation mail to a recipient
func (m *Member) SendMail(inst *instance.Instance, s *Sharing, sharer, description, link string) error {
	addr := &mail.Address{
		Email: m.Email,
		Name:  m.PrimaryName(),
	}
	sharerMail, _ := inst.SettingsEMail()
	var action string
	if s.ReadOnlyRules() {
		action = inst.Translate("Mail Sharing Request Action Read")
	} else {
		action = inst.Translate("Mail Sharing Request Action Write")
	}
	docType := getDocumentType(inst, s)
	mailValues := map[string]interface{}{
		"SharerPublicName": sharer,
		"SharerEmail":      sharerMail,
		"Action":           action,
		"Description":      description,
		"DocType":          docType,
		"SharingLink":      link,
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "from",
		To:             []*mail.Address{addr},
		TemplateName:   "sharing_request",
		TemplateValues: mailValues,
		RecipientName:  addr.Name,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}

func getDocumentType(inst *instance.Instance, s *Sharing) string {
	rule := s.FirstFilesRule()
	if rule == nil {
		return inst.Translate("Notification Sharing Type Document")
	}
	_, err := inst.VFS().FileByID(rule.Values[0])
	if err != nil {
		return inst.Translate("Notification Sharing Type Directory")
	}
	return inst.Translate("Notification Sharing Type File")
}

// CreateShortcut is used to create a shortcut for a Cozy to Cozy sharing that
// has not yet been accepted.
func (s *Sharing) CreateShortcut(inst *instance.Instance, discoveryURL string) error {
	dir, err := EnsureSharedWithMeDir(inst)
	if err != nil {
		return err
	}

	body := shortcut.Generate(discoveryURL)
	cm := vfs.NewCozyMetadata(s.Members[0].Instance)
	fileDoc, err := vfs.NewFileDoc(
		s.Description+".url",
		dir.DocID,
		int64(len(body)),
		nil, // Let the VFS compute the md5sum
		consts.ShortcutMimeType,
		"shortcut",
		cm.UpdatedAt,
		false, // Not executable
		false, // Not trashed
		nil,   // No tags
	)
	if err != nil {
		return err
	}
	fileDoc.CozyMetadata = cm
	fileDoc.Metadata = vfs.Metadata{
		"sharing": map[string]interface{}{
			"status": "new",
		},
		"target": map[string]interface{}{
			"cozyMetadata": map[string]interface{}{
				"instance": s.Members[0].Instance,
			},
			"_type": s.Rules[0].DocType,
			"mime":  s.Rules[0].Mime,
		},
	}
	fileDoc.AddReferencedBy(couchdb.DocReference{
		ID:   s.SID,
		Type: consts.Sharings,
	})

	file, err := inst.VFS().CreateFile(fileDoc, nil)
	if err != nil {
		return err
	}
	_, err = file.Write(body)
	if cerr := file.Close(); cerr != nil && err == nil {
		err = cerr
	}
	if err != nil {
		return err
	}

	s.ShortcutID = fileDoc.DocID
	if err := couchdb.UpdateDoc(inst, s); err != nil {
		inst.Logger().Warnf("Cannot save shortcut id %s: %s", s.ShortcutID, err)
	}

	return s.SendShortcutMail(inst, fileDoc)
}

// SendShortcut sends the HTTP request to the cozy of the recipient for adding
// a shortcut on the recipient's instance.
func (m *Member) SendShortcut(inst *instance.Instance, s *Sharing, link string) error {
	u, err := url.Parse(m.Instance)
	if err != nil || u.Host == "" {
		return ErrInvalidURL
	}

	creds := s.FindCredentials(m)
	if creds == nil {
		return ErrInvalidSharing
	}

	v := url.Values{}
	v.Add("shortcut", "true")
	v.Add("url", link)
	u.RawQuery = v.Encode()
	return m.CreateSharingRequest(inst, s, creds, u)
}

// SendShortcutMail will send a notification mail after a shortcut for a
// sharing has been created.
func (s *Sharing) SendShortcutMail(inst *instance.Instance, fileDoc *vfs.FileDoc) error {
	sharerName := s.Members[0].PublicName
	if sharerName == "" {
		sharerName = inst.Translate("Sharing Empty name")
	}
	u := inst.SubDomain(consts.DriveSlug)
	u.Fragment = "/folder/" + fileDoc.DirID
	targetType := getTargetType(inst, fileDoc.Metadata)
	mailValues := map[string]interface{}{
		"SharerPublicName": sharerName,
		"TargetType":       targetType,
		"TargetName":       s.Description,
		"SharingsLink":     u.String(),
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "noreply",
		TemplateName:   "notifications_sharing",
		TemplateValues: mailValues,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}

func getTargetType(inst *instance.Instance, metadata map[string]interface{}) string {
	target, _ := metadata["target"].(map[string]interface{})
	if target["_type"] != consts.Files {
		return inst.Translate("Notification Sharing Type Document")
	}
	if target["mime"] == nil || target["mime"] == "" {
		return inst.Translate("Notification Sharing Type Directory")
	}
	return inst.Translate("Notification Sharing Type File")
}

// InviteMsg is the struct for the invite route
type InviteMsg struct {
	Sharer      string `json:"sharer_public_name"`
	Description string `json:"description"`
	Link        string `json:"sharing_link"`
}

// SendInviteMail will send an invitation email to the owner of this cozy.
func SendInviteMail(inst *instance.Instance, invite *InviteMsg) error {
	action := inst.Translate("Mail Sharing Request Action Read")
	docType := inst.Translate("Notification Sharing Type Directory")
	mailValues := map[string]interface{}{
		"SharerPublicName": invite.Sharer,
		"SharerEmail":      "",
		"Action":           action,
		"Description":      invite.Description,
		"DocType":          docType,
		"SharingLink":      invite.Link,
	}
	msg, err := job.NewMessage(mail.Options{
		Mode:           "noreply",
		TemplateName:   "sharing_request",
		TemplateValues: mailValues,
		Layout:         mail.CozyCloudLayout,
	})
	if err != nil {
		return err
	}
	_, err = job.System().PushJob(inst, &job.JobRequest{
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/gourd/goparser"
)

func init() {
	tpls.Append("gen service:upperio",
		`{{ define "package" }}package {{ .Pkg }}{{ end }}`,
		`{{ define "imports" }}"net/http"
		"github.com/gourd/service"
		"github.com/gourd/service/upperio"
		"upper.io/db"{{ end }}`,
		`{{ define "code" }}

func init() {
	service.Providers.Define("{{ .Type.Name }}", Get{{ .Type.Name }}Service)
}

// Get{{ .Type.Name }}Service implements service.ProviderFunc
func Get{{ .Type.Name }}Service(r *http.Request) (s service.Service, err error) {

	// obtain database
	db, err := upperio.Open(r, "default")
	if err != nil {
		return
	}

	// define service and return
	s = &{{ .Type.Name }}Service{db}
	return
}

// {{ .Type.Name }}Service serves generic CURD for type {{ .Type.Name }}
// Generated by gourd CLI tool
type {{ .Type.Name }}Service struct {
	Db db.Database
}

// Create a {{ .Type.Name }} in the database, of the parent
func (s *{{ .Type.Name }}Service) Create(
	cond service.Conds, ep service.EntityPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	//TODO: convert cond into parentkey and
	//      enforce to the entity

	// add the entity to collection
	id, err := coll.Append(ep)
	if err != nil {
		err = service.Errorf(500, err.Error())
		return
	}

	//TODO: apply the key to the entity
	e := ep.(*{{.Type.Name}})
	e.{{ .Id.Name }} = {{ .Id.Type }}(id.({{ .Id.DbType }}))

	return
}

// Search a {{ .Type.Name }} by its condition(s)
func (s *{{ .Type.Name }}Service) Search(
	c service.Conds, lp service.EntityListPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get list condition and ignore the error
	cond, _ := c.GetMap()

	// retrieve all users
	res := coll.Find(db.Cond(cond))

	// TODO: also work with c.Cond for ListCond (limit and offset)
	err = res.All(lp)
	if err != nil {
		err = service.Errorf(500, err.Error())
	}

	return nil
}

// One returns the first {{ .Type.Name }} matches condition(s)
func (s *{{ .Type.Name }}Service) One(
	c service.Conds, ep service.EntityPtr) (err error) {

	// retrieve from database
	l := &[]{{ .Type.Name }}{}
	err = s.Search(c, l)
	if err != nil {
		return
	}

	// if not found, report
	if len(*l) == 0 {
		err = service.Errorf(404, "Not found")
		return
	}

	// assign the value of given point
	// to the first retrieved value
	(*ep.(*{{ .Type.Name }})) = (*l)[0]
	return nil
}

// Update {{ .Type.Name }} on condition(s)
func (s *{{ .Type.Name }}Service) Update(
	c service.Conds, ep service.EntityPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get by condition and ignore the error
	cond, _ := c.GetMap()
	res := coll.Find(db.Cond(cond))

	// update the matched entities
	err = res.Update(ep)
	if err != nil {
		err = service.Errorf(500, err.Error())
	}
	return
}

// Delete {{ .Type.Name }} on condition(s)
func (s *{{ .Type.Name }}Service) Delete(
	c service.Conds) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get by condition and ignore the error
	cond, _ := c.GetMap()
	res := coll.Find(db.Cond(cond))

	// remove the matched entities
	err = res.Remove()
	if err != nil {
		err = service.Errorf(500, err.Error())
	}
	return nil
}

// AllocEntity allocate memory for an entity
func (s *{{ .Type.Name }}Service) AllocEntity() service.EntityPtr {
	return &{{ .Type.Name }}{}
}

// AllocEntity allocate memory for an entity list
func (s *{{ .Type.Name }}Service) AllocEntityList() service.EntityListPtr {
	return &[]{{ .Type.Name }}{}
}

// Len inspect the length of an entity list
func (s *{{ .Type.Name }}Service) Len(pl service.EntityListPtr) int64 {
	el := pl.(*[]{{ .Type.Name }})
	return int64(len(*el))
}

// Coll return the raw upper.io collection
func (s *{{ .Type.Name }}Service) Coll() (coll db.Collection, err error) {
	// get raw collection
	coll, err = s.Db.Collection("{{.Coll}}")
	if err != nil {
		err = service.Errorf(http.StatusInternalServerError, err.Error())
	}
	return
}

{{ end }}`)

	tpls.AddDeps("gen service:upperio", "gen:general")

	tpls.AddPrep("gen service:upperio", func(in interface{}) (interface{}, error) {
		var data map[string]interface{}
		var f interface{}
		var id *goparser.FieldSpec
		var ok bool

		if data, ok = in.(map[string]interface{}); !ok {
			return in, fmt.Errorf("Unable to prepare. Incorrect data provided: %#v", in)
		}

		if f, ok = data["Id"]; !ok {
			return in, fmt.Errorf("Unable to prepare. No Id found in given data: %#v",
				data)
		}

		if id, ok = f.(*goparser.FieldSpec); !ok {
			return in, fmt.Errorf("Unable to prepare. Wrong Id type: %#v", f)
		}

		// determine database return type
		// case by case
		dbtype := "int64"
		if id.Type == "string" {
			dbtype = "string"
		}

		// override the field spec
		data["Id"] = &UpperFieldSpec{
			id,
			dbtype,
		}
		return data, nil

	})

}

type UpperFieldSpec struct {
	*goparser.FieldSpec
	DbType string // type of id returned by upperio
}
<commit_msg>Adapt gourd/service breaking change<commit_after>package main

import (
	"fmt"
	"github.com/gourd/goparser"
)

func init() {
	tpls.Append("gen service:upperio",
		`{{ define "package" }}package {{ .Pkg }}{{ end }}`,
		`{{ define "imports" }}"net/http"
		"github.com/gourd/service"
		"github.com/gourd/service/upperio"
		"upper.io/db"{{ end }}`,
		`{{ define "code" }}

func init() {
	service.Providers.Define("{{ .Type.Name }}", Get{{ .Type.Name }}Service)
}

// Get{{ .Type.Name }}Service implements service.ProviderFunc
func Get{{ .Type.Name }}Service(r *http.Request) (s service.Service, err error) {

	// obtain database
	db, err := upperio.Open(r, "default")
	if err != nil {
		return
	}

	// define service and return
	s = &{{ .Type.Name }}Service{db}
	return
}

// {{ .Type.Name }}Service serves generic CURD for type {{ .Type.Name }}
// Generated by gourd CLI tool
type {{ .Type.Name }}Service struct {
	Db db.Database
}

// Create a {{ .Type.Name }} in the database, of the parent
func (s *{{ .Type.Name }}Service) Create(
	cond service.Conds, ep service.EntityPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	//TODO: convert cond into parentkey and
	//      enforce to the entity

	// add the entity to collection
	id, err := coll.Append(ep)
	if err != nil {
		err = service.Error(500, err.Error())
		return
	}

	//TODO: apply the key to the entity
	e := ep.(*{{.Type.Name}})
	e.{{ .Id.Name }} = {{ .Id.Type }}(id.({{ .Id.DbType }}))

	return
}

// Search a {{ .Type.Name }} by its condition(s)
func (s *{{ .Type.Name }}Service) Search(
	c service.Conds, lp service.EntityListPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get list condition and ignore the error
	cond, _ := c.GetMap()

	// retrieve all users
	res := coll.Find(db.Cond(cond))

	// TODO: also work with c.Cond for ListCond (limit and offset)
	err = res.All(lp)
	if err != nil {
		err = service.Error(500, err.Error())
	}

	return nil
}

// One returns the first {{ .Type.Name }} matches condition(s)
func (s *{{ .Type.Name }}Service) One(
	c service.Conds, ep service.EntityPtr) (err error) {

	// retrieve from database
	l := &[]{{ .Type.Name }}{}
	err = s.Search(c, l)
	if err != nil {
		return
	}

	// if not found, report
	if len(*l) == 0 {
		err = service.Error(404, "Not found")
		return
	}

	// assign the value of given point
	// to the first retrieved value
	(*ep.(*{{ .Type.Name }})) = (*l)[0]
	return nil
}

// Update {{ .Type.Name }} on condition(s)
func (s *{{ .Type.Name }}Service) Update(
	c service.Conds, ep service.EntityPtr) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get by condition and ignore the error
	cond, _ := c.GetMap()
	res := coll.Find(db.Cond(cond))

	// update the matched entities
	err = res.Update(ep)
	if err != nil {
		err = service.Error(500, err.Error())
	}
	return
}

// Delete {{ .Type.Name }} on condition(s)
func (s *{{ .Type.Name }}Service) Delete(
	c service.Conds) (err error) {

	// get collection
	coll, err := s.Coll()
	if err != nil {
		return
	}

	// get by condition and ignore the error
	cond, _ := c.GetMap()
	res := coll.Find(db.Cond(cond))

	// remove the matched entities
	err = res.Remove()
	if err != nil {
		err = service.Error(500, err.Error())
	}
	return nil
}

// AllocEntity allocate memory for an entity
func (s *{{ .Type.Name }}Service) AllocEntity() service.EntityPtr {
	return &{{ .Type.Name }}{}
}

// AllocEntity allocate memory for an entity list
func (s *{{ .Type.Name }}Service) AllocEntityList() service.EntityListPtr {
	return &[]{{ .Type.Name }}{}
}

// Len inspect the length of an entity list
func (s *{{ .Type.Name }}Service) Len(pl service.EntityListPtr) int64 {
	el := pl.(*[]{{ .Type.Name }})
	return int64(len(*el))
}

// Coll return the raw upper.io collection
func (s *{{ .Type.Name }}Service) Coll() (coll db.Collection, err error) {
	// get raw collection
	coll, err = s.Db.Collection("{{.Coll}}")
	if err != nil {
		err = service.Error(http.StatusInternalServerError, err.Error())
	}
	return
}

{{ end }}`)

	tpls.AddDeps("gen service:upperio", "gen:general")

	tpls.AddPrep("gen service:upperio", func(in interface{}) (interface{}, error) {
		var data map[string]interface{}
		var f interface{}
		var id *goparser.FieldSpec
		var ok bool

		if data, ok = in.(map[string]interface{}); !ok {
			return in, fmt.Errorf("Unable to prepare. Incorrect data provided: %#v", in)
		}

		if f, ok = data["Id"]; !ok {
			return in, fmt.Errorf("Unable to prepare. No Id found in given data: %#v",
				data)
		}

		if id, ok = f.(*goparser.FieldSpec); !ok {
			return in, fmt.Errorf("Unable to prepare. Wrong Id type: %#v", f)
		}

		// determine database return type
		// case by case
		dbtype := "int64"
		if id.Type == "string" {
			dbtype = "string"
		}

		// override the field spec
		data["Id"] = &UpperFieldSpec{
			id,
			dbtype,
		}
		return data, nil

	})

}

type UpperFieldSpec struct {
	*goparser.FieldSpec
	DbType string // type of id returned by upperio
}
<|endoftext|>


<commit_before>// Copyright Amazon.com Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 handlers deals with the agent introspection api.
package handlers

import (
	"context"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	handlersutils "github.com/aws/amazon-ecs-agent/agent/handlers/utils"
	v1 "github.com/aws/amazon-ecs-agent/agent/handlers/v1"
	"github.com/aws/amazon-ecs-agent/agent/utils/retry"
	"github.com/cihub/seelog"
)

type rootResponse struct {
	AvailableCommands []string
}

func introspectionServerSetup(containerInstanceArn *string, taskEngine handlersutils.DockerStateResolver, cfg *config.Config) *http.Server {
	paths := []string{v1.AgentMetadataPath, v1.TaskContainerMetadataPath, v1.LicensePath}
	availableCommands := &rootResponse{paths}
	// Autogenerated list of the above serverFunctions paths
	availableCommandResponse, err := json.Marshal(&availableCommands)
	if err != nil {
		seelog.Errorf("Error marshaling JSON in introspection server setup: %s", err)
	}

	defaultHandler := func(w http.ResponseWriter, r *http.Request) {
		w.Write(availableCommandResponse)
	}

	serverMux := http.NewServeMux()
	serverMux.HandleFunc("/", defaultHandler)

	v1HandlersSetup(serverMux, containerInstanceArn, taskEngine, cfg)

	// Log all requests and then pass through to serverMux
	loggingServeMux := http.NewServeMux()
	loggingServeMux.Handle("/", LoggingHandler{serverMux})

	server := &http.Server{
		Addr:         "127.0.0.1:" + strconv.Itoa(config.AgentIntrospectionPort),
		Handler:      loggingServeMux,
		ReadTimeout:  readTimeout,
		WriteTimeout: writeTimeout,
	}

	return server
}

// v1HandlersSetup adds all handlers except CredentialsHandler in v1 package to the server mux.
func v1HandlersSetup(serverMux *http.ServeMux,
	containerInstanceArn *string,
	taskEngine handlersutils.DockerStateResolver,
	cfg *config.Config) {
	serverMux.HandleFunc(v1.AgentMetadataPath, v1.AgentMetadataHandler(containerInstanceArn, cfg))
	serverMux.HandleFunc(v1.TaskContainerMetadataPath, v1.TaskContainerMetadataHandler(taskEngine))
	serverMux.HandleFunc(v1.LicensePath, v1.LicenseHandler)
}

// ServeIntrospectionHTTPEndpoint serves information about this agent/containerInstance and tasks
// running on it. "V1" here indicates the hostname version of this server instead
// of the handler versions, i.e. "V1" server can include "V1" and "V2" handlers.
func ServeIntrospectionHTTPEndpoint(ctx context.Context, containerInstanceArn *string, taskEngine engine.TaskEngine, cfg *config.Config) {
	// Is this the right level to type assert, assuming we'd abstract multiple taskengines here?
	// Revisit if we ever add another type..
	dockerTaskEngine := taskEngine.(*engine.DockerTaskEngine)

	server := introspectionServerSetup(containerInstanceArn, dockerTaskEngine, cfg)

	go func() {
		<-ctx.Done()
		if err := server.Shutdown(context.Background()); err != nil {
			// Error from closing listeners, or context timeout:
			seelog.Infof("HTTP server Shutdown: %v", err)
		}
	}()

	for {
		retry.RetryWithBackoff(retry.NewExponentialBackoff(time.Second, time.Minute, 0.2, 2), func() error {
			if err := server.ListenAndServe(); err != http.ErrServerClosed {
				seelog.Errorf("Error running introspection endpoint: %v", err)
				return err
			}
			// server was cleanly closed via context
			return nil
		})
	}
}
<commit_msg>Revert "bind introspection to localhost"<commit_after>// Copyright Amazon.com Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 handlers deals with the agent introspection api.
package handlers

import (
	"context"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/aws/amazon-ecs-agent/agent/config"
	"github.com/aws/amazon-ecs-agent/agent/engine"
	handlersutils "github.com/aws/amazon-ecs-agent/agent/handlers/utils"
	v1 "github.com/aws/amazon-ecs-agent/agent/handlers/v1"
	"github.com/aws/amazon-ecs-agent/agent/utils/retry"
	"github.com/cihub/seelog"
)

type rootResponse struct {
	AvailableCommands []string
}

func introspectionServerSetup(containerInstanceArn *string, taskEngine handlersutils.DockerStateResolver, cfg *config.Config) *http.Server {
	paths := []string{v1.AgentMetadataPath, v1.TaskContainerMetadataPath, v1.LicensePath}
	availableCommands := &rootResponse{paths}
	// Autogenerated list of the above serverFunctions paths
	availableCommandResponse, err := json.Marshal(&availableCommands)
	if err != nil {
		seelog.Errorf("Error marshaling JSON in introspection server setup: %s", err)
	}

	defaultHandler := func(w http.ResponseWriter, r *http.Request) {
		w.Write(availableCommandResponse)
	}

	serverMux := http.NewServeMux()
	serverMux.HandleFunc("/", defaultHandler)

	v1HandlersSetup(serverMux, containerInstanceArn, taskEngine, cfg)

	// Log all requests and then pass through to serverMux
	loggingServeMux := http.NewServeMux()
	loggingServeMux.Handle("/", LoggingHandler{serverMux})

	server := &http.Server{
		Addr:         ":" + strconv.Itoa(config.AgentIntrospectionPort),
		Handler:      loggingServeMux,
		ReadTimeout:  readTimeout,
		WriteTimeout: writeTimeout,
	}

	return server
}

// v1HandlersSetup adds all handlers except CredentialsHandler in v1 package to the server mux.
func v1HandlersSetup(serverMux *http.ServeMux,
	containerInstanceArn *string,
	taskEngine handlersutils.DockerStateResolver,
	cfg *config.Config) {
	serverMux.HandleFunc(v1.AgentMetadataPath, v1.AgentMetadataHandler(containerInstanceArn, cfg))
	serverMux.HandleFunc(v1.TaskContainerMetadataPath, v1.TaskContainerMetadataHandler(taskEngine))
	serverMux.HandleFunc(v1.LicensePath, v1.LicenseHandler)
}

// ServeIntrospectionHTTPEndpoint serves information about this agent/containerInstance and tasks
// running on it. "V1" here indicates the hostname version of this server instead
// of the handler versions, i.e. "V1" server can include "V1" and "V2" handlers.
func ServeIntrospectionHTTPEndpoint(ctx context.Context, containerInstanceArn *string, taskEngine engine.TaskEngine, cfg *config.Config) {
	// Is this the right level to type assert, assuming we'd abstract multiple taskengines here?
	// Revisit if we ever add another type..
	dockerTaskEngine := taskEngine.(*engine.DockerTaskEngine)

	server := introspectionServerSetup(containerInstanceArn, dockerTaskEngine, cfg)

	go func() {
		<-ctx.Done()
		if err := server.Shutdown(context.Background()); err != nil {
			// Error from closing listeners, or context timeout:
			seelog.Infof("HTTP server Shutdown: %v", err)
		}
	}()

	for {
		retry.RetryWithBackoff(retry.NewExponentialBackoff(time.Second, time.Minute, 0.2, 2), func() error {
			if err := server.ListenAndServe(); err != http.ErrServerClosed {
				seelog.Errorf("Error running introspection endpoint: %v", err)
				return err
			}
			// server was cleanly closed via context
			return nil
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageHome struct {
	Websockerurl string
}

type pageLog struct {
	LogTxt string
}

type pageHistory struct {
	Websockerurl string
	Store        template.JS
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON)
				checkErr(err, funcName(), "Impossible to write to websocket", "")
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {

	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	fmt.Println(httpServ.store.String("temp"))

	var prim template.JS
	prim = "[[new Date(1416013200000), 22],[new Date(2014, 10, 15, 0, 30), 23],[new Date(2014, 10, 15, 0, 00), 22],[new Date(2014, 10, 14, 23, 30), 21],[new Date(2014, 10, 14, 23, 00), 22],[new Date(2014, 10, 14, 22, 30), 18],]"

	p := pageHistory{Websockerurl: getWs(r) + r.Host + "/refreshdata", Store: prim}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog()}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog() (logRange []logStru) {
	file, err := os.Open("bloomsky.log")
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<commit_msg>Fix error historu<commit_after>package main

import (
	"bufio"
	"context"
	"encoding/json"
	"html/template"
	"net/http"
	"net/http/pprof"
	"os"
	"time"

	"github.com/elazarl/go-bindata-assetfs"
	"github.com/gorilla/websocket"
	"github.com/nicksnyder/go-i18n/i18n"
	bloomsky "github.com/patrickalin/bloomsky-api-go"
	"github.com/patrickalin/bloomsky-client-go/assembly-assetfs"
)

type httpServer struct {
	bloomskyMessageToHTTP chan bloomsky.Bloomsky
	httpServ              *http.Server
	conn                  *websocket.Conn
	msgJSON               []byte
	templates             map[string]*template.Template
	store                 store
}

type meas struct {
	Timestamp time.Time
	Value     float64
}

type pageHome struct {
	Websockerurl string
}

type pageLog struct {
	LogTxt string
}

type pageHistory struct {
	Websockerurl string
	Store        template.JS
}

type logStru struct {
	Time  string `json:"time"`
	Msg   string `json:"msg"`
	Level string `json:"level"`
	Param string `json:"param"`
	Fct   string `json:"fct"`
}

//listen
func (httpServ *httpServer) listen(context context.Context) {
	go func() {
		for {
			mybloomsky := <-httpServ.bloomskyMessageToHTTP
			var err error

			httpServ.msgJSON, err = json.Marshal(mybloomsky.GetBloomskyStruct())
			checkErr(err, funcName(), "Marshal json Error", "")

			if httpServ.msgJSON == nil {
				logFatal(err, funcName(), "JSON Empty", "")
			}

			if httpServ.conn != nil {
				err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON)
				checkErr(err, funcName(), "Impossible to write to websocket", "")
			}

			logDebug(funcName(), "Listen", string(httpServ.msgJSON))
		}
	}()
}

// Websocket handler to send data
func (httpServ *httpServer) refreshdata(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Refresh data Websocket handle", "")

	upgrader := websocket.Upgrader{}

	var err error

	httpServ.conn, err = upgrader.Upgrade(w, r, nil)
	checkErr(err, funcName(), "Upgrade upgrader", "")

	if err = httpServ.conn.WriteMessage(websocket.TextMessage, httpServ.msgJSON); err != nil {
		logFatal(err, funcName(), "Impossible to write to websocket", "")
	}
}

func getWs(r *http.Request) string {
	if r.TLS == nil {
		return "ws://"
	}
	return "wss://"
}

// Home bloomsky handler
func (httpServ *httpServer) home(w http.ResponseWriter, r *http.Request) {

	logDebug(funcName(), "Home Http handle", "")

	p := pageHome{Websockerurl: getWs(r) + r.Host + "/refreshdata"}
	if err := httpServ.templates["home"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template home", "")
	}
}

// Home bloomsky handler
func (httpServ *httpServer) history(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Home History handle", "")

	//fmt.Println(httpServ.store.String("temp"))

	var prim template.JS
	prim = "[[new Date(1416013200000), 22],[new Date(2014, 10, 15, 0, 30), 23],[new Date(2014, 10, 15, 0, 00), 22],[new Date(2014, 10, 14, 23, 30), 21],[new Date(2014, 10, 14, 23, 00), 22],[new Date(2014, 10, 14, 22, 30), 18],]"

	p := pageHistory{Websockerurl: getWs(r) + r.Host + "/refreshdata", Store: prim}
	if err := httpServ.templates["history"].Execute(w, p); err != nil {
		logFatal(err, funcName(), "Execute template history", "")
	}
}

// Log handler
func (httpServ *httpServer) log(w http.ResponseWriter, r *http.Request) {
	logDebug(funcName(), "Log Http handle", "")

	p := map[string]interface{}{"logRange": createArrayLog()}

	err := httpServ.templates["log"].Execute(w, p)
	checkErr(err, funcName(), "Compile template log", "")
}

func getFileServer(dev bool) http.FileSystem {
	if dev {
		return http.Dir("static")
	}
	return &assetfs.AssetFS{Asset: assemblyAssetfs.Asset, AssetDir: assemblyAssetfs.AssetDir, AssetInfo: assemblyAssetfs.AssetInfo, Prefix: "static"}
}

//createWebServer create web server
func createWebServer(in chan bloomsky.Bloomsky, HTTPPort string, HTTPSPort string, translate i18n.TranslateFunc, devel bool, store store) (*httpServer, error) {

	t := make(map[string]*template.Template)
	t["home"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/bloomsky/script.html", "tmpl/bloomsky/body.html", "tmpl/bloomsky/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["history"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/history/script.html", "tmpl/history/body.html", "tmpl/history/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)
	t["log"] = GetHTMLTemplate("bloomsky", []string{"tmpl/index.html", "tmpl/log/script.html", "tmpl/log/body.html", "tmpl/log/menu.html", "tmpl/header.html", "tmpl/endScript.html"}, map[string]interface{}{"T": translate}, devel)

	server := &httpServer{bloomskyMessageToHTTP: in,
		templates: t,
		store:     store}

	fs := http.FileServer(getFileServer(devel))

	s := http.NewServeMux()

	s.Handle("/static/", http.StripPrefix("/static/", fs))
	s.Handle("/favicon.ico", fs)
	s.HandleFunc("/", server.home)
	s.HandleFunc("/refreshdata", server.refreshdata)
	s.HandleFunc("/log", server.log)
	s.HandleFunc("/history", server.history)
	s.HandleFunc("/debug/pprof/", pprof.Index)
	s.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.HandleFunc("/debug/pprof/trace", pprof.Trace)

	h := &http.Server{Addr: HTTPPort, Handler: s}
	go func() {
		err := h.ListenAndServe()
		checkErr(err, funcName(), "Error when I create the server HTTP (don't forget ':')", "")
	}()

	hs := &http.Server{Addr: HTTPSPort, Handler: s}
	go func() {
		err := hs.ListenAndServeTLS("server.crt", "server.key")
		checkErr(err, funcName(), "Error when I create the server HTTPS (don't forget ':')", "")
	}()

	logInfo(funcName(), "Server HTTP listen on port", HTTPPort)
	logInfo(funcName(), "Server HTTPS listen on port", HTTPSPort)

	server.httpServ = h
	return server, nil
}

func createArrayLog() (logRange []logStru) {
	file, err := os.Open("bloomsky.log")
	checkErr(err, funcName(), "Imposible to open file", "bloomsky.log")

	defer file.Close()
	scanner := bufio.NewScanner(file)

	var tt logStru
	for scanner.Scan() {
		json.Unmarshal([]byte(scanner.Text()), &tt)
		checkErr(err, funcName(), "Impossible to unmarshall log", scanner.Text())

		logRange = append(logRange, tt)
	}

	scanner.Err()
	checkErr(err, funcName(), "Scanner Err", "")

	return logRange
}
<|endoftext|>


<commit_before>// Packge gdbm implements a wrapper around libgdbm, the GNU DataBase Manager
// library, for Go.
package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
	"errors"
	"unsafe"
)

// GDBM database "connection" type.
type Database struct {
	dbf  C.GDBM_FILE
	mode int
}

// GDBM database configuration type that can be used to specify how the
// database is created and treated. The `Mode` determines what the user of the
// `Database` object can do with it. The field can be a string, either "r" for
// Read-only, "w" for Read-Write, "c" for Read-Write and create if it doesn't
// exist, and "n" for Read-Write and always recreate database, even if it
// exists. TODO: write descriptions for other params... too lazy right now
type DatabaseCfg struct {
	Mode        string
	BlockSize   int
	Permissions int
}

func lastError() error {
	return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db *Database, err error) {
	return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

// More complex database initialization function that takes in a `DatabaseCfg`
// struct to allow more fine-grained control over database settings.
func OpenWithCfg(filename string, cfg DatabaseCfg) (db *Database, err error) {
	db = new(Database)

	// Convert a human-readable mode string into a libgdbm-usable constant.
	switch cfg.Mode {
	case "r":
		db.mode = C.GDBM_READER
	case "w":
		db.mode = C.GDBM_WRITER
	case "c":
		db.mode = C.GDBM_WRCREAT
	case "n":
		db.mode = C.GDBM_NEWDB
	}

	cs := C.CString(filename)
	defer C.free(unsafe.Pointer(cs))
	db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(db.mode), C.int(cfg.Permissions), nil)
	if db.dbf == nil {
		err = lastError()
	}
	return db, err
}

// Closes a database's internal file pointer.
func (db *Database) Close() {
	C.gdbm_close(db.dbf)
}

// Internal helper method to hide the two constants GDBM_INSERT and
// GDBM_REPLACE from the user.
func (db *Database) update(key string, value string, flag C.int) (err error) {
	// Convert key and value into libgdbm's `datum` data structure. See the
	// C definition at the top for the implementation of C.mk_datum(string).
	kcs := C.CString(key)
	vcs := C.CString(value)
	k := C.mk_datum(kcs)
	v := C.mk_datum(vcs)
	defer C.free(unsafe.Pointer(kcs))
	defer C.free(unsafe.Pointer(vcs))

	retv := C.gdbm_store(db.dbf, k, v, flag)
	if retv != 0 {
		err = lastError()
	}
	return err
}

// Inserts a key-value pair into the database. If the database is opened
// in "r" mode, this will return an error. Also, if the key already exists in
// the database, and error will be returned.
func (db *Database) Insert(key string, value string) (err error) {
	return db.update(key, value, C.GDBM_INSERT)
}

// Updates a key-value pair to use a new value, specified by the `value` string
// parameter. An error will be returned if the database is opened in "r" mode.
func (db *Database) Replace(key string, value string) (err error) {
	return db.update(key, value, C.GDBM_REPLACE)
}

// Returns true or false, depending on whether the specified key exists in the
// database.
func (db *Database) Exists(key string) bool {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	e := C.gdbm_exists(db.dbf, k)
	if e == 1 {
		return true
	}
	return false
}

// Returns the firstkey in this gdbm.Database. If there is not a key, an
// error will be returned in err.
func (db *Database) FirstKey() (value string, err error) {
	vdatum := C.gdbm_firstkey(db.dbf)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// Returns the nextkey after `key`. If there is not a next key, an
// error will be returned in err.
func (db *Database) NextKey(key string) (value string, err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	vdatum := C.gdbm_nextkey(db.dbf, k)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// return a map of [key]value, otherwise `err`
func (db *Database) ToMap() (db_map map[string]string, err error) {
	var (
		curr_k string
		curr_v string
		next_k string
		next_v string
	)
	db_map = make(map[string]string)

	curr_k, err = db.FirstKey()
	if err != nil {
		return db, nil
	}

	curr_v, err = db.Fetch(curr_k)
	if err != nil {
		return db, nill
	}

	db_map[curr_k] = curr_v

	for {
		next_k, err = db.NextKey(curr_k)
		if err != nil {
			break
		}

		next_v, err = db.Fetch(next_k)
		if err != nil {
			break
		}
		db_map[next_k] = next_v

		curr_k = next_k
	}
	return db_map, nil
}

// Fetches the value of the given key. If the key is not in the database, an
// error will be returned in err. Otherwise, value will be the value string
// that is keyed by `key`.
func (db *Database) Fetch(key string) (value string, err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	vdatum := C.gdbm_fetch(db.dbf, k)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// Removes a key-value pair from the database. If the database is opened in "r"
// mode, an error is returned
func (db *Database) Delete(key string) (err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	retv := C.gdbm_delete(db.dbf, k)
	if retv == -1 && db.mode == C.GDBM_READER {
		err = lastError()
	}
	return err
}

// Reorganizes the database for more efficient use of disk space. This method
// can be used if Delete(k) is called many times.
func (db *Database) Reorganize() {
	C.gdbm_reorganize(db.dbf)
}

// Synchronizes all pending database changes to the disk. TODO: note this is
// only needed in FAST mode, and FAST mode needs implemented!
func (db *Database) Sync() {
	C.gdbm_sync(db.dbf)
}
<commit_msg>typo in varname<commit_after>// Packge gdbm implements a wrapper around libgdbm, the GNU DataBase Manager
// library, for Go.
package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
	"errors"
	"unsafe"
)

// GDBM database "connection" type.
type Database struct {
	dbf  C.GDBM_FILE
	mode int
}

// GDBM database configuration type that can be used to specify how the
// database is created and treated. The `Mode` determines what the user of the
// `Database` object can do with it. The field can be a string, either "r" for
// Read-only, "w" for Read-Write, "c" for Read-Write and create if it doesn't
// exist, and "n" for Read-Write and always recreate database, even if it
// exists. TODO: write descriptions for other params... too lazy right now
type DatabaseCfg struct {
	Mode        string
	BlockSize   int
	Permissions int
}

func lastError() error {
	return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db *Database, err error) {
	return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

// More complex database initialization function that takes in a `DatabaseCfg`
// struct to allow more fine-grained control over database settings.
func OpenWithCfg(filename string, cfg DatabaseCfg) (db *Database, err error) {
	db = new(Database)

	// Convert a human-readable mode string into a libgdbm-usable constant.
	switch cfg.Mode {
	case "r":
		db.mode = C.GDBM_READER
	case "w":
		db.mode = C.GDBM_WRITER
	case "c":
		db.mode = C.GDBM_WRCREAT
	case "n":
		db.mode = C.GDBM_NEWDB
	}

	cs := C.CString(filename)
	defer C.free(unsafe.Pointer(cs))
	db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(db.mode), C.int(cfg.Permissions), nil)
	if db.dbf == nil {
		err = lastError()
	}
	return db, err
}

// Closes a database's internal file pointer.
func (db *Database) Close() {
	C.gdbm_close(db.dbf)
}

// Internal helper method to hide the two constants GDBM_INSERT and
// GDBM_REPLACE from the user.
func (db *Database) update(key string, value string, flag C.int) (err error) {
	// Convert key and value into libgdbm's `datum` data structure. See the
	// C definition at the top for the implementation of C.mk_datum(string).
	kcs := C.CString(key)
	vcs := C.CString(value)
	k := C.mk_datum(kcs)
	v := C.mk_datum(vcs)
	defer C.free(unsafe.Pointer(kcs))
	defer C.free(unsafe.Pointer(vcs))

	retv := C.gdbm_store(db.dbf, k, v, flag)
	if retv != 0 {
		err = lastError()
	}
	return err
}

// Inserts a key-value pair into the database. If the database is opened
// in "r" mode, this will return an error. Also, if the key already exists in
// the database, and error will be returned.
func (db *Database) Insert(key string, value string) (err error) {
	return db.update(key, value, C.GDBM_INSERT)
}

// Updates a key-value pair to use a new value, specified by the `value` string
// parameter. An error will be returned if the database is opened in "r" mode.
func (db *Database) Replace(key string, value string) (err error) {
	return db.update(key, value, C.GDBM_REPLACE)
}

// Returns true or false, depending on whether the specified key exists in the
// database.
func (db *Database) Exists(key string) bool {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	e := C.gdbm_exists(db.dbf, k)
	if e == 1 {
		return true
	}
	return false
}

// Returns the firstkey in this gdbm.Database. If there is not a key, an
// error will be returned in err.
func (db *Database) FirstKey() (value string, err error) {
	vdatum := C.gdbm_firstkey(db.dbf)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// Returns the nextkey after `key`. If there is not a next key, an
// error will be returned in err.
func (db *Database) NextKey(key string) (value string, err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	vdatum := C.gdbm_nextkey(db.dbf, k)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// return a map of [key]value, otherwise `err`
func (db *Database) ToMap() (db_map map[string]string, err error) {
	var (
		curr_k string
		curr_v string
		next_k string
		next_v string
	)
	db_map = make(map[string]string)

	curr_k, err = db.FirstKey()
	if err != nil {
		return db_map, nil
	}

	curr_v, err = db.Fetch(curr_k)
	if err != nil {
		return db_map, nil
	}

	db_map[curr_k] = curr_v

	for {
		next_k, err = db.NextKey(curr_k)
		if err != nil {
			break
		}

		next_v, err = db.Fetch(next_k)
		if err != nil {
			break
		}
		db_map[next_k] = next_v

		curr_k = next_k
	}
	return db_map, nil
}

// Fetches the value of the given key. If the key is not in the database, an
// error will be returned in err. Otherwise, value will be the value string
// that is keyed by `key`.
func (db *Database) Fetch(key string) (value string, err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	vdatum := C.gdbm_fetch(db.dbf, k)
	if vdatum.dptr == nil {
		return "", lastError()
	}

	value = C.GoString(vdatum.dptr)
	defer C.free(unsafe.Pointer(vdatum.dptr))
	return value, nil
}

// Removes a key-value pair from the database. If the database is opened in "r"
// mode, an error is returned
func (db *Database) Delete(key string) (err error) {
	kcs := C.CString(key)
	k := C.mk_datum(kcs)
	defer C.free(unsafe.Pointer(kcs))

	retv := C.gdbm_delete(db.dbf, k)
	if retv == -1 && db.mode == C.GDBM_READER {
		err = lastError()
	}
	return err
}

// Reorganizes the database for more efficient use of disk space. This method
// can be used if Delete(k) is called many times.
func (db *Database) Reorganize() {
	C.gdbm_reorganize(db.dbf)
}

// Synchronizes all pending database changes to the disk. TODO: note this is
// only needed in FAST mode, and FAST mode needs implemented!
func (db *Database) Sync() {
	C.gdbm_sync(db.dbf)
}
<|endoftext|>


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

package frankenstein

import (
	"fmt"
	"net"
	"sort"
	"sync"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/prometheus/frankenstein/wire"
)

var (
	memcacheRequests = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "prometheus",
		Name:      "memcache_requests_total",
		Help:      "Total count of chunks requested from memcache.",
	})

	memcacheHits = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "prometheus",
		Name:      "memcache_hits_total",
		Help:      "Total count of chunks found in memcache.",
	})

	memcacheRequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: "prometheus",
		Name:      "memcache_request_duration_seconds",
		Help:      "Total time spent in seconds doing memcache requests.",
		Buckets:   prometheus.DefBuckets,
	}, []string{"method", "status_code"})
)

func init() {
	prometheus.MustRegister(memcacheRequests)
	prometheus.MustRegister(memcacheHits)
	prometheus.MustRegister(memcacheRequestDuration)
}

// MemcacheClient is a memcache client that gets its server list from SRV
// records, and periodically updates that ServerList.
type MemcacheClient struct {
	client     *memcache.Client
	serverList *memcache.ServerList
	expiration int32
	hostname   string
	service    string

	quit chan struct{}
	wait sync.WaitGroup
}

// MemcacheConfig defines how a MemcacheClient should be constructed.
type MemcacheConfig struct {
	Host           string
	Service        string
	Timeout        time.Duration
	UpdateInterval time.Duration
	Expiration     time.Duration
}

// NewMemcacheClient creates a new MemcacheClient that gets its server list
// from SRV and updates the server list on a regular basis.
func NewMemcacheClient(config MemcacheConfig) *MemcacheClient {
	var servers memcache.ServerList
	client := memcache.NewFromSelector(&servers)
	client.Timeout = config.Timeout

	newClient := &MemcacheClient{
		client:     client,
		serverList: &servers,
		expiration: int32(config.Expiration.Seconds()),
		hostname:   config.Host,
		service:    config.Service,
		quit:       make(chan struct{}),
	}
	err := newClient.updateMemcacheServers()
	if err != nil {
		log.Errorf("Error setting memcache servers to '%v': %v", config.Host, err)
	}

	newClient.wait.Add(1)
	go newClient.updateLoop(config.UpdateInterval)
	return newClient
}

// Stop the memcache client.
func (c *MemcacheClient) Stop() {
	close(c.quit)
	c.wait.Wait()
}

func (c *MemcacheClient) updateLoop(updateInterval time.Duration) error {
	defer c.wait.Done()
	ticker := time.NewTicker(updateInterval)
	var err error
	for {
		select {
		case <-ticker.C:
			err = c.updateMemcacheServers()
			if err != nil {
				log.Warnf("Error updating memcache servers: %v", err)
			}
		case <-c.quit:
			ticker.Stop()
		}
	}
}

// updateMemcacheServers sets a memcache server list from SRV records. SRV
// priority & weight are ignored.
func (c *MemcacheClient) updateMemcacheServers() error {
	_, addrs, err := net.LookupSRV(c.service, "tcp", c.hostname)
	if err != nil {
		return err
	}
	var servers []string
	for _, srv := range addrs {
		servers = append(servers, fmt.Sprintf("%s:%d", srv.Target, srv.Port))
	}
	// ServerList deterministically maps keys to _index_ of the server list.
	// Since DNS returns records in different order each time, we sort to
	// guarantee best possible match between nodes.
	sort.Strings(servers)
	return c.serverList.SetServers(servers...)
}

func memcacheStatusCode(err error) string {
	// See https://godoc.org/github.com/bradfitz/gomemcache/memcache#pkg-variables
	switch err {
	case nil:
		return "200"
	case memcache.ErrCacheMiss:
		return "404"
	case memcache.ErrMalformedKey:
		return "400"
	default:
		return "500"
	}
}

// FetchChunkData gets chunks from memcache.
func (c *MemcacheClient) FetchChunkData(userID string, chunks []wire.Chunk) (found []wire.Chunk, missing []wire.Chunk, err error) {
	memcacheRequests.Add(float64(len(chunks)))

	keys := make([]string, 0, len(chunks))
	for _, chunk := range chunks {
		keys = append(keys, fmt.Sprintf("%s/%s", userID, chunk.ID))
	}

	var items map[string]*memcache.Item
	err = TimeRequestHistogramStatus("Get", memcacheRequestDuration, memcacheStatusCode, func() error {
		var err error
		items, err = c.client.GetMulti(keys)
		return err
	})
	if err != nil {
		return nil, chunks, err
	}

	for _, chunk := range chunks {
		item, ok := items[chunk.ID]
		if !ok {
			missing = append(missing, chunk)
		} else {
			chunk.Data = item.Value
			found = append(found, chunk)
		}
	}

	memcacheHits.Add(float64(len(found)))
	return found, missing, nil
}

// StoreChunkData serializes and stores a chunk in memcache.
func (c *MemcacheClient) StoreChunkData(userID string, chunk *wire.Chunk) error {
	return TimeRequestHistogramStatus("Put", memcacheRequestDuration, memcacheStatusCode, func() error {
		// TODO: Add compression - maybe encapsulated in marshaling/unmarshaling
		// methods of wire.Chunk.
		item := memcache.Item{
			Key:        fmt.Sprintf("%s/%s", userID, chunk.ID),
			Value:      chunk.Data,
			Expiration: c.expiration,
		}
		return c.client.Set(&item)
	})
}
<commit_msg>Fix multitenant memcache chunk keys<commit_after>// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package frankenstein

import (
	"fmt"
	"net"
	"sort"
	"sync"
	"time"

	"github.com/bradfitz/gomemcache/memcache"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/prometheus/frankenstein/wire"
)

var (
	memcacheRequests = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "prometheus",
		Name:      "memcache_requests_total",
		Help:      "Total count of chunks requested from memcache.",
	})

	memcacheHits = prometheus.NewCounter(prometheus.CounterOpts{
		Namespace: "prometheus",
		Name:      "memcache_hits_total",
		Help:      "Total count of chunks found in memcache.",
	})

	memcacheRequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
		Namespace: "prometheus",
		Name:      "memcache_request_duration_seconds",
		Help:      "Total time spent in seconds doing memcache requests.",
		Buckets:   prometheus.DefBuckets,
	}, []string{"method", "status_code"})
)

func init() {
	prometheus.MustRegister(memcacheRequests)
	prometheus.MustRegister(memcacheHits)
	prometheus.MustRegister(memcacheRequestDuration)
}

// MemcacheClient is a memcache client that gets its server list from SRV
// records, and periodically updates that ServerList.
type MemcacheClient struct {
	client     *memcache.Client
	serverList *memcache.ServerList
	expiration int32
	hostname   string
	service    string

	quit chan struct{}
	wait sync.WaitGroup
}

// MemcacheConfig defines how a MemcacheClient should be constructed.
type MemcacheConfig struct {
	Host           string
	Service        string
	Timeout        time.Duration
	UpdateInterval time.Duration
	Expiration     time.Duration
}

// NewMemcacheClient creates a new MemcacheClient that gets its server list
// from SRV and updates the server list on a regular basis.
func NewMemcacheClient(config MemcacheConfig) *MemcacheClient {
	var servers memcache.ServerList
	client := memcache.NewFromSelector(&servers)
	client.Timeout = config.Timeout

	newClient := &MemcacheClient{
		client:     client,
		serverList: &servers,
		expiration: int32(config.Expiration.Seconds()),
		hostname:   config.Host,
		service:    config.Service,
		quit:       make(chan struct{}),
	}
	err := newClient.updateMemcacheServers()
	if err != nil {
		log.Errorf("Error setting memcache servers to '%v': %v", config.Host, err)
	}

	newClient.wait.Add(1)
	go newClient.updateLoop(config.UpdateInterval)
	return newClient
}

// Stop the memcache client.
func (c *MemcacheClient) Stop() {
	close(c.quit)
	c.wait.Wait()
}

func (c *MemcacheClient) updateLoop(updateInterval time.Duration) error {
	defer c.wait.Done()
	ticker := time.NewTicker(updateInterval)
	var err error
	for {
		select {
		case <-ticker.C:
			err = c.updateMemcacheServers()
			if err != nil {
				log.Warnf("Error updating memcache servers: %v", err)
			}
		case <-c.quit:
			ticker.Stop()
		}
	}
}

// updateMemcacheServers sets a memcache server list from SRV records. SRV
// priority & weight are ignored.
func (c *MemcacheClient) updateMemcacheServers() error {
	_, addrs, err := net.LookupSRV(c.service, "tcp", c.hostname)
	if err != nil {
		return err
	}
	var servers []string
	for _, srv := range addrs {
		servers = append(servers, fmt.Sprintf("%s:%d", srv.Target, srv.Port))
	}
	// ServerList deterministically maps keys to _index_ of the server list.
	// Since DNS returns records in different order each time, we sort to
	// guarantee best possible match between nodes.
	sort.Strings(servers)
	return c.serverList.SetServers(servers...)
}

func memcacheStatusCode(err error) string {
	// See https://godoc.org/github.com/bradfitz/gomemcache/memcache#pkg-variables
	switch err {
	case nil:
		return "200"
	case memcache.ErrCacheMiss:
		return "404"
	case memcache.ErrMalformedKey:
		return "400"
	default:
		return "500"
	}
}

func memcacheKey(userID, chunkID string) string {
	return fmt.Sprintf("%s/%s", userID, chunkID)
}

// FetchChunkData gets chunks from memcache.
func (c *MemcacheClient) FetchChunkData(userID string, chunks []wire.Chunk) (found []wire.Chunk, missing []wire.Chunk, err error) {
	memcacheRequests.Add(float64(len(chunks)))

	keys := make([]string, 0, len(chunks))
	for _, chunk := range chunks {
		keys = append(keys, memcacheKey(userID, chunk.ID))
	}

	var items map[string]*memcache.Item
	err = TimeRequestHistogramStatus("Get", memcacheRequestDuration, memcacheStatusCode, func() error {
		var err error
		items, err = c.client.GetMulti(keys)
		return err
	})
	if err != nil {
		return nil, chunks, err
	}

	for _, chunk := range chunks {
		item, ok := items[memcacheKey(userID, chunk.ID)]
		if !ok {
			missing = append(missing, chunk)
		} else {
			chunk.Data = item.Value
			found = append(found, chunk)
		}
	}

	memcacheHits.Add(float64(len(found)))
	return found, missing, nil
}

// StoreChunkData serializes and stores a chunk in memcache.
func (c *MemcacheClient) StoreChunkData(userID string, chunk *wire.Chunk) error {
	return TimeRequestHistogramStatus("Put", memcacheRequestDuration, memcacheStatusCode, func() error {
		// TODO: Add compression - maybe encapsulated in marshaling/unmarshaling
		// methods of wire.Chunk.
		item := memcache.Item{
			Key:        memcacheKey(userID, chunk.ID),
			Value:      chunk.Data,
			Expiration: c.expiration,
		}
		return c.client.Set(&item)
	})
}
<|endoftext|>