<commit_before>package profile

import (
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/cachecontrol"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/service/image"
	"github.com/hackform/governor/service/objstore"
	"github.com/hackform/governor/service/profile/model"
	"github.com/hackform/governor/service/user/gate"
	"github.com/labstack/echo"
	"github.com/sirupsen/logrus"
	"io"
	"net/http"
)

type (
	reqProfileGetID struct {
		Userid string `json:"userid"`
	}

	reqProfileModel struct {
		Userid string `json:"-"`
		Email  string `json:"contact_email"`
		Bio    string `json:"bio"`
	}

	resProfileUpdate struct {
		Userid string `json:"userid"`
	}

	resProfileModel struct {
		Email string `json:"contact_email"`
		Bio   string `json:"bio"`
		Image string `json:"image"`
	}
)

func (r *reqProfileGetID) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	return nil
}

func (r *reqProfileModel) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	if err := validEmail(r.Email); err != nil {
		return err
	}
	if err := validBio(r.Email); err != nil {
		return err
	}
	return nil
}

const (
	moduleID    = "profile"
	imageBucket = "profile-image"
	min1        = 60
	min15       = 900
)

type (
	// Profile is a service for storing user profile information
	Profile interface {
		governor.Service
	}

	profileService struct {
		db   db.Database
		obj  objstore.Bucket
		gate gate.Gate
		img  image.Image
		cc   cachecontrol.CacheControl
	}
)

// New creates a new Profile service
func New(conf governor.Config, l *logrus.Logger, db db.Database, obj objstore.Objstore, g gate.Gate, img image.Image, cc cachecontrol.CacheControl) Profile {
	b, err := obj.GetBucketDefLoc(imageBucket)
	if err != nil {
		l.Errorf("failed to get bucket: %s\n", err.Error())
	}

	l.Info("initialized profile service")

	return &profileService{
		db:   db,
		obj:  b,
		gate: g,
		img:  img,
		cc:   cc,
	}
}

