<commit_before>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"sync"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/terminal/pty"
)

type Terminal struct {
	InputHook    func()
	Log          kite.Logger
	screenrcPath string

	Users      map[string]*User
	sync.Mutex // protects Users
}

func New(log kite.Logger, screenPath string) *Terminal {
	return &Terminal{
		Users:        make(map[string]*User),
		screenrcPath: screenPath,
		Log:          log,
	}
}

func (t *Terminal) HasLimit(username string) bool {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		// not available yet so it's good to go
		return false
	}

	return user.HasLimit()
}

// AddUserSession adds the given username and session
func (t *Terminal) AddUserSession(username, session string, server *Server) {
	t.Lock()
	defer t.Unlock()

	// check if it's exists, if not go and lazy initialize a new user instance
	var user *User
	var ok bool
	user, ok = t.Users[username]
	if !ok {
		user = NewUser(username)
	}

	user.AddSession(session, server)
	t.Users[username] = user
}

// DeleteUserSession deletes the given session from the Users map
func (t *Terminal) DeleteUserSession(username, session string) {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		return // nothing to do
	}

	user.DeleteSession(session)

	if len(user.Sessions) == 0 {
		delete(t.Users, username)
	}
}

// RenameUserSession renames the given users session in the Users map
func (t *Terminal) RenameUserSession(username, oldName, newName string) error {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		return errors.New("username not available")
	}

	user.RenameSession(oldName, newName)
	t.Users[username] = user

	return nil
}

// KillSession kills the given screen session
func (t *Terminal) KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	t.DeleteUserSession(r.Username, params.Session)

	return true, nil
}

// KillSessions kills all available screen sessions
func (t *Terminal) KillSessions(r *kite.Request) (interface{}, error) {
	if err := killSessions(r.Username); err != nil {
		return nil, err
	}

	return true, nil
}

// RenameSession renames the given session to the new session name
func (t *Terminal) RenameSession(r *kite.Request) (interface{}, error) {
	var params struct {
		OldName string `json:"oldName"`
		NewName string `json:"newName"`
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ oldName: [string] newName: [string] }")
	}

	if params.OldName == "" {
		return nil, errors.New("session name empty")
	}

	if params.NewName == "" {
		return nil, errors.New("session name to be renamed is empty")
	}

	// prevent to rename it to a session that exists already
	if sessionExists(params.NewName, r.Username) {
		return nil, ErrNoSession
	}

	if err := renameSession(params.OldName, params.NewName); err != nil {
		return nil, err
	}

	t.RenameUserSession(r.Username, params.OldName, params.NewName)

	return true, nil
}

// GetSessions return a list of curren active screen sessions
func (t *Terminal) GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

// Connect creates and open a new TTY instance. It returns a *Server instance
// so every caller can send and receive from the connected TTY end.
func (t *Terminal) Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	if params.Mode == "create" && t.HasLimit(r.Username) {
		return nil, errors.New("session limit has reached")
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// get pty and tty descriptors
	p, err := pty.NewPTY()
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session:   command.Session,
		remote:    params.Remote,
		pty:       p,
		inputHook: t.InputHook,
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	t.AddUserSession(r.Username, command.Session, server)

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	// check also if klient was started in root mode or not.
	var args []string
	if os.Geteuid() == 0 {
		args = []string{"-i", command.Name}
	} else {
		args = []string{"-i", "-u", "#" + user.Uid, "--", command.Name}
	}

	// check if we have custom screenrc path and there is a file for it. If yes
	// use it for screen binary otherwise it'll just start without any screenrc.
	if t.screenrcPath != "" {
		if _, err := os.Stat(t.screenrcPath); err == nil {
			args = append(args, "-c", t.screenrcPath)
		}
	}

	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sudo is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()

		t.DeleteUserSession(r.Username, command.Session)
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (4096)-utf8.UTFMax, 4096)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			if server.throttling {
				s := time.Now().Unix()
				if server.currentSecond != s {
					server.currentSecond = s
					server.messageCounter = 0
					server.byteCounter = 0
					server.lineFeeedCounter = 0
				}
				server.messageCounter += 1
				server.byteCounter += n
				server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
				if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
					time.Sleep(time.Second)
				}
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// CloseSessions closes all active session for the given username and deletes
// it from the internal user map
func (t *Terminal) CloseSessions(username string) {
	t.Log.Info("Closing terminal sessions of user '%s'", username)

	t.Lock()
	user, ok := t.Users[username]
	t.Unlock()

	if !ok {
		return
	}

	user.CloseSessions()

	t.Lock()
	delete(t.Users, username)
	t.Unlock()
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}
<commit_msg>terminal: use process owner, instead of the requester<commit_after>// Package terminal provides a tty emulation and session handlings that is
// supported via Screen
package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/user"
	"sync"
	"syscall"
	"time"
	"unicode/utf8"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/terminal/pty"
)

type Terminal struct {
	InputHook    func()
	Log          kite.Logger
	screenrcPath string

	Users      map[string]*User
	sync.Mutex // protects Users
}

func New(log kite.Logger, screenPath string) *Terminal {
	return &Terminal{
		Users:        make(map[string]*User),
		screenrcPath: screenPath,
		Log:          log,
	}
}

func (t *Terminal) HasLimit(username string) bool {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		// not available yet so it's good to go
		return false
	}

	return user.HasLimit()
}

// AddUserSession adds the given username and session
func (t *Terminal) AddUserSession(username, session string, server *Server) {
	t.Lock()
	defer t.Unlock()

	// check if it's exists, if not go and lazy initialize a new user instance
	var user *User
	var ok bool
	user, ok = t.Users[username]
	if !ok {
		user = NewUser(username)
	}

	user.AddSession(session, server)
	t.Users[username] = user
}

// DeleteUserSession deletes the given session from the Users map
func (t *Terminal) DeleteUserSession(username, session string) {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		return // nothing to do
	}

	user.DeleteSession(session)

	if len(user.Sessions) == 0 {
		delete(t.Users, username)
	}
}

// RenameUserSession renames the given users session in the Users map
func (t *Terminal) RenameUserSession(username, oldName, newName string) error {
	t.Lock()
	defer t.Unlock()

	user, ok := t.Users[username]
	if !ok {
		return errors.New("username not available")
	}

	user.RenameSession(oldName, newName)
	t.Users[username] = user

	return nil
}

// KillSession kills the given screen session
func (t *Terminal) KillSession(r *kite.Request) (interface{}, error) {
	var params struct {
		Session string
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ session: [string] }")
	}

	if params.Session == "" {
		return nil, errors.New("session is empty")
	}

	if err := killSession(params.Session); err != nil {
		return nil, err
	}

	t.DeleteUserSession(r.Username, params.Session)

	return true, nil
}

// KillSessions kills all available screen sessions
func (t *Terminal) KillSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	if err := killSessions(user.Username); err != nil {
		return nil, err
	}

	return true, nil
}

// RenameSession renames the given session to the new session name
func (t *Terminal) RenameSession(r *kite.Request) (interface{}, error) {
	var params struct {
		OldName string `json:"oldName"`
		NewName string `json:"newName"`
	}

	if r.Args.One().Unmarshal(&params) != nil {
		return nil, errors.New("{ oldName: [string] newName: [string] }")
	}

	if params.OldName == "" {
		return nil, errors.New("session name empty")
	}

	if params.NewName == "" {
		return nil, errors.New("session name to be renamed is empty")
	}

	// prevent to rename it to a session that exists already
	if sessionExists(params.NewName, r.Username) {
		return nil, ErrNoSession
	}

	if err := renameSession(params.OldName, params.NewName); err != nil {
		return nil, err
	}

	t.RenameUserSession(r.Username, params.OldName, params.NewName)

	return true, nil
}

// GetSessions return a list of curren active screen sessions
func (t *Terminal) GetSessions(r *kite.Request) (interface{}, error) {
	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	sessions := screenSessions(user.Username)
	if len(sessions) == 0 {
		return nil, errors.New("no sessions available")
	}

	return sessions, nil
}

