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

package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
)

const (
	PortNum  = "8080"
	HtmlPage = `

{{$NoEnc := 0}}
{{$WpaPsk := 1}}
{{$WpaEap := 2}}
{{$connectedEssid := .ConnectedEssid}}

Please choose your Wifi

    	Essid
    	Identity
    	Password / Passphrase
    	<input type="submit" class="btn" onclick=sendRefresh(this) value="Refresh"></button>

	{{range $idx, $opt := .WifiOpts}}
		<form id="f{{$idx}}" method="post"></form>
		{{if eq $opt.AuthSuite $NoEnc}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>

    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else if eq $opt.AuthSuite $WpaPsk}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>

    			<input type="password" name="pass" form="f{{$idx}}">
    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else if eq $opt.AuthSuite $WpaEap}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>
    			<input type="text" name="identity" form="f{{$idx}}">
				<input type="password" name="pass" form="f{{$idx}}">
    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>
    			Not a supported protocol

		{{end}}
	{{else}}
		No essids found
	{{end}}

`
)

func userInputValidation(essid, pass, id string) ([]string, error) {
	switch {
	case essid != "" && pass != "" && id != "":
		return []string{essid, pass, id}, nil
	case essid != "" && pass != "" && id == "":
		return []string{essid, pass}, nil
	case essid != "" && pass == "" && id == "":
		return []string{essid}, nil
	default:
		return nil, fmt.Errorf("Invalid user input")
	}
}

func startServer() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		s := getState()

		if r.Method != http.MethodPost {
			err := displayWifi(w, s.nearbyWifis, s.curEssid)
			if err != nil {
				log.Fatalf("error: %v", err)
			}
			return
		}

		a, err := userInputValidation(r.FormValue("essid"), r.FormValue("pass"), r.FormValue("identity"))
		if err != nil {
			// TODO: Need proper error handling
			log.Printf("error: %v", err)
			return
		}

		UserInputChannel <- UserInputMessage{args: a}
		sMsg := <-StateChannel
		displayWifi(w, sMsg.nearbyWifis, sMsg.curEssid)
	})
	http.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) {
		scanWifi()
		w.Write(nil)
	})

	http.ListenAndServe(fmt.Sprintf(":%s", PortNum), nil)
}

func displayWifi(wr io.Writer, wifiOpts []WifiOption, connectedEssid string) error {
	wifiData := struct {
		WifiOpts       []WifiOption
		ConnectedEssid string
	}{wifiOpts, connectedEssid}

	tmpl := template.Must(template.New("name").Parse(HtmlPage))

	return tmpl.Execute(wr, wifiData)
}
<commit_msg>Fixed wifiServer according to comment<commit_after>// Copyright 2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
)

const (
	PortNum  = "8080"
	HtmlPage = `

{{$NoEnc := 0}}
{{$WpaPsk := 1}}
{{$WpaEap := 2}}
{{$connectedEssid := .ConnectedEssid}}

Please choose your Wifi

    	Essid
    	Identity
    	Password / Passphrase
    	<input type="submit" class="btn" onclick=sendRefresh(this) value="Refresh"></button>

	{{range $idx, $opt := .WifiOpts}}
		<form id="f{{$idx}}" method="post"></form>
		{{if eq $opt.AuthSuite $NoEnc}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>

    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else if eq $opt.AuthSuite $WpaPsk}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>

    			<input type="password" name="pass" form="f{{$idx}}">
    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else if eq $opt.AuthSuite $WpaEap}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>
    			<input type="text" name="identity" form="f{{$idx}}">
				<input type="password" name="pass" form="f{{$idx}}">
    			{{if and (eq $connectedEssid $opt.Essid) (ne $connectedEssid "")}}
    				Connected
				{{else}}
    				<input type="submit" class="btn" form="f{{$idx}}" onclick=replaceWithConnecting(this) value="Connect">
    			{{end}}

		{{else}}

    			<input type="text" name="essid" class="essid" form="f{{$idx}}" readonly value={{$opt.Essid}}>
    			Not a supported protocol

		{{end}}
	{{else}}
		No essids found
	{{end}}

`
)

func userInputValidation(essid, pass, id string) ([]string, error) {
	switch {
	case essid != "" && pass != "" && id != "":
		return []string{essid, pass, id}, nil
	case essid != "" && pass != "" && id == "":
		return []string{essid, pass}, nil
	case essid != "" && pass == "" && id == "":
		return []string{essid}, nil
	default:
		return nil, fmt.Errorf("Invalid user input")
	}
}

func startServer() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		s := getState()

		if r.Method != http.MethodPost {
			err := displayWifi(w, s.nearbyWifis, s.curEssid)
			if err != nil {
				log.Fatalf("error: %v", err)
			}
			return
		}

		a, err := userInputValidation(r.FormValue("essid"), r.FormValue("pass"), r.FormValue("identity"))
		if err != nil {
			// TODO: Need proper error handling
			log.Printf("error: %v", err)
			return
		}

		UserInputChannel <- UserInputMessage{args: a}
		sMsg := <-StateChannel
		displayWifi(w, sMsg.nearbyWifis, sMsg.curEssid)
	})
	http.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) {
		scanWifi()
		w.Write(nil)
	})

	http.ListenAndServe(fmt.Sprintf(":%s", PortNum), nil)
}

func displayWifi(wr io.Writer, wifiOpts []WifiOption, connectedEssid string) error {
	wifiData := struct {
		WifiOpts       []WifiOption
		ConnectedEssid string
	}{wifiOpts, connectedEssid}

	tmpl := template.Must(template.New("name").Parse(HtmlPage))

	return tmpl.Execute(wr, wifiData)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/codegangsta/negroni"
	"github.com/goincremental/negroni-sessions"
	"podipo.com/skellago/be"
)

// VERSION is the API version
var VERSION = "0.1.0"

var logger = log.New(os.Stdout, "[api] ", 0)

func main() {
	// Get the required environment variables
	port, err := strconv.ParseInt(os.Getenv("PORT"), 10, 64)
	if err != nil {
		logger.Panic("No PORT env variable")
		return
	}
	staticDir := os.Getenv("STATIC_DIR")
	if staticDir == "" {
		logger.Panic("No STATIC_DIR env variable")
		return
	}
	fsDir := os.Getenv("FILE_STORAGE_DIR")
	if fsDir == "" {
		logger.Panic("No FILE_STORAGE_DIR env variable")
		return
	}
	frontEndDir := os.Getenv("FRONT_END_DIR") // Optional

	logger.Print("PORT:\t\t", port)
	logger.Print("STATIC_DIR:\t", staticDir)
	logger.Print("FRONT_END_DIR:\t", frontEndDir)
	logger.Print("FILE_STORAGE_DIR:\t", fsDir)

	err = be.InitDB()
	if err != nil {
		logger.Panic("DB Registration Error: " + err.Error())
		return
	}

	server := negroni.New()
	store := sessions.NewCookieStore([]byte(os.Getenv("SESSION_SECRET")))
	server.Use(sessions.Sessions("api_session", store))

	if frontEndDir != "" {
		feStatic := negroni.NewStatic(http.Dir(frontEndDir))
		feStatic.Prefix = ""
		server.Use(feStatic)
	}

	static := negroni.NewStatic(http.Dir(staticDir))
	static.Prefix = "/api/static"
	server.Use(static)

	fs, err := be.NewLocalFileStorage(fsDir)
	if err != nil {
		logger.Panic("Could not open file storage directory: " + fsDir)
		return
	}

	api := be.NewAPI("/api/"+VERSION, VERSION, fs)
	server.UseHandler(api.Mux)
	server.Run(":" + strconv.FormatInt(port, 10))
}
<commit_msg>Clean up the environment variable handling.<commit_after>package main

import (
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/codegangsta/negroni"
	"github.com/goincremental/negroni-sessions"
	"podipo.com/skellago/be"
)

// VERSION is the API version
var VERSION = "0.1.0"

var logger = log.New(os.Stdout, "[api] ", 0)

func main() {
	// Get the required environment variables
	port, err := strconv.ParseInt(os.Getenv("PORT"), 10, 64)
	if err != nil {
		logger.Panic("No PORT env variable")
		return
	}
	staticDir := os.Getenv("STATIC_DIR")
	if staticDir == "" {
		logger.Panic("No STATIC_DIR env variable")
		return
	}
	fsDir := os.Getenv("FILE_STORAGE_DIR")
	if fsDir == "" {
		logger.Panic("No FILE_STORAGE_DIR env variable")
		return
	}
	sessionSecret := os.Getenv("SESSION_SECRET")
	if sessionSecret == "" {
		logger.Panic("No SESSION_SECRET env variable")
		return
	}
	frontEndDir := os.Getenv("FRONT_END_DIR") // Optional

	logger.Print("PORT:\t\t", port)
	logger.Print("STATIC_DIR:\t", staticDir)
	logger.Print("FRONT_END_DIR:\t", frontEndDir)
	logger.Print("FILE_STORAGE_DIR:\t", fsDir)

	err = be.InitDB()
	if err != nil {
		logger.Panic("DB Registration Error: " + err.Error())
		return
	}

	fs, err := be.NewLocalFileStorage(fsDir)
	if err != nil {
		logger.Panic("Could not open file storage directory: " + fsDir)
		return
	}

	server := negroni.New()
	store := sessions.NewCookieStore([]byte(sessionSecret))
	server.Use(sessions.Sessions("api_session", store))

	if frontEndDir != "" {
		feStatic := negroni.NewStatic(http.Dir(frontEndDir))
		feStatic.Prefix = ""
		server.Use(feStatic)
	}

	static := negroni.NewStatic(http.Dir(staticDir))
	static.Prefix = "/api/static"
	server.Use(static)

	api := be.NewAPI("/api/"+VERSION, VERSION, fs)
	server.UseHandler(api.Mux)
	server.Run(":" + strconv.FormatInt(port, 10))
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"github.com/koding/bongo"
	"time"
)

var (
	NOTIFIER_LIMIT = 3
)

type Notifiable interface {
	// users that will be notified are fetched while creating notification
	GetNotifiedUsers(notificationContentId int64) ([]int64, error)
	GetType() string
	GetTargetId() int64
	FetchActors() (*ActorContainer, error)
	SetTargetId(int64)
	SetListerId(int64)
}

type InteractionNotification struct {
	TargetId     int64
	TypeConstant string
	ListerId     int64
	NotifierId   int64
}

func (n *InteractionNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	i := NewInteraction()
	i.MessageId = n.TargetId

	// fetch message owner
	targetMessage := NewChannelMessage()
	if err := targetMessage.ById(n.TargetId); err != nil {
		return nil, err
	}

	notifiedUsers := make([]int64, 0)
	// notify just the owner
	if targetMessage.AccountId != n.NotifierId {
		notifiedUsers = append(notifiedUsers, targetMessage.AccountId)
	}

	return notifiedUsers, nil
}

func (n *InteractionNotification) GetType() string {
	return n.TypeConstant
}

func (n *InteractionNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *InteractionNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *InteractionNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	i := NewInteraction()
	p := bongo.NewPagination(NOTIFIER_LIMIT, 0)
	i.MessageId = n.TargetId

	actors, err := i.FetchInteractorIds(n.GetType(), p)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count, err = i.FetchInteractorCount()
	if err != nil {
		return nil, err
	}

	return ac, nil
}