// Mount is a collection of routes for accessing and modifying profile data
func (p *profileService) Mount(conf governor.Config, r *echo.Group, l *logrus.Logger) error {
	db := p.db.DB()

	r.POST("", func(c echo.Context) error {
		rprofile := reqProfileModel{}
		if err := c.Bind(&rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Get("userid").(string)
		if err := rprofile.valid(); err != nil {
			return err
		}

		m := profilemodel.Model{
			Email: rprofile.Email,
			Bio:   rprofile.Bio,
		}

		if err := m.SetIDB64(rprofile.Userid); err != nil {
			err.SetErrorUser()
			return err
		}

		if err := m.Insert(db); err != nil {
			if err.Code() == 3 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		userid, _ := m.IDBase64()

		return c.JSON(http.StatusCreated, resProfileUpdate{
			Userid: userid,
		})
	}, gate.User(p.gate))

	r.PUT("", func(c echo.Context) error {
		rprofile := reqProfileModel{}
		if err := c.Bind(&rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Get("userid").(string)
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		m.Email = rprofile.Email
		m.Bio = rprofile.Bio

		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.User(p.gate))

	r.POST("/image", func(c echo.Context) error {
		img := c.Get("image").(io.Reader)
		thumb64 := c.Get("thumbnail").(string)
		userid := c.Get("userid").(string)

		m, err := profilemodel.GetByIDB64(db, userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		if err := p.obj.Put(userid+"-profile", image.MediaTypeJpeg, img); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		m.Image = thumb64
		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.User(p.gate), p.img.LoadJpeg("image", image.Options{
		Width:          384,
		Height:         384,
		ThumbWidth:     32,
		ThumbHeight:    32,
		Quality:        85,
		ThumbQuality:   65,
		Crop:           true,
		ContextField:   "image",
		ThumbnailField: "thumbnail",
	}))

	r.DELETE("/:id", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		if err := m.Delete(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.OwnerOrAdmin(p.gate, "id"))

	r.GET("", func(c echo.Context) error {
		userid := c.Get("userid").(string)

		m, err := profilemodel.GetByIDB64(db, userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.JSON(http.StatusOK, resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	}, gate.User(p.gate))

	r.GET("/:id", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.JSON(http.StatusOK, resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	}, p.cc.Control(true, false, min15, nil))

	r.GET("/image", func(c echo.Context) error {
		userid := c.Get("userid").(string)

		obj, objinfo, err := p.obj.Get(userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}
		return c.Stream(http.StatusOK, objinfo.ContentType, obj)
	}, gate.User(p.gate),
		p.cc.Control(true, false, min1, func(c echo.Context) (string, *governor.Error) {
			userid := c.Get("userid").(string)

			objinfo, err := p.obj.Stat(userid + "-profile")
			if err != nil {
				if err.Code() == 2 {
					err.SetErrorUser()
				}
				err.AddTrace(moduleID)
				return "", err
			}

			return objinfo.ETag, nil
		}))

	r.GET("/:id/image", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		obj, objinfo, err := p.obj.Get(rprofile.Userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}
		return c.Stream(http.StatusOK, objinfo.ContentType, obj)
	}, p.cc.Control(false, true, min15, func(c echo.Context) (string, *governor.Error) {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return "", err
		}

		objinfo, err := p.obj.Stat(rprofile.Userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return "", err
		}

		return objinfo.ETag, nil
	}))

	l.Info("mounted profile service")

	return nil
}

// Health is a check for service health
func (p *profileService) Health() *governor.Error {
	return nil
}

// Setup is run on service setup
func (p *profileService) Setup(conf governor.Config, l *logrus.Logger, rsetup governor.ReqSetupPost) *governor.Error {
	if err := profilemodel.Setup(p.db.DB()); err != nil {
		err.AddTrace(moduleID)
		return err
	}
	l.Info("created new profile table")
	return nil
}
<commit_msg>bug fix profile cache<commit_after>package profile

import (
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/cachecontrol"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/service/image"
	"github.com/hackform/governor/service/objstore"
	"github.com/hackform/governor/service/profile/model"
	"github.com/hackform/governor/service/user/gate"
	"github.com/labstack/echo"
	"github.com/sirupsen/logrus"
	"io"
	"net/http"
)

type (
	reqProfileGetID struct {
		Userid string `json:"userid"`
	}

	reqProfileModel struct {
		Userid string `json:"-"`
		Email  string `json:"contact_email"`
		Bio    string `json:"bio"`
	}

	resProfileUpdate struct {
		Userid string `json:"userid"`
	}

	resProfileModel struct {
		Email string `json:"contact_email"`
		Bio   string `json:"bio"`
		Image string `json:"image"`
	}
)

func (r *reqProfileGetID) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	return nil
}

func (r *reqProfileModel) valid() *governor.Error {
	if err := hasUserid(r.Userid); err != nil {
		return err
	}
	if err := validEmail(r.Email); err != nil {
		return err
	}
	if err := validBio(r.Email); err != nil {
		return err
	}
	return nil
}

const (
	moduleID    = "profile"
	imageBucket = "profile-image"
	min1        = 60
	min15       = 900
)

type (
	// Profile is a service for storing user profile information
	Profile interface {
		governor.Service
	}

	profileService struct {
		db   db.Database
		obj  objstore.Bucket
		gate gate.Gate
		img  image.Image
		cc   cachecontrol.CacheControl
	}
)

// New creates a new Profile service
func New(conf governor.Config, l *logrus.Logger, db db.Database, obj objstore.Objstore, g gate.Gate, img image.Image, cc cachecontrol.CacheControl) Profile {
	b, err := obj.GetBucketDefLoc(imageBucket)
	if err != nil {
		l.Errorf("failed to get bucket: %s\n", err.Error())
	}

	l.Info("initialized profile service")

	return &profileService{
		db:   db,
		obj:  b,
		gate: g,
		img:  img,
		cc:   cc,
	}
}

// Mount is a collection of routes for accessing and modifying profile data
func (p *profileService) Mount(conf governor.Config, r *echo.Group, l *logrus.Logger) error {
	db := p.db.DB()

	r.POST("", func(c echo.Context) error {
		rprofile := reqProfileModel{}
		if err := c.Bind(&rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Get("userid").(string)
		if err := rprofile.valid(); err != nil {
			return err
		}

		m := profilemodel.Model{
			Email: rprofile.Email,
			Bio:   rprofile.Bio,
		}

		if err := m.SetIDB64(rprofile.Userid); err != nil {
			err.SetErrorUser()
			return err
		}

		if err := m.Insert(db); err != nil {
			if err.Code() == 3 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		userid, _ := m.IDBase64()

		return c.JSON(http.StatusCreated, resProfileUpdate{
			Userid: userid,
		})
	}, gate.User(p.gate))

	r.PUT("", func(c echo.Context) error {
		rprofile := reqProfileModel{}
		if err := c.Bind(&rprofile); err != nil {
			return governor.NewErrorUser(moduleID, err.Error(), 0, http.StatusBadRequest)
		}
		rprofile.Userid = c.Get("userid").(string)
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		m.Email = rprofile.Email
		m.Bio = rprofile.Bio

		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.User(p.gate))

	r.POST("/image", func(c echo.Context) error {
		img := c.Get("image").(io.Reader)
		thumb64 := c.Get("thumbnail").(string)
		userid := c.Get("userid").(string)

		m, err := profilemodel.GetByIDB64(db, userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		if err := p.obj.Put(userid+"-profile", image.MediaTypeJpeg, img); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		m.Image = thumb64
		if err := m.Update(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.User(p.gate), p.img.LoadJpeg("image", image.Options{
		Width:          384,
		Height:         384,
		ThumbWidth:     32,
		ThumbHeight:    32,
		Quality:        85,
		ThumbQuality:   65,
		Crop:           true,
		ContextField:   "image",
		ThumbnailField: "thumbnail",
	}))

	r.DELETE("/:id", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		if err := m.Delete(db); err != nil {
			err.AddTrace(moduleID)
			return err
		}

		return c.NoContent(http.StatusNoContent)
	}, gate.OwnerOrAdmin(p.gate, "id"))

	r.GET("", func(c echo.Context) error {
		userid := c.Get("userid").(string)

		m, err := profilemodel.GetByIDB64(db, userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.JSON(http.StatusOK, resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	}, gate.User(p.gate),
		p.cc.Control(true, false, min1, nil))

	r.GET("/:id", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		m, err := profilemodel.GetByIDB64(db, rprofile.Userid)
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}

		return c.JSON(http.StatusOK, resProfileModel{
			Email: m.Email,
			Bio:   m.Bio,
			Image: m.Image,
		})
	}, p.cc.Control(true, false, min15, nil))

	r.GET("/image", func(c echo.Context) error {
		userid := c.Get("userid").(string)

		obj, objinfo, err := p.obj.Get(userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}
		return c.Stream(http.StatusOK, objinfo.ContentType, obj)
	}, gate.User(p.gate),
		p.cc.Control(true, false, min1, func(c echo.Context) (string, *governor.Error) {
			userid := c.Get("userid").(string)

			objinfo, err := p.obj.Stat(userid + "-profile")
			if err != nil {
				if err.Code() == 2 {
					err.SetErrorUser()
				}
				err.AddTrace(moduleID)
				return "", err
			}

			return objinfo.ETag, nil
		}))

	r.GET("/:id/image", func(c echo.Context) error {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return err
		}

		obj, objinfo, err := p.obj.Get(rprofile.Userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return err
		}
		return c.Stream(http.StatusOK, objinfo.ContentType, obj)
	}, p.cc.Control(true, false, min15, func(c echo.Context) (string, *governor.Error) {
		rprofile := reqProfileGetID{
			Userid: c.Param("id"),
		}
		if err := rprofile.valid(); err != nil {
			return "", err
		}

		objinfo, err := p.obj.Stat(rprofile.Userid + "-profile")
		if err != nil {
			if err.Code() == 2 {
				err.SetErrorUser()
			}
			err.AddTrace(moduleID)
			return "", err
		}

		return objinfo.ETag, nil
	}))

	l.Info("mounted profile service")

	return nil
}

// Health is a check for service health
func (p *profileService) Health() *governor.Error {
	return nil
}

// Setup is run on service setup
func (p *profileService) Setup(conf governor.Config, l *logrus.Logger, rsetup governor.ReqSetupPost) *governor.Error {
	if err := profilemodel.Setup(p.db.DB()); err != nil {
		err.AddTrace(moduleID)
		return err
	}
	l.Info("created new profile table")
	return nil
}
<|endoftext|>


<commit_before>package content

import (
	contentapi "github.com/containerd/containerd/api/services/content"
)

type remoteReader struct {
	client contentapi.Content_ReadClient
	extra  []byte
}

func (rr *remoteReader) Read(p []byte) (n int, err error) {
	n += copy(p, rr.extra)
	if n >= len(p) {
		if n <= len(rr.extra) {
			rr.extra = rr.extra[n:]
		} else {
			rr.extra = rr.extra[:0]
		}
		return
	}

	p = p[n:]
	for len(p) > 0 {
		var resp *contentapi.ReadResponse
		// fill our buffer up until we can fill p.
		resp, err = rr.client.Recv()
		if err != nil {
			return
		}

		copied := copy(p, resp.Data)
		n += copied
		p = p[copied:]

		if copied < len(p) {
			continue
		}

		rr.extra = append(rr.extra, resp.Data[copied:]...)
	}

	return
}

// TODO(stevvooe): Implemente io.ReaderAt.

func (rr *remoteReader) Close() error {
	return rr.client.CloseSend()
}
<commit_msg>Clear extra in content service reader after consumed<commit_after>package content

import (
	contentapi "github.com/containerd/containerd/api/services/content"
)

type remoteReader struct {
	client contentapi.Content_ReadClient
	extra  []byte
}

func (rr *remoteReader) Read(p []byte) (n int, err error) {
	n += copy(p, rr.extra)
	if n >= len(p) {
		if n <= len(rr.extra) {
			rr.extra = rr.extra[n:]
		} else {
			rr.extra = rr.extra[:0]
		}
		return
	}
	rr.extra = rr.extra[:0]

	p = p[n:]
	for len(p) > 0 {
		var resp *contentapi.ReadResponse
		// fill our buffer up until we can fill p.
		resp, err = rr.client.Recv()
		if err != nil {
			return
		}

		copied := copy(p, resp.Data)
		n += copied
		p = p[copied:]

		if copied < len(p) {
			continue
		}

		rr.extra = append(rr.extra, resp.Data[copied:]...)
	}

	return
}

// TODO(stevvooe): Implemente io.ReaderAt.

func (rr *remoteReader) Close() error {
	return rr.client.CloseSend()
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"fmt"
)

// isFlag checks whether the given argument is a valid flag or not
func isFlag(arg string) bool {
	if _, err := parseFlag(arg); err != nil {
		return false
	}

	return true
}

// parseFlag parses a flags name. A flag can be in form of --name=value,
// -name=value, -n=value, or --name, -name=, etc...  If it's a correct flag,
// the name is returned. If not an empty string and an error message is
// returned
func parseFlag(arg string) (string, error) {
	if arg == "" {
		return "", errors.New("argument is empty")
	}

	if len(arg) == 1 {
		return "", errors.New("argument is too short")
	}

	if arg[0] != '-' {
		return "", errors.New("argument doesn't start with dash")
	}

	numMinuses := 1

	if arg[1] == '-' {
		numMinuses++
		if len(arg) == 2 {
			return "", errors.New("argument is too short")
		}
	}

	name := arg[numMinuses:]
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		return "", fmt.Errorf("bad flag syntax: %s", arg)
	}

	return name, nil
}

// parseValue parses the value from the given flag. A flag name can be in
// form of name=value, n=value, n=, n.
func parseValue(flag string) (name, value string) {
	for i, r := range flag {
		if r == '=' {
			value = flag[i+1:]
			name = flag[0:i]
		}
	}

	// special case of "n"
	if name == "" {
		name = flag
	}

	return
}

// parseName parses the given flagName from the args slice and returns the
// value passed to the flag. An example: args: ["--provider", "aws", "--foo"],
// flagName: "provider" will return "aws".
func parseFlagValue(flagName string, args []string) (string, error) {
	if len(args) == 0 {
		return "", errors.New("argument is empty")
	}

	for i, arg := range args {
		flag, err := parseFlag(arg)
		if err != nil {
			fmt.Println("err")
			continue
		}

		name, value := parseValue(flag)
		if name != flagName && name[0] != flagName[0] {
			continue
		}

		if value != "" {
			return value, nil // found
		}

		// no value found, check out the next argument. at least two args must
		// be present
		if len(args) > 1 {
			// value must be next argument
			if !isFlag(args[i+1]) {
				return args[i+1], nil
			}
		}
	}

	return "", errors.New("couldn't find")
}
<commit_msg>command/flag: just print err<commit_after>package command

import (
	"errors"
	"fmt"
)

// isFlag checks whether the given argument is a valid flag or not
func isFlag(arg string) bool {
	if _, err := parseFlag(arg); err != nil {
		return false
	}

	return true
}

// parseFlag parses a flags name. A flag can be in form of --name=value,
// -name=value, -n=value, or --name, -name=, etc...  If it's a correct flag,
// the name is returned. If not an empty string and an error message is
// returned
func parseFlag(arg string) (string, error) {
	if arg == "" {
		return "", errors.New("argument is empty")
	}

	if len(arg) == 1 {
		return "", errors.New("argument is too short")
	}

	if arg[0] != '-' {
		return "", errors.New("argument doesn't start with dash")
	}

	numMinuses := 1

	if arg[1] == '-' {
		numMinuses++
		if len(arg) == 2 {
			return "", errors.New("argument is too short")
		}
	}

	name := arg[numMinuses:]
	if len(name) == 0 || name[0] == '-' || name[0] == '=' {
		return "", fmt.Errorf("bad flag syntax: %s", arg)
	}

	return name, nil
}

// parseValue parses the value from the given flag. A flag name can be in
// form of name=value, n=value, n=, n.
func parseValue(flag string) (name, value string) {
	for i, r := range flag {
		if r == '=' {
			value = flag[i+1:]
			name = flag[0:i]
		}
	}

	// special case of "n"
	if name == "" {
		name = flag
	}

	return
}

// parseName parses the given flagName from the args slice and returns the
// value passed to the flag. An example: args: ["--provider", "aws", "--foo"],
// flagName: "provider" will return "aws".
func parseFlagValue(flagName string, args []string) (string, error) {
	if len(args) == 0 {
		return "", errors.New("argument slice is empty")
	}

	for i, arg := range args {
		flag, err := parseFlag(arg)
		if err != nil {
			fmt.Println("bad syntax err: ", err)
			continue
		}

		name, value := parseValue(flag)
		if name != flagName && name[0] != flagName[0] {
			continue
		}

		if value != "" {
			return value, nil // found
		}

		// no value found, check out the next argument. at least two args must
		// be present
		if len(args) > 1 {
			// value must be next argument
			if !isFlag(args[i+1]) {
				return args[i+1], nil
			}
		}
	}

	return "", errors.New("couldn't find any value")
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"io"
	"os"
	"strings"

	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/tunnel"
	"github.com/ondevice/ondevice/util"
)

// pipeCommand -- `ondevice pipe` implementation
type pipeCommand struct {
	BaseCommand
	tunnel *tunnel.Tunnel

	sentEOF bool

	reader *bufio.Reader
	writer *bufio.Writer
}

func (p pipeCommand) run(args []string) int {
	// parse arguments
	if len(args) < 1 {
		logg.Fatal("Missing devId")
	}
	if len(args) < 2 {
		logg.Fatal("Missing service name")
	}

	devID := args[0]
	service := args[1]

	auth, err := api.CreateClientAuth()
	if err != nil {
		logg.Fatal("Missing client credentials")
	}

	if strings.Contains(devID, ".") {
		parts := strings.SplitN(devID, ".", 2)
		var user, pwd string
		if user, pwd, err = config.GetClientUserAuth(parts[0]); err == nil {
			devID = parts[1]
			auth = api.CreateAuth(user, pwd)
		}
	}

	p.writer = bufio.NewWriter(os.Stdout)
	p.reader = bufio.NewReader(os.Stdin)

	// initiate connection
	c := tunnel.Tunnel{}
	p.tunnel = &c
	c.CloseListeners = append(c.CloseListeners, p.onClose)
	c.DataListeners = append(c.DataListeners, p.onData)
	c.ErrorListeners = append(c.ErrorListeners, p.onError)
	if e := tunnel.Connect(&c, devID, service, service, auth); e != nil {
		logg.FailWithAPIError(e)
	}

	buff := make([]byte, 8100)
	var count int

	for {
		if count, err = p.reader.Read(buff); err != nil {
			if count == 0 && err == io.EOF {
				// we can't simply call c.Close() here because the other side might still
				// send data. A simple test would be (assuming the device has the 'echo' service enabled):
				//   echo hello | ondevice pipe <dev> echo
				p.sentEOF = true
				c.SendEOF()
				break
			} else {
				logg.Fatal("error reading from stdin: ", err)
			}
		}

		c.Write(buff[:count])
	}

	c.Wait()
	return 0
}

func (p *pipeCommand) onClose() {
	// odds are run() is currently blocking in the p.reader.Read(). Close stdin to
	// allow it to return gracefully
	logg.Debug("Tunnel closed")
	os.Stdin.Close()
}

func (p *pipeCommand) onError(err util.APIError) {
	if err.Code() != util.OtherError {
		logg.FailWithAPIError(err)
	} else if !p.sentEOF {
		logg.Fatal("Lost connection: ", err)
	}
	p.tunnel.Close()
}

// OnMessage -- Handles incoming WebSocket messages
func (p *pipeCommand) onData(data []byte) {
	p.writer.Write(data)
	p.writer.Flush()
}

// PipeCommand -- Implements `ondevice pipe`
var PipeCommand = pipeCommand{
	BaseCommand: BaseCommand{
		Arguments: "<devId> <service>",
		ShortHelp: "Pipes a device's service to stdout/stdin",
		RunFn:     nil,
		LongHelp: `$ ondevice pipe <devId> <service>

Sends data from stdin to the specified service - and prints whatever it gets in
return to stdout.

This command is used internally by 'ondevice ssh' to serve as ssh's ProxyCommand.

Example:
  $ echo hello world | ondevice pipe <devId> echo
  hello world

	Sends 'hello world' to q5dkpm's 'echo' service. The echo service simply returns
	the data it gets back to the sender. Therefore the above command is equivalent
	to simply calling 'echo hello world' (as long as your device is online).
`,
	},
}
<commit_msg>PipeCommand: added comment explaining why RunFn is nil<commit_after>package command

import (
	"bufio"
	"io"
	"os"
	"strings"

	"github.com/ondevice/ondevice/api"
	"github.com/ondevice/ondevice/config"
	"github.com/ondevice/ondevice/logg"
	"github.com/ondevice/ondevice/tunnel"
	"github.com/ondevice/ondevice/util"
)

// pipeCommand -- `ondevice pipe` implementation
type pipeCommand struct {
	BaseCommand
	tunnel *tunnel.Tunnel

	sentEOF bool

	reader *bufio.Reader
	writer *bufio.Writer
}

func (p pipeCommand) run(args []string) int {
	// parse arguments
	if len(args) < 1 {
		logg.Fatal("Missing devId")
	}
	if len(args) < 2 {
		logg.Fatal("Missing service name")
	}

	devID := args[0]
	service := args[1]

	auth, err := api.CreateClientAuth()
	if err != nil {
		logg.Fatal("Missing client credentials")
	}

	if strings.Contains(devID, ".") {
		parts := strings.SplitN(devID, ".", 2)
		var user, pwd string
		if user, pwd, err = config.GetClientUserAuth(parts[0]); err == nil {
			devID = parts[1]
			auth = api.CreateAuth(user, pwd)
		}
	}

	p.writer = bufio.NewWriter(os.Stdout)
	p.reader = bufio.NewReader(os.Stdin)

	// initiate connection
	c := tunnel.Tunnel{}
	p.tunnel = &c
	c.CloseListeners = append(c.CloseListeners, p.onClose)
	c.DataListeners = append(c.DataListeners, p.onData)
	c.ErrorListeners = append(c.ErrorListeners, p.onError)
	if e := tunnel.Connect(&c, devID, service, service, auth); e != nil {
		logg.FailWithAPIError(e)
	}

	buff := make([]byte, 8100)
	var count int

	for {
		if count, err = p.reader.Read(buff); err != nil {
			if count == 0 && err == io.EOF {
				// we can't simply call c.Close() here because the other side might still
				// send data. A simple test would be (assuming the device has the 'echo' service enabled):
				//   echo hello | ondevice pipe <dev> echo
				p.sentEOF = true
				c.SendEOF()
				break
			} else {
				logg.Fatal("error reading from stdin: ", err)
			}
		}

		c.Write(buff[:count])
	}

	c.Wait()
	return 0
}

func (p *pipeCommand) onClose() {
	// odds are run() is currently blocking in the p.reader.Read(). Close stdin to
	// allow it to return gracefully
	logg.Debug("Tunnel closed")
	os.Stdin.Close()
}

func (p *pipeCommand) onError(err util.APIError) {
	if err.Code() != util.OtherError {
		logg.FailWithAPIError(err)
	} else if !p.sentEOF {
		logg.Fatal("Lost connection: ", err)
	}
	p.tunnel.Close()
}

// OnMessage -- Handles incoming WebSocket messages
func (p *pipeCommand) onData(data []byte) {
	p.writer.Write(data)
	p.writer.Flush()
}

// PipeCommand -- Implements `ondevice pipe`
var PipeCommand = pipeCommand{
	BaseCommand: BaseCommand{
		Arguments: "<devId> <service>",
		ShortHelp: "Pipes a device's service to stdout/stdin",
		RunFn:     nil, // we're implemnting our own run() method
		LongHelp: `$ ondevice pipe <devId> <service>

Sends data from stdin to the specified service - and prints whatever it gets in
return to stdout.

This command is used internally by 'ondevice ssh' to serve as ssh's ProxyCommand.

Example:
  $ echo hello world | ondevice pipe <devId> echo
  hello world

	Sends 'hello world' to q5dkpm's 'echo' service. The echo service simply returns
	the data it gets back to the sender. Therefore the above command is equivalent
	to simply calling 'echo hello world' (as long as your device is online).
`,
	},
}
<|endoftext|>


<commit_before>package commands

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/toffanin/go-todo/utils"

	"github.com/codegangsta/cli"
)

func GetAdd() cli.Command {

	return cli.Command{
		Name:      "add",
		ShortName: "a",
		Usage:     "Adds a task to your todo.txt file",
		Description: `
   This command can be used to add the specified task to your todo.txt file on
   its own line.

   Project and content notation are optional. Quotation marks are optional too.

EXAMPLES

   Adds a simple task (quotes are optional):

	  $ todo add "Move out cardboard boxes from the garage"

   Adds tasks with a project notation (quotes are optional):

	  $ todo add "Move out cardboard boxes from the garage +cleaning"
	  $ todo add "Do a load of laundry +cleaning"
	  $ todo add "Vacuum the house +cleaning"

   Adds tasks with a context notation (quotes are optional):

	  $ todo add "Buy eggs and milk @grocery"
	  $ todo add "Buy a cake for friday's dinner party with friends @backery"

   Adds tasks with both project and context notation (quotes are optional):

	  $ todo add "Feed the kitten +BellyOfTheBeast"
	  $ todo add "Buy food with amino acid taurine @petshop +BellyOfTheBeast"
	  $ todo add "Buy huge amount of meat @butcher +BellyOfTheBeast"
	  $ todo add "Hire a bouncer to protect @kitchen cupboard from the cat +BellyOfTheBeast"
`,
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// debugging
			/*fmt.Printf("(add::Action) args (%d): %s\n", len(args), args)
			fmt.Printf("(add::Action) global flag: %s (%t)\n", "-t", c.GlobalBool("t"))*/

			// task mangler
			task := ""
			switch {
			case len(args) == 0: // no options specified

				// check incorrect usage of the command
				if c.GlobalBool("f") {
					fmt.Print("\nDetected missing option with command \"add [task]\"\n")
					fmt.Print("Usage: todo -f add [task]\"\n\n")
					cli.ShowCommandHelp(c, "add")
					return
				}

				// invoke interactive input
				task = utils.InteractiveInput("Add:")

			default: // collect all the arguments into a single string
				task = strings.Join(args[0:], " ")
			}

			// TODO: validating input as a task

			// sanitize input
			task = utils.SanitizeInput(task)

			// honour the -t global flag
			if c.GlobalBool("t") {
				date := time.Now().Format("2006-01-02 ")
				task = date + task
			}

			// save the new task
			addAction(task)
		},
	}
}

func GetAddm() cli.Command {

	return cli.Command{
		Name:      "addm",
		ShortName: "",
		Usage:     "Adds multiple tasks to your todo.txt file",
		Description: `
   This command can be used to add the specified tasks to your todo.txt file.

   Project and content notation are optional. Quotation marks are optional too.

EXAMPLES

   Adds some simple tasks (quotes are optional):

	  $ todo addm "Buy eggs and milk @grocery"
	  $ > Buy a cake for Friday's dinner party with friends @backery
`,
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// check incorrect usage of the command
			if len(args) == 0 {
				fmt.Print("\nDetected missing option with command \"addm [task]\"\n")
				fmt.Print("Usage: todo addm [task]\"\n\n")
				cli.ShowCommandHelp(c, "addm")
				return
			}

			// collect all the arguments into a single string
			firstTask := strings.Join(args[0:], " ")

			// invoke interactive input
			secondTask := utils.InteractiveInput(">")

			// TODO: validating input as a task

			// sanitize tasks
			firstTask = utils.SanitizeInput(firstTask)
			secondTask = utils.SanitizeInput(secondTask)

			// honour the -t global flag
			if c.GlobalBool("t") {
				date := time.Now().Format("2006-01-02 ")
				firstTask = date + firstTask
				secondTask = date + secondTask
			}

			// save task
			addAction(firstTask)
			addAction(secondTask)
		},
	}
}

// Adds a task to a todo.txt file.
func addAction(task string) {

	todoFile := utils.GetSetting("TODO_FILE")
	todoDir := path.Dir(todoFile)
	//fmt.Printf("*DIR: %s\n", todoDir)

	// validate dir path
	finfo, err := os.Stat(todoDir)
	//fmt.Printf("FileMode IsDir: %t\n", finfo.Mode().IsDir())
	//fmt.Printf("FileMode IsRegular: %t\n", finfo.Mode().IsRegular())
	//fmt.Printf("FileMode Perm: %t\n", finfo.Mode().Perm())
	if err != nil {
		// path doesn't exists
		if os.IsNotExist(err) {
			fmt.Printf("DIR:%s doesn't exists.\n", todoDir)
			fmt.Printf("Please create the missing directory with: `mkdir -p %s`.\n\n", todoDir)
			os.Exit(1)
		}

		// brace yourself: unknown errors are coming
		utils.Check(err)
	}

	// path exists but is not a directory
	if !finfo.IsDir() {
		fmt.Printf("DIR:%s is not a directory.\n", todoDir)
		fmt.Println("Please fix your todo.cfg file and be sure to specify a directory with an absolute path.")
		os.Exit(1)
	}

	// determine the number of tasks in todo.txt
	// TODO: with NewReadWriter the code should be more compact
	//       buf := bufio.NewReadWriter(bufio.NewReader(r), bufio.NewWriter(w))
	fd, err := os.OpenFile(todoFile, os.O_RDONLY|os.O_CREATE, 0600)
	if err != nil {
		// file isn't readable
		if os.IsPermission(err) {
			fmt.Printf("%s or %s don't have correct permission bits.\n", todoFile, todoDir)
			fmt.Println("Please fix the directory / file permissions")
			os.Exit(1)
		}

		// brace yourself: unknown errors are coming
		utils.Check(err)
	}

	scanner := bufio.NewScanner(fd)
	ntasks := 1
	for scanner.Scan() {
		ntasks++
	}
	if err := scanner.Err(); err != nil {
		fd.Close()
		utils.Check(err)
	}
	//fmt.Printf("n. lines: %d\n", ntasks)
	err = fd.Close()
	utils.Check(err)

	// Open todo.txt in append mode only
	fd, err = os.OpenFile(todoFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	utils.Check(err)
	defer fd.Close()

	// use buffered I/O
	writer := bufio.NewWriter(fd)

	// add the task to todo.txt
	_, err = writer.WriteString(task + "\n")
	utils.Check(err)
	err = writer.Flush()
	utils.Check(err)

	// print summary
	fmt.Printf("%d: %s\n", ntasks, task)
	fmt.Printf("TODO: %d added\n", ntasks)
}
<commit_msg>fix typos<commit_after>package commands

import (
	"bufio"
	"fmt"
	"os"
	"path"
	"strings"
	"time"

	"github.com/toffanin/go-todo/utils"

	"github.com/codegangsta/cli"
)

func GetAdd() cli.Command {

	return cli.Command{
		Name:      "add",
		ShortName: "a",
		Usage:     "Adds a task to your todo.txt file",
		Description: `
   This command can be used to add the specified task to your todo.txt file on
   its own line.

   Project and content notation are optional. Quotation marks are optional too.

EXAMPLES

   Adds a simple task (quotes are optional):

	  $ todo add "Move out cardboard boxes from the garage"

   Adds tasks with a project notation (quotes are optional):

	  $ todo add "Move out cardboard boxes from the garage +cleaning"
	  $ todo add "Do a load of laundry +cleaning"
	  $ todo add "Vacuum the house +cleaning"

   Adds tasks with a context notation (quotes are optional):

	  $ todo add "Buy eggs and milk @grocery"
	  $ todo add "Buy a cake for Friday's dinner party with friends @backery"

   Adds tasks with both project and context notation (quotes are optional):

	  $ todo add "Feed the kitten +BellyOfTheBeast"
	  $ todo add "Buy food with amino acid taurine @petshop +BellyOfTheBeast"
	  $ todo add "Buy huge amount of meat @butcher +BellyOfTheBeast"
	  $ todo add "Hire a bouncer to protect @kitchen cupboard from the cat +BellyOfTheBeast"
`,
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// debugging
			/*fmt.Printf("(add::Action) args (%d): %s\n", len(args), args)
			fmt.Printf("(add::Action) global flag: %s (%t)\n", "-t", c.GlobalBool("t"))*/

			// task mangler
			task := ""
			switch {
			case len(args) == 0: // no options specified

				// check incorrect usage of the command
				if c.GlobalBool("f") {
					fmt.Print("\nDetected missing option with command \"add [task]\"\n")
					fmt.Print("Usage: todo -f add [task]\"\n\n")
					cli.ShowCommandHelp(c, "add")
					return
				}

				// invoke interactive input
				task = utils.InteractiveInput("Add:")

			default: // collect all the arguments into a single string
				task = strings.Join(args[0:], " ")
			}

			// TODO: validating input as a task

			// sanitize input
			task = utils.SanitizeInput(task)

			// honour the -t global flag
			if c.GlobalBool("t") {
				date := time.Now().Format("2006-01-02 ")
				task = date + task
			}

			// save the new task
			addAction(task)
		},
	}
}

func GetAddm() cli.Command {

	return cli.Command{
		Name:      "addm",
		ShortName: "",
		Usage:     "Adds multiple tasks to your todo.txt file",
		Description: `
   This command can be used to add the specified tasks to your todo.txt file.

   Project and content notation are optional. Quotation marks are optional too.

EXAMPLES

   Adds some simple tasks (quotes are optional):

	  $ todo addm "Buy eggs and milk @grocery"
	  $ > Buy a cake for Friday's dinner party with friends @backery
`,
		Action: func(c *cli.Context) {
			// collect all the user-submitted arguments in an array
			args := c.Args()

			// check incorrect usage of the command
			if len(args) == 0 {
				fmt.Print("\nDetected missing option with command \"addm [task]\"\n")
				fmt.Print("Usage: todo addm [task]\"\n\n")
				cli.ShowCommandHelp(c, "addm")
				return
			}

			// collect all the arguments into a single string
			firstTask := strings.Join(args[0:], " ")

			// invoke interactive input
			secondTask := utils.InteractiveInput(">")

			// TODO: validating input as a task

			// sanitize tasks
			firstTask = utils.SanitizeInput(firstTask)
			secondTask = utils.SanitizeInput(secondTask)

			// honour the -t global flag
			if c.GlobalBool("t") {
				date := time.Now().Format("2006-01-02 ")
				firstTask = date + firstTask
				secondTask = date + secondTask
			}

			// save task
			addAction(firstTask)
			addAction(secondTask)
		},
	}
}

// Adds a task to a todo.txt file.
func addAction(task string) {

	todoFile := utils.GetSetting("TODO_FILE")
	todoDir := path.Dir(todoFile)
	//fmt.Printf("*DIR: %s\n", todoDir)

	// validate dir path
	finfo, err := os.Stat(todoDir)
	//fmt.Printf("FileMode IsDir: %t\n", finfo.Mode().IsDir())
	//fmt.Printf("FileMode IsRegular: %t\n", finfo.Mode().IsRegular())
	//fmt.Printf("FileMode Perm: %t\n", finfo.Mode().Perm())
	if err != nil {
		// path doesn't exists
		if os.IsNotExist(err) {
			fmt.Printf("DIR:%s doesn't exists.\n", todoDir)
			fmt.Printf("Please create the missing directory with: `mkdir -p %s`.\n\n", todoDir)
			os.Exit(1)
		}

		// brace yourself: unknown errors are coming
		utils.Check(err)
	}

	// path exists but is not a directory
	if !finfo.IsDir() {
		fmt.Printf("DIR:%s is not a directory.\n", todoDir)
		fmt.Println("Please fix your todo.cfg file and be sure to specify a directory with an absolute path.")
		os.Exit(1)
	}

	// determine the number of tasks in todo.txt
	// TODO: with NewReadWriter the code should be more compact
	//       buf := bufio.NewReadWriter(bufio.NewReader(r), bufio.NewWriter(w))
	fd, err := os.OpenFile(todoFile, os.O_RDONLY|os.O_CREATE, 0600)
	if err != nil {
		// file isn't readable
		if os.IsPermission(err) {
			fmt.Printf("%s or %s don't have correct permission bits.\n", todoFile, todoDir)
			fmt.Println("Please fix the directory / file permissions")
			os.Exit(1)
		}

		// brace yourself: unknown errors are coming
		utils.Check(err)
	}

	scanner := bufio.NewScanner(fd)
	ntasks := 1
	for scanner.Scan() {
		ntasks++
	}
	if err := scanner.Err(); err != nil {
		fd.Close()
		utils.Check(err)
	}
	//fmt.Printf("n. lines: %d\n", ntasks)
	err = fd.Close()
	utils.Check(err)

	// Open todo.txt in append mode only
	fd, err = os.OpenFile(todoFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	utils.Check(err)
	defer fd.Close()

	// use buffered I/O
	writer := bufio.NewWriter(fd)

	// add the task to todo.txt
	_, err = writer.WriteString(task + "\n")
	utils.Check(err)
	err = writer.Flush()
	utils.Check(err)

	// print summary
	fmt.Printf("%d: %s\n", ntasks, task)
	fmt.Printf("TODO: %d added\n", ntasks)
}
<|endoftext|>


<commit_before>/*
 * docker-machine driver for VMware's photoncontroller
 * photon-controller: http://vmware.github.io/photon-controller/
 *
 * This version is currently just a stub.
 */

package photoncontroller

import (
	"fmt"
	"time"
	"net"
	"strconv"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/engine"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/state"
	"github.com/vmware/photon-controller-go-sdk/photon"
)

const (
	driverName = "photoncontroller"
	defaultEndpoint = "https://192.0.2.2"
	defaultAuthEndpoint = "10.146.64.236"
	defaultSSHUser = "docker"
	defaultSSHKeyPath = "/ssh/id_rsa"
	maxRetries = 150
)

type Driver struct {
	*drivers.BaseDriver
	Name           string
	Project        string
	VMFlavor       string
	DiskFlavor     string
	DiskName       string
	Image          string
	VMId           string
	PhotonEndpoint string
}

type NotLoadable struct {
	Name string
}

func (e NotLoadable) Error() string {
	return fmt.Sprintf("Driver %q not found. Do you have the plugin binary accessible in your PATH?", e.Name)
}

func NewDriver(hostName, storePath string) *Driver {
	return &Driver{
		Project:		"00000000-0000-0000-0000-000000000000",
		VMFlavor:       "VMFlavor",
		DiskFlavor:     "DiskFlavor",
		DiskName:       "DiskName",
		Image:          "00000000-0000-0000-0000-000000000000",
		PhotonEndpoint: defaultEndpoint,
		BaseDriver:     &drivers.BaseDriver{
			SSHUser:	 defaultSSHUser,
			MachineName: hostName,
			StorePath:   storePath,
			SSHPort:     22,
			SSHKeyPath:  defaultSSHKeyPath,
		},
	}
}

func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:   "photon-endpoint",
			Usage:  "Photon Controller endpoint in format: http://<ip address>:<port>",
			EnvVar: "PHOTON_ENDPOINT",
		},
		mcnflag.StringFlag{
			Name:   "photon-project",
			Usage:  "Project Id",
			EnvVar: "PHOTON_PROJECT",
		},
		mcnflag.StringFlag{
			Name:   "photon-vmflavor",
			Usage:  "VM flavor name",
			EnvVar: "PHOTON_VM_FLAVOR",
		},
		mcnflag.StringFlag{
			Name:   "photon-diskflavor",
			Usage:  "Disk flavor name",
			EnvVar: "PHOTON_DISK_FLAVOR",
		},
		mcnflag.StringFlag{
			Name:   "photon-diskname",
			Usage:  "Disk name",
			EnvVar: "PHOTON_DISK_Name",
		},
		mcnflag.StringFlag{
			Name:   "photon-image",
			Usage:  "Image Id",
			EnvVar: "PHOTON_IMAGE_ID",
		},
		mcnflag.StringFlag{
			Name:   "photon-ssh-keypath",
			Usage:  "SSH key path",
			Value:  defaultSSHKeyPath,
			EnvVar: "PHOTON_SSH_KEYPATH",
		},
		mcnflag.StringFlag{
			Name:   "photon-ssh-user",
			Usage:  "SSH user",
			Value:  defaultSSHUser,
			EnvVar: "PHOTON_SSH_USER",
		},
	}
}

func (d *Driver) getClient() *photon.Client {
	return photon.NewClient(d.PhotonEndpoint, defaultAuthEndpoint, nil)
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return driverName
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, strconv.Itoa(engine.DefaultPort))), nil
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

func (d *Driver) GetIP() (string, error) {
	return d.IPAddress, nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHKeyPath() string {
	return d.SSHKeyPath
}

func (d *Driver) GetSSHPort() (int, error) {
	return d.SSHPort, nil
}

func (d *Driver) GetSSHUsername() string {
	return d.SSHUser
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Project = flags.String("photon-project")
	d.VMFlavor = flags.String("photon-vmflavor")
	d.DiskFlavor = flags.String("photon-diskflavor")
	d.DiskName = flags.String("photon-diskname")
	d.Image = flags.String("photon-image")
	d.PhotonEndpoint = flags.String("photon-endpoint")
	d.SSHKeyPath = flags.String("photon-ssh-keypath")
	d.SSHUser = flags.String("photon-ssh-user")
	d.SSHPort = 22
	d.SetSwarmConfigFromFlags(flags)

	return nil
}

func (d *Driver) Create() error {
	vmSpec := &photon.VmCreateSpec {
		Name:          d.MachineName,
		Flavor:        d.VMFlavor,
		SourceImageID: d.Image,
		AttachedDisks: []photon.AttachedDisk{
			photon.AttachedDisk{
				CapacityGB: 1,
				Flavor:     d.DiskFlavor,
				Kind:       "ephemeral-disk",
				Name:       d.DiskName,
				State:      "STARTED",
				BootDisk:   true,
			},
		},
	}
	client := d.getClient()

	// Creating VM task
	vmCreateTask, err := client.Projects.CreateVM(d.Project, vmSpec)
	if (err != nil) {
		return fmt.Errorf("Error creating VM Task: ", err)
	}

	// Waiting for create VM task completion
	vmCreateTask, err = client.Tasks.Wait(vmCreateTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error creating VM: ", err)
	}

	d.VMId = vmCreateTask.Entity.ID
	fmt.Println("VM was created with Id: ", d.VMId)

	vmStartTask, err := client.VMs.Start(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Starting VM task not completed in time: ", err)
	}

	// Waiting for start VM task completion
	vmStartTask, err = client.Tasks.Wait(vmStartTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error starting VM: ", err)
	}

	fmt.Println("VM is started.")

	ipAddress := ""
	numRetries := 0
	for numRetries < maxRetries {
		vmNetworksTask, err := client.VMs.GetNetworks(d.VMId)
		if (err != nil) {
			return fmt.Errorf("Error creating task for get VM networks: ", err)
		}

		// Waiting for get VM networks task completion
		vmNetworksTask, err = client.Tasks.Wait(vmNetworksTask.ID)
		if (err != nil) {
			return fmt.Errorf("Get VM networks taks not completed: ", err)
		}

		networkConnections := vmNetworksTask.ResourceProperties.(map[string]interface{})
		networks := networkConnections["networkConnections"].([]interface{})

		for _, nt := range networks {
			network := nt.(map[string]interface{})
			if val, ok := network["ipAddress"]; ok && val != nil {
				ipAddress = val.(string)
				break
			}
		}

		if ipAddress != "" {
			break;
		}
		numRetries++
		time.Sleep(1 * time.Second)
	}

	d.IPAddress = ipAddress
	fmt.Println("VM IP: ", d.VMId)

	return nil
}

func (d *Driver) PreCreateCheck() error {
	if d.PhotonEndpoint == "" {
		return fmt.Errorf("Photon controller endpoint is not provided.")
	}

	if d.Project == "" {
		return fmt.Errorf("Project Id is not provided.")
	}

	if d.VMFlavor == "" {
		return fmt.Errorf("VM flavor name is not provided.")
	}

	if d.DiskFlavor == "" {
		return fmt.Errorf("Disk flavor name is not provided.")
	}

	if d.DiskName == "" {
		return fmt.Errorf("Disk name is not provided.")
	}

	if d.Image == "" {
		return fmt.Errorf("Image Id is not provided.")
	}

	return nil
}

func (d *Driver) GetState() (state.State, error) {
	client := d.getClient()
	vm, err := client.VMs.Get(d.VMId)
	if (err != nil) {
		return state.Error, fmt.Errorf("Error getting VM: ", err)
	}

	if (vm.State == "STOPPED") {
		return state.Stopped, nil
	}

	if (vm.State == "STARTED") {
		return state.Running, nil
	}

	if (vm.State == "SUSPENDED") {
		return state.Paused, nil
	}

	return state.Error, nil
}

func (d *Driver) Remove() error {
	// Stop the VM before attempting delete
	d.Stop()

	client := d.getClient()
	opTask, err := client.VMs.Delete(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating delete VM Task: ", err)
	}

	// Waiting for delete VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error deleting VM: ", err)
	}

	fmt.Println("VM was deleted.")

	return nil
}

func (d *Driver) Start() error {
	client := d.getClient()
	opTask, err := client.VMs.Start(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating start VM Task: ", err)
	}

	// Waiting for start VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error starting VM: ", err)
	}

	fmt.Println("VM was started.")

	return nil
}

func (d *Driver) Stop() error {
	client := d.getClient()
	opTask, err := client.VMs.Stop(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating stop VM task: ", err)
	}

	// Waiting for stop VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error stopping VM: ", err)
	}

	fmt.Println("VM was stopped.")

	return nil
}

func (d *Driver) Restart() error {
	client := d.getClient()
	opTask, err := client.VMs.Restart(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating restart VM task: ", err)
	}

	// Waiting for restart VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error restarting VM: ", err)
	}

	fmt.Println("VM was restarted.")

	return nil
}