// Connect creates and open a new TTY instance. It returns a *Server instance
// so every caller can send and receive from the connected TTY end.
func (t *Terminal) Connect(r *kite.Request) (interface{}, error) {
	var params struct {
		Remote       Remote
		Session      string
		SizeX, SizeY int
		Mode         string
	}

	if err := r.Args.One().Unmarshal(&params); err != nil {
		return nil, fmt.Errorf("{ remote: [object], session: %s, noScreen: [bool] }, err: %s",
			params.Session, err)
	}

	if params.SizeX <= 0 || params.SizeY <= 0 {
		return nil, fmt.Errorf("{ sizeX: %d, sizeY: %d } { raw JSON : %v }", params.SizeX, params.SizeY, r.Args.One())
	}

	user, err := user.Current()
	if err != nil {
		return nil, fmt.Errorf("Could not get home dir: %s", err)
	}

	if params.Mode == "create" && t.HasLimit(r.Username) {
		return nil, errors.New("session limit has reached")
	}

	command, err := newCommand(params.Mode, params.Session, user.Username)
	if err != nil {
		return nil, err
	}

	// get pty and tty descriptors
	p, err := pty.NewPTY()
	if err != nil {
		return nil, err
	}

	// We will return this object to the client.
	server := &Server{
		Session:   command.Session,
		remote:    params.Remote,
		pty:       p,
		inputHook: t.InputHook,
	}
	server.setSize(float64(params.SizeX), float64(params.SizeY))

	t.AddUserSession(r.Username, command.Session, server)

	// wrap the command with sudo -i for initiation login shell. This is needed
	// in order to have Environments and other to be initialized correctly.
	// check also if klient was started in root mode or not.
	var args []string
	if os.Geteuid() == 0 {
		args = []string{"-i", command.Name}
	} else {
		args = []string{"-i", "-u", "#" + user.Uid, "--", command.Name}
	}

	// check if we have custom screenrc path and there is a file for it. If yes
	// use it for screen binary otherwise it'll just start without any screenrc.
	if t.screenrcPath != "" {
		if _, err := os.Stat(t.screenrcPath); err == nil {
			args = append(args, "-c", t.screenrcPath)
		}
	}

	args = append(args, command.Args...)
	cmd := exec.Command("/usr/bin/sudo", args...)

	// For test use this, sudo is not going to work
	// cmd := exec.Command(command.Name, command.Args...)

	cmd.Env = []string{"TERM=xterm-256color", "HOME=" + user.HomeDir}
	cmd.Stdin = server.pty.Slave
	cmd.Stdout = server.pty.Slave
	cmd.Dir = user.HomeDir
	// cmd.Stderr = server.pty.Slave

	// Open in background, this is needed otherwise the process will be killed
	// if you hit close on the client side.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
	err = cmd.Start()
	if err != nil {
		fmt.Println("could not start", err)
	}

	// Wait until the shell process is closed and notify the client.
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println("cmd.wait err", err)
		}

		server.pty.Slave.Close()
		server.pty.Master.Close()
		server.remote.SessionEnded.Call()

		t.DeleteUserSession(r.Username, command.Session)
	}()

	// Read the STDOUT from shell process and send to the connected client.
	go func() {
		buf := make([]byte, (4096)-utf8.UTFMax, 4096)
		for {
			n, err := server.pty.Master.Read(buf)
			for n < cap(buf)-1 {
				r, _ := utf8.DecodeLastRune(buf[:n])
				if r != utf8.RuneError {
					break
				}
				server.pty.Master.Read(buf[n : n+1])
				n++
			}

			// Rate limiting...
			if server.throttling {
				s := time.Now().Unix()
				if server.currentSecond != s {
					server.currentSecond = s
					server.messageCounter = 0
					server.byteCounter = 0
					server.lineFeeedCounter = 0
				}
				server.messageCounter += 1
				server.byteCounter += n
				server.lineFeeedCounter += bytes.Count(buf[:n], []byte{'\n'})
				if server.messageCounter > 100 || server.byteCounter > 1<<18 || server.lineFeeedCounter > 300 {
					time.Sleep(time.Second)
				}
			}

			server.remote.Output.Call(string(filterInvalidUTF8(buf[:n])))
			if err != nil {
				break
			}
		}
	}()

	return server, nil
}

// CloseSessions closes all active session for the given username and deletes
// it from the internal user map
func (t *Terminal) CloseSessions(username string) {
	t.Log.Info("Closing terminal sessions of user '%s'", username)

	t.Lock()
	user, ok := t.Users[username]
	t.Unlock()

	if !ok {
		return
	}

	user.CloseSessions()

	t.Lock()
	delete(t.Users, username)
	t.Unlock()
}

func filterInvalidUTF8(buf []byte) []byte {
	i := 0
	j := 0
	for {
		r, l := utf8.DecodeRune(buf[i:])
		if l == 0 {
			break
		}
		if r < 0xD800 {
			if i != j {
				copy(buf[j:], buf[i:i+l])
			}
			j += l
		}
		i += l
	}
	return buf[:j]
}
<|endoftext|>


<commit_before>// Copyright 2017-2018 Authors of Cilium
//
// 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 helpers

import (
	"bytes"
	"fmt"
	"os"
	"time"

	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
)

