<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

type commandPostContent struct {
	Command      []string          `json:"command"`
	WaitForWS    bool              `json:"wait-for-websocket"`
	RecordOutput bool              `json:"record-output"`
	Interactive  bool              `json:"interactive"`
	Environment  map[string]string `json:"environment"`
	Width        int               `json:"width"`
	Height       int               `json:"height"`
}

type execWs struct {
	command   []string
	container container
	env       map[string]string

	rootUid          int
	rootGid          int
	conns            map[int]*websocket.Conn
	connsLock        sync.Mutex
	allConnected     chan bool
	controlConnected chan bool
	interactive      bool
	fds              map[int]string
	width            int
	height           int
}

func (s *execWs) Metadata() interface{} {
	fds := shared.Jmap{}
	for fd, secret := range s.fds {
		if fd == -1 {
			fds["control"] = secret
		} else {
			fds[strconv.Itoa(fd)] = secret
		}
	}

	return shared.Jmap{"fds": fds}
}

func (s *execWs) Connect(op *operation, r *http.Request, w http.ResponseWriter) error {
	secret := r.FormValue("secret")
	if secret == "" {
		return fmt.Errorf("missing secret")
	}

	for fd, fdSecret := range s.fds {
		if secret == fdSecret {
			conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
			if err != nil {
				return err
			}

			s.connsLock.Lock()
			s.conns[fd] = conn
			s.connsLock.Unlock()

			if fd == -1 {
				s.controlConnected <- true
				return nil
			}

			for i, c := range s.conns {
				if i != -1 && c == nil {
					return nil
				}
			}
			s.allConnected <- true
			return nil
		}
	}

	/* If we didn't find the right secret, the user provided a bad one,
	 * which 403, not 404, since this operation actually exists */
	return os.ErrPermission
}

func (s *execWs) Do(op *operation) error {
	<-s.allConnected

	var err error
	var ttys []*os.File
	var ptys []*os.File

	var stdin *os.File
	var stdout *os.File
	var stderr *os.File

	if s.interactive {
		ttys = make([]*os.File, 1)
		ptys = make([]*os.File, 1)
		ptys[0], ttys[0], err = shared.OpenPty(s.rootUid, s.rootGid)

		stdin = ttys[0]
		stdout = ttys[0]
		stderr = ttys[0]

		if s.width > 0 && s.height > 0 {
			shared.SetSize(int(ptys[0].Fd()), s.width, s.height)
		}
	} else {
		ttys = make([]*os.File, 3)
		ptys = make([]*os.File, 3)
		for i := 0; i < len(ttys); i++ {
			ptys[i], ttys[i], err = shared.Pipe()
			if err != nil {
				return err
			}
		}

		stdin = ptys[0]
		stdout = ttys[1]
		stderr = ttys[2]
	}

	controlExit := make(chan bool)
	receivePid := make(chan int)
	var wgEOF sync.WaitGroup

	if s.interactive {
		wgEOF.Add(1)
		go func() {
			receivedPid := <-receivePid
			select {
			case <-s.controlConnected:
				break

			case <-controlExit:
				return
			}

			for {
				mt, r, err := s.conns[-1].NextReader()
				if mt == websocket.CloseMessage {
					break
				}

				if err != nil {
					shared.LogDebugf("Got error getting next reader %s", err)
					break
				}

				buf, err := ioutil.ReadAll(r)
				if err != nil {
					shared.LogDebugf("Failed to read message %s", err)
					break
				}

				command := shared.ContainerExecControl{}

				if err := json.Unmarshal(buf, &command); err != nil {
					shared.LogDebugf("Failed to unmarshal control socket command: %s", err)
					continue
				}

				if command.Command == "window-resize" {
					winchWidth, err := strconv.Atoi(command.Args["width"])
					if err != nil {
						shared.LogDebugf("Unable to extract window width: %s", err)
						continue
					}

					winchHeight, err := strconv.Atoi(command.Args["height"])
					if err != nil {
						shared.LogDebugf("Unable to extract window height: %s", err)
						continue
					}

					err = shared.SetSize(int(ptys[0].Fd()), winchWidth, winchHeight)
					if err != nil {
						shared.LogDebugf("Failed to set window size to: %dx%d", winchWidth, winchHeight)
						continue
					}
				} else if command.Command == "signal" {
					if err := syscall.Kill(receivedPid, command.Signal); err != nil {
						shared.LogDebugf("Failed forwarding signal '%s' to PID %d.", command.Signal, receivedPid)
						continue
					}
					shared.LogDebugf("Forwarded signal '%s' to PID %d.", command.Signal, receivedPid)
				}
			}
		}()
		go func() {
			readDone, writeDone := shared.WebsocketMirror(s.conns[0], ptys[0], ptys[0])
			<-readDone
			<-writeDone
			s.conns[0].Close()
			wgEOF.Done()
		}()
	} else {
		wgEOF.Add(len(ttys) - 1)
		for i := 0; i < len(ttys); i++ {
			go func(i int) {
				if i == 0 {
					<-shared.WebsocketRecvStream(ttys[i], s.conns[i])
					ttys[i].Close()
				} else {
					<-shared.WebsocketSendStream(s.conns[i], ptys[i], -1)
					ptys[i].Close()
					wgEOF.Done()
				}
			}(i)
		}
	}

	finisher := func(cmdResult int, cmdErr error) error {
		for _, tty := range ttys {
			tty.Close()
		}

		if s.conns[-1] == nil {
			if s.interactive {
				controlExit <- true
			}
		} else {
			s.conns[-1].Close()
		}

		wgEOF.Wait()

		for _, pty := range ptys {
			pty.Close()
		}

		metadata := shared.Jmap{"return": cmdResult}
		err = op.UpdateMetadata(metadata)
		if err != nil {
			return err
		}

		return cmdErr
	}

	r, w, err := shared.Pipe()
	defer r.Close()
	if err != nil {
		shared.LogErrorf("s", err)
		return err
	}

	cmd, err := s.container.ExecNoWait(s.command, s.env, stdin, stdout, stderr, w)
	if err != nil {
		w.Close()
		return err
	}

	err = cmd.Start()
	if err != nil {
		w.Close()
		return err
	}
	w.Close()

	attachedPid := -1
	if err := json.NewDecoder(r).Decode(&attachedPid); err != nil {
		shared.LogErrorf("Failed to retrieve PID of executing child process: %s", err)
		return finisher(-1, err)
	}

	if s.interactive {
		receivePid <- attachedPid
	}

	err = cmd.Wait()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok {
			status, ok := exitErr.Sys().(syscall.WaitStatus)
			if ok {
				return finisher(status.ExitStatus(), nil)
			}
		}
	}

	return finisher(0, nil)
}

func containerExecPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	c, err := containerLoadByName(d, name)
	if err != nil {
		return SmartError(err)
	}

	if !c.IsRunning() {
		return BadRequest(fmt.Errorf("Container is not running."))
	}

	if c.IsFrozen() {
		return BadRequest(fmt.Errorf("Container is frozen."))
	}

	post := commandPostContent{}
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return BadRequest(err)
	}

	if err := json.Unmarshal(buf, &post); err != nil {
		return BadRequest(err)
	}

	env := map[string]string{}

	for k, v := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "environment.") {
			env[strings.TrimPrefix(k, "environment.")] = v
		}
	}

	if post.Environment != nil {
		for k, v := range post.Environment {
			env[k] = v
		}
	}

	_, ok := env["PATH"]
	if !ok {
		env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
		if shared.PathExists(fmt.Sprintf("%s/snap/bin", c.RootfsPath())) {
			env["PATH"] = fmt.Sprintf("%s:/snap/bin", env["PATH"])
		}
	}

	if post.WaitForWS {
		ws := &execWs{}
		ws.fds = map[int]string{}
		idmapset := c.IdmapSet()
		if idmapset != nil {
			ws.rootUid, ws.rootGid = idmapset.ShiftIntoNs(0, 0)
		}
		ws.conns = map[int]*websocket.Conn{}
		ws.conns[-1] = nil
		ws.conns[0] = nil
		if !post.Interactive {
			ws.conns[1] = nil
			ws.conns[2] = nil
		}
		ws.allConnected = make(chan bool, 1)
		ws.controlConnected = make(chan bool, 1)
		ws.interactive = post.Interactive
		for i := -1; i < len(ws.conns)-1; i++ {
			ws.fds[i], err = shared.RandomCryptoString()
			if err != nil {
				return InternalError(err)
			}
		}

		ws.command = post.Command
		ws.container = c
		ws.env = env

		ws.width = post.Width
		ws.height = post.Height

		resources := map[string][]string{}
		resources["containers"] = []string{ws.container.Name()}

		op, err := operationCreate(operationClassWebsocket, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
		if err != nil {
			return InternalError(err)
		}

		return OperationResponse(op)
	}

	run := func(op *operation) error {
		var cmdErr error
		var cmdResult int
		metadata := shared.Jmap{}

		if post.RecordOutput {
			// Prepare stdout and stderr recording
			stdout, err := os.OpenFile(filepath.Join(c.LogPath(), fmt.Sprintf("exec_%s.stdout", op.id)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			if err != nil {
				return err
			}
			defer stdout.Close()

			stderr, err := os.OpenFile(filepath.Join(c.LogPath(), fmt.Sprintf("exec_%s.stderr", op.id)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			if err != nil {
				return err
			}
			defer stderr.Close()

			// Run the command
			cmdResult, cmdErr = c.Exec(post.Command, env, nil, stdout, stderr)

			// Update metadata with the right URLs
			metadata["return"] = cmdResult
			metadata["output"] = shared.Jmap{
				"1": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stdout.Name())),
				"2": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stderr.Name())),
			}
		} else {
			cmdResult, cmdErr = c.Exec(post.Command, env, nil, nil, nil)
			metadata["return"] = cmdResult
		}

		err = op.UpdateMetadata(metadata)
		if err != nil {
			shared.LogError("error updating metadata for cmd", log.Ctx{"err": err, "cmd": post.Command})
		}

		return cmdErr
	}

	resources := map[string][]string{}
	resources["containers"] = []string{name}

	op, err := operationCreate(operationClassTask, resources, nil, run, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
<commit_msg>Add /snap/bin to PATH even if only /snap exists<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"syscall"

	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

type commandPostContent struct {
	Command      []string          `json:"command"`
	WaitForWS    bool              `json:"wait-for-websocket"`
	RecordOutput bool              `json:"record-output"`
	Interactive  bool              `json:"interactive"`
	Environment  map[string]string `json:"environment"`
	Width        int               `json:"width"`
	Height       int               `json:"height"`
}

type execWs struct {
	command   []string
	container container
	env       map[string]string

	rootUid          int
	rootGid          int
	conns            map[int]*websocket.Conn
	connsLock        sync.Mutex
	allConnected     chan bool
	controlConnected chan bool
	interactive      bool
	fds              map[int]string
	width            int
	height           int
}

func (s *execWs) Metadata() interface{} {
	fds := shared.Jmap{}
	for fd, secret := range s.fds {
		if fd == -1 {
			fds["control"] = secret
		} else {
			fds[strconv.Itoa(fd)] = secret
		}
	}

	return shared.Jmap{"fds": fds}
}

func (s *execWs) Connect(op *operation, r *http.Request, w http.ResponseWriter) error {
	secret := r.FormValue("secret")
	if secret == "" {
		return fmt.Errorf("missing secret")
	}

	for fd, fdSecret := range s.fds {
		if secret == fdSecret {
			conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
			if err != nil {
				return err
			}

			s.connsLock.Lock()
			s.conns[fd] = conn
			s.connsLock.Unlock()

			if fd == -1 {
				s.controlConnected <- true
				return nil
			}

			for i, c := range s.conns {
				if i != -1 && c == nil {
					return nil
				}
			}
			s.allConnected <- true
			return nil
		}
	}

	/* If we didn't find the right secret, the user provided a bad one,
	 * which 403, not 404, since this operation actually exists */
	return os.ErrPermission
}

func (s *execWs) Do(op *operation) error {
	<-s.allConnected

	var err error
	var ttys []*os.File
	var ptys []*os.File

	var stdin *os.File
	var stdout *os.File
	var stderr *os.File

	if s.interactive {
		ttys = make([]*os.File, 1)
		ptys = make([]*os.File, 1)
		ptys[0], ttys[0], err = shared.OpenPty(s.rootUid, s.rootGid)

		stdin = ttys[0]
		stdout = ttys[0]
		stderr = ttys[0]

		if s.width > 0 && s.height > 0 {
			shared.SetSize(int(ptys[0].Fd()), s.width, s.height)
		}
	} else {
		ttys = make([]*os.File, 3)
		ptys = make([]*os.File, 3)
		for i := 0; i < len(ttys); i++ {
			ptys[i], ttys[i], err = shared.Pipe()
			if err != nil {
				return err
			}
		}

		stdin = ptys[0]
		stdout = ttys[1]
		stderr = ttys[2]
	}

	controlExit := make(chan bool)
	receivePid := make(chan int)
	var wgEOF sync.WaitGroup

	if s.interactive {
		wgEOF.Add(1)
		go func() {
			receivedPid := <-receivePid
			select {
			case <-s.controlConnected:
				break

			case <-controlExit:
				return
			}

			for {
				mt, r, err := s.conns[-1].NextReader()
				if mt == websocket.CloseMessage {
					break
				}

				if err != nil {
					shared.LogDebugf("Got error getting next reader %s", err)
					break
				}

				buf, err := ioutil.ReadAll(r)
				if err != nil {
					shared.LogDebugf("Failed to read message %s", err)
					break
				}

				command := shared.ContainerExecControl{}

				if err := json.Unmarshal(buf, &command); err != nil {
					shared.LogDebugf("Failed to unmarshal control socket command: %s", err)
					continue
				}

				if command.Command == "window-resize" {
					winchWidth, err := strconv.Atoi(command.Args["width"])
					if err != nil {
						shared.LogDebugf("Unable to extract window width: %s", err)
						continue
					}

					winchHeight, err := strconv.Atoi(command.Args["height"])
					if err != nil {
						shared.LogDebugf("Unable to extract window height: %s", err)
						continue
					}

					err = shared.SetSize(int(ptys[0].Fd()), winchWidth, winchHeight)
					if err != nil {
						shared.LogDebugf("Failed to set window size to: %dx%d", winchWidth, winchHeight)
						continue
					}
				} else if command.Command == "signal" {
					if err := syscall.Kill(receivedPid, command.Signal); err != nil {
						shared.LogDebugf("Failed forwarding signal '%s' to PID %d.", command.Signal, receivedPid)
						continue
					}
					shared.LogDebugf("Forwarded signal '%s' to PID %d.", command.Signal, receivedPid)
				}
			}
		}()
		go func() {
			readDone, writeDone := shared.WebsocketMirror(s.conns[0], ptys[0], ptys[0])
			<-readDone
			<-writeDone
			s.conns[0].Close()
			wgEOF.Done()
		}()
	} else {
		wgEOF.Add(len(ttys) - 1)
		for i := 0; i < len(ttys); i++ {
			go func(i int) {
				if i == 0 {
					<-shared.WebsocketRecvStream(ttys[i], s.conns[i])
					ttys[i].Close()
				} else {
					<-shared.WebsocketSendStream(s.conns[i], ptys[i], -1)
					ptys[i].Close()
					wgEOF.Done()
				}
			}(i)
		}
	}

	finisher := func(cmdResult int, cmdErr error) error {
		for _, tty := range ttys {
			tty.Close()
		}

		if s.conns[-1] == nil {
			if s.interactive {
				controlExit <- true
			}
		} else {
			s.conns[-1].Close()
		}

		wgEOF.Wait()

		for _, pty := range ptys {
			pty.Close()
		}

		metadata := shared.Jmap{"return": cmdResult}
		err = op.UpdateMetadata(metadata)
		if err != nil {
			return err
		}

		return cmdErr
	}

	r, w, err := shared.Pipe()
	defer r.Close()
	if err != nil {
		shared.LogErrorf("s", err)
		return err
	}

	cmd, err := s.container.ExecNoWait(s.command, s.env, stdin, stdout, stderr, w)
	if err != nil {
		w.Close()
		return err
	}

	err = cmd.Start()
	if err != nil {
		w.Close()
		return err
	}
	w.Close()

	attachedPid := -1
	if err := json.NewDecoder(r).Decode(&attachedPid); err != nil {
		shared.LogErrorf("Failed to retrieve PID of executing child process: %s", err)
		return finisher(-1, err)
	}

	if s.interactive {
		receivePid <- attachedPid
	}

	err = cmd.Wait()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if ok {
			status, ok := exitErr.Sys().(syscall.WaitStatus)
			if ok {
				return finisher(status.ExitStatus(), nil)
			}
		}
	}

	return finisher(0, nil)
}

func containerExecPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]
	c, err := containerLoadByName(d, name)
	if err != nil {
		return SmartError(err)
	}

	if !c.IsRunning() {
		return BadRequest(fmt.Errorf("Container is not running."))
	}

	if c.IsFrozen() {
		return BadRequest(fmt.Errorf("Container is frozen."))
	}

	post := commandPostContent{}
	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return BadRequest(err)
	}

	if err := json.Unmarshal(buf, &post); err != nil {
		return BadRequest(err)
	}

	env := map[string]string{}

	for k, v := range c.ExpandedConfig() {
		if strings.HasPrefix(k, "environment.") {
			env[strings.TrimPrefix(k, "environment.")] = v
		}
	}

	if post.Environment != nil {
		for k, v := range post.Environment {
			env[k] = v
		}
	}

	_, ok := env["PATH"]
	if !ok {
		env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
		if shared.PathExists(fmt.Sprintf("%s/snap", c.RootfsPath())) {
			env["PATH"] = fmt.Sprintf("%s:/snap/bin", env["PATH"])
		}
	}

	if post.WaitForWS {
		ws := &execWs{}
		ws.fds = map[int]string{}
		idmapset := c.IdmapSet()
		if idmapset != nil {
			ws.rootUid, ws.rootGid = idmapset.ShiftIntoNs(0, 0)
		}
		ws.conns = map[int]*websocket.Conn{}
		ws.conns[-1] = nil
		ws.conns[0] = nil
		if !post.Interactive {
			ws.conns[1] = nil
			ws.conns[2] = nil
		}
		ws.allConnected = make(chan bool, 1)
		ws.controlConnected = make(chan bool, 1)
		ws.interactive = post.Interactive
		for i := -1; i < len(ws.conns)-1; i++ {
			ws.fds[i], err = shared.RandomCryptoString()
			if err != nil {
				return InternalError(err)
			}
		}

		ws.command = post.Command
		ws.container = c
		ws.env = env

		ws.width = post.Width
		ws.height = post.Height

		resources := map[string][]string{}
		resources["containers"] = []string{ws.container.Name()}

		op, err := operationCreate(operationClassWebsocket, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
		if err != nil {
			return InternalError(err)
		}

		return OperationResponse(op)
	}

	run := func(op *operation) error {
		var cmdErr error
		var cmdResult int
		metadata := shared.Jmap{}

		if post.RecordOutput {
			// Prepare stdout and stderr recording
			stdout, err := os.OpenFile(filepath.Join(c.LogPath(), fmt.Sprintf("exec_%s.stdout", op.id)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			if err != nil {
				return err
			}
			defer stdout.Close()

			stderr, err := os.OpenFile(filepath.Join(c.LogPath(), fmt.Sprintf("exec_%s.stderr", op.id)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
			if err != nil {
				return err
			}
			defer stderr.Close()

			// Run the command
			cmdResult, cmdErr = c.Exec(post.Command, env, nil, stdout, stderr)

			// Update metadata with the right URLs
			metadata["return"] = cmdResult
			metadata["output"] = shared.Jmap{
				"1": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stdout.Name())),
				"2": fmt.Sprintf("/%s/containers/%s/logs/%s", shared.APIVersion, c.Name(), filepath.Base(stderr.Name())),
			}
		} else {
			cmdResult, cmdErr = c.Exec(post.Command, env, nil, nil, nil)
			metadata["return"] = cmdResult
		}

		err = op.UpdateMetadata(metadata)
		if err != nil {
			shared.LogError("error updating metadata for cmd", log.Ctx{"err": err, "cmd": post.Command})
		}

		return cmdErr
	}

	resources := map[string][]string{}
	resources["containers"] = []string{name}

	op, err := operationCreate(operationClassTask, resources, nil, run, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
<|endoftext|>


<commit_before>/**
 * Created by IntelliJ IDEA.
 * User: DlaminiSi
 * Date: 2011/07/26
 * Time: 5:47 PM
 * To change this template use File | Settings | File Templates.
 */
package gorest

import (
	"testing"
)

type User struct{
    Id string
    FirstName string
    LastName string
    Age int
    Weight float32
}

type Service struct{
    RestService    "root=/serv/; consumes=application/json; produces=application/json"

    usersByNameAndAge EndPoint "method=GET; path=/person/{FName:string}/{Age:int}; output=[]User"
}

func(serv Service)    UsersByNameAndAge(FName string,Age int) []User{
    users:=make([]User,0)
    users=append(users,User{"user1",FName,"Soap",Age,89.7})
    users=append(users,User{"user2",FName,"Soap2",Age,89.7})
    return users
}

func TestServiceMetaData(t *testing.T){
    meta:= prepServiceMetaData("root=/serv/; consumes=application/json; produces=application/xml",new(Service))

    if meta.consumesMime != "application/json" {
       t.Error("Parsed incorrectly: 'consumesMime'")
    }
    if meta.producesMime != "application/xml" {
       t.Error("Parsed incorrectly: 'producesMime' ")
    }
    if meta.root != "/serv/" {
       t.Error("Parsed incorrectly: root ")
    }

}

func TestEndPointStruct(t *testing.T){
    meta:=makeEndPointStruct("method=GET; path=/person/{FName:string}/{Age:int}; output=[]User","/serv/")

    if meta.requestMethod != GET{
       t.Error("Parsed incorrectly: request method ")
    }
    if meta.signiture != "serv/person/"{
       t.Error("Parsed incorrectly: root ")
    }

    if meta.outputType == "User" && meta.outputTypeIsArray{
    }else{
       t.Error("Parsed incorrectly: return parameter ")
    }

    if meta.paramLen != 2{
       t.Error("Parsed incorrectly: parameter length ")
    }else if meta.params[0].parName != "FName" && meta.params[0].pos != 0 && meta.params[0].typeName != "User"{
       t.Error("Parsed incorrectly: path parameter names and types")
    }

}
<commit_msg>Update test cases<commit_after>/**
 * Created by IntelliJ IDEA.
 * User: DlaminiSi
 * Date: 2011/07/26
 * Time: 5:47 PM
 * To change this template use File | Settings | File Templates.
 */
package gorest

import (
	"testing"
)

type User struct{
    Id string
    FirstName string
    LastName string
    Age int
    Weight float32
}

type Service struct{
    RestService    "root=/serv/; consumes=application/json; produces=application/json"

    usersByNameAndAge EndPoint "method=GET; path=/person/{FName:string}/{Age:int}; output=[]User"
}

func(serv Service)    UsersByNameAndAge(FName string,Age int) []User{
    users:=make([]User,0)
    users=append(users,User{"user1",FName,"Soap",Age,89.7})
    users=append(users,User{"user2",FName,"Soap2",Age,89.7})
    return users
}

func TestServiceMetaData(t *testing.T){
    meta:= prepServiceMetaData("root=/serv/; consumes=application/json; produces=application/xml",new(Service))

    if meta.consumesMime != "application/json" {
       t.Error("Parsed incorrectly: 'consumesMime'")
    }
    if meta.producesMime != "application/xml" {
       t.Error("Parsed incorrectly: 'producesMime' ")
    }
    if meta.root != "/serv/" {
       t.Error("Parsed incorrectly: root ")
    }

}

func TestEndPointStruct(t *testing.T){
    meta:=makeEndPointStruct("method=GET; path=/person/{FName:string}/{Age:int}; output=[]User","/serv/")

    if meta.requestMethod != GET{
       t.Error("Parsed incorrectly: request method ")
    }
    if meta.root != "serv/person/"{
       t.Error("Parsed incorrectly: root ")
    }

    if meta.outputType == "User" && meta.outputTypeIsArray{
    }else{
       t.Error("Parsed incorrectly: return parameter ")
    }

    if meta.paramLen != 2{
       t.Error("Parsed incorrectly: parameter length ")
    }else if meta.params[0].name != "FName"  && meta.params[0].typeName != "User"{
       t.Error("Parsed incorrectly: path parameter names and types")
    }

}
<|endoftext|>


<commit_before>package gosrc

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"strings"
)

// Adapted from github.com/golang/gddo/gosrc.

// RuntimeVersion is the version of go stdlib to use. We allow it to be
// different to runtime.Version for test data.
var RuntimeVersion = runtime.Version()

var noGoGetDomains = getenvArray("NO_GO_GET_DOMAINS")
var blacklistGoGet = getenvArray("BLACKLIST_GO_GET")

func getenv(key, fallback string) string {
	value := os.Getenv(key)
	if value == "" {
		return fallback
	}
	return value
}

func getenvArray(key string) []string {
	var value []string
	err := json.Unmarshal([]byte(getenv(key, "[]")), &value)
	if err != nil {
		fmt.Println("Unable to parse environment variable", key, getenv(key, "[]"), "as a JSON array")
		return []string{}
	}
	return value
}

type Directory struct {
	ImportPath  string // the Go import path for this package
	ProjectRoot string // import path prefix for all packages in the project
	CloneURL    string // the VCS clone URL
	RepoPrefix  string // the path to this directory inside the repo, if set
	VCS         string // one of "git", "hg", "svn", etc.
	Rev         string // the VCS revision specifier, if any
}

var errNoMatch = errors.New("no match")

func ResolveImportPath(client *http.Client, importPath string) (*Directory, error) {
	if d, err := resolveStaticImportPath(importPath); err == nil {
		return d, nil
	} else if err != nil && err != errNoMatch {
		return nil, err
	}
	return resolveDynamicImportPath(client, importPath)
}

func resolveStaticImportPath(importPath string) (*Directory, error) {
	if IsStdlibPkg(importPath) {
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: "",
			CloneURL:    "https://github.com/golang/go",
			RepoPrefix:  "src",
			VCS:         "git",
			Rev:         RuntimeVersion,
		}, nil
	}

	// This allows users to set a list of domains that we should NEVER perform
	// go get or git clone against. This is useful when e.g. a user has not
	// correctly configured a monorepo and we are constantly hitting their
	// production website to resolve import paths like "facebook.com/pkg/util"
	// and skewing their own 404 metrics. This DOES mean these imports will be
	// broken until they do correctly configure their monorepo (so we can
	// identify its GOPATH), but it gives them a quick escape hatch that is
	// better than "turn off the Sourcegraph server".
	for _, domain := range blacklistGoGet {
		if strings.HasPrefix(importPath, domain) {
			return nil, errors.New("import path in blacklistGoGet configuration")
		}
	}

	// noGoGetDomains is a list of domains we do not attempt standard go vanity
	// import resolution. Instead we take an educated guess based on the URL how
	// to create the directory struct.
	//
	// This allows a user to set a list of domains that are considered to be
	// non-go-gettable, i.e. standard git repositories. Some on-prem customers
	// use setups like this, where they directly import non-go-gettable git
	// repository URLs like "mygitolite.aws.me.org/mux.git/subpkg"
	for _, domain := range noGoGetDomains {
		if !strings.HasPrefix(importPath, domain) {
			continue
		}
		d, err := guessImportPath(importPath)
		if d != nil || err != nil {
			return d, err
		}
	}

	switch {
	case strings.HasPrefix(importPath, "github.com/"):
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("invalid github.com/golang.org import path: %q", importPath)
		}
		repo := parts[0] + "/" + parts[1] + "/" + parts[2]
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: repo,
			CloneURL:    "https://" + repo,
			VCS:         "git",
		}, nil

	// The next two cases check for dependencies that actually come from the Go
	// standard library. This approach to resolving the import paths could link
	// the user to a newer version of a golang/org/x/ package than what is in use
	// when go-langserver is released. That's acceptable because we don't support
	// multiple Go versions, we don't publish which Go version our language server
	// is using anywhere, and these dependencies are very rare in practive (99% of
	// users are unlikely to encounter them).
	case strings.HasPrefix(importPath, "golang.org/x/"):
		d, err := resolveStaticImportPath(strings.Replace(importPath, "golang.org/x/", "github.com/golang/", 1))
		if err != nil {
			return nil, err
		}
		d.ProjectRoot = strings.Replace(d.ProjectRoot, "github.com/golang/", "golang.org/x/", 1)
		return d, nil

	// This is the same as the previous case, except with the `.` replaced with an `_`.
	case strings.HasPrefix(importPath, "golang_org/x/"):
		d, err := resolveStaticImportPath(strings.Replace(importPath, "golang_org/x/", "github.com/golang/", 1))
		if err != nil {
			return nil, err
		}
		d.ProjectRoot = strings.Replace(d.ProjectRoot, "github.com/golang/", "golang_org/x/", 1)
		return d, nil
	}
	return nil, errNoMatch
}