func (d *Driver) Kill() error {
	return d.Stop()
}
<commit_msg>Photon Controller Driver: Fixed IP retrieval for Machine Signed-off-by: Fatima Ijaz <fijaz@vmware.com><commit_after>/*
 * docker-machine driver for VMware's photoncontroller
 * photon-controller: http://vmware.github.io/photon-controller/
 *
 * This version is currently just a stub.
 */

package photoncontroller

import (
	"fmt"
	"time"
	"net"
	"strconv"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/engine"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/state"
	"github.com/vmware/photon-controller-go-sdk/photon"
)

const (
	driverName = "photoncontroller"
	defaultEndpoint = "https://192.0.2.2"
	defaultAuthEndpoint = "10.146.64.236"
	defaultSSHUser = "docker"
	defaultSSHKeyPath = "/ssh/id_rsa"
	maxRetries = 150
)

type Driver struct {
	*drivers.BaseDriver
	Name           string
	Project        string
	VMFlavor       string
	DiskFlavor     string
	DiskName       string
	Image          string
	VMId           string
	PhotonEndpoint string
}

type NotLoadable struct {
	Name string
}

func (e NotLoadable) Error() string {
	return fmt.Sprintf("Driver %q not found. Do you have the plugin binary accessible in your PATH?", e.Name)
}

