<commit_before>// +build linux

package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runc/libcontainer/user"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/vishvananda/netlink"
)

type initType string

const (
	initSetns    initType = "setns"
	initStandard initType = "standard"
)

type pid struct {
	Pid int `json:"pid"`
}

// network is an internal struct used to setup container networks.
type network struct {
	configs.Network

	// TempVethPeerName is a unique temporary veth peer name that was placed into
	// the container's namespace.
	TempVethPeerName string `json:"temp_veth_peer_name"`
}

// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
	Args             []string        `json:"args"`
	Env              []string        `json:"env"`
	Cwd              string          `json:"cwd"`
	Capabilities     []string        `json:"capabilities"`
	User             string          `json:"user"`
	Config           *configs.Config `json:"config"`
	Console          string          `json:"console"`
	Networks         []*network      `json:"network"`
	PassedFilesCount int             `json:"passed_files_count"`
}

type initer interface {
	Init() error
}

func newContainerInit(t initType, pipe *os.File) (initer, error) {
	var config *initConfig
	if err := json.NewDecoder(pipe).Decode(&config); err != nil {
		return nil, err
	}
	if err := populateProcessEnvironment(config.Env); err != nil {
		return nil, err
	}
	switch t {
	case initSetns:
		return &linuxSetnsInit{
			config: config,
		}, nil
	case initStandard:
		return &linuxStandardInit{
			pipe:      pipe,
			parentPid: syscall.Getppid(),
			config:    config,
		}, nil
	}
	return nil, fmt.Errorf("unknown init type %q", t)
}

// populateProcessEnvironment loads the provided environment variables into the
// current processes's environment.
func populateProcessEnvironment(env []string) error {
	for _, pair := range env {
		p := strings.SplitN(pair, "=", 2)
		if len(p) < 2 {
			return fmt.Errorf("invalid environment '%v'", pair)
		}
		if err := os.Setenv(p[0], p[1]); err != nil {
			return err
		}
	}
	return nil
}

// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
	// Ensure that all unwanted fds we may have accidentally
	// inherited are marked close-on-exec so they stay out of the
	// container
	if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
		return err
	}

	capabilities := config.Config.Capabilities
	if config.Capabilities != nil {
		capabilities = config.Capabilities
	}
	w, err := newCapWhitelist(capabilities)
	if err != nil {
		return err
	}
	// drop capabilities in bounding set before changing user
	if err := w.dropBoundingSet(); err != nil {
		return err
	}
	// preserve existing capabilities while we change users
	if err := system.SetKeepCaps(); err != nil {
		return err
	}
	if err := setupUser(config); err != nil {
		return err
	}
	if err := system.ClearKeepCaps(); err != nil {
		return err
	}
	// drop all other capabilities
	if err := w.drop(); err != nil {
		return err
	}
	if err := syscall.Chdir(config.Cwd); err != nil {
		return err
	}
	return nil
}

// syncParentReady sends to the given pipe a JSON payload which indicates that
// the init is ready to Exec the child process. It then waits for the parent to
// indicate that it is cleared to Exec.
func syncParentReady(pipe io.ReadWriter) error {
	// Tell parent.
	if err := json.NewEncoder(pipe).Encode(procReady); err != nil {
		return err
	}

	// Wait for parent to give the all-clear.
	var procSync syncType
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync != procRun {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// joinExistingNamespaces gets all the namespace paths specified for the container and
// does a setns on the namespace fd so that the current process joins the namespace.
func joinExistingNamespaces(namespaces []configs.Namespace) error {
	for _, ns := range namespaces {
		if ns.Path != "" {
			f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0)
			if err != nil {
				return err
			}
			err = system.Setns(f.Fd(), uintptr(ns.Syscall()))
			f.Close()
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// setupUser changes the groups, gid, and uid for the user inside the container
func setupUser(config *initConfig) error {
	// Set up defaults.
	defaultExecUser := user.ExecUser{
		Uid:  syscall.Getuid(),
		Gid:  syscall.Getgid(),
		Home: "/",
	}
	passwdPath, err := user.GetPasswdPath()
	if err != nil {
		return err
	}
	groupPath, err := user.GetGroupPath()
	if err != nil {
		return err
	}
	execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
	if err != nil {
		return err
	}

	var addGroups []int
	if len(config.Config.AdditionalGroups) > 0 {
		addGroups, err = user.GetAdditionalGroupsPath(config.Config.AdditionalGroups, groupPath)
		if err != nil {
			return err
		}
	}
	// before we change to the container's user make sure that the processes STDIO
	// is correctly owned by the user that we are switching to.
	if err := fixStdioPermissions(execUser); err != nil {
		return err
	}
	suppGroups := append(execUser.Sgids, addGroups...)
	if err := syscall.Setgroups(suppGroups); err != nil {
		return err
	}

	if err := system.Setgid(execUser.Gid); err != nil {
		return err
	}
	if err := system.Setuid(execUser.Uid); err != nil {
		return err
	}
	// if we didn't get HOME already, set it based on the user's HOME
	if envHome := os.Getenv("HOME"); envHome == "" {
		if err := os.Setenv("HOME", execUser.Home); err != nil {
			return err
		}
	}
	return nil
}

// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
// The ownership needs to match because it is created outside of the container and needs to be
// localized.
func fixStdioPermissions(u *user.ExecUser) error {
	var null syscall.Stat_t
	if err := syscall.Stat("/dev/null", &null); err != nil {
		return err
	}
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stderr.Fd(),
		os.Stdout.Fd(),
	} {
		var s syscall.Stat_t
		if err := syscall.Fstat(int(fd), &s); err != nil {
			return err
		}
		// skip chown of /dev/null if it was used as one of the STDIO fds.
		if s.Rdev == null.Rdev {
			continue
		}
		if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil {
			return err
		}
	}
	return nil
}

// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
	for _, config := range config.Networks {
		strategy, err := getStrategy(config.Type)
		if err != nil {
			return err
		}
		if err := strategy.initialize(config); err != nil {
			return err
		}
	}
	return nil
}

func setupRoute(config *configs.Config) error {
	for _, config := range config.Routes {
		_, dst, err := net.ParseCIDR(config.Destination)
		if err != nil {
			return err
		}
		src := net.ParseIP(config.Source)
		if src == nil {
			return fmt.Errorf("Invalid source for route: %s", config.Source)
		}
		gw := net.ParseIP(config.Gateway)
		if gw == nil {
			return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
		}
		l, err := netlink.LinkByName(config.InterfaceName)
		if err != nil {
			return err
		}
		route := &netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			Dst:       dst,
			Src:       src,
			Gw:        gw,
			LinkIndex: l.Attrs().Index,
		}
		if err := netlink.RouteAdd(route); err != nil {
			return err
		}
	}
	return nil
}

func setupRlimits(config *configs.Config) error {
	for _, rlimit := range config.Rlimits {
		l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}
		if err := syscall.Setrlimit(rlimit.Type, l); err != nil {
			return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
		}
	}
	return nil
}

func setOomScoreAdj(oomScoreAdj int) error {
	path := "/proc/self/oom_score_adj"
	return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0700)
}

// killCgroupProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending a SIGKILL to each process then waiting for them to
// exit.
func killCgroupProcesses(m cgroups.Manager) error {
	var procs []*os.Process
	if err := m.Freeze(configs.Frozen); err != nil {
		logrus.Warn(err)
	}
	pids, err := m.GetAllPids()
	if err != nil {
		m.Freeze(configs.Thawed)
		return err
	}
	for _, pid := range pids {
		if p, err := os.FindProcess(pid); err == nil {
			procs = append(procs, p)
			if err := p.Kill(); err != nil {
				logrus.Warn(err)
			}
		}
	}
	if err := m.Freeze(configs.Thawed); err != nil {
		logrus.Warn(err)
	}
	for _, p := range procs {
		if _, err := p.Wait(); err != nil {
			logrus.Warn(err)
		}
	}
	return nil
}
<commit_msg>Only set cwd when not empty<commit_after>// +build linux

package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"

	"github.com/Sirupsen/logrus"
	"github.com/opencontainers/runc/libcontainer/cgroups"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/opencontainers/runc/libcontainer/user"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/vishvananda/netlink"
)

type initType string

const (
	initSetns    initType = "setns"
	initStandard initType = "standard"
)

type pid struct {
	Pid int `json:"pid"`
}

// network is an internal struct used to setup container networks.
type network struct {
	configs.Network

	// TempVethPeerName is a unique temporary veth peer name that was placed into
	// the container's namespace.
	TempVethPeerName string `json:"temp_veth_peer_name"`
}

// initConfig is used for transferring parameters from Exec() to Init()
type initConfig struct {
	Args             []string        `json:"args"`
	Env              []string        `json:"env"`
	Cwd              string          `json:"cwd"`
	Capabilities     []string        `json:"capabilities"`
	User             string          `json:"user"`
	Config           *configs.Config `json:"config"`
	Console          string          `json:"console"`
	Networks         []*network      `json:"network"`
	PassedFilesCount int             `json:"passed_files_count"`
}

type initer interface {
	Init() error
}

func newContainerInit(t initType, pipe *os.File) (initer, error) {
	var config *initConfig
	if err := json.NewDecoder(pipe).Decode(&config); err != nil {
		return nil, err
	}
	if err := populateProcessEnvironment(config.Env); err != nil {
		return nil, err
	}
	switch t {
	case initSetns:
		return &linuxSetnsInit{
			config: config,
		}, nil
	case initStandard:
		return &linuxStandardInit{
			pipe:      pipe,
			parentPid: syscall.Getppid(),
			config:    config,
		}, nil
	}
	return nil, fmt.Errorf("unknown init type %q", t)
}

// populateProcessEnvironment loads the provided environment variables into the
// current processes's environment.
func populateProcessEnvironment(env []string) error {
	for _, pair := range env {
		p := strings.SplitN(pair, "=", 2)
		if len(p) < 2 {
			return fmt.Errorf("invalid environment '%v'", pair)
		}
		if err := os.Setenv(p[0], p[1]); err != nil {
			return err
		}
	}
	return nil
}

// finalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
	// Ensure that all unwanted fds we may have accidentally
	// inherited are marked close-on-exec so they stay out of the
	// container
	if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
		return err
	}

	capabilities := config.Config.Capabilities
	if config.Capabilities != nil {
		capabilities = config.Capabilities
	}
	w, err := newCapWhitelist(capabilities)
	if err != nil {
		return err
	}
	// drop capabilities in bounding set before changing user
	if err := w.dropBoundingSet(); err != nil {
		return err
	}
	// preserve existing capabilities while we change users
	if err := system.SetKeepCaps(); err != nil {
		return err
	}
	if err := setupUser(config); err != nil {
		return err
	}
	if err := system.ClearKeepCaps(); err != nil {
		return err
	}
	// drop all other capabilities
	if err := w.drop(); err != nil {
		return err
	}
	if config.Cwd != "" {
		if err := syscall.Chdir(config.Cwd); err != nil {
			return err
		}
	}
	return nil
}