// guessImportPath is used by noGoGetDomains since we can't do the usual
// go get resolution.
func guessImportPath(importPath string) (*Directory, error) {
	if !strings.Contains(importPath, ".git") {
		// Assume GitHub-like where two path elements is the project
		// root.
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("invalid GitHub-like import path: %q", importPath)
		}
		repo := parts[0] + "/" + parts[1] + "/" + parts[2]
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: repo,
			CloneURL:    "http://" + repo,
			VCS:         "git",
		}, nil
	}

	// TODO(slimsag): We assume that .git only shows up
	// once in the import path. Not always true, but generally
	// should be in 99% of cases.
	split := strings.Split(importPath, ".git")
	if len(split) != 2 {
		return nil, fmt.Errorf("expected one .git in %q", importPath)
	}

	return &Directory{
		ImportPath:  importPath,
		ProjectRoot: split[0] + ".git",
		CloneURL:    "http://" + split[0] + ".git",
		VCS:         "git",
	}, nil
}

// gopkgSrcTemplate matches the go-source dir templates specified by the
// popular gopkg.in
var gopkgSrcTemplate = regexp.MustCompile(`https://(github.com/[^/]*/[^/]*)/tree/([^/]*)\{/dir\}`)

func resolveDynamicImportPath(client *http.Client, importPath string) (*Directory, error) {
	metaProto, im, sm, err := fetchMeta(client, importPath)
	if err != nil {
		return nil, err
	}

	if im.prefix != importPath {
		var imRoot *importMeta
		metaProto, imRoot, _, err = fetchMeta(client, im.prefix)
		if err != nil {
			return nil, err
		}
		if *imRoot != *im {
			return nil, fmt.Errorf("project root mismatch: %q != %q", *imRoot, *im)
		}
	}

	// clonePath is the repo URL from import meta tag, with the "scheme://" prefix removed.
	// It should be used for cloning repositories.
	// repo is the repo URL from import meta tag, with the "scheme://" prefix removed, and
	// a possible ".vcs" suffix trimmed.
	i := strings.Index(im.repo, "://")
	if i < 0 {
		return nil, fmt.Errorf("bad repo URL: %s", im.repo)
	}
	clonePath := im.repo[i+len("://"):]
	repo := strings.TrimSuffix(clonePath, "."+im.vcs)
	dirName := importPath[len(im.prefix):]

	var dir *Directory
	if sm != nil {
		m := gopkgSrcTemplate.FindStringSubmatch(sm.dirTemplate)
		if len(m) > 0 {
			// We are doing best effort, so we ignore err
			dir, _ = resolveStaticImportPath(m[1] + dirName)
			if dir != nil {
				dir.Rev = m[2]
			}
		}
	}

	if dir == nil {
		// We are doing best effort, so we ignore err
		dir, _ = resolveStaticImportPath(repo + dirName)
	}

	if dir == nil {
		dir = &Directory{}
	}
	dir.ImportPath = importPath
	dir.ProjectRoot = im.prefix
	if dir.CloneURL == "" {
		dir.CloneURL = metaProto + "://" + repo + "." + im.vcs
	}
	dir.VCS = im.vcs
	return dir, nil
}

// importMeta represents the values in a go-import meta tag.
//
// See https://golang.org/cmd/go/#hdr-Remote_import_paths.
type importMeta struct {
	prefix string // the import path corresponding to the repository root
	vcs    string // one of "git", "hg", "svn", etc.
	repo   string // root of the VCS repo containing a scheme and not containing a .vcs qualifier
}

// sourceMeta represents the values in a go-source meta tag.
type sourceMeta struct {
	prefix       string
	projectURL   string
	dirTemplate  string
	fileTemplate string
}