func NewDriver(hostName, storePath string) *Driver {
	return &Driver{
		Project:		"00000000-0000-0000-0000-000000000000",
		VMFlavor:       "VMFlavor",
		DiskFlavor:     "DiskFlavor",
		DiskName:       "DiskName",
		Image:          "00000000-0000-0000-0000-000000000000",
		PhotonEndpoint: defaultEndpoint,
		BaseDriver:     &drivers.BaseDriver{
			SSHUser:	 defaultSSHUser,
			MachineName: hostName,
			StorePath:   storePath,
			SSHPort:     22,
			SSHKeyPath:  defaultSSHKeyPath,
		},
	}
}

func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:   "photon-endpoint",
			Usage:  "Photon Controller endpoint in format: http://<ip address>:<port>",
			EnvVar: "PHOTON_ENDPOINT",
		},
		mcnflag.StringFlag{
			Name:   "photon-project",
			Usage:  "Project Id",
			EnvVar: "PHOTON_PROJECT",
		},
		mcnflag.StringFlag{
			Name:   "photon-vmflavor",
			Usage:  "VM flavor name",
			EnvVar: "PHOTON_VM_FLAVOR",
		},
		mcnflag.StringFlag{
			Name:   "photon-diskflavor",
			Usage:  "Disk flavor name",
			EnvVar: "PHOTON_DISK_FLAVOR",
		},
		mcnflag.StringFlag{
			Name:   "photon-diskname",
			Usage:  "Disk name",
			EnvVar: "PHOTON_DISK_Name",
		},
		mcnflag.StringFlag{
			Name:   "photon-image",
			Usage:  "Image Id",
			EnvVar: "PHOTON_IMAGE_ID",
		},
		mcnflag.StringFlag{
			Name:   "photon-ssh-keypath",
			Usage:  "SSH key path",
			Value:  defaultSSHKeyPath,
			EnvVar: "PHOTON_SSH_KEYPATH",
		},
		mcnflag.StringFlag{
			Name:   "photon-ssh-user",
			Usage:  "SSH user",
			Value:  defaultSSHUser,
			EnvVar: "PHOTON_SSH_USER",
		},
	}
}