func (n *InteractionNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewInteractionNotification(notificationType string) *InteractionNotification {
	return &InteractionNotification{TypeConstant: notificationType}
}

type ReplyNotification struct {
	TargetId   int64
	ListerId   int64
	NotifierId int64
}

func (n *ReplyNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	// fetch all repliers
	cm := NewChannelMessage()
	cm.Id = n.TargetId

	p := &bongo.Pagination{}
	replierIds, err := cm.FetchReplierIds(p, true, time.Time{})

	if err != nil {
		return nil, err
	}

	// regress notifier from notified users
	filteredRepliers := make([]int64, 0)
	for _, replierId := range replierIds {
		if replierId != n.NotifierId {
			filteredRepliers = append(filteredRepliers, replierId)
		}
	}

	return filteredRepliers, nil
}

func (n *ReplyNotification) GetType() string {
	return NotificationContent_TYPE_COMMENT
}

func (n *ReplyNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *ReplyNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *ReplyNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	mr := NewMessageReply()
	mr.MessageId = n.TargetId

	// we are gonna fetch actors after notified users first reply
	if err := mr.FetchFirstAccountReply(n.ListerId); err != nil {
		return nil, err
	}

	cm := NewChannelMessage()
	cm.Id = n.TargetId
	cm.AccountId = n.ListerId

	p := &bongo.Pagination{
		Limit: NOTIFIER_LIMIT,
	}

	// for preparing Actor Container we need latest actors and total replier count
	var count int
	actors, err := cm.FetchReplierIdsWithCount(p, &count, mr.CreatedAt)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count = count

	return ac, nil
}

func (n *ReplyNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewReplyNotification() *ReplyNotification {
	return &ReplyNotification{}
}

type FollowNotification struct {
	// followed account
	TargetId int64
	ListerId int64
	// follower account
	NotifierId int64
}

func (n *FollowNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	users := make([]int64, 0)
	return append(users, n.TargetId), nil
}

func (n *FollowNotification) GetType() string {
	return NotificationContent_TYPE_FOLLOW
}

func (n *FollowNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *FollowNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	ac := NewActorContainer()

	a := NewActivity()
	a.TargetId = n.TargetId
	a.TypeConstant = NotificationContent_TYPE_FOLLOW
	actorIds, err := a.FetchActorIds(NOTIFIER_LIMIT)
	if err != nil {
		return nil, err
	}

	ac.LatestActors = actorIds
	ac.Count = len(ac.LatestActors) // TODO count also must be retrieved

	return ac, nil
}

func (n *FollowNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *FollowNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewFollowNotification() *FollowNotification {
	return &FollowNotification{}
}

type GroupNotification struct {
	TargetId     int64
	ListerId     int64
	OwnerId      int64
	NotifierId   int64
	TypeConstant string
	Admins       []int64
}

// fetch group admins
func (n *GroupNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	if len(n.Admins) == 0 {
		return nil, errors.New("admins cannot be empty")
	}

	return n.Admins, nil
}

func (n *GroupNotification) GetType() string {
	return n.TypeConstant
}

func (n *GroupNotification) GetTargetId() int64 {
	return n.TargetId
}

// fetch notifiers
func (n *GroupNotification) FetchActors() (*ActorContainer, error) {
	a := NewActivity()
	a.TargetId = n.TargetId
	a.TypeConstant = n.TypeConstant
	actors, err := a.FetchActorIds(NOTIFIER_LIMIT)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count = len(actors)

	return ac, nil
}

func (n *GroupNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *GroupNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewGroupNotification(typeConstant string) *GroupNotification {
	return &GroupNotification{TypeConstant: typeConstant}
}
<commit_msg>Social: GetNotifiedUser method now supports subscribed/unsubscribed users<commit_after>package models

import (
	"errors"
	"github.com/koding/bongo"
	"time"
)

var (
	NOTIFIER_LIMIT = 3
)

type Notifiable interface {
	// users that will be notified are fetched while creating notification
	GetNotifiedUsers(notificationContentId int64) ([]int64, error)
	GetType() string
	GetTargetId() int64
	FetchActors() (*ActorContainer, error)
	SetTargetId(int64)
	SetListerId(int64)
}

type InteractionNotification struct {
	TargetId     int64
	TypeConstant string
	ListerId     int64
	NotifierId   int64
}

func (n *InteractionNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	i := NewInteraction()
	i.MessageId = n.TargetId

	// fetch message owner
	targetMessage := NewChannelMessage()
	if err := targetMessage.ById(n.TargetId); err != nil {
		return nil, err
	}

	notifiedUsers := make([]int64, 0)
	// notify just the owner
	if targetMessage.AccountId != n.NotifierId {
		notifiedUsers = append(notifiedUsers, targetMessage.AccountId)
	}

	return notifiedUsers, nil
}

func (n *InteractionNotification) GetType() string {
	return n.TypeConstant
}

func (n *InteractionNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *InteractionNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *InteractionNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	i := NewInteraction()
	p := bongo.NewPagination(NOTIFIER_LIMIT, 0)
	i.MessageId = n.TargetId

	actors, err := i.FetchInteractorIds(n.GetType(), p)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count, err = i.FetchInteractorCount()
	if err != nil {
		return nil, err
	}

	return ac, nil
}

func (n *InteractionNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewInteractionNotification(notificationType string) *InteractionNotification {
	return &InteractionNotification{TypeConstant: notificationType}
}

type ReplyNotification struct {
	TargetId   int64
	ListerId   int64
	NotifierId int64
}

func (n *ReplyNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	// fetch all repliers
	cm := NewChannelMessage()
	cm.Id = n.TargetId

	p := &bongo.Pagination{}
	replierIds, err := cm.FetchReplierIds(p, true, time.Time{})
	if err != nil {
		return nil, err
	}
	repliersMap := map[int64]struct{}{}
	for _, replierId := range replierIds {
		repliersMap[replierId] = struct{}{}
	}

	var subscribers []NotificationSubscription
	q := &bongo.Query{
		Selector: map[string]interface{}{
			"notification_content_id": notificationContentId,
		},
	}
	ns := NewNotificationSubscription()
	if err := ns.Some(&subscribers, q); err != nil {
		return nil, err
	}

	// regress unsubscribed users and append subscribed ones
	for _, subscriber := range subscribers {
		switch subscriber.TypeConstant {
		case NotificationSubscription_TYPE_SUBSCRIBE:
			repliersMap[subscriber.AccountId] = struct{}{}
		case NotificationSubscription_TYPE_UNSUBSCRIBE:
			delete(repliersMap, subscriber.AccountId)
		}
	}

	// regress notifier from notified users
	delete(repliersMap, n.NotifierId)

	filteredRepliers := make([]int64, 0)
	for replierId, _ := range repliersMap {
		filteredRepliers = append(filteredRepliers, replierId)
	}

	return filteredRepliers, nil
}

func (n *ReplyNotification) GetType() string {
	return NotificationContent_TYPE_COMMENT
}

func (n *ReplyNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *ReplyNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *ReplyNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	mr := NewMessageReply()
	mr.MessageId = n.TargetId

	// we are gonna fetch actors after notified users first reply
	if err := mr.FetchFirstAccountReply(n.ListerId); err != nil {
		return nil, err
	}

	cm := NewChannelMessage()
	cm.Id = n.TargetId
	cm.AccountId = n.ListerId

	p := &bongo.Pagination{
		Limit: NOTIFIER_LIMIT,
	}

	// for preparing Actor Container we need latest actors and total replier count
	var count int
	actors, err := cm.FetchReplierIdsWithCount(p, &count, mr.CreatedAt)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count = count

	return ac, nil
}

func (n *ReplyNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewReplyNotification() *ReplyNotification {
	return &ReplyNotification{}
}

type FollowNotification struct {
	// followed account
	TargetId int64
	ListerId int64
	// follower account
	NotifierId int64
}

func (n *FollowNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	users := make([]int64, 0)
	return append(users, n.TargetId), nil
}

func (n *FollowNotification) GetType() string {
	return NotificationContent_TYPE_FOLLOW
}

func (n *FollowNotification) GetTargetId() int64 {
	return n.TargetId
}

func (n *FollowNotification) FetchActors() (*ActorContainer, error) {
	if n.TargetId == 0 {
		return nil, errors.New("TargetId is not set")
	}

	ac := NewActorContainer()

	a := NewActivity()
	a.TargetId = n.TargetId
	a.TypeConstant = NotificationContent_TYPE_FOLLOW
	actorIds, err := a.FetchActorIds(NOTIFIER_LIMIT)
	if err != nil {
		return nil, err
	}

	ac.LatestActors = actorIds
	ac.Count = len(ac.LatestActors) // TODO count also must be retrieved

	return ac, nil
}

func (n *FollowNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *FollowNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewFollowNotification() *FollowNotification {
	return &FollowNotification{}
}

type GroupNotification struct {
	TargetId     int64
	ListerId     int64
	OwnerId      int64
	NotifierId   int64
	TypeConstant string
	Admins       []int64
}

// fetch group admins
func (n *GroupNotification) GetNotifiedUsers(notificationContentId int64) ([]int64, error) {
	if len(n.Admins) == 0 {
		return nil, errors.New("admins cannot be empty")
	}

	return n.Admins, nil
}

func (n *GroupNotification) GetType() string {
	return n.TypeConstant
}

func (n *GroupNotification) GetTargetId() int64 {
	return n.TargetId
}

// fetch notifiers
func (n *GroupNotification) FetchActors() (*ActorContainer, error) {
	a := NewActivity()
	a.TargetId = n.TargetId
	a.TypeConstant = n.TypeConstant
	actors, err := a.FetchActorIds(NOTIFIER_LIMIT)
	if err != nil {
		return nil, err
	}

	ac := NewActorContainer()
	ac.LatestActors = actors
	ac.Count = len(actors)

	return ac, nil
}

func (n *GroupNotification) SetTargetId(targetId int64) {
	n.TargetId = targetId
}

func (n *GroupNotification) SetListerId(listerId int64) {
	n.ListerId = listerId
}

func NewGroupNotification(typeConstant string) *GroupNotification {
	return &GroupNotification{TypeConstant: typeConstant}
}
<|endoftext|>


<commit_before>package queue

import (
	"fmt"
	"strings"
	"time"

	"../../config"

	"strconv"

	etcd "github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

var (
	ctx     = context.Background()
	etcdAPI etcd.KeysAPI
	// Config is a pointer need to be set to the main configuration
	Config *config.ConfigurationInfo
)

// Run checks for waiting units and assigns them
func Run() {
	setUpEtcd()
	importExisting()
	go watchQueue()
}

// AddUnit adds a unit to the queue
func AddUnit(name string) {
	if etcdAPI == nil {
		setUpEtcd()
	}
	etcdAPI.Set(ctx, fmt.Sprintf("/dispatch/queue/%s/%s", Config.Zone, name), name, &etcd.SetOptions{})
}

func importExisting() {
	response, err := etcdAPI.Get(ctx, fmt.Sprintf("/dispatch/queue/%s/", Config.Zone), &etcd.GetOptions{})
	if err != nil {
		return
	}
	for _, node := range response.Node.Nodes {
		go assignUnit(node.Value)
	}
}

func watchQueue() {
	w := etcdAPI.Watcher(fmt.Sprintf("/dispatch/queue/%s/", Config.Zone), &etcd.WatcherOptions{Recursive: true})
	for {
		r, err := w.Next(ctx)
		if err != nil {
			go watchQueue()
			return
		}
		if r.Action == "set" {
			go assignUnit(r.Node.Value)
		}
	}
}

func assignUnit(name string) {
	fmt.Println(name)
	machine := getMachineForConstraints(map[string]string{}) // TO DO implement constraints
	assignUnitToMachine(name, machine)
}

func getMachineForConstraints(contraints map[string]string) string {
	machinesForLoad := map[string]float64{}
	response, err := etcdAPI.Get(ctx, fmt.Sprintf("/dispatch/machines/%s/", Config.Zone), &etcd.GetOptions{Recursive: true})
	if err != nil {
		return "" //not important for now
	}
	for _, node := range response.Node.Nodes {
		keyParts := strings.Split(node.Key, "/")
		if keyParts[len(keyParts)-1] == "load" {
			if load, err := strconv.ParseFloat(keyParts[len(keyParts)-1], 64); err == nil {
				// TO DO ADD CONTRAINTS
				machinesForLoad[keyParts[len(keyParts)-2]] = load
			}
		}
	}
	isCompared := false
	lowestLoad := 0.0
	lowestLoadMachine := ""
	for machine, load := range machinesForLoad {
		if load < lowestLoad || !isCompared {
			lowestLoad = load
			lowestLoadMachine = machine
			isCompared = true
		}
	}
	return lowestLoadMachine
}

func assignUnitToMachine(unit, machine string) {
	etcdAPI.Set(ctx, fmt.Sprintf("/dispatch/machines/%s/%s/units/%s", Config.Zone, machine, unit), unit, &etcd.SetOptions{})
	etcdAPI.Delete(ctx, fmt.Sprintf("/dispatch/queue/%s/%s", Config.Zone, unit), &etcd.DeleteOptions{})
}

func setUpEtcd() {
	c, err := etcd.New(etcd.Config{
		Endpoints:               []string{Config.EtcdAddress},
		Transport:               etcd.DefaultTransport,
		HeaderTimeoutPerRequest: 10 * time.Second,
	})
	if err != nil {
		panic(err)
	}
	etcdAPI = etcd.NewKeysAPI(c)
}
<commit_msg>Fix incorrect machine reading<commit_after>package queue

import (
	"fmt"
	"strings"
	"time"

	"../../config"

	"strconv"

	etcd "github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

var (
	ctx     = context.Background()
	etcdAPI etcd.KeysAPI
	// Config is a pointer need to be set to the main configuration
	Config *config.ConfigurationInfo
)

// Run checks for waiting units and assigns them
func Run() {
	setUpEtcd()
	importExisting()
	go watchQueue()
}

// AddUnit adds a unit to the queue
func AddUnit(name string) {
	if etcdAPI == nil {
		setUpEtcd()
	}
	etcdAPI.Set(ctx, fmt.Sprintf("/dispatch/queue/%s/%s", Config.Zone, name), name, &etcd.SetOptions{})
}

func importExisting() {
	response, err := etcdAPI.Get(ctx, fmt.Sprintf("/dispatch/queue/%s/", Config.Zone), &etcd.GetOptions{})
	if err != nil {
		return
	}
	for _, node := range response.Node.Nodes {
		go assignUnit(node.Value)
	}
}

func watchQueue() {
	w := etcdAPI.Watcher(fmt.Sprintf("/dispatch/queue/%s/", Config.Zone), &etcd.WatcherOptions{Recursive: true})
	for {
		r, err := w.Next(ctx)
		if err != nil {
			go watchQueue()
			return
		}
		if r.Action == "set" {
			go assignUnit(r.Node.Value)
		}
	}
}

func assignUnit(name string) {
	fmt.Println(name)
	machine := getMachineForConstraints(map[string]string{}) // TO DO implement constraints
	assignUnitToMachine(name, machine)
}

func getMachineForConstraints(contraints map[string]string) string {
	machinesForLoad := map[string]float64{}
	response, err := etcdAPI.Get(ctx, fmt.Sprintf("/dispatch/machines/%s/", Config.Zone), &etcd.GetOptions{Recursive: true})
	if err != nil {
		fmt.Println(err)
		return "" //not important for now
	}
	for _, node := range response.Node.Nodes {
		for _, key := range node.Nodes {
			keyParts := strings.Split(key.Key, "/")
			fmt.Println(keyParts[len(keyParts)-1])
			if keyParts[len(keyParts)-1] == "load" {
				load, err := strconv.ParseFloat(key.Value, 64)
				fmt.Println(err)
				if err == nil {
					// TO DO ADD CONTRAINTS
					machinesForLoad[keyParts[len(keyParts)-2]] = load
				}
			}
		}
	}
	isCompared := false
	lowestLoad := 0.0
	lowestLoadMachine := ""
	for machine, load := range machinesForLoad {
		if load < lowestLoad || !isCompared {
			lowestLoad = load
			lowestLoadMachine = machine
			isCompared = true
		}
	}
	return lowestLoadMachine
}

func assignUnitToMachine(unit, machine string) {
	etcdAPI.Set(ctx, fmt.Sprintf("/dispatch/machines/%s/%s/units/%s", Config.Zone, machine, unit), unit, &etcd.SetOptions{})
	etcdAPI.Delete(ctx, fmt.Sprintf("/dispatch/queue/%s/%s", Config.Zone, unit), &etcd.DeleteOptions{})
}

func setUpEtcd() {
	c, err := etcd.New(etcd.Config{
		Endpoints:               []string{Config.EtcdAddress},
		Transport:               etcd.DefaultTransport,
		HeaderTimeoutPerRequest: 10 * time.Second,
	})
	if err != nil {
		panic(err)
	}
	etcdAPI = etcd.NewKeysAPI(c)
}
<|endoftext|>


<commit_before>package js

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/orktes/orlang/analyser"
	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/types"
)

type JSCodeGen struct {
	analyserInfo *analyser.Info
	buffer       bytes.Buffer
	currentFile  *ast.File
	identNumbers map[ast.Node]int
}

func New(info *analyser.Info) *JSCodeGen {
	return &JSCodeGen{analyserInfo: info, identNumbers: map[ast.Node]int{}}
}

func (jscg *JSCodeGen) getTempVar() string {
	// TODO check if temp var is defined in scope
	return "_temp"
}

func (jscg *JSCodeGen) getIdentifierForNode(node ast.Node, name string) string {
	identNumber := 0
	if number, ok := jscg.identNumbers[node]; ok {
		identNumber = number
	} else {
		identNumber = len(jscg.identNumbers)
		jscg.identNumbers[node] = identNumber
	}

	return fmt.Sprintf("$%d_%s", identNumber, name)
}

func (jscg *JSCodeGen) getIdentifier(ident *ast.Identifier) string {
	nodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[ident]
	scopeItemDetals := nodeInfo.Scope.GetDetails(ident.Text, true)
	return jscg.getIdentifierForNode(scopeItemDetals.DefineIdentifier, ident.Text)
}

func (jscg *JSCodeGen) Visit(node ast.Node) ast.Visitor {
	nodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[node]
	switch n := node.(type) {
	case *ast.Macro, *ast.CallArgument:
	case *ast.TupleDeclaration:

		var varName string

		if n.DefaultValue != nil {
			if ident, ok := n.DefaultValue.(*ast.Identifier); ok {
				varName = jscg.getIdentifier(ident)
			}
		}

		if varName == "" {
			varName = jscg.getTempVar()
			jscg.buffer.WriteString(fmt.Sprintf(
				`var %s`,
				varName,
			))

			if n.DefaultValue != nil {
				jscg.buffer.WriteString("=")
			}

			ast.Walk(jscg, n.DefaultValue)

			jscg.buffer.WriteString(";")
		}

		var ptrnPrint func(pattern *ast.TuplePattern, prefix string)
		ptrnPrint = func(pattern *ast.TuplePattern, prefix string) {
			for i, ptrrn := range pattern.Patterns {
				switch pt := ptrrn.(type) {
				case *ast.TuplePattern:
					ptrnPrint(pt, fmt.Sprintf("%s[%d]", prefix, i))
				case *ast.Identifier:
					jscg.buffer.WriteString(
						fmt.Sprintf(
							"var %s = %s[%d]",
							jscg.getIdentifier(pt),
							prefix,
							i,
						))
					if i != len(pattern.Patterns)-1 {
						jscg.buffer.WriteString(";")
					}
				}

			}
		}

		ptrnPrint(n.Pattern, varName)

		return nil
	case *ast.VariableDeclaration:
		jscg.buffer.WriteString(fmt.Sprintf(
			`var %s`,
			jscg.getIdentifier(n.Name),
		))
		if n.DefaultValue != nil {
			jscg.buffer.WriteString("=")
		}

		ast.Walk(jscg, n.DefaultValue)
		return nil
	case *ast.Assigment:
		ast.Walk(jscg, n.Left)
		jscg.buffer.WriteString(" = ")
		ast.Walk(jscg, n.Right)
		return nil
	case *ast.IfStatement:
		jscg.buffer.WriteString(" if (")
		ast.Walk(jscg, n.Condition)
		jscg.buffer.WriteString(")")

		ast.Walk(jscg, n.Block)

		if n.Else != nil {
			jscg.buffer.WriteString(" else ")
			ast.Walk(jscg, n.Else)
		}

		return nil
	case *ast.TupleExpression:
		jscg.buffer.WriteString(`[`)

		for i, expr := range n.Expressions {
			ast.Walk(jscg, expr)
			if i < len(n.Expressions)-1 {
				jscg.buffer.WriteString(`,`)
			}
		}

		jscg.buffer.WriteString(`]`)
		return nil
	case *ast.UnaryExpression:
		if n.Postfix {
			ast.Walk(jscg, n.Expression)

		}

		jscg.buffer.WriteString(n.Operator.Text)

		if n.Postfix {
			return nil
		}
	case *ast.ComparisonExpression:
		ast.Walk(jscg, n.Left)
		jscg.buffer.WriteString(n.Operator.Text)
		ast.Walk(jscg, n.Right)
		return nil
	case *ast.BinaryExpression:
		if nodeInfo.OverloadedOperation == nil {
			ast.Walk(jscg, n.Left)
			jscg.buffer.WriteString(n.Operator.Text)
			ast.Walk(jscg, n.Right)
		} else {
			// Operator has been overloaded
			name := jscg.getIdentifierForNode(nodeInfo.OverloadedOperation, n.Operator.Type.String())
			jscg.buffer.WriteString(fmt.Sprintf("%s(", name))
			ast.Walk(jscg, n.Left)
			jscg.buffer.WriteString(", ")
			ast.Walk(jscg, n.Right)
			jscg.buffer.WriteString(")")
		}
		return nil
	case *ast.Identifier:
		if n == nil {
			break
		}
		if sigType, ok := nodeInfo.Type.(*types.SignatureType); ok {
			if sigType.Extern {
				jscg.buffer.WriteString(n.Text)
				break
			}
		}

		jscg.buffer.WriteString(jscg.getIdentifier(n))
	case *ast.ValueExpression:
		jscg.buffer.WriteString(fmt.Sprintf(
			`%s`,
			n.Text,
		))
	case *ast.ReturnStatement:
		jscg.buffer.WriteString(`return `)
	case *ast.FunctionCall:
		if nodeInfo.TypeCast {
			if nodeInfo.Type == types.Int32Type || nodeInfo.Type == types.Int64Type {
				jscg.buffer.WriteString(`Math.floor`)
			} else {
				ast.Walk(jscg, n.Arguments[0])
				return nil
			}
		} else {
			ast.Walk(jscg, n.Callee)
		}
		jscg.buffer.WriteString(`(`)

		// TODO set named args into the right order
		var argNames []string

		calleeNodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[n.Callee]
		if calleeNodeInfo != nil && calleeNodeInfo.Type != nil {
			argNames = calleeNodeInfo.Type.(*types.SignatureType).ArgumentNames
		}

		namedArgs := len(n.Arguments) > 0 && n.Arguments[0].Name != nil
		if len(argNames) == 0 || !namedArgs {
			for i, expr := range n.Arguments {
				ast.Walk(jscg, expr)
				if i < len(n.Arguments)-1 {
					jscg.buffer.WriteString(`,`)
				}
			}
		} else {
			for i, argName := range argNames {
				found := false
				for _, expr := range n.Arguments {
					if expr.Name.Text == argName {
						ast.Walk(jscg, expr.Expression)
						found = true
						break
					}
				}

				if !found {
					jscg.buffer.WriteString("undefined")
				}

				if i < len(argNames)-1 {
					jscg.buffer.WriteString(`,`)
				}
			}
		}

		jscg.buffer.WriteString(`)`)

		return nil
	case *ast.Block:
		jscg.buffer.WriteString(`{`)

		for _, node := range n.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}

		jscg.buffer.WriteString(`}`)
		return nil
	case *ast.File:
		jscg.buffer.WriteString("(function () {")
		for _, node := range n.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}
		return nil
	case *ast.ForLoop:
		jscg.buffer.WriteString(" for (")

		if n.Init != nil {
			ast.Walk(jscg, n.Init)
		}
		jscg.buffer.WriteString(";")

		if n.Condition != nil {
			ast.Walk(jscg, n.Condition)
		}
		jscg.buffer.WriteString(";")

		if n.After != nil {
			ast.Walk(jscg, n.After)
		}
		jscg.buffer.WriteString(")")

		ast.Walk(jscg, n.Block)
		return nil
	case *ast.FunctionDeclaration:
		var name string
		var args []string
		if n.Signature.Identifier != nil {
			name = jscg.getIdentifier(n.Signature.Identifier)
		} else if n.Signature.Operator != nil {
			// Operator overload
			name = jscg.getIdentifierForNode(n, n.Signature.Operator.Type.String())
		}

		for _, arg := range n.Signature.Arguments {
			args = append(args, jscg.getIdentifier(arg.Name))
		}

		jscg.buffer.WriteString(fmt.Sprintf(
			`function %s (%s)`,
			name,
			strings.Join(args, ","),
		))

		jscg.buffer.WriteString("{")

		for _, arg := range n.Signature.Arguments {
			if arg.DefaultValue != nil {
				name := jscg.getIdentifier(arg.Name)
				jscg.buffer.WriteString(fmt.Sprintf(
					`if (%s === undefined) {%s =`,
					name,
					name,
				))
				ast.Walk(jscg, arg.DefaultValue)
				jscg.buffer.WriteString("}")
			}
		}

		for _, node := range n.Block.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}

		jscg.buffer.WriteString("}")
		return nil
	case *ast.ParenExpression:
		jscg.buffer.WriteString("(")
	default:
		panic(fmt.Sprintf("TODO: %T", n))
	}
	return jscg
}

func (jscg *JSCodeGen) Leave(node ast.Node) {
	switch n := node.(type) {
	case *ast.File:
		for _, n := range n.Body {
			if funDecl, ok := n.(*ast.FunctionDeclaration); ok {
				if funDecl.Signature != nil && funDecl.Signature.Identifier != nil && funDecl.Signature.Identifier.Text == "main" {
					name := jscg.getIdentifier(funDecl.Signature.Identifier)
					jscg.buffer.WriteString(fmt.Sprintf("%s();", name))
					break
				}
			}
		}

		jscg.buffer.WriteString("})();")
	case *ast.ParenExpression:
		jscg.buffer.WriteString(")")
	}
}

func (jscg *JSCodeGen) Generate(file *ast.File) []byte {
	jscg.currentFile = file
	ast.Walk(jscg, file)
	return jscg.buffer.Bytes()
}
<commit_msg>Create variable name based tuple pattern node pointer<commit_after>package js

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/orktes/orlang/analyser"
	"github.com/orktes/orlang/ast"
	"github.com/orktes/orlang/types"
)

type JSCodeGen struct {
	analyserInfo *analyser.Info
	buffer       bytes.Buffer
	currentFile  *ast.File
	identNumbers map[ast.Node]int
}

func New(info *analyser.Info) *JSCodeGen {
	return &JSCodeGen{analyserInfo: info, identNumbers: map[ast.Node]int{}}
}

func (jscg *JSCodeGen) getIdentifierForNode(node ast.Node, name string) string {
	identNumber := 0
	if number, ok := jscg.identNumbers[node]; ok {
		identNumber = number
	} else {
		identNumber = len(jscg.identNumbers)
		jscg.identNumbers[node] = identNumber
	}

	return fmt.Sprintf("$%d_%s", identNumber, name)
}

func (jscg *JSCodeGen) getIdentifier(ident *ast.Identifier) string {
	nodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[ident]
	scopeItemDetals := nodeInfo.Scope.GetDetails(ident.Text, true)
	return jscg.getIdentifierForNode(scopeItemDetals.DefineIdentifier, ident.Text)
}

func (jscg *JSCodeGen) Visit(node ast.Node) ast.Visitor {
	nodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[node]
	switch n := node.(type) {
	case *ast.Macro, *ast.CallArgument:
	case *ast.TupleDeclaration:

		var varName string

		if n.DefaultValue != nil {
			if ident, ok := n.DefaultValue.(*ast.Identifier); ok {
				varName = jscg.getIdentifier(ident)
			}
		}

		if varName == "" {
			varName = jscg.getIdentifierForNode(n, "tuple_temp")
			jscg.buffer.WriteString(fmt.Sprintf(
				`var %s`,
				varName,
			))

			if n.DefaultValue != nil {
				jscg.buffer.WriteString("=")
			}

			ast.Walk(jscg, n.DefaultValue)

			jscg.buffer.WriteString(";")
		}

		var ptrnPrint func(pattern *ast.TuplePattern, prefix string)
		ptrnPrint = func(pattern *ast.TuplePattern, prefix string) {
			for i, ptrrn := range pattern.Patterns {
				switch pt := ptrrn.(type) {
				case *ast.TuplePattern:
					ptrnPrint(pt, fmt.Sprintf("%s[%d]", prefix, i))
				case *ast.Identifier:
					jscg.buffer.WriteString(
						fmt.Sprintf(
							"var %s = %s[%d]",
							jscg.getIdentifier(pt),
							prefix,
							i,
						))
					if i != len(pattern.Patterns)-1 {
						jscg.buffer.WriteString(";")
					}
				}

			}
		}

		ptrnPrint(n.Pattern, varName)

		return nil
	case *ast.VariableDeclaration:
		jscg.buffer.WriteString(fmt.Sprintf(
			`var %s`,
			jscg.getIdentifier(n.Name),
		))
		if n.DefaultValue != nil {
			jscg.buffer.WriteString("=")
		}

		ast.Walk(jscg, n.DefaultValue)
		return nil
	case *ast.Assigment:
		ast.Walk(jscg, n.Left)
		jscg.buffer.WriteString(" = ")
		ast.Walk(jscg, n.Right)
		return nil
	case *ast.IfStatement:
		jscg.buffer.WriteString(" if (")
		ast.Walk(jscg, n.Condition)
		jscg.buffer.WriteString(")")

		ast.Walk(jscg, n.Block)

		if n.Else != nil {
			jscg.buffer.WriteString(" else ")
			ast.Walk(jscg, n.Else)
		}

		return nil
	case *ast.TupleExpression:
		jscg.buffer.WriteString(`[`)

		for i, expr := range n.Expressions {
			ast.Walk(jscg, expr)
			if i < len(n.Expressions)-1 {
				jscg.buffer.WriteString(`,`)
			}
		}

		jscg.buffer.WriteString(`]`)
		return nil
	case *ast.UnaryExpression:
		if n.Postfix {
			ast.Walk(jscg, n.Expression)

		}

		jscg.buffer.WriteString(n.Operator.Text)

		if n.Postfix {
			return nil
		}
	case *ast.ComparisonExpression:
		ast.Walk(jscg, n.Left)
		jscg.buffer.WriteString(n.Operator.Text)
		ast.Walk(jscg, n.Right)
		return nil
	case *ast.BinaryExpression:
		if nodeInfo.OverloadedOperation == nil {
			ast.Walk(jscg, n.Left)
			jscg.buffer.WriteString(n.Operator.Text)
			ast.Walk(jscg, n.Right)
		} else {
			// Operator has been overloaded
			name := jscg.getIdentifierForNode(nodeInfo.OverloadedOperation, n.Operator.Type.String())
			jscg.buffer.WriteString(fmt.Sprintf("%s(", name))
			ast.Walk(jscg, n.Left)
			jscg.buffer.WriteString(", ")
			ast.Walk(jscg, n.Right)
			jscg.buffer.WriteString(")")
		}
		return nil
	case *ast.Identifier:
		if n == nil {
			break
		}
		if sigType, ok := nodeInfo.Type.(*types.SignatureType); ok {
			if sigType.Extern {
				jscg.buffer.WriteString(n.Text)
				break
			}
		}

		jscg.buffer.WriteString(jscg.getIdentifier(n))
	case *ast.ValueExpression:
		jscg.buffer.WriteString(fmt.Sprintf(
			`%s`,
			n.Text,
		))
	case *ast.ReturnStatement:
		jscg.buffer.WriteString(`return `)
	case *ast.FunctionCall:
		if nodeInfo.TypeCast {
			if nodeInfo.Type == types.Int32Type || nodeInfo.Type == types.Int64Type {
				jscg.buffer.WriteString(`Math.floor`)
			} else {
				ast.Walk(jscg, n.Arguments[0])
				return nil
			}
		} else {
			ast.Walk(jscg, n.Callee)
		}
		jscg.buffer.WriteString(`(`)

		// TODO set named args into the right order
		var argNames []string

		calleeNodeInfo := jscg.analyserInfo.FileInfo[jscg.currentFile].NodeInfo[n.Callee]
		if calleeNodeInfo != nil && calleeNodeInfo.Type != nil {
			argNames = calleeNodeInfo.Type.(*types.SignatureType).ArgumentNames
		}

		namedArgs := len(n.Arguments) > 0 && n.Arguments[0].Name != nil
		if len(argNames) == 0 || !namedArgs {
			for i, expr := range n.Arguments {
				ast.Walk(jscg, expr)
				if i < len(n.Arguments)-1 {
					jscg.buffer.WriteString(`,`)
				}
			}
		} else {
			for i, argName := range argNames {
				found := false
				for _, expr := range n.Arguments {
					if expr.Name.Text == argName {
						ast.Walk(jscg, expr.Expression)
						found = true
						break
					}
				}

				if !found {
					jscg.buffer.WriteString("undefined")
				}

				if i < len(argNames)-1 {
					jscg.buffer.WriteString(`,`)
				}
			}
		}

		jscg.buffer.WriteString(`)`)

		return nil
	case *ast.Block:
		jscg.buffer.WriteString(`{`)

		for _, node := range n.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}

		jscg.buffer.WriteString(`}`)
		return nil
	case *ast.File:
		jscg.buffer.WriteString("(function () {")
		for _, node := range n.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}
		return nil
	case *ast.ForLoop:
		jscg.buffer.WriteString(" for (")

		if n.Init != nil {
			ast.Walk(jscg, n.Init)
		}
		jscg.buffer.WriteString(";")

		if n.Condition != nil {
			ast.Walk(jscg, n.Condition)
		}
		jscg.buffer.WriteString(";")

		if n.After != nil {
			ast.Walk(jscg, n.After)
		}
		jscg.buffer.WriteString(")")

		ast.Walk(jscg, n.Block)
		return nil
	case *ast.FunctionDeclaration:
		var name string
		var args []string
		if n.Signature.Identifier != nil {
			name = jscg.getIdentifier(n.Signature.Identifier)
		} else if n.Signature.Operator != nil {
			// Operator overload
			name = jscg.getIdentifierForNode(n, n.Signature.Operator.Type.String())
		}

		for _, arg := range n.Signature.Arguments {
			args = append(args, jscg.getIdentifier(arg.Name))
		}

		jscg.buffer.WriteString(fmt.Sprintf(
			`function %s (%s)`,
			name,
			strings.Join(args, ","),
		))

		jscg.buffer.WriteString("{")

		for _, arg := range n.Signature.Arguments {
			if arg.DefaultValue != nil {
				name := jscg.getIdentifier(arg.Name)
				jscg.buffer.WriteString(fmt.Sprintf(
					`if (%s === undefined) {%s =`,
					name,
					name,
				))
				ast.Walk(jscg, arg.DefaultValue)
				jscg.buffer.WriteString("}")
			}
		}

		for _, node := range n.Block.Body {
			ast.Walk(jscg, node)
			jscg.buffer.WriteString(";")
		}

		jscg.buffer.WriteString("}")
		return nil
	case *ast.ParenExpression:
		jscg.buffer.WriteString("(")
	default:
		panic(fmt.Sprintf("TODO: %T", n))
	}
	return jscg
}

func (jscg *JSCodeGen) Leave(node ast.Node) {
	switch n := node.(type) {
	case *ast.File:
		for _, n := range n.Body {
			if funDecl, ok := n.(*ast.FunctionDeclaration); ok {
				if funDecl.Signature != nil && funDecl.Signature.Identifier != nil && funDecl.Signature.Identifier.Text == "main" {
					name := jscg.getIdentifier(funDecl.Signature.Identifier)
					jscg.buffer.WriteString(fmt.Sprintf("%s();", name))
					break
				}
			}
		}

		jscg.buffer.WriteString("})();")
	case *ast.ParenExpression:
		jscg.buffer.WriteString(")")
	}
}

func (jscg *JSCodeGen) Generate(file *ast.File) []byte {
	jscg.currentFile = file
	ast.Walk(jscg, file)
	return jscg.buffer.Bytes()
}
<|endoftext|>


<commit_before>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package saltpack

import (
	"bufio"
	"io"
	"regexp"
	"strings"

	"github.com/keybase/saltpack/encoding/basex"

	"github.com/keybase/go-codec/codec"
)

const (
	// To decide if a byte slice might contain a valild header of a saltpack message, and identify its type, we need to consider at most:
	// - 1 byte for bin tag at the beginning
	// - at most 4 bytes for the bin length (i.e. the length of the binary encoded header)
	// - at most 5 bytes to determine how many parts the header consists of (we expect 6 parts, but newer versions of saltpack might add more, which we would ignore as per the spec)
	// - 9 bytes for the format name
	// - 3 bytes for the version number
	// - 1 byte for the message type
	// In the common case less might be enough, but this makes the code simpler, and 23 bytes is not that much anyways.
	minLengthToIdentifyBinarySaltpack int = 23
)

// IsSaltpackBinary peeks into the provided bufio.Reader to determine whether it encodes a binary saltpack message.
// It does not consume any of the reader's bytes (whose buffer length must be at least minLengthToIdentifyBinarySaltpack). It returns a non nil error if the buffer
// size of the reader is not large enough (ErrShortSliceOrBuffer), or if the stream does not appear to contain a binary
// saltpack message. If err is nil, msgType will return the type of the encoded message, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackBinary(stream *bufio.Reader) (msgType MessageType, version Version, err error) {

	if stream.Size() < minLengthToIdentifyBinarySaltpack {
		return MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}

	b, err := stream.Peek(minLengthToIdentifyBinarySaltpack)
	if err != nil {
		return MessageTypeUnknown, Version{}, err
	}
	return IsSaltpackBinarySlice(b)
}

// IsSaltpackBinarySlice tries to determine if the input slice encodes the beginning of a binary saltpack message. It returns a non nil error if the slice is not
// long enough to make this determination, or if it does not appear to contain a binary saltpack message. If err is nil, msgType
// will return the type of the encoded message, but this does *NOT* guarantee that the rest of the message is well formed.
func IsSaltpackBinarySlice(b []byte) (msgType MessageType, version Version, err error) {

	// To avoid decoding the whole header, part of the messagepack decoding is done manually
	// instead of through go-codec. See https://github.com/msgpack/msgpack/blob/master/spec.md
	// for details on the encoding.

	if len(b) < minLengthToIdentifyBinarySaltpack {
		return MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}

	// The header is double-encoded, so we need to skip the "bin" tag at the front to
	// get at the encoded header array.
	var binTagBytesToSkip int
	if b[0] == 0xc4 {
		binTagBytesToSkip = 2
	} else if b[0] == 0xc5 {
		binTagBytesToSkip = 3
	} else if b[0] == 0xc6 {
		binTagBytesToSkip = 5
	} else {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	// The header should be a msg pack encoded array: verify it is, that it has at least three elements, and
	// note how many bytes to skip to point to its first element.
	arrayTagByte := b[binTagBytesToSkip]
	var arrayTagBytesToSkip int
	if 0x93 <= arrayTagByte && arrayTagByte <= 0x9f {
		arrayTagBytesToSkip = 1
	} else if arrayTagByte == 0xdc {
		arrayTagBytesToSkip = 3
	} else if arrayTagByte == 0xdd {
		arrayTagBytesToSkip = 5
	} else {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	// Devode the first 3 elements of the header, and use them to classify the message.
	var mh codec.MsgpackHandle
	decoder := codec.NewDecoderBytes(b[binTagBytesToSkip+arrayTagBytesToSkip:], &mh)
	var formatName string

	if err := decoder.Decode(&formatName); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if formatName != FormatName {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if err := decoder.Decode(&version); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if err := decoder.Decode(&msgType); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	switch msgType {
	case MessageTypeEncryption, MessageTypeSigncryption, MessageTypeAttachedSignature, MessageTypeDetachedSignature:
		return msgType, version, nil
	default:
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
}

// IsSaltpackArmored peeks into the provided bufio.Reader to determine whether it encodes an ASCII-armored saltpack message.
// It does not consume any of the reader's bytes. The buffer size of the reader must be sufficient to contain the header frame plus
// the first Base62 encoded block of the payload. It returns a non nil error if the buffer
// size of the reader is not large enough to make this determination (ErrShortSliceOrBuffer), or if the stream does not appear to contain a valid
// saltpack message. If err is nil, then the brand, version and expected type of the message will be returned, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackArmored(stream *bufio.Reader) (brand string, msgType MessageType, ver Version, err error) {
	buf, err := stream.Peek(stream.Size())
	if (err != nil && err != io.EOF) || len(buf) == 0 {
		return "", MessageTypeUnknown, ver, err
	}

	return IsSaltpackArmoredPrefix(string(buf))
}

// IsSaltpackArmored tries to determine whether the string is the prefix of a valid ASCII-armored saltpack message.
// The prefix must be large enough to contain the header frame plus the first Base62 encoded block of the payload. It returns a non nil error if the buffer
// size of the reader is not large enough to make this determination (ErrShortSliceOrBuffer), or if the stream does not appear to contain a valid
// saltpack message. If err is nil, then the brand, version and expected type of the message will be returned, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackArmoredPrefix(pref string) (brand string, messageType MessageType, ver Version, err error) {
	// replace blocks of characters in the set [>\n\r\t ] with a single space, so that the next regexp is simpler
	re := regexp.MustCompile("[>\n\r\t ]+")
	s := strings.TrimSpace(re.ReplaceAllString(pref, " "))

	headerRegExpSt := "^BEGIN (?:([a-zA-Z0-9]+) )?SALTPACK (" + EncryptionArmorString + "|" + SignedArmorString + "|" + DetachedSignatureArmorString + ") ?\\.([a-zA-Z0-9 ]*)"
	headerRegExp := regexp.MustCompile(headerRegExpSt)

	m := headerRegExp.FindStringSubmatch(s)
	if m == nil || len(m) == 0 {
		// Matches at most five words
		if !regexp.MustCompile("^([a-zA-Z0-9]+ ?){0,5}$").MatchString(s) {
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		}

		strs := strings.Split(s, " ")

		switch len(strs) {
		case 1:
			if strings.HasPrefix(string(headerMarker), strs[0]) {
				return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
			}
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		case 2:
			if string(headerMarker) == strs[0] {
				return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
			}
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		case 3, 4, 5:
			// more processing needed.
		default:
			panic("logic error in ClassifyStream")
		}

		headerWithoutBrand := strings.Join(append([]string{strs[0]}, strs[2:]...), " ")

		if strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+EncryptionArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+SignedArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+DetachedSignatureArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+EncryptionArmorString, s) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+SignedArmorString, s) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+DetachedSignatureArmorString, s) {
			return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
		}
		return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	brand = m[1]
	headerArmorType := m[2] // can be one of SignedArmorString, DetachedSignatureArmorString, EncryptionArmorString

	dec, err := basex.Base62StdEncoding.DecodeString(m[3])
	// This is not a prefix free encoding, so we need to decode a full codeword (32 bytes) to make sure we are not interpreting
	// a truncated block as if it was a short block. Moreover, we only need one codeword, so if an error is returned but a codeword
	// was decoded, the error can be ignored.
	if len(dec) < 32 {
		if err == basex.ErrInvalidEncodingLength || err == nil {
			return "", MessageTypeUnknown, ver, ErrShortSliceOrBuffer
		}
		return "", MessageTypeUnknown, ver, ErrNotASaltpackMessage
	}

	messageType, ver, err = IsSaltpackBinarySlice(dec)
	if err != nil {
		return "", MessageTypeUnknown, ver, err
	}

	// ensure that the type of the armor matches the type of the inner header
	if (messageType == MessageTypeSigncryption && headerArmorType != EncryptionArmorString) ||
		(messageType == MessageTypeEncryption && headerArmorType != EncryptionArmorString) ||
		(messageType == MessageTypeAttachedSignature && headerArmorType != SignedArmorString) ||
		(messageType == MessageTypeDetachedSignature && headerArmorType != DetachedSignatureArmorString) {
		return "", MessageTypeUnknown, ver, ErrNotASaltpackMessage
	}

	return m[1], messageType, ver, nil
}

// ClassifyStream peeks at the beginning of a stream and checks wether it seems to contain a valid
// saltpack message (either armored or not).
// The buffer size must be at least minLengthToIdentifyBinarySaltpack bytes for binary messages, and large
// enough that if there is a header frame (i.e. "BEGIN FOO."), plus the first
// base62 encoded block (43 characters) of the steam (the default buffer size of bufio.Reader
// will be enough in most cases). Otherwise, ErrShortSliceOrBuffer will be returned.
// If err is nil, then the expected message type and version will be returned, as well as a booleand
// indicating if the message is ASCII-armored and the brand (for armored messages only).
// Note this classification is just a guess based on the beginning of the stream, and it does not
// guarantee that the message is valid or well formed.
func ClassifyStream(stream *bufio.Reader) (isArmored bool, brand string, messageType MessageType, ver Version, err error) {
	brand, messageType, ver, err = IsSaltpackArmored(stream)
	if err == nil {
		return true, brand, messageType, ver, err
	} else if err == ErrShortSliceOrBuffer {
		return false, "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}
	messageType, ver, err = IsSaltpackBinary(stream)
	if err == nil {
		return false, "", messageType, ver, err
	}
	return false, "", MessageTypeUnknown, Version{}, err
}

// ClassifyEncryptedStreamAndMakeDecoder takes as input an io.Reader (containing an encrypted saltpack stream),
// a SigncryptKeyring containing the keys to use for decryption, and a SymmetricKeyResolver (used to map an
// identifiers to symmetric keys in an application specific way). It classifies the encrypted stream
// (encryption vs signcryption mode and binary vs armored format) and returns a reader for the decoded stream,
// as well as some informtation about the stream. The brand is only returned for armored ciphertexts, mki only
// for encryption-mode ciphertext, senderPublic only for signcryption-mode ciphertexts.
func ClassifyEncryptedStreamAndMakeDecoder(source io.Reader, decryptionKeyring SigncryptKeyring, keyResolver SymmetricKeyResolver) (
	plainsource io.Reader, msgType MessageType, mki *MessageKeyInfo, senderPublic SigningPublicKey, isArmored bool, brand string, ver Version, err error) {
	stream := bufio.NewReader(source)

	isArmored, _, msgType, ver, err = ClassifyStream(stream)
	if err == ErrShortSliceOrBuffer {
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrShortSliceOrBuffer
	}
	if err != nil {
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrNotASaltpackMessage
	}

	switch msgType {
	case MessageTypeEncryption:
		if isArmored {
			mki, plainsource, brand, err = NewDearmor62DecryptStream(CheckKnownMajorVersion, stream, decryptionKeyring)
		} else {
			mki, plainsource, err = NewDecryptStream(CheckKnownMajorVersion, stream, decryptionKeyring)
		}
		return plainsource, msgType, mki, nil, isArmored, brand, ver, err
	case MessageTypeSigncryption:
		if isArmored {
			senderPublic, plainsource, brand, err = NewDearmor62SigncryptOpenStream(stream, decryptionKeyring, keyResolver)
		} else {
			senderPublic, plainsource, err = NewSigncryptOpenStream(stream, decryptionKeyring, keyResolver)
		}
		return plainsource, msgType, nil, senderPublic, isArmored, brand, ver, err
	default:
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrWrongMessageType{}
	}
}
<commit_msg>minor<commit_after>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package saltpack

import (
	"bufio"
	"io"
	"regexp"
	"strings"

	"github.com/keybase/saltpack/encoding/basex"

	"github.com/keybase/go-codec/codec"
)

const (
	// To decide if a byte slice might contain a valild header of a saltpack message, and identify its type, we need to consider at most:
	// - 1 byte for bin tag at the beginning
	// - at most 4 bytes for the bin length (i.e. the length of the binary encoded header)
	// - at most 5 bytes to determine how many parts the header consists of (we expect 6 parts, but newer versions of saltpack might add more, which we would ignore as per the spec)
	// - 9 bytes for the format name
	// - 3 bytes for the version number
	// - 1 byte for the message type
	// In the common case less might be enough, but this makes the code simpler, and 23 bytes is not that much anyways.
	minLengthToIdentifyBinarySaltpack int = 23
)

// IsSaltpackBinary peeks into the provided bufio.Reader to determine whether it encodes a binary saltpack message.
// It does not consume any of the reader's bytes (whose buffer length must be at least minLengthToIdentifyBinarySaltpack). It returns a non nil error if the buffer
// size of the reader is not large enough (ErrShortSliceOrBuffer), or if the stream does not appear to contain a binary
// saltpack message. If err is nil, msgType will return the type of the encoded message, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackBinary(stream *bufio.Reader) (msgType MessageType, version Version, err error) {

	b, err := stream.Peek(minLengthToIdentifyBinarySaltpack)
	if err == bufio.ErrBufferFull {
		return MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}
	if err != nil {
		return MessageTypeUnknown, Version{}, err
	}
	return IsSaltpackBinarySlice(b)
}

// IsSaltpackBinarySlice tries to determine if the input slice encodes the beginning of a binary saltpack message. It returns a non nil error if the slice is not
// long enough to make this determination, or if it does not appear to contain a binary saltpack message. If err is nil, msgType
// will return the type of the encoded message, but this does *NOT* guarantee that the rest of the message is well formed.
func IsSaltpackBinarySlice(b []byte) (msgType MessageType, version Version, err error) {

	// To avoid decoding the whole header, part of the messagepack decoding is done manually
	// instead of through go-codec. See https://github.com/msgpack/msgpack/blob/master/spec.md
	// for details on the encoding.

	if len(b) < minLengthToIdentifyBinarySaltpack {
		return MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}

	// The header is double-encoded, so we need to skip the "bin" tag at the front to
	// get at the encoded header array.
	var binTagBytesToSkip int
	if b[0] == 0xc4 {
		binTagBytesToSkip = 2
	} else if b[0] == 0xc5 {
		binTagBytesToSkip = 3
	} else if b[0] == 0xc6 {
		binTagBytesToSkip = 5
	} else {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	// The header should be a msg pack encoded array: verify it is, that it has at least three elements, and
	// note how many bytes to skip to point to its first element.
	arrayTagByte := b[binTagBytesToSkip]
	var arrayTagBytesToSkip int
	if 0x93 <= arrayTagByte && arrayTagByte <= 0x9f {
		arrayTagBytesToSkip = 1
	} else if arrayTagByte == 0xdc {
		arrayTagBytesToSkip = 3
	} else if arrayTagByte == 0xdd {
		arrayTagBytesToSkip = 5
	} else {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	// Devode the first 3 elements of the header, and use them to classify the message.
	var mh codec.MsgpackHandle
	decoder := codec.NewDecoderBytes(b[binTagBytesToSkip+arrayTagBytesToSkip:], &mh)
	var formatName string

	if err := decoder.Decode(&formatName); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if formatName != FormatName {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if err := decoder.Decode(&version); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	if err := decoder.Decode(&msgType); err != nil {
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
	switch msgType {
	case MessageTypeEncryption, MessageTypeSigncryption, MessageTypeAttachedSignature, MessageTypeDetachedSignature:
		return msgType, version, nil
	default:
		return MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}
}

// IsSaltpackArmored peeks into the provided bufio.Reader to determine whether it encodes an ASCII-armored saltpack message.
// It does not consume any of the reader's bytes. The buffer size of the reader must be sufficient to contain the header frame plus
// the first Base62 encoded block of the payload. It returns a non nil error if the buffer
// size of the reader is not large enough to make this determination (ErrShortSliceOrBuffer), or if the stream does not appear to contain a valid
// saltpack message. If err is nil, then the brand, version and expected type of the message will be returned, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackArmored(stream *bufio.Reader) (brand string, msgType MessageType, ver Version, err error) {
	buf, err := stream.Peek(stream.Size())
	if (err != nil && err != io.EOF) || len(buf) == 0 {
		return "", MessageTypeUnknown, ver, err
	}

	return IsSaltpackArmoredPrefix(string(buf))
}

// IsSaltpackArmored tries to determine whether the string is the prefix of a valid ASCII-armored saltpack message.
// The prefix must be large enough to contain the header frame plus the first Base62 encoded block of the payload. It returns a non nil error if the buffer
// size of the reader is not large enough to make this determination (ErrShortSliceOrBuffer), or if the stream does not appear to contain a valid
// saltpack message. If err is nil, then the brand, version and expected type of the message will be returned, but this does *NOT* guarantee that the
// rest of the message is well formed.
func IsSaltpackArmoredPrefix(pref string) (brand string, messageType MessageType, ver Version, err error) {
	// replace blocks of characters in the set [>\n\r\t ] with a single space, so that the next regexp is simpler
	re := regexp.MustCompile("[>\n\r\t ]+")
	s := strings.TrimSpace(re.ReplaceAllString(pref, " "))

	headerRegExpSt := "^BEGIN (?:([a-zA-Z0-9]+) )?SALTPACK (" + EncryptionArmorString + "|" + SignedArmorString + "|" + DetachedSignatureArmorString + ") ?\\.([a-zA-Z0-9 ]*)"
	headerRegExp := regexp.MustCompile(headerRegExpSt)

	m := headerRegExp.FindStringSubmatch(s)
	if m == nil || len(m) == 0 {
		// Matches at most five words
		if !regexp.MustCompile("^([a-zA-Z0-9]+ ?){0,5}$").MatchString(s) {
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		}

		strs := strings.Split(s, " ")

		switch len(strs) {
		case 1:
			if strings.HasPrefix(string(headerMarker), strs[0]) {
				return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
			}
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		case 2:
			if string(headerMarker) == strs[0] {
				return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
			}
			return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
		case 3, 4, 5:
			// more processing needed.
		default:
			panic("logic error in ClassifyStream")
		}

		headerWithoutBrand := strings.Join(append([]string{strs[0]}, strs[2:]...), " ")

		if strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+EncryptionArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+SignedArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+DetachedSignatureArmorString, headerWithoutBrand) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+EncryptionArmorString, s) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+SignedArmorString, s) ||
			strings.HasPrefix(string(headerMarker)+" "+strings.ToUpper(FormatName)+" "+DetachedSignatureArmorString, s) {
			return "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
		}
		return "", MessageTypeUnknown, Version{}, ErrNotASaltpackMessage
	}

	brand = m[1]
	headerArmorType := m[2] // can be one of SignedArmorString, DetachedSignatureArmorString, EncryptionArmorString

	dec, err := basex.Base62StdEncoding.DecodeString(m[3])
	// This is not a prefix free encoding, so we need to decode a full codeword (32 bytes) to make sure we are not interpreting
	// a truncated block as if it was a short block. Moreover, we only need one codeword, so if an error is returned but a codeword
	// was decoded, the error can be ignored.
	if len(dec) < 32 {
		if err == basex.ErrInvalidEncodingLength || err == nil {
			return "", MessageTypeUnknown, ver, ErrShortSliceOrBuffer
		}
		return "", MessageTypeUnknown, ver, ErrNotASaltpackMessage
	}

	messageType, ver, err = IsSaltpackBinarySlice(dec)
	if err != nil {
		return "", MessageTypeUnknown, ver, err
	}

	// ensure that the type of the armor matches the type of the inner header
	if (messageType == MessageTypeSigncryption && headerArmorType != EncryptionArmorString) ||
		(messageType == MessageTypeEncryption && headerArmorType != EncryptionArmorString) ||
		(messageType == MessageTypeAttachedSignature && headerArmorType != SignedArmorString) ||
		(messageType == MessageTypeDetachedSignature && headerArmorType != DetachedSignatureArmorString) {
		return "", MessageTypeUnknown, ver, ErrNotASaltpackMessage
	}

	return m[1], messageType, ver, nil
}

// ClassifyStream peeks at the beginning of a stream and checks wether it seems to contain a valid
// saltpack message (either armored or not).
// The buffer size must be at least minLengthToIdentifyBinarySaltpack bytes for binary messages, and large
// enough that if there is a header frame (i.e. "BEGIN FOO."), plus the first
// base62 encoded block (43 characters) of the steam (the default buffer size of bufio.Reader
// will be enough in most cases). Otherwise, ErrShortSliceOrBuffer will be returned.
// If err is nil, then the expected message type and version will be returned, as well as a booleand
// indicating if the message is ASCII-armored and the brand (for armored messages only).
// Note this classification is just a guess based on the beginning of the stream, and it does not
// guarantee that the message is valid or well formed.
func ClassifyStream(stream *bufio.Reader) (isArmored bool, brand string, messageType MessageType, ver Version, err error) {
	brand, messageType, ver, err = IsSaltpackArmored(stream)
	if err == nil {
		return true, brand, messageType, ver, err
	} else if err == ErrShortSliceOrBuffer {
		return false, "", MessageTypeUnknown, Version{}, ErrShortSliceOrBuffer
	}
	messageType, ver, err = IsSaltpackBinary(stream)
	if err == nil {
		return false, "", messageType, ver, err
	}
	return false, "", MessageTypeUnknown, Version{}, err
}

// ClassifyEncryptedStreamAndMakeDecoder takes as input an io.Reader (containing an encrypted saltpack stream),
// a SigncryptKeyring containing the keys to use for decryption, and a SymmetricKeyResolver (used to map an
// identifiers to symmetric keys in an application specific way). It classifies the encrypted stream
// (encryption vs signcryption mode and binary vs armored format) and returns a reader for the decoded stream,
// as well as some informtation about the stream. The brand is only returned for armored ciphertexts, mki only
// for encryption-mode ciphertext, senderPublic only for signcryption-mode ciphertexts.
func ClassifyEncryptedStreamAndMakeDecoder(source io.Reader, decryptionKeyring SigncryptKeyring, keyResolver SymmetricKeyResolver) (
	plainsource io.Reader, msgType MessageType, mki *MessageKeyInfo, senderPublic SigningPublicKey, isArmored bool, brand string, ver Version, err error) {
	stream := bufio.NewReader(source)

	isArmored, _, msgType, ver, err = ClassifyStream(stream)
	if err == ErrShortSliceOrBuffer {
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrShortSliceOrBuffer
	}
	if err != nil {
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrNotASaltpackMessage
	}

	switch msgType {
	case MessageTypeEncryption:
		if isArmored {
			mki, plainsource, brand, err = NewDearmor62DecryptStream(CheckKnownMajorVersion, stream, decryptionKeyring)
		} else {
			mki, plainsource, err = NewDecryptStream(CheckKnownMajorVersion, stream, decryptionKeyring)
		}
		return plainsource, msgType, mki, nil, isArmored, brand, ver, err
	case MessageTypeSigncryption:
		if isArmored {
			senderPublic, plainsource, brand, err = NewDearmor62SigncryptOpenStream(stream, decryptionKeyring, keyResolver)
		} else {
			senderPublic, plainsource, err = NewSigncryptOpenStream(stream, decryptionKeyring, keyResolver)
		}
		return plainsource, msgType, nil, senderPublic, isArmored, brand, ver, err
	default:
		return nil, MessageTypeUnknown, nil, nil, false, "", Version{}, ErrWrongMessageType{}
	}
}
<|endoftext|>


<commit_before>package collection

import (
  "github.com/kuzzleio/sdk-go/internal"
  "errors"
  "encoding/json"
  "github.com/kuzzleio/sdk-go/types"
)

/*
  Returns the number of documents matching the provided set of filters.

  There is a small delay between documents creation and their existence in our advanced search layer,
  usually a couple of seconds.
  That means that a document that was just been created won’t be returned by this function
*/
func (dc *Collection) Count(filters interface{}, options *types.Options) (*int, error) {
  type countResult struct {
    Count int `json:"count"`
  }

  ch := make(chan types.KuzzleResponse)

  go dc.kuzzle.Query(internal.BuildQuery(dc.collection, dc.index, "document", "count", filters), options, ch)

  res := <-ch

  if res.Error.Message != "" {
    return nil, errors.New(res.Error.Message)
  }
  result := &countResult{}
  json.Unmarshal(res.Result, result)

  return &result.Count, nil
}<commit_msg>Update count.go<commit_after>package collection

import (
  "github.com/kuzzleio/sdk-go/internal"
  "errors"
  "encoding/json"
  "github.com/kuzzleio/sdk-go/types"
)

/*
  Returns the number of documents matching the provided set of filters.

  There is a small delay between documents creation and their existence in our advanced search layer,
  usually a couple of seconds.
  That means that a document that was just been created won’t be returned by this function
*/
func (dc Collection) Count(filters interface{}, options *types.Options) (*int, error) {
  type countResult struct {
    Count int `json:"count"`
  }

  ch := make(chan types.KuzzleResponse)

  go dc.kuzzle.Query(internal.BuildQuery(dc.collection, dc.index, "document", "count", filters), options, ch)

  res := <-ch

  if res.Error.Message != "" {
    return nil, errors.New(res.Error.Message)
  }
  result := &countResult{}
  json.Unmarshal(res.Result, result)

  return &result.Count, nil
}
<|endoftext|>


<commit_before>// kstat zone collector
// this will :
//  - call kstat inside a zone
//  - gather zone metrics
//  - feed the collector

package collector

import (
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	// Prometheus Go toolset
	"github.com/prometheus/client_golang/prometheus"
)

// ZoneKstatCollector declares the data type within the prometheus metrics package.
type ZoneKstatCollector struct {
	ZoneKstatCPUBaseline *prometheus.GaugeVec
	ZoneKstatCPUCap      *prometheus.GaugeVec
	ZoneKstatCPUMaxUsage *prometheus.GaugeVec
	ZoneKstatCPUUsage    *prometheus.GaugeVec
	ZoneKstatMemCap      *prometheus.GaugeVec
	ZoneKstatMemFree     *prometheus.GaugeVec
	ZoneKstatMemNover    *prometheus.GaugeVec
	ZoneKstatMemPagedOut *prometheus.GaugeVec
	ZoneKstatMemRSS      *prometheus.GaugeVec
	ZoneKstatSwapCap     *prometheus.GaugeVec
	ZoneKstatSwapFree    *prometheus.GaugeVec
	ZoneKstatSwapUsed    *prometheus.GaugeVec
}

// NewZoneKstatExporter returns a newly allocated exporter ZoneKstatCollector.
// It exposes the kstat command result.
func NewZoneKstatExporter() (*ZoneKstatCollector, error) {
	return &ZoneKstatCollector{
		ZoneKstatCPUBaseline: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_baseline",
			Help: "A soft limit on the number of CPU cycles a hosted application can consume.",
		}, []string{"zonename"}),
		ZoneKstatCPUCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_cap",
			Help: "The maximum number of CPU cycles that are allocated to a zone.",
		}, []string{"zonename"}),
		ZoneKstatCPUMaxUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_maxusage",
			Help: "The maximum percentage of CPU used.",
		}, []string{"zonename"}),
		ZoneKstatCPUUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_usage",
			Help: "The current percentage of CPU used.",
		}, []string{"zonename"}),
		ZoneKstatMemCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_cap_bytes",
			Help: "The physical memory limit in bytes.",
		}, []string{"zonename"}),
		ZoneKstatMemFree: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_free_bytes",
			Help: "Free memory available in bytes.",
		}, []string{"zonename"}),
		ZoneKstatMemNover: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_nover_total",
			Help: "The number of times the zone has gone over its cap.",
		}, []string{"zonename"}),
		ZoneKstatMemPagedOut: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_pagedout_bytes",
			Help: "Total amount of memory that has been paged out when the zone has gone over its cap.",
		}, []string{"zonename"}),
		ZoneKstatMemRSS: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_rss_bytes",
			Help: "Entire amount of allocated memory.",
		}, []string{"zonename"}),
		ZoneKstatSwapCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_cap_bytes",
			Help: "The SWAP limit in bytes.",
		}, []string{"zonename"}),
		ZoneKstatSwapFree: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_free_bytes",
			Help: "Free SWAP available in bytes.",
		}, []string{"zonename"}),
		ZoneKstatSwapUsed: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_used_bytes",
			Help: "Used SWAP in bytes.",
		}, []string{"zonename"}),
	}, nil
}