func fetchMeta(client *http.Client, importPath string) (scheme string, im *importMeta, sm *sourceMeta, err error) {
	uri := importPath
	if !strings.Contains(uri, "/") {
		// Add slash for root of domain.
		uri = uri + "/"
	}
	uri = uri + "?go-get=1"

	scheme = "https"
	resp, err := client.Get(scheme + "://" + uri)
	if err != nil || resp.StatusCode != 200 {
		if err == nil {
			resp.Body.Close()
		}
		scheme = "http"
		resp, err = client.Get(scheme + "://" + uri)
		if err != nil {
			return scheme, nil, nil, err
		}
	}
	defer resp.Body.Close()
	im, sm, err = parseMeta(scheme, importPath, resp.Body)
	return scheme, im, sm, err
}

func parseMeta(scheme, importPath string, r io.Reader) (im *importMeta, sm *sourceMeta, err error) {
	errorMessage := "go-import meta tag not found"

	d := xml.NewDecoder(r)
	d.Strict = false
metaScan:
	for {
		t, tokenErr := d.Token()
		if tokenErr != nil {
			break metaScan
		}
		switch t := t.(type) {
		case xml.EndElement:
			if strings.EqualFold(t.Name.Local, "head") {
				break metaScan
			}
		case xml.StartElement:
			if strings.EqualFold(t.Name.Local, "body") {
				break metaScan
			}
			if !strings.EqualFold(t.Name.Local, "meta") {
				continue metaScan
			}
			nameAttr := attrValue(t.Attr, "name")
			if nameAttr != "go-import" && nameAttr != "go-source" {
				continue metaScan
			}
			fields := strings.Fields(attrValue(t.Attr, "content"))
			if len(fields) < 1 {
				continue metaScan
			}
			prefix := fields[0]
			if !strings.HasPrefix(importPath, prefix) ||
				!(len(importPath) == len(prefix) || importPath[len(prefix)] == '/') {
				// Ignore if root is not a prefix of the  path. This allows a
				// site to use a single error page for multiple repositories.
				continue metaScan
			}
			switch nameAttr {
			case "go-import":
				if len(fields) != 3 {
					errorMessage = "go-import meta tag content attribute does not have three fields"
					continue metaScan
				}
				if im != nil {
					im = nil
					errorMessage = "more than one go-import meta tag found"
					break metaScan
				}
				im = &importMeta{
					prefix: prefix,
					vcs:    fields[1],
					repo:   fields[2],
				}
			case "go-source":
				if sm != nil {
					// Ignore extra go-source meta tags.
					continue metaScan
				}
				if len(fields) != 4 {
					continue metaScan
				}
				sm = &sourceMeta{
					prefix:       prefix,
					projectURL:   fields[1],
					dirTemplate:  fields[2],
					fileTemplate: fields[3],
				}
			}
		}
	}
	if im == nil {
		return nil, nil, fmt.Errorf("%s at %s://%s", errorMessage, scheme, importPath)
	}
	if sm != nil && sm.prefix != im.prefix {
		sm = nil
	}
	return im, sm, nil
}

func attrValue(attrs []xml.Attr, name string) string {
	for _, a := range attrs {
		if strings.EqualFold(a.Name.Local, name) {
			return a.Value
		}
	}
	return ""
}
<commit_msg>Correct docs about golang.org/x/ vs golang_org/x/<commit_after>package gosrc

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"strings"
)

// Adapted from github.com/golang/gddo/gosrc.

// RuntimeVersion is the version of go stdlib to use. We allow it to be
// different to runtime.Version for test data.
var RuntimeVersion = runtime.Version()

var noGoGetDomains = getenvArray("NO_GO_GET_DOMAINS")
var blacklistGoGet = getenvArray("BLACKLIST_GO_GET")

func getenv(key, fallback string) string {
	value := os.Getenv(key)
	if value == "" {
		return fallback
	}
	return value
}

func getenvArray(key string) []string {
	var value []string
	err := json.Unmarshal([]byte(getenv(key, "[]")), &value)
	if err != nil {
		fmt.Println("Unable to parse environment variable", key, getenv(key, "[]"), "as a JSON array")
		return []string{}
	}
	return value
}

type Directory struct {
	ImportPath  string // the Go import path for this package
	ProjectRoot string // import path prefix for all packages in the project
	CloneURL    string // the VCS clone URL
	RepoPrefix  string // the path to this directory inside the repo, if set
	VCS         string // one of "git", "hg", "svn", etc.
	Rev         string // the VCS revision specifier, if any
}

var errNoMatch = errors.New("no match")

func ResolveImportPath(client *http.Client, importPath string) (*Directory, error) {
	if d, err := resolveStaticImportPath(importPath); err == nil {
		return d, nil
	} else if err != nil && err != errNoMatch {
		return nil, err
	}
	return resolveDynamicImportPath(client, importPath)
}

func resolveStaticImportPath(importPath string) (*Directory, error) {
	if IsStdlibPkg(importPath) {
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: "",
			CloneURL:    "https://github.com/golang/go",
			RepoPrefix:  "src",
			VCS:         "git",
			Rev:         RuntimeVersion,
		}, nil
	}

	// This allows users to set a list of domains that we should NEVER perform
	// go get or git clone against. This is useful when e.g. a user has not
	// correctly configured a monorepo and we are constantly hitting their
	// production website to resolve import paths like "facebook.com/pkg/util"
	// and skewing their own 404 metrics. This DOES mean these imports will be
	// broken until they do correctly configure their monorepo (so we can
	// identify its GOPATH), but it gives them a quick escape hatch that is
	// better than "turn off the Sourcegraph server".
	for _, domain := range blacklistGoGet {
		if strings.HasPrefix(importPath, domain) {
			return nil, errors.New("import path in blacklistGoGet configuration")
		}
	}

	// noGoGetDomains is a list of domains we do not attempt standard go vanity
	// import resolution. Instead we take an educated guess based on the URL how
	// to create the directory struct.
	//
	// This allows a user to set a list of domains that are considered to be
	// non-go-gettable, i.e. standard git repositories. Some on-prem customers
	// use setups like this, where they directly import non-go-gettable git
	// repository URLs like "mygitolite.aws.me.org/mux.git/subpkg"
	for _, domain := range noGoGetDomains {
		if !strings.HasPrefix(importPath, domain) {
			continue
		}
		d, err := guessImportPath(importPath)
		if d != nil || err != nil {
			return d, err
		}
	}

	switch {
	case strings.HasPrefix(importPath, "github.com/"):
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("invalid github.com/golang.org import path: %q", importPath)
		}
		repo := parts[0] + "/" + parts[1] + "/" + parts[2]
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: repo,
			CloneURL:    "https://" + repo,
			VCS:         "git",
		}, nil

	case strings.HasPrefix(importPath, "golang.org/x/"):
		d, err := resolveStaticImportPath(strings.Replace(importPath, "golang.org/x/", "github.com/golang/", 1))
		if err != nil {
			return nil, err
		}
		d.ProjectRoot = strings.Replace(d.ProjectRoot, "github.com/golang/", "golang.org/x/", 1)
		return d, nil

	// The next case checks for dependencies that actually come from the Go
	// standard library. This approach to resolving the import paths could link
	// the user to a newer version of a golang.org/x/ package than what is in use
	// when go-langserver is released. That's acceptable because we don't support
	// multiple Go versions, we don't publish which Go version our language server
	// is using anywhere, and these dependencies are very rare in practice (99% of
	// users are unlikely to encounter them).
	//
	// This is the same as the previous case, except with the `.` replaced with an
	// `_`.
	case strings.HasPrefix(importPath, "golang_org/x/"):
		d, err := resolveStaticImportPath(strings.Replace(importPath, "golang_org/x/", "github.com/golang/", 1))
		if err != nil {
			return nil, err
		}
		d.ProjectRoot = strings.Replace(d.ProjectRoot, "github.com/golang/", "golang_org/x/", 1)
		return d, nil
	}
	return nil, errNoMatch
}

// guessImportPath is used by noGoGetDomains since we can't do the usual
// go get resolution.
func guessImportPath(importPath string) (*Directory, error) {
	if !strings.Contains(importPath, ".git") {
		// Assume GitHub-like where two path elements is the project
		// root.
		parts := strings.SplitN(importPath, "/", 4)
		if len(parts) < 3 {
			return nil, fmt.Errorf("invalid GitHub-like import path: %q", importPath)
		}
		repo := parts[0] + "/" + parts[1] + "/" + parts[2]
		return &Directory{
			ImportPath:  importPath,
			ProjectRoot: repo,
			CloneURL:    "http://" + repo,
			VCS:         "git",
		}, nil
	}

	// TODO(slimsag): We assume that .git only shows up
	// once in the import path. Not always true, but generally
	// should be in 99% of cases.
	split := strings.Split(importPath, ".git")
	if len(split) != 2 {
		return nil, fmt.Errorf("expected one .git in %q", importPath)
	}

	return &Directory{
		ImportPath:  importPath,
		ProjectRoot: split[0] + ".git",
		CloneURL:    "http://" + split[0] + ".git",
		VCS:         "git",
	}, nil
}

// gopkgSrcTemplate matches the go-source dir templates specified by the
// popular gopkg.in
var gopkgSrcTemplate = regexp.MustCompile(`https://(github.com/[^/]*/[^/]*)/tree/([^/]*)\{/dir\}`)

func resolveDynamicImportPath(client *http.Client, importPath string) (*Directory, error) {
	metaProto, im, sm, err := fetchMeta(client, importPath)
	if err != nil {
		return nil, err
	}

	if im.prefix != importPath {
		var imRoot *importMeta
		metaProto, imRoot, _, err = fetchMeta(client, im.prefix)
		if err != nil {
			return nil, err
		}
		if *imRoot != *im {
			return nil, fmt.Errorf("project root mismatch: %q != %q", *imRoot, *im)
		}
	}

	// clonePath is the repo URL from import meta tag, with the "scheme://" prefix removed.
	// It should be used for cloning repositories.
	// repo is the repo URL from import meta tag, with the "scheme://" prefix removed, and
	// a possible ".vcs" suffix trimmed.
	i := strings.Index(im.repo, "://")
	if i < 0 {
		return nil, fmt.Errorf("bad repo URL: %s", im.repo)
	}
	clonePath := im.repo[i+len("://"):]
	repo := strings.TrimSuffix(clonePath, "."+im.vcs)
	dirName := importPath[len(im.prefix):]

	var dir *Directory
	if sm != nil {
		m := gopkgSrcTemplate.FindStringSubmatch(sm.dirTemplate)
		if len(m) > 0 {
			// We are doing best effort, so we ignore err
			dir, _ = resolveStaticImportPath(m[1] + dirName)
			if dir != nil {
				dir.Rev = m[2]
			}
		}
	}

	if dir == nil {
		// We are doing best effort, so we ignore err
		dir, _ = resolveStaticImportPath(repo + dirName)
	}

	if dir == nil {
		dir = &Directory{}
	}
	dir.ImportPath = importPath
	dir.ProjectRoot = im.prefix
	if dir.CloneURL == "" {
		dir.CloneURL = metaProto + "://" + repo + "." + im.vcs
	}
	dir.VCS = im.vcs
	return dir, nil
}