var (
	// HelperTimeout is a predefined timeout value for commands.
	HelperTimeout time.Duration = 300 // WithTimeout helper translates it to seconds

	// BasePath is the path in the Vagrant VMs to which the test directory
	// is mounted
	BasePath = "/home/vagrant/go/src/github.com/cilium/cilium/test"
	// CheckLogs newtes a new buffer where all the warnings and checks that
	// happens during the test are saved. This buffer will be printed in the
	// test output inside <checks> labels.
	CheckLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

const (

	//CiliumPath is the path where cilium test code is located.
	CiliumPath = "/src/github.com/cilium/cilium/test"

	// K8sManifestBase tells ginkgo suite where to look for manifests
	K8sManifestBase = "k8sT/manifests"

	// VM / Test suite constants.
	K8s     = "k8s"
	K8s1    = "k8s1"
	K8s1Ip  = "192.168.36.11"
	K8s2    = "k8s2"
	K8s2Ip  = "192.168.36.12"
	Runtime = "runtime"

	Enabled  = "enabled"
	Disabled = "disabled"
	Total    = "total"
	Public   = "public"
	Private  = "private"
	Name     = "Name"

	// PolicyEnforcement represents the PolicyEnforcement configuration option
	// for the Cilium agent.
	PolicyEnforcement = "PolicyEnforcement"

	// PolicyEnforcementDefault represents the default PolicyEnforcement mode
	// for Cilium.
	PolicyEnforcementDefault = "default"

	// PolicyEnforcementAlways represents the PolicyEnforcement mode
	// for Cilium in which traffic is denied by default even when no policy
	// is imported.
	PolicyEnforcementAlways = "always"

	// PolicyEnforcementNever represents the PolicyEnforcement mode
	// for Cilium in which traffic is always allowed even if there is a policy
	// selecting endpoints.
	PolicyEnforcementNever = "never"

	// Docker Image names

	// CiliumDockerNetwork is the name of the Docker network which Cilium manages.
	CiliumDockerNetwork = "cilium-net"

	// HostDockerNetwork is the name of the host network driver.
	HostDockerNetwork = "host"

	// NetperfImage is the Docker image used for performance testing
	NetperfImage = "tgraf/netperf"

	// HttpdImage is the image used for starting an HTTP server.
	HttpdImage = "cilium/demo-httpd"

	// Names of commonly used containers in tests.
	Httpd1 = "httpd1"
	Httpd2 = "httpd2"
	Httpd3 = "httpd3"
	App1   = "app1"
	App2   = "app2"
	App3   = "app3"
	Client = "client"
	Server = "server"
	Host   = "host"
	// Container lifecycle actions.
	Create = "create"
	Delete = "delete"

	// IP Address families.
	IPv4 = "IPv4"
	IPv6 = "IPv6"

	// LogPerm is the permission for files that are created by this framework
	// that contain logs, outputs of Cilium CLI commands, etc.
	LogPerm = os.FileMode(0666)

	// Configuration options for endpoints. Copied from endpoint/endpoint.go
	// TODO: these should be converted into types for use in configuration
	// functions instead of using basic strings.

	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrackLocal      = "ConntrackLocal"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionTraceNotify         = "TraceNotification"
	OptionNAT46               = "NAT46"
	OptionIngressPolicy       = "IngressPolicy"
	OptionEgressPolicy        = "EgressPolicy"
	OptionIngress             = "ingress"
	OptionEgress              = "egress"
	OptionNone                = "none"
	OptionDisabled            = "Disabled"
	OptionEnabled             = "Enabled"

	StateTerminating = "Terminating"
	StateRunning     = "Running"

	PingCount          = 5
	CurlConnectTimeout = 3

	DefaultNamespace    = "default"
	KubeSystemNamespace = "kube-system"

	TestResultsPath = "test_results/"
	RunDir          = "/var/run/cilium"
	LibDir          = "/var/lib/cilium"

	DaemonName             = "cilium"
	CiliumBugtool          = "cilium-bugtool"
	CiliumDockerDaemonName = "cilium-docker"
	AgentDaemon            = "cilium-agent"

	GeneratedHTMLManifest   = "html.yaml"
	GeneratedServerManifest = "server.yaml"
	GeneratedClientManifest = "client.yaml"

	KubectlCreate = ResourceLifeCycleAction("create")
	KubectlDelete = ResourceLifeCycleAction("delete")
	KubectlApply  = ResourceLifeCycleAction("apply")

	KubectlPolicyNameLabel      = k8sConst.PolicyLabelName
	KubectlPolicyNameSpaceLabel = k8sConst.PolicyLabelNamespace

	StableImage = "cilium/cilium:v1.0.4"
	configMap   = "ConfigMap"
	daemonSet   = "DaemonSet"

	MonitorLogFileName = "monitor.log"
	microscopeManifest = "microscope.yaml"

	// IPv4Host is an IP which is used in some datapath tests for simulating external IPv4 connectivity.
	IPv4Host = "192.168.254.254"

	// IPv6Host is an IP which is used in some datapath tests for simulating external IPv6 connectivity.
	IPv6Host = "fdff::ff"

	// Logs messages that should not be in the cilium logs.
	panicMessage      = "panic:"
	deadLockHeader    = "POTENTIAL DEADLOCK:"       // from github.com/sasha-s/go-deadlock/deadlock.go:header
	segmentationFault = "segmentation fault"        // from https://github.com/cilium/cilium/issues/3233
	NACKreceived      = "NACK received for version" // from https://github.com/cilium/cilium/issues/4003

	contextDeadlineExceeded = "context deadline exceeded"
	ErrorLogs               = "level=error"
	WarningLogs             = "level=warning"
)

// Re-definitions of stable constants in the API. The re-definition is on
// purpose to validate these values in the API. They may never change
const (
	// ReservedIdentityHealth is equivalent to pkg/identity.ReservedIdentityHealth
	ReservedIdentityHealth = 4
)

// CiliumDSPath is the default Cilium DaemonSet path to use in all test.
var CiliumDSPath = "cilium_ds.jsonnet"

var checkLogsMessages = []string{panicMessage, deadLockHeader, segmentationFault, NACKreceived}
var countLogsMessages = []string{contextDeadlineExceeded, ErrorLogs, WarningLogs}

var ciliumCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"sudo cilium bpf lb list":               "bpf_lb_list.txt",
	"sudo cilium bpf ct list global":        "bpf_ct_list.txt",
	"sudo cilium bpf tunnel list":           "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

// ciliumKubCLICommands these commands are the same as `ciliumCLICommands` but
// it'll run inside a container and it does not have sudo support
var ciliumKubCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"cilium bpf lb list":                    "bpf_lb_list.txt",
	"cilium bpf ct list global":             "bpf_ct_list.txt",
	"cilium bpf tunnel list":                "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

//GetFilePath returns the absolute path of the provided filename
func GetFilePath(filename string) string {
	return fmt.Sprintf("%s/%s", BasePath, filename)
}

// K8s1VMName is the name of the Kubernetes master node when running K8s tests.
func K8s1VMName() string {
	return fmt.Sprintf("k8s1-%s", GetCurrentK8SEnv())
}

// K8s2VMName is the name of the Kubernetes worker node when running K8s tests.
func K8s2VMName() string {
	return fmt.Sprintf("k8s2-%s", GetCurrentK8SEnv())
}
<commit_msg>Test: Validate on Fail, added test panicked.<commit_after>// Copyright 2017-2018 Authors of Cilium
//
// 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 helpers

import (
	"bytes"
	"fmt"
	"os"
	"time"

	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
	ginkgoext "github.com/cilium/cilium/test/ginkgo-ext"
)

var (
	// HelperTimeout is a predefined timeout value for commands.
	HelperTimeout time.Duration = 300 // WithTimeout helper translates it to seconds

	// BasePath is the path in the Vagrant VMs to which the test directory
	// is mounted
	BasePath = "/home/vagrant/go/src/github.com/cilium/cilium/test"
	// CheckLogs newtes a new buffer where all the warnings and checks that
	// happens during the test are saved. This buffer will be printed in the
	// test output inside <checks> labels.
	CheckLogs = ginkgoext.NewWriter(new(bytes.Buffer))
)

const (

	//CiliumPath is the path where cilium test code is located.
	CiliumPath = "/src/github.com/cilium/cilium/test"

	// K8sManifestBase tells ginkgo suite where to look for manifests
	K8sManifestBase = "k8sT/manifests"

	// VM / Test suite constants.
	K8s     = "k8s"
	K8s1    = "k8s1"
	K8s1Ip  = "192.168.36.11"
	K8s2    = "k8s2"
	K8s2Ip  = "192.168.36.12"
	Runtime = "runtime"

	Enabled  = "enabled"
	Disabled = "disabled"
	Total    = "total"
	Public   = "public"
	Private  = "private"
	Name     = "Name"

	// PolicyEnforcement represents the PolicyEnforcement configuration option
	// for the Cilium agent.
	PolicyEnforcement = "PolicyEnforcement"

	// PolicyEnforcementDefault represents the default PolicyEnforcement mode
	// for Cilium.
	PolicyEnforcementDefault = "default"

	// PolicyEnforcementAlways represents the PolicyEnforcement mode
	// for Cilium in which traffic is denied by default even when no policy
	// is imported.
	PolicyEnforcementAlways = "always"

	// PolicyEnforcementNever represents the PolicyEnforcement mode
	// for Cilium in which traffic is always allowed even if there is a policy
	// selecting endpoints.
	PolicyEnforcementNever = "never"

	// Docker Image names

	// CiliumDockerNetwork is the name of the Docker network which Cilium manages.
	CiliumDockerNetwork = "cilium-net"

	// HostDockerNetwork is the name of the host network driver.
	HostDockerNetwork = "host"

	// NetperfImage is the Docker image used for performance testing
	NetperfImage = "tgraf/netperf"

	// HttpdImage is the image used for starting an HTTP server.
	HttpdImage = "cilium/demo-httpd"

	// Names of commonly used containers in tests.
	Httpd1 = "httpd1"
	Httpd2 = "httpd2"
	Httpd3 = "httpd3"
	App1   = "app1"
	App2   = "app2"
	App3   = "app3"
	Client = "client"
	Server = "server"
	Host   = "host"
	// Container lifecycle actions.
	Create = "create"
	Delete = "delete"

	// IP Address families.
	IPv4 = "IPv4"
	IPv6 = "IPv6"

	// LogPerm is the permission for files that are created by this framework
	// that contain logs, outputs of Cilium CLI commands, etc.
	LogPerm = os.FileMode(0666)

	// Configuration options for endpoints. Copied from endpoint/endpoint.go
	// TODO: these should be converted into types for use in configuration
	// functions instead of using basic strings.

	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrackLocal      = "ConntrackLocal"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionTraceNotify         = "TraceNotification"
	OptionNAT46               = "NAT46"
	OptionIngressPolicy       = "IngressPolicy"
	OptionEgressPolicy        = "EgressPolicy"
	OptionIngress             = "ingress"
	OptionEgress              = "egress"
	OptionNone                = "none"
	OptionDisabled            = "Disabled"
	OptionEnabled             = "Enabled"

	StateTerminating = "Terminating"
	StateRunning     = "Running"

	PingCount          = 5
	CurlConnectTimeout = 3

	DefaultNamespace    = "default"
	KubeSystemNamespace = "kube-system"

	TestResultsPath = "test_results/"
	RunDir          = "/var/run/cilium"
	LibDir          = "/var/lib/cilium"

	DaemonName             = "cilium"
	CiliumBugtool          = "cilium-bugtool"
	CiliumDockerDaemonName = "cilium-docker"
	AgentDaemon            = "cilium-agent"

	GeneratedHTMLManifest   = "html.yaml"
	GeneratedServerManifest = "server.yaml"
	GeneratedClientManifest = "client.yaml"

	KubectlCreate = ResourceLifeCycleAction("create")
	KubectlDelete = ResourceLifeCycleAction("delete")
	KubectlApply  = ResourceLifeCycleAction("apply")

	KubectlPolicyNameLabel      = k8sConst.PolicyLabelName
	KubectlPolicyNameSpaceLabel = k8sConst.PolicyLabelNamespace

	StableImage = "cilium/cilium:v1.0.4"
	configMap   = "ConfigMap"
	daemonSet   = "DaemonSet"

	MonitorLogFileName = "monitor.log"
	microscopeManifest = "microscope.yaml"

	// IPv4Host is an IP which is used in some datapath tests for simulating external IPv4 connectivity.
	IPv4Host = "192.168.254.254"

	// IPv6Host is an IP which is used in some datapath tests for simulating external IPv6 connectivity.
	IPv6Host = "fdff::ff"

	// Logs messages that should not be in the cilium logs.
	panicMessage      = "panic:"
	deadLockHeader    = "POTENTIAL DEADLOCK:"       // from github.com/sasha-s/go-deadlock/deadlock.go:header
	segmentationFault = "segmentation fault"        // from https://github.com/cilium/cilium/issues/3233
	NACKreceived      = "NACK received for version" // from https://github.com/cilium/cilium/issues/4003

	contextDeadlineExceeded = "context deadline exceeded"
	ErrorLogs               = "level=error"
	WarningLogs             = "level=warning"
	APIPanicked             = "Cilium API handler panicked"
)

// Re-definitions of stable constants in the API. The re-definition is on
// purpose to validate these values in the API. They may never change
const (
	// ReservedIdentityHealth is equivalent to pkg/identity.ReservedIdentityHealth
	ReservedIdentityHealth = 4
)

// CiliumDSPath is the default Cilium DaemonSet path to use in all test.
var CiliumDSPath = "cilium_ds.jsonnet"

var checkLogsMessages = []string{panicMessage, deadLockHeader, segmentationFault, NACKreceived}
var countLogsMessages = []string{contextDeadlineExceeded, ErrorLogs, WarningLogs, APIPanicked}

var ciliumCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"sudo cilium bpf lb list":               "bpf_lb_list.txt",
	"sudo cilium bpf ct list global":        "bpf_ct_list.txt",
	"sudo cilium bpf tunnel list":           "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

// ciliumKubCLICommands these commands are the same as `ciliumCLICommands` but
// it'll run inside a container and it does not have sudo support
var ciliumKubCLICommands = map[string]string{
	"cilium endpoint list -o json":          "endpoint_list.txt",
	"cilium service list -o json":           "service_list.txt",
	"cilium config":                         "config.txt",
	"cilium bpf lb list":                    "bpf_lb_list.txt",
	"cilium bpf ct list global":             "bpf_ct_list.txt",
	"cilium bpf tunnel list":                "bpf_tunnel_list.txt",
	"cilium policy get":                     "policy_get.txt",
	"cilium status --all-controllers":       "status.txt",
	"cilium kvstore get cilium --recursive": "kvstore_get.txt",
}

//GetFilePath returns the absolute path of the provided filename
func GetFilePath(filename string) string {
	return fmt.Sprintf("%s/%s", BasePath, filename)
}

// K8s1VMName is the name of the Kubernetes master node when running K8s tests.
func K8s1VMName() string {
	return fmt.Sprintf("k8s1-%s", GetCurrentK8SEnv())
}

// K8s2VMName is the name of the Kubernetes worker node when running K8s tests.
func K8s2VMName() string {
	return fmt.Sprintf("k8s2-%s", GetCurrentK8SEnv())
}
<|endoftext|>


<commit_before>package dictionary

import (
	"errors"
	"net/url"
	"net/http"
	"encoding/json"
	"fmt"
)

// PearsonDictionary implements the Dictionary interface and is used to call the Pearson Dictionary API's
type PearsonDictionary struct {
	endpoint string
	activeDictionary string
	cachedResults []PearsonDefineWordContract
}

// NewPearsonDictionaryDefault creates a new dictionary using the Pearson api and the Longman Dictionary of Contemporary English (5th edition)
func NewPearsonDictionaryDefault() *PearsonDictionary {
	return &PearsonDictionary {
		endpoint: "http://api.pearson.com/v2/dictionaries",
		activeDictionary: "ldoce5",
	}
}

// NewPearsonDictionaryCustom creates a new dictionary using the provided endpoint and dictionary code while conforming to the Pearson API
func NewPearsonDictionaryCustom(baseEndpoint, dictionaryCode string) *PearsonDictionary {
	return &PearsonDictionary {
		endpoint: baseEndpoint,
		activeDictionary: dictionaryCode,
	}
}

// DefineWord gets all definitions of a word from the Pearson Dictionary API
func (p *PearsonDictionary) DefineWord(word string) ([]string, error) {
	var err error
	var response *http.Response
	var parsedResult PearsonDefineWordContract
	var definitions []string

	// Call the API
	pearsonURL, err := p.constructDefineWordURL(word)

	if err == nil {
		response, err = http.Get(pearsonURL)
	}

	if err == nil {
		if response.Body != nil {
			defer response.Body.Close()
		}

		if response.StatusCode != 200 {
			err = fmt.Errorf("Request failed with HTTP: %v", response.StatusCode)
		}
	}

	// Parse the result
	if err == nil {
		err = json.NewDecoder(response.Body).Decode(&parsedResult)
	}

	// Save the data
	if err == nil {
		p.cachedResults = append(p.cachedResults, parsedResult)
	}

	// Ensure results were returned
	if err == nil {
		if parsedResult.Count == 0 {
			err = errors.New("No results for given word")
		}
	}

	// Search for the word that matches the passed in word
	if err == nil {
		for _, result := range parsedResult.Results {
			if result.Headword == word {
				for _, sense := range result.Senses {
					definitions = sense.Definition
					break
				}
				break
			}
		}
	}
	// Return that result
	return definitions, err
}

func (p *PearsonDictionary) constructDefineWordURL(word string) (string, error) {
	var err error
	URL := fmt.Sprintf("%s/%s/entries", p.endpoint, p.activeDictionary)
	baseURL, err := url.Parse(URL)

	if err == nil {
		queryParams := url.Values{}
		queryParams.Add("headword", word)
		baseURL.RawQuery = queryParams.Encode()
	}

	return baseURL.String(), err
}<commit_msg>Now only constructing the URL once<commit_after>package dictionary

import (
	"errors"
	"net/url"
	"net/http"
	"encoding/json"
	"fmt"
)

// PearsonDictionary implements the Dictionary interface and is used to call the Pearson Dictionary API's
type PearsonDictionary struct {
	endpoint string
	activeDictionary string
	cachedResults []PearsonDefineWordContract
}

// NewPearsonDictionaryDefault creates a new dictionary using the Pearson api and the Longman Dictionary of Contemporary English (5th edition)
func NewPearsonDictionaryDefault() *PearsonDictionary {

	dict := &PearsonDictionary {
		activeDictionary: "ldoce5",
	}

	dict.endpoint = fmt.Sprintf("http://api.pearson.com/v2/dictionaries/%s/entries", dict.activeDictionary)

	return dict
}

// NewPearsonDictionaryCustom creates a new dictionary using the provided endpoint and dictionary code while conforming to the Pearson API
func NewPearsonDictionaryCustom(baseEndpoint, dictionaryCode string) *PearsonDictionary {
	dict := &PearsonDictionary {
		activeDictionary: dictionaryCode,
	}

	dict.endpoint = fmt.Sprintf("%s/%s/entries", baseEndpoint, dict.activeDictionary)

	return dict
}

// DefineWord gets all definitions of a word from the Pearson Dictionary API
func (p *PearsonDictionary) DefineWord(word string) ([]string, error) {
	var err error
	var response *http.Response
	var parsedResult PearsonDefineWordContract
	var definitions []string

	// Call the API
	pearsonURL, err := p.constructDefineWordURL(word)

	if err == nil {
		response, err = http.Get(pearsonURL)
	}

	if err == nil {
		if response.Body != nil {
			defer response.Body.Close()
		}

		if response.StatusCode != 200 {
			err = fmt.Errorf("Request failed with HTTP: %v", response.StatusCode)
		}
	}

	// Parse the result
	if err == nil {
		err = json.NewDecoder(response.Body).Decode(&parsedResult)
	}

	// Save the data
	if err == nil {
		p.cachedResults = append(p.cachedResults, parsedResult)
	}

	// Ensure results were returned
	if err == nil {
		if parsedResult.Count == 0 {
			err = errors.New("No results for given word")
		}
	}

	// Search for the word that matches the passed in word
	if err == nil {
		for _, result := range parsedResult.Results {
			if result.Headword == word {
				for _, sense := range result.Senses {
					definitions = sense.Definition
					break
				}
				break
			}
		}
	}
	// Return that result
	return definitions, err
}

func (p *PearsonDictionary) constructDefineWordURL(word string) (string, error) {
	var err error
	baseURL, err := url.Parse(p.endpoint)

	if err == nil {
		queryParams := url.Values{}
		queryParams.Add("headword", word)
		baseURL.RawQuery = queryParams.Encode()
	}

	return baseURL.String(), err
}<|endoftext|>


<commit_before>package plugin

import (
	"zvr/server"
	"zvr/utils"
	"fmt"
)

const(
	CREATE_IPSEC_CONNECTION = "/vyos/createipsecconnection"
	DELETE_IPSEC_CONNECTION = "/vyos/deleteipsecconnection"
	SYNC_IPSEC_CONNECTION = "/vyos/syncipsecconnection"
)

type ipsecInfo struct {
	Uuid string `json:"uuid"`
	LocalCidrs []string `json:"localCidrs"`
	PeerAddress string `json:"peerAddress"`
	AuthMode string `json:"authMode"`
	AuthKey string `json:"authKey"`
	Vip string `json:"vip"`
	IkeAuthAlgorithm string `json:"ikeAuthAlgorithm"`
	IkeEncryptionAlgorithm string `json:"ikeEncryptionAlgorithm"`
	IkeDhGroup int `json:"ikeDhGroup"`
	PolicyAuthAlgorithm string `json:"policyAuthAlgorithm"`
	PolicyEncryptionAlgorithm string `json:"policyEncryptionAlgorithm"`
	Pfs string `json:"pfs"`
	PolicyMode string `json:"policyMode"`
	TransformProtocol string `json:"transformProtocol"`
	PeerCidrs []string `json:"peerCidrs"`
	ExcludeSnat bool `json:"excludeSnat"`
}

type createIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

type deleteIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

type syncIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

func createIPsec(tree *server.VyosConfigTree, info ipsecInfo)  {
	nicname, err := utils.GetNicNameByIp(info.Vip); utils.PanicOnError(err)

	tree.Setf("vpn ipsec ipsec-interfaces interface %s", nicname)

	// create ike group
	tree.Setf("vpn ipsec ike-group %s proposal 1 dh-group %v", info.Uuid, info.IkeDhGroup)
	tree.Setf("vpn ipsec ike-group %s proposal 1 encryption %v", info.Uuid, info.IkeEncryptionAlgorithm)
	tree.Setf("vpn ipsec ike-group %s proposal 1 hash %v", info.Uuid, info.IkeAuthAlgorithm)

	// create esp group
	if info.Pfs == "" {
		tree.Setf("vpn ipsec esp-group %s pfs disable", info.Uuid)
	} else {
		tree.Setf("vpn ipsec esp-group %s pfs %s", info.Uuid, info.Pfs)
	}
	tree.Setf("vpn ipsec esp-group %s proposal 1 encryption %s", info.Uuid, info.PolicyEncryptionAlgorithm)
	tree.Setf("vpn ipsec esp-group %s proposal 1 hash %s", info.Uuid, info.PolicyAuthAlgorithm)
	tree.Setf("vpn ipsec esp-group %s mode %s", info.Uuid, info.PolicyMode)

	// create peer connection
	utils.Assertf(info.AuthMode == "psk", "vyos plugin only supports authMode 'psk', %s is not supported yet", info.AuthMode)
	tree.Setf("vpn ipsec site-to-site peer %s authentication mode pre-shared-secret", info.PeerAddress)
	tree.Setf("vpn ipsec site-to-site peer %s authentication pre-shared-secret %s", info.PeerAddress, info.AuthKey)
	tree.Setf("vpn ipsec site-to-site peer %s default-esp-group %s", info.PeerAddress, info.Uuid)
	tree.Setf("vpn ipsec site-to-site peer %s ike-group %s", info.PeerAddress, info.Uuid)

	tree.Setf("vpn ipsec site-to-site peer %s local-address %s", info.PeerAddress, info.Vip)
	utils.Assertf(len(info.LocalCidrs) == 1, "localCidrs%v containing more than one CIDR is not supported yet", info.LocalCidrs)
	localCidr := info.LocalCidrs[0]
	for i, remoteCidr := range info.PeerCidrs {
		tree.Setf("vpn ipsec site-to-site peer %v tunnel %v local prefix %v", info.PeerAddress, i+1, localCidr)
		tree.Setf("vpn ipsec site-to-site peer %v tunnel %v remote prefix %v", info.PeerAddress, i+1, remoteCidr)
	}

	// configure firewall
	des := "ipsec-500-udp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			"destination port 500",
			fmt.Sprintf("description %s", des),
			"protocol udp",
			"action accept",
		)
	}

	des = "ipsec-4500-udp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			"destination port 4500",
			fmt.Sprintf("description %s", des),
			"protocol udp",
			"action accept",
		)
	}

	des = "ipsec-esp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			fmt.Sprintf("description %s", des),
			"protocol esp",
			"action accept",
		)
	}

	des = "ipsec-ah"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			fmt.Sprintf("description %s", des),
			"protocol ah",
			"action accept",
		)
	}

	tree.AttachFirewallToInterface(nicname, "local")

	if info.ExcludeSnat {
		for _, remoteCidr := range info.PeerCidrs {
			des = fmt.Sprintf("ipsec-%s-%s-%s", info.Uuid, localCidr, remoteCidr)
			if r := tree.FindSnatRuleDescription(des); r == nil {
				tree.SetSnat(
					fmt.Sprintf("destination address %v", remoteCidr),
					fmt.Sprintf("source address %v", localCidr),
					fmt.Sprintf("outbound-interface %v", nicname),
					fmt.Sprintf("description %v", des),
					"exclude",
				)
			}
		}
	}
}

func createIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &createIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree
	for _, info := range cmd.Infos {
		createIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func syncIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &syncIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree

	tree.Delete("vpn ipsec")

	for _, info := range cmd.Infos {
		createIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func deleteIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &deleteIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree
	for _, info := range cmd.Infos {
		deleteIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func deleteIPsec(tree *server.VyosConfigTree, info ipsecInfo) {
	nicname, err := utils.GetNicNameByIp(info.Vip); utils.PanicOnError(err)

	tree.Deletef("vpn ipsec ike-group %s", info.Uuid)
	tree.Deletef("vpn ipsec esp-group %s", info.Uuid)
	tree.Deletef("vpn ipsec site-to-site peer %s", info.PeerAddress)

	if info.ExcludeSnat {
		utils.Assertf(len(info.LocalCidrs) == 1, "localCidrs%v containing more than one CIDR is not supported yet", info.LocalCidrs)
		localCidr := info.LocalCidrs[0]

		for _, remoteCidr := range info.PeerCidrs {
			des := fmt.Sprintf("ipsec-%s-%s-%s", info.Uuid, localCidr, remoteCidr)
			if r := tree.FindSnatRuleDescription(des); r != nil {
				r.Delete()
			}
		}
	}

	ipsec := tree.Get("vpn ipsec site-to-site peer")
	if ipsec == nil || ipsec.Size() == 0 {
		// no any ipsec connection

		// delete ipsec related rules
		tree.Delete("vpn ipsec")

		// delete firewall
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-500-udp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-4500-udp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-esp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-ah"); r != nil {
			r.Delete()
		}
	}
}

func IPsecEntryPoint() {
	server.RegisterAsyncCommandHandler(CREATE_IPSEC_CONNECTION, server.VyosLock(createIPsecConnection))
	server.RegisterAsyncCommandHandler(DELETE_IPSEC_CONNECTION, server.VyosLock(deleteIPsecConnection))
	server.RegisterAsyncCommandHandler(SYNC_IPSEC_CONNECTION, server.VyosLock(syncIPsecConnection))
}
<commit_msg>[VyOS] attach EIP but cannot download anything in VM<commit_after>package plugin

import (
	"zvr/server"
	"zvr/utils"
	"fmt"
)

const(
	CREATE_IPSEC_CONNECTION = "/vyos/createipsecconnection"
	DELETE_IPSEC_CONNECTION = "/vyos/deleteipsecconnection"
	SYNC_IPSEC_CONNECTION = "/vyos/syncipsecconnection"
)

type ipsecInfo struct {
	Uuid string `json:"uuid"`
	LocalCidrs []string `json:"localCidrs"`
	PeerAddress string `json:"peerAddress"`
	AuthMode string `json:"authMode"`
	AuthKey string `json:"authKey"`
	Vip string `json:"vip"`
	IkeAuthAlgorithm string `json:"ikeAuthAlgorithm"`
	IkeEncryptionAlgorithm string `json:"ikeEncryptionAlgorithm"`
	IkeDhGroup int `json:"ikeDhGroup"`
	PolicyAuthAlgorithm string `json:"policyAuthAlgorithm"`
	PolicyEncryptionAlgorithm string `json:"policyEncryptionAlgorithm"`
	Pfs string `json:"pfs"`
	PolicyMode string `json:"policyMode"`
	TransformProtocol string `json:"transformProtocol"`
	PeerCidrs []string `json:"peerCidrs"`
	ExcludeSnat bool `json:"excludeSnat"`
}

type createIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

type deleteIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

type syncIPsecCmd struct {
	Infos []ipsecInfo `json:"infos"`
}

func createIPsec(tree *server.VyosConfigTree, info ipsecInfo)  {
	nicname, err := utils.GetNicNameByIp(info.Vip); utils.PanicOnError(err)

	tree.Setf("vpn ipsec ipsec-interfaces interface %s", nicname)

	// create ike group
	tree.Setf("vpn ipsec ike-group %s proposal 1 dh-group %v", info.Uuid, info.IkeDhGroup)
	tree.Setf("vpn ipsec ike-group %s proposal 1 encryption %v", info.Uuid, info.IkeEncryptionAlgorithm)
	tree.Setf("vpn ipsec ike-group %s proposal 1 hash %v", info.Uuid, info.IkeAuthAlgorithm)

	// create esp group
	if info.Pfs == "" {
		tree.Setf("vpn ipsec esp-group %s pfs disable", info.Uuid)
	} else {
		tree.Setf("vpn ipsec esp-group %s pfs %s", info.Uuid, info.Pfs)
	}
	tree.Setf("vpn ipsec esp-group %s proposal 1 encryption %s", info.Uuid, info.PolicyEncryptionAlgorithm)
	tree.Setf("vpn ipsec esp-group %s proposal 1 hash %s", info.Uuid, info.PolicyAuthAlgorithm)
	tree.Setf("vpn ipsec esp-group %s mode %s", info.Uuid, info.PolicyMode)

	// create peer connection
	utils.Assertf(info.AuthMode == "psk", "vyos plugin only supports authMode 'psk', %s is not supported yet", info.AuthMode)
	tree.Setf("vpn ipsec site-to-site peer %s authentication mode pre-shared-secret", info.PeerAddress)
	tree.Setf("vpn ipsec site-to-site peer %s authentication pre-shared-secret %s", info.PeerAddress, info.AuthKey)
	tree.Setf("vpn ipsec site-to-site peer %s default-esp-group %s", info.PeerAddress, info.Uuid)
	tree.Setf("vpn ipsec site-to-site peer %s ike-group %s", info.PeerAddress, info.Uuid)

	tree.Setf("vpn ipsec site-to-site peer %s local-address %s", info.PeerAddress, info.Vip)
	utils.Assertf(len(info.LocalCidrs) == 1, "localCidrs%v containing more than one CIDR is not supported yet", info.LocalCidrs)
	localCidr := info.LocalCidrs[0]
	for i, remoteCidr := range info.PeerCidrs {
		tree.Setf("vpn ipsec site-to-site peer %v tunnel %v local prefix %v", info.PeerAddress, i+1, localCidr)
		tree.Setf("vpn ipsec site-to-site peer %v tunnel %v remote prefix %v", info.PeerAddress, i+1, remoteCidr)
	}

	// configure firewall
	des := "ipsec-500-udp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			"destination port 500",
			fmt.Sprintf("description %s", des),
			"protocol udp",
			"action accept",
		)
	}

	des = "ipsec-4500-udp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			"destination port 4500",
			fmt.Sprintf("description %s", des),
			"protocol udp",
			"action accept",
		)
	}

	des = "ipsec-esp"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			fmt.Sprintf("description %s", des),
			"protocol esp",
			"action accept",
		)
	}

	des = "ipsec-ah"
	if r := tree.FindFirewallRuleByDescription(nicname, "local", des); r == nil {
		tree.SetFirewallOnInterface(nicname, "local",
			fmt.Sprintf("description %s", des),
			"protocol ah",
			"action accept",
		)
	}

	for _, cidr := range info.PeerCidrs {
		des = fmt.Sprintf("IPSEC-%s-%s", info.Uuid, cidr)
		if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r == nil {
			tree.SetFirewallOnInterface(nicname, "in",
				"action accept",
				"state established enable",
				"state related enable",
				"state new enable",
				fmt.Sprintf("description %v", des),
				fmt.Sprintf("source address %v", cidr),
			)
		}
	}

	tree.AttachFirewallToInterface(nicname, "local")
	tree.AttachFirewallToInterface(nicname, "in")

	if info.ExcludeSnat {
		for _, remoteCidr := range info.PeerCidrs {
			des = fmt.Sprintf("ipsec-%s-%s-%s", info.Uuid, localCidr, remoteCidr)
			if r := tree.FindSnatRuleDescription(des); r == nil {
				tree.SetSnat(
					fmt.Sprintf("destination address %v", remoteCidr),
					fmt.Sprintf("source address %v", localCidr),
					fmt.Sprintf("outbound-interface %v", nicname),
					fmt.Sprintf("description %v", des),
					"exclude",
				)
			}
		}
	}
}

func createIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &createIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree
	for _, info := range cmd.Infos {
		createIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func syncIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &syncIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree

	tree.Delete("vpn ipsec")

	for _, info := range cmd.Infos {
		createIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func deleteIPsecConnection(ctx *server.CommandContext) interface{} {
	cmd := &deleteIPsecCmd{}
	ctx.GetCommand(cmd)

	vyos := server.NewParserFromShowConfiguration()
	tree := vyos.Tree
	for _, info := range cmd.Infos {
		deleteIPsec(tree, info)
	}
	tree.Apply(false)

	return nil
}

func deleteIPsec(tree *server.VyosConfigTree, info ipsecInfo) {
	nicname, err := utils.GetNicNameByIp(info.Vip); utils.PanicOnError(err)

	tree.Deletef("vpn ipsec ike-group %s", info.Uuid)
	tree.Deletef("vpn ipsec esp-group %s", info.Uuid)
	tree.Deletef("vpn ipsec site-to-site peer %s", info.PeerAddress)

	if info.ExcludeSnat {
		utils.Assertf(len(info.LocalCidrs) == 1, "localCidrs%v containing more than one CIDR is not supported yet", info.LocalCidrs)
		localCidr := info.LocalCidrs[0]

		for _, remoteCidr := range info.PeerCidrs {
			des := fmt.Sprintf("ipsec-%s-%s-%s", info.Uuid, localCidr, remoteCidr)
			if r := tree.FindSnatRuleDescription(des); r != nil {
				r.Delete()
			}
		}
	}

	for _, cidr := range info.PeerCidrs {
		des := fmt.Sprintf("IPSEC-%s-%s", info.Uuid, cidr)
		if r := tree.FindFirewallRuleByDescription(nicname, "in", des); r != nil {
			r.Delete()
		}
	}

	ipsec := tree.Get("vpn ipsec site-to-site peer")
	if ipsec == nil || ipsec.Size() == 0 {
		// no any ipsec connection

		// delete ipsec related rules
		tree.Delete("vpn ipsec")

		// delete firewall
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-500-udp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-4500-udp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-esp"); r != nil {
			r.Delete()
		}
		if r := tree.FindFirewallRuleByDescription(nicname, "local", "ipsec-ah"); r != nil {
			r.Delete()
		}
	}
}

func IPsecEntryPoint() {
	server.RegisterAsyncCommandHandler(CREATE_IPSEC_CONNECTION, server.VyosLock(createIPsecConnection))
	server.RegisterAsyncCommandHandler(DELETE_IPSEC_CONNECTION, server.VyosLock(deleteIPsecConnection))
	server.RegisterAsyncCommandHandler(SYNC_IPSEC_CONNECTION, server.VyosLock(syncIPsecConnection))
}
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 state_test

import (
	"github.com/jacobsa/comeback/state"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestScoreMap(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ScoreMapTest struct {
	m state.ScoreMap
}

func init() { RegisterTestSuite(&ScoreMapTest{}) }

func (t *ScoreMapTest) SetUp(i *TestInfo) {
	t.m = state.NewScoreMap()
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ScoreMapTest) EmptyMap() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) SomeElements() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) AddTwice() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) GobRoundTrip() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) DecodingOverwritesContents() {
	ExpectEq("TODO", "")
}
<commit_msg>ScoreMapTest.SomeElements<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 state_test