// syncParentReady sends to the given pipe a JSON payload which indicates that
// the init is ready to Exec the child process. It then waits for the parent to
// indicate that it is cleared to Exec.
func syncParentReady(pipe io.ReadWriter) error {
	// Tell parent.
	if err := json.NewEncoder(pipe).Encode(procReady); err != nil {
		return err
	}

	// Wait for parent to give the all-clear.
	var procSync syncType
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}
		if procSync != procRun {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// joinExistingNamespaces gets all the namespace paths specified for the container and
// does a setns on the namespace fd so that the current process joins the namespace.
func joinExistingNamespaces(namespaces []configs.Namespace) error {
	for _, ns := range namespaces {
		if ns.Path != "" {
			f, err := os.OpenFile(ns.Path, os.O_RDONLY, 0)
			if err != nil {
				return err
			}
			err = system.Setns(f.Fd(), uintptr(ns.Syscall()))
			f.Close()
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// setupUser changes the groups, gid, and uid for the user inside the container
func setupUser(config *initConfig) error {
	// Set up defaults.
	defaultExecUser := user.ExecUser{
		Uid:  syscall.Getuid(),
		Gid:  syscall.Getgid(),
		Home: "/",
	}
	passwdPath, err := user.GetPasswdPath()
	if err != nil {
		return err
	}
	groupPath, err := user.GetGroupPath()
	if err != nil {
		return err
	}
	execUser, err := user.GetExecUserPath(config.User, &defaultExecUser, passwdPath, groupPath)
	if err != nil {
		return err
	}

	var addGroups []int
	if len(config.Config.AdditionalGroups) > 0 {
		addGroups, err = user.GetAdditionalGroupsPath(config.Config.AdditionalGroups, groupPath)
		if err != nil {
			return err
		}
	}
	// before we change to the container's user make sure that the processes STDIO
	// is correctly owned by the user that we are switching to.
	if err := fixStdioPermissions(execUser); err != nil {
		return err
	}
	suppGroups := append(execUser.Sgids, addGroups...)
	if err := syscall.Setgroups(suppGroups); err != nil {
		return err
	}

	if err := system.Setgid(execUser.Gid); err != nil {
		return err
	}
	if err := system.Setuid(execUser.Uid); err != nil {
		return err
	}
	// if we didn't get HOME already, set it based on the user's HOME
	if envHome := os.Getenv("HOME"); envHome == "" {
		if err := os.Setenv("HOME", execUser.Home); err != nil {
			return err
		}
	}
	return nil
}

// fixStdioPermissions fixes the permissions of PID 1's STDIO within the container to the specified user.
// The ownership needs to match because it is created outside of the container and needs to be
// localized.
func fixStdioPermissions(u *user.ExecUser) error {
	var null syscall.Stat_t
	if err := syscall.Stat("/dev/null", &null); err != nil {
		return err
	}
	for _, fd := range []uintptr{
		os.Stdin.Fd(),
		os.Stderr.Fd(),
		os.Stdout.Fd(),
	} {
		var s syscall.Stat_t
		if err := syscall.Fstat(int(fd), &s); err != nil {
			return err
		}
		// skip chown of /dev/null if it was used as one of the STDIO fds.
		if s.Rdev == null.Rdev {
			continue
		}
		if err := syscall.Fchown(int(fd), u.Uid, u.Gid); err != nil {
			return err
		}
	}
	return nil
}

// setupNetwork sets up and initializes any network interface inside the container.
func setupNetwork(config *initConfig) error {
	for _, config := range config.Networks {
		strategy, err := getStrategy(config.Type)
		if err != nil {
			return err
		}
		if err := strategy.initialize(config); err != nil {
			return err
		}
	}
	return nil
}

func setupRoute(config *configs.Config) error {
	for _, config := range config.Routes {
		_, dst, err := net.ParseCIDR(config.Destination)
		if err != nil {
			return err
		}
		src := net.ParseIP(config.Source)
		if src == nil {
			return fmt.Errorf("Invalid source for route: %s", config.Source)
		}
		gw := net.ParseIP(config.Gateway)
		if gw == nil {
			return fmt.Errorf("Invalid gateway for route: %s", config.Gateway)
		}
		l, err := netlink.LinkByName(config.InterfaceName)
		if err != nil {
			return err
		}
		route := &netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			Dst:       dst,
			Src:       src,
			Gw:        gw,
			LinkIndex: l.Attrs().Index,
		}
		if err := netlink.RouteAdd(route); err != nil {
			return err
		}
	}
	return nil
}

func setupRlimits(config *configs.Config) error {
	for _, rlimit := range config.Rlimits {
		l := &syscall.Rlimit{Max: rlimit.Hard, Cur: rlimit.Soft}
		if err := syscall.Setrlimit(rlimit.Type, l); err != nil {
			return fmt.Errorf("error setting rlimit type %v: %v", rlimit.Type, err)
		}
	}
	return nil
}

func setOomScoreAdj(oomScoreAdj int) error {
	path := "/proc/self/oom_score_adj"
	return ioutil.WriteFile(path, []byte(strconv.Itoa(oomScoreAdj)), 0700)
}

// killCgroupProcesses freezes then iterates over all the processes inside the
// manager's cgroups sending a SIGKILL to each process then waiting for them to
// exit.
func killCgroupProcesses(m cgroups.Manager) error {
	var procs []*os.Process
	if err := m.Freeze(configs.Frozen); err != nil {
		logrus.Warn(err)
	}
	pids, err := m.GetAllPids()
	if err != nil {
		m.Freeze(configs.Thawed)
		return err
	}
	for _, pid := range pids {
		if p, err := os.FindProcess(pid); err == nil {
			procs = append(procs, p)
			if err := p.Kill(); err != nil {
				logrus.Warn(err)
			}
		}
	}
	if err := m.Freeze(configs.Thawed); err != nil {
		logrus.Warn(err)
	}
	for _, p := range procs {
		if _, err := p.Wait(); err != nil {
			logrus.Warn(err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package errors

import (
	"strconv"
)

type Error struct {
	Code    int
	Name    string
	Message string
}

func (e Error) Error() string {
	return strconv.Itoa(e.Code) + ":" + e.Message
}

var (
	InvalidParameter = Error{400, "InvalidParameter", "A parameter specified in a request is not valid, is unsupported, or cannot be used."}
	InternalError    = Error{500, "InternalError", "An internal error has occurred. Retry your request."}
)

type ErrorResponse struct {
	Code    int    `json:"code"`
	Name    string `json:"name"`
	Message string `json:"Message"`
}

func NewErrorResponse(err Error) *ErrorResponse {
	return &ErrorResponse{
		Code:    err.Code,
		Name:    err.Name,
		Message: err.Message,
	}
}
<commit_msg>add forbiddent errors<commit_after>package errors

import (
	"strconv"
)

type Error struct {
	Code    int
	Name    string
	Message string
}

func (e Error) Error() string {
	return strconv.Itoa(e.Code) + ":" + e.Message
}

var (
	InvalidParameter = Error{400, "InvalidParameter", "A parameter specified in a request is not valid, is unsupported, or cannot be used."}
	AccessForbidden  = Error{403, "AccessDenied", "You have no correct right to access"}
	InternalError    = Error{500, "InternalError", "An internal error has occurred. Retry your request."}
)

type ErrorResponse struct {
	Code    int    `json:"code"`
	Name    string `json:"name"`
	Message string `json:"Message"`
}

func NewErrorResponse(err Error) *ErrorResponse {
	return &ErrorResponse{
		Code:    err.Code,
		Name:    err.Name,
		Message: err.Message,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"strings"
	"time"
)

func main() {
	timesPer := 50

	// just to create this one container for docker start command
	run("docker run --name reuse treeder/hello:sh")
	cmds := []string{
		// "./hello.sh",
		// "docker run treeder/hello:sh",
		// "docker run --rm treeder/hello:sh",
		"docker start -a reuse",
	}
	for _, cmd := range cmds {
		fmt.Println("Running:", cmd)
		totalDuration, err := runTimes(timesPer, cmd)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("avg:", (totalDuration / time.Duration(timesPer)))
	}
}
func runTimes(x int, s string) (time.Duration, error) {
	totalDuration := time.Duration(0)
	for i := 0; i < x; i++ {
		td, err := run(s)
		if err != nil {
			return totalDuration, err
		}
		totalDuration += td
	}
	return totalDuration, nil
}

func run(s string) (time.Duration, error) {
	cmd := exec.Command("time", strings.Split(s, " ")...)
	var outb, errb bytes.Buffer
	cmd.Stdout = &outb
	cmd.Stderr = &errb
	start := time.Now()
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
		return 0, err
	}
	duration := time.Now().Sub(start)
	// fmt.Println("duration:", duration)
	// o := outb.String()
	// e := errb.String()
	// fmt.Println("out:", o, "err:", e)
	return duration, nil
}
<commit_msg>Added all commands back in.<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"os/exec"
	"strings"
	"time"
)

func main() {
	timesPer := 50

	// just to create this one container for docker start command
	run("docker run --name reuse treeder/hello:sh")
	cmds := []string{
		"./hello.sh",
		"docker run treeder/hello:sh",
		"docker run --rm treeder/hello:sh",
		"docker start -a reuse",
	}
	for _, cmd := range cmds {
		fmt.Println("Running:", cmd)
		totalDuration, err := runTimes(timesPer, cmd)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("avg:", (totalDuration / time.Duration(timesPer)))
	}
}
func runTimes(x int, s string) (time.Duration, error) {
	totalDuration := time.Duration(0)
	for i := 0; i < x; i++ {
		td, err := run(s)
		if err != nil {
			return totalDuration, err
		}
		totalDuration += td
	}
	return totalDuration, nil
}

func run(s string) (time.Duration, error) {
	cmd := exec.Command("time", strings.Split(s, " ")...)
	var outb, errb bytes.Buffer
	cmd.Stdout = &outb
	cmd.Stderr = &errb
	start := time.Now()
	err := cmd.Run()
	if err != nil {
		fmt.Println(err)
		return 0, err
	}
	duration := time.Now().Sub(start)
	// fmt.Println("duration:", duration)
	// o := outb.String()
	// e := errb.String()
	// fmt.Println("out:", o, "err:", e)
	return duration, nil
}
<|endoftext|>


<commit_before><commit_msg>Fix race in executor/depot/uploader test<commit_after><|endoftext|>


<commit_before>package main

import (
	"bufio"
	"hash/fnv"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	pb "github.com/brotherlogic/cardserver/card"
)

const (
	datestr = "2006-01-02 15:04"
)

var (
	daysOfTheWeek = []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
)

func getTime(timestr string) (time.Time, error) {
	t := time.Now()
	return time.ParseInLocation(datestr, timestr, t.Location())
}

func getUnixTime(timestr string) int64 {
	t, _ := getTime(timestr)
	return t.Unix()
}

type cronentry struct {
	time  *time.Time
	daily bool
	day   string
	text  string
	hash  string
}

// Cron the main cronentry holder
type Cron struct {
	crons   []cronentry
	last    time.Time
	dir     string
	written map[string]bool
}

// Init prepares a cron for use
func Init(dir string) *Cron {
	c := &Cron{}
	c.last = time.Unix(0, 0)
	c.dir = dir

	if _, err := os.Stat(c.dir); os.IsNotExist(err) {
		os.MkdirAll(c.dir, 0777)
	}

	c.loadhash()

	return c
}

// InitFromFile loads a cron from a given file
func InitFromFile(dir string, filename string) *Cron {
	c := Init(dir)

	file, _ := os.Open(filename)
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.loadline(scanner.Text())
	}

	//if err := scanner.Err(); err != nil {
	//	log.Fatal(err)
	//}

	return c
}

func hash(s string) string {
	h := fnv.New32a()
	h.Write([]byte(s))
	return strconv.Itoa(int(h.Sum32()))
}

func (c *Cron) loadhash() {
	log.Printf("LOADING HASh")
	c.written = make(map[string]bool)
	file, _ := os.Open(c.dir + "/hash")
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.written[scanner.Text()] = true
	}

	for key, blah := range c.written {
		log.Printf("LOADED %v -> %v", key, blah)
	}
}

func (c *Cron) clearhash() {
	c.written = make(map[string]bool)
	os.Remove(c.dir + "/hash")
}

func (c *Cron) writehash(card pb.Card) {
	if _, err := os.Stat(c.dir + "/hash"); os.IsNotExist(err) {
		os.Create(c.dir + "/hash")
	}

	f, _ := os.OpenFile(c.dir+"/hash", os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	c.written[hash(card.String())] = true
	f.WriteString(hash(card.String()) + "\n")
}

func (c *Cron) isWritten(card pb.Card) bool {
	return c.written[hash(card.String())]
}

//GetCards gets the cards between the specified times
func (c *Cron) GetCards(ts time.Time, te time.Time) []*pb.Card {
	log.Printf("FROM %v to %v with %v", ts, te, c.crons)
	newindex := 0
	var cards []*pb.Card
	for i, entry := range c.crons {
		if entry.time != nil {
			if (entry.time.Before(te) || entry.time.Equal(te)) && entry.time.After(ts) {
				newindex = i + 1
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: entry.time.Unix(), Priority: -1, Hash: entry.hash}
				if !c.isWritten(card) {
					cards = append(cards, &card)
					c.writehash(card)
				}
			}
		} else if entry.day != "" {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))

			count := 1
			for stime.Before(te) {
				if stime.Format("Mon") == entry.day {
					card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
					if !c.isWritten(card) {
						cards = append(cards, &card)
						c.writehash(card)
					}
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		} else if entry.daily {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))
			stime = stime.Add(time.Hour * time.Duration(5))

			count := 1
			for stime.Before(te) {
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
				log.Printf("WRITTEN = %v", c.isWritten(card))
				if !c.isWritten(card) {
					log.Printf("%v - %v", stime, entry.hash)
					cards = append(cards, &card)
					c.writehash(card)
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		}
	}
	c.crons = c.crons[newindex:]
	return cards
}

func (c *Cron) logd() {
	log.Printf("LEN = %v", len(c.crons))
}

func matches(s string, strs []string) bool {
	for _, str := range strs {
		if s == str {
			return true
		}
	}
	return false
}

func (c *Cron) loadline(line string) {
	elems := strings.Split(line, "~")
	entry := cronentry{}
	if matches(elems[0], daysOfTheWeek) {
		entry.day = elems[0]
	} else if elems[0] == "Daily" {
		entry.daily = true
	} else {
		t, _ := getTime(elems[0])
		entry.time = &t
	}
	entry.text = elems[2] + "|" + elems[3]
	entry.hash = elems[1] + "-" + elems[4]

	c.crons = append(c.crons, entry)
}
<commit_msg>Cleared log outputs<commit_after>package main

import (
	"bufio"
	"hash/fnv"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	pb "github.com/brotherlogic/cardserver/card"
)

const (
	datestr = "2006-01-02 15:04"
)

var (
	daysOfTheWeek = []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
)

func getTime(timestr string) (time.Time, error) {
	t := time.Now()
	return time.ParseInLocation(datestr, timestr, t.Location())
}

func getUnixTime(timestr string) int64 {
	t, _ := getTime(timestr)
	return t.Unix()
}

type cronentry struct {
	time  *time.Time
	daily bool
	day   string
	text  string
	hash  string
}

// Cron the main cronentry holder
type Cron struct {
	crons   []cronentry
	last    time.Time
	dir     string
	written map[string]bool
}

// Init prepares a cron for use
func Init(dir string) *Cron {
	c := &Cron{}
	c.last = time.Unix(0, 0)
	c.dir = dir

	if _, err := os.Stat(c.dir); os.IsNotExist(err) {
		os.MkdirAll(c.dir, 0777)
	}

	c.loadhash()

	return c
}

// InitFromFile loads a cron from a given file
func InitFromFile(dir string, filename string) *Cron {
	c := Init(dir)

	file, _ := os.Open(filename)
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.loadline(scanner.Text())
	}

	//if err := scanner.Err(); err != nil {
	//	log.Fatal(err)
	//}

	return c
}

func hash(s string) string {
	h := fnv.New32a()
	h.Write([]byte(s))
	return strconv.Itoa(int(h.Sum32()))
}

func (c *Cron) loadhash() {
	c.written = make(map[string]bool)
	file, _ := os.Open(c.dir + "/hash")
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.written[scanner.Text()] = true
	}

}

func (c *Cron) clearhash() {
	c.written = make(map[string]bool)
	os.Remove(c.dir + "/hash")
}

func (c *Cron) writehash(card pb.Card) {
	if _, err := os.Stat(c.dir + "/hash"); os.IsNotExist(err) {
		os.Create(c.dir + "/hash")
	}

	f, _ := os.OpenFile(c.dir+"/hash", os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	c.written[hash(card.String())] = true
	f.WriteString(hash(card.String()) + "\n")
}

func (c *Cron) isWritten(card pb.Card) bool {
	return c.written[hash(card.String())]
}

//GetCards gets the cards between the specified times
func (c *Cron) GetCards(ts time.Time, te time.Time) []*pb.Card {
	newindex := 0
	var cards []*pb.Card
	for i, entry := range c.crons {
		if entry.time != nil {
			if (entry.time.Before(te) || entry.time.Equal(te)) && entry.time.After(ts) {
				newindex = i + 1
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: entry.time.Unix(), Priority: -1, Hash: entry.hash}
				if !c.isWritten(card) {
					cards = append(cards, &card)
					c.writehash(card)
				}
			}
		} else if entry.day != "" {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))

			count := 1
			for stime.Before(te) {
				if stime.Format("Mon") == entry.day {
					card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
					if !c.isWritten(card) {
						cards = append(cards, &card)
						c.writehash(card)
					}
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		} else if entry.daily {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))
			stime = stime.Add(time.Hour * time.Duration(5))

			count := 1
			for stime.Before(te) {
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
				log.Printf("WRITTEN = %v", c.isWritten(card))
				if !c.isWritten(card) {
					log.Printf("%v - %v", stime, entry.hash)
					cards = append(cards, &card)
					c.writehash(card)
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		}
	}
	c.crons = c.crons[newindex:]
	return cards
}

func (c *Cron) logd() {
	log.Printf("LEN = %v", len(c.crons))
}

func matches(s string, strs []string) bool {
	for _, str := range strs {
		if s == str {
			return true
		}
	}
	return false
}

func (c *Cron) loadline(line string) {
	elems := strings.Split(line, "~")
	entry := cronentry{}
	if matches(elems[0], daysOfTheWeek) {
		entry.day = elems[0]
	} else if elems[0] == "Daily" {
		entry.daily = true
	} else {
		t, _ := getTime(elems[0])
		entry.time = &t
	}
	entry.text = elems[2] + "|" + elems[3]
	entry.hash = elems[1] + "-" + elems[4]

	c.crons = append(c.crons, entry)
}
<|endoftext|>


<commit_before>package game

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"

	"golang.org/x/net/websocket"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/net/encoding"
	"github.com/ghthor/engine/net/protocol"
	"github.com/ghthor/engine/sim"
)

type LoginReq struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type packetHandler func(actorHandler) (actorHandler, error)

type actorHandler struct {
	protocol.Conn
	handlePacket packetHandler

	sim       sim.RunningSimulation
	datastore datastore.Datastore

	actor datastore.Actor
}

// Starts the packet handler loop.
// This function is blocking.
func (c actorHandler) run() (err error) {
	for {
		c, err = c.handlePacket(c)
		if err != nil {
			break
		}
	}
	return
}

var ErrWebsocketClientDisconnected = errors.New("websocket client disconnected")

type ErrUnexpectedPacket struct {
	Handler packetHandler
	Packet  encoding.Packet
}

func (e ErrUnexpectedPacket) String() string {
	return fmt.Sprint("unexpected packet {%v} in %v", e.Packet, e.Handler)
}

func (e ErrUnexpectedPacket) Error() string {
	return e.String()
}

// An implementation of packetHandler which
// will handle an actor logging in.
func (c actorHandler) loginHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login":
			return c.respondToLoginReq(packet)
		case "create":
			return c.respondToCreateReq(packet)
		default:
		}
	default:
	}

	// TODO Improve this message with how to login
	c.SendMessage("notLoggedIn", "")
	return c, nil
}