// importMeta represents the values in a go-import meta tag.
//
// See https://golang.org/cmd/go/#hdr-Remote_import_paths.
type importMeta struct {
	prefix string // the import path corresponding to the repository root
	vcs    string // one of "git", "hg", "svn", etc.
	repo   string // root of the VCS repo containing a scheme and not containing a .vcs qualifier
}

// sourceMeta represents the values in a go-source meta tag.
type sourceMeta struct {
	prefix       string
	projectURL   string
	dirTemplate  string
	fileTemplate string
}

func fetchMeta(client *http.Client, importPath string) (scheme string, im *importMeta, sm *sourceMeta, err error) {
	uri := importPath
	if !strings.Contains(uri, "/") {
		// Add slash for root of domain.
		uri = uri + "/"
	}
	uri = uri + "?go-get=1"

	scheme = "https"
	resp, err := client.Get(scheme + "://" + uri)
	if err != nil || resp.StatusCode != 200 {
		if err == nil {
			resp.Body.Close()
		}
		scheme = "http"
		resp, err = client.Get(scheme + "://" + uri)
		if err != nil {
			return scheme, nil, nil, err
		}
	}
	defer resp.Body.Close()
	im, sm, err = parseMeta(scheme, importPath, resp.Body)
	return scheme, im, sm, err
}

func parseMeta(scheme, importPath string, r io.Reader) (im *importMeta, sm *sourceMeta, err error) {
	errorMessage := "go-import meta tag not found"

	d := xml.NewDecoder(r)
	d.Strict = false
metaScan:
	for {
		t, tokenErr := d.Token()
		if tokenErr != nil {
			break metaScan
		}
		switch t := t.(type) {
		case xml.EndElement:
			if strings.EqualFold(t.Name.Local, "head") {
				break metaScan
			}
		case xml.StartElement:
			if strings.EqualFold(t.Name.Local, "body") {
				break metaScan
			}
			if !strings.EqualFold(t.Name.Local, "meta") {
				continue metaScan
			}
			nameAttr := attrValue(t.Attr, "name")
			if nameAttr != "go-import" && nameAttr != "go-source" {
				continue metaScan
			}
			fields := strings.Fields(attrValue(t.Attr, "content"))
			if len(fields) < 1 {
				continue metaScan
			}
			prefix := fields[0]
			if !strings.HasPrefix(importPath, prefix) ||
				!(len(importPath) == len(prefix) || importPath[len(prefix)] == '/') {
				// Ignore if root is not a prefix of the  path. This allows a
				// site to use a single error page for multiple repositories.
				continue metaScan
			}
			switch nameAttr {
			case "go-import":
				if len(fields) != 3 {
					errorMessage = "go-import meta tag content attribute does not have three fields"
					continue metaScan
				}
				if im != nil {
					im = nil
					errorMessage = "more than one go-import meta tag found"
					break metaScan
				}
				im = &importMeta{
					prefix: prefix,
					vcs:    fields[1],
					repo:   fields[2],
				}
			case "go-source":
				if sm != nil {
					// Ignore extra go-source meta tags.
					continue metaScan
				}
				if len(fields) != 4 {
					continue metaScan
				}
				sm = &sourceMeta{
					prefix:       prefix,
					projectURL:   fields[1],
					dirTemplate:  fields[2],
					fileTemplate: fields[3],
				}
			}
		}
	}
	if im == nil {
		return nil, nil, fmt.Errorf("%s at %s://%s", errorMessage, scheme, importPath)
	}
	if sm != nil && sm.prefix != im.prefix {
		sm = nil
	}
	return im, sm, nil
}

func attrValue(attrs []xml.Attr, name string) string {
	for _, a := range attrs {
		if strings.EqualFold(a.Name.Local, name) {
			return a.Value
		}
	}
	return ""
}
<|endoftext|>


<commit_before>// AUTO-GENERATED BY kocha build
// DO NOT EDIT THIS FILE
package main

import (
	"bytes"
	"compress/gzip"
	"{{.controllersImportPath}}"
	"github.com/naoina/kocha"
	"html/template"
	"io/ioutil"
	"regexp"
)

func main() {
	kocha.Init(&kocha.AppConfig{
		AppPath: "{{.appPath}}",
		AppName: "{{.appName}}",
		RouteTable: kocha.RouteTable{
			{{range .routeTable}}
			{
				Name: "{{.Name}}",
				Path: "{{.Path}}",
				Controller: {{.Controller|printf "%#v"}},
				MethodTypes: {{.MethodTypes|printf "%#v"}},
				RegexpPath: regexp.MustCompile("{{.RegexpPath.String}}"),
			},
			{{end}}
		},
		TemplateSet: kocha.TemplateSet{
			{{range $appName, $value := .templateSet}}
			"{{$appName}}": {{range $tmplName, $tmpl := $value}}{
				"{{$tmplName}}": template.Must(template.New("{{$tmplName}}").Funcs(kocha.TemplateFuncs).Parse(gunzip({{$tmpl|templateText}}))),
			},
			{{end}}
			{{end}}
		},
	})
	kocha.Run("{{.addr}}", {{.port}})
}

func init() {
	config := kocha.Config("{{.appName}}")
	{{range $name, $value := .config}}
	config.Set("{{$name}}", {{$value|printf "%#v"}}){{end}}
}

func gunzip(gz string) string {
	r, err := gzip.NewReader(bytes.NewReader([]byte(gz)))
	if err != nil {
		panic(err)
	}
	result, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return string(result)
}
<commit_msg>Fix an issue that 'build' command doesn't work<commit_after>// AUTO-GENERATED BY kocha build
// DO NOT EDIT THIS FILE
package main

import (
	"bytes"
	"compress/gzip"
	"{{.controllersImportPath}}"
	"github.com/naoina/kocha"
	"html/template"
	"io/ioutil"
	"regexp"
)

func main() {
	kocha.Init(&kocha.AppConfig{
		AppPath: "{{.appPath}}",
		AppName: "{{.appName}}",
		RouteTable: kocha.RouteTable{
			{{range .routeTable}}
			{
				Name: "{{.Name}}",
				Path: "{{.Path}}",
				Controller: {{.Controller|printf "%T"}}{},
				MethodTypes: {{.MethodTypes|printf "%#v"}},
				RegexpPath: regexp.MustCompile("{{.RegexpPath.String}}"),
			},
			{{end}}
		},
		TemplateSet: kocha.TemplateSet{
			{{range $appName, $value := .templateSet}}
			"{{$appName}}": {{range $tmplName, $tmpl := $value}}{
				"{{$tmplName}}": template.Must(template.New("{{$tmplName}}").Funcs(kocha.TemplateFuncs).Parse(gunzip({{$tmpl|templateText}}))),
			},
			{{end}}
			{{end}}
		},
	})
	kocha.Run("{{.addr}}", {{.port}})
}

func init() {
	config := kocha.Config("{{.appName}}")
	{{range $name, $value := .config}}
	config.Set("{{$name}}", {{$value|printf "%#v"}}){{end}}
}

func gunzip(gz string) string {
	r, err := gzip.NewReader(bytes.NewReader([]byte(gz)))
	if err != nil {
		panic(err)
	}
	result, err := ioutil.ReadAll(r)
	if err != nil {
		panic(err)
	}
	return string(result)
}
<|endoftext|>


<commit_before>// Copyright 2013 Prometheus Team
// 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 manager

import (
	"errors"
	"log"
	"time"
)

const (
	minimumRefreshPeriod    = 5 * time.Minute
	notificationRetryPeriod = 1 * time.Minute
)

// AggregationRule creates and manages the scope for received events.
type AggregationRule struct {
	Filters Filters

	RepeatRate time.Duration
}

type AggregationInstances []*AggregationInstance

type AggregationInstance struct {
	Rule  *AggregationRule
	Event *Event

	// When was this AggregationInstance created?
	Created time.Time
	// When was the last refresh received into this AggregationInstance?
	LastRefreshed time.Time

	// When was the last successful notification sent out for this
	// AggregationInstance?
	lastNotificationSent time.Time
	// Timer used to trigger a notification retry/resend.
	notificationResendTimer *time.Timer
	// Timer used to trigger the deletion of the AggregationInstance after it
	// hasn't been refreshed for too long.
	expiryTimer *time.Timer
}

func (r *AggregationRule) Handles(e *Event) bool {
	return r.Filters.Handles(e)
}

func (r *AggregationInstance) Ingest(e *Event) {
	r.Event = e
	r.LastRefreshed = time.Now()

	r.expiryTimer.Reset(minimumRefreshPeriod)
}

func (r *AggregationInstance) SendNotification(s SummaryReceiver) {
	if time.Since(r.lastNotificationSent) < r.Rule.RepeatRate {
		return
	}

	err := s.Receive(&EventSummary{
		Rule:  r.Rule,
		Event: r.Event,
	})
	if err != nil {
		log.Printf("Error while sending notification: %s, retrying in %v", err, notificationRetryPeriod)
		r.resendNotificationAfter(notificationRetryPeriod, s)
		return
	}

	r.resendNotificationAfter(r.Rule.RepeatRate, s)
	r.lastNotificationSent = time.Now()
}

func (r *AggregationInstance) resendNotificationAfter(d time.Duration, s SummaryReceiver) {
	// BUG: we can't just call SendNotification whenever the timer ends without
	// any synchronisation. The timer should instead feed into a channel which is
	// served by the main Dispatch() loop.
	r.notificationResendTimer = time.AfterFunc(d, func() {
		r.SendNotification(s)
	})
}

func (r *AggregationInstance) Close() {
	if r.notificationResendTimer != nil {
		r.notificationResendTimer.Stop()
	}
	if r.expiryTimer != nil {
		r.expiryTimer.Stop()
	}
}

type AggregationRules []*AggregationRule

type Aggregator struct {
	Rules      AggregationRules
	Aggregates map[EventFingerprint]*AggregationInstance

	aggRequests             chan *aggregateEventsRequest
	getAggregatesRequests   chan *getAggregatesRequest
	removeAggregateRequests chan EventFingerprint
	rulesRequests           chan *aggregatorResetRulesRequest
	closeRequests           chan *closeRequest
}

func NewAggregator() *Aggregator {
	return &Aggregator{
		Aggregates: make(map[EventFingerprint]*AggregationInstance),

		aggRequests:             make(chan *aggregateEventsRequest),
		getAggregatesRequests:   make(chan *getAggregatesRequest),
		removeAggregateRequests: make(chan EventFingerprint),
		rulesRequests:           make(chan *aggregatorResetRulesRequest),
		closeRequests:           make(chan *closeRequest),
	}
}

func (a *Aggregator) Close() {
	req := &closeRequest{
		done: make(chan bool),
	}
	a.closeRequests <- req
	<-req.done
}

func (a *Aggregator) closeInternal() {
	close(a.rulesRequests)
	close(a.aggRequests)
	close(a.getAggregatesRequests)
	close(a.removeAggregateRequests)
	close(a.closeRequests)
}

