<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/mux"
	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/daemon"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/request"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/ucred"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/version"
)

// DevLxdServer creates an http.Server capable of handling requests against the
// /dev/lxd Unix socket endpoint created inside containers.
func devLxdServer(d *Daemon) *http.Server {
	return &http.Server{
		Handler:     devLxdAPI(d),
		ConnState:   pidMapper.ConnStateHandler,
		ConnContext: request.SaveConnectionInContext,
	}
}

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse
}

var devlxdConfigGet = devLxdHandler{"/1.0/config", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") || strings.HasPrefix(k, "cloud-init.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var devlxdConfigKeyGet = devLxdHandler{"/1.0/config/{key}", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") && !strings.HasPrefix(key, "cloud-init.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var devlxdImageExport = devLxdHandler{"/1.0/images/{fingerprint}/export", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	if !shared.IsTrue(c.ExpandedConfig()["security.devlxd.images"]) {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	// Use by security checks to distinguish devlxd vs lxd APIs
	r.RemoteAddr = "@devlxd"

	resp := imageExport(d, r)
	err := resp.Render(w)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	return &devLxdResponse{"", http.StatusOK, "raw"}
}}

var devlxdMetadataGet = devLxdHandler{"/1.0/meta-data", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var devlxdEventsGet = devLxdHandler{"/1.0/events", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "config,device"
	}

	conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}
	defer conn.Close() // This ensures the go routine below is ended when this function ends.

	listener, err := d.devlxdEvents.AddListener(strconv.Itoa(c.ID()), false, conn, strings.Split(typeStr, ","), "", false)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	logger.Debugf("New container event listener for '%s': %s", project.Instance(c.Project(), c.Name()), listener.ID())
	listener.Wait(r.Context())

	return &devLxdResponse{"websocket", http.StatusOK, "websocket"}
}}

var devlxdAPIGet = devLxdHandler{"/1.0", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	location := "none"
	clustered, err := cluster.Enabled(d.db)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	if clustered {
		location = c.Location()
	}
	return okResponse(shared.Jmap{"api_version": version.APIVersion, "location": location, "instance_type": c.Type().String()}, "json")
}}

var devlxdDevicesGet = devLxdHandler{"/1.0/devices", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	return okResponse(c.ExpandedDevices(), "json")
}}

var handlers = []devLxdHandler{
	{"/", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devlxdAPIGet,
	devlxdConfigGet,
	devlxdConfigKeyGet,
	devlxdMetadataGet,
	devlxdEventsGet,
	devlxdImageExport,
	devlxdDevicesGet,
}

func hoistReq(f func(*Daemon, instance.Instance, http.ResponseWriter, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := ucred.GetConnFromContext(r.Context())
		cred, ok := pidMapper.m[conn.(*net.UnixConn)]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(cred.Pid, d.State())
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		// Access control
		rootUID := uint32(0)

		idmapset, err := c.CurrentIdmap()
		if err == nil && idmapset != nil {
			uid, _ := idmapset.ShiftIntoNs(0, 0)
			rootUID = uint32(uid)
		}

		if rootUID != cred.Uid {
			http.Error(w, "Access denied for non-root user", 401)
			return
		}

		resp := f(d, c, w, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")

			var debugLogger logger.Logger
			if daemon.Debug {
				debugLogger = logger.Logger(logger.Log)
			}

			util.WriteJSON(w, resp.content, debugLogger)
		} else if resp.ctype != "websocket" {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func devLxdAPI(d *Daemon) http.Handler {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return m
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]*unix.Ucred{}}

type ConnPidMapper struct {
	m     map[*net.UnixConn]*unix.Ucred
	mLock sync.Mutex
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		cred, err := ucred.GetCred(unixConn)
		if err != nil {
			logger.Debugf("Error getting ucred for conn %s", err)
		} else {
			m.mLock.Lock()
			m.m[unixConn] = cred
			m.mLock.Unlock()
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		m.mLock.Lock()
		delete(m.m, unixConn)
		m.mLock.Unlock()
	case http.StateClosed:
		m.mLock.Lock()
		delete(m.m, unixConn)
		m.mLock.Unlock()
	default:
		logger.Debugf("Unknown state for connection %s", state)
	}
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, s *state.State) (instance.Container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a descendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")

			projectName := project.Default
			if strings.Contains(name, "_") {
				fields := strings.SplitN(name, "_", 2)
				projectName = fields[0]
				name = fields[1]
			}

			inst, err := instance.LoadByProjectAndName(s, projectName, name)
			if err != nil {
				return nil, err
			}

			if inst.Type() != instancetype.Container {
				return nil, fmt.Errorf("Instance is not container type")
			}

			return inst.(instance.Container), nil
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile(`^PPid:\s+([0-9]+)`)
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	instances, err := instance.LoadNodeAll(s, instancetype.Container)
	if err != nil {
		return nil, err
	}

	for _, inst := range instances {
		if inst.Type() != instancetype.Container {
			continue
		}

		if !inst.IsRunning() {
			continue
		}

		initpid := inst.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return inst.(instance.Container), nil
		}
	}

	return nil, pidNotInContainerErr
}
<commit_msg>lxd/devlxd: d.devlxdEvents.AddListener usage<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/gorilla/mux"
	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/daemon"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/request"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/lxd/ucred"
	"github.com/lxc/lxd/lxd/util"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/version"
)

// DevLxdServer creates an http.Server capable of handling requests against the
// /dev/lxd Unix socket endpoint created inside containers.
func devLxdServer(d *Daemon) *http.Server {
	return &http.Server{
		Handler:     devLxdAPI(d),
		ConnState:   pidMapper.ConnStateHandler,
		ConnContext: request.SaveConnectionInContext,
	}
}

type devLxdResponse struct {
	content interface{}
	code    int
	ctype   string
}

func okResponse(ct interface{}, ctype string) *devLxdResponse {
	return &devLxdResponse{ct, http.StatusOK, ctype}
}

type devLxdHandler struct {
	path string

	/*
	 * This API will have to be changed slightly when we decide to support
	 * websocket events upgrading, but since we don't have events on the
	 * server side right now either, I went the simple route to avoid
	 * needless noise.
	 */
	f func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse
}

var devlxdConfigGet = devLxdHandler{"/1.0/config", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	filtered := []string{}
	for k := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "user.") || strings.HasPrefix(k, "cloud-init.") {
			filtered = append(filtered, fmt.Sprintf("/1.0/config/%s", k))
		}
	}
	return okResponse(filtered, "json")
}}

var devlxdConfigKeyGet = devLxdHandler{"/1.0/config/{key}", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	key := mux.Vars(r)["key"]
	if !strings.HasPrefix(key, "user.") && !strings.HasPrefix(key, "cloud-init.") {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	value, ok := c.ExpandedConfig()[key]
	if !ok {
		return &devLxdResponse{"not found", http.StatusNotFound, "raw"}
	}

	return okResponse(value, "raw")
}}

var devlxdImageExport = devLxdHandler{"/1.0/images/{fingerprint}/export", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	if !shared.IsTrue(c.ExpandedConfig()["security.devlxd.images"]) {
		return &devLxdResponse{"not authorized", http.StatusForbidden, "raw"}
	}

	// Use by security checks to distinguish devlxd vs lxd APIs
	r.RemoteAddr = "@devlxd"

	resp := imageExport(d, r)
	err := resp.Render(w)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	return &devLxdResponse{"", http.StatusOK, "raw"}
}}

var devlxdMetadataGet = devLxdHandler{"/1.0/meta-data", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	value := c.ExpandedConfig()["user.meta-data"]
	return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: %s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
}}

var devlxdEventsGet = devLxdHandler{"/1.0/events", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	typeStr := r.FormValue("type")
	if typeStr == "" {
		typeStr = "config,device"
	}

	conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}
	defer conn.Close() // This ensures the go routine below is ended when this function ends.

	listener, err := d.devlxdEvents.AddListener(c.ID(), conn, strings.Split(typeStr, ","))
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	logger.Debugf("New container event listener for '%s': %s", project.Instance(c.Project(), c.Name()), listener.ID())
	listener.Wait(r.Context())

	return &devLxdResponse{"websocket", http.StatusOK, "websocket"}
}}