// Describe describes all the metrics.
func (e *ZoneKstatCollector) Describe(ch chan<- *prometheus.Desc) {
	e.ZoneKstatCPUBaseline.Describe(ch)
	e.ZoneKstatCPUCap.Describe(ch)
	e.ZoneKstatCPUMaxUsage.Describe(ch)
	e.ZoneKstatCPUUsage.Describe(ch)
	e.ZoneKstatMemCap.Describe(ch)
	e.ZoneKstatMemFree.Describe(ch)
	e.ZoneKstatMemNover.Describe(ch)
	e.ZoneKstatMemPagedOut.Describe(ch)
	e.ZoneKstatMemRSS.Describe(ch)
	e.ZoneKstatSwapCap.Describe(ch)
	e.ZoneKstatSwapFree.Describe(ch)
	e.ZoneKstatSwapUsed.Describe(ch)
}

// Collect fetches the stats.
func (e *ZoneKstatCollector) Collect(ch chan<- prometheus.Metric) {
	e.kstatCPUList()
	e.kstatMemList()
	e.ZoneKstatCPUBaseline.Collect(ch)
	e.ZoneKstatCPUCap.Collect(ch)
	e.ZoneKstatCPUMaxUsage.Collect(ch)
	e.ZoneKstatCPUUsage.Collect(ch)
	e.ZoneKstatMemCap.Collect(ch)
	e.ZoneKstatMemFree.Collect(ch)
	e.ZoneKstatMemNover.Collect(ch)
	e.ZoneKstatMemPagedOut.Collect(ch)
	e.ZoneKstatMemRSS.Collect(ch)
	e.ZoneKstatSwapCap.Collect(ch)
	e.ZoneKstatSwapFree.Collect(ch)
	e.ZoneKstatSwapUsed.Collect(ch)
}