import (
	"github.com/jacobsa/comeback/state"
	. "github.com/jacobsa/ogletest"
	"testing"
)

func TestScoreMap(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type ScoreMapTest struct {
	m state.ScoreMap
}

func init() { RegisterTestSuite(&ScoreMapTest{}) }

func (t *ScoreMapTest) SetUp(i *TestInfo) {
	t.m = state.NewScoreMap()
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *ScoreMapTest) EmptyMap() {
	var key state.ScoreMapKey

	key = state.ScoreMapKey{Path: ""}
	ExpectEq(nil, t.m.Get(key))

	key = state.ScoreMapKey{Path: "taco"}
	ExpectEq(nil, t.m.Get(key))

	key = state.ScoreMapKey{Path: "burrito"}
	ExpectEq(nil, t.m.Get(key))
}

func (t *ScoreMapTest) SomeElements() {
	// Add taco
	tacoKey := state.ScoreMapKey{
		Path: "taco",
		Size: 17,
	}

	tacoScores := []blob.Score{
		blob.ComputeScore("foo"),
		blob.ComputeScore("bar"),
	}

	t.m.Add(tacoKey, tacoScores)

	// Add burrito
	burritoKey := tacoKey
	burritoKey.Path = "burrito"

	burritoScores := []blob.Score{
		blob.ComputeScore("baz"),
	}

	t.m.Add(burritoKey, burritoScores)

	// Look up
	ExpectThat(t.m.Get(tacoKey), DeepEquals(tacoScores))
	ExpectThat(t.m.Get(burritoKey), DeepEquals(burritoScores))
	ExpectEq(nil, t.m.Get(state.ScoreMapKey{}))
}

func (t *ScoreMapTest) AddTwice() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) GobRoundTrip() {
	ExpectEq("TODO", "")
}

func (t *ScoreMapTest) DecodingOverwritesContents() {
	ExpectEq("TODO", "")
}
<|endoftext|>


<commit_before>package driver

import (
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"time"
)

func publicKeyMarshal(value interface{}) (data []byte, err error) {
	pemStr, err := publicKeyToPem(value)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	return pemStr.([]byte), nil
}

// data を PEM 形式の文字列として、rsa.PublicKey にデコードする。
func publicKeyUnmarshal(data []byte) (interface{}, error) {
	pubKey, err := util.ParseRsaPublicKey(string(data))
	if err != nil {
		return nil, erro.Wrap(err)
	}
	return pubKey, nil
}

func pemKeyGen(before string) string {
	return before + ".pub.pem"
}

// スレッドセーフ。
func NewFileTaKeyProvider(path string, expiDur time.Duration) TaKeyProvider {
	return newTaKeyProvider(NewFileKeyValueStore(path, pemKeyGen, publicKeyMarshal, publicKeyUnmarshal, expiDur))
}
<commit_msg>公開鍵の拡張子を pub.pem から pub に変更<commit_after>package driver

import (
	"github.com/realglobe-Inc/edo/util"
	"github.com/realglobe-Inc/go-lib-rg/erro"
	"time"
)

func publicKeyMarshal(value interface{}) (data []byte, err error) {
	pemStr, err := publicKeyToPem(value)
	if err != nil {
		return nil, erro.Wrap(err)
	}
	return pemStr.([]byte), nil
}

// data を PEM 形式の文字列として、rsa.PublicKey にデコードする。
func publicKeyUnmarshal(data []byte) (interface{}, error) {
	pubKey, err := util.ParseRsaPublicKey(string(data))
	if err != nil {
		return nil, erro.Wrap(err)
	}
	return pubKey, nil
}

func pemKeyGen(before string) string {
	return before + ".pub"
}

// スレッドセーフ。
func NewFileTaKeyProvider(path string, expiDur time.Duration) TaKeyProvider {
	return newTaKeyProvider(NewFileKeyValueStore(path, pemKeyGen, publicKeyMarshal, publicKeyUnmarshal, expiDur))
}
<|endoftext|>


<commit_before>package goplate

import (
	"fmt"
	"github.com/blackss2/goplate"
	"github.com/revel/revel"
	"net/http"
	"html/template"
	"os"
	"path/filepath"
	"time"
)

var (
	MainRenderer *Renderer
)

type Renderer struct {
	loader *goplate.PlateLoader
	templateSet *template.Template
	isInit bool
}

func Render(c *revel.Controller, v ...interface{}) revel.Result {
	MainRenderer.Refresh()
	/*
	filepath := revel.TemplatePaths[0] + "/" + c.Name + "/" + c.MethodName + c.Request.Format
	result := MainRenderer.loader.ApplyFile(filepath)
	return c.RenderHtml(result)
	*/
	c.Response.Status = http.StatusOK

	template, err := MainRenderer.templateSet.Template(templatePath)
	if err != nil {
		return c.RenderError(err)
	}

	return &RenderTemplateResult{
		Template:   template,
		RenderArgs: c.RenderArgs,
	}
}

func (this *Renderer) Refresh() {
	//TODO : mutex lock MainRenderer
	if !MainRenderer.isInit {
		this.loader = goplate.NewPlateLoader()
		if len(revel.TemplatePaths) > 0 {
			path := revel.TemplatePaths[0] //TEMP
			rootPath := fmt.Sprintf("%s/goplates", filepath.Dir(path))
			filepath.Walk(rootPath, func(path string, f os.FileInfo, err error) error {
				if filepath.Ext(path) == ".htm" || filepath.Ext(path) == ".html" {
					this.loader.LoadFile(path)
					result := MainRenderer.loader.ApplyFile(path)
					MainRenderer.templateSet.Parse(result)
				}
				return nil
			})
			MainRenderer.isInit = true
		}
	}
}

func init() {
	MainRenderer = &Renderer{
		templateSet: template.New(templateName).Funcs(TemplateFuncs),
	}
	go func() {
		tick := time.Tick(5 * time.Second)
		select {
		case <-tick:
			MainRenderer.isInit = false
			MainRenderer.Refresh() //TEMP
		}
	}()
}
<commit_msg>revel merge v0.3<commit_after>package goplate

import (
	"fmt"
	"github.com/blackss2/goplate"
	"github.com/revel/revel"
	"os"
	"path/filepath"
	"strings"
)

var (
	MainGoplateLoader *GoplateLoader
	MainWatcher       *revel.Watcher
)

type GoplateLoader struct {
	*goplate.PlateLoader
	paths []string
}

func NewGoplateLoader() *GoplateLoader {
	return &GoplateLoader{
		paths: make([]string, 0, 1),
	}
}

func (this *GoplateLoader) Refresh() *revel.Error {
	this.PlateLoader = goplate.NewPlateLoader()
	viewsPath := revel.TemplatePaths[0]

	for _, rootPath := range MainGoplateLoader.paths {
		filepath.Walk(rootPath, func(path string, f os.FileInfo, err error) error {
			if filepath.Ext(path) == ".htm" || filepath.Ext(path) == ".html" {
				this.LoadFile(path)
				relPath, err := filepath.Rel(rootPath, path)
				if err != nil {
					panic(err)
				}
				if strings.HasPrefix(relPath, "views\\") || strings.HasPrefix(relPath, "views/") {
					outputPath := fmt.Sprintf("%s/%s", viewsPath, relPath[5:])
					file, err := os.Create(outputPath)
					fmt.Println(outputPath)
					if err != nil {
						panic(err)
					}
					if file != nil {
						result := this.ApplyFile(path)
						file.WriteString(result)
					}
				}
			}
			return nil
		})
	}
	return nil
}

func (this *GoplateLoader) WatchDir(info os.FileInfo) bool {
	return !strings.HasPrefix(info.Name(), ".")
}

func (this *GoplateLoader) WatchFile(basename string) bool {
	return !strings.HasPrefix(basename, ".")
}

var WatchFilter = func(c *revel.Controller, fc []revel.Filter) {
	if MainWatcher != nil {
		err := MainWatcher.Notify()
		if err != nil {
			c.Result = c.RenderError(err)
			return
		}
	}
	fc[0](c, fc[1:])
}