var devlxdAPIGet = devLxdHandler{"/1.0", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	location := "none"
	clustered, err := cluster.Enabled(d.db)
	if err != nil {
		return &devLxdResponse{"internal server error", http.StatusInternalServerError, "raw"}
	}

	if clustered {
		location = c.Location()
	}
	return okResponse(shared.Jmap{"api_version": version.APIVersion, "location": location, "instance_type": c.Type().String()}, "json")
}}

var devlxdDevicesGet = devLxdHandler{"/1.0/devices", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
	return okResponse(c.ExpandedDevices(), "json")
}}

var handlers = []devLxdHandler{
	{"/", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
		return okResponse([]string{"/1.0"}, "json")
	}},
	devlxdAPIGet,
	devlxdConfigGet,
	devlxdConfigKeyGet,
	devlxdMetadataGet,
	devlxdEventsGet,
	devlxdImageExport,
	devlxdDevicesGet,
}

func hoistReq(f func(*Daemon, instance.Instance, http.ResponseWriter, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		conn := ucred.GetConnFromContext(r.Context())
		cred, ok := pidMapper.m[conn.(*net.UnixConn)]
		if !ok {
			http.Error(w, pidNotInContainerErr.Error(), 500)
			return
		}

		c, err := findContainerForPid(cred.Pid, d.State())
		if err != nil {
			http.Error(w, err.Error(), 500)
			return
		}

		// Access control
		rootUID := uint32(0)

		idmapset, err := c.CurrentIdmap()
		if err == nil && idmapset != nil {
			uid, _ := idmapset.ShiftIntoNs(0, 0)
			rootUID = uint32(uid)
		}

		if rootUID != cred.Uid {
			http.Error(w, "Access denied for non-root user", 401)
			return
		}

		resp := f(d, c, w, r)
		if resp.code != http.StatusOK {
			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
		} else if resp.ctype == "json" {
			w.Header().Set("Content-Type", "application/json")

			var debugLogger logger.Logger
			if daemon.Debug {
				debugLogger = logger.Logger(logger.Log)
			}

			util.WriteJSON(w, resp.content, debugLogger)
		} else if resp.ctype != "websocket" {
			w.Header().Set("Content-Type", "application/octet-stream")
			fmt.Fprintf(w, resp.content.(string))
		}
	}
}

func devLxdAPI(d *Daemon) http.Handler {
	m := mux.NewRouter()

	for _, handler := range handlers {
		m.HandleFunc(handler.path, hoistReq(handler.f, d))
	}

	return m
}

/*
 * Everything below here is the guts of the unix socket bits. Unfortunately,
 * golang's API does not make this easy. What happens is:
 *
 * 1. We install a ConnState listener on the http.Server, which does the
 *    initial unix socket credential exchange. When we get a connection started
 *    event, we use SO_PEERCRED to extract the creds for the socket.
 *
 * 2. We store a map from the connection pointer to the pid for that
 *    connection, so that once the HTTP negotiation occurrs and we get a
 *    ResponseWriter, we know (because we negotiated on the first byte) which
 *    pid the connection belogs to.
 *
 * 3. Regular HTTP negotiation and dispatch occurs via net/http.
 *
 * 4. When rendering the response via ResponseWriter, we match its underlying
 *    connection against what we stored in step (2) to figure out which container
 *    it came from.
 */

/*
 * We keep this in a global so that we can reference it from the server and
 * from our http handlers, since there appears to be no way to pass information
 * around here.
 */
var pidMapper = ConnPidMapper{m: map[*net.UnixConn]*unix.Ucred{}}

type ConnPidMapper struct {
	m     map[*net.UnixConn]*unix.Ucred
	mLock sync.Mutex
}

func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
	unixConn := conn.(*net.UnixConn)
	switch state {
	case http.StateNew:
		cred, err := ucred.GetCred(unixConn)
		if err != nil {
			logger.Debugf("Error getting ucred for conn %s", err)
		} else {
			m.mLock.Lock()
			m.m[unixConn] = cred
			m.mLock.Unlock()
		}
	case http.StateActive:
		return
	case http.StateIdle:
		return
	case http.StateHijacked:
		/*
		 * The "Hijacked" state indicates that the connection has been
		 * taken over from net/http. This is useful for things like
		 * developing websocket libraries, who want to upgrade the
		 * connection to a websocket one, and not use net/http any
		 * more. Whatever the case, we want to forget about it since we
		 * won't see it either.
		 */
		m.mLock.Lock()
		delete(m.m, unixConn)
		m.mLock.Unlock()
	case http.StateClosed:
		m.mLock.Lock()
		delete(m.m, unixConn)
		m.mLock.Unlock()
	default:
		logger.Debugf("Unknown state for connection %s", state)
	}
}

var pidNotInContainerErr = fmt.Errorf("pid not in container?")

func findContainerForPid(pid int32, s *state.State) (instance.Container, error) {
	/*
	 * Try and figure out which container a pid is in. There is probably a
	 * better way to do this. Based on rharper's initial performance
	 * metrics, looping over every container and calling newLxdContainer is
	 * expensive, so I wanted to avoid that if possible, so this happens in
	 * a two step process:
	 *
	 * 1. Walk up the process tree until you see something that looks like
	 *    an lxc monitor process and extract its name from there.
	 *
	 * 2. If this fails, it may be that someone did an `lxc exec foo bash`,
	 *    so the process isn't actually a descendant of the container's
	 *    init. In this case we just look through all the containers until
	 *    we find an init with a matching pid namespace. This is probably
	 *    uncommon, so hopefully the slowness won't hurt us.
	 */

	origpid := pid

	for pid > 1 {
		cmdline, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
		if err != nil {
			return nil, err
		}

		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
			// container names can't have spaces
			parts := strings.Split(string(cmdline), " ")
			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")

			projectName := project.Default
			if strings.Contains(name, "_") {
				fields := strings.SplitN(name, "_", 2)
				projectName = fields[0]
				name = fields[1]
			}

			inst, err := instance.LoadByProjectAndName(s, projectName, name)
			if err != nil {
				return nil, err
			}

			if inst.Type() != instancetype.Container {
				return nil, fmt.Errorf("Instance is not container type")
			}

			return inst.(instance.Container), nil
		}

		status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid))
		if err != nil {
			return nil, err
		}

		re := regexp.MustCompile(`^PPid:\s+([0-9]+)`)
		for _, line := range strings.Split(string(status), "\n") {
			m := re.FindStringSubmatch(line)
			if m != nil && len(m) > 1 {
				result, err := strconv.Atoi(m[1])
				if err != nil {
					return nil, err
				}

				pid = int32(result)
				break
			}
		}
	}

	origPidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", origpid))
	if err != nil {
		return nil, err
	}

	instances, err := instance.LoadNodeAll(s, instancetype.Container)
	if err != nil {
		return nil, err
	}

	for _, inst := range instances {
		if inst.Type() != instancetype.Container {
			continue
		}

		if !inst.IsRunning() {
			continue
		}

		initpid := inst.InitPID()
		pidNs, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/pid", initpid))
		if err != nil {
			return nil, err
		}

		if origPidNs == pidNs {
			return inst.(instance.Container), nil
		}
	}

	return nil, pidNotInContainerErr
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/lomik/elock"
	"github.com/lomik/elock/etcd"
)

const APP = "elock"
const VERSION = "0.5.0"

type Config struct {
	EtcdEndpoints []string     `json:"etcd-endpoints"`
	EtcdRoot      string       `json:"etcd-root"`
	EtcdTTL       string       `json:"etcd-default-ttl"`
	EtcdRefresh   string       `json:"etcd-default-refresh"`
	EtcdTls       etcd.TlsOpts `json:"etcd-tls"`
}