func (e *ZoneKstatCollector) kstatCPUList() {
	out, eerr := exec.Command("kstat", "-p", "-c", "zone_caps", "-n", "cpucaps_zone*").Output()
	if eerr != nil {
		log.Fatal(eerr)
	}
	perr := e.parseKstatCPUListOutput(string(out))
	if perr != nil {
		log.Fatal(perr)
	}
}

func (e *ZoneKstatCollector) kstatMemList() {
	out, eerr := exec.Command("kstat", "-p", "-c", "zone_memory_cap").Output()
	if eerr != nil {
		log.Fatal(eerr)
	}
	perr := e.parseKstatMemListOutput(string(out))
	if perr != nil {
		log.Fatal(perr)
	}
}

func (e *ZoneKstatCollector) parseKstatCPUListOutput(out string) error {
	// trim the label in order to obtain the type of metric
	r, _ := regexp.Compile(`(?m)^.*:.*:.*:`)

	outlines := strings.Split(out, "\n")
	l := len(outlines)
	m := make(map[string]string)
	for _, line := range outlines[1 : l-1] {
		parsedLine := strings.Fields(line)
		fullLabel := parsedLine[0]
		label := r.ReplaceAllString(fullLabel, "")
		// map label and values
		m[label] = parsedLine[1]
	}

	baseline, err := strconv.ParseFloat(m["baseline"], 64)
	if err != nil {
		return err
	}
	cap, err := strconv.ParseFloat(m["value"], 64)
	if err != nil {
		return err
	}
	maxUsage, err := strconv.ParseFloat(m["maxusage"], 64)
	if err != nil {
		return err
	}
	usage, err := strconv.ParseFloat(m["usage"], 64)
	if err != nil {
		return err
	}

	e.ZoneKstatCPUBaseline.With(prometheus.Labels{"zonename": m["zonename"]}).Set(baseline)
	e.ZoneKstatCPUCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(cap)
	e.ZoneKstatCPUMaxUsage.With(prometheus.Labels{"zonename": m["zonename"]}).Set(maxUsage)
	e.ZoneKstatCPUUsage.With(prometheus.Labels{"zonename": m["zonename"]}).Set(usage)

	return nil
}