type aggregateEventsResponse struct {
	Err error
}

type aggregateEventsRequest struct {
	Events Events

	Response chan *aggregateEventsResponse
}

type getAggregatesResponse struct {
	Aggregates AggregationInstances
}

type getAggregatesRequest struct {
	Response chan getAggregatesResponse
}

type closeRequest struct {
	done chan bool
}

func (a *Aggregator) aggregate(req *aggregateEventsRequest, s SummaryReceiver) {
	if len(a.Rules) == 0 {
		req.Response <- &aggregateEventsResponse{
			Err: errors.New("No aggregation rules"),
		}
		close(req.Response)
		return
	}
	log.Println("aggregating", *req)
	for _, element := range req.Events {
		for _, r := range a.Rules {
			log.Println("Checking rule", r, r.Handles(element))
			if r.Handles(element) {
				fp := element.Fingerprint()
				aggregation, ok := a.Aggregates[fp]
				if !ok {
					expTimer := time.AfterFunc(minimumRefreshPeriod, func() {
						a.removeAggregateRequests <- fp
					})

					aggregation = &AggregationInstance{
						Rule:        r,
						Created:     time.Now(),
						expiryTimer: expTimer,
					}

					a.Aggregates[fp] = aggregation
				}

				aggregation.Ingest(element)
				aggregation.SendNotification(s)
				break
			}
		}
	}

	req.Response <- new(aggregateEventsResponse)
	close(req.Response)
}

type aggregatorResetRulesResponse struct{}

type aggregatorResetRulesRequest struct {
	Rules AggregationRules

	Response chan *aggregatorResetRulesResponse
}

func (a *Aggregator) replaceRules(r *aggregatorResetRulesRequest) {
	log.Println("Replacing", len(r.Rules), "aggregator rules...")
	a.Rules = r.Rules

	r.Response <- new(aggregatorResetRulesResponse)
	close(r.Response)
}

func (a *Aggregator) AlertAggregates() AggregationInstances {
	req := &getAggregatesRequest{
		Response: make(chan getAggregatesResponse),
	}

	a.getAggregatesRequests <- req

	result := <-req.Response

	return result.Aggregates
}

func (a *Aggregator) aggregates() AggregationInstances {
	aggs := make(AggregationInstances, 0, len(a.Aggregates))
	for _, agg := range a.Aggregates {
		aggs = append(aggs, agg)
	}
	return aggs
}

func (a *Aggregator) Receive(e Events) error {
	req := &aggregateEventsRequest{
		Events:   e,
		Response: make(chan *aggregateEventsResponse),
	}

	a.aggRequests <- req

	result := <-req.Response

	return result.Err
}

func (a *Aggregator) SetRules(r AggregationRules) error {
	req := &aggregatorResetRulesRequest{
		Rules:    r,
		Response: make(chan *aggregatorResetRulesResponse),
	}

	a.rulesRequests <- req

	_ = <-req.Response

	return nil
}

func (a *Aggregator) Dispatch(s SummaryReceiver) {
	for {
		select {
		case req := <-a.aggRequests:
			a.aggregate(req, s)

		case rules := <-a.rulesRequests:
			a.replaceRules(rules)

		case req := <-a.getAggregatesRequests:
			aggs := a.aggregates()
			req.Response <- getAggregatesResponse{
				Aggregates: aggs,
			}
			close(req.Response)

		case fp := <-a.removeAggregateRequests:
			log.Println("Deleting expired aggregation instance", a)
			a.Aggregates[fp].Close()
			delete(a.Aggregates, fp)

		case req := <-a.closeRequests:
			a.closeInternal()
			req.done <- true
      // BUG: Simply returning here will prevent proper draining. Fix this.
			return
		}
	}
}
<commit_msg>Run go fmt.<commit_after>// Copyright 2013 Prometheus Team
// 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 manager

import (
	"errors"
	"log"
	"time"
)

const (
	minimumRefreshPeriod    = 5 * time.Minute
	notificationRetryPeriod = 1 * time.Minute
)

// AggregationRule creates and manages the scope for received events.
type AggregationRule struct {
	Filters Filters

	RepeatRate time.Duration
}

type AggregationInstances []*AggregationInstance

type AggregationInstance struct {
	Rule  *AggregationRule
	Event *Event

	// When was this AggregationInstance created?
	Created time.Time
	// When was the last refresh received into this AggregationInstance?
	LastRefreshed time.Time

	// When was the last successful notification sent out for this
	// AggregationInstance?
	lastNotificationSent time.Time
	// Timer used to trigger a notification retry/resend.
	notificationResendTimer *time.Timer
	// Timer used to trigger the deletion of the AggregationInstance after it
	// hasn't been refreshed for too long.
	expiryTimer *time.Timer
}

func (r *AggregationRule) Handles(e *Event) bool {
	return r.Filters.Handles(e)
}

func (r *AggregationInstance) Ingest(e *Event) {
	r.Event = e
	r.LastRefreshed = time.Now()

	r.expiryTimer.Reset(minimumRefreshPeriod)
}

func (r *AggregationInstance) SendNotification(s SummaryReceiver) {
	if time.Since(r.lastNotificationSent) < r.Rule.RepeatRate {
		return
	}

	err := s.Receive(&EventSummary{
		Rule:  r.Rule,
		Event: r.Event,
	})
	if err != nil {
		log.Printf("Error while sending notification: %s, retrying in %v", err, notificationRetryPeriod)
		r.resendNotificationAfter(notificationRetryPeriod, s)
		return
	}

	r.resendNotificationAfter(r.Rule.RepeatRate, s)
	r.lastNotificationSent = time.Now()
}

func (r *AggregationInstance) resendNotificationAfter(d time.Duration, s SummaryReceiver) {
	// BUG: we can't just call SendNotification whenever the timer ends without
	// any synchronisation. The timer should instead feed into a channel which is
	// served by the main Dispatch() loop.
	r.notificationResendTimer = time.AfterFunc(d, func() {
		r.SendNotification(s)
	})
}

func (r *AggregationInstance) Close() {
	if r.notificationResendTimer != nil {
		r.notificationResendTimer.Stop()
	}
	if r.expiryTimer != nil {
		r.expiryTimer.Stop()
	}
}

type AggregationRules []*AggregationRule

type Aggregator struct {
	Rules      AggregationRules
	Aggregates map[EventFingerprint]*AggregationInstance

	aggRequests             chan *aggregateEventsRequest
	getAggregatesRequests   chan *getAggregatesRequest
	removeAggregateRequests chan EventFingerprint
	rulesRequests           chan *aggregatorResetRulesRequest
	closeRequests           chan *closeRequest
}

func NewAggregator() *Aggregator {
	return &Aggregator{
		Aggregates: make(map[EventFingerprint]*AggregationInstance),

		aggRequests:             make(chan *aggregateEventsRequest),
		getAggregatesRequests:   make(chan *getAggregatesRequest),
		removeAggregateRequests: make(chan EventFingerprint),
		rulesRequests:           make(chan *aggregatorResetRulesRequest),
		closeRequests:           make(chan *closeRequest),
	}
}

func (a *Aggregator) Close() {
	req := &closeRequest{
		done: make(chan bool),
	}
	a.closeRequests <- req
	<-req.done
}

func (a *Aggregator) closeInternal() {
	close(a.rulesRequests)
	close(a.aggRequests)
	close(a.getAggregatesRequests)
	close(a.removeAggregateRequests)
	close(a.closeRequests)
}

type aggregateEventsResponse struct {
	Err error
}

type aggregateEventsRequest struct {
	Events Events

	Response chan *aggregateEventsResponse
}

type getAggregatesResponse struct {
	Aggregates AggregationInstances
}

type getAggregatesRequest struct {
	Response chan getAggregatesResponse
}

type closeRequest struct {
	done chan bool
}

func (a *Aggregator) aggregate(req *aggregateEventsRequest, s SummaryReceiver) {
	if len(a.Rules) == 0 {
		req.Response <- &aggregateEventsResponse{
			Err: errors.New("No aggregation rules"),
		}
		close(req.Response)
		return
	}
	log.Println("aggregating", *req)
	for _, element := range req.Events {
		for _, r := range a.Rules {
			log.Println("Checking rule", r, r.Handles(element))
			if r.Handles(element) {
				fp := element.Fingerprint()
				aggregation, ok := a.Aggregates[fp]
				if !ok {
					expTimer := time.AfterFunc(minimumRefreshPeriod, func() {
						a.removeAggregateRequests <- fp
					})

					aggregation = &AggregationInstance{
						Rule:        r,
						Created:     time.Now(),
						expiryTimer: expTimer,
					}

					a.Aggregates[fp] = aggregation
				}

				aggregation.Ingest(element)
				aggregation.SendNotification(s)
				break
			}
		}
	}

	req.Response <- new(aggregateEventsResponse)
	close(req.Response)
}

type aggregatorResetRulesResponse struct{}

type aggregatorResetRulesRequest struct {
	Rules AggregationRules

	Response chan *aggregatorResetRulesResponse
}

func (a *Aggregator) replaceRules(r *aggregatorResetRulesRequest) {
	log.Println("Replacing", len(r.Rules), "aggregator rules...")
	a.Rules = r.Rules

	r.Response <- new(aggregatorResetRulesResponse)
	close(r.Response)
}

func (a *Aggregator) AlertAggregates() AggregationInstances {
	req := &getAggregatesRequest{
		Response: make(chan getAggregatesResponse),
	}

	a.getAggregatesRequests <- req

	result := <-req.Response

	return result.Aggregates
}

func (a *Aggregator) aggregates() AggregationInstances {
	aggs := make(AggregationInstances, 0, len(a.Aggregates))
	for _, agg := range a.Aggregates {
		aggs = append(aggs, agg)
	}
	return aggs
}

func (a *Aggregator) Receive(e Events) error {
	req := &aggregateEventsRequest{
		Events:   e,
		Response: make(chan *aggregateEventsResponse),
	}

	a.aggRequests <- req

	result := <-req.Response

	return result.Err
}

func (a *Aggregator) SetRules(r AggregationRules) error {
	req := &aggregatorResetRulesRequest{
		Rules:    r,
		Response: make(chan *aggregatorResetRulesResponse),
	}

	a.rulesRequests <- req

	_ = <-req.Response

	return nil
}