func (d *Driver) getClient() *photon.Client {
	return photon.NewClient(d.PhotonEndpoint, defaultAuthEndpoint, nil)
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return driverName
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}
	if ip == "" {
		return "", nil
	}
	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, strconv.Itoa(engine.DefaultPort))), nil
}

func (d *Driver) GetMachineName() string {
	return d.MachineName
}

func (d *Driver) GetIP() (string, error) {
	return d.IPAddress, nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

func (d *Driver) GetSSHKeyPath() string {
	return d.SSHKeyPath
}

func (d *Driver) GetSSHPort() (int, error) {
	return d.SSHPort, nil
}

func (d *Driver) GetSSHUsername() string {
	return d.SSHUser
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Project = flags.String("photon-project")
	d.VMFlavor = flags.String("photon-vmflavor")
	d.DiskFlavor = flags.String("photon-diskflavor")
	d.DiskName = flags.String("photon-diskname")
	d.Image = flags.String("photon-image")
	d.PhotonEndpoint = flags.String("photon-endpoint")
	d.SSHKeyPath = flags.String("photon-ssh-keypath")
	d.SSHUser = flags.String("photon-ssh-user")
	d.SSHPort = 22
	d.SetSwarmConfigFromFlags(flags)

	return nil
}

func (d *Driver) Create() error {
	vmSpec := &photon.VmCreateSpec {
		Name:          d.MachineName,
		Flavor:        d.VMFlavor,
		SourceImageID: d.Image,
		AttachedDisks: []photon.AttachedDisk{
			photon.AttachedDisk{
				CapacityGB: 1,
				Flavor:     d.DiskFlavor,
				Kind:       "ephemeral-disk",
				Name:       d.DiskName,
				State:      "STARTED",
				BootDisk:   true,
			},
		},
	}
	client := d.getClient()

	// Creating VM task
	vmCreateTask, err := client.Projects.CreateVM(d.Project, vmSpec)
	if (err != nil) {
		return fmt.Errorf("Error creating VM Task: ", err)
	}

	// Waiting for create VM task completion
	vmCreateTask, err = client.Tasks.Wait(vmCreateTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error creating VM: ", err)
	}

	d.VMId = vmCreateTask.Entity.ID
	fmt.Println("VM was created with Id: ", d.VMId)

	vmStartTask, err := client.VMs.Start(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Starting VM task not completed in time: ", err)
	}

	// Waiting for start VM task completion
	vmStartTask, err = client.Tasks.Wait(vmStartTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error starting VM: ", err)
	}

	fmt.Println("VM is started.")

	if err = d.RetrieveMachineIP(client); err != nil {
		return err
	}

	fmt.Println("VM IP: ", d.IPAddress)

	return nil
}

func (d *Driver) PreCreateCheck() error {
	if d.PhotonEndpoint == "" {
		return fmt.Errorf("Photon controller endpoint was not specified. Use --photon-endpoint option to specify it.")
	}

	if d.Project == "" {
		return fmt.Errorf("Project Id was not provided. Use --photon-project option to specify it.")
	}

	if d.VMFlavor == "" {
		return fmt.Errorf("VM flavor name was not provided. Use --photon-vmflavor option to specify it.")
	}

	if d.DiskFlavor == "" {
		return fmt.Errorf("Disk flavor name was not provided. Use --photon-diskflavor option to specify it.")
	}

	if d.DiskName == "" {
		return fmt.Errorf("Disk name was not provided. Use --photon-diskname option to specify it.")
	}

	if d.Image == "" {
		return fmt.Errorf("Image Id was not provided. Use --photon-image option to specify it.")
	}

	return nil
}

func (d *Driver) GetState() (state.State, error) {
	client := d.getClient()
	vm, err := client.VMs.Get(d.VMId)
	if (err != nil) {
		return state.Error, fmt.Errorf("Error getting VM: ", err)
	}

	if (vm.State == "STOPPED") {
		return state.Stopped, nil
	}

	if (vm.State == "STARTED") {
		return state.Running, nil
	}

	if (vm.State == "SUSPENDED") {
		return state.Paused, nil
	}

	return state.Error, nil
}

func (d *Driver) Remove() error {
	// Stop the VM before attempting delete
	d.Stop()

	client := d.getClient()
	opTask, err := client.VMs.Delete(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating delete VM Task: ", err)
	}

	// Waiting for delete VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error deleting VM: ", err)
	}

	fmt.Println("VM was deleted.")

	return nil
}

func (d *Driver) Start() error {
	client := d.getClient()
	opTask, err := client.VMs.Start(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating start VM Task: ", err)
	}

	// Waiting for start VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error starting VM: ", err)
	}

	fmt.Println("VM was started.")

	return nil
}

func (d *Driver) Stop() error {
	client := d.getClient()
	opTask, err := client.VMs.Stop(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating stop VM task: ", err)
	}

	// Waiting for stop VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error stopping VM: ", err)
	}

	fmt.Println("VM was stopped.")

	return nil
}