// A login request is a event that can modify the
// state of the packet handler. If the login is
// successful the packet handler will transition
// to the input handler..
func (c actorHandler) respondToLoginReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		log.Printf("login failed: actor %s doesn't exist", r.Name)
		c.SendJson("actorDoesntExist", r)
		return c, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		log.Printf("login failed: password for %s was incorrect", r.Name)
		c.SendMessage("authFailed", r.Name)
		return c, nil
	}

	c = c.loginActor(actor)

	log.Print("login success: ", r.Name)
	c.SendJson("loginSuccess", actor)
	return c, nil
}

// A create request is an event that can modify th
// state of the packet handler. If the create is
// successful the packet handler will transition
// in the input handler.
func (c actorHandler) respondToCreateReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		// TODO determine if this an error that should terminate the connection
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		log.Printf("create failed: actor %s already exists", r.Name)
		c.SendMessage("actorAlreadyExists", "actor already exists")
		return c, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		// we should retry contacting the database or something
		return c, err
	}

	c = c.loginActor(actor)

	log.Print("created actor: ", actor.Name)

	c.SendJson("createSuccess", actor)

	return c, nil
}

func (c actorHandler) loginActor(actor datastore.Actor) actorHandler {
	// Set the actor this connection is now associated with
	c.actor = actor
	// Mutate the packet handler into the next state
	c.handlePacket = (actorHandler).inputHandler

	// TODO hook the actor into the simulation
	return c
}

// An implementation of packetHandler which will
// process input requests and prepare them
// for consumption by the input phase.
func (c actorHandler) inputHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login", "create":
			goto alreadyLoggedIn
		}
	}

alreadyLoggedIn:
	c.SendMessage("alreadyLoggedIn", "an actor has already been logged into this connection")
	return c, nil
}

// Return the actor bound to the connection.
func (c actorHandler) Actor() datastore.Actor { return c.actor }

func newWebsocketActorHandler(sim sim.RunningSimulation, datastore datastore.Datastore) websocket.Handler {
	return func(ws *websocket.Conn) {
		err := actorHandler{
			Conn:         protocol.NewWebsocketConn(ws),
			handlePacket: (actorHandler).loginHandler,

			sim:       sim,
			datastore: datastore,
		}.run()

		// TODO Maybe send a http response if there is an error
		if err != nil {
			log.Printf("disconnected: %v", err)
		}
	}
}
<commit_msg>A successful login creates a new actor and connects it to the simulation<commit_after>package game

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"

	"golang.org/x/net/websocket"

	"github.com/ghthor/aodd/game/datastore"
	"github.com/ghthor/engine/net/encoding"
	"github.com/ghthor/engine/net/protocol"
	"github.com/ghthor/engine/sim"
)

type LoginReq struct {
	Name     string `json:"name"`
	Password string `json:"password"`
}

type packetHandler func(actorHandler) (actorHandler, error)

type actorHandler struct {
	protocol.Conn
	handlePacket packetHandler

	sim       sim.RunningSimulation
	datastore datastore.Datastore

	actor actor
}

// Starts the packet handler loop.
// This function is blocking.
func (c actorHandler) run() (err error) {
	for {
		c, err = c.handlePacket(c)
		if err != nil {
			break
		}
	}
	return
}

var ErrWebsocketClientDisconnected = errors.New("websocket client disconnected")

type ErrUnexpectedPacket struct {
	Handler packetHandler
	Packet  encoding.Packet
}

func (e ErrUnexpectedPacket) String() string {
	return fmt.Sprint("unexpected packet {%v} in %v", e.Packet, e.Handler)
}

func (e ErrUnexpectedPacket) Error() string {
	return e.String()
}

// An implementation of packetHandler which
// will handle an actor logging in.
func (c actorHandler) loginHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login":
			return c.respondToLoginReq(packet)
		case "create":
			return c.respondToCreateReq(packet)
		default:
		}
	default:
	}

	// TODO Improve this message with how to login
	c.SendMessage("notLoggedIn", "")
	return c, nil
}