func (a *Aggregator) Dispatch(s SummaryReceiver) {
	for {
		select {
		case req := <-a.aggRequests:
			a.aggregate(req, s)

		case rules := <-a.rulesRequests:
			a.replaceRules(rules)

		case req := <-a.getAggregatesRequests:
			aggs := a.aggregates()
			req.Response <- getAggregatesResponse{
				Aggregates: aggs,
			}
			close(req.Response)

		case fp := <-a.removeAggregateRequests:
			log.Println("Deleting expired aggregation instance", a)
			a.Aggregates[fp].Close()
			delete(a.Aggregates, fp)

		case req := <-a.closeRequests:
			a.closeInternal()
			req.done <- true
			// BUG: Simply returning here will prevent proper draining. Fix this.
			return
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 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 common

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/tools/cache"
	watchtools "k8s.io/client-go/tools/watch"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

var _ = ginkgo.Describe("[sig-node] ConfigMap", func() {
	f := framework.NewDefaultFramework("configmap")

	var dc dynamic.Interface

	ginkgo.BeforeEach(func() {
		dc = f.DynamicClient
	})

	/*
		Release : v1.9
		Testname: ConfigMap, from environment field
		Description: Create a Pod with an environment variable value set using a value from ConfigMap. A ConfigMap value MUST be accessible in the container environment.
	*/
	framework.ConformanceIt("should be consumable via environment variable [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						Env: []v1.EnvVar{
							{
								Name: "CONFIG_DATA_1",
								ValueFrom: &v1.EnvVarSource{
									ConfigMapKeyRef: &v1.ConfigMapKeySelector{
										LocalObjectReference: v1.LocalObjectReference{
											Name: name,
										},
										Key: "data-1",
									},
								},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"CONFIG_DATA_1=value-1",
		})
	})

	/*
		Release: v1.9
		Testname: ConfigMap, from environment variables
		Description: Create a Pod with a environment source from ConfigMap. All ConfigMap values MUST be available as environment variables in the container.
	*/
	framework.ConformanceIt("should be consumable via the environment [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newEnvFromConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						EnvFrom: []v1.EnvFromSource{
							{
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
							{
								Prefix:       "p_",
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"data_1=value-1", "data_2=value-2", "data_3=value-3",
			"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
		})
	})

	/*
	   Release : v1.14
	   Testname: ConfigMap, with empty-key
	   Description: Attempt to create a ConfigMap with an empty key. The creation MUST fail.
	*/
	framework.ConformanceIt("should fail to create ConfigMap with empty key", func() {
		configMap, err := newConfigMapWithEmptyKey(f)
		framework.ExpectError(err, "created configMap %q with empty key in namespace %q", configMap.Name, f.Namespace.Name)
	})

	ginkgo.It("should update ConfigMap successfully", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		configMap.Data = map[string]string{
			"data": "value",
		}
		ginkgo.By(fmt.Sprintf("Updating configMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(context.TODO(), configMap, metav1.UpdateOptions{})
		framework.ExpectNoError(err, "failed to update ConfigMap")

		configMapFromUpdate, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		ginkgo.By(fmt.Sprintf("Verifying update of ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		framework.ExpectEqual(configMapFromUpdate.Data, configMap.Data)
	})

	ginkgo.It("should run through a ConfigMap lifecycle", func() {
		testNamespaceName := f.Namespace.Name
		testConfigMapName := "test-configmap" + string(uuid.NewUUID())

		testConfigMap := v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: testConfigMapName,
				Labels: map[string]string{
					"test-configmap-static": "true",
				},
			},
			Data: map[string]string{
				"valueName": "value",
			},
		}

		w := &cache.ListWatch{
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				options.LabelSelector = "test-configmap-static=true"
				return f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Watch(context.TODO(), options)
			},
		}
		cml, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: "test-configmap-static=true"})
		framework.ExpectNoError(err)
		retryWatcher, err := watchtools.NewRetryWatcher(cml.ResourceVersion, w)

		ginkgo.By("creating a ConfigMap")
		_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Create(context.TODO(), &testConfigMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
			return watchEvent.Type == watch.Added, nil
		})
		framework.ExpectNoError(err, "failed to see a watch.Added event for the configmap we created")

		configMapPatchPayload, err := json.Marshal(v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"test-configmap": "patched",
				},
			},
			Data: map[string]string{
				"valueName": "value1",
			},
		})
		framework.ExpectNoError(err, "failed to marshal patch data")

		ginkgo.By("patching the ConfigMap")
		_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Patch(context.TODO(), testConfigMapName, types.StrategicMergePatchType, []byte(configMapPatchPayload), metav1.PatchOptions{})
		framework.ExpectNoError(err, "failed to patch ConfigMap")
		ginkgo.By("waiting for the ConfigMap to be modified")
		ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
			return watchEvent.Type == watch.Modified, nil
		})
		framework.ExpectNoError(err, "failed to see a watch.Modified event for the configmap we patched")

		ginkgo.By("fetching the ConfigMap")
		configMap, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Get(context.TODO(), testConfigMapName, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		framework.ExpectEqual(configMap.Data["valueName"], "value1", "failed to patch ConfigMap")
		framework.ExpectEqual(configMap.Labels["test-configmap"], "patched", "failed to patch ConfigMap")

		ginkgo.By("listing all ConfigMaps in all namespaces")
		configMapList, err := f.ClientSet.CoreV1().ConfigMaps("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to list ConfigMaps with LabelSelector")
		framework.ExpectNotEqual(len(configMapList.Items), 0, "no ConfigMaps found in ConfigMap list")
		testConfigMapFound := false
		for _, cm := range configMapList.Items {
			if cm.ObjectMeta.Name == testConfigMapName &&
				cm.ObjectMeta.Namespace == testNamespaceName &&
				cm.ObjectMeta.Labels["test-configmap-static"] == "true" &&
				cm.Data["valueName"] == "value1" {
				testConfigMapFound = true
				break
			}
		}
		framework.ExpectEqual(testConfigMapFound, true, "failed to find ConfigMap in list")

		ginkgo.By("deleting the ConfigMap by a collection")
		err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to delete ConfigMap collection with LabelSelector")
		ginkgo.By("waiting for the ConfigMap to be deleted")
		ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(watchEvent watch.Event) (bool, error) {
			return watchEvent.Type == watch.Deleted, nil
		})
		framework.ExpectNoError(err, "fasiled to observe a watch.Deleted event for the ConfigMap we deleted")
	})
})

func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"data_1": "value-1",
			"data_2": "value-2",
			"data_3": "value-3",
		},
	}
}

func newConfigMapWithEmptyKey(f *framework.Framework) (*v1.ConfigMap, error) {
	name := "configmap-test-emptyKey-" + string(uuid.NewUUID())
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"": "value-1",
		},
	}

	ginkgo.By(fmt.Sprintf("Creating configMap that has name %s", configMap.Name))
	return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
}
<commit_msg>For grins, make the watch checks very detailed<commit_after>/*
Copyright 2016 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 common

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	watch "k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/tools/cache"
	watchtools "k8s.io/client-go/tools/watch"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

var _ = ginkgo.Describe("[sig-node] ConfigMap", func() {
	f := framework.NewDefaultFramework("configmap")

	var dc dynamic.Interface

	ginkgo.BeforeEach(func() {
		dc = f.DynamicClient
	})

	/*
		Release : v1.9
		Testname: ConfigMap, from environment field
		Description: Create a Pod with an environment variable value set using a value from ConfigMap. A ConfigMap value MUST be accessible in the container environment.
	*/
	framework.ConformanceIt("should be consumable via environment variable [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						Env: []v1.EnvVar{
							{
								Name: "CONFIG_DATA_1",
								ValueFrom: &v1.EnvVarSource{
									ConfigMapKeyRef: &v1.ConfigMapKeySelector{
										LocalObjectReference: v1.LocalObjectReference{
											Name: name,
										},
										Key: "data-1",
									},
								},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"CONFIG_DATA_1=value-1",
		})
	})

	/*
		Release: v1.9
		Testname: ConfigMap, from environment variables
		Description: Create a Pod with a environment source from ConfigMap. All ConfigMap values MUST be available as environment variables in the container.
	*/
	framework.ConformanceIt("should be consumable via the environment [NodeConformance]", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newEnvFromConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
		var err error
		if configMap, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}); err != nil {
			framework.Failf("unable to create test configMap %s: %v", configMap.Name, err)
		}

		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "pod-configmaps-" + string(uuid.NewUUID()),
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "env-test",
						Image:   imageutils.GetE2EImage(imageutils.BusyBox),
						Command: []string{"sh", "-c", "env"},
						EnvFrom: []v1.EnvFromSource{
							{
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
							{
								Prefix:       "p_",
								ConfigMapRef: &v1.ConfigMapEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
							},
						},
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		f.TestContainerOutput("consume configMaps", pod, 0, []string{
			"data_1=value-1", "data_2=value-2", "data_3=value-3",
			"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
		})
	})

	/*
	   Release : v1.14
	   Testname: ConfigMap, with empty-key
	   Description: Attempt to create a ConfigMap with an empty key. The creation MUST fail.
	*/
	framework.ConformanceIt("should fail to create ConfigMap with empty key", func() {
		configMap, err := newConfigMapWithEmptyKey(f)
		framework.ExpectError(err, "created configMap %q with empty key in namespace %q", configMap.Name, f.Namespace.Name)
	})

	ginkgo.It("should update ConfigMap successfully", func() {
		name := "configmap-test-" + string(uuid.NewUUID())
		configMap := newConfigMap(f, name)
		ginkgo.By(fmt.Sprintf("Creating ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		configMap.Data = map[string]string{
			"data": "value",
		}
		ginkgo.By(fmt.Sprintf("Updating configMap %v/%v", f.Namespace.Name, configMap.Name))
		_, err = f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Update(context.TODO(), configMap, metav1.UpdateOptions{})
		framework.ExpectNoError(err, "failed to update ConfigMap")

		configMapFromUpdate, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Get(context.TODO(), name, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		ginkgo.By(fmt.Sprintf("Verifying update of ConfigMap %v/%v", f.Namespace.Name, configMap.Name))
		framework.ExpectEqual(configMapFromUpdate.Data, configMap.Data)
	})

	ginkgo.It("should run through a ConfigMap lifecycle", func() {
		testNamespaceName := f.Namespace.Name
		testConfigMapName := "test-configmap" + string(uuid.NewUUID())

		testConfigMap := v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: testConfigMapName,
				Labels: map[string]string{
					"test-configmap-static": "true",
				},
			},
			Data: map[string]string{
				"valueName": "value",
			},
		}

		w := &cache.ListWatch{
			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
				options.LabelSelector = "test-configmap-static=true"
				return f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Watch(context.TODO(), options)
			},
		}
		cml, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).List(context.TODO(), metav1.ListOptions{LabelSelector: "test-configmap-static=true"})
		framework.ExpectNoError(err)
		retryWatcher, err := watchtools.NewRetryWatcher(cml.ResourceVersion, w)

		ginkgo.By("creating a ConfigMap")
		_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Create(context.TODO(), &testConfigMap, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create ConfigMap")

		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(event watch.Event) (bool, error) {
			switch event.Type {
			case watch.Added:
				if cm, ok := event.Object.(*v1.ConfigMap); ok {
					found := cm.ObjectMeta.Name == testConfigMap.Name &&
						cm.Labels["test-configmap-static"] == "true" &&
						cm.Data["valueName"] == "value"
					return found, nil
				}
			default:
				framework.Logf("observed event type %v", event.Type)
			}
			return false, nil
		})
		framework.ExpectNoError(err, "failed to see a watch.Added event for the configmap we created")

		configMapPatchPayload, err := json.Marshal(v1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"test-configmap": "patched",
				},
			},
			Data: map[string]string{
				"valueName": "value1",
			},
		})
		framework.ExpectNoError(err, "failed to marshal patch data")

		ginkgo.By("patching the ConfigMap")
		_, err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Patch(context.TODO(), testConfigMapName, types.StrategicMergePatchType, []byte(configMapPatchPayload), metav1.PatchOptions{})
		framework.ExpectNoError(err, "failed to patch ConfigMap")
		ginkgo.By("waiting for the ConfigMap to be modified")
		ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(event watch.Event) (bool, error) {
			switch event.Type {
			case watch.Modified:
				if cm, ok := event.Object.(*v1.ConfigMap); ok {
					found := cm.ObjectMeta.Name == testConfigMap.Name &&
						cm.Labels["test-configmap-static"] == "true" &&
						cm.Labels["test-configmap"] == "patched" &&
						cm.Data["valueName"] == "value1"
					return found, nil
				}
			default:
				framework.Logf("observed event type %v", event.Type)
			}
			return false, nil
		})
		framework.ExpectNoError(err, "failed to see a watch.Modified event for the configmap we patched")

		ginkgo.By("fetching the ConfigMap")
		configMap, err := f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).Get(context.TODO(), testConfigMapName, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to get ConfigMap")
		framework.ExpectEqual(configMap.Data["valueName"], "value1", "failed to patch ConfigMap")
		framework.ExpectEqual(configMap.Labels["test-configmap"], "patched", "failed to patch ConfigMap")

		ginkgo.By("listing all ConfigMaps in all namespaces")
		configMapList, err := f.ClientSet.CoreV1().ConfigMaps("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to list ConfigMaps with LabelSelector")
		framework.ExpectNotEqual(len(configMapList.Items), 0, "no ConfigMaps found in ConfigMap list")
		testConfigMapFound := false
		for _, cm := range configMapList.Items {
			if cm.ObjectMeta.Name == testConfigMapName &&
				cm.ObjectMeta.Namespace == testNamespaceName &&
				cm.ObjectMeta.Labels["test-configmap-static"] == "true" &&
				cm.Data["valueName"] == "value1" {
				testConfigMapFound = true
				break
			}
		}
		framework.ExpectEqual(testConfigMapFound, true, "failed to find ConfigMap in list")

		ginkgo.By("deleting the ConfigMap by a collection")
		err = f.ClientSet.CoreV1().ConfigMaps(testNamespaceName).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{
			LabelSelector: "test-configmap-static=true",
		})
		framework.ExpectNoError(err, "failed to delete ConfigMap collection with LabelSelector")
		ginkgo.By("waiting for the ConfigMap to be deleted")
		ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()
		_, err = framework.WatchUntilWithoutRetry(ctx, retryWatcher, func(event watch.Event) (bool, error) {
			switch event.Type {
			case watch.Deleted:
				if cm, ok := event.Object.(*v1.ConfigMap); ok {
					found := cm.ObjectMeta.Name == testConfigMap.Name &&
						cm.Labels["test-configmap-static"] == "true" &&
						cm.Labels["test-configmap"] == "patched" &&
						cm.Data["valueName"] == "value1"
					return found, nil
				}
			default:
				framework.Logf("observed event type %v", event.Type)
			}
			return false, nil
		})
		framework.ExpectNoError(err, "fasiled to observe a watch.Deleted event for the ConfigMap we deleted")
	})
})