func init() {
	revel.OnAppStart(func() {
		MainGoplateLoader = NewGoplateLoader()
		viewsPath := revel.TemplatePaths[0]
		appPath := fmt.Sprintf("%s/goplates", filepath.Dir(viewsPath))
		MainGoplateLoader.paths = append(MainGoplateLoader.paths, appPath)
		revel.TemplateDelims = "[[ ]]"

		if revel.Config.BoolDefault("watch", true) {
			MainWatcher = revel.NewWatcher()
			revel.Filters = append([]revel.Filter{WatchFilter}, revel.Filters...)
		}
		if MainWatcher != nil && revel.Config.BoolDefault("watch.templates", true) {
			MainWatcher.Listen(MainGoplateLoader, MainGoplateLoader.paths...)
		}
	})
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 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 server

import (
	"encoding/json"
	"io"
	"io/ioutil"
	gohttp "net/http"
	"os"
	"path/filepath"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/itsjamie/gin-cors"
	"github.com/pkg/errors"
	ppErrors "github.com/pufferpanel/pufferd/errors"
	"github.com/pufferpanel/pufferd/http"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
	"strconv"
	"strings"
)

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *gohttp.Request) bool {
		return true
	},
}

func RegisterRoutes(e *gin.Engine) {
	l := e.Group("/server")
	{
		l.Handle("CONNECT", "/:id/console", func(c *gin.Context) {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Credentials", "false")
		})
		l.PUT("/:id", httphandlers.OAuth2Handler("server.create", false), CreateServer)
		l.DELETE("/:id", httphandlers.OAuth2Handler("server.delete", true), DeleteServer)
		l.POST("/:id", httphandlers.OAuth2Handler("server.edit", true), EditServer)
		l.GET("/:id/start", httphandlers.OAuth2Handler("server.start", true), StartServer)
		l.GET("/:id/stop", httphandlers.OAuth2Handler("server.stop", true), StopServer)
		l.POST("/:id/install", httphandlers.OAuth2Handler("server.install", true), InstallServer)
		l.GET("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.get", true), GetFile)
		l.PUT("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.put", true), PutFile)
		l.DELETE("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.delete", true), DeleteFile)
		l.POST("/:id/console", httphandlers.OAuth2Handler("server.console.send", true), PostConsole)
		l.GET("/:id/stats", httphandlers.OAuth2Handler("server.stats", true), GetStats)
		l.POST("/:id/reload", httphandlers.OAuth2Handler("server.reload", true), ReloadServer)
		l.GET("/:id/console", httphandlers.OAuth2Handler("server.console", true), cors.Middleware(cors.Config{
			Origins:     "*",
			Credentials: true,
		}), GetConsole)
		l.GET("/:id/logs", httphandlers.OAuth2Handler("server.logs", true), GetLogs)
	}
	e.GET("/network", httphandlers.OAuth2Handler("server.network", false), NetworkServer)
}

func StartServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	server.Start()
	http.Respond(c).Send()
}

func StopServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	wait := c.Param("wait")
	if wait == "" || (wait != "true" && wait != "false") {
		wait = "true"
	}

	err := server.Stop()
	if err != nil {
		errorConnection(c, err)
		return
	}

	if wait == "true" {
		err = server.GetEnvironment().WaitForMainProcess()
		if err != nil {
			errorConnection(c, err)
			return
		}
	}
	http.Respond(c).Send()
}

func CreateServer(c *gin.Context) {
	serverId := c.Param("id")
	prg, err := programs.Get(serverId)

	if prg != nil{
		http.Respond(c).Code(409).Message("server already exists").Send()
		return
	}

	if err != nil {
		http.Respond(c).Code(500).Data(err).Message("error checking if server exists").Send()
		return
	}

	data := make(map[string]interface{}, 0)
	err = json.NewDecoder(c.Request.Body).Decode(&data)

	if err != nil {
		logging.Error("Error decoding JSON body", err)
		http.Respond(c).Code(400).Message("error parsing json").Data(err).MessageCode(http.MALFORMEDJSON).Send()
		return
	}

	serverType := data["type"].(string)

	if !programs.Create(serverId, serverType, data) {
		errorConnection(c, nil)
	} else {
		http.Respond(c).Send()
	}
}

func DeleteServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)
	err := programs.Delete(prg.Id())
	if err != nil {
		http.Respond(c).Code(500).Data(err).Message("error deleting server").Send()
	} else {
		http.Respond(c).Send()
	}
}

func InstallServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	http.Respond(c).Send()
	go func() {
		prg.Install()
	}()
}

func EditServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	data := make(map[string]interface{}, 0)
	json.NewDecoder(c.Request.Body).Decode(&data)

	prg.Edit(data)
	http.Respond(c).Send()
}

func GetFile(c *gin.Context) {

	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	info, err := os.Stat(targetFile)

	if os.IsNotExist(err) {
		errorConnection(c, err)
		return
	}

	if info.IsDir() {
		type FileDesc struct {
			Name      string `json:"name"`
			Modified  int64  `json:"modifyTime"`
			Size      int64  `json:"size,omitempty"`
			File      bool   `json:"isFile"`
			Extension string `json:"extension,omitempty"`
		}

		files, _ := ioutil.ReadDir(targetFile)
		fileNames := make([]interface{}, 0)
		if targetPath != "" && targetPath != "." && targetPath != "/" {
			newFile := &FileDesc{
				Name: "..",
				File: false,
			}
			fileNames = append(fileNames, newFile)
		}
		for _, file := range files {
			newFile := &FileDesc{
				Name: file.Name(),
				File: !file.IsDir(),
			}

			if newFile.File {
				newFile.Size = file.Size()
				newFile.Modified = file.ModTime().Unix()
				newFile.Extension = filepath.Ext(file.Name())
			}

			fileNames = append(fileNames, newFile)
		}
		http.Respond(c).Data(fileNames).Send()
	} else {
		_, err := os.Open(targetFile)
		if err != nil {
			if err == os.ErrNotExist {
				http.Respond(c).Code(404).MessageCode(http.NOFILE).Send()
			} else {
				errorConnection(c, err)
			}
		}
		c.File(targetFile)
	}
}

func PutFile(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	if targetPath == "" {
		c.Status(404)
		return
	}

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	_, mkFolder := c.GetQuery("folder")
	if mkFolder {
		err := os.Mkdir(targetFile, 0644)
		if err != nil {
			errorConnection(c, err)
		} else {
			http.Respond(c).Send()
		}
		return
	}
	file, err := os.Create(targetFile)

	if err != nil {
		errorConnection(c, err)
		logging.Error("Error writing file", err)
		return
	}

	_, noform := c.GetQuery("noform")

	var sourceFile io.ReadCloser

	if noform {
		c.Request.ParseMultipartForm(32 << 20)
		sourceFile, _, err = c.Request.FormFile("file")
	} else {
		sourceFile = c.Request.Body
	}

	_, err = io.Copy(file, sourceFile)

	if err != nil {
		errorConnection(c, err)
		logging.Error("Error writing file", err)
	} else {
		http.Respond(c).Send()
	}
}

func DeleteFile(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	err := os.Remove(targetFile)
	if err != nil {
		errorConnection(c, err)
		logging.Error("Failed to delete file", err)
	} else {
		http.Respond(c).Send()
	}
}

func PostConsole(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	d, _ := ioutil.ReadAll(c.Request.Body)
	cmd := string(d)
	err := prg.Execute(cmd)
	if err != nil {
		errorConnection(c, err)
	} else {
		http.Respond(c).Send()
	}
}

func GetConsole(c *gin.Context) {
	item, _ := c.Get("server")
	program := item.(programs.Program)

	conn, err := wsupgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		logging.Error("Error creating websocket", err)
		errorConnection(c, err)
		return
	}
	console, _ := program.GetEnvironment().GetConsole()
	for _, v := range console {
		conn.WriteMessage(websocket.TextMessage, []byte(v))
	}
	program.GetEnvironment().AddListener(conn)
}

func GetStats(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	results, err := server.GetEnvironment().GetStats()
	if err != nil {
		result := make(map[string]interface{})
		result["error"] = err.Error()
		_, isOffline := err.(ppErrors.ServerOffline)
		if isOffline {
			http.Respond(c).Data(result).Code(200).Send()
		} else {
			http.Respond(c).Data(result).Code(500).Send()
		}
	} else {
		http.Respond(c).Data(results).Send()
	}
}

func ReloadServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	err := programs.Reload(server.Id())
	if err != nil {
		errorConnection(c, err)
		return
	}
	http.Respond(c).Send()
}

func NetworkServer(c *gin.Context) {
	scopes, _ := c.Get("scopes")
	valid := false
	for _, v := range scopes.([]string) {
		if v == "server.network" {
			valid = true
		}
	}
	if !valid {
		http.Respond(c).Code(403).MessageCode(http.NOTAUTHORIZED).Message("missing scope server.network").Send()
		return
	}

	servers := c.DefaultQuery("ids", "")
	if servers == "" {
		http.Respond(c).Code(400).MessageCode(http.NOSERVERID).Message("no server ids provided").Send()
		return
	}
	serverIds := strings.Split(servers, ",")
	result := make(map[string]string)
	for _, v := range serverIds {
		program, _ := programs.Get(v)
		if program == nil {
			continue
		}
		result[program.Id()] = program.GetNetwork()
	}
	http.Respond(c).Data(result).Send()
}

func GetLogs(c *gin.Context) {
	item, _ := c.Get("server")
	program := item.(programs.Program)

	time := c.DefaultQuery("time", "0")

	castedTime, ok := strconv.ParseInt(time, 10, 64)

	if ok != nil {
		c.AbortWithError(400, errors.New("Time provided is not a valid UNIX time"))
		return
	}

	console, epoch := program.GetEnvironment().GetConsoleFrom(castedTime)
	msg := ""
	for _, k := range console {
		msg += k
	}
	result := make(map[string]interface{})
	result["epoch"] = epoch
	result["logs"] = msg
	http.Respond(c).Data(result).Send()
}