// A login request is a event that can modify the
// state of the packet handler. If the login is
// successful the packet handler will transition
// to the input handler..
func (c actorHandler) respondToLoginReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	actor, exists := c.datastore.ActorExists(r.Name)
	if !exists {
		log.Printf("login failed: actor %s doesn't exist", r.Name)
		c.SendJson("actorDoesntExist", r)
		return c, nil
	}

	if !actor.Authenticate(r.Name, r.Password) {
		log.Printf("login failed: password for %s was incorrect", r.Name)
		c.SendMessage("authFailed", r.Name)
		return c, nil
	}

	c = c.loginActor(actor)

	log.Print("login success: ", r.Name)
	c.SendJson("loginSuccess", actor)
	return c, nil
}

// A create request is an event that can modify th
// state of the packet handler. If the create is
// successful the packet handler will transition
// in the input handler.
func (c actorHandler) respondToCreateReq(p encoding.Packet) (actorHandler, error) {
	r := LoginReq{}

	err := json.Unmarshal([]byte(p.Payload), &r)
	if err != nil {
		// TODO determine if this an error that should terminate the connection
		return c, errors.New(fmt.Sprint("error parsing login request:", err))
	}

	_, exists := c.datastore.ActorExists(r.Name)
	if exists {
		log.Printf("create failed: actor %s already exists", r.Name)
		c.SendMessage("actorAlreadyExists", "actor already exists")
		return c, nil
	}

	actor, err := c.datastore.AddActor(r.Name, r.Password)
	if err != nil {
		// TODO Instead of terminating the connection here
		// we should retry contacting the database or something
		return c, err
	}

	c = c.loginActor(actor)

	log.Print("created actor: ", actor.Name)

	c.SendJson("createSuccess", actor)

	return c, nil
}

// Creates a new actor struct using a datastore.Actor struct.
// Adds this new actor into the simulation.
func (c actorHandler) loginActor(dsactor datastore.Actor) actorHandler {
	// Set the actor this connection is now associated with
	// Mutate the packet handler into the next state
	c.handlePacket = (actorHandler).inputHandler

	// Create an actorEntity for this object
	c.actor = actor{
		&actorEntity{
			id: dsactor.Id,

			name: dsactor.Name,

			cell:   dsactor.Loc,
			facing: dsactor.Facing,
		},

		newActorConn(c),
	}

	// TODO don't ignore this error
	c.sim.ConnectActor(c.actor)
	return c
}

// An implementation of packetHandler which will
// process input requests and prepare them
// for consumption by the input phase.
func (c actorHandler) inputHandler() (actorHandler, error) {
	packet, err := c.Read()
	if err != nil {
		return c, err
	}

	if packet.Type == encoding.PT_DISCONNECT {
		return c, ErrWebsocketClientDisconnected
	}

	switch packet.Type {
	case encoding.PT_JSON:
		switch packet.Msg {
		case "login", "create":
			goto alreadyLoggedIn
		}
	}

alreadyLoggedIn:
	c.SendMessage("alreadyLoggedIn", "an actor has already been logged into this connection")
	return c, nil
}

// Return the actor bound to the connection.
func (c actorHandler) Actor() datastore.Actor {
	if c.actor.actorEntity == nil {
		return datastore.Actor{}
	}

	return datastore.Actor{
		Id: c.actor.id,

		Name: c.actor.name,

		Loc:    c.actor.cell,
		Facing: c.actor.facing,
	}
}

func newWebsocketActorHandler(sim sim.RunningSimulation, datastore datastore.Datastore) websocket.Handler {
	return func(ws *websocket.Conn) {
		err := actorHandler{
			Conn:         protocol.NewWebsocketConn(ws),
			handlePacket: (actorHandler).loginHandler,

			sim:       sim,
			datastore: datastore,
		}.run()

		// TODO Maybe send a http response if there is an error
		if err != nil {
			log.Printf("disconnected: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright The Kubernetes 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.
*/

// Code generated by client-gen. DO NOT EDIT.

package fake

import (
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	runtime "k8s.io/apimachinery/pkg/runtime"
	schema "k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
)

var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
var parameterCodec = runtime.NewParameterCodec(scheme)
var localSchemeBuilder = runtime.SchemeBuilder{
	apiregistrationv1beta1.AddToScheme,
	apiregistrationv1.AddToScheme,
}

// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
//   import (
//     "k8s.io/client-go/kubernetes"
//     clientsetscheme "k8s.io/client-go/kubernetes/scheme"
//     aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
//   )
//
//   kclientset, _ := kubernetes.NewForConfig(c)
//   _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme

func init() {
	v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
	utilruntime.Must(AddToScheme(scheme))
}
<commit_msg>Run ./hack/update-codegen.sh.<commit_after>/*
Copyright The Kubernetes 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.
*/

// Code generated by client-gen. DO NOT EDIT.

package fake

import (
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	runtime "k8s.io/apimachinery/pkg/runtime"
	schema "k8s.io/apimachinery/pkg/runtime/schema"
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
	apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
)

var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)

var localSchemeBuilder = runtime.SchemeBuilder{
	apiregistrationv1beta1.AddToScheme,
	apiregistrationv1.AddToScheme,
}

// AddToScheme adds all types of this clientset into the given scheme. This allows composition
// of clientsets, like in:
//
//   import (
//     "k8s.io/client-go/kubernetes"
//     clientsetscheme "k8s.io/client-go/kubernetes/scheme"
//     aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
//   )
//
//   kclientset, _ := kubernetes.NewForConfig(c)
//   _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
//
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
// correctly.
var AddToScheme = localSchemeBuilder.AddToScheme

func init() {
	v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
	utilruntime.Must(AddToScheme(scheme))
}
<|endoftext|>


<commit_before>package lazyquicktime

import "fmt"
import "image"
import "errors"

import "time"
import "log"

import "github.com/amarburg/go-lazyfs"
import "github.com/amarburg/go-quicktime"
import "github.com/amarburg/go-prores-ffmpeg"

// Version string for the package
const Version = "v0.1.0"

// LazyQuicktime stores the metadata from a Quicktime Movie
// extracted via a lazyFS.
// Stores a copy of the lazyFS to allow lazy-loading
type LazyQuicktime struct {
	file lazyfs.FileSource
	Tree quicktime.AtomArray
	Trak quicktime.TRAKAtom
	Stbl *quicktime.STBLAtom
	Mvhd quicktime.MVHDAtom

	FileSize int64
}

// LoadMovMetadata creates a LazyQuicktime by querying a lazyfs.FileSource.
func LoadMovMetadata(file lazyfs.FileSource) (*LazyQuicktime, error) {

	mov := &LazyQuicktime{file: file}

	sz, err := file.FileSize()
	if sz < 0 || err != nil {
		return mov, fmt.Errorf("unable to retrieve file size")
	}

	mov.FileSize = sz

	setEagerload := func(conf *quicktime.BuildTreeConfig) {
		conf.EagerloadTypes = []string{"moov"}
	}

	//fmt.Println("Reading Mov of size ", mov.FileSize)
	tree, err := quicktime.BuildTree(file, uint64(mov.FileSize), setEagerload)

	if err != nil {
		return mov, err
	}
	mov.Tree = tree

	moov := mov.Tree.FindAtom("moov")
	if moov == nil {
		return mov, errors.New("can't find MOOV atom")
	}

	mvhd := moov.FindAtom("mvhd")
	if mvhd == nil {
		return mov, errors.New("couldn't find MVHD in the moov atom")
	}
	mov.Mvhd, _ = quicktime.ParseMVHD(mvhd)

	tracks := moov.FindAtoms("trak")
	if tracks == nil || len(tracks) == 0 {
		return mov, errors.New("couldn't find any TRAKs in the MOOV")
	}

	var track *quicktime.Atom
	for i, t := range tracks {
		mdia := t.FindAtom("mdia")
		if mdia == nil {
			fmt.Println("No mdia track", i)
			continue
		}

		minf := mdia.FindAtom("minf")
		if minf == nil {
			fmt.Println("No minf track", i)
			continue
		}

		if minf.FindAtom("vmhd") != nil {
			track = t
			break
		}
	}

	if track == nil {
		return mov, errors.New("couldn't identify the Video track")
	}

	mov.Trak, err = quicktime.ParseTRAK(track)
	if err != nil {
		return mov, fmt.Errorf("unable to parse TRAK atom: %s", err.Error())
	}

	mov.Stbl = &mov.Trak.Mdia.Minf.Stbl // Just an alias

	return mov, nil
}

// NumFrames reports the number of frames in the LazyQuicktime
func (mov *LazyQuicktime) NumFrames() int {
	return mov.Stbl.NumFrames()
}

// Duration reports the length of the LazyQuicktime in seconds.
func (mov *LazyQuicktime) Duration() float32 {
	return mov.Mvhd.Duration()
}

// ExtractFrame extracts an individual frame from a ProRes file as an Image
func (mov *LazyQuicktime) ExtractFrame(frame int) (image.Image, error) {
	return mov.ExtractNRGBA(frame)
}

// ExtractNRGBA extracts an individual frame from a ProRes file specifically
// as an image.NRGBA
func (mov *LazyQuicktime) ExtractNRGBA(frame int) (*image.NRGBA, error) {

	frameOffset, frameSize, _ := mov.Stbl.SampleOffsetSize(frame)

	//fmt.Printf("Extracting frame %d at offset %d size %d\n", frame, frame_offset, frame_size)

	buf := make([]byte, frameSize)

	if buf == nil {
		return nil, fmt.Errorf("couldn't make buffer of size %d", frameSize)
	}

	startRead := time.Now()
	n, _ := mov.file.ReadAt(buf, frameOffset)
	log.Printf("HTTP read took %s", time.Since(startRead))

	if n != frameSize {
		return nil, fmt.Errorf("tried to read %d bytes but got %d instead", frameSize, n)
	}

	width, height := int(mov.Trak.Tkhd.Width), int(mov.Trak.Tkhd.Height)

	//startDecode := time.Now()
	img, err := prores.DecodeProRes(buf, width, height)
	//log.Printf("Prores decode took %s", time.Since(startDecode))

	return img, err

}
<commit_msg>Exposed the lazyfs source within e LazyQuicktime<commit_after>package lazyquicktime

import "fmt"
import "image"
import "errors"

import "time"
import "log"

import "github.com/amarburg/go-lazyfs"
import "github.com/amarburg/go-quicktime"
import "github.com/amarburg/go-prores-ffmpeg"

// Version string for the package
const Version = "v0.1.0"

// LazyQuicktime stores the metadata from a Quicktime Movie
// extracted via a lazyFS.
// Stores a copy of the lazyFS to allow lazy-loading
type LazyQuicktime struct {
	Source lazyfs.FileSource
	Tree   quicktime.AtomArray
	Trak   quicktime.TRAKAtom
	Stbl   *quicktime.STBLAtom
	Mvhd   quicktime.MVHDAtom

	FileSize int64
}

// LoadMovMetadata creates a LazyQuicktime by querying a lazyfs.FileSource.
func LoadMovMetadata(file lazyfs.FileSource) (*LazyQuicktime, error) {

	mov := &LazyQuicktime{Source: file}

	sz, err := file.FileSize()
	if sz < 0 || err != nil {
		return mov, fmt.Errorf("unable to retrieve file size")
	}

	mov.FileSize = sz

	setEagerload := func(conf *quicktime.BuildTreeConfig) {
		conf.EagerloadTypes = []string{"moov"}
	}

	//fmt.Println("Reading Mov of size ", mov.FileSize)
	tree, err := quicktime.BuildTree(file, uint64(mov.FileSize), setEagerload)

	if err != nil {
		return mov, err
	}
	mov.Tree = tree

	moov := mov.Tree.FindAtom("moov")
	if moov == nil {
		return mov, errors.New("can't find MOOV atom")
	}

	mvhd := moov.FindAtom("mvhd")
	if mvhd == nil {
		return mov, errors.New("couldn't find MVHD in the moov atom")
	}
	mov.Mvhd, _ = quicktime.ParseMVHD(mvhd)

	tracks := moov.FindAtoms("trak")
	if tracks == nil || len(tracks) == 0 {
		return mov, errors.New("couldn't find any TRAKs in the MOOV")
	}

	var track *quicktime.Atom
	for i, t := range tracks {
		mdia := t.FindAtom("mdia")
		if mdia == nil {
			fmt.Println("No mdia track", i)
			continue
		}

		minf := mdia.FindAtom("minf")
		if minf == nil {
			fmt.Println("No minf track", i)
			continue
		}

		if minf.FindAtom("vmhd") != nil {
			track = t
			break
		}
	}

	if track == nil {
		return mov, errors.New("couldn't identify the Video track")
	}

	mov.Trak, err = quicktime.ParseTRAK(track)
	if err != nil {
		return mov, fmt.Errorf("unable to parse TRAK atom: %s", err.Error())
	}

	mov.Stbl = &mov.Trak.Mdia.Minf.Stbl // Just an alias

	return mov, nil
}

// NumFrames reports the number of frames in the LazyQuicktime
func (mov *LazyQuicktime) NumFrames() int {
	return mov.Stbl.NumFrames()
}

// Duration reports the length of the LazyQuicktime in seconds.
func (mov *LazyQuicktime) Duration() float32 {
	return mov.Mvhd.Duration()
}

// ExtractFrame extracts an individual frame from a ProRes file as an Image
func (mov *LazyQuicktime) ExtractFrame(frame int) (image.Image, error) {
	return mov.ExtractNRGBA(frame)
}

// ExtractNRGBA extracts an individual frame from a ProRes file specifically
// as an image.NRGBA
func (mov *LazyQuicktime) ExtractNRGBA(frame int) (*image.NRGBA, error) {

	frameOffset, frameSize, _ := mov.Stbl.SampleOffsetSize(frame)

	//fmt.Printf("Extracting frame %d at offset %d size %d\n", frame, frame_offset, frame_size)

	buf := make([]byte, frameSize)

	if buf == nil {
		return nil, fmt.Errorf("couldn't make buffer of size %d", frameSize)
	}

	startRead := time.Now()
	n, _ := mov.Source.ReadAt(buf, frameOffset)
	log.Printf("HTTP read took %s", time.Since(startRead))

	if n != frameSize {
		return nil, fmt.Errorf("tried to read %d bytes but got %d instead", frameSize, n)
	}

	width, height := int(mov.Trak.Tkhd.Width), int(mov.Trak.Tkhd.Height)

	//startDecode := time.Now()
	img, err := prores.DecodeProRes(buf, width, height)
	//log.Printf("Prores decode took %s", time.Since(startDecode))

	return img, err

}
<|endoftext|>


<commit_before>package torrent

import (
	"io"
	"io/ioutil"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent/internal/testutil"
	"github.com/anacrolix/torrent/storage"
)

func TestDropTorrentWhileHashing(t *testing.T) {
	cfg := TestingConfig()
	// Ensure the data is present when the torrent is added, and not obtained
	// over the network as the test runs.
	cfg.DownloadRateLimiter = rate.NewLimiter(0, 0)
	cl, err := NewClient(cfg)
	require.NoError(t, err)
	defer cl.Close()

	td, mi := testutil.GreetingTestTorrent()
	tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
		Storage:   storage.NewMMap(td),
		InfoHash:  mi.HashInfoBytes(),
		InfoBytes: mi.InfoBytes,
	})
	require.NoError(t, err)
	assert.True(t, new)

	// Make sure some of the data is actually there.
	sub := tt.SubscribePieceStateChanges()
	for range sub.Values {
		if tt.BytesCompleted() > 0 {
			break
		}
	}
	sub.Close()

	r := tt.NewReader()
	go tt.Drop()
	io.Copy(ioutil.Discard, r)
}
<commit_msg>Improve issue #211 test name<commit_after>package torrent

import (
	"io"
	"io/ioutil"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent/internal/testutil"
	"github.com/anacrolix/torrent/storage"
)

func TestDropTorrentWithMmapStorageWhileHashing(t *testing.T) {
	cfg := TestingConfig()
	// Ensure the data is present when the torrent is added, and not obtained
	// over the network as the test runs.
	cfg.DownloadRateLimiter = rate.NewLimiter(0, 0)
	cl, err := NewClient(cfg)
	require.NoError(t, err)
	defer cl.Close()

	td, mi := testutil.GreetingTestTorrent()
	tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
		Storage:   storage.NewMMap(td),
		InfoHash:  mi.HashInfoBytes(),
		InfoBytes: mi.InfoBytes,
	})
	require.NoError(t, err)
	assert.True(t, new)

	// Make sure some of the data is actually there.
	sub := tt.SubscribePieceStateChanges()
	for range sub.Values {
		if tt.BytesCompleted() > 0 {
			break
		}
	}
	sub.Close()

	r := tt.NewReader()
	go tt.Drop()
	io.Copy(ioutil.Discard, r)
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/atotto/clipboard"
	"github.com/kr/pretty"
	"github.com/tcolar/goed/core"
)