func (e *ZoneKstatCollector) parseKstatMemListOutput(out string) error {
	// trim the label in order to obtain the type of metric
	r, _ := regexp.Compile(`(?m)^.*:.*:.*:`)

	outlines := strings.Split(out, "\n")
	l := len(outlines)
	m := make(map[string]string)
	for _, line := range outlines[1 : l-1] {
		parsedLine := strings.Fields(line)
		fullLabel := parsedLine[0]
		label := r.ReplaceAllString(fullLabel, "")
		// map label and values
		m[label] = parsedLine[1]
	}

	memCap, err := strconv.ParseFloat(m["physcap"], 64)
	if err != nil {
		return err
	}
	memNover, err := strconv.ParseFloat(m["nover"], 64)
	if err != nil {
		return err
	}
	memPagedOut, err := strconv.ParseFloat(m["pagedout"], 64)
	if err != nil {
		return err
	}
	memRSS, err := strconv.ParseFloat(m["rss"], 64)
	if err != nil {
		return err
	}
	memFree := memCap - memRSS

	swapCap, err := strconv.ParseFloat(m["swapcap"], 64)
	if err != nil {
		return err
	}
	swapUsed, err := strconv.ParseFloat(m["swap"], 64)
	if err != nil {
		return err
	}
	swapFree := memCap - memRSS

	e.ZoneKstatMemCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memCap)
	e.ZoneKstatMemFree.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memFree)
	e.ZoneKstatMemNover.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memNover)
	e.ZoneKstatMemPagedOut.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memPagedOut)
	e.ZoneKstatMemRSS.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memRSS)

	e.ZoneKstatSwapCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapCap)
	e.ZoneKstatSwapFree.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapFree)
	e.ZoneKstatSwapUsed.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapUsed)

	return nil
}
<commit_msg>First try to directly gather NIC stats from kstat<commit_after>// kstat zone collector
// this will :
//  - call kstat inside a zone
//  - gather zone metrics
//  - feed the collector