func errorConnection(c *gin.Context, err error) {
	http.Respond(c).Code(500).MessageCode(http.UNKNOWN).Data(err).Message("error handling request").Send()
}
<commit_msg>Load will always error if there's no server, so create can't check that<commit_after>/*
 Copyright 2016 Padduck, LLC

 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 server

import (
	"encoding/json"
	"io"
	"io/ioutil"
	gohttp "net/http"
	"os"
	"path/filepath"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/itsjamie/gin-cors"
	"github.com/pkg/errors"
	ppErrors "github.com/pufferpanel/pufferd/errors"
	"github.com/pufferpanel/pufferd/http"
	"github.com/pufferpanel/pufferd/httphandlers"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/utils"
	"strconv"
	"strings"
)

var wsupgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *gohttp.Request) bool {
		return true
	},
}

func RegisterRoutes(e *gin.Engine) {
	l := e.Group("/server")
	{
		l.Handle("CONNECT", "/:id/console", func(c *gin.Context) {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Credentials", "false")
		})
		l.PUT("/:id", httphandlers.OAuth2Handler("server.create", false), CreateServer)
		l.DELETE("/:id", httphandlers.OAuth2Handler("server.delete", true), DeleteServer)
		l.POST("/:id", httphandlers.OAuth2Handler("server.edit", true), EditServer)
		l.GET("/:id/start", httphandlers.OAuth2Handler("server.start", true), StartServer)
		l.GET("/:id/stop", httphandlers.OAuth2Handler("server.stop", true), StopServer)
		l.POST("/:id/install", httphandlers.OAuth2Handler("server.install", true), InstallServer)
		l.GET("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.get", true), GetFile)
		l.PUT("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.put", true), PutFile)
		l.DELETE("/:id/file/*filename", httphandlers.OAuth2Handler("server.file.delete", true), DeleteFile)
		l.POST("/:id/console", httphandlers.OAuth2Handler("server.console.send", true), PostConsole)
		l.GET("/:id/stats", httphandlers.OAuth2Handler("server.stats", true), GetStats)
		l.POST("/:id/reload", httphandlers.OAuth2Handler("server.reload", true), ReloadServer)
		l.GET("/:id/console", httphandlers.OAuth2Handler("server.console", true), cors.Middleware(cors.Config{
			Origins:     "*",
			Credentials: true,
		}), GetConsole)
		l.GET("/:id/logs", httphandlers.OAuth2Handler("server.logs", true), GetLogs)
	}
	e.GET("/network", httphandlers.OAuth2Handler("server.network", false), NetworkServer)
}

func StartServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	server.Start()
	http.Respond(c).Send()
}

func StopServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	wait := c.Param("wait")
	if wait == "" || (wait != "true" && wait != "false") {
		wait = "true"
	}

	err := server.Stop()
	if err != nil {
		errorConnection(c, err)
		return
	}

	if wait == "true" {
		err = server.GetEnvironment().WaitForMainProcess()
		if err != nil {
			errorConnection(c, err)
			return
		}
	}
	http.Respond(c).Send()
}

func CreateServer(c *gin.Context) {
	serverId := c.Param("id")
	prg, _ := programs.Get(serverId)

	if prg != nil{
		http.Respond(c).Code(409).Message("server already exists").Send()
		return
	}

	data := make(map[string]interface{}, 0)
	err := json.NewDecoder(c.Request.Body).Decode(&data)

	if err != nil {
		logging.Error("Error decoding JSON body", err)
		http.Respond(c).Code(400).Message("error parsing json").Data(err).MessageCode(http.MALFORMEDJSON).Send()
		return
	}

	serverType := data["type"].(string)

	if !programs.Create(serverId, serverType, data) {
		errorConnection(c, nil)
	} else {
		http.Respond(c).Send()
	}
}

func DeleteServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)
	err := programs.Delete(prg.Id())
	if err != nil {
		http.Respond(c).Code(500).Data(err).Message("error deleting server").Send()
	} else {
		http.Respond(c).Send()
	}
}

func InstallServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	http.Respond(c).Send()
	go func() {
		prg.Install()
	}()
}

func EditServer(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	data := make(map[string]interface{}, 0)
	json.NewDecoder(c.Request.Body).Decode(&data)

	prg.Edit(data)
	http.Respond(c).Send()
}

func GetFile(c *gin.Context) {

	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	info, err := os.Stat(targetFile)

	if os.IsNotExist(err) {
		errorConnection(c, err)
		return
	}

	if info.IsDir() {
		type FileDesc struct {
			Name      string `json:"name"`
			Modified  int64  `json:"modifyTime"`
			Size      int64  `json:"size,omitempty"`
			File      bool   `json:"isFile"`
			Extension string `json:"extension,omitempty"`
		}

		files, _ := ioutil.ReadDir(targetFile)
		fileNames := make([]interface{}, 0)
		if targetPath != "" && targetPath != "." && targetPath != "/" {
			newFile := &FileDesc{
				Name: "..",
				File: false,
			}
			fileNames = append(fileNames, newFile)
		}
		for _, file := range files {
			newFile := &FileDesc{
				Name: file.Name(),
				File: !file.IsDir(),
			}

			if newFile.File {
				newFile.Size = file.Size()
				newFile.Modified = file.ModTime().Unix()
				newFile.Extension = filepath.Ext(file.Name())
			}

			fileNames = append(fileNames, newFile)
		}
		http.Respond(c).Data(fileNames).Send()
	} else {
		_, err := os.Open(targetFile)
		if err != nil {
			if err == os.ErrNotExist {
				http.Respond(c).Code(404).MessageCode(http.NOFILE).Send()
			} else {
				errorConnection(c, err)
			}
		}
		c.File(targetFile)
	}
}

func PutFile(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	if targetPath == "" {
		c.Status(404)
		return
	}

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	_, mkFolder := c.GetQuery("folder")
	if mkFolder {
		err := os.Mkdir(targetFile, 0644)
		if err != nil {
			errorConnection(c, err)
		} else {
			http.Respond(c).Send()
		}
		return
	}
	file, err := os.Create(targetFile)

	if err != nil {
		errorConnection(c, err)
		logging.Error("Error writing file", err)
		return
	}

	_, noform := c.GetQuery("noform")

	var sourceFile io.ReadCloser

	if noform {
		c.Request.ParseMultipartForm(32 << 20)
		sourceFile, _, err = c.Request.FormFile("file")
	} else {
		sourceFile = c.Request.Body
	}

	_, err = io.Copy(file, sourceFile)

	if err != nil {
		errorConnection(c, err)
		logging.Error("Error writing file", err)
	} else {
		http.Respond(c).Send()
	}
}

func DeleteFile(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	targetPath := c.Param("filename")

	targetFile := utils.JoinPath(server.GetEnvironment().GetRootDirectory(), targetPath)

	if !utils.EnsureAccess(targetFile, server.GetEnvironment().GetRootDirectory()) {
		http.Respond(c).Code(403).Message("invalid file path").Code(http.NOTAUTHORIZED).Send()
		return
	}

	err := os.Remove(targetFile)
	if err != nil {
		errorConnection(c, err)
		logging.Error("Failed to delete file", err)
	} else {
		http.Respond(c).Send()
	}
}

func PostConsole(c *gin.Context) {
	item, _ := c.Get("server")
	prg := item.(programs.Program)

	d, _ := ioutil.ReadAll(c.Request.Body)
	cmd := string(d)
	err := prg.Execute(cmd)
	if err != nil {
		errorConnection(c, err)
	} else {
		http.Respond(c).Send()
	}
}

func GetConsole(c *gin.Context) {
	item, _ := c.Get("server")
	program := item.(programs.Program)

	conn, err := wsupgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		logging.Error("Error creating websocket", err)
		errorConnection(c, err)
		return
	}
	console, _ := program.GetEnvironment().GetConsole()
	for _, v := range console {
		conn.WriteMessage(websocket.TextMessage, []byte(v))
	}
	program.GetEnvironment().AddListener(conn)
}

func GetStats(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	results, err := server.GetEnvironment().GetStats()
	if err != nil {
		result := make(map[string]interface{})
		result["error"] = err.Error()
		_, isOffline := err.(ppErrors.ServerOffline)
		if isOffline {
			http.Respond(c).Data(result).Code(200).Send()
		} else {
			http.Respond(c).Data(result).Code(500).Send()
		}
	} else {
		http.Respond(c).Data(results).Send()
	}
}

func ReloadServer(c *gin.Context) {
	item, _ := c.Get("server")
	server := item.(programs.Program)

	err := programs.Reload(server.Id())
	if err != nil {
		errorConnection(c, err)
		return
	}
	http.Respond(c).Send()
}

func NetworkServer(c *gin.Context) {

	servers := c.DefaultQuery("ids", "")
	if servers == "" {
		http.Respond(c).Code(400).MessageCode(http.NOSERVERID).Message("no server ids provided").Send()
		return
	}
	serverIds := strings.Split(servers, ",")
	result := make(map[string]string)
	for _, v := range serverIds {
		program, _ := programs.Get(v)
		if program == nil {
			continue
		}
		result[program.Id()] = program.GetNetwork()
	}
	http.Respond(c).Data(result).Send()
}

func GetLogs(c *gin.Context) {
	item, _ := c.Get("server")
	program := item.(programs.Program)

	time := c.DefaultQuery("time", "0")

	castedTime, ok := strconv.ParseInt(time, 10, 64)

	if ok != nil {
		c.AbortWithError(400, errors.New("Time provided is not a valid UNIX time"))
		return
	}

	console, epoch := program.GetEnvironment().GetConsoleFrom(castedTime)
	msg := ""
	for _, k := range console {
		msg += k
	}
	result := make(map[string]interface{})
	result["epoch"] = epoch
	result["logs"] = msg
	http.Respond(c).Data(result).Send()
}

func errorConnection(c *gin.Context, err error) {
	http.Respond(c).Code(500).MessageCode(http.UNKNOWN).Data(err).Message("error handling request").Send()
}
<|endoftext|>