func (v *View) ClearSelections() {
	v.selections = []core.Selection{}
}

// Text returns the text contained in the selection of the given view
// Note: **NOT** a rectangle but from pt1 to pt2
func (v *View) SelectionText(s *core.Selection) [][]rune {
	cf := s.ColFrom
	ct := s.ColTo
	lt := s.LineTo
	lf := s.LineFrom
	if lf == lt {
		return *v.backend.Slice(lf, cf, lt, ct).Text()
	}
	// first line
	text := *v.backend.Slice(lf, cf, lf, -1).Text()
	for l := lf + 1; l < lt; l++ {
		// middle
		text = append(text, *v.backend.Slice(l, 0, l, -1).Text()...)
	}
	// last line
	text = append(text, *v.backend.Slice(lt, 0, lt, ct).Text()...)
	return text
}

// Selected returns whether the text at line, col is current selected
// also returns the matching selection, if any.
func (v *View) Selected(col, line int) (bool, *core.Selection) {
	for _, s := range v.selections {
		if line < s.LineFrom || line > s.LineTo {
			continue
		} else if line > s.LineFrom && line < s.LineTo {
			return true, &s
		} else if s.LineFrom == s.LineTo {
			return col >= s.ColFrom && col <= s.ColTo, &s
		} else if line == s.LineFrom && col >= s.ColFrom {
			return true, &s
		} else if line == s.LineTo && col <= s.ColTo {
			return true, &s
		}
	}
	return false, nil
}

func (v *View) Cut() {
	if len(v.selections) == 0 {
		v.SelectLine(v.CurLine())
	}
	v.SelectionCopy(&v.selections[0])
	v.SelectionDelete(&v.selections[0])
}

func (v *View) Copy() {
	if len(v.selections) == 0 {
		v.SelectLine(v.CurLine())
	}
	v.SelectionCopy(&v.selections[0])
}

func (v *View) SelectionCopy(s *core.Selection) {
	t := v.SelectionText(s)
	text := core.RunesToString(t)
	if len(text) == 0 {
		return
	}
	core.Ed.SetStatus(fmt.Sprintf("Copied %d lines to clipboard.", len(t)))
	clipboard.WriteAll(text)
}

func (v *View) SelectionDelete(s *core.Selection) {
	v.Delete(s.LineFrom, s.ColFrom, s.LineTo, s.ColTo, true)
}

func (v *View) Paste() {
	text, err := clipboard.ReadAll()
	if err != nil {
		core.Ed.SetStatusErr(err.Error())
		return
	}
	if len(v.selections) > 0 {
		v.DeleteCur()
	}
	ln, col := v.CurTextPos()
	v.Insert(ln, col, text, true)
}

var locationRegexp = regexp.MustCompile(`([^"\s(){}[\]<>,?|+=&^%#@!;':\x1B]+)(:\d+)?(:\d+)?`)

// Try to select a "location" from the given position
// a location is a path with possibly a line number and maybe a column number as well
func (v *View) ExpandSelectionPath(line, col int) *core.Selection {
	l := v.Line(v.slice, line)
	ln := string(l)
	// Note: Indexes taken and returned by FindAllStringIndex are in BYTES, not runes
	// not very inutitive to say the least
	c := core.RunesLen(l[:col])
	matches := locationRegexp.FindAllStringIndex(ln, -1)
	var best []int
	// Find the "narrowest" match around the cursor
	for _, s := range matches {
		if s[0] <= c && s[1] >= c {
			if best == nil || s[1]-s[0] < best[1]-best[0] {
				best = s
			}
		}
	}
	if best == nil {
		return nil
	}
	// convert byte indexes back to rune count
	r1 := utf8.RuneCountInString(ln[:best[0]])
	r2 := utf8.RuneCountInString(ln[:best[1]])
	return core.NewSelection(line, r1, line, r2-1)
}

// Try to select the longest "word" from current position.
func (v *View) ExpandSelectionWord(line, col int) *core.Selection {
	l := v.Line(v.slice, line)
	c := v.LineRunesTo(v.slice, line, col)
	if c < 0 || c >= len(l) {
		return nil
	}
	c1, c2 := c, c
	for ; c1 >= 0 && isWordRune(l[c1]); c1-- {
	}
	c1++
	for ; c2 < len(l) && isWordRune(l[c2]); c2++ {
	}
	c2--
	if c1 >= c2 {
		return nil
	}
	return core.NewSelection(line, c1, line, c2)
}

func isWordRune(r rune) bool {
	return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_'
}

func (v *View) SelectAll() {
	lastLn := v.LineCount() - 1
	fmt.Printf("lln: %d\n", lastLn)
	s := core.NewSelection(0, 0, lastLn, v.LineLen(v.slice, lastLn)-1)
	pretty.Println(s)
	v.selections = []core.Selection{
		*s,
	}
}

// Select the whole given line
func (v *View) SelectLine(line int) {
	s := core.NewSelection(line, 0, line, v.LineLen(v.slice, line))
	v.selections = []core.Selection{
		*s,
	}
}

// Select a word at the given location (if any)
func (v *View) SelectWord(line, col int) {
	s := v.ExpandSelectionWord(line, col)
	if s != nil {
		v.selections = []core.Selection{
			*s,
		}
	}
}

// Parses a selection into a location (file, line, col)
func (v *View) SelectionToLoc(sel *core.Selection) (loc string, line, col int) {
	sub := locationRegexp.FindAllStringSubmatch(core.RunesToString(v.SelectionText(sel)), 1)
	if len(sub) == 0 {
		return
	}
	s := sub[0]
	if len(s) >= 1 {
		loc = s[1]
	}
	if len(s[2]) > 0 {
		line, _ = strconv.Atoi(s[2][1:])
	}
	if len(s[3]) > 0 {
		col, _ = strconv.Atoi(s[3][1:])
	}
	return loc, line, col
}

// Stretch a selection toward a new position
func (v *View) StretchSelection(prevl, prevc, l, c int) {
	s := &core.Selection{}
	if len(v.selections) != 0 {
		s = &v.selections[0]
	}
	s.LineFrom, s.LineTo, s.ColFrom, s.ColTo = prevl, l, prevc, c
	s.Normalize()
}