package collector

import (
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	// Prometheus Go toolset
	"github.com/prometheus/client_golang/prometheus"
)

// ZoneKstatCollector declares the data type within the prometheus metrics package.
type ZoneKstatCollector struct {
	ZoneKstatCPUBaseline   *prometheus.GaugeVec
	ZoneKstatCPUCap        *prometheus.GaugeVec
	ZoneKstatCPUMaxUsage   *prometheus.GaugeVec
	ZoneKstatCPUUsage      *prometheus.GaugeVec
	ZoneKstatMemCap        *prometheus.GaugeVec
	ZoneKstatMemFree       *prometheus.GaugeVec
	ZoneKstatMemNover      *prometheus.GaugeVec
	ZoneKstatMemPagedOut   *prometheus.GaugeVec
	ZoneKstatMemRSS        *prometheus.GaugeVec
	ZoneKstatNICCollisions *prometheus.GaugeVec
	ZoneKstatSwapCap       *prometheus.GaugeVec
	ZoneKstatSwapFree      *prometheus.GaugeVec
	ZoneKstatSwapUsed      *prometheus.GaugeVec
}

// ZoneKstatNIC defines the mapping of kstat link structure.
type ZoneKstatNIC struct {
	ifName, ifLabel string
}

// ZoneKstatNICs slice for key iteration
type ZoneKstatNICs []ZoneKstatNIC