func main() {
	configFile := flag.String("config", fmt.Sprintf("/etc/%s/%s.json", APP, APP), "Config file in json format")
	printConfig := flag.Bool("config-print-default", false, "Print default config")
	nowait := flag.Bool("n", false, "Fail rather than wait")
	slots := flag.Int("s", 1, "Available slots count for lock")
	timeout := flag.Duration("w", 0, "Wait for a limited amount of time")
	version := flag.Bool("V", false, "Display version")
	debug := flag.Bool("debug", false, "Enable debug log")
	ttl := flag.Duration("ttl", 0, "Lock ttl (default from config)")
	refresh := flag.Duration("refresh", 0, "Refresh interval (default from config)")
	minLockTime := flag.Duration("min", 0, "Minimum lock time")
	list := flag.Bool("list", false, "List all active locks")
	remove := flag.Bool("rm", false, "Remove lock by path (from list)")
	zeroExit := flag.Bool("0", false, "Exit code 0 on etcd errors or lock timeout")
	runAnyway := flag.Bool("run-anyway", false, "Run the command even if the lock could not take")
	sleepBefore := flag.Duration("sleep-before", 0, "Sleep random time (from zero to selected) before lock attempt")
	quiet := flag.Bool("quiet", false, "Don't print anything")
	waitTime := flag.Duration("wait-time", 0, "Wait time for inner command to end")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s %s
Usage: %s [options] etcd_key command

`, APP, VERSION, APP)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *version {
		fmt.Println(APP, VERSION)
		return
	}

	config := &Config{
		EtcdEndpoints: []string{"http://localhost:2379"},
		EtcdRoot:      "/elock",
		EtcdTTL:       "1m",
		EtcdRefresh:   "10s",
	}

	if *printConfig {
		b, _ := json.MarshalIndent(config, "", "\t")
		fmt.Println(string(b))
		return
	}

	if *quiet {
		log.SetOutput(ioutil.Discard)
	}

	if *sleepBefore != 0 {
		rand.Seed(time.Now().UnixNano())
		sleepTime := time.Duration(rand.Int63n((*sleepBefore).Nanoseconds()))
		if *debug {
			log.Printf("sleep-before: %s", sleepTime.String())
		}
		time.Sleep(sleepTime)
	}

	exit := func(code int) {
		if *zeroExit {
			os.Exit(0)
		} else {
			os.Exit(code)
		}
	}

	fatal := func(err error) {
		if *zeroExit {
			log.Println(err)
			os.Exit(0)
		} else {
			log.Fatal(err)
		}
	}

	cfgData, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(cfgData, config)
	if err != nil {
		log.Fatal(err)
	}

	lockTLL, err := time.ParseDuration(config.EtcdTTL)
	if err != nil {
		log.Fatal(err)
	}

	lockRefresh, err := time.ParseDuration(config.EtcdRefresh)
	if err != nil {
		log.Fatal(err)
	}

	if *ttl != 0 {
		lockTLL = *ttl
	}

	if *refresh != 0 {
		lockRefresh = *refresh
	}

	args := flag.Args()

	if *list {
		records, err := elock.List(elock.Options{
			EtcdEndpoints: config.EtcdEndpoints,
			EtcdTls:       config.EtcdTls,
			Path:          config.EtcdRoot,
			Debug:         *debug,
		}, *timeout)

		if err != nil {
			fatal(err)
		}

		for _, r := range records {
			if r.ValueError != nil {
				fmt.Printf("[error] %s: %s\n", r.Path, r.ValueError.Error())
			} else {
				if r.IsDead() {
					d := time.Now().Sub(r.LastRefresh())
					d = time.Duration(int64(d.Seconds())) * time.Second
					fmt.Printf(
						" [died] %s: last refresh %s ago\n",
						r.Path,
						d.String(),
					)
				} else {
					fmt.Printf("   [ok] %s\n", r.Path)
				}
			}
		}

		return
	}

	if *remove {
		err := elock.Remove(elock.Options{
			EtcdEndpoints: config.EtcdEndpoints,
			EtcdTls:       config.EtcdTls,
			Path:          config.EtcdRoot,
			Debug:         *debug,
		}, *timeout, args)

		if err != nil {
			fatal(err)
		}

		return
	}

	if len(args) < 2 || *slots < 1 {
		flag.Usage()
		return
	}

	x, err := elock.New(elock.Options{
		EtcdEndpoints: config.EtcdEndpoints,
		EtcdTls:       config.EtcdTls,
		Path:          filepath.Join(config.EtcdRoot, args[0]),
		Slots:         *slots,
		TTL:           lockTLL,
		Refresh:       lockRefresh,
		Debug:         *debug,
		MinLockTime:   *minLockTime,
	})

	if err != nil {
		log.Fatal(err)
	}

	if *nowait {
		err = x.LockNoWait()
	} else if *timeout != 0 {
		err = x.LockTimeout(*timeout)
	} else {
		err = x.Lock()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGSTOP, syscall.SIGTERM)

	go func() {
		for s := range c {
			// sig is a ^C, handle it
			if *debug {
				log.Printf("signal received: %#v", s)
			}

			x.Unlock()
			exit(1)
		}
	}()

	if err != nil { // lock failed
		if *runAnyway { // force run
			log.Println(err)

			cmd := exec.Command(args[1], args[2:]...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Stdin = os.Stdin
			err = cmd.Run()

			if err != nil {
				log.Fatal(err)

			}

			return
		}

		fatal(err)
	}

	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Start()

	if err != nil {
		x.Unlock()
		log.Fatal(err)
	}

	x.OnExpired(func() {
		cmd.Process.Signal(syscall.SIGINT)
		timer := time.AfterFunc(*waitTime, func() {
			if *debug {
				log.Printf("lock expired, send kill to pid %d", cmd.Process.Pid)
			}
			cmd.Process.Kill()
		})
		cmd.Wait()
		timer.Stop()
	})

	err = cmd.Wait()
	x.Unlock()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>keep old behavior when wait-timeout not specified<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"github.com/lomik/elock"
	"github.com/lomik/elock/etcd"
)

const APP = "elock"
const VERSION = "0.5.0"

type Config struct {
	EtcdEndpoints []string     `json:"etcd-endpoints"`
	EtcdRoot      string       `json:"etcd-root"`
	EtcdTTL       string       `json:"etcd-default-ttl"`
	EtcdRefresh   string       `json:"etcd-default-refresh"`
	EtcdTls       etcd.TlsOpts `json:"etcd-tls"`
}

func main() {
	configFile := flag.String("config", fmt.Sprintf("/etc/%s/%s.json", APP, APP), "Config file in json format")
	printConfig := flag.Bool("config-print-default", false, "Print default config")
	nowait := flag.Bool("n", false, "Fail rather than wait")
	slots := flag.Int("s", 1, "Available slots count for lock")
	timeout := flag.Duration("w", 0, "Wait for a limited amount of time")
	version := flag.Bool("V", false, "Display version")
	debug := flag.Bool("debug", false, "Enable debug log")
	ttl := flag.Duration("ttl", 0, "Lock ttl (default from config)")
	refresh := flag.Duration("refresh", 0, "Refresh interval (default from config)")
	minLockTime := flag.Duration("min", 0, "Minimum lock time")
	list := flag.Bool("list", false, "List all active locks")
	remove := flag.Bool("rm", false, "Remove lock by path (from list)")
	zeroExit := flag.Bool("0", false, "Exit code 0 on etcd errors or lock timeout")
	runAnyway := flag.Bool("run-anyway", false, "Run the command even if the lock could not take")
	sleepBefore := flag.Duration("sleep-before", 0, "Sleep random time (from zero to selected) before lock attempt")
	quiet := flag.Bool("quiet", false, "Don't print anything")
	waitTime := flag.Duration("wait-time", 0, "Wait time for inner command to end")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, `%s %s
Usage: %s [options] etcd_key command

`, APP, VERSION, APP)
		flag.PrintDefaults()
	}

	flag.Parse()

	if *version {
		fmt.Println(APP, VERSION)
		return
	}

	config := &Config{
		EtcdEndpoints: []string{"http://localhost:2379"},
		EtcdRoot:      "/elock",
		EtcdTTL:       "1m",
		EtcdRefresh:   "10s",
	}

	if *printConfig {
		b, _ := json.MarshalIndent(config, "", "\t")
		fmt.Println(string(b))
		return
	}

	if *quiet {
		log.SetOutput(ioutil.Discard)
	}

	if *sleepBefore != 0 {
		rand.Seed(time.Now().UnixNano())
		sleepTime := time.Duration(rand.Int63n((*sleepBefore).Nanoseconds()))
		if *debug {
			log.Printf("sleep-before: %s", sleepTime.String())
		}
		time.Sleep(sleepTime)
	}

	exit := func(code int) {
		if *zeroExit {
			os.Exit(0)
		} else {
			os.Exit(code)
		}
	}

	fatal := func(err error) {
		if *zeroExit {
			log.Println(err)
			os.Exit(0)
		} else {
			log.Fatal(err)
		}
	}

	cfgData, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(cfgData, config)
	if err != nil {
		log.Fatal(err)
	}

	lockTLL, err := time.ParseDuration(config.EtcdTTL)
	if err != nil {
		log.Fatal(err)
	}

	lockRefresh, err := time.ParseDuration(config.EtcdRefresh)
	if err != nil {
		log.Fatal(err)
	}

	if *ttl != 0 {
		lockTLL = *ttl
	}

	if *refresh != 0 {
		lockRefresh = *refresh
	}

	args := flag.Args()

	if *list {
		records, err := elock.List(elock.Options{
			EtcdEndpoints: config.EtcdEndpoints,
			EtcdTls:       config.EtcdTls,
			Path:          config.EtcdRoot,
			Debug:         *debug,
		}, *timeout)

		if err != nil {
			fatal(err)
		}

		for _, r := range records {
			if r.ValueError != nil {
				fmt.Printf("[error] %s: %s\n", r.Path, r.ValueError.Error())
			} else {
				if r.IsDead() {
					d := time.Now().Sub(r.LastRefresh())
					d = time.Duration(int64(d.Seconds())) * time.Second
					fmt.Printf(
						" [died] %s: last refresh %s ago\n",
						r.Path,
						d.String(),
					)
				} else {
					fmt.Printf("   [ok] %s\n", r.Path)
				}
			}
		}

		return
	}

	if *remove {
		err := elock.Remove(elock.Options{
			EtcdEndpoints: config.EtcdEndpoints,
			EtcdTls:       config.EtcdTls,
			Path:          config.EtcdRoot,
			Debug:         *debug,
		}, *timeout, args)

		if err != nil {
			fatal(err)
		}

		return
	}

	if len(args) < 2 || *slots < 1 {
		flag.Usage()
		return
	}

	x, err := elock.New(elock.Options{
		EtcdEndpoints: config.EtcdEndpoints,
		EtcdTls:       config.EtcdTls,
		Path:          filepath.Join(config.EtcdRoot, args[0]),
		Slots:         *slots,
		TTL:           lockTLL,
		Refresh:       lockRefresh,
		Debug:         *debug,
		MinLockTime:   *minLockTime,
	})

	if err != nil {
		log.Fatal(err)
	}

	if *nowait {
		err = x.LockNoWait()
	} else if *timeout != 0 {
		err = x.LockTimeout(*timeout)
	} else {
		err = x.Lock()
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGSTOP, syscall.SIGTERM)

	go func() {
		for s := range c {
			// sig is a ^C, handle it
			if *debug {
				log.Printf("signal received: %#v", s)
			}

			x.Unlock()
			exit(1)
		}
	}()

	if err != nil { // lock failed
		if *runAnyway { // force run
			log.Println(err)

			cmd := exec.Command(args[1], args[2:]...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			cmd.Stdin = os.Stdin
			err = cmd.Run()

			if err != nil {
				log.Fatal(err)

			}

			return
		}

		fatal(err)
	}

	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	err = cmd.Start()

	if err != nil {
		x.Unlock()
		log.Fatal(err)
	}

	x.OnExpired(func() {
		killCmd := func() {
			if *debug {
				log.Printf("lock expired, send kill to pid %d", cmd.Process.Pid)
			}
			cmd.Process.Kill()
		}

		if *waitTime > 0 {
			cmd.Process.Signal(syscall.SIGINT)
			timer := time.AfterFunc(*waitTime, killCmd)
			cmd.Wait()
			timer.Stop()
		} else {
			killCmd()
		}
	})

	err = cmd.Wait()
	x.Unlock()
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


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

package walk

import . "github.com/lxn/go-winapi"

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

func init() {
	MustRegisterWindowClass(mainWindowWindowClass)
}

type MainWindow struct {
	TopLevelWindow
	menu            *Menu
	toolBar         *ToolBar
	clientComposite *Composite
}

func NewMainWindow() (*MainWindow, error) {
	mw := &MainWindow{}

	if err := InitWidget(
		mw,
		nil,
		mainWindowWindowClass,
		WS_OVERLAPPEDWINDOW,
		WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	SetMenu(mw.hWnd, mw.menu.hMenu)

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}

	if mw.clientComposite, err = NewComposite(mw); err != nil {
		return nil, err
	}
	mw.clientComposite.SetName("clientComposite")

	// This forces display of focus rectangles, as soon as the user starts to type.
	mw.SendMessage(WM_CHANGEUISTATE, UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Children() *WidgetList {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Children()
}

func (mw *MainWindow) Layout() Layout {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Layout()
}

func (mw *MainWindow) SetLayout(value Layout) error {
	if mw.clientComposite == nil {
		return newError("clientComposite not initialized")
	}

	return mw.clientComposite.SetLayout(value)
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.WidgetBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	return bounds
}

func (mw *MainWindow) Show() {
	DrawMenuBar(mw.hWnd)

	if mw.clientComposite.layout != nil {
		mw.clientComposite.layout.Update(false)
	}

	mw.TopLevelWindow.Show()
}

func (mw *MainWindow) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_SIZE, WM_SIZING:
		mw.toolBar.SendMessage(TB_AUTOSIZE, 0, 0)

		mw.clientComposite.SetBounds(mw.ClientBounds())
	}

	return mw.TopLevelWindow.WndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>MainWindow: Improve auto-sizing<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import . "github.com/lxn/go-winapi"

const mainWindowWindowClass = `\o/ Walk_MainWindow_Class \o/`

func init() {
	MustRegisterWindowClass(mainWindowWindowClass)
}

type MainWindow struct {
	TopLevelWindow
	menu            *Menu
	toolBar         *ToolBar
	clientComposite *Composite
}

func NewMainWindow() (*MainWindow, error) {
	mw := &MainWindow{}

	if err := InitWidget(
		mw,
		nil,
		mainWindowWindowClass,
		WS_OVERLAPPEDWINDOW,
		WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			mw.Dispose()
		}
	}()

	mw.SetPersistent(true)

	var err error

	if mw.menu, err = newMenuBar(); err != nil {
		return nil, err
	}
	SetMenu(mw.hWnd, mw.menu.hMenu)

	if mw.toolBar, err = NewToolBar(mw); err != nil {
		return nil, err
	}

	if mw.clientComposite, err = NewComposite(mw); err != nil {
		return nil, err
	}
	mw.clientComposite.SetName("clientComposite")

	mw.clientComposite.children.observer = mw

	// This forces display of focus rectangles, as soon as the user starts to type.
	mw.SendMessage(WM_CHANGEUISTATE, UIS_INITIALIZE, 0)

	succeeded = true

	return mw, nil
}

func (mw *MainWindow) Children() *WidgetList {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Children()
}

func (mw *MainWindow) Layout() Layout {
	if mw.clientComposite == nil {
		return nil
	}

	return mw.clientComposite.Layout()
}

func (mw *MainWindow) SetLayout(value Layout) error {
	if mw.clientComposite == nil {
		return newError("clientComposite not initialized")
	}

	return mw.clientComposite.SetLayout(value)
}

func (mw *MainWindow) Menu() *Menu {
	return mw.menu
}

func (mw *MainWindow) ToolBar() *ToolBar {
	return mw.toolBar
}

func (mw *MainWindow) ClientBounds() Rectangle {
	bounds := mw.WidgetBase.ClientBounds()

	if mw.toolBar.Actions().Len() > 0 {
		tlbBounds := mw.toolBar.Bounds()

		bounds.Y += tlbBounds.Height
		bounds.Height -= tlbBounds.Height
	}

	return bounds
}

func (mw *MainWindow) Show() {
	DrawMenuBar(mw.hWnd)

	if mw.clientComposite.layout != nil {
		mw.clientComposite.layout.Update(false)
	}

	mw.TopLevelWindow.Show()
}

func (mw *MainWindow) onInsertingWidget(index int, widget Widget) error {
	return mw.clientComposite.onInsertingWidget(index, widget)
}

func (mw *MainWindow) onInsertedWidget(index int, widget Widget) error {
	err := mw.clientComposite.onInsertedWidget(index, widget)
	if err == nil {
		minClientSize := mw.Layout().MinSize()
		clientSize := mw.clientComposite.Size()

		if clientSize.Width < minClientSize.Width || clientSize.Height < minClientSize.Height {
			mw.SetClientSize(minClientSize)
		}
	}

	return err
}

func (mw *MainWindow) onRemovingWidget(index int, widget Widget) error {
	return mw.clientComposite.onRemovingWidget(index, widget)
}

func (mw *MainWindow) onRemovedWidget(index int, widget Widget) error {
	return mw.clientComposite.onRemovedWidget(index, widget)
}

func (mw *MainWindow) onClearingWidgets() error {
	return mw.clientComposite.onClearingWidgets()
}

func (mw *MainWindow) onClearedWidgets() error {
	return mw.clientComposite.onClearedWidgets()
}

func (mw *MainWindow) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_SIZE, WM_SIZING:
		mw.toolBar.SendMessage(TB_AUTOSIZE, 0, 0)

		mw.clientComposite.SetBounds(mw.ClientBounds())
	}

	return mw.TopLevelWindow.WndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package ostent
import (
	"fmt"
	"testing"
// 	"strconv"
// 	"github.com/dustin/go-humanize"
	"github.com/rzab/gosigar"
)

/* func Test_humanizeParseBytes(t *testing.T) {
	_, err := humanize.ParseBytes("70GiB")
	if err != nil {
		t.Error(err)
	}
	cmp := strconv.FormatFloat(float64(10.6), 'f', 0, 64)
	if cmp != "11" {
		t.Errorf("Mismatch, cmp: \"%v\"\n", cmp)
	}
} // */

func Test_humanB(t *testing.T) {
	for _, v := range []struct{
		a uint64
		cmp string
	}{
		{117649480 * 1024, "112G" /* "iB" */}, // sigar.FileSystemUsage....[uint64] value is /1024
		{1023, "1023B"},
		{1024, "1.0K"},
	} {
		cmp := humanB(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: humanB(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
} // */

func Test_humanUnitless(t *testing.T) {
	for _, v := range []struct{
		a uint64
		cmp string
	}{
		{999,  "999"},
		{1000, "1.0k"},
		{1001, "1.0k"},
	} {
		cmp := humanUnitless(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: humanUnitless(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
}

func Test_percent(t *testing.T) {
	for _, v := range []struct{
		a, b uint64
		cmp uint
	}{
		{ 201, 1000,  21},
		{ 800, 1000,  80},
		{ 890, 1000,  89},
		{ 891, 1000,  90},
		{ 899, 1000,  90},
		{ 900, 1000,  90},
		{ 901, 1000,  91},
		{ 990, 1000,  99},
		{ 991, 1000,  99},
		{ 995, 1000,  99},
		{ 996, 1000,  99},
		{ 999, 1000,  99},
		{1000, 1000, 100},
	} {
		cmp := percent(v.a, v.b)
		if cmp != v.cmp {
			t.Errorf("Mismatch: percent(%v, %v) == %v != %v\n", v.a, v.b, v.cmp, cmp)
		}
	}
}

func Test_formatUptime(t *testing.T) {
	for _, v := range []struct{
		a float64
		cmp string
	}{
		{ 1080720, "12 days, 12:12"},
		{ 1069920, "12 days,  9:12"},
		{ 43920,   "12:12"},
		{ 33120,   " 9:12"},
	} {
		cmp := formatUptime(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: formatUptime(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
}

func sigarUptime(t *testing.B) *sigar.Uptime {
	return &sigar.Uptime{Length: 1080720 + float64(t.N)}
}

func BenchmarkUptimeFormat		(t *testing.B) { 						  sigarUptime(t).Format() }
func BenchmarkFormatUptime		(t *testing.B) { formatUptime		   ((*sigarUptime(t)).Length) }
func BenchmarkSigarUptimeFormat	(t *testing.B) { sigarUptimeFormatString(*sigarUptime(t)) }

// the way sigar.Uptime.Format implemented
// sans bytes.Buffer, bufio.NewWriter stuff
func sigarUptimeFormatString(u sigar.Uptime) string {
	uptime := uint64(u.Length)
	days := uptime / (60 * 60 * 24)

	s := ""
	if days != 0 {
		end := ""
		if days > 1 {
			end = "s"
		}
		s = fmt.Sprintf("%d day%s, ", days, end)
	}

	minutes := uptime / 60
	hours := minutes / 60
	hours %= 24
	minutes %= 60

	s += fmt.Sprintf("%2d:%02d", hours, minutes)
	return s
}
<commit_msg>gofmt format tests<commit_after>package ostent

import (
	"fmt"
	"testing"
	// "strconv"
	// "github.com/dustin/go-humanize"
	"github.com/rzab/gosigar"
)

/* func Test_humanizeParseBytes(t *testing.T) {
	_, err := humanize.ParseBytes("70GiB")
	if err != nil {
		t.Error(err)
	}
	cmp := strconv.FormatFloat(float64(10.6), 'f', 0, 64)
	if cmp != "11" {
		t.Errorf("Mismatch, cmp: \"%v\"\n", cmp)
	}
} // */

func Test_humanB(t *testing.T) {
	for _, v := range []struct {
		a   uint64
		cmp string
	}{
		{117649480 * 1024, "112G" /* "iB" */}, // sigar.FileSystemUsage....[uint64] value is /1024
		{1023, "1023B"},
		{1024, "1.0K"},
	} {
		cmp := humanB(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: humanB(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
} // */

func Test_humanUnitless(t *testing.T) {
	for _, v := range []struct {
		a   uint64
		cmp string
	}{
		{999, "999"},
		{1000, "1.0k"},
		{1001, "1.0k"},
	} {
		cmp := humanUnitless(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: humanUnitless(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
}

func Test_percent(t *testing.T) {
	for _, v := range []struct {
		a, b uint64
		cmp  uint
	}{
		{201, 1000, 21},
		{800, 1000, 80},
		{890, 1000, 89},
		{891, 1000, 90},
		{899, 1000, 90},
		{900, 1000, 90},
		{901, 1000, 91},
		{990, 1000, 99},
		{991, 1000, 99},
		{995, 1000, 99},
		{996, 1000, 99},
		{999, 1000, 99},
		{1000, 1000, 100},
	} {
		cmp := percent(v.a, v.b)
		if cmp != v.cmp {
			t.Errorf("Mismatch: percent(%v, %v) == %v != %v\n", v.a, v.b, v.cmp, cmp)
		}
	}
}

func Test_formatUptime(t *testing.T) {
	for _, v := range []struct {
		a   float64
		cmp string
	}{
		{1080720, "12 days, 12:12"},
		{1069920, "12 days,  9:12"},
		{43920, "12:12"},
		{33120, " 9:12"},
	} {
		cmp := formatUptime(v.a)
		if cmp != v.cmp {
			t.Errorf("Mismatch: formatUptime(%v) == %v != %v\n", v.a, v.cmp, cmp)
		}
	}
}

func sigarUptime(t *testing.B) *sigar.Uptime {
	return &sigar.Uptime{Length: 1080720 + float64(t.N)}
}

func BenchmarkUptimeFormat(t *testing.B)      { sigarUptime(t).Format() }
func BenchmarkFormatUptime(t *testing.B)      { formatUptime((*sigarUptime(t)).Length) }
func BenchmarkSigarUptimeFormat(t *testing.B) { sigarUptimeFormatString(*sigarUptime(t)) }

// the way sigar.Uptime.Format implemented
// sans bytes.Buffer, bufio.NewWriter stuff
func sigarUptimeFormatString(u sigar.Uptime) string {
	uptime := uint64(u.Length)
	days := uptime / (60 * 60 * 24)

	s := ""
	if days != 0 {
		end := ""
		if days > 1 {
			end = "s"
		}
		s = fmt.Sprintf("%d day%s, ", days, end)
	}

	minutes := uptime / 60
	hours := minutes / 60
	hours %= 24
	minutes %= 60

	s += fmt.Sprintf("%2d:%02d", hours, minutes)
	return s
}
<|endoftext|>


<commit_before>package physical

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"strings"
	"sync"

	"github.com/hashicorp/golang-lru"
	"github.com/hashicorp/vault/helper/locksutil"
	"github.com/hashicorp/vault/helper/strutil"
	log "github.com/mgutz/logxi/v1"
)

const (
	// DefaultCacheSize is used if no cache size is specified for NewCache
	DefaultCacheSize = 32 * 1024
)

// Cache is used to wrap an underlying physical backend
// and provide an LRU cache layer on top. Most of the reads done by
// Vault are for policy objects so there is a large read reduction
// by using a simple write-through cache.
type Cache struct {
	backend       Backend
	transactional Transactional
	lru           *lru.TwoQueueCache
	locks         map[string]*sync.RWMutex
	logger        log.Logger
}

// NewCache returns a physical cache of the given size.
// If no size is provided, the default size is used.
func NewCache(b Backend, size int, logger log.Logger) *Cache {
	if size <= 0 {
		size = DefaultCacheSize
	}
	if logger.IsTrace() {
		logger.Trace("physical/cache: creating LRU cache", "size", size)
	}
	cache, _ := lru.New2Q(size)
	c := &Cache{
		backend: b,
		lru:     cache,
		locks:   make(map[string]*sync.RWMutex, 256),
		logger:  logger,
	}
	if err := locksutil.CreateLocks(c.locks, 256); err != nil {
		logger.Error("physical/cache: error creating locks", "error", err)
		return nil
	}

	if txnl, ok := c.backend.(Transactional); ok {
		c.transactional = txnl
	}

	return c
}

func (c *Cache) lockHashForKey(key string) string {
	hf := sha1.New()
	hf.Write([]byte(key))
	return strings.ToLower(hex.EncodeToString(hf.Sum(nil))[:2])
}

func (c *Cache) lockForKey(key string) *sync.RWMutex {
	return c.locks[c.lockHashForKey(key)]
}

// Purge is used to clear the cache
func (c *Cache) Purge() {
	// Lock the world
	lockHashes := make([]string, 0, len(c.locks))
	for hash := range c.locks {
		lockHashes = append(lockHashes, hash)
	}

	// Sort and deduplicate. This ensures we don't try to grab the same lock
	// twice, and enforcing a sort means we'll not have multiple goroutines
	// deadlock by acquiring in different orders.
	lockHashes = strutil.RemoveDuplicates(lockHashes)

	for _, lockHash := range lockHashes {
		lock := c.locks[lockHash]
		lock.Lock()
		defer lock.Unlock()
	}

	c.lru.Purge()
}

func (c *Cache) Put(entry *Entry) error {
	lock := c.lockForKey(entry.Key)
	lock.Lock()
	defer lock.Unlock()

	err := c.backend.Put(entry)
	if err == nil && !strings.HasPrefix(entry.Key, "core/") {
		c.lru.Add(entry.Key, entry)
	}
	return err
}

func (c *Cache) Get(key string) (*Entry, error) {
	lock := c.lockForKey(key)
	lock.RLock()
	defer lock.RUnlock()

	// We do NOT cache negative results for keys in the 'core/' prefix
	// otherwise we risk certain race conditions upstream. The primary issue is
	// with the HA mode, we could potentially negatively cache the leader entry
	// and cause leader discovery to fail.
	if strings.HasPrefix(key, "core/") {
		return c.backend.Get(key)
	}

	// Check the LRU first
	if raw, ok := c.lru.Get(key); ok {
		if raw == nil {
			return nil, nil
		} else {
			return raw.(*Entry), nil
		}
	}

	// Read from the underlying backend
	ent, err := c.backend.Get(key)
	if err != nil {
		return nil, err
	}

	// Cache the result
	if ent != nil {
		c.lru.Add(key, ent)
	}

	return ent, nil
}

func (c *Cache) Delete(key string) error {
	lock := c.lockForKey(key)
	lock.Lock()
	defer lock.Unlock()

	err := c.backend.Delete(key)
	if err == nil && !strings.HasPrefix(key, "core/") {
		c.lru.Remove(key)
	}
	return err
}

func (c *Cache) List(prefix string) ([]string, error) {
	// Always pass-through as this would be difficult to cache. For the same
	// reason we don't lock as we can't reasonably know which locks to readlock
	// ahead of time.
	return c.backend.List(prefix)
}

func (c *Cache) Transaction(txns []TxnEntry) error {
	if c.transactional == nil {
		return fmt.Errorf("physical/cache: underlying backend does not support transactions")
	}

	var lockHashes []string
	for _, txn := range txns {
		lockHashes = append(lockHashes, c.lockHashForKey(txn.Entry.Key))
	}

	// Sort and deduplicate. This ensures we don't try to grab the same lock
	// twice, and enforcing a sort means we'll not have multiple goroutines
	// deadlock by acquiring in different orders.
	lockHashes = strutil.RemoveDuplicates(lockHashes)

	for _, lockHash := range lockHashes {
		lock := c.locks[lockHash]
		lock.Lock()
		defer lock.Unlock()
	}

	if err := c.transactional.Transaction(txns); err != nil {
		return err
	}

	for _, txn := range txns {
		switch txn.Operation {
		case PutOperation:
			c.lru.Add(txn.Entry.Key, txn.Entry)
		case DeleteOperation:
			c.lru.Remove(txn.Entry.Key)
		}
	}

	return nil
}
<commit_msg>Switch physical cache map index value to md5 from sha1 for all the performances<commit_after>package physical

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"strings"
	"sync"

	"github.com/hashicorp/golang-lru"
	"github.com/hashicorp/vault/helper/locksutil"
	"github.com/hashicorp/vault/helper/strutil"
	log "github.com/mgutz/logxi/v1"
)

const (
	// DefaultCacheSize is used if no cache size is specified for NewCache
	DefaultCacheSize = 32 * 1024
)

// Cache is used to wrap an underlying physical backend
// and provide an LRU cache layer on top. Most of the reads done by
// Vault are for policy objects so there is a large read reduction
// by using a simple write-through cache.
type Cache struct {
	backend       Backend
	transactional Transactional
	lru           *lru.TwoQueueCache
	locks         map[string]*sync.RWMutex
	logger        log.Logger
}

// NewCache returns a physical cache of the given size.
// If no size is provided, the default size is used.
func NewCache(b Backend, size int, logger log.Logger) *Cache {
	if size <= 0 {
		size = DefaultCacheSize
	}
	if logger.IsTrace() {
		logger.Trace("physical/cache: creating LRU cache", "size", size)
	}
	cache, _ := lru.New2Q(size)
	c := &Cache{
		backend: b,
		lru:     cache,
		locks:   make(map[string]*sync.RWMutex, 256),
		logger:  logger,
	}
	if err := locksutil.CreateLocks(c.locks, 256); err != nil {
		logger.Error("physical/cache: error creating locks", "error", err)
		return nil
	}

	if txnl, ok := c.backend.(Transactional); ok {
		c.transactional = txnl
	}

	return c
}

func (c *Cache) lockHashForKey(key string) string {
	hf := md5.New()
	hf.Write([]byte(key))
	return strings.ToLower(hex.EncodeToString(hf.Sum(nil))[:2])
}

func (c *Cache) lockForKey(key string) *sync.RWMutex {
	return c.locks[c.lockHashForKey(key)]
}

// Purge is used to clear the cache
func (c *Cache) Purge() {
	// Lock the world
	lockHashes := make([]string, 0, len(c.locks))
	for hash := range c.locks {
		lockHashes = append(lockHashes, hash)
	}

	// Sort and deduplicate. This ensures we don't try to grab the same lock
	// twice, and enforcing a sort means we'll not have multiple goroutines
	// deadlock by acquiring in different orders.
	lockHashes = strutil.RemoveDuplicates(lockHashes)

	for _, lockHash := range lockHashes {
		lock := c.locks[lockHash]
		lock.Lock()
		defer lock.Unlock()
	}

	c.lru.Purge()
}

func (c *Cache) Put(entry *Entry) error {
	lock := c.lockForKey(entry.Key)
	lock.Lock()
	defer lock.Unlock()

	err := c.backend.Put(entry)
	if err == nil && !strings.HasPrefix(entry.Key, "core/") {
		c.lru.Add(entry.Key, entry)
	}
	return err
}

func (c *Cache) Get(key string) (*Entry, error) {
	lock := c.lockForKey(key)
	lock.RLock()
	defer lock.RUnlock()

	// We do NOT cache negative results for keys in the 'core/' prefix
	// otherwise we risk certain race conditions upstream. The primary issue is
	// with the HA mode, we could potentially negatively cache the leader entry
	// and cause leader discovery to fail.
	if strings.HasPrefix(key, "core/") {
		return c.backend.Get(key)
	}

	// Check the LRU first
	if raw, ok := c.lru.Get(key); ok {
		if raw == nil {
			return nil, nil
		} else {
			return raw.(*Entry), nil
		}
	}

	// Read from the underlying backend
	ent, err := c.backend.Get(key)
	if err != nil {
		return nil, err
	}

	// Cache the result
	if ent != nil {
		c.lru.Add(key, ent)
	}

	return ent, nil
}

func (c *Cache) Delete(key string) error {
	lock := c.lockForKey(key)
	lock.Lock()
	defer lock.Unlock()

	err := c.backend.Delete(key)
	if err == nil && !strings.HasPrefix(key, "core/") {
		c.lru.Remove(key)
	}
	return err
}

func (c *Cache) List(prefix string) ([]string, error) {
	// Always pass-through as this would be difficult to cache. For the same
	// reason we don't lock as we can't reasonably know which locks to readlock
	// ahead of time.
	return c.backend.List(prefix)
}

func (c *Cache) Transaction(txns []TxnEntry) error {
	if c.transactional == nil {
		return fmt.Errorf("physical/cache: underlying backend does not support transactions")
	}

	var lockHashes []string
	for _, txn := range txns {
		lockHashes = append(lockHashes, c.lockHashForKey(txn.Entry.Key))
	}

	// Sort and deduplicate. This ensures we don't try to grab the same lock
	// twice, and enforcing a sort means we'll not have multiple goroutines
	// deadlock by acquiring in different orders.
	lockHashes = strutil.RemoveDuplicates(lockHashes)

	for _, lockHash := range lockHashes {
		lock := c.locks[lockHash]
		lock.Lock()
		defer lock.Unlock()
	}

	if err := c.transactional.Transaction(txns); err != nil {
		return err
	}

	for _, txn := range txns {
		switch txn.Operation {
		case PutOperation:
			c.lru.Add(txn.Entry.Key, txn.Entry)
		case DeleteOperation:
			c.lru.Remove(txn.Entry.Key)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package pixelgl

import (
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Fullscreen *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// If set to true, the Window will be initially invisible.
	Hidden bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// If set to true, the Window will not get focused upon showing up.
	Unfocused bool

	// Whether the Window is maximized.
	Maximized bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds pixel.Rect
	canvas *Canvas
	vsync  bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, tempInp, currInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		scroll  pixel.Vec
	}
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Visible, bool2int[!cfg.Hidden])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])
		glfw.WindowHint(glfw.Focused, bool2int[!cfg.Unfocused])
		glfw.WindowHint(glfw.Maximized, bool2int[cfg.Maximized])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Fullscreen)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)

	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size() + pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		glhf.Bounds(0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height())

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})

	w.updateInput()
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

// Show makes the Window visible if it was hidden.
func (w *Window) Show() {
	mainthread.Call(func() {
		w.window.Show()
	})
}

// Hide hides the Window if it was visible.
func (w *Window) Hide() {
	mainthread.Call(func() {
		w.window.Hide()
	})
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// IsFullscreen returns true if the Window is in fullscreen mode.
func (w *Window) IsFullscreen() bool {
	return w.Monitor() != nil
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focus brings the Window to the front and sets input focus.
func (w *Window) Focus() {
	mainthread.Call(func() {
		w.window.Focus()
	})
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// Maximize puts the Window to the maximized state.
func (w *Window) Maximize() {
	mainthread.Call(func() {
		w.window.Maximize()
	})
}

// Restore restores the Window from the maximized state.
func (w *Window) Restore() {
	mainthread.Call(func() {
		w.window.Restore()
	})
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		glhf.Init()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}
<commit_msg>remove a bunch of unnecessary Window control methods<commit_after>package pixelgl

import (
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Monitor *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds pixel.Rect
	canvas *Canvas
	vsync  bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, tempInp, currInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		scroll  pixel.Vec
	}
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Monitor)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)

	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size() + pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		glhf.Bounds(0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height())

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})

	w.updateInput()
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		glhf.Init()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}
<|endoftext|>


<commit_before>package units

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

var sizeRegex *regexp.Regexp

func init() {
	var err error
	if sizeRegex, err = regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$"); err != nil {
		panic("Failed to compile the 'size' regular expression")
	}
}

// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size int64) string {
	units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
	i := 0
	sizef := float64(size)
	for sizef >= 1000.0 {
		sizef = sizef / 1000.0
		i++
	}
	return fmt.Sprintf("%.4g %s", sizef, units[i])
}

// FromHumanSize returns an integer from a human-readable specification of a size
// using SI standard (eg. "44kB", "17MB")
func FromHumanSize(size string) (int64, error) {
	matches := sizeRegex.FindStringSubmatch(size)

	if len(matches) != 3 {
		return -1, fmt.Errorf("Invalid size: '%s'", size)
	}

	theSize, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return -1, err
	}

	unit := strings.ToLower(matches[2])

	switch unit {
	case "k":
		theSize *= 1000
	case "m":
		theSize *= 1000 * 1000
	case "g":
		theSize *= 1000 * 1000 * 1000
	case "t":
		theSize *= 1000 * 1000 * 1000 * 1000
	case "p":
		theSize *= 1000 * 1000 * 1000 * 1000 * 1000
	}

	return theSize, nil
}

// Parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
// returns the number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes(size string) (int64, error) {
	matches := sizeRegex.FindStringSubmatch(size)

	if len(matches) != 3 {
		return -1, fmt.Errorf("Invalid size: '%s'", size)
	}

	memLimit, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return -1, err
	}

	unit := strings.ToLower(matches[2])

	switch unit {
	case "k":
		memLimit *= 1024
	case "m":
		memLimit *= 1024 * 1024
	case "g":
		memLimit *= 1024 * 1024 * 1024
	case "t":
		memLimit *= 1024 * 1024 * 1024 * 1024
	case "p":
		memLimit *= 1024 * 1024 * 1024 * 1024 * 1024
	}

	return memLimit, nil
}
<commit_msg>pkg/units: Moved 'units' out of function<commit_after>package units

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

var sizeRegex *regexp.Regexp

func init() {
	var err error
	if sizeRegex, err = regexp.Compile("^(\\d+)([kKmMgGtTpP])?[bB]?$"); err != nil {
		panic("Failed to compile the 'size' regular expression")
	}
}

var bytePrefixes = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}

// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size int64) string {
	i := 0
	sizef := float64(size)
	for sizef >= 1000.0 {
		sizef = sizef / 1000.0
		i++
	}
	return fmt.Sprintf("%.4g %s", sizef, bytePrefixes[i])
}

// FromHumanSize returns an integer from a human-readable specification of a size
// using SI standard (eg. "44kB", "17MB")
func FromHumanSize(size string) (int64, error) {
	matches := sizeRegex.FindStringSubmatch(size)

	if len(matches) != 3 {
		return -1, fmt.Errorf("Invalid size: '%s'", size)
	}

	theSize, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return -1, err
	}

	unit := strings.ToLower(matches[2])

	switch unit {
	case "k":
		theSize *= 1000
	case "m":
		theSize *= 1000 * 1000
	case "g":
		theSize *= 1000 * 1000 * 1000
	case "t":
		theSize *= 1000 * 1000 * 1000 * 1000
	case "p":
		theSize *= 1000 * 1000 * 1000 * 1000 * 1000
	}

	return theSize, nil
}

// Parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
// returns the number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes(size string) (int64, error) {
	matches := sizeRegex.FindStringSubmatch(size)

	if len(matches) != 3 {
		return -1, fmt.Errorf("Invalid size: '%s'", size)
	}

	memLimit, err := strconv.ParseInt(matches[1], 10, 0)
	if err != nil {
		return -1, err
	}

	unit := strings.ToLower(matches[2])

	switch unit {
	case "k":
		memLimit *= 1024
	case "m":
		memLimit *= 1024 * 1024
	case "g":
		memLimit *= 1024 * 1024 * 1024
	case "t":
		memLimit *= 1024 * 1024 * 1024 * 1024
	case "p":
		memLimit *= 1024 * 1024 * 1024 * 1024 * 1024
	}

	return memLimit, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	units "github.com/docker/go-units"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

const (
	downloadURL = "https://storage.googleapis.com/minikube/releases/%s/minikube-%s-amd64%s"
)

type RetriableError struct {
	Err error
}

func (r RetriableError) Error() string { return "Temporary Error: " + r.Err.Error() }

func CalculateDiskSizeInMB(humanReadableDiskSize string) int {
	diskSize, err := units.FromHumanSize(humanReadableDiskSize)
	if err != nil {
		glog.Errorf("Invalid disk size: %v", err)
	}
	return int(diskSize / units.MB)
}

// Until endlessly loops the provided function until a message is received on the done channel.
// The function will wait the duration provided in sleep between function calls. Errors will be sent on provider Writer.
func Until(fn func() error, w io.Writer, name string, sleep time.Duration, done <-chan struct{}) {
	var exitErr error
	for {
		select {
		case <-done:
			return
		default:
			exitErr = fn()
			if exitErr == nil {
				fmt.Fprintf(w, Pad("%s: Exited with no errors.\n"), name)
			} else {
				fmt.Fprintf(w, Pad("%s: Exit with error: %v"), name, exitErr)
			}

			// wait provided duration before trying again
			time.Sleep(sleep)
		}
	}
}

func Pad(str string) string {
	return fmt.Sprintf("\n%s\n", str)
}

// If the file represented by path exists and
// readable, return true otherwise return false.
func CanReadFile(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}

	defer f.Close()

	return true
}

func Retry(attempts int, callback func() error) (err error) {
	return RetryAfter(attempts, callback, 0)
}

func RetryAfter(attempts int, callback func() error, d time.Duration) (err error) {
	m := MultiError{}
	for i := 0; i < attempts; i++ {
		glog.V(1).Infof("retry loop %d", i)
		err = callback()
		if err == nil {
			return nil
		}
		m.Collect(err)
		if _, ok := err.(*RetriableError); !ok {
			glog.Infof("non-retriable error: %v", err)
			return m.ToError()
		}
		glog.V(2).Infof("sleeping %s", d)
		time.Sleep(d)
	}
	return m.ToError()
}

func ParseSHAFromURL(url string) (string, error) {
	r, err := http.Get(url)
	if err != nil {
		return "", errors.Wrap(err, "Error downloading checksum.")
	} else if r.StatusCode != http.StatusOK {
		return "", errors.Errorf("Error downloading checksum. Got HTTP Error: %s", r.Status)
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", errors.Wrap(err, "Error reading checksum.")
	}

	return strings.Trim(string(body), "\n"), nil
}

func GetBinaryDownloadURL(version, platform string) string {
	switch platform {
	case "windows":
		return fmt.Sprintf(downloadURL, version, platform, ".exe")
	default:
		return fmt.Sprintf(downloadURL, version, platform, "")
	}
}

type MultiError struct {
	Errors []error
}

func (m *MultiError) Collect(err error) {
	if err != nil {
		m.Errors = append(m.Errors, err)
	}
}

func (m MultiError) ToError() error {
	if len(m.Errors) == 0 {
		return nil
	}

	errStrings := []string{}
	for _, err := range m.Errors {
		errStrings = append(errStrings, err.Error())
	}
	return errors.New(strings.Join(errStrings, "\n"))
}

func IsDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return false, errors.Wrapf(err, "Error calling os.Stat on file %s", path)
	}
	return fileInfo.IsDir(), nil
}

func ChownR(path string, uid, gid int) error {
	return filepath.Walk(path, func(name string, info os.FileInfo, err error) error {
		if err == nil {
			err = os.Chown(name, uid, gid)
		}
		return err
	})
}

func MaybeChownDirRecursiveToMinikubeUser(dir string) error {
	if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" && os.Getenv("SUDO_USER") != "" {
		username := os.Getenv("SUDO_USER")
		usr, err := user.Lookup(username)
		if err != nil {
			return errors.Wrap(err, "Error looking up user")
		}
		uid, err := strconv.Atoi(usr.Uid)
		if err != nil {
			return errors.Wrapf(err, "Error parsing uid for user: %s", username)
		}
		gid, err := strconv.Atoi(usr.Gid)
		if err != nil {
			return errors.Wrapf(err, "Error parsing gid for user: %s", username)
		}
		if err := ChownR(dir, uid, gid); err != nil {
			return errors.Wrapf(err, "Error changing ownership for: %s", dir)
		}
	}
	return nil
}
<commit_msg>Decrease log noise by logging retry loops after the initial attempt<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	units "github.com/docker/go-units"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

const (
	downloadURL = "https://storage.googleapis.com/minikube/releases/%s/minikube-%s-amd64%s"
)

type RetriableError struct {
	Err error
}

func (r RetriableError) Error() string { return "Temporary Error: " + r.Err.Error() }

func CalculateDiskSizeInMB(humanReadableDiskSize string) int {
	diskSize, err := units.FromHumanSize(humanReadableDiskSize)
	if err != nil {
		glog.Errorf("Invalid disk size: %v", err)
	}
	return int(diskSize / units.MB)
}

// Until endlessly loops the provided function until a message is received on the done channel.
// The function will wait the duration provided in sleep between function calls. Errors will be sent on provider Writer.
func Until(fn func() error, w io.Writer, name string, sleep time.Duration, done <-chan struct{}) {
	var exitErr error
	for {
		select {
		case <-done:
			return
		default:
			exitErr = fn()
			if exitErr == nil {
				fmt.Fprintf(w, Pad("%s: Exited with no errors.\n"), name)
			} else {
				fmt.Fprintf(w, Pad("%s: Exit with error: %v"), name, exitErr)
			}

			// wait provided duration before trying again
			time.Sleep(sleep)
		}
	}
}

func Pad(str string) string {
	return fmt.Sprintf("\n%s\n", str)
}

// If the file represented by path exists and
// readable, return true otherwise return false.
func CanReadFile(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}

	defer f.Close()

	return true
}

func Retry(attempts int, callback func() error) (err error) {
	return RetryAfter(attempts, callback, 0)
}

func RetryAfter(attempts int, callback func() error, d time.Duration) (err error) {
	m := MultiError{}
	for i := 0; i < attempts; i++ {
		if i > 0 {
			glog.V(1).Infof("retry loop %d", i)
		}
		err = callback()
		if err == nil {
			return nil
		}
		m.Collect(err)
		if _, ok := err.(*RetriableError); !ok {
			glog.Infof("non-retriable error: %v", err)
			return m.ToError()
		}
		glog.V(2).Infof("sleeping %s", d)
		time.Sleep(d)
	}
	return m.ToError()
}

func ParseSHAFromURL(url string) (string, error) {
	r, err := http.Get(url)
	if err != nil {
		return "", errors.Wrap(err, "Error downloading checksum.")
	} else if r.StatusCode != http.StatusOK {
		return "", errors.Errorf("Error downloading checksum. Got HTTP Error: %s", r.Status)
	}

	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", errors.Wrap(err, "Error reading checksum.")
	}

	return strings.Trim(string(body), "\n"), nil
}

func GetBinaryDownloadURL(version, platform string) string {
	switch platform {
	case "windows":
		return fmt.Sprintf(downloadURL, version, platform, ".exe")
	default:
		return fmt.Sprintf(downloadURL, version, platform, "")
	}
}

type MultiError struct {
	Errors []error
}

func (m *MultiError) Collect(err error) {
	if err != nil {
		m.Errors = append(m.Errors, err)
	}
}

func (m MultiError) ToError() error {
	if len(m.Errors) == 0 {
		return nil
	}

	errStrings := []string{}
	for _, err := range m.Errors {
		errStrings = append(errStrings, err.Error())
	}
	return errors.New(strings.Join(errStrings, "\n"))
}

func IsDirectory(path string) (bool, error) {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return false, errors.Wrapf(err, "Error calling os.Stat on file %s", path)
	}
	return fileInfo.IsDir(), nil
}

func ChownR(path string, uid, gid int) error {
	return filepath.Walk(path, func(name string, info os.FileInfo, err error) error {
		if err == nil {
			err = os.Chown(name, uid, gid)
		}
		return err
	})
}

func MaybeChownDirRecursiveToMinikubeUser(dir string) error {
	if os.Getenv("CHANGE_MINIKUBE_NONE_USER") != "" && os.Getenv("SUDO_USER") != "" {
		username := os.Getenv("SUDO_USER")
		usr, err := user.Lookup(username)
		if err != nil {
			return errors.Wrap(err, "Error looking up user")
		}
		uid, err := strconv.Atoi(usr.Uid)
		if err != nil {
			return errors.Wrapf(err, "Error parsing uid for user: %s", username)
		}
		gid, err := strconv.Atoi(usr.Gid)
		if err != nil {
			return errors.Wrapf(err, "Error parsing gid for user: %s", username)
		}
		if err := ChownR(dir, uid, gid); err != nil {
			return errors.Wrapf(err, "Error changing ownership for: %s", dir)
		}
	}
	return nil
}
<|endoftext|>