// Open what's selected or under the cursor
// if newView is true then open in a new view, otherwise
// replace content of v
func (v *View) OpenSelection(newView bool) {
	ed := core.Ed.(*Editor)
	newView = newView || v.Dirty()
	if len(v.selections) == 0 {
		ln, col := v.CurTextPos()
		selection := v.ExpandSelectionPath(ln, col)
		if selection == nil {
			ed.SetStatusErr("Could not expand location from cursor location.")
			return
		}
		v.selections = []core.Selection{*selection}
	}
	loc, line, col := v.SelectionToLoc(&v.selections[0])
	line-- // we use 0 indexes in views
	col--
	isDir := false
	loc, isDir = core.LookupLocation(v.WorkDir(), strings.TrimSpace(loc))
	vid := ed.ViewByLoc(loc)
	if vid >= 0 {
		vv := ed.ViewById(vid)
		if vv != nil {
			// Already open
			vv.MoveCursor(line-vv.CurLine(), col-vv.CurCol())
			ed.ViewActivate(vv.Id())
			return
		}
	}
	v2 := ed.NewView(loc)
	if _, err := ed.Open(loc, v2.Id(), v.WorkDir(), false); err != nil {
		ed.SetStatusErr(err.Error())
		return
	}
	if newView {
		if isDir {
			ed.AddDirViewSmart(v2)
		} else {
			ed.InsertViewSmart(v2)
		}
	} else {
		ed.ReplaceView(v, v2)
	}
	v2.MoveCursor(line-v2.CurLine(), col-v2.CurCol())
	ed.ViewActivate(v2.Id())
}
<commit_msg>Fix select all<commit_after>package ui

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/atotto/clipboard"
	"github.com/tcolar/goed/core"
)

func (v *View) ClearSelections() {
	v.selections = []core.Selection{}
}

// Text returns the text contained in the selection of the given view
// Note: **NOT** a rectangle but from pt1 to pt2
func (v *View) SelectionText(s *core.Selection) [][]rune {
	cf := s.ColFrom
	ct := s.ColTo
	lt := s.LineTo
	lf := s.LineFrom
	if lf == lt {
		return *v.backend.Slice(lf, cf, lt, ct).Text()
	}
	// first line
	text := *v.backend.Slice(lf, cf, lf, -1).Text()
	for l := lf + 1; l < lt; l++ {
		// middle
		text = append(text, *v.backend.Slice(l, 0, l, -1).Text()...)
	}
	// last line
	text = append(text, *v.backend.Slice(lt, 0, lt, ct).Text()...)
	return text
}

// Selected returns whether the text at line, col is current selected
// also returns the matching selection, if any.
func (v *View) Selected(col, line int) (bool, *core.Selection) {
	for _, s := range v.selections {
		if line < s.LineFrom || line > s.LineTo {
			continue
		} else if line > s.LineFrom && line < s.LineTo {
			return true, &s
		} else if s.LineFrom == s.LineTo {
			return col >= s.ColFrom && col <= s.ColTo, &s
		} else if line == s.LineFrom && col >= s.ColFrom {
			return true, &s
		} else if line == s.LineTo && col <= s.ColTo {
			return true, &s
		}
	}
	return false, nil
}

func (v *View) Cut() {
	if len(v.selections) == 0 {
		v.SelectLine(v.CurLine())
	}
	v.SelectionCopy(&v.selections[0])
	v.SelectionDelete(&v.selections[0])
}

func (v *View) Copy() {
	if len(v.selections) == 0 {
		v.SelectLine(v.CurLine())
	}
	v.SelectionCopy(&v.selections[0])
}

func (v *View) SelectionCopy(s *core.Selection) {
	t := v.SelectionText(s)
	text := core.RunesToString(t)
	if len(text) == 0 {
		return
	}
	core.Ed.SetStatus(fmt.Sprintf("Copied %d lines to clipboard.", len(t)))
	clipboard.WriteAll(text)
}

func (v *View) SelectionDelete(s *core.Selection) {
	v.Delete(s.LineFrom, s.ColFrom, s.LineTo, s.ColTo, true)
}

func (v *View) Paste() {
	text, err := clipboard.ReadAll()
	if err != nil {
		core.Ed.SetStatusErr(err.Error())
		return
	}
	if len(v.selections) > 0 {
		v.DeleteCur()
	}
	ln, col := v.CurTextPos()
	v.Insert(ln, col, text, true)
}

var locationRegexp = regexp.MustCompile(`([^"\s(){}[\]<>,?|+=&^%#@!;':\x1B]+)(:\d+)?(:\d+)?`)

// Try to select a "location" from the given position
// a location is a path with possibly a line number and maybe a column number as well
func (v *View) ExpandSelectionPath(line, col int) *core.Selection {
	l := v.Line(v.slice, line)
	ln := string(l)
	// Note: Indexes taken and returned by FindAllStringIndex are in BYTES, not runes
	// not very inutitive to say the least
	c := core.RunesLen(l[:col])
	matches := locationRegexp.FindAllStringIndex(ln, -1)
	var best []int
	// Find the "narrowest" match around the cursor
	for _, s := range matches {
		if s[0] <= c && s[1] >= c {
			if best == nil || s[1]-s[0] < best[1]-best[0] {
				best = s
			}
		}
	}
	if best == nil {
		return nil
	}
	// convert byte indexes back to rune count
	r1 := utf8.RuneCountInString(ln[:best[0]])
	r2 := utf8.RuneCountInString(ln[:best[1]])
	return core.NewSelection(line, r1, line, r2-1)
}

// Try to select the longest "word" from current position.
func (v *View) ExpandSelectionWord(line, col int) *core.Selection {
	l := v.Line(v.slice, line)
	c := v.LineRunesTo(v.slice, line, col)
	if c < 0 || c >= len(l) {
		return nil
	}
	c1, c2 := c, c
	for ; c1 >= 0 && isWordRune(l[c1]); c1-- {
	}
	c1++
	for ; c2 < len(l) && isWordRune(l[c2]); c2++ {
	}
	c2--
	if c1 >= c2 {
		return nil
	}
	return core.NewSelection(line, c1, line, c2)
}

func isWordRune(r rune) bool {
	return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_'
}

func (v *View) SelectAll() {
	lastLn := v.LineCount() - 1
	slice := v.Backend().Slice(lastLn, 0, lastLn, -1)
	s := core.NewSelection(0, 0, lastLn, v.LineLen(slice, lastLn)-1)
	v.selections = []core.Selection{
		*s,
	}
}

// Select the whole given line
func (v *View) SelectLine(line int) {
	s := core.NewSelection(line, 0, line, v.LineLen(v.slice, line))
	v.selections = []core.Selection{
		*s,
	}
}

// Select a word at the given location (if any)
func (v *View) SelectWord(line, col int) {
	s := v.ExpandSelectionWord(line, col)
	if s != nil {
		v.selections = []core.Selection{
			*s,
		}
	}
}

// Parses a selection into a location (file, line, col)
func (v *View) SelectionToLoc(sel *core.Selection) (loc string, line, col int) {
	sub := locationRegexp.FindAllStringSubmatch(core.RunesToString(v.SelectionText(sel)), 1)
	if len(sub) == 0 {
		return
	}
	s := sub[0]
	if len(s) >= 1 {
		loc = s[1]
	}
	if len(s[2]) > 0 {
		line, _ = strconv.Atoi(s[2][1:])
	}
	if len(s[3]) > 0 {
		col, _ = strconv.Atoi(s[3][1:])
	}
	return loc, line, col
}

// Stretch a selection toward a new position
func (v *View) StretchSelection(prevl, prevc, l, c int) {
	s := &core.Selection{}
	if len(v.selections) != 0 {
		s = &v.selections[0]
	}
	s.LineFrom, s.LineTo, s.ColFrom, s.ColTo = prevl, l, prevc, c
	s.Normalize()
}

// Open what's selected or under the cursor
// if newView is true then open in a new view, otherwise
// replace content of v
func (v *View) OpenSelection(newView bool) {
	ed := core.Ed.(*Editor)
	newView = newView || v.Dirty()
	if len(v.selections) == 0 {
		ln, col := v.CurTextPos()
		selection := v.ExpandSelectionPath(ln, col)
		if selection == nil {
			ed.SetStatusErr("Could not expand location from cursor location.")
			return
		}
		v.selections = []core.Selection{*selection}
	}
	loc, line, col := v.SelectionToLoc(&v.selections[0])
	line-- // we use 0 indexes in views
	col--
	isDir := false
	loc, isDir = core.LookupLocation(v.WorkDir(), strings.TrimSpace(loc))
	vid := ed.ViewByLoc(loc)
	if vid >= 0 {
		vv := ed.ViewById(vid)
		if vv != nil {
			// Already open
			vv.MoveCursor(line-vv.CurLine(), col-vv.CurCol())
			ed.ViewActivate(vv.Id())
			return
		}
	}
	v2 := ed.NewView(loc)
	if _, err := ed.Open(loc, v2.Id(), v.WorkDir(), false); err != nil {
		ed.SetStatusErr(err.Error())
		return
	}
	if newView {
		if isDir {
			ed.AddDirViewSmart(v2)
		} else {
			ed.InsertViewSmart(v2)
		}
	} else {
		ed.ReplaceView(v, v2)
	}
	v2.MoveCursor(line-v2.CurLine(), col-v2.CurCol())
	ed.ViewActivate(v2.Id())
}
<|endoftext|>


<commit_before>// Copyright 2019 Istio 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 mtls

import (
	"sort"

	"istio.io/istio/galley/pkg/config/resource"

	"istio.io/api/networking/v1alpha3"
	"istio.io/istio/galley/pkg/config/analysis/analyzers/util"
	"istio.io/istio/pkg/config/host"
)

// DestinationRuleChecker computes whether or not MTLS is used according to
// DestinationRules added to the instance. It handles the complicated logic of
// looking up which DestinationRule takes effect for a given source namespace,
// destination namespace and host name.
type DestinationRuleChecker struct {
	namespaceToDestinations map[string]destinations
	rootNamespace           string
}

// destination represents a destination specified in a DestinationRule.
type destination struct {
	targetService TargetService
	usesMTLS      bool
	isPrivate     bool
	resource      *resource.Entry
}

// destinations is a list of destinations that supports being sorted by
// hostname. This means that, once sorted, you can iterate over the list going
// from most-specific rules to least-specific. This matches how the API behaves.
type destinations []destination

// destinations implements sort.Interface
var _ sort.Interface = destinations{}

func (d destinations) Len() int {
	return len(d)
}

func (d destinations) Less(i, j int) bool {
	// First, check to see if we have a tie on FQDN. If we do, we want to break
	// ties so that target services with a port specified come before those that
	// don't.
	ts1 := d[i].targetService
	ts2 := d[j].targetService
	if ts1.FQDN() == ts2.FQDN() {
		return ts1.PortNumber() != 0
	}

	// Defer to the sort order for target service hostname
	hosts := []string{d[i].targetService.FQDN(), d[j].targetService.FQDN()}
	return host.NewNames(hosts).Less(0, 1)
}

func (d destinations) Swap(i, j int) {
	d[i], d[j] = d[j], d[i]
}

// NewDestinationRuleChecker creates a new instance with the given config root
// namespace.
func NewDestinationRuleChecker(rootNamespace string) *DestinationRuleChecker {
	return &DestinationRuleChecker{
		namespaceToDestinations: make(map[string]destinations),
		rootNamespace:           rootNamespace,
	}
}

// TargetServices returns the list of TargetServices known to the checker. These
// services are generated from DestinationRules previously added to the checker.
func (dc *DestinationRuleChecker) TargetServices() []TargetService {
	var targetServices []TargetService
	for _, destinations := range dc.namespaceToDestinations {
		for _, destination := range destinations {
			targetServices = append(targetServices, destination.targetService)
		}
	}

	return targetServices
}