// NewZoneKstatExporter returns a newly allocated exporter ZoneKstatCollector.
// It exposes the kstat command result.
func NewZoneKstatExporter() (*ZoneKstatCollector, error) {
	return &ZoneKstatCollector{
		ZoneKstatCPUBaseline: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_baseline",
			Help: "A soft limit on the number of CPU cycles a hosted application can consume.",
		}, []string{"zonename"}),
		ZoneKstatCPUCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_cap",
			Help: "The maximum number of CPU cycles that are allocated to a zone.",
		}, []string{"zonename"}),
		ZoneKstatCPUMaxUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_maxusage",
			Help: "The maximum percentage of CPU used.",
		}, []string{"zonename"}),
		ZoneKstatCPUUsage: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_cpu_usage",
			Help: "The current percentage of CPU used.",
		}, []string{"zonename"}),
		ZoneKstatMemCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_cap_bytes",
			Help: "The physical memory limit in bytes.",
		}, []string{"zonename"}),
		ZoneKstatMemFree: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_free_bytes",
			Help: "Free memory available in bytes.",
		}, []string{"zonename"}),
		ZoneKstatMemNover: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_nover_total",
			Help: "The number of times the zone has gone over its cap.",
		}, []string{"zonename"}),
		ZoneKstatMemPagedOut: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_pagedout_bytes",
			Help: "Total amount of memory that has been paged out when the zone has gone over its cap.",
		}, []string{"zonename"}),
		ZoneKstatMemRSS: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_rss_bytes",
			Help: "Entire amount of allocated memory.",
		}, []string{"zonename"}),
		ZoneKstatNICCollisions: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_network_collisions",
			Help: "Entire amount of collisions.",
		}, []string{"zonename", "device"}),
		ZoneKstatSwapCap: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_cap_bytes",
			Help: "The SWAP limit in bytes.",
		}, []string{"zonename"}),
		ZoneKstatSwapFree: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_free_bytes",
			Help: "Free SWAP available in bytes.",
		}, []string{"zonename"}),
		ZoneKstatSwapUsed: prometheus.NewGaugeVec(prometheus.GaugeOpts{
			Name: "smartos_memory_swap_used_bytes",
			Help: "Used SWAP in bytes.",
		}, []string{"zonename"}),
	}, nil
}