func (d *Driver) Restart() error {
	client := d.getClient()
	opTask, err := client.VMs.Restart(d.VMId)
	if (err != nil) {
		return fmt.Errorf("Error creating restart VM task: ", err)
	}

	// Waiting for restart VM task completion
	opTask, err = client.Tasks.Wait(opTask.ID)
	if (err != nil) {
		return fmt.Errorf("Error restarting VM: ", err)
	}

	fmt.Println("VM was restarted.")

	return nil
}

func (d *Driver) Kill() error {
	return d.Stop()
}

func (d *Driver) RetrieveMachineIP(client *photon.Client) error {
	d.IPAddress = ""
	numRetries := 0
	for numRetries < maxRetries {
		vmNetworksTask, err := client.VMs.GetNetworks(d.VMId)
		if (err != nil) {
			return fmt.Errorf("Error creating task for get VM networks: ", err)
		}

		// Waiting for get VM networks task completion
		vmNetworksTask, err = client.Tasks.Wait(vmNetworksTask.ID)
		if (err != nil) {
			return fmt.Errorf("Get VM networks taks not completed: ", err)
		}

		networkConnections := vmNetworksTask.ResourceProperties.(map[string]interface{})
		networks := networkConnections["networkConnections"].([]interface{})

		for _, nt := range networks {
			network := nt.(map[string]interface{})
			networkValue, ok := network["network"]
			if !ok || networkValue == nil || networkValue.(string) == "" {
				continue
			}

			ipAddressValue, ok := network["ipAddress"]
			if !ok || ipAddressValue == nil || ipAddressValue.(string) == "" {
				continue
			}

			d.IPAddress = ipAddressValue.(string)
			break
		}

		if d.IPAddress != "" {
			break;
		}
		numRetries++
		time.Sleep(1 * time.Second)
	}

	if d.IPAddress == "" {
		return fmt.Errorf("Fail to retrieve VM IP.")
	}

	return nil
}
<|endoftext|>


<commit_before>// +build windows

package windows

import (
	"os"
	"syscall"
	"unsafe"
)

// ref. https://github.com/mackerelio/mackerel-agent/pull/134

/*
//#include <pdh.h>
typedef unsigned long DWORD;
// Union specialization for double values
typedef struct _PDH_FMT_COUNTERVALUE_DOUBLE {
	DWORD  CStatus;
	double DoubleValue;
} PDH_FMT_COUNTERVALUE_DOUBLE;
*/
import "C"

// SYSTEM_INFO XXX
type SYSTEM_INFO struct {
	ProcessorArchitecture     uint16
	PageSize                  uint32
	MinimumApplicationAddress *byte
	MaximumApplicationAddress *byte
	ActiveProcessorMask       *byte
	NumberOfProcessors        uint32
	ProcessorType             uint32
	AllocationGranularity     uint32
	ProcessorLevel            uint16
	ProcessorRevision         uint16
}

// MEMORY_STATUS_EX XXX
type MEMORY_STATUS_EX struct {
	Length               uint32
	MemoryLoad           uint32
	TotalPhys            uint64
	AvailPhys            uint64
	TotalPageFile        uint64
	AvailPageFile        uint64
	TotalVirtual         uint64
	AvailVirtual         uint64
	AvailExtendedVirtual uint64
}

// PDH_FMT_COUNTERVALUE_DOUBLE XXX
type PDH_FMT_COUNTERVALUE_DOUBLE struct {
	CStatus     uint32
	DoubleValue float64
}

// windows system const
const (
	ERROR_SUCCESS        = 0
	ERROR_FILE_NOT_FOUND = 2
	DRIVE_REMOVABLE      = 2
	DRIVE_FIXED          = 3
	HKEY_LOCAL_MACHINE   = 0x80000002
	RRF_RT_REG_SZ        = 0x00000002
	RRF_RT_REG_DWORD     = 0x00000010
	PDH_FMT_DOUBLE       = 0x00000200
	PDH_INVALID_DATA     = 0xc0000bc6
	PDH_INVALID_HANDLE   = 0xC0000bbc
	PDH_NO_DATA          = 0x800007d5
)

// windows procs
var (
	modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
	modpdh      = syscall.NewLazyDLL("pdh.dll")

	RegGetValue                 = modadvapi32.NewProc("RegGetValueW")
	GetSystemInfo               = modkernel32.NewProc("GetSystemInfo")
	GetDiskFreeSpaceEx          = modkernel32.NewProc("GetDiskFreeSpaceExW")
	GetLogicalDriveStrings      = modkernel32.NewProc("GetLogicalDriveStringsW")
	GetDriveType                = modkernel32.NewProc("GetDriveTypeW")
	QueryDosDevice              = modkernel32.NewProc("QueryDosDeviceW")
	GetVolumeInformationW       = modkernel32.NewProc("GetVolumeInformationW")
	GlobalMemoryStatusEx        = modkernel32.NewProc("GlobalMemoryStatusEx")
	GetLastError                = modkernel32.NewProc("GetLastError")
	MultiByteToWideChar         = modkernel32.NewProc("MultiByteToWideChar")
	PdhOpenQuery                = modpdh.NewProc("PdhOpenQuery")
	PdhAddCounter               = modpdh.NewProc("PdhAddCounterW")
	PdhCollectQueryData         = modpdh.NewProc("PdhCollectQueryData")
	PdhGetFormattedCounterValue = modpdh.NewProc("PdhGetFormattedCounterValue")
	PdhCloseQuery               = modpdh.NewProc("PdhCloseQuery")
)

// RegGetInt XXX
func RegGetInt(hKey uint32, subKey string, value string) (uint32, uintptr, error) {
	var num, numlen uint32
	numlen = 4
	ret, _, err := RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_DWORD),
		0,
		uintptr(unsafe.Pointer(&num)),
		uintptr(unsafe.Pointer(&numlen)))
	if ret != ERROR_SUCCESS {
		return 0, ret, err
	}

	return num, ret, nil
}

// RegGetString XXX
func RegGetString(hKey uint32, subKey string, value string) (string, uintptr, error) {
	var bufLen uint32
	ret, _, err := RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_SZ),
		0,
		0,
		uintptr(unsafe.Pointer(&bufLen)))
	if ret != ERROR_SUCCESS {
		return "", ret, err
	}
	if bufLen == 0 {
		return "", ret, nil
	}

	buf := make([]uint16, bufLen)
	ret, _, err = RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_SZ),
		0,
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(&bufLen)))
	if ret != ERROR_SUCCESS {
		return "", ret, err
	}

	return syscall.UTF16ToString(buf), ret, nil
}

// CounterInfo XXX
type CounterInfo struct {
	PostName    string
	CounterName string
	Counter     syscall.Handle
}

// CreateQuery XXX
func CreateQuery() (syscall.Handle, error) {
	var query syscall.Handle
	r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
	if r != 0 {
		return 0, err
	}
	return query, nil
}

// CreateCounter XXX
func CreateCounter(query syscall.Handle, k, v string) (*CounterInfo, error) {
	var counter syscall.Handle
	r, _, err := PdhAddCounter.Call(
		uintptr(query),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))),
		0,
		uintptr(unsafe.Pointer(&counter)))
	if r != 0 {
		return nil, err
	}
	return &CounterInfo{
		PostName:    k,
		CounterName: v,
		Counter:     counter,
	}, nil
}

// GetCounterValue get counter value from handle
func GetCounterValue(counter syscall.Handle) (float64, error) {
	var value C.PDH_FMT_COUNTERVALUE_DOUBLE
	r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
	if r != 0 && r != PDH_INVALID_DATA {
		return 0.0, err
	}
	return float64(value.DoubleValue), nil
}

// GetAdapterList XXX
func GetAdapterList() (*syscall.IpAdapterInfo, error) {
	b := make([]byte, 1000)
	l := uint32(len(b))
	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
	err := syscall.GetAdaptersInfo(a, &l)
	if err == syscall.ERROR_BUFFER_OVERFLOW {
		b = make([]byte, l)
		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
		err = syscall.GetAdaptersInfo(a, &l)
	}
	if err != nil {
		return nil, os.NewSyscallError("GetAdaptersInfo", err)
	}
	return a, nil
}

// BytePtrToString XXX
func BytePtrToString(p *uint8) string {
	a := (*[10000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	return string(a[:i])
}

const (
	CP_ACP = 0
)

func AnsiBytePtrToString(p *uint8) (string, error) {
	a := (*[10000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	n, _, _ := MultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(p)), uintptr(i), uintptr(0), 0)
	if n <= 0 {
		return "", syscall.GetLastError()
	}
	us := make([]uint16, n)
	r, _, _ := MultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(p)), uintptr(i), uintptr(unsafe.Pointer(&us[0])), n)
	if r == 0 {
		return "", syscall.GetLastError()
	}
	return syscall.UTF16ToString(us), nil
}
<commit_msg>remove unused struct PDH_FMT_COUNTERVALUE_DOUBLE<commit_after>// +build windows

package windows

import (
	"os"
	"syscall"
	"unsafe"
)

// ref. https://github.com/mackerelio/mackerel-agent/pull/134

/*
//#include <pdh.h>
typedef unsigned long DWORD;
// Union specialization for double values
typedef struct _PDH_FMT_COUNTERVALUE_DOUBLE {
	DWORD  CStatus;
	double DoubleValue;
} PDH_FMT_COUNTERVALUE_DOUBLE;
*/
import "C"

// SYSTEM_INFO XXX
type SYSTEM_INFO struct {
	ProcessorArchitecture     uint16
	PageSize                  uint32
	MinimumApplicationAddress *byte
	MaximumApplicationAddress *byte
	ActiveProcessorMask       *byte
	NumberOfProcessors        uint32
	ProcessorType             uint32
	AllocationGranularity     uint32
	ProcessorLevel            uint16
	ProcessorRevision         uint16
}

// MEMORY_STATUS_EX XXX
type MEMORY_STATUS_EX struct {
	Length               uint32
	MemoryLoad           uint32
	TotalPhys            uint64
	AvailPhys            uint64
	TotalPageFile        uint64
	AvailPageFile        uint64
	TotalVirtual         uint64
	AvailVirtual         uint64
	AvailExtendedVirtual uint64
}

// windows system const
const (
	ERROR_SUCCESS        = 0
	ERROR_FILE_NOT_FOUND = 2
	DRIVE_REMOVABLE      = 2
	DRIVE_FIXED          = 3
	HKEY_LOCAL_MACHINE   = 0x80000002
	RRF_RT_REG_SZ        = 0x00000002
	RRF_RT_REG_DWORD     = 0x00000010
	PDH_FMT_DOUBLE       = 0x00000200
	PDH_INVALID_DATA     = 0xc0000bc6
	PDH_INVALID_HANDLE   = 0xC0000bbc
	PDH_NO_DATA          = 0x800007d5
)