// AddDestinationRule adds a DestinationRule to the checker.
func (dc *DestinationRuleChecker) AddDestinationRule(resource *resource.Entry, rule *v1alpha3.DestinationRule) {
	// By default Destination rules are exported publicly.
	isPrivate := false
	for _, export := range rule.ExportTo {
		if export == "." {
			isPrivate = true
		}
	}

	namespace, _ := resource.Metadata.Name.InterpretAsNamespaceAndName()
	fqdn := util.ConvertHostToFQDN(namespace, rule.GetHost())
	// By default, we are not using MTLS
	usesMTLS := false
	if rule.TrafficPolicy != nil && rule.TrafficPolicy.Tls != nil && rule.TrafficPolicy.Tls.Mode == v1alpha3.TLSSettings_ISTIO_MUTUAL {
		usesMTLS = true
	}

	dc.namespaceToDestinations[namespace] = append(dc.namespaceToDestinations[namespace], destination{
		targetService: NewTargetService(fqdn),
		usesMTLS:      usesMTLS,
		isPrivate:     isPrivate,
		resource:      resource,
	})

	if rule.TrafficPolicy == nil {
		// No overrides to check
		return
	}
	// TODO Support checking subsets.
	// Now check if we have any overrides
	for _, pls := range rule.TrafficPolicy.PortLevelSettings {
		portUsesMTLS := false
		if pls.Tls != nil && pls.Tls.Mode == v1alpha3.TLSSettings_ISTIO_MUTUAL {
			portUsesMTLS = true
		}

		dc.namespaceToDestinations[namespace] = append(dc.namespaceToDestinations[namespace], destination{
			targetService: NewTargetServiceWithPortNumber(fqdn, pls.Port.GetNumber()),
			usesMTLS:      portUsesMTLS,
			isPrivate:     isPrivate,
		})
	}
}

// DoesNamespaceUseMTLSToService returns true if, according to DestinationRules
// added to the checker, mTLS will be used when communicating to the specified
// TargetService from the source namespace to the destination namespace.
//
// If the TargetService's FQDN has a wildcard, then the set of DestinationRules
// considered for routing are only rules that match a superset of the hosts
// specified by the TargetService FQDN. This means you can check, for example,
// the hostname '*.svc.cluster.local' to see if strict MTLS is enforced globally.
func (dc *DestinationRuleChecker) DoesNamespaceUseMTLSToService(srcNamespace, dstNamespace string, ts TargetService) (bool, *resource.Entry) {
	var matchingDestination *destination
	// First, check for a destination rule for src namespace only if the
	// namespace isn't the root namespace. Pilot has this behavior to ensure that the
	// rules in the root namespace don't override other rules.
	if srcNamespace != dc.rootNamespace {
		matchingDestination = dc.findMatchingRuleInNamespace(srcNamespace, ts, true)
		if matchingDestination != nil {
			return matchingDestination.usesMTLS, matchingDestination.resource
		}
	}

	// Now check destination namespace
	matchingDestination = dc.findMatchingRuleInNamespace(dstNamespace, ts, false)
	if matchingDestination != nil {
		return matchingDestination.usesMTLS, matchingDestination.resource
	}

	// Finally, try the root namespace
	matchingDestination = dc.findMatchingRuleInNamespace(dc.rootNamespace, ts, false)
	if matchingDestination != nil {
		return matchingDestination.usesMTLS, matchingDestination.resource
	}

	// no matches found - just return false
	return false, nil
}

// findMatchingRuleInNamespace looks up a DestinationRule in a namespace for a
// specified target service, optionally including privately-exported rules. Note
// that if target service has a wildcard in it, then matched rules must be a
// strict superset of the target service hostnames.
func (dc *DestinationRuleChecker) findMatchingRuleInNamespace(namespace string, ts TargetService, includePrivate bool) *destination {
	// TODO Port name should be handled at some point.
	// TODO We should really presort these ahead of time.
	var ds destinations
	for _, d := range dc.namespaceToDestinations[namespace] {
		if !includePrivate && d.isPrivate {
			continue
		}
		ds = append(ds, d)
	}
	// Sort our destinations, which allows us to find the first match by iterating.
	sort.Sort(ds)

	for _, d := range ds {
		if !host.Name(ts.FQDN()).SubsetOf(host.Name(d.targetService.FQDN())) {
			continue
		}

		// If a port is specified for ts, then skip if destination also has a
		// port number and it doesn't match.
		if ts.PortNumber() != 0 && d.targetService.PortNumber() != 0 && ts.PortNumber() != d.targetService.PortNumber() {
			continue
		}
		// If target service doesn't specify a port, then skip if destination
		// does specify a port.
		if ts.PortNumber() == 0 && d.targetService.PortNumber() != 0 {
			continue
		}
		return &d
	}

	// No match
	return nil
}
<commit_msg>Document where DestinationRule routing logic is defined (#19247)<commit_after>// Copyright 2019 Istio 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 mtls

import (
	"sort"

	"istio.io/istio/galley/pkg/config/resource"

	"istio.io/api/networking/v1alpha3"
	"istio.io/istio/galley/pkg/config/analysis/analyzers/util"
	"istio.io/istio/pkg/config/host"
)

// DestinationRuleChecker computes whether or not MTLS is used according to
// DestinationRules added to the instance. It handles the complicated logic of
// looking up which DestinationRule takes effect for a given source namespace,
// destination namespace and host name.
//
// This logic matches the logic Pilot uses:
// https://github.com/istio/istio/blob/4a442e9f4cbdedb0accdb33bd8b96a3e59691b0b/pilot/pkg/model/push_context.go#L605
// and what's documented on the istio.io site:
// https://istio.io/docs/ops/traffic-management/deploy-guidelines/#cross-namespace-configuration-sharing
type DestinationRuleChecker struct {
	namespaceToDestinations map[string]destinations
	rootNamespace           string
}

// destination represents a destination specified in a DestinationRule.
type destination struct {
	targetService TargetService
	usesMTLS      bool
	isPrivate     bool
	resource      *resource.Entry
}

// destinations is a list of destinations that supports being sorted by
// hostname. This means that, once sorted, you can iterate over the list going
// from most-specific rules to least-specific. This matches how the API behaves.
type destinations []destination

// destinations implements sort.Interface
var _ sort.Interface = destinations{}

func (d destinations) Len() int {
	return len(d)
}

func (d destinations) Less(i, j int) bool {
	// First, check to see if we have a tie on FQDN. If we do, we want to break
	// ties so that target services with a port specified come before those that
	// don't.
	ts1 := d[i].targetService
	ts2 := d[j].targetService
	if ts1.FQDN() == ts2.FQDN() {
		return ts1.PortNumber() != 0
	}

	// Defer to the sort order for target service hostname
	hosts := []string{d[i].targetService.FQDN(), d[j].targetService.FQDN()}
	return host.NewNames(hosts).Less(0, 1)
}

func (d destinations) Swap(i, j int) {
	d[i], d[j] = d[j], d[i]
}

// NewDestinationRuleChecker creates a new instance with the given config root
// namespace.
func NewDestinationRuleChecker(rootNamespace string) *DestinationRuleChecker {
	return &DestinationRuleChecker{
		namespaceToDestinations: make(map[string]destinations),
		rootNamespace:           rootNamespace,
	}
}

// TargetServices returns the list of TargetServices known to the checker. These
// services are generated from DestinationRules previously added to the checker.
func (dc *DestinationRuleChecker) TargetServices() []TargetService {
	var targetServices []TargetService
	for _, destinations := range dc.namespaceToDestinations {
		for _, destination := range destinations {
			targetServices = append(targetServices, destination.targetService)
		}
	}

	return targetServices
}

// AddDestinationRule adds a DestinationRule to the checker.
func (dc *DestinationRuleChecker) AddDestinationRule(resource *resource.Entry, rule *v1alpha3.DestinationRule) {
	// By default Destination rules are exported publicly.
	isPrivate := false
	for _, export := range rule.ExportTo {
		if export == "." {
			isPrivate = true
		}
	}

	namespace, _ := resource.Metadata.Name.InterpretAsNamespaceAndName()
	fqdn := util.ConvertHostToFQDN(namespace, rule.GetHost())
	// By default, we are not using MTLS
	usesMTLS := false
	if rule.TrafficPolicy != nil && rule.TrafficPolicy.Tls != nil && rule.TrafficPolicy.Tls.Mode == v1alpha3.TLSSettings_ISTIO_MUTUAL {
		usesMTLS = true
	}

	dc.namespaceToDestinations[namespace] = append(dc.namespaceToDestinations[namespace], destination{
		targetService: NewTargetService(fqdn),
		usesMTLS:      usesMTLS,
		isPrivate:     isPrivate,
		resource:      resource,
	})

	if rule.TrafficPolicy == nil {
		// No overrides to check
		return
	}
	// TODO Support checking subsets.
	// Now check if we have any overrides
	for _, pls := range rule.TrafficPolicy.PortLevelSettings {
		portUsesMTLS := false
		if pls.Tls != nil && pls.Tls.Mode == v1alpha3.TLSSettings_ISTIO_MUTUAL {
			portUsesMTLS = true
		}

		dc.namespaceToDestinations[namespace] = append(dc.namespaceToDestinations[namespace], destination{
			targetService: NewTargetServiceWithPortNumber(fqdn, pls.Port.GetNumber()),
			usesMTLS:      portUsesMTLS,
			isPrivate:     isPrivate,
		})
	}
}

// DoesNamespaceUseMTLSToService returns true if, according to DestinationRules
// added to the checker, mTLS will be used when communicating to the specified
// TargetService from the source namespace to the destination namespace.
//
// If the TargetService's FQDN has a wildcard, then the set of DestinationRules
// considered for routing are only rules that match a superset of the hosts
// specified by the TargetService FQDN. This means you can check, for example,
// the hostname '*.svc.cluster.local' to see if strict MTLS is enforced globally.
func (dc *DestinationRuleChecker) DoesNamespaceUseMTLSToService(srcNamespace, dstNamespace string, ts TargetService) (bool, *resource.Entry) {
	var matchingDestination *destination
	// First, check for a destination rule for src namespace only if the
	// namespace isn't the root namespace. Pilot has this behavior to ensure that the
	// rules in the root namespace don't override other rules.
	if srcNamespace != dc.rootNamespace {
		matchingDestination = dc.findMatchingRuleInNamespace(srcNamespace, ts, true)
		if matchingDestination != nil {
			return matchingDestination.usesMTLS, matchingDestination.resource
		}
	}

	// Now check destination namespace
	matchingDestination = dc.findMatchingRuleInNamespace(dstNamespace, ts, false)
	if matchingDestination != nil {
		return matchingDestination.usesMTLS, matchingDestination.resource
	}

	// Finally, try the root namespace
	matchingDestination = dc.findMatchingRuleInNamespace(dc.rootNamespace, ts, false)
	if matchingDestination != nil {
		return matchingDestination.usesMTLS, matchingDestination.resource
	}

	// no matches found - just return false
	return false, nil
}

// findMatchingRuleInNamespace looks up a DestinationRule in a namespace for a
// specified target service, optionally including privately-exported rules. Note
// that if target service has a wildcard in it, then matched rules must be a
// strict superset of the target service hostnames.
func (dc *DestinationRuleChecker) findMatchingRuleInNamespace(namespace string, ts TargetService, includePrivate bool) *destination {
	// TODO Port name should be handled at some point.
	// TODO We should really presort these ahead of time.
	var ds destinations
	for _, d := range dc.namespaceToDestinations[namespace] {
		if !includePrivate && d.isPrivate {
			continue
		}
		ds = append(ds, d)
	}
	// Sort our destinations, which allows us to find the first match by iterating.
	sort.Sort(ds)

	for _, d := range ds {
		if !host.Name(ts.FQDN()).SubsetOf(host.Name(d.targetService.FQDN())) {
			continue
		}

		// If a port is specified for ts, then skip if destination also has a
		// port number and it doesn't match.
		if ts.PortNumber() != 0 && d.targetService.PortNumber() != 0 && ts.PortNumber() != d.targetService.PortNumber() {
			continue
		}
		// If target service doesn't specify a port, then skip if destination
		// does specify a port.
		if ts.PortNumber() == 0 && d.targetService.PortNumber() != 0 {
			continue
		}
		return &d
	}

	// No match
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 pluginwatcher

import (
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/fsnotify/fsnotify"
	"k8s.io/klog/v2"

	"k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache"
	"k8s.io/kubernetes/pkg/kubelet/util"
	utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)

// Watcher is the plugin watcher
type Watcher struct {
	path                string
	fs                  utilfs.Filesystem
	fsWatcher           *fsnotify.Watcher
	desiredStateOfWorld cache.DesiredStateOfWorld
}

// NewWatcher provides a new watcher for socket registration
func NewWatcher(sockDir string, desiredStateOfWorld cache.DesiredStateOfWorld) *Watcher {
	return &Watcher{
		path:                sockDir,
		fs:                  &utilfs.DefaultFs{},
		desiredStateOfWorld: desiredStateOfWorld,
	}
}

// Start watches for the creation and deletion of plugin sockets at the path
func (w *Watcher) Start(stopCh <-chan struct{}) error {
	klog.V(2).Infof("Plugin Watcher Start at %s", w.path)

	// Creating the directory to be watched if it doesn't exist yet,
	// and walks through the directory to discover the existing plugins.
	if err := w.init(); err != nil {
		return err
	}

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("failed to start plugin fsWatcher, err: %v", err)
	}
	w.fsWatcher = fsWatcher

	// Traverse plugin dir and add filesystem watchers before starting the plugin processing goroutine.
	if err := w.traversePluginDir(w.path); err != nil {
		klog.Errorf("failed to traverse plugin socket path %q, err: %v", w.path, err)
	}

	go func(fsWatcher *fsnotify.Watcher) {
		for {
			select {
			case event := <-fsWatcher.Events:
				//TODO: Handle errors by taking corrective measures
				if event.Op&fsnotify.Create == fsnotify.Create {
					err := w.handleCreateEvent(event)
					if err != nil {
						klog.Errorf("error %v when handling create event: %s", err, event)
					}
				} else if event.Op&fsnotify.Remove == fsnotify.Remove {
					w.handleDeleteEvent(event)
				}
				continue
			case err := <-fsWatcher.Errors:
				if err != nil {
					klog.Errorf("fsWatcher received error: %v", err)
				}
				continue
			case <-stopCh:
				w.fsWatcher.Close()
				return
			}
		}
	}(fsWatcher)

	return nil
}

func (w *Watcher) init() error {
	klog.V(4).Infof("Ensuring Plugin directory at %s ", w.path)

	if err := w.fs.MkdirAll(w.path, 0755); err != nil {
		return fmt.Errorf("error (re-)creating root %s: %v", w.path, err)
	}

	return nil
}

// Walks through the plugin directory discover any existing plugin sockets.
// Ignore all errors except root dir not being walkable
func (w *Watcher) traversePluginDir(dir string) error {
	// watch the new dir
	err := w.fsWatcher.Add(dir)
	if err != nil {
		return fmt.Errorf("failed to watch %s, err: %v", w.path, err)
	}
	// traverse existing children in the dir
	return w.fs.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			if path == dir {
				return fmt.Errorf("error accessing path: %s error: %v", path, err)
			}

			klog.Errorf("error accessing path: %s error: %v", path, err)
			return nil
		}

		// do not call fsWatcher.Add twice on the root dir to avoid potential problems.
		if path == dir {
			return nil
		}

		switch mode := info.Mode(); {
		case mode.IsDir():
			if err := w.fsWatcher.Add(path); err != nil {
				return fmt.Errorf("failed to watch %s, err: %v", path, err)
			}
		case mode&os.ModeSocket != 0:
			event := fsnotify.Event{
				Name: path,
				Op:   fsnotify.Create,
			}
			//TODO: Handle errors by taking corrective measures
			if err := w.handleCreateEvent(event); err != nil {
				klog.Errorf("error %v when handling create event: %s", err, event)
			}
		default:
			klog.V(5).Infof("Ignoring file %s with mode %v", path, mode)
		}

		return nil
	})
}