func newEnvFromConfigMap(f *framework.Framework, name string) *v1.ConfigMap {
	return &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"data_1": "value-1",
			"data_2": "value-2",
			"data_3": "value-3",
		},
	}
}

func newConfigMapWithEmptyKey(f *framework.Framework) (*v1.ConfigMap, error) {
	name := "configmap-test-emptyKey-" + string(uuid.NewUUID())
	configMap := &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: f.Namespace.Name,
			Name:      name,
		},
		Data: map[string]string{
			"": "value-1",
		},
	}

	ginkgo.By(fmt.Sprintf("Creating configMap that has name %s", configMap.Name))
	return f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(context.TODO(), configMap, metav1.CreateOptions{})
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"
)

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	CreateFile(fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)
	GetDiskFreeSpace() (FreeSpace, error)

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.DeleteOnClose in Cleanup.
	CanDeleteFile(*FileInfo) error
	CanDeleteDirectory(*FileInfo) error
	MoveFile(source *FileInfo, targetPath string, replaceExisting bool) error

	GetVolumeInformation() (VolumeInformation, error)
	Mounted() error
}

type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    uint32
	ShareAccess       uint32
	CreateDisposition uint32
	CreateOptions     uint32
}

// File is the interface for files and directories.
type File interface {
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.DeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(fi *FileInfo)
	// CloseFile is called when closing a handle to the file
	CloseFile(fi *FileInfo)

	ReadFile(fi *FileInfo, bs []byte, offset int64) (int, error)
	WriteFile(fi *FileInfo, bs []byte, offset int64) (int, error)
	FlushFileBuffers(fi *FileInfo) error

	GetFileInformation(*FileInfo) (*Stat, error)
	FindFiles(*FileInfo, func(*NamedStat) error) error

	//FindFilesWithPattern

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	SetFileAttributes(fi *FileInfo, fileAttributes uint32) error
	SetEndOfFile(fi *FileInfo, length int64) error

	LockFile(fi *FileInfo, offset int64, length int64) error
	UnlockFile(fi *FileInfo, offset int64, length int64) error
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// FileAttributes holds the file attributes (see package syscall).
	FileAttributes uint32
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// FileSize is the size of the file in bytes
	FileSize int64
	// NumberOfLinks should typically be 1
	NumberOfLinks uint32
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a dos-name insert the corresponding
// dos-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtError is a type of errors for NTSTATUS values
type NtError uint32

func (n NtError) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtError(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtError(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtError(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtError(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtError(0xC00000BB)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtError(0xC000003A)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtError(0x40000000)
)
<commit_msg>Add comment for CreateData<commit_after>// Copyright 2015 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package dokan

import (
	"strconv"
	"time"
)

// FileSystem is the inteface for filesystems in Dokan.
type FileSystem interface {
	CreateFile(fi *FileInfo, data *CreateData) (file File, isDirectory bool, err error)
	GetDiskFreeSpace() (FreeSpace, error)

	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
	// can be deleted. The actual deletion should be done by checking
	// FileInfo.DeleteOnClose in Cleanup.
	CanDeleteFile(*FileInfo) error
	CanDeleteDirectory(*FileInfo) error
	MoveFile(source *FileInfo, targetPath string, replaceExisting bool) error

	GetVolumeInformation() (VolumeInformation, error)
	Mounted() error
}

// CreateData contains all the info needed to create a file.
type CreateData struct {
	DesiredAccess     uint32
	FileAttributes    uint32
	ShareAccess       uint32
	CreateDisposition uint32
	CreateOptions     uint32
}

// File is the interface for files and directories.
type File interface {
	// Cleanup is called after the last handle from userspace is closed.
	// Cleanup must perform actual deletions marked from CanDelete*
	// by checking FileInfo.DeleteOnClose if the filesystem supports
	// deletions.
	Cleanup(fi *FileInfo)
	// CloseFile is called when closing a handle to the file
	CloseFile(fi *FileInfo)

	ReadFile(fi *FileInfo, bs []byte, offset int64) (int, error)
	WriteFile(fi *FileInfo, bs []byte, offset int64) (int, error)
	FlushFileBuffers(fi *FileInfo) error

	GetFileInformation(*FileInfo) (*Stat, error)
	FindFiles(*FileInfo, func(*NamedStat) error) error

	//FindFilesWithPattern

	// SetFileTime sets the file time. Test times with .IsZero
	// whether they should be set.
	SetFileTime(fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
	SetFileAttributes(fi *FileInfo, fileAttributes uint32) error
	SetEndOfFile(fi *FileInfo, length int64) error

	LockFile(fi *FileInfo, offset int64, length int64) error
	UnlockFile(fi *FileInfo, offset int64, length int64) error
}

// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
type FreeSpace struct {
	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
}

// VolumeInformation - see WINAPI GetVolumeInformation for hints
type VolumeInformation struct {
	VolumeName             string
	VolumeSerialNumber     uint32
	MaximumComponentLength uint32
	FileSystemFlags        FileSystemFlags
	FileSystemName         string
}

// FileSystemFlags holds flags for filesystem features.
type FileSystemFlags uint32

// Various FileSystemFlags constants, see winapi documentation for details.
const (
	FileCasePreservedNames         = FileSystemFlags(0x2)
	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
	FileFileCompression            = FileSystemFlags(0x10)
	FileNamedStreams               = FileSystemFlags(0x40000)
	FilePersistentAcls             = FileSystemFlags(0x8)
	FileReadOnlyVolume             = FileSystemFlags(0x80000)
	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
	FileSupportsEncryption         = FileSystemFlags(0x20000)
	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
	FileSupportsHardLinks          = FileSystemFlags(0x400000)
	FileSupportObjectIDs           = FileSystemFlags(0x10000)
	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
	FileSupportsReparsePoints      = FileSystemFlags(0x80)
	FileSupportsSparseFiles        = FileSystemFlags(0x40)
	FileSupportsTransactions       = FileSystemFlags(0x200000)
	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
	FileUnicodeOnDisk              = FileSystemFlags(0x4)
	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
	FileVolumeQuotas               = FileSystemFlags(0x20)
)

// Stat is for GetFileInformation and friends.
type Stat struct {
	// FileAttributes holds the file attributes (see package syscall).
	FileAttributes uint32
	// Timestamps for the file
	Creation, LastAccess, LastWrite time.Time
	// VolumeSerialNumber is the serial number of the volume (0 is fine)
	VolumeSerialNumber uint32
	// FileSize is the size of the file in bytes
	FileSize int64
	// NumberOfLinks should typically be 1
	NumberOfLinks uint32
	// FileIndex is a 64 bit (nearly) unique ID of the file
	FileIndex uint64
	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
	ReparsePointTag uint32
}

// NamedStat is used to for stat responses that require file names.
// If the name is longer than a dos-name insert the corresponding
// dos-name to ShortName.
type NamedStat struct {
	Name      string
	ShortName string
	Stat
}

// NtError is a type of errors for NTSTATUS values
type NtError uint32

func (n NtError) Error() string {
	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
}

const (
	// ErrAccessDenied - access denied (EPERM)
	ErrAccessDenied = NtError(0xC0000022)
	// ErrObjectNameNotFound - filename does not exist (ENOENT)
	ErrObjectNameNotFound = NtError(0xC0000034)
	// ErrObjectNameCollision - a pathname already exists (EEXIST)
	ErrObjectNameCollision = NtError(0xC0000035)
	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
	ErrObjectPathNotFound = NtError(0xC000003A)
	// ErrNotSupported - not supported.
	ErrNotSupported = NtError(0xC00000BB)
	// ErrFileAlreadyExists - file already exists - fatal.
	ErrFileAlreadyExists = NtError(0xC000003A)
	// StatusObjectNameExists - already exists, may be non-fatal...
	StatusObjectNameExists = NtError(0x40000000)
)
<|endoftext|>