// windows procs
var (
	modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
	modpdh      = syscall.NewLazyDLL("pdh.dll")

	RegGetValue                 = modadvapi32.NewProc("RegGetValueW")
	GetSystemInfo               = modkernel32.NewProc("GetSystemInfo")
	GetDiskFreeSpaceEx          = modkernel32.NewProc("GetDiskFreeSpaceExW")
	GetLogicalDriveStrings      = modkernel32.NewProc("GetLogicalDriveStringsW")
	GetDriveType                = modkernel32.NewProc("GetDriveTypeW")
	QueryDosDevice              = modkernel32.NewProc("QueryDosDeviceW")
	GetVolumeInformationW       = modkernel32.NewProc("GetVolumeInformationW")
	GlobalMemoryStatusEx        = modkernel32.NewProc("GlobalMemoryStatusEx")
	GetLastError                = modkernel32.NewProc("GetLastError")
	MultiByteToWideChar         = modkernel32.NewProc("MultiByteToWideChar")
	PdhOpenQuery                = modpdh.NewProc("PdhOpenQuery")
	PdhAddCounter               = modpdh.NewProc("PdhAddCounterW")
	PdhCollectQueryData         = modpdh.NewProc("PdhCollectQueryData")
	PdhGetFormattedCounterValue = modpdh.NewProc("PdhGetFormattedCounterValue")
	PdhCloseQuery               = modpdh.NewProc("PdhCloseQuery")
)

// RegGetInt XXX
func RegGetInt(hKey uint32, subKey string, value string) (uint32, uintptr, error) {
	var num, numlen uint32
	numlen = 4
	ret, _, err := RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_DWORD),
		0,
		uintptr(unsafe.Pointer(&num)),
		uintptr(unsafe.Pointer(&numlen)))
	if ret != ERROR_SUCCESS {
		return 0, ret, err
	}

	return num, ret, nil
}

// RegGetString XXX
func RegGetString(hKey uint32, subKey string, value string) (string, uintptr, error) {
	var bufLen uint32
	ret, _, err := RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_SZ),
		0,
		0,
		uintptr(unsafe.Pointer(&bufLen)))
	if ret != ERROR_SUCCESS {
		return "", ret, err
	}
	if bufLen == 0 {
		return "", ret, nil
	}

	buf := make([]uint16, bufLen)
	ret, _, err = RegGetValue.Call(
		uintptr(hKey),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(subKey))),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(value))),
		uintptr(RRF_RT_REG_SZ),
		0,
		uintptr(unsafe.Pointer(&buf[0])),
		uintptr(unsafe.Pointer(&bufLen)))
	if ret != ERROR_SUCCESS {
		return "", ret, err
	}

	return syscall.UTF16ToString(buf), ret, nil
}

// CounterInfo XXX
type CounterInfo struct {
	PostName    string
	CounterName string
	Counter     syscall.Handle
}

// CreateQuery XXX
func CreateQuery() (syscall.Handle, error) {
	var query syscall.Handle
	r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
	if r != 0 {
		return 0, err
	}
	return query, nil
}

// CreateCounter XXX
func CreateCounter(query syscall.Handle, k, v string) (*CounterInfo, error) {
	var counter syscall.Handle
	r, _, err := PdhAddCounter.Call(
		uintptr(query),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))),
		0,
		uintptr(unsafe.Pointer(&counter)))
	if r != 0 {
		return nil, err
	}
	return &CounterInfo{
		PostName:    k,
		CounterName: v,
		Counter:     counter,
	}, nil
}

// GetCounterValue get counter value from handle
func GetCounterValue(counter syscall.Handle) (float64, error) {
	var value C.PDH_FMT_COUNTERVALUE_DOUBLE
	r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
	if r != 0 && r != PDH_INVALID_DATA {
		return 0.0, err
	}
	return float64(value.DoubleValue), nil
}

// GetAdapterList XXX
func GetAdapterList() (*syscall.IpAdapterInfo, error) {
	b := make([]byte, 1000)
	l := uint32(len(b))
	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
	err := syscall.GetAdaptersInfo(a, &l)
	if err == syscall.ERROR_BUFFER_OVERFLOW {
		b = make([]byte, l)
		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
		err = syscall.GetAdaptersInfo(a, &l)
	}
	if err != nil {
		return nil, os.NewSyscallError("GetAdaptersInfo", err)
	}
	return a, nil
}

// BytePtrToString XXX
func BytePtrToString(p *uint8) string {
	a := (*[10000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	return string(a[:i])
}

const (
	CP_ACP = 0
)

func AnsiBytePtrToString(p *uint8) (string, error) {
	a := (*[10000]uint8)(unsafe.Pointer(p))
	i := 0
	for a[i] != 0 {
		i++
	}
	n, _, _ := MultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(p)), uintptr(i), uintptr(0), 0)
	if n <= 0 {
		return "", syscall.GetLastError()
	}
	us := make([]uint16, n)
	r, _, _ := MultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(p)), uintptr(i), uintptr(unsafe.Pointer(&us[0])), n)
	if r == 0 {
		return "", syscall.GetLastError()
	}
	return syscall.UTF16ToString(us), nil
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 metric_test

import (
	"context"
	"fmt"
	"math/rand"
	"testing"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/metric/global"
	"go.opentelemetry.io/otel/metric/instrument"
	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
	"go.opentelemetry.io/otel/metric/instrument/syncint64"
	sdk "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/export"
	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
)

type benchFixture struct {
	meter       metric.Meter
	accumulator *sdk.Accumulator
	B           *testing.B
	export.AggregatorSelector
}

func newFixture(b *testing.B) *benchFixture {
	b.ReportAllocs()
	bf := &benchFixture{
		B:                  b,
		AggregatorSelector: processortest.AggregatorSelector(),
	}

	bf.accumulator = sdk.NewAccumulator(bf)
	bf.meter = sdkapi.WrapMeterImpl(bf.accumulator)
	return bf
}

func (f *benchFixture) Process(export.Accumulation) error {
	return nil
}

func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
	return f.meter
}

func (f *benchFixture) iCounter(name string) syncint64.Counter {
	ctr, err := f.meter.SyncInt64().Counter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}
func (f *benchFixture) fCounter(name string) syncfloat64.Counter {
	ctr, err := f.meter.SyncFloat64().Counter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}
func (f *benchFixture) iHistogram(name string) syncint64.Histogram {
	ctr, err := f.meter.SyncInt64().Histogram(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}
func (f *benchFixture) fHistogram(name string) syncfloat64.Histogram {
	ctr, err := f.meter.SyncFloat64().Histogram(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func makeAttrs(n int) []attribute.KeyValue {
	used := map[string]bool{}
	l := make([]attribute.KeyValue, n)
	for i := 0; i < n; i++ {
		var k string
		for {
			k = fmt.Sprint("k", rand.Intn(1000000000))
			if !used[k] {
				used[k] = true
				break
			}
		}
		l[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
	}
	return l
}

func benchmarkAttrs(b *testing.B, n int) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(n)
	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkInt64CounterAddWithAttrs_1(b *testing.B) {
	benchmarkAttrs(b, 1)
}

func BenchmarkInt64CounterAddWithAttrs_2(b *testing.B) {
	benchmarkAttrs(b, 2)
}

func BenchmarkInt64CounterAddWithAttrs_4(b *testing.B) {
	benchmarkAttrs(b, 4)
}

func BenchmarkInt64CounterAddWithAttrs_8(b *testing.B) {
	benchmarkAttrs(b, 8)
}

func BenchmarkInt64CounterAddWithAttrs_16(b *testing.B) {
	benchmarkAttrs(b, 16)
}

// Note: performance does not depend on attribute set size for the benchmarks
// below--all are benchmarked for a single attribute.

// Iterators

var benchmarkIteratorVar attribute.KeyValue

func benchmarkIterator(b *testing.B, n int) {
	attrs := attribute.NewSet(makeAttrs(n)...)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		iter := attrs.Iter()
		for iter.Next() {
			benchmarkIteratorVar = iter.Attribute()
		}
	}
}

func BenchmarkIterator_0(b *testing.B) {
	benchmarkIterator(b, 0)
}

func BenchmarkIterator_1(b *testing.B) {
	benchmarkIterator(b, 1)
}

func BenchmarkIterator_2(b *testing.B) {
	benchmarkIterator(b, 2)
}

func BenchmarkIterator_4(b *testing.B) {
	benchmarkIterator(b, 4)
}

func BenchmarkIterator_8(b *testing.B) {
	benchmarkIterator(b, 8)
}

func BenchmarkIterator_16(b *testing.B) {
	benchmarkIterator(b, 16)
}

// Counters

func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
	// Compare with BenchmarkInt64CounterAdd() to see overhead of global
	// package. This is in the SDK to avoid the API from depending on the
	// SDK.
	ctx := context.Background()
	fix := newFixture(b)

	global.SetMeterProvider(fix)

	labs := []attribute.KeyValue{attribute.String("A", "B")}

	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkInt64CounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkFloat64CounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.fCounter("float64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1.1, labs...)
	}
}

// LastValue

func BenchmarkInt64LastValueAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.iHistogram("int64.lastvalue")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, int64(i), labs...)
	}
}

func BenchmarkFloat64LastValueAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.fHistogram("float64.lastvalue")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, float64(i), labs...)
	}
}

// Histograms

func BenchmarkInt64HistogramAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.iHistogram("int64.histogram")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, int64(i), labs...)
	}
}

func BenchmarkFloat64HistogramAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.fHistogram("float64.histogram")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, float64(i), labs...)
	}
}

// Observers

func BenchmarkObserverRegistration(b *testing.B) {
	fix := newFixture(b)
	names := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		names = append(names, fmt.Sprintf("test.%d.lastvalue", i))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ctr, _ := fix.meter.AsyncInt64().Counter(names[i])
		_ = fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(context.Context) {})
	}
}

func BenchmarkGaugeObserverObservationInt64(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	ctr, _ := fix.meter.AsyncInt64().Counter("test.lastvalue")
	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
		for i := 0; i < b.N; i++ {
			ctr.Observe(ctx, (int64)(i), labs...)
		}
	})
	if err != nil {
		b.Errorf("could not register callback: %v", err)
		b.FailNow()
	}

	b.ResetTimer()

	fix.accumulator.Collect(ctx)
}

func BenchmarkGaugeObserverObservationFloat64(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	ctr, _ := fix.meter.AsyncFloat64().Counter("test.lastvalue")
	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
		for i := 0; i < b.N; i++ {
			ctr.Observe(ctx, (float64)(i), labs...)
		}
	})
	if err != nil {
		b.Errorf("could not register callback: %v", err)
		b.FailNow()
	}

	b.ResetTimer()

	fix.accumulator.Collect(ctx)
}

// BatchRecord

func benchmarkBatchRecord8Attrs(b *testing.B, numInst int) {
	const numAttrs = 8
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(numAttrs)
	var meas []syncint64.Counter

	for i := 0; i < numInst; i++ {
		meas = append(meas, fix.iCounter(fmt.Sprintf("int64.%d.sum", i)))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, ctr := range meas {
			ctr.Add(ctx, 1, labs...)
		}
	}
}

func BenchmarkBatchRecord8Attrs_1Instrument(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 1)
}

func BenchmarkBatchRecord_8Attrs_2Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 2)
}

func BenchmarkBatchRecord_8Attrs_4Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 4)
}

func BenchmarkBatchRecord_8Attrs_8Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 8)
}

// Record creation