// Handle filesystem notify event.
// Files names:
// - MUST NOT start with a '.'
func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
	klog.V(6).Infof("Handling create event: %v", event)

	fi, err := os.Stat(event.Name)
	if err != nil {
		return fmt.Errorf("stat file %s failed: %v", event.Name, err)
	}

	if strings.HasPrefix(fi.Name(), ".") {
		klog.V(5).Infof("Ignoring file (starts with '.'): %s", fi.Name())
		return nil
	}

	if !fi.IsDir() {
		isSocket, err := util.IsUnixDomainSocket(util.NormalizePath(event.Name))
		if err != nil {
			return fmt.Errorf("failed to determine if file: %s is a unix domain socket: %v", event.Name, err)
		}
		if !isSocket {
			klog.V(5).Infof("Ignoring non socket file %s", fi.Name())
			return nil
		}

		return w.handlePluginRegistration(event.Name)
	}

	return w.traversePluginDir(event.Name)
}

func (w *Watcher) handlePluginRegistration(socketPath string) error {
	if runtime.GOOS == "windows" {
		socketPath = util.NormalizePath(socketPath)
	}
	// Update desired state of world list of plugins
	// If the socket path does exist in the desired world cache, there's still
	// a possibility that it has been deleted and recreated again before it is
	// removed from the desired world cache, so we still need to call AddOrUpdatePlugin
	// in this case to update the timestamp
	klog.V(2).Infof("Adding socket path or updating timestamp %s to desired state cache", socketPath)
	err := w.desiredStateOfWorld.AddOrUpdatePlugin(socketPath)
	if err != nil {
		return fmt.Errorf("error adding socket path %s or updating timestamp to desired state cache: %v", socketPath, err)
	}
	return nil
}

func (w *Watcher) handleDeleteEvent(event fsnotify.Event) {
	klog.V(6).Infof("Handling delete event: %v", event)

	socketPath := event.Name
	klog.V(2).Infof("Removing socket path %s from desired state cache", socketPath)
	w.desiredStateOfWorld.RemovePlugin(socketPath)
}
<commit_msg>Use Lstat in plugin watcher to avoid Windows problem<commit_after>/*
Copyright 2018 The Kubernetes 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 pluginwatcher

import (
	"fmt"
	"os"
	"runtime"
	"strings"

	"github.com/fsnotify/fsnotify"
	"k8s.io/klog/v2"

	"k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache"
	"k8s.io/kubernetes/pkg/kubelet/util"
	utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)

// Watcher is the plugin watcher
type Watcher struct {
	path                string
	fs                  utilfs.Filesystem
	fsWatcher           *fsnotify.Watcher
	desiredStateOfWorld cache.DesiredStateOfWorld
}

// NewWatcher provides a new watcher for socket registration
func NewWatcher(sockDir string, desiredStateOfWorld cache.DesiredStateOfWorld) *Watcher {
	return &Watcher{
		path:                sockDir,
		fs:                  &utilfs.DefaultFs{},
		desiredStateOfWorld: desiredStateOfWorld,
	}
}

// Start watches for the creation and deletion of plugin sockets at the path
func (w *Watcher) Start(stopCh <-chan struct{}) error {
	klog.V(2).Infof("Plugin Watcher Start at %s", w.path)

	// Creating the directory to be watched if it doesn't exist yet,
	// and walks through the directory to discover the existing plugins.
	if err := w.init(); err != nil {
		return err
	}

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("failed to start plugin fsWatcher, err: %v", err)
	}
	w.fsWatcher = fsWatcher

	// Traverse plugin dir and add filesystem watchers before starting the plugin processing goroutine.
	if err := w.traversePluginDir(w.path); err != nil {
		klog.Errorf("failed to traverse plugin socket path %q, err: %v", w.path, err)
	}

	go func(fsWatcher *fsnotify.Watcher) {
		for {
			select {
			case event := <-fsWatcher.Events:
				//TODO: Handle errors by taking corrective measures
				if event.Op&fsnotify.Create == fsnotify.Create {
					err := w.handleCreateEvent(event)
					if err != nil {
						klog.Errorf("error %v when handling create event: %s", err, event)
					}
				} else if event.Op&fsnotify.Remove == fsnotify.Remove {
					w.handleDeleteEvent(event)
				}
				continue
			case err := <-fsWatcher.Errors:
				if err != nil {
					klog.Errorf("fsWatcher received error: %v", err)
				}
				continue
			case <-stopCh:
				w.fsWatcher.Close()
				return
			}
		}
	}(fsWatcher)

	return nil
}

func (w *Watcher) init() error {
	klog.V(4).Infof("Ensuring Plugin directory at %s ", w.path)

	if err := w.fs.MkdirAll(w.path, 0755); err != nil {
		return fmt.Errorf("error (re-)creating root %s: %v", w.path, err)
	}

	return nil
}

// Walks through the plugin directory discover any existing plugin sockets.
// Ignore all errors except root dir not being walkable
func (w *Watcher) traversePluginDir(dir string) error {
	// watch the new dir
	err := w.fsWatcher.Add(dir)
	if err != nil {
		return fmt.Errorf("failed to watch %s, err: %v", w.path, err)
	}
	// traverse existing children in the dir
	return w.fs.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			if path == dir {
				return fmt.Errorf("error accessing path: %s error: %v", path, err)
			}

			klog.Errorf("error accessing path: %s error: %v", path, err)
			return nil
		}

		// do not call fsWatcher.Add twice on the root dir to avoid potential problems.
		if path == dir {
			return nil
		}

		switch mode := info.Mode(); {
		case mode.IsDir():
			if err := w.fsWatcher.Add(path); err != nil {
				return fmt.Errorf("failed to watch %s, err: %v", path, err)
			}
		case mode&os.ModeSocket != 0:
			event := fsnotify.Event{
				Name: path,
				Op:   fsnotify.Create,
			}
			//TODO: Handle errors by taking corrective measures
			if err := w.handleCreateEvent(event); err != nil {
				klog.Errorf("error %v when handling create event: %s", err, event)
			}
		default:
			klog.V(5).Infof("Ignoring file %s with mode %v", path, mode)
		}

		return nil
	})
}

// Handle filesystem notify event.
// Files names:
// - MUST NOT start with a '.'
func (w *Watcher) handleCreateEvent(event fsnotify.Event) error {
	klog.V(6).Infof("Handling create event: %v", event)

	fi, err := os.Lstat(event.Name)
	if err != nil {
		return fmt.Errorf("stat file %s failed: %v", event.Name, err)
	}

	if strings.HasPrefix(fi.Name(), ".") {
		klog.V(5).Infof("Ignoring file (starts with '.'): %s", fi.Name())
		return nil
	}

	if !fi.IsDir() {
		isSocket, err := util.IsUnixDomainSocket(util.NormalizePath(event.Name))
		if err != nil {
			return fmt.Errorf("failed to determine if file: %s is a unix domain socket: %v", event.Name, err)
		}
		if !isSocket {
			klog.V(5).Infof("Ignoring non socket file %s", fi.Name())
			return nil
		}

		return w.handlePluginRegistration(event.Name)
	}

	return w.traversePluginDir(event.Name)
}

func (w *Watcher) handlePluginRegistration(socketPath string) error {
	if runtime.GOOS == "windows" {
		socketPath = util.NormalizePath(socketPath)
	}
	// Update desired state of world list of plugins
	// If the socket path does exist in the desired world cache, there's still
	// a possibility that it has been deleted and recreated again before it is
	// removed from the desired world cache, so we still need to call AddOrUpdatePlugin
	// in this case to update the timestamp
	klog.V(2).Infof("Adding socket path or updating timestamp %s to desired state cache", socketPath)
	err := w.desiredStateOfWorld.AddOrUpdatePlugin(socketPath)
	if err != nil {
		return fmt.Errorf("error adding socket path %s or updating timestamp to desired state cache: %v", socketPath, err)
	}
	return nil
}

func (w *Watcher) handleDeleteEvent(event fsnotify.Event) {
	klog.V(6).Infof("Handling delete event: %v", event)

	socketPath := event.Name
	klog.V(2).Infof("Removing socket path %s from desired state cache", socketPath)
	w.desiredStateOfWorld.RemovePlugin(socketPath)
}
<|endoftext|>