// Describe describes all the metrics.
func (e *ZoneKstatCollector) Describe(ch chan<- *prometheus.Desc) {
	e.ZoneKstatCPUBaseline.Describe(ch)
	e.ZoneKstatCPUCap.Describe(ch)
	e.ZoneKstatCPUMaxUsage.Describe(ch)
	e.ZoneKstatCPUUsage.Describe(ch)
	e.ZoneKstatMemCap.Describe(ch)
	e.ZoneKstatMemFree.Describe(ch)
	e.ZoneKstatMemNover.Describe(ch)
	e.ZoneKstatMemPagedOut.Describe(ch)
	e.ZoneKstatMemRSS.Describe(ch)
	e.ZoneKstatNICCollisions(ch)
	e.ZoneKstatSwapCap.Describe(ch)
	e.ZoneKstatSwapFree.Describe(ch)
	e.ZoneKstatSwapUsed.Describe(ch)
}

// Collect fetches the stats.
func (e *ZoneKstatCollector) Collect(ch chan<- prometheus.Metric) {
	e.kstatCPUList()
	e.kstatMemList()
	e.kstatNICList()
	e.ZoneKstatCPUBaseline.Collect(ch)
	e.ZoneKstatCPUCap.Collect(ch)
	e.ZoneKstatCPUMaxUsage.Collect(ch)
	e.ZoneKstatCPUUsage.Collect(ch)
	e.ZoneKstatMemCap.Collect(ch)
	e.ZoneKstatMemFree.Collect(ch)
	e.ZoneKstatMemNover.Collect(ch)
	e.ZoneKstatMemPagedOut.Collect(ch)
	e.ZoneKstatMemRSS.Collect(ch)
	e.ZoneKstatNICCollisions(ch)
	e.ZoneKstatSwapCap.Collect(ch)
	e.ZoneKstatSwapFree.Collect(ch)
	e.ZoneKstatSwapUsed.Collect(ch)
}

func (e *ZoneKstatCollector) kstatCPUList() {
	out, eerr := exec.Command("kstat", "-p", "-c", "zone_caps", "-n", "cpucaps_zone*").Output()
	if eerr != nil {
		log.Fatal(eerr)
	}
	perr := e.parseKstatCPUListOutput(string(out))
	if perr != nil {
		log.Fatal(perr)
	}
}

func (e *ZoneKstatCollector) kstatMemList() {
	out, eerr := exec.Command("kstat", "-p", "-c", "zone_memory_cap").Output()
	if eerr != nil {
		log.Fatal(eerr)
	}
	perr := e.parseKstatMemListOutput(string(out))
	if perr != nil {
		log.Fatal(perr)
	}
}

func (e *ZoneKstatCollector) kstatNICList() {
	out, eerr := exec.Command("kstat", "-p", "-m", "link").Output()
	if eerr != nil {
		log.Fatal(eerr)
	}
	perr := e.parseKstatNICListOutput(string(out))
	if perr != nil {
		log.Fatal(perr)
	}
}

func (e *ZoneKstatCollector) parseKstatCPUListOutput(out string) error {
	// trim the label in order to obtain the type of metric
	r, _ := regexp.Compile(`(?m)^.*:.*:.*:`)

	outlines := strings.Split(out, "\n")
	l := len(outlines)
	m := make(map[string]string)
	for _, line := range outlines[1 : l-1] {
		parsedLine := strings.Fields(line)
		fullLabel := parsedLine[0]
		label := r.ReplaceAllString(fullLabel, "")
		// map label and values
		m[label] = parsedLine[1]
	}

	baseline, err := strconv.ParseFloat(m["baseline"], 64)
	if err != nil {
		return err
	}
	cap, err := strconv.ParseFloat(m["value"], 64)
	if err != nil {
		return err
	}
	maxUsage, err := strconv.ParseFloat(m["maxusage"], 64)
	if err != nil {
		return err
	}
	usage, err := strconv.ParseFloat(m["usage"], 64)
	if err != nil {
		return err
	}

	e.ZoneKstatCPUBaseline.With(prometheus.Labels{"zonename": m["zonename"]}).Set(baseline)
	e.ZoneKstatCPUCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(cap)
	e.ZoneKstatCPUMaxUsage.With(prometheus.Labels{"zonename": m["zonename"]}).Set(maxUsage)
	e.ZoneKstatCPUUsage.With(prometheus.Labels{"zonename": m["zonename"]}).Set(usage)

	return nil
}

func (e *ZoneKstatCollector) parseKstatMemListOutput(out string) error {
	// trim the label in order to obtain the type of metric
	r, _ := regexp.Compile(`(?m)^.*:.*:.*:`)

	outlines := strings.Split(out, "\n")
	l := len(outlines)
	m := make(map[string]string)
	for _, line := range outlines[1 : l-1] {
		parsedLine := strings.Fields(line)
		fullLabel := parsedLine[0]
		label := r.ReplaceAllString(fullLabel, "")
		// map label and values
		m[label] = parsedLine[1]
	}

	memCap, err := strconv.ParseFloat(m["physcap"], 64)
	if err != nil {
		return err
	}
	memNover, err := strconv.ParseFloat(m["nover"], 64)
	if err != nil {
		return err
	}
	memPagedOut, err := strconv.ParseFloat(m["pagedout"], 64)
	if err != nil {
		return err
	}
	memRSS, err := strconv.ParseFloat(m["rss"], 64)
	if err != nil {
		return err
	}
	memFree := memCap - memRSS

	swapCap, err := strconv.ParseFloat(m["swapcap"], 64)
	if err != nil {
		return err
	}
	swapUsed, err := strconv.ParseFloat(m["swap"], 64)
	if err != nil {
		return err
	}
	swapFree := memCap - memRSS

	e.ZoneKstatMemCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memCap)
	e.ZoneKstatMemFree.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memFree)
	e.ZoneKstatMemNover.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memNover)
	e.ZoneKstatMemPagedOut.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memPagedOut)
	e.ZoneKstatMemRSS.With(prometheus.Labels{"zonename": m["zonename"]}).Set(memRSS)

	e.ZoneKstatSwapCap.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapCap)
	e.ZoneKstatSwapFree.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapFree)
	e.ZoneKstatSwapUsed.With(prometheus.Labels{"zonename": m["zonename"]}).Set(swapUsed)

	return nil
}

func (e *ZoneKstatCollector) parseKstatNICListOutput(out string) error {
	// trim the label in order to obtain the type of metric and interface name
	r, _ := regexp.Compile(`(?m)^.+:(.+):`)

	outlines := strings.Split(out, "\n")
	l := len(outlines)
	m := make(map[ZoneKstatNIC]string)
	for _, line := range outlines[1 : l-1] {
		parsedLine := strings.Fields(line)
		fullLabel := parsedLine[0]
		ifname := r.FindStringSubmatch(fullLabel)[1]
		label := r.ReplaceAllString(fullLabel, "")
		// map struct label and values
		m[ZoneKstatNIC{ifname, label}] = parsedLine[1]
	}

	// populates the slice with the map
	var ZoneKstatNICkeys ZoneKstatNICs
	for k := range m {
		ZoneKstatNICkeys = append(ZoneKstatNICkeys, k)
	}

	for _, k := range ZoneKstatNICkeys {
		if k.ifLabel == "collisions" {
			collisions, err := strconv.ParseFloat(m[ZoneKstatNIC{k.ifName, "collisions"}], 64)
			if err != nil {
				return err
			}
			e.ZoneKstatNICCollisions.With(
				prometheus.Labels{"zonename": m[ZoneKstatNIC{k.ifName, "zonename"}], "device": k.ifName},
			).Set(collisions)
		}
	}
	// RESERVED
	//ierrors, err := strconv.ParseFloat(m["ierrors"], 64)
	//if err != nil {
	//	return err
	//}
	//ipackets64, err := strconv.ParseFloat(m["ipackets64"], 64)
	//if err != nil {
	//	return err
	//}
	//link_state, err := strconv.ParseFloat(m["link_state"], 64)
	//if err != nil {
	//	return err
	//}
	//obytes64, err := strconv.ParseFloat(m["obytes64"], 64)
	//if err != nil {
	//	return err
	//}
	//oerrors, err := strconv.ParseFloat(m["oerrors"], 64)
	//if err != nil {
	//	return err
	//}
	//opackets64, err := strconv.ParseFloat(m["opackets64"], 64)
	//if err != nil {
	//	return err
	//}
	//rbytes64, err := strconv.ParseFloat(m["rbytes64"], 64)
	//if err != nil {
	//	return err
	//}

	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 compose

import (
	"context"

	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/api/client"
	"github.com/docker/compose-cli/api/progress"
)

type pullOptions struct {
	*projectOptions
	composeOptions
}

func pullCommand(p *projectOptions) *cobra.Command {
	opts := pullOptions{
		projectOptions: p,
	}
	pullCmd := &cobra.Command{
		Use:   "pull [SERVICE...]",
		Short: "Pull service images",
		RunE: func(cmd *cobra.Command, args []string) error {
			return runPull(cmd.Context(), opts, args)
		},
	}
	return pullCmd
}

func runPull(ctx context.Context, opts pullOptions, services []string) error {
	c, err := client.NewWithDefaultLocalBackend(ctx)
	if err != nil {
		return err
	}

	project, err := opts.toProject(services)
	if err != nil {
		return err
	}

	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
		return "", c.ComposeService().Pull(ctx, project)
	})
	return err
}
<commit_msg>introduce pull --quiet option<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 compose

import (
	"context"
	"github.com/spf13/cobra"

	"github.com/docker/compose-cli/api/client"
	"github.com/docker/compose-cli/api/progress"
)

type pullOptions struct {
	*projectOptions
	composeOptions
	quiet bool
}

func pullCommand(p *projectOptions) *cobra.Command {
	opts := pullOptions{
		projectOptions: p,
	}
	cmd := &cobra.Command{
		Use:   "pull [SERVICE...]",
		Short: "Pull service images",
		RunE: func(cmd *cobra.Command, args []string) error {
			return runPull(cmd.Context(), opts, args)
		},
	}
	cmd.Flags().BoolVarP(&opts.quiet, "quiet", "q", false, "Pull without printing progress information")
	return cmd
}

func runPull(ctx context.Context, opts pullOptions, services []string) error {
	c, err := client.NewWithDefaultLocalBackend(ctx)
	if err != nil {
		return err
	}

	project, err := opts.toProject(services)
	if err != nil {
		return err
	}

	if opts.quiet {
		return c.ComposeService().Pull(ctx, project)
	}

	_, err = progress.Run(ctx, func(ctx context.Context) (string, error) {
		return "", c.ComposeService().Pull(ctx, project)
	})
	return err
}
<|endoftext|>