func BenchmarkRepeatedDirectCalls(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)

	c := fix.iCounter("int64.sum")
	k := attribute.String("bench", "true")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		c.Add(ctx, 1, k)
		fix.accumulator.Collect(ctx)
	}
}
<commit_msg>Add benchmark metric test for UpDownCounter (#2655)<commit_after>// Copyright The OpenTelemetry 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 metric_test

import (
	"context"
	"fmt"
	"math/rand"
	"testing"

	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/metric/global"
	"go.opentelemetry.io/otel/metric/instrument"
	"go.opentelemetry.io/otel/metric/instrument/syncfloat64"
	"go.opentelemetry.io/otel/metric/instrument/syncint64"
	sdk "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/export"
	"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
	"go.opentelemetry.io/otel/sdk/metric/sdkapi"
)

type benchFixture struct {
	meter       metric.Meter
	accumulator *sdk.Accumulator
	B           *testing.B
	export.AggregatorSelector
}

func newFixture(b *testing.B) *benchFixture {
	b.ReportAllocs()
	bf := &benchFixture{
		B:                  b,
		AggregatorSelector: processortest.AggregatorSelector(),
	}

	bf.accumulator = sdk.NewAccumulator(bf)
	bf.meter = sdkapi.WrapMeterImpl(bf.accumulator)
	return bf
}

func (f *benchFixture) Process(export.Accumulation) error {
	return nil
}

func (f *benchFixture) Meter(_ string, _ ...metric.MeterOption) metric.Meter {
	return f.meter
}

func (f *benchFixture) iCounter(name string) syncint64.Counter {
	ctr, err := f.meter.SyncInt64().Counter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func (f *benchFixture) fCounter(name string) syncfloat64.Counter {
	ctr, err := f.meter.SyncFloat64().Counter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func (f *benchFixture) iUpDownCounter(name string) syncint64.UpDownCounter {
	ctr, err := f.meter.SyncInt64().UpDownCounter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func (f *benchFixture) fUpDownCounter(name string) syncfloat64.UpDownCounter {
	ctr, err := f.meter.SyncFloat64().UpDownCounter(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func (f *benchFixture) iHistogram(name string) syncint64.Histogram {
	ctr, err := f.meter.SyncInt64().Histogram(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func (f *benchFixture) fHistogram(name string) syncfloat64.Histogram {
	ctr, err := f.meter.SyncFloat64().Histogram(name)
	if err != nil {
		f.B.Error(err)
	}
	return ctr
}

func makeAttrs(n int) []attribute.KeyValue {
	used := map[string]bool{}
	l := make([]attribute.KeyValue, n)
	for i := 0; i < n; i++ {
		var k string
		for {
			k = fmt.Sprint("k", rand.Intn(1000000000))
			if !used[k] {
				used[k] = true
				break
			}
		}
		l[i] = attribute.String(k, fmt.Sprint("v", rand.Intn(1000000000)))
	}
	return l
}

func benchmarkAttrs(b *testing.B, n int) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(n)
	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkInt64CounterAddWithAttrs_1(b *testing.B) {
	benchmarkAttrs(b, 1)
}

func BenchmarkInt64CounterAddWithAttrs_2(b *testing.B) {
	benchmarkAttrs(b, 2)
}

func BenchmarkInt64CounterAddWithAttrs_4(b *testing.B) {
	benchmarkAttrs(b, 4)
}

func BenchmarkInt64CounterAddWithAttrs_8(b *testing.B) {
	benchmarkAttrs(b, 8)
}

func BenchmarkInt64CounterAddWithAttrs_16(b *testing.B) {
	benchmarkAttrs(b, 16)
}

// Note: performance does not depend on attribute set size for the benchmarks
// below--all are benchmarked for a single attribute.

// Iterators

var benchmarkIteratorVar attribute.KeyValue

func benchmarkIterator(b *testing.B, n int) {
	attrs := attribute.NewSet(makeAttrs(n)...)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		iter := attrs.Iter()
		for iter.Next() {
			benchmarkIteratorVar = iter.Attribute()
		}
	}
}

func BenchmarkIterator_0(b *testing.B) {
	benchmarkIterator(b, 0)
}

func BenchmarkIterator_1(b *testing.B) {
	benchmarkIterator(b, 1)
}

func BenchmarkIterator_2(b *testing.B) {
	benchmarkIterator(b, 2)
}

func BenchmarkIterator_4(b *testing.B) {
	benchmarkIterator(b, 4)
}

func BenchmarkIterator_8(b *testing.B) {
	benchmarkIterator(b, 8)
}

func BenchmarkIterator_16(b *testing.B) {
	benchmarkIterator(b, 16)
}

// Counters

func BenchmarkGlobalInt64CounterAddWithSDK(b *testing.B) {
	// Compare with BenchmarkInt64CounterAdd() to see overhead of global
	// package. This is in the SDK to avoid the API from depending on the
	// SDK.
	ctx := context.Background()
	fix := newFixture(b)

	global.SetMeterProvider(fix)

	labs := []attribute.KeyValue{attribute.String("A", "B")}

	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkInt64CounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.iCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkFloat64CounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.fCounter("float64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1.1, labs...)
	}
}

// UpDownCounter

func BenchmarkInt64UpDownCounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.iUpDownCounter("int64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1, labs...)
	}
}

func BenchmarkFloat64UpDownCounterAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	cnt := fix.fUpDownCounter("float64.sum")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		cnt.Add(ctx, 1.1, labs...)
	}
}

// LastValue

func BenchmarkInt64LastValueAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.iHistogram("int64.lastvalue")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, int64(i), labs...)
	}
}

func BenchmarkFloat64LastValueAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.fHistogram("float64.lastvalue")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, float64(i), labs...)
	}
}

// Histograms

func BenchmarkInt64HistogramAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.iHistogram("int64.histogram")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, int64(i), labs...)
	}
}

func BenchmarkFloat64HistogramAdd(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	mea := fix.fHistogram("float64.histogram")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		mea.Record(ctx, float64(i), labs...)
	}
}

// Observers

func BenchmarkObserverRegistration(b *testing.B) {
	fix := newFixture(b)
	names := make([]string, 0, b.N)
	for i := 0; i < b.N; i++ {
		names = append(names, fmt.Sprintf("test.%d.lastvalue", i))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		ctr, _ := fix.meter.AsyncInt64().Counter(names[i])
		_ = fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(context.Context) {})
	}
}

func BenchmarkGaugeObserverObservationInt64(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	ctr, _ := fix.meter.AsyncInt64().Counter("test.lastvalue")
	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
		for i := 0; i < b.N; i++ {
			ctr.Observe(ctx, (int64)(i), labs...)
		}
	})
	if err != nil {
		b.Errorf("could not register callback: %v", err)
		b.FailNow()
	}

	b.ResetTimer()

	fix.accumulator.Collect(ctx)
}

func BenchmarkGaugeObserverObservationFloat64(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(1)
	ctr, _ := fix.meter.AsyncFloat64().Counter("test.lastvalue")
	err := fix.meter.RegisterCallback([]instrument.Asynchronous{ctr}, func(ctx context.Context) {
		for i := 0; i < b.N; i++ {
			ctr.Observe(ctx, (float64)(i), labs...)
		}
	})
	if err != nil {
		b.Errorf("could not register callback: %v", err)
		b.FailNow()
	}

	b.ResetTimer()

	fix.accumulator.Collect(ctx)
}

// BatchRecord

func benchmarkBatchRecord8Attrs(b *testing.B, numInst int) {
	const numAttrs = 8
	ctx := context.Background()
	fix := newFixture(b)
	labs := makeAttrs(numAttrs)
	var meas []syncint64.Counter

	for i := 0; i < numInst; i++ {
		meas = append(meas, fix.iCounter(fmt.Sprintf("int64.%d.sum", i)))
	}

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		for _, ctr := range meas {
			ctr.Add(ctx, 1, labs...)
		}
	}
}

func BenchmarkBatchRecord8Attrs_1Instrument(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 1)
}

func BenchmarkBatchRecord_8Attrs_2Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 2)
}

func BenchmarkBatchRecord_8Attrs_4Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 4)
}

func BenchmarkBatchRecord_8Attrs_8Instruments(b *testing.B) {
	benchmarkBatchRecord8Attrs(b, 8)
}

// Record creation

func BenchmarkRepeatedDirectCalls(b *testing.B) {
	ctx := context.Background()
	fix := newFixture(b)

	c := fix.iCounter("int64.sum")
	k := attribute.String("bench", "true")

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		c.Add(ctx, 1, k)
		fix.accumulator.Collect(ctx)
	}
}
<|endoftext|>


<commit_before>package dagger

import (
	"time"

	"github.com/nsaje/dagger/structs"
)

type LwmTracker interface {
	TupleProcessor
	SentTracker
	BeforeDispatching([]*structs.Tuple)
	GetLWM() (time.Time, error)
}

type lwmTracker struct {
	upstream     map[string]time.Time
	inProcessing map[string]time.Time
}

func NewLWMTracker() *lwmTracker {
	return &lwmTracker{
		make(map[string]time.Time),
		make(map[string]time.Time),
	}
}

func (lwmT *lwmTracker) BeforeDispatching(ts []*structs.Tuple) {
	for _, t := range ts {
		lwmT.inProcessing[t.ID] = t.LWM
	}
}

func (lwmT *lwmTracker) SentSuccessfuly(compID string, t *structs.Tuple) error {
	delete(lwmT.inProcessing, t.ID)
	return nil
}

func (lwmT *lwmTracker) GetLWM() (time.Time, error) {
	min := time.Now().Add(1 << 62)
	for _, lwm := range lwmT.inProcessing {
		if lwm.Before(min) {
			min = lwm
		}
	}
	for _, lwm := range lwmT.upstream {
		if lwm.Before(min) {
			min = lwm
		}
	}
	// log.Println("[lwm_tracker]", min, lwmT.upstream, lwmT.inProcessing)
	return min, nil
}

func (lwmT *lwmTracker) ProcessTuple(t *structs.Tuple) error {
	lwmT.upstream[t.StreamID] = t.LWM
	return nil
}
<commit_msg>lwm should be monotonically increasing<commit_after>package dagger

import (
	"time"

	"github.com/nsaje/dagger/structs"
)

type LwmTracker interface {
	TupleProcessor
	SentTracker
	BeforeDispatching([]*structs.Tuple)
	GetLWM() (time.Time, error)
}

type lwmTracker struct {
	upstream     map[string]time.Time
	inProcessing map[string]time.Time
}

func NewLWMTracker() *lwmTracker {
	return &lwmTracker{
		make(map[string]time.Time),
		make(map[string]time.Time),
	}
}

func (lwmT *lwmTracker) BeforeDispatching(ts []*structs.Tuple) {
	for _, t := range ts {
		lwmT.inProcessing[t.ID] = t.LWM
	}
}

func (lwmT *lwmTracker) SentSuccessfuly(compID string, t *structs.Tuple) error {
	delete(lwmT.inProcessing, t.ID)
	return nil
}

func (lwmT *lwmTracker) GetLWM() (time.Time, error) {
	min := time.Now().Add(1 << 62)
	for _, lwm := range lwmT.inProcessing {
		if lwm.Before(min) {
			min = lwm
		}
	}
	for _, lwm := range lwmT.upstream {
		if lwm.Before(min) {
			min = lwm
		}
	}
	// log.Println("[lwm_tracker]", min, lwmT.upstream, lwmT.inProcessing)
	return min, nil
}

func (lwmT *lwmTracker) ProcessTuple(t *structs.Tuple) error {
	if t.LWM.After(lwmT.upstream[t.StreamID]) {
		lwmT.upstream[t.StreamID] = t.LWM
	}
	return nil
}
<|endoftext|>