<commit_before>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package serviced

import (
	"database/sql"
	_ "github.com/ziutek/mymysql/godrv"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"os/exec"
	"testing"
)

var (
	server  ControlPlane
	client  *ControlClient
	unused  int
	tempdir string
)

var (
	database_name     = "cp_test"
	database_user     = "root"
	database_password = ""
)

func connectionString() string {
	return database_name + "/" + database_user + "/" + database_password
}

func cleanTestDB(t *testing.T) {
	conn, err := sql.Open("mymysql", "/"+database_user+"/")
	defer conn.Close()
	_, err = conn.Exec("DROP DATABASE IF EXISTS `" + database_name + "`")
	if err != nil {
		log.Fatal("Could not drop test database:", err)
	}
	_, err = conn.Exec("CREATE DATABASE `" + database_name + "`")
	if err != nil {
		log.Fatal("Could not create test database: ", err)
	}
	cmd := exec.Command("mysql", "-u", "root", database_name, "-e", "source database.sql")
	output, err := cmd.Output()
	if err != nil {
		log.Fatal("Problem souring schema", err)
	}
	log.Print(string(output))
}

func setup(t *testing.T) {

	cleanTestDB(t)
	server, err := NewControlSvc(connectionString())

	// register the server API
	rpc.RegisterName("ControlPlane", server)
	rpc.HandleHTTP()

	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatalf("net.Listen tcp :0 %v", err)
	}
	go http.Serve(l, nil) // start the server
	log.Printf("Test Server started on %s", l.Addr().String())

	// setup the client
	client, err = NewControlClient(l.Addr().String())
	if err != nil {
		log.Fatalf("Coult not start client %v", err)
	}
	log.Printf("Client started: %v", client)
}

func TestControlAPI(t *testing.T) {
	setup(t)

	request := EntityRequest{}
	var hosts map[string]*Host = nil

	err := client.GetHosts(request, &hosts)
	if err != nil {
		log.Fatalf("Could not get hosts", err)
	}
	host, err := CurrentContextAsHost()
	log.Printf("Got a currentContextAsHost()\n")
	if err != nil {
		t.Fatal("Could not get currentContextAsHost", err)
	}
	err = client.AddHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not add host", err)
	}

	host.Name = "foo"
	err = client.UpdateHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not update host", err)
	} else {
		log.Print("update of host is OK")
	}
	err = client.GetHosts(request, &hosts)
	if err != nil {
		t.Fatal("Error getting updated hosts.", err)
	}
	if hosts[host.Id].Name != "foo" {
		t.Fatal("Expected host to be named foo.", err)
	}

	err = client.RemoveHost(host.Id, &unused)
	if err != nil {
		t.Fatal("Could not remove host.", err)
	}
	hosts = nil
	err = client.GetHosts(request, &hosts)
	if err != nil {
		t.Fatal("Error getting updated hosts.", err)
	}
	_, exists := hosts[host.Id]
	if exists {
		t.Fatal("Host was not removed.", err)
	}

	var services []*Service
	err = client.GetServices(request, &services)
	if err != nil {
		t.Fatal("Error getting services.", err)
	}
	if len(services) != 0 {
		t.Fatal("Expecting 0 services")
	}

	/*
		service, err := NewService()
		if err != nil {
			t.Fatal("Error creating new service.")
		}
		service.Name = "helloworld"
		err = client.AddService(*service, &unused)
		if err != nil {
			t.Fatal("Could not add service.")
		}
		services = nil
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 1 {
			t.Fatal("Expecting 1 service, got ", len(services))
		}
		if services[0].Id != service.Id {
			t.Fatalf("Created service %s but got back %s", services[0].Id, service.Id)
		}

		service.Name = "Roger"
		err = client.UpdateService(*service, &unused)
		if err != nil {
			t.Fatalf("Could not save service.")
		}
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 1 {
			t.Fatal("Expecting 1 service, got ", len(services))
		}
		if services[0].Id != service.Id {
			t.Fatalf("Created service %s but got back %s", services[0].Id, service.Id)
		}

		err = client.RemoveService(service.Id, &unused)
		if err != nil {
			t.Fatal("error removing service.")
		}
		services = nil
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 0 {
			t.Fatal("Expecting 0 service, got ", len(services))
		}
	*/

	services = nil
	err = client.GetServicesForHost("dasdfasdf", &services)
	log.Printf("Got %d services", len(services))
	if err == nil {
		t.Fatal("Expected error looking for non-existent service.")
	}

	var pools map[string]*ResourcePool = nil
	err = client.GetResourcePools(request, &pools)
	if err != nil {
		t.Fatal("Problem getting empty resource pool list.", err)
	}

	pool, _ := NewResourcePool()
	pool.Name = "unit_test_pool"
	err = client.AddResourcePool(*pool, &unused)
	if err != nil {
		t.Fatal("Problem adding resource pool", err)
	}

	err = client.RemoveResourcePool(pool.Id, &unused)
	if err != nil {
		t.Fatal("Problem removing resource pool", err)
	}

	pools = nil
	err = client.GetResourcePools(request, &pools)
	if err != nil {
		t.Fatal("Problem getting empty resource pool list.")
	}
	if len(pools) != 0 {
		t.Fatal("Expected 0 pools: ", len(pools))
	}
}

func TestServiceStart(t *testing.T) {

	cleanTestDB(t)
	host, err := CurrentContextAsHost()
	log.Printf("Got a currentContextAsHost()\n")
	if err != nil {
		t.Fatal("Could not get currentContextAsHost", err)
	}
	err = client.AddHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not add host", err)
	}

	pool, _ := NewResourcePool()
	pool.Name = "default"
	err = client.AddResourcePool(*pool, &unused)
	if err != nil {
		t.Fatal("Problem adding resource pool", err)
	}
	err = client.AddHostToResourcePool(PoolHost{HostId: host.Id, PoolId: pool.Id}, &unused)
	if err != nil {
		t.Fatal("Problem adding host to resource pool", err)
	}

	// add a new service
	service, _ := NewService()
	service.Name = "My test service!"
	service.PoolId = pool.Id
	service.Startup = "/bin/sh -c \"while true; do echo hello world; sleep 1; done\""
	err = client.AddService(*service, &unused)
	if err != nil {
		t.Fatal("Could not add service: ", err)
	}

	// start the service
	var hostId string
	err = client.StartService(service.Id, &hostId)
	if err != nil {
		t.Fatal("Got error starting service: ", err)
	}

	var services []*Service
	// get the services for a host
	err = client.GetServicesForHost(host.Id, &services)
	if err != nil {
		t.Fatal("Could not get services for host: ", err)
	}
	log.Printf("Got %d services for %s", len(services), host.Id)
}
<commit_msg>fix spelling error.<commit_after>/*******************************************************************************
* Copyright (C) Zenoss, Inc. 2013, all rights reserved.
*
* This content is made available according to terms specified in
* License.zenoss under the directory where your Zenoss product is installed.
*
*******************************************************************************/

package serviced

import (
	"database/sql"
	_ "github.com/ziutek/mymysql/godrv"
	"log"
	"net"
	"net/http"
	"net/rpc"
	"os/exec"
	"testing"
)

var (
	server  ControlPlane
	client  *ControlClient
	unused  int
	tempdir string
)

var (
	database_name     = "cp_test"
	database_user     = "root"
	database_password = ""
)

func connectionString() string {
	return database_name + "/" + database_user + "/" + database_password
}

func cleanTestDB(t *testing.T) {
	conn, err := sql.Open("mymysql", "/"+database_user+"/")
	defer conn.Close()
	_, err = conn.Exec("DROP DATABASE IF EXISTS `" + database_name + "`")
	if err != nil {
		log.Fatal("Could not drop test database:", err)
	}
	_, err = conn.Exec("CREATE DATABASE `" + database_name + "`")
	if err != nil {
		log.Fatal("Could not create test database: ", err)
	}
	cmd := exec.Command("mysql", "-u", "root", database_name, "-e", "source database.sql")
	output, err := cmd.Output()
	if err != nil {
		log.Fatal("Problem sourcing schema", err)
	}
	log.Print(string(output))
}

func setup(t *testing.T) {

	cleanTestDB(t)
	server, err := NewControlSvc(connectionString())

	// register the server API
	rpc.RegisterName("ControlPlane", server)
	rpc.HandleHTTP()

	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		log.Fatalf("net.Listen tcp :0 %v", err)
	}
	go http.Serve(l, nil) // start the server
	log.Printf("Test Server started on %s", l.Addr().String())

	// setup the client
	client, err = NewControlClient(l.Addr().String())
	if err != nil {
		log.Fatalf("Coult not start client %v", err)
	}
	log.Printf("Client started: %v", client)
}

func TestControlAPI(t *testing.T) {
	setup(t)

	request := EntityRequest{}
	var hosts map[string]*Host = nil

	err := client.GetHosts(request, &hosts)
	if err != nil {
		log.Fatalf("Could not get hosts", err)
	}
	host, err := CurrentContextAsHost()
	log.Printf("Got a currentContextAsHost()\n")
	if err != nil {
		t.Fatal("Could not get currentContextAsHost", err)
	}
	err = client.AddHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not add host", err)
	}

	host.Name = "foo"
	err = client.UpdateHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not update host", err)
	} else {
		log.Print("update of host is OK")
	}
	err = client.GetHosts(request, &hosts)
	if err != nil {
		t.Fatal("Error getting updated hosts.", err)
	}
	if hosts[host.Id].Name != "foo" {
		t.Fatal("Expected host to be named foo.", err)
	}

	err = client.RemoveHost(host.Id, &unused)
	if err != nil {
		t.Fatal("Could not remove host.", err)
	}
	hosts = nil
	err = client.GetHosts(request, &hosts)
	if err != nil {
		t.Fatal("Error getting updated hosts.", err)
	}
	_, exists := hosts[host.Id]
	if exists {
		t.Fatal("Host was not removed.", err)
	}

	var services []*Service
	err = client.GetServices(request, &services)
	if err != nil {
		t.Fatal("Error getting services.", err)
	}
	if len(services) != 0 {
		t.Fatal("Expecting 0 services")
	}

	/*
		service, err := NewService()
		if err != nil {
			t.Fatal("Error creating new service.")
		}
		service.Name = "helloworld"
		err = client.AddService(*service, &unused)
		if err != nil {
			t.Fatal("Could not add service.")
		}
		services = nil
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 1 {
			t.Fatal("Expecting 1 service, got ", len(services))
		}
		if services[0].Id != service.Id {
			t.Fatalf("Created service %s but got back %s", services[0].Id, service.Id)
		}

		service.Name = "Roger"
		err = client.UpdateService(*service, &unused)
		if err != nil {
			t.Fatalf("Could not save service.")
		}
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 1 {
			t.Fatal("Expecting 1 service, got ", len(services))
		}
		if services[0].Id != service.Id {
			t.Fatalf("Created service %s but got back %s", services[0].Id, service.Id)
		}

		err = client.RemoveService(service.Id, &unused)
		if err != nil {
			t.Fatal("error removing service.")
		}
		services = nil
		err = client.GetServices(request, &services)
		if err != nil {
			t.Fatal("Error getting services.")
		}
		if len(services) != 0 {
			t.Fatal("Expecting 0 service, got ", len(services))
		}
	*/

	services = nil
	err = client.GetServicesForHost("dasdfasdf", &services)
	log.Printf("Got %d services", len(services))
	if err == nil {
		t.Fatal("Expected error looking for non-existent service.")
	}

	var pools map[string]*ResourcePool = nil
	err = client.GetResourcePools(request, &pools)
	if err != nil {
		t.Fatal("Problem getting empty resource pool list.", err)
	}

	pool, _ := NewResourcePool()
	pool.Name = "unit_test_pool"
	err = client.AddResourcePool(*pool, &unused)
	if err != nil {
		t.Fatal("Problem adding resource pool", err)
	}

	err = client.RemoveResourcePool(pool.Id, &unused)
	if err != nil {
		t.Fatal("Problem removing resource pool", err)
	}

	pools = nil
	err = client.GetResourcePools(request, &pools)
	if err != nil {
		t.Fatal("Problem getting empty resource pool list.")
	}
	if len(pools) != 0 {
		t.Fatal("Expected 0 pools: ", len(pools))
	}
}

func TestServiceStart(t *testing.T) {

	cleanTestDB(t)
	host, err := CurrentContextAsHost()
	log.Printf("Got a currentContextAsHost()\n")
	if err != nil {
		t.Fatal("Could not get currentContextAsHost", err)
	}
	err = client.AddHost(*host, &unused)
	if err != nil {
		t.Fatal("Could not add host", err)
	}

	pool, _ := NewResourcePool()
	pool.Name = "default"
	err = client.AddResourcePool(*pool, &unused)
	if err != nil {
		t.Fatal("Problem adding resource pool", err)
	}
	err = client.AddHostToResourcePool(PoolHost{HostId: host.Id, PoolId: pool.Id}, &unused)
	if err != nil {
		t.Fatal("Problem adding host to resource pool", err)
	}

	// add a new service
	service, _ := NewService()
	service.Name = "My test service!"
	service.PoolId = pool.Id
	service.Startup = "/bin/sh -c \"while true; do echo hello world; sleep 1; done\""
	err = client.AddService(*service, &unused)
	if err != nil {
		t.Fatal("Could not add service: ", err)
	}

	// start the service
	var hostId string
	err = client.StartService(service.Id, &hostId)
	if err != nil {
		t.Fatal("Got error starting service: ", err)
	}

	var services []*Service
	// get the services for a host
	err = client.GetServicesForHost(host.Id, &services)
	if err != nil {
		t.Fatal("Could not get services for host: ", err)
	}
	log.Printf("Got %d services for %s", len(services), host.Id)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"encoding/json"

	"os/exec"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/redist"
	"github.com/natefinch/npipe"
	"github.com/olekukonko/tablewriter"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
)

// PrereqTask describes something the prereq installer has to do
type PrereqTask struct {
	Name    string             `json:"name"`
	WorkDir string             `json:"workDir"`
	Info    redist.RedistEntry `json:"info"`
}

// PrereqPlan contains a list of tasks for the prereq installer
type PrereqPlan struct {
	Tasks []*PrereqTask `json:"tasks"`
}

// PrereqState informs the caller on the current status of a prereq
type PrereqState struct {
	Type   string `json:"type"`
	Name   string `json:"name"`
	Status string `json:"status"`
}

// PrereqLogEntry sends an information to the caller on the progress of the task
type PrereqLogEntry struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func installPrereqs(planPath string, pipePath string) {
	must(doInstallPrereqs(planPath, pipePath))
}

func doInstallPrereqs(planPath string, pipePath string) error {
	planReader, err := os.Open(planPath)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	decoder := json.NewDecoder(planReader)

	plan := &PrereqPlan{}
	err = decoder.Decode(plan)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	hasConn := true
	conn, err := npipe.Dial(pipePath)
	if err != nil {
		comm.Warnf("Could not dial pipe %s", conn)
		hasConn = false
	}

	writeLine := func(contents []byte) error {
		if !hasConn {
			return nil
		}

		contents = append(contents, '\n')

		_, err = conn.Write(contents)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return nil
	}

	doWriteState := func(taskName string, status string) error {
		contents, err := json.Marshal(&PrereqState{
			Type:   "state",
			Name:   taskName,
			Status: status,
		})
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return writeLine(contents)
	}

	writeState := func(taskName string, status string) {
		err := doWriteState(taskName, status)
		if err != nil {
			switch err := err.(type) {
			case *errors.Error:
				comm.Warnf("Couldn't write log entry: %s", err.ErrorStack())
			default:
				comm.Warnf("Couldn't write log entry: %s", err.Error())
			}
		}
	}

	doLogf := func(format string, args ...interface{}) error {
		comm.Logf(format, args...)
		message := fmt.Sprintf(format, args...)

		contents, err := json.Marshal(&PrereqLogEntry{
			Type:    "log",
			Message: message,
		})
		if err != nil {
			return errors.Wrap(err, 0)
		}

		err = writeLine([]byte(contents))
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return nil
	}

	logf := func(format string, args ...interface{}) {
		err := doLogf(format, args...)
		if err != nil {
			switch err := err.(type) {
			case *errors.Error:
				comm.Warnf("Couldn't write log entry: %s", err.ErrorStack())
			default:
				comm.Warnf("Couldn't write log entry: %s", err.Error())
			}
		}
	}

	logf("Installing %d prereqs", len(plan.Tasks))
	startTime := time.Now()

	var failed []string

	for _, task := range plan.Tasks {
		taskStartTime := time.Now()
		writeState(task.Name, "installing")

		logf("Installing %s", task.Name)

		commandPath := filepath.Join(task.WorkDir, task.Info.Command)
		args := task.Info.Args

		// MSI packages get special treatment for reasons.
		if strings.HasSuffix(strings.ToLower(task.Info.Command), ".msi") {
			logf("It's an MSI package")

			tempDir, err := ioutil.TempDir("", "butler-msi-logs")
			if err != nil {
				return errors.Wrap(err, 0)
			}

			defer func() {
				os.RemoveAll(tempDir)
			}()

			logPath := filepath.Join(tempDir, "msi-install-log.txt")
			logf("Writing MSI install log to %s", logPath)

			err = doMsiInstall(commandPath, logPath, "")
			if err != nil {
				logf("MSI install failed: %s", err.Error())
				lf, openErr := os.Open(logPath)
				if openErr != nil {
					logf("And what's more, we can't open the log: %s", openErr.Error())
				} else {
					// grok UTF-16
					win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
					// ...but abide by the BOM if there's one
					utf16bom := unicode.BOMOverride(win16be.NewDecoder())

					unicodeReader := transform.NewReader(lf, utf16bom)

					defer lf.Close()
					logf("Full MSI log follows:")
					s := bufio.NewScanner(unicodeReader)
					for s.Scan() {
						logf("[msi] %s", s.Text())
					}
					if scanErr := s.Err(); scanErr != nil {
						logf("While reading msi log: %s", scanErr.Error())
					}
				}

				failed = append(failed, task.Name)
			}
		} else {
			cmd := exec.Command(commandPath, args...)
			cmd.Dir = task.WorkDir

			logf("It's an installer, launching %s", commandPath)
			logf("...with args %s", strings.Join(args, " "))
			logf("...in dir %s", cmd.Dir)

			err = cmd.Run()
			if err != nil {
				if exitError, ok := err.(*exec.ExitError); ok {
					if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
						code := status.ExitStatus()
						logf("Prereq %s has exited with code %d", task.Name, code)

						known := false
						for _, exitCode := range task.Info.ExitCodes {
							if code == exitCode.Code {
								logf("That code means: %s", exitCode.Message)
								if exitCode.Success {
									logf("...and it's harmless! Continuing...")
								} else {
									logf("...and it's fatal! We'll error out.")
									failed = append(failed, task.Name)
								}
								known = true
							}
							break
						}

						if !known {
							logf("We don't know what code %d means for %s", code, task.Name)
							logf("We'll error out eventually")
							failed = append(failed, task.Name)
						}
					} else {
						return errors.Wrap(err, 0)
					}
				} else {
					return errors.Wrap(err, 0)
				}
			}
		}

		writeState(task.Name, "done")
		logf("Done installing %s - took %s", task.Name, time.Since(taskStartTime))
	}

	if len(failed) > 0 {
		logf("Some prereqs failed to install: %s", strings.Join(failed, ", "))
	} else {
		logf("All done! Took %s", time.Since(startTime))
	}

	return nil
}

func testPrereqs(prereqs []string) {
	must(doTestPrereqs(prereqs))
}

func doTestPrereqs(prereqs []string) error {
	comm.Opf("Fetching registry...")

	baseURL := "https://dl.itch.ovh/itch-redists"

	res, err := http.Get(baseURL + "/info.json")
	if err != nil {
		return errors.Wrap(err, 0)
	}

	if res.StatusCode != 200 {
		return errors.Wrap(fmt.Errorf("While getting redist registry, got HTTP %d", res.StatusCode), 0)
	}

	dec := json.NewDecoder(res.Body)

	registry := &redist.RedistRegistry{}
	err = dec.Decode(registry)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	if len(prereqs) == 0 {
		comm.Logf("")
		comm.Statf("No prereqs specified, here are those we know about: \n")

		table := tablewriter.NewWriter(os.Stdout)
		table.SetAutoFormatHeaders(false)
		table.SetColWidth(60)
		table.SetHeader([]string{"Name", "Arch", "Description", "Version"})
		for name, info := range registry.Entries {
			table.Append([]string{name, info.FullName, info.Version, info.Arch})
		}
		table.Render()
		return nil
	}

	if len(prereqs) == 1 && prereqs[0] == "all" {
		prereqs = nil
		for name := range registry.Entries {
			prereqs = append(prereqs, name)
		}
	}

	comm.Logf("Testing out prereqs %s", strings.Join(prereqs, ", "))

	plan := &PrereqPlan{}

	tempDir := filepath.Join(os.TempDir(), "butler-test-prereqs")
	comm.Logf("Working in %s", tempDir)
	comm.Logf("(This helps not having to re-download the prereqs between runs, but feel free to wipe it)")

	err = os.MkdirAll(tempDir, 0755)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	for _, name := range prereqs {
		info, ok := registry.Entries[name]
		if !ok {
			comm.Warnf("Unknown prereq %s, skipping", name)
			continue
		}

		comm.Opf("Downloading prereq %s", name)

		workDir := filepath.Join(tempDir, name)
		err = os.MkdirAll(workDir, 0755)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		task := &PrereqTask{
			Info:    *info,
			Name:    name,
			WorkDir: workDir,
		}

		url := fmt.Sprintf("%s/%s/%s", baseURL, name, info.Command)
		dest := filepath.Join(workDir, info.Command)
		_, err = tryDl(url, dest)
		if err != nil {
			comm.Logf("Could not donwload prereq %s", name)
			return errors.Wrap(err, 0)
		}

		plan.Tasks = append(plan.Tasks, task)
	}

	planPath := filepath.Join(tempDir, "butler_install_plan.json")
	comm.Logf("Writing plan to %s", planPath)

	planContents, err := json.Marshal(plan)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	err = ioutil.WriteFile(planPath, planContents, 0644)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	comm.Opf("Handing off to install-prereqs...")

	err = doInstallPrereqs(planPath, "")
	if err != nil {
		return errors.Wrap(err, 0)
	}

	return nil
}
<commit_msg>Who needs headers anyways, cf. https://github.com/itchio/itch/issues/1304<commit_after>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"encoding/json"

	"os/exec"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/redist"
	"github.com/natefinch/npipe"
	"github.com/olekukonko/tablewriter"
	"golang.org/x/text/encoding/unicode"
	"golang.org/x/text/transform"
)

// PrereqTask describes something the prereq installer has to do
type PrereqTask struct {
	Name    string             `json:"name"`
	WorkDir string             `json:"workDir"`
	Info    redist.RedistEntry `json:"info"`
}

// PrereqPlan contains a list of tasks for the prereq installer
type PrereqPlan struct {
	Tasks []*PrereqTask `json:"tasks"`
}

// PrereqState informs the caller on the current status of a prereq
type PrereqState struct {
	Type   string `json:"type"`
	Name   string `json:"name"`
	Status string `json:"status"`
}

// PrereqLogEntry sends an information to the caller on the progress of the task
type PrereqLogEntry struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

func installPrereqs(planPath string, pipePath string) {
	must(doInstallPrereqs(planPath, pipePath))
}

func doInstallPrereqs(planPath string, pipePath string) error {
	planReader, err := os.Open(planPath)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	decoder := json.NewDecoder(planReader)

	plan := &PrereqPlan{}
	err = decoder.Decode(plan)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	hasConn := true
	conn, err := npipe.Dial(pipePath)
	if err != nil {
		comm.Warnf("Could not dial pipe %s", conn)
		hasConn = false
	}

	writeLine := func(contents []byte) error {
		if !hasConn {
			return nil
		}

		contents = append(contents, '\n')

		_, err = conn.Write(contents)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return nil
	}

	doWriteState := func(taskName string, status string) error {
		contents, err := json.Marshal(&PrereqState{
			Type:   "state",
			Name:   taskName,
			Status: status,
		})
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return writeLine(contents)
	}

	writeState := func(taskName string, status string) {
		err := doWriteState(taskName, status)
		if err != nil {
			switch err := err.(type) {
			case *errors.Error:
				comm.Warnf("Couldn't write log entry: %s", err.ErrorStack())
			default:
				comm.Warnf("Couldn't write log entry: %s", err.Error())
			}
		}
	}

	doLogf := func(format string, args ...interface{}) error {
		comm.Logf(format, args...)
		message := fmt.Sprintf(format, args...)

		contents, err := json.Marshal(&PrereqLogEntry{
			Type:    "log",
			Message: message,
		})
		if err != nil {
			return errors.Wrap(err, 0)
		}

		err = writeLine([]byte(contents))
		if err != nil {
			return errors.Wrap(err, 0)
		}

		return nil
	}

	logf := func(format string, args ...interface{}) {
		err := doLogf(format, args...)
		if err != nil {
			switch err := err.(type) {
			case *errors.Error:
				comm.Warnf("Couldn't write log entry: %s", err.ErrorStack())
			default:
				comm.Warnf("Couldn't write log entry: %s", err.Error())
			}
		}
	}

	logf("Installing %d prereqs", len(plan.Tasks))
	startTime := time.Now()

	var failed []string

	for _, task := range plan.Tasks {
		taskStartTime := time.Now()
		writeState(task.Name, "installing")

		logf("Installing %s", task.Name)

		commandPath := filepath.Join(task.WorkDir, task.Info.Command)
		args := task.Info.Args

		// MSI packages get special treatment for reasons.
		if strings.HasSuffix(strings.ToLower(task.Info.Command), ".msi") {
			logf("It's an MSI package")

			tempDir, err := ioutil.TempDir("", "butler-msi-logs")
			if err != nil {
				return errors.Wrap(err, 0)
			}

			defer func() {
				os.RemoveAll(tempDir)
			}()

			logPath := filepath.Join(tempDir, "msi-install-log.txt")
			logf("Writing MSI install log to %s", logPath)

			err = doMsiInstall(commandPath, logPath, "")
			if err != nil {
				logf("MSI install failed: %s", err.Error())
				lf, openErr := os.Open(logPath)
				if openErr != nil {
					logf("And what's more, we can't open the log: %s", openErr.Error())
				} else {
					// grok UTF-16
					win16be := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
					// ...but abide by the BOM if there's one
					utf16bom := unicode.BOMOverride(win16be.NewDecoder())

					unicodeReader := transform.NewReader(lf, utf16bom)

					defer lf.Close()
					logf("Full MSI log follows:")
					s := bufio.NewScanner(unicodeReader)
					for s.Scan() {
						logf("[msi] %s", s.Text())
					}
					if scanErr := s.Err(); scanErr != nil {
						logf("While reading msi log: %s", scanErr.Error())
					}
				}

				failed = append(failed, task.Name)
			}
		} else {
			cmd := exec.Command(commandPath, args...)
			cmd.Dir = task.WorkDir

			logf("It's an installer, launching %s", commandPath)
			logf("...with args %s", strings.Join(args, " "))
			logf("...in dir %s", cmd.Dir)

			err = cmd.Run()
			if err != nil {
				if exitError, ok := err.(*exec.ExitError); ok {
					if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
						code := status.ExitStatus()
						logf("Prereq %s has exited with code %d", task.Name, code)

						known := false
						for _, exitCode := range task.Info.ExitCodes {
							if code == exitCode.Code {
								logf("That code means: %s", exitCode.Message)
								if exitCode.Success {
									logf("...and it's harmless! Continuing...")
								} else {
									logf("...and it's fatal! We'll error out.")
									failed = append(failed, task.Name)
								}
								known = true
							}
							break
						}

						if !known {
							logf("We don't know what code %d means for %s", code, task.Name)
							logf("We'll error out eventually")
							failed = append(failed, task.Name)
						}
					} else {
						return errors.Wrap(err, 0)
					}
				} else {
					return errors.Wrap(err, 0)
				}
			}
		}

		writeState(task.Name, "done")
		logf("Done installing %s - took %s", task.Name, time.Since(taskStartTime))
	}

	if len(failed) > 0 {
		logf("Some prereqs failed to install: %s", strings.Join(failed, ", "))
	} else {
		logf("All done! Took %s", time.Since(startTime))
	}

	return nil
}

func testPrereqs(prereqs []string) {
	must(doTestPrereqs(prereqs))
}

func doTestPrereqs(prereqs []string) error {
	comm.Opf("Fetching registry...")

	baseURL := "https://dl.itch.ovh/itch-redists"

	res, err := http.Get(baseURL + "/info.json")
	if err != nil {
		return errors.Wrap(err, 0)
	}

	if res.StatusCode != 200 {
		return errors.Wrap(fmt.Errorf("While getting redist registry, got HTTP %d", res.StatusCode), 0)
	}

	dec := json.NewDecoder(res.Body)

	registry := &redist.RedistRegistry{}
	err = dec.Decode(registry)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	if len(prereqs) == 0 {
		comm.Logf("")
		comm.Statf("No prereqs specified, here are those we know about: \n")

		table := tablewriter.NewWriter(os.Stdout)
		table.SetAutoFormatHeaders(false)
		table.SetColWidth(60)
		table.SetHeader([]string{"Name", "Arch", "Description", "Version"})
		for name, info := range registry.Entries {
			table.Append([]string{name, info.Arch, info.FullName, info.Version})
		}
		table.Render()
		return nil
	}

	if len(prereqs) == 1 && prereqs[0] == "all" {
		prereqs = nil
		for name := range registry.Entries {
			prereqs = append(prereqs, name)
		}
	}

	comm.Logf("Testing out prereqs %s", strings.Join(prereqs, ", "))

	plan := &PrereqPlan{}

	tempDir := filepath.Join(os.TempDir(), "butler-test-prereqs")
	comm.Logf("Working in %s", tempDir)
	comm.Logf("(This helps not having to re-download the prereqs between runs, but feel free to wipe it)")

	err = os.MkdirAll(tempDir, 0755)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	for _, name := range prereqs {
		info, ok := registry.Entries[name]
		if !ok {
			comm.Warnf("Unknown prereq %s, skipping", name)
			continue
		}

		comm.Opf("Downloading prereq %s", name)

		workDir := filepath.Join(tempDir, name)
		err = os.MkdirAll(workDir, 0755)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		task := &PrereqTask{
			Info:    *info,
			Name:    name,
			WorkDir: workDir,
		}

		url := fmt.Sprintf("%s/%s/%s", baseURL, name, info.Command)
		dest := filepath.Join(workDir, info.Command)
		_, err = tryDl(url, dest)
		if err != nil {
			comm.Logf("Could not donwload prereq %s", name)
			return errors.Wrap(err, 0)
		}

		plan.Tasks = append(plan.Tasks, task)
	}

	planPath := filepath.Join(tempDir, "butler_install_plan.json")
	comm.Logf("Writing plan to %s", planPath)

	planContents, err := json.Marshal(plan)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	err = ioutil.WriteFile(planPath, planContents, 0644)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	comm.Opf("Handing off to install-prereqs...")

	err = doInstallPrereqs(planPath, "")
	if err != nil {
		return errors.Wrap(err, 0)
	}

	return nil
}
<|endoftext|>


<commit_before>package kuiperbelt

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestProxySendHandlerFunc__BulkSend(t *testing.T) {
	s1 := &TestSession{new(bytes.Buffer), "hogehoge", false, false}
	s2 := &TestSession{new(bytes.Buffer), "fugafuga", false, false}

	AddSession(s1)
	AddSession(s2)

	tc := TestConfig
	p := Proxy{tc}
	ts := httptest.NewServer(http.HandlerFunc(p.SendHandlerFunc))
	defer ts.Close()

	req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("test message"))
	if err != nil {
		t.Fatal("proxy handler new request unexpected error:", err)
	}
	req.Header.Add(tc.SessionHeader, "hogehoge")
	req.Header.Add(tc.SessionHeader, "fugafuga")

	client := new(http.Client)
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal("proxy handler request unexpected error:", err)
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	result := struct {
		Result string `json:"result"`
	}{}
	err = dec.Decode(&result)
	if err != nil {
		t.Fatal("proxy handler response unexpected error:", err)
	}
	if result.Result != "OK" {
		t.Fatalf("proxy handler response unexpected response: %+v", result)
	}

	if s1.String() != "test message" {
		t.Fatalf("proxy handler s1 not receive message: %s", s1.String())
	}
	if s2.String() != "test message" {
		t.Fatalf("proxy handler s2 not receive message: %s", s2.String())
	}
}

func TestProxyCloseHandlerFunc__BulkClose(t *testing.T) {
	s1 := &TestSession{new(bytes.Buffer), "hogehoge", false, false}
	s2 := &TestSession{new(bytes.Buffer), "fugafuga", false, false}

	AddSession(s1)
	AddSession(s2)

	tc := TestConfig
	p := Proxy{tc}
	ts := httptest.NewServer(http.HandlerFunc(p.CloseHandlerFunc))
	defer ts.Close()

	req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("test message"))
	if err != nil {
		t.Fatal("proxy handler new request unexpected error:", err)
	}
	req.Header.Add(tc.SessionHeader, "hogehoge")
	req.Header.Add(tc.SessionHeader, "fugafuga")

	client := new(http.Client)
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal("proxy handler request unexpected error:", err)
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	result := struct {
		Result string `json:"result"`
	}{}
	err = dec.Decode(&result)
	if err != nil {
		t.Fatal("proxy handler response unexpected error:", err)
	}
	if result.Result != "OK" {
		t.Fatalf("proxy handler response unexpected response: %+v", result)
	}

	if s1.String() != "test message" {
		t.Fatalf("proxy handler s1 is not receive message: %s", s1.String())
	}
	if s2.String() != "test message" {
		t.Fatalf("proxy handler s2 is not receive message: %s", s2.String())
	}

	if !s1.isClosed {
		t.Fatalf("proxy handler s1 is not closed")
	}
	if !s2.isClosed {
		t.Fatalf("proxy handler s1 is not closed")
	}
}
<commit_msg>add: test of sending binary frame<commit_after>package kuiperbelt

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"golang.org/x/net/websocket"
)

func TestProxySendHandlerFunc__BulkSend(t *testing.T) {
	s1 := &TestSession{new(bytes.Buffer), "hogehoge", false, false}
	s2 := &TestSession{new(bytes.Buffer), "fugafuga", false, false}

	AddSession(s1)
	AddSession(s2)

	tc := TestConfig
	p := Proxy{tc}
	ts := httptest.NewServer(http.HandlerFunc(p.SendHandlerFunc))
	defer ts.Close()

	req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("test message"))
	if err != nil {
		t.Fatal("proxy handler new request unexpected error:", err)
	}
	req.Header.Add(tc.SessionHeader, "hogehoge")
	req.Header.Add(tc.SessionHeader, "fugafuga")

	client := new(http.Client)
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal("proxy handler request unexpected error:", err)
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	result := struct {
		Result string `json:"result"`
	}{}
	err = dec.Decode(&result)
	if err != nil {
		t.Fatal("proxy handler response unexpected error:", err)
	}
	if result.Result != "OK" {
		t.Fatalf("proxy handler response unexpected response: %+v", result)
	}

	if s1.String() != "test message" {
		t.Fatalf("proxy handler s1 not receive message: %s", s1.String())
	}
	if s2.String() != "test message" {
		t.Fatalf("proxy handler s2 not receive message: %s", s2.String())
	}
}

func TestProxySendHandlerFunc__SendInBinary(t *testing.T) {
	callbackServer := new(testSuccessConnectCallbackServer)
	tcc := httptest.NewServer(http.HandlerFunc(callbackServer.SuccessHandler))

	tc := TestConfig
	tc.Callback.Connect = tcc.URL
	p := Proxy{tc}
	ts := httptest.NewServer(http.HandlerFunc(p.SendHandlerFunc))
	server := WebSocketServer{tc}
	th := httptest.NewServer(http.HandlerFunc(server.Handler))

	wsURL := strings.Replace(th.URL, "http://", "ws://", -1)
	wsConfig, err := websocket.NewConfig(wsURL, "http://localhost/")
	if err != nil {
		t.Fatal("cannot create connection config error:", err)
	}
	wsConfig.Header.Add(testRequestSessionHeader, "hogehoge")
	conn, err := websocket.DialConfig(wsConfig)
	if err != nil {
		t.Fatal("cannot connect error:", err)
	}

	io.CopyN(new(blackholeWriter), conn, int64(len([]byte("hello"))))

	codec := &websocket.Codec{
		Unmarshal: func(data []byte, payloadType byte, v interface{}) error {
			rb, _ := v.(*byte)
			*rb = payloadType
			return nil
		},
		Marshal: nil,
	}

	req, err := http.NewRequest("POST", ts.URL, bytes.NewBuffer([]byte("hogehoge")))
	if err != nil {
		t.Fatal("creadrequest unexpected error:", err)
	}
	req.Header.Add("Content-Type", "application/octet-stream")
	req.Header.Add(tc.SessionHeader, "hogehoge")
	_, err = http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal("send request unexpected error:", err)
	}

	var rb byte
	codec.Receive(conn, &rb)
	if rb != websocket.BinaryFrame {
		t.Fatal("receved message is not binary frame:", rb)
	}
}

func TestProxyCloseHandlerFunc__BulkClose(t *testing.T) {
	s1 := &TestSession{new(bytes.Buffer), "hogehoge", false, false}
	s2 := &TestSession{new(bytes.Buffer), "fugafuga", false, false}

	AddSession(s1)
	AddSession(s2)

	tc := TestConfig
	p := Proxy{tc}
	ts := httptest.NewServer(http.HandlerFunc(p.CloseHandlerFunc))
	defer ts.Close()

	req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("test message"))
	if err != nil {
		t.Fatal("proxy handler new request unexpected error:", err)
	}
	req.Header.Add(tc.SessionHeader, "hogehoge")
	req.Header.Add(tc.SessionHeader, "fugafuga")

	client := new(http.Client)
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal("proxy handler request unexpected error:", err)
	}
	defer resp.Body.Close()

	dec := json.NewDecoder(resp.Body)
	result := struct {
		Result string `json:"result"`
	}{}
	err = dec.Decode(&result)
	if err != nil {
		t.Fatal("proxy handler response unexpected error:", err)
	}
	if result.Result != "OK" {
		t.Fatalf("proxy handler response unexpected response: %+v", result)
	}

	if s1.String() != "test message" {
		t.Fatalf("proxy handler s1 is not receive message: %s", s1.String())
	}
	if s2.String() != "test message" {
		t.Fatalf("proxy handler s2 is not receive message: %s", s2.String())
	}

	if !s1.isClosed {
		t.Fatalf("proxy handler s1 is not closed")
	}
	if !s2.isClosed {
		t.Fatalf("proxy handler s1 is not closed")
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Samuel Stauffer. All rights reserved.
// Use of this source code is governed by a 3-clause BSD
// license that can be found in the LICENSE file.

package metrics

import (
	"math"
	"strconv"
	"sync/atomic"
	"time"
)

type FloatGaugeFunc func() float64

type EWMAGauge struct {
	interval       time.Duration // tick interval in seconds
	alpha          float64       // the smoothing constant
	initialized    bool
	mean           uint64 // really a float64 but using uint64 for atomicity
	ticker         *time.Ticker
	tickerStopChan chan bool
	fun            FloatGaugeFunc
}

func NewEWMAGauge(interval time.Duration, alpha float64, fun FloatGaugeFunc) *EWMAGauge {
	ewma := &EWMAGauge{
		interval:    interval,
		alpha:       alpha,
		initialized: false,
		fun:         fun,
	}
	return ewma
}

func (e *EWMAGauge) String() string {
	rate := e.Mean()
	return strconv.FormatFloat(rate, 'g', -1, 64)
}

func (e *EWMAGauge) Mean() float64 {
	return math.Float64frombits(atomic.LoadUint64(&e.mean))
}

// Start the ticker
func (e *EWMAGauge) Start() {
	if e.ticker == nil {
		e.ticker = time.NewTicker(e.interval)
		e.tickerStopChan = make(chan bool)
		go e.tickWatcher()
	}
}

// Stop the ticker
func (e *EWMAGauge) Stop() {
	if e.ticker != nil {
		e.ticker.Stop()
		close(e.tickerStopChan)
	}
}

func (e *EWMAGauge) tickWatcher() {
	defer func() {
		e.ticker.Stop()
		e.ticker = nil
		e.tickerStopChan = nil
	}()
	for {
		select {
		case _ = <-e.tickerStopChan:
			return
		case _ = <-e.ticker.C:
			e.Tick()
		}
	}
}

// Tick the moving average - NOT thread safe
func (e *EWMAGauge) Tick() {
	value := e.fun()
	mean := e.Mean()
	if e.initialized {
		mean += e.alpha * (value - mean)
	} else {
		mean = value
		e.initialized = true
	}
	atomic.StoreUint64(&e.mean, math.Float64bits(mean))
}
<commit_msg>Fix one more 64-bit alignment issue<commit_after>// Copyright 2012 Samuel Stauffer. All rights reserved.
// Use of this source code is governed by a 3-clause BSD
// license that can be found in the LICENSE file.

package metrics

import (
	"math"
	"strconv"
	"sync/atomic"
	"time"
)

type FloatGaugeFunc func() float64

type EWMAGauge struct {
	mean           uint64        // really a float64 but using uint64 for atomicity
	alpha          float64       // the smoothing constant
	interval       time.Duration // tick interval in seconds
	initialized    bool
	ticker         *time.Ticker
	tickerStopChan chan bool
	fun            FloatGaugeFunc
}

func NewEWMAGauge(interval time.Duration, alpha float64, fun FloatGaugeFunc) *EWMAGauge {
	ewma := &EWMAGauge{
		interval:    interval,
		alpha:       alpha,
		initialized: false,
		fun:         fun,
	}
	return ewma
}

func (e *EWMAGauge) String() string {
	rate := e.Mean()
	return strconv.FormatFloat(rate, 'g', -1, 64)
}

func (e *EWMAGauge) Mean() float64 {
	return math.Float64frombits(atomic.LoadUint64(&e.mean))
}

// Start the ticker
func (e *EWMAGauge) Start() {
	if e.ticker == nil {
		e.ticker = time.NewTicker(e.interval)
		e.tickerStopChan = make(chan bool)
		go e.tickWatcher()
	}
}

// Stop the ticker
func (e *EWMAGauge) Stop() {
	if e.ticker != nil {
		e.ticker.Stop()
		close(e.tickerStopChan)
	}
}

func (e *EWMAGauge) tickWatcher() {
	defer func() {
		e.ticker.Stop()
		e.ticker = nil
		e.tickerStopChan = nil
	}()
	for {
		select {
		case _ = <-e.tickerStopChan:
			return
		case _ = <-e.ticker.C:
			e.Tick()
		}
	}
}

// Tick the moving average - NOT thread safe
func (e *EWMAGauge) Tick() {
	value := e.fun()
	mean := e.Mean()
	if e.initialized {
		mean += e.alpha * (value - mean)
	} else {
		mean = value
		e.initialized = true
	}
	atomic.StoreUint64(&e.mean, math.Float64bits(mean))
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"strconv"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Events", func() {
	var c *client.Client

	BeforeEach(func() {
		var err error
		c, err = loadClient()
		Expect(err).NotTo(HaveOccurred())
	})

	It("should be sent by kubelets and the scheduler about pods scheduling and running", func() {
		provider := testContext.Provider
		if len(provider) > 0 && provider != "gce" && provider != "gke" && provider != "aws" {
			By(fmt.Sprintf("skipping TestKubeletSendsEvent on cloud provider %s", provider))
			return
		}

		podClient := c.Pods(api.NamespaceDefault)

		By("creating the pod")
		name := "send-events-" + string(util.NewUUID())
		value := strconv.Itoa(time.Now().Nanosecond())
		pod := &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: name,
				Labels: map[string]string{
					"name": "foo",
					"time": value,
				},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "p",
						Image: "gcr.io/google_containers/serve_hostname:1.1",
						Ports: []api.ContainerPort{{ContainerPort: 80}},
					},
				},
			},
		}

		By("submitting the pod to kubernetes")
		defer func() {
			By("deleting the pod")
			podClient.Delete(pod.Name)
		}()
		if _, err := podClient.Create(pod); err != nil {
			Failf("Failed to create pod: %v", err)
		}

		expectNoError(waitForPodRunning(c, pod.Name))

		By("verifying the pod is in kubernetes")
		pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
		Expect(len(pods.Items)).To(Equal(1))

		By("retrieving the pod")
		podWithUid, err := podClient.Get(pod.Name)
		if err != nil {
			Failf("Failed to get pod: %v", err)
		}
		fmt.Printf("%+v\n", podWithUid)

		// Check for scheduler event about the pod.
		By("checking for scheduler event about the pod")
		events, err := c.Events(api.NamespaceDefault).List(
			labels.Everything(),
			fields.Set{
				"involvedObject.kind":      "Pod",
				"involvedObject.uid":       string(podWithUid.UID),
				"involvedObject.namespace": api.NamespaceDefault,
				"source":                   "scheduler",
			}.AsSelector(),
		)
		if err != nil {
			Failf("Error while listing events: %v", err)
		}
		Expect(len(events.Items)).ToNot(BeZero(), "scheduler events from running pod")
		fmt.Println("Saw scheduler event for our pod.")

		// Check for kubelet event about the pod.
		By("checking for kubelet event about the pod")
		events, err = c.Events(api.NamespaceDefault).List(
			labels.Everything(),
			fields.Set{
				"involvedObject.uid":       string(podWithUid.UID),
				"involvedObject.kind":      "Pod",
				"involvedObject.namespace": api.NamespaceDefault,
				"source":                   "kubelet",
			}.AsSelector(),
		)
		if err != nil {
			Failf("Error while listing events: %v", err)
		}
		Expect(len(events.Items)).ToNot(BeZero(), "kubelet events from running pod")
		fmt.Println("Saw kubelet event for our pod.")
	})
})
<commit_msg>Enable 'Events' tests for all providers, we need it for local<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package e2e

import (
	"fmt"
	"strconv"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Events", func() {
	var c *client.Client

	BeforeEach(func() {
		var err error
		c, err = loadClient()
		Expect(err).NotTo(HaveOccurred())
	})

	It("should be sent by kubelets and the scheduler about pods scheduling and running", func() {

		podClient := c.Pods(api.NamespaceDefault)

		By("creating the pod")
		name := "send-events-" + string(util.NewUUID())
		value := strconv.Itoa(time.Now().Nanosecond())
		pod := &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: name,
				Labels: map[string]string{
					"name": "foo",
					"time": value,
				},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "p",
						Image: "gcr.io/google_containers/serve_hostname:1.1",
						Ports: []api.ContainerPort{{ContainerPort: 80}},
					},
				},
			},
		}

		By("submitting the pod to kubernetes")
		defer func() {
			By("deleting the pod")
			podClient.Delete(pod.Name)
		}()
		if _, err := podClient.Create(pod); err != nil {
			Failf("Failed to create pod: %v", err)
		}

		expectNoError(waitForPodRunning(c, pod.Name))

		By("verifying the pod is in kubernetes")
		pods, err := podClient.List(labels.SelectorFromSet(labels.Set(map[string]string{"time": value})))
		Expect(len(pods.Items)).To(Equal(1))

		By("retrieving the pod")
		podWithUid, err := podClient.Get(pod.Name)
		if err != nil {
			Failf("Failed to get pod: %v", err)
		}
		fmt.Printf("%+v\n", podWithUid)

		// Check for scheduler event about the pod.
		By("checking for scheduler event about the pod")
		events, err := c.Events(api.NamespaceDefault).List(
			labels.Everything(),
			fields.Set{
				"involvedObject.kind":      "Pod",
				"involvedObject.uid":       string(podWithUid.UID),
				"involvedObject.namespace": api.NamespaceDefault,
				"source":                   "scheduler",
			}.AsSelector(),
		)
		if err != nil {
			Failf("Error while listing events: %v", err)
		}
		Expect(len(events.Items)).ToNot(BeZero(), "scheduler events from running pod")
		fmt.Println("Saw scheduler event for our pod.")

		// Check for kubelet event about the pod.
		By("checking for kubelet event about the pod")
		events, err = c.Events(api.NamespaceDefault).List(
			labels.Everything(),
			fields.Set{
				"involvedObject.uid":       string(podWithUid.UID),
				"involvedObject.kind":      "Pod",
				"involvedObject.namespace": api.NamespaceDefault,
				"source":                   "kubelet",
			}.AsSelector(),
		)
		if err != nil {
			Failf("Error while listing events: %v", err)
		}
		Expect(len(events.Items)).ToNot(BeZero(), "kubelet events from running pod")
		fmt.Println("Saw kubelet event for our pod.")
	})
})
<|endoftext|>


<commit_before>package actions

import (
	"github.com/containrrr/watchtower/internal/util"
	"github.com/containrrr/watchtower/pkg/container"
	"github.com/deckarep/golang-set"
	log "github.com/sirupsen/logrus"
)

// Update looks at the running Docker containers to see if any of the images
// used to start those containers have been updated. If a change is detected in
// any of the images, the associated containers are stopped and restarted with
// the new image.
func Update(client container.Client, params UpdateParams) error {
	log.Debug("Checking containers for updated images")

	executePreCheck(client, params)

	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return err
	}

	for i, container := range containers {
		stale, err := client.IsContainerStale(container)
		if err != nil {
			log.Infof("Unable to update container %s. Proceeding to next.", containers[i].Name())
			log.Debug(err)
			stale = false
		}
		containers[i].Stale = stale
	}

	containers, err = container.SortByDependencies(containers)
	if err != nil {
		return err
	}

	checkDependencies(containers)

	if params.MonitorOnly {
		executePostCheck(client, params)
		return nil
	}

	stopContainersInReversedOrder(containers, client, params)
	restartContainersInSortedOrder(containers, client, params)

	executePostCheck(client, params)
	return nil
}

func stopContainersInReversedOrder(containers []container.Container, client container.Client, params UpdateParams) {
	for i := len(containers) - 1; i >= 0; i-- {
		stopStaleContainer(containers[i], client, params)
	}
}

func stopStaleContainer(container container.Container, client container.Client, params UpdateParams) {
	if container.IsWatchtower() {
		log.Debugf("This is the watchtower container %s", container.Name())
		return
	}

	if !container.Stale {
		return
	}

	executePreUpdateCommand(client, container)

	if err := client.StopContainer(container, params.Timeout); err != nil {
		log.Error(err)
	}
}

func restartContainersInSortedOrder(containers []container.Container, client container.Client, params UpdateParams) {
	toDelete := mapset.NewSet()
	for _, container := range containers {
		if !container.Stale {
			continue
		}
		restartStaleContainer(container, client, params)
		toDelete.Add(container)
	}
	if params.Cleanup {
		iterator := toDelete.Iterator()
		for c := range iterator.C {
			cont := c.(container.Container)
			if err := client.RemoveImage(cont); err != nil {
				log.Error(err)
			}
		}
	}
}

func restartStaleContainer(container container.Container, client container.Client, params UpdateParams) {
	// Since we can't shutdown a watchtower container immediately, we need to
	// start the new one while the old one is still running. This prevents us
	// from re-using the same container name so we first rename the current
	// instance so that the new one can adopt the old name.
	if container.IsWatchtower() {
		if err := client.RenameContainer(container, util.RandName()); err != nil {
			log.Error(err)
			return
		}
	}

	if !params.NoRestart {
		if newContainerID, err := client.StartContainer(container); err != nil {
			log.Error(err)
		} else if container.Stale && params.LifecycleHooks {
			executePostUpdateCommand(client, newContainerID)
		}
	}
}

func checkDependencies(containers []container.Container) {

	for i, parent := range containers {
		if parent.ToRestart() {
			continue
		}

	LinkLoop:
		for _, linkName := range parent.Links() {
			for _, child := range containers {
				if child.Name() == linkName && child.ToRestart() {
					containers[i].Linked = true
					break LinkLoop
				}
			}
		}
	}
}

func executePreCheck(client container.Client, params UpdateParams) {
	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return
	}
	for _, container := range containers {
		executePreCheckCommand(client, container)
	}
}

func executePostCheck(client container.Client, params UpdateParams) {
	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return
	}
	for _, container := range containers {
		executePostCheckCommand(client, container)
	}
}

func executePreCheckCommand(client container.Client, container container.Container) {
	command := container.GetLifecyclePreCheckCommand()
	if len(command) == 0 {
		log.Debug("No pre-check command supplied. Skipping")
		return
	}

	log.Info("Executing pre-check command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePostCheckCommand(client container.Client, container container.Container) {
	command := container.GetLifecyclePostCheckCommand()
	if len(command) == 0 {
		log.Debug("No post-check command supplied. Skipping")
		return
	}

	log.Info("Executing post-check command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePreUpdateCommand(client container.Client, container container.Container) {

	command := container.GetLifecyclePreUpdateCommand()
	if len(command) == 0 {
		log.Debug("No pre-update command supplied. Skipping")
		return
	}

	log.Info("Executing pre-update command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePostUpdateCommand(client container.Client, newContainerID string) {
	newContainer, err := client.GetContainer(newContainerID)
	if err != nil {
		log.Error(err)
		return
	}

	command := newContainer.GetLifecyclePostUpdateCommand()
	if len(command) == 0 {
		log.Debug("No post-update command supplied. Skipping")
		return
	}

	log.Info("Executing post-update command.")
	if err := client.ExecuteCommand(newContainerID, command); err != nil {
		log.Error(err)
	}
}
<commit_msg>proper set implementation<commit_after>package actions

import (
	"github.com/containrrr/watchtower/internal/util"
	"github.com/containrrr/watchtower/pkg/container"
	log "github.com/sirupsen/logrus"
)

// Update looks at the running Docker containers to see if any of the images
// used to start those containers have been updated. If a change is detected in
// any of the images, the associated containers are stopped and restarted with
// the new image.
func Update(client container.Client, params UpdateParams) error {
	log.Debug("Checking containers for updated images")

	executePreCheck(client, params)

	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return err
	}

	for i, container := range containers {
		stale, err := client.IsContainerStale(container)
		if err != nil {
			log.Infof("Unable to update container %s. Proceeding to next.", containers[i].Name())
			log.Debug(err)
			stale = false
		}
		containers[i].Stale = stale
	}

	containers, err = container.SortByDependencies(containers)
	if err != nil {
		return err
	}

	checkDependencies(containers)

	if params.MonitorOnly {
		executePostCheck(client, params)
		return nil
	}

	stopContainersInReversedOrder(containers, client, params)
	restartContainersInSortedOrder(containers, client, params)

	executePostCheck(client, params)
	return nil
}

func stopContainersInReversedOrder(containers []container.Container, client container.Client, params UpdateParams) {
	for i := len(containers) - 1; i >= 0; i-- {
		stopStaleContainer(containers[i], client, params)
	}
}

func stopStaleContainer(container container.Container, client container.Client, params UpdateParams) {
	if container.IsWatchtower() {
		log.Debugf("This is the watchtower container %s", container.Name())
		return
	}

	if !container.Stale {
		return
	}

	executePreUpdateCommand(client, container)

	if err := client.StopContainer(container, params.Timeout); err != nil {
		log.Error(err)
	}
}

func restartContainersInSortedOrder(containers []container.Container, client container.Client, params UpdateParams) {
	toDelete := make(map[container.Container]bool)
	for _, container := range containers {
		if !container.Stale {
			continue
		}
		restartStaleContainer(container, client, params)
		toDelete[container] = true
	}
	if params.Cleanup {
		for cont := range toDelete {
			if err := client.RemoveImage(cont); err != nil {
				log.Error(err)
			}
		}
	}
}

func restartStaleContainer(container container.Container, client container.Client, params UpdateParams) {
	// Since we can't shutdown a watchtower container immediately, we need to
	// start the new one while the old one is still running. This prevents us
	// from re-using the same container name so we first rename the current
	// instance so that the new one can adopt the old name.
	if container.IsWatchtower() {
		if err := client.RenameContainer(container, util.RandName()); err != nil {
			log.Error(err)
			return
		}
	}

	if !params.NoRestart {
		if newContainerID, err := client.StartContainer(container); err != nil {
			log.Error(err)
		} else if container.Stale && params.LifecycleHooks {
			executePostUpdateCommand(client, newContainerID)
		}
	}
}

func checkDependencies(containers []container.Container) {

	for i, parent := range containers {
		if parent.ToRestart() {
			continue
		}

	LinkLoop:
		for _, linkName := range parent.Links() {
			for _, child := range containers {
				if child.Name() == linkName && child.ToRestart() {
					containers[i].Linked = true
					break LinkLoop
				}
			}
		}
	}
}

func executePreCheck(client container.Client, params UpdateParams) {
	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return
	}
	for _, container := range containers {
		executePreCheckCommand(client, container)
	}
}

func executePostCheck(client container.Client, params UpdateParams) {
	containers, err := client.ListContainers(params.Filter)
	if err != nil {
		return
	}
	for _, container := range containers {
		executePostCheckCommand(client, container)
	}
}

func executePreCheckCommand(client container.Client, container container.Container) {
	command := container.GetLifecyclePreCheckCommand()
	if len(command) == 0 {
		log.Debug("No pre-check command supplied. Skipping")
		return
	}

	log.Info("Executing pre-check command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePostCheckCommand(client container.Client, container container.Container) {
	command := container.GetLifecyclePostCheckCommand()
	if len(command) == 0 {
		log.Debug("No post-check command supplied. Skipping")
		return
	}

	log.Info("Executing post-check command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePreUpdateCommand(client container.Client, container container.Container) {

	command := container.GetLifecyclePreUpdateCommand()
	if len(command) == 0 {
		log.Debug("No pre-update command supplied. Skipping")
		return
	}

	log.Info("Executing pre-update command.")
	if err := client.ExecuteCommand(container.ID(), command); err != nil {
		log.Error(err)
	}
}

func executePostUpdateCommand(client container.Client, newContainerID string) {
	newContainer, err := client.GetContainer(newContainerID)
	if err != nil {
		log.Error(err)
		return
	}

	command := newContainer.GetLifecyclePostUpdateCommand()
	if len(command) == 0 {
		log.Debug("No post-update command supplied. Skipping")
		return
	}

	log.Info("Executing post-update command.")
	if err := client.ExecuteCommand(newContainerID, command); err != nil {
		log.Error(err)
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package ast

import (
	"io/ioutil"
	"testing"

	"github.com/issue9/assert"

	"github.com/caixw/apidoc/v7/core"
	"github.com/caixw/apidoc/v7/core/messagetest"
	"github.com/caixw/apidoc/v7/internal/token"
)

func loadAPIDoc(a *assert.Assertion) *APIDoc {
	data, err := ioutil.ReadFile("./testdata/doc.xml")
	a.NotError(err).NotNil(data)

	doc := &APIDoc{}
	a.NotNil(doc)

	rslt := messagetest.NewMessageHandler()
	doc.Parse(rslt.Handler, core.Block{
		Location: core.Location{
			URI:   "doc.xml",
			Range: core.Range{},
		},
		Data: data,
	})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)

	return doc
}

func TestAPIDoc(t *testing.T) {
	a := assert.New(t)
	doc := loadAPIDoc(a)

	a.Equal(doc.BaseTag, token.BaseTag{
		Base: token.Base{
			UsageKey: "usage-apidoc",
			Range: core.Range{
				Start: core.Position{Character: 0, Line: 2},
				End:   core.Position{Character: 9, Line: 35},
			},
		},
		StartTag: token.String{
			Range: core.Range{
				Start: core.Position{Character: 1, Line: 2},
				End:   core.Position{Character: 7, Line: 2},
			},
			Value: "apidoc",
		},
		EndTag: token.String{
			Range: core.Range{
				Start: core.Position{Character: 2, Line: 35},
				End:   core.Position{Character: 8, Line: 35},
			},
			Value: "apidoc",
		},
	})

	a.Equal(doc.Version, &VersionAttribute{
		BaseAttribute: token.BaseAttribute{
			Base: token.Base{
				UsageKey: "usage-apidoc-version",
				Range: core.Range{
					Start: core.Position{Character: 8, Line: 2},
					End:   core.Position{Character: 23, Line: 2},
				},
			},
			AttributeName: token.String{
				Range: core.Range{
					Start: core.Position{Character: 8, Line: 2},
					End:   core.Position{Character: 15, Line: 2},
				},
				Value: "version",
			},
		},

		Value: token.String{
			Range: core.Range{
				Start: core.Position{Character: 17, Line: 2},
				End:   core.Position{Character: 22, Line: 2},
			},
			Value: "1.1.1",
		},
	})

	a.Equal(len(doc.Tags), 2)
	tag := &Tag{
		BaseTag: token.BaseTag{
			Base: token.Base{
				UsageKey: "usage-apidoc-tags",
				Range: core.Range{
					Start: core.Position{Character: 4, Line: 10},
					End:   core.Position{Character: 47, Line: 10},
				},
			},
			StartTag: token.String{
				Range: core.Range{
					Start: core.Position{Character: 5, Line: 10},
					End:   core.Position{Character: 8, Line: 10},
				},
				Value: "tag",
			},
		},
		Name: &Attribute{
			BaseAttribute: token.BaseAttribute{
				Base: token.Base{
					UsageKey: "usage-tag-name",
					Range: core.Range{
						Start: core.Position{Character: 9, Line: 10},
						End:   core.Position{Character: 20, Line: 10},
					},
				},
				AttributeName: token.String{
					Range: core.Range{
						Start: core.Position{Character: 9, Line: 10},
						End:   core.Position{Character: 13, Line: 10},
					},
					Value: "name",
				},
			},
			Value: token.String{
				Range: core.Range{
					Start: core.Position{Character: 15, Line: 10},
					End:   core.Position{Character: 19, Line: 10},
				},
				Value: "tag1",
			},
		},
		Title: &Attribute{
			BaseAttribute: token.BaseAttribute{
				Base: token.Base{
					UsageKey: "usage-tag-title",
					Range: core.Range{
						Start: core.Position{Character: 21, Line: 10},
						End:   core.Position{Character: 44, Line: 10},
					},
				},
				AttributeName: token.String{
					Range: core.Range{
						Start: core.Position{Character: 21, Line: 10},
						End:   core.Position{Character: 26, Line: 10},
					},
					Value: "title",
				},
			},
			Value: token.String{
				Range: core.Range{
					Start: core.Position{Character: 28, Line: 10},
					End:   core.Position{Character: 43, Line: 10},
				},
				Value: "tag description",
			},
		},
	}
	a.Equal(doc.Tags[0], tag)

	tag = doc.Tags[1]
	a.Equal(tag.Deprecated.V(), "1.0.1").
		Empty(tag.EndTag.Value).
		Equal(tag.UsageKey, "usage-apidoc-tags")

	a.Equal(2, len(doc.Servers))
	srv := doc.Servers[0]
	a.Equal(srv.Name.V(), "admin").
		Equal(srv.URL.V(), "https://api.example.com/admin").
		Nil(srv.Description).
		Equal(srv.Summary.V(), "admin api")

	desc := "\n
client api
\n        "
	srv = doc.Servers[1]
	a.Equal(srv.Name.V(), "client").
		Equal(srv.URL.V(), "https://api.example.com/client").
		Equal(srv.Deprecated.V(), "1.0.1").
		Equal(srv.Description.V(), desc, "%d,%d\nv1=%v\nv2=%v", len(srv.Description.V()), len(desc), srv.Description.V(), desc)

	a.NotNil(doc.License).
		Equal(doc.License.Text.V(), "MIT").
		Equal(doc.License.URL.V(), "https://opensource.org/licenses/MIT")

	a.NotNil(doc.Contact).
		Equal(doc.Contact.Name.V(), "test").
		Equal(doc.Contact.URL.V(), "https://example.com").
		Equal(doc.Contact.Email.V(), "test@example.com")

	a.Contains(doc.Description.V(), "
h2
").
		NotContains(doc.Description.V(), "</description>")

	a.True(doc.tagExists("tag1")).
		False(doc.tagExists("not-exists"))

	a.True(doc.serverExists("admin")).
		False(doc.serverExists("not-exists"))

	a.Equal(2, len(doc.Mimetypes)).
		Equal("application/xml", doc.Mimetypes[0].Content.Value)
}

func TestAPIDoc_all(t *testing.T) {
	a := assert.New(t)

	data, err := ioutil.ReadFile("./testdata/all.xml")
	a.NotError(err).NotNil(data)
	rslt := messagetest.NewMessageHandler()
	doc := &APIDoc{}
	doc.Parse(rslt.Handler, core.Block{Data: data})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)

	a.Equal(doc.Version.V(), "1.1.1").False(doc.Version.AttributeName.IsEmpty())

	a.Equal(len(doc.Tags), 2)
	tag := doc.Tags[0]
	a.Equal(tag.Name.V(), "tag1").
		NotEmpty(tag.Title.V())
	tag = doc.Tags[1]
	a.Equal(tag.Deprecated.V(), "1.0.1").
		Equal(tag.Name.V(), "tag2")

	a.Equal(2, len(doc.Servers))
	srv := doc.Servers[0]
	a.Equal(srv.Name.V(), "admin").
		Equal(srv.URL.V(), "https://api.example.com/admin").
		Nil(srv.Description)

	a.True(doc.tagExists("tag1")).
		False(doc.tagExists("not-exists"))

	a.True(doc.serverExists("admin")).
		False(doc.serverExists("not-exists"))

	a.Equal(2, len(doc.Mimetypes)).
		Equal("application/xml", doc.Mimetypes[0].Content.Value)

	// api
	a.Equal(1, len(doc.APIs))
}

func loadAPI(a *assert.Assertion) *API {
	doc := loadAPIDoc(a)

	data, err := ioutil.ReadFile("./testdata/api.xml")
	a.NotError(err).NotNil(data)

	rslt := messagetest.NewMessageHandler()
	doc.Parse(rslt.Handler, core.Block{Data: data})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)
	return doc.APIs[0]
}

func TestAPI(t *testing.T) {
	a := assert.New(t)
	api := loadAPI(a)

	a.Equal(api.Version.V(), "1.1.0").
		Equal(2, len(api.Tags))

	a.Equal(len(api.Responses), 2)
	resp := api.Responses[0]
	a.Equal(resp.Mimetype.V(), "json").
		Equal(resp.Status.V(), 200).
		Equal(resp.Type.V(), TypeObject).
		Equal(len(resp.Items), 3)
	sex := resp.Items[1]
	a.Equal(sex.Type.V(), TypeString).
		Equal(sex.Default.V(), "male").
		Equal(len(sex.Enums), 2)
	example := resp.Examples[0]
	a.Equal(example.Mimetype.V(), "json").
		NotEmpty(example.Content.Value.Value)

	a.Equal(1, len(api.Requests))
	req := api.Requests[0]
	a.Equal(req.Mimetype.V(), "json").
		Equal(req.Headers[0].Name.V(), "authorization")

	// callback
	cb := api.Callback
	a.Equal(cb.Method.V(), "POST").
		Equal(cb.Requests[0].Type.V(), TypeObject).
		Equal(cb.Requests[0].Mimetype.V(), "json").
		Equal(cb.Responses[0].Status.V(), 200)
}

func TestAPIDoc_DeleteURI(t *testing.T) {
	a := assert.New(t)

	d := &APIDoc{}
	d.APIDoc = &APIDocVersionAttribute{Value: token.String{Value: "1.0.0"}}
	d.URI = core.URI("uri1")
	d.APIs = []*API{
		{
			URI: core.URI("uri1"),
		},
		{
			URI: core.URI("uri2"),
		},
		{
			URI: core.URI("uri3"),
		},
	}

	d.DeleteURI(core.URI("uri3"))
	a.Equal(2, len(d.APIs)).NotNil(d.APIDoc)

	d.DeleteURI(core.URI("uri1"))
	a.Equal(1, len(d.APIs)).Nil(d.APIDoc)

	d.DeleteURI(core.URI("uri2"))
	a.Equal(0, len(d.APIs)).Nil(d.APIDoc)
}

func TestRequest_Param(t *testing.T) {
	a := assert.New(t)

	var req *Request
	a.Nil(req.Param())

	req = &Request{Type: &TypeAttribute{Value: token.String{Value: TypeObject}}}
	param := req.Param()
	a.Equal(req.Type, param.Type)
}
<commit_msg>ci: 仅 action 中 windows 测试错误<commit_after>// SPDX-License-Identifier: MIT

package ast

import (
	"io/ioutil"
	"testing"

	"github.com/issue9/assert"

	"github.com/caixw/apidoc/v7/core"
	"github.com/caixw/apidoc/v7/core/messagetest"
	"github.com/caixw/apidoc/v7/internal/token"
)

func loadAPIDoc(a *assert.Assertion) *APIDoc {
	data, err := ioutil.ReadFile("./testdata/doc.xml")
	a.NotError(err).NotNil(data)

	doc := &APIDoc{}
	a.NotNil(doc)

	rslt := messagetest.NewMessageHandler()
	doc.Parse(rslt.Handler, core.Block{
		Location: core.Location{
			URI:   "doc.xml",
			Range: core.Range{},
		},
		Data: data,
	})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)

	return doc
}

func TestAPIDoc(t *testing.T) {
	a := assert.New(t)
	doc := loadAPIDoc(a)

	a.Equal(doc.BaseTag, token.BaseTag{
		Base: token.Base{
			UsageKey: "usage-apidoc",
			Range: core.Range{
				Start: core.Position{Character: 0, Line: 2},
				End:   core.Position{Character: 9, Line: 35},
			},
		},
		StartTag: token.String{
			Range: core.Range{
				Start: core.Position{Character: 1, Line: 2},
				End:   core.Position{Character: 7, Line: 2},
			},
			Value: "apidoc",
		},
		EndTag: token.String{
			Range: core.Range{
				Start: core.Position{Character: 2, Line: 35},
				End:   core.Position{Character: 8, Line: 35},
			},
			Value: "apidoc",
		},
	})

	a.Equal(doc.Version, &VersionAttribute{
		BaseAttribute: token.BaseAttribute{
			Base: token.Base{
				UsageKey: "usage-apidoc-version",
				Range: core.Range{
					Start: core.Position{Character: 8, Line: 2},
					End:   core.Position{Character: 23, Line: 2},
				},
			},
			AttributeName: token.String{
				Range: core.Range{
					Start: core.Position{Character: 8, Line: 2},
					End:   core.Position{Character: 15, Line: 2},
				},
				Value: "version",
			},
		},

		Value: token.String{
			Range: core.Range{
				Start: core.Position{Character: 17, Line: 2},
				End:   core.Position{Character: 22, Line: 2},
			},
			Value: "1.1.1",
		},
	})

	a.Equal(len(doc.Tags), 2)
	tag := &Tag{
		BaseTag: token.BaseTag{
			Base: token.Base{
				UsageKey: "usage-apidoc-tags",
				Range: core.Range{
					Start: core.Position{Character: 4, Line: 10},
					End:   core.Position{Character: 47, Line: 10},
				},
			},
			StartTag: token.String{
				Range: core.Range{
					Start: core.Position{Character: 5, Line: 10},
					End:   core.Position{Character: 8, Line: 10},
				},
				Value: "tag",
			},
		},
		Name: &Attribute{
			BaseAttribute: token.BaseAttribute{
				Base: token.Base{
					UsageKey: "usage-tag-name",
					Range: core.Range{
						Start: core.Position{Character: 9, Line: 10},
						End:   core.Position{Character: 20, Line: 10},
					},
				},
				AttributeName: token.String{
					Range: core.Range{
						Start: core.Position{Character: 9, Line: 10},
						End:   core.Position{Character: 13, Line: 10},
					},
					Value: "name",
				},
			},
			Value: token.String{
				Range: core.Range{
					Start: core.Position{Character: 15, Line: 10},
					End:   core.Position{Character: 19, Line: 10},
				},
				Value: "tag1",
			},
		},
		Title: &Attribute{
			BaseAttribute: token.BaseAttribute{
				Base: token.Base{
					UsageKey: "usage-tag-title",
					Range: core.Range{
						Start: core.Position{Character: 21, Line: 10},
						End:   core.Position{Character: 44, Line: 10},
					},
				},
				AttributeName: token.String{
					Range: core.Range{
						Start: core.Position{Character: 21, Line: 10},
						End:   core.Position{Character: 26, Line: 10},
					},
					Value: "title",
				},
			},
			Value: token.String{
				Range: core.Range{
					Start: core.Position{Character: 28, Line: 10},
					End:   core.Position{Character: 43, Line: 10},
				},
				Value: "tag description",
			},
		},
	}
	a.Equal(doc.Tags[0], tag)

	tag = doc.Tags[1]
	a.Equal(tag.Deprecated.V(), "1.0.1").
		Empty(tag.EndTag.Value).
		Equal(tag.UsageKey, "usage-apidoc-tags")

	a.Equal(2, len(doc.Servers))
	srv := doc.Servers[0]
	a.Equal(srv.Name.V(), "admin").
		Equal(srv.URL.V(), "https://api.example.com/admin").
		Nil(srv.Description).
		Equal(srv.Summary.V(), "admin api")

	srv = doc.Servers[1]
	a.Equal(srv.Name.V(), "client").
		Equal(srv.URL.V(), "https://api.example.com/client").
		Equal(srv.Deprecated.V(), "1.0.1").
		Equal(srv.Description.V(), "\n
client api
\n        ")

	desc2 := []rune(srv.Description.V())
	desc := "\n
client api
\n        "
	for i, b := range desc {
		b2 := desc2[i]
		if b != b2 {
			println("not equal", i, b, b2)
		}
	}

	a.NotNil(doc.License).
		Equal(doc.License.Text.V(), "MIT").
		Equal(doc.License.URL.V(), "https://opensource.org/licenses/MIT")

	a.NotNil(doc.Contact).
		Equal(doc.Contact.Name.V(), "test").
		Equal(doc.Contact.URL.V(), "https://example.com").
		Equal(doc.Contact.Email.V(), "test@example.com")

	a.Contains(doc.Description.V(), "
h2
").
		NotContains(doc.Description.V(), "</description>")

	a.True(doc.tagExists("tag1")).
		False(doc.tagExists("not-exists"))

	a.True(doc.serverExists("admin")).
		False(doc.serverExists("not-exists"))

	a.Equal(2, len(doc.Mimetypes)).
		Equal("application/xml", doc.Mimetypes[0].Content.Value)
}

func TestAPIDoc_all(t *testing.T) {
	a := assert.New(t)

	data, err := ioutil.ReadFile("./testdata/all.xml")
	a.NotError(err).NotNil(data)
	rslt := messagetest.NewMessageHandler()
	doc := &APIDoc{}
	doc.Parse(rslt.Handler, core.Block{Data: data})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)

	a.Equal(doc.Version.V(), "1.1.1").False(doc.Version.AttributeName.IsEmpty())

	a.Equal(len(doc.Tags), 2)
	tag := doc.Tags[0]
	a.Equal(tag.Name.V(), "tag1").
		NotEmpty(tag.Title.V())
	tag = doc.Tags[1]
	a.Equal(tag.Deprecated.V(), "1.0.1").
		Equal(tag.Name.V(), "tag2")

	a.Equal(2, len(doc.Servers))
	srv := doc.Servers[0]
	a.Equal(srv.Name.V(), "admin").
		Equal(srv.URL.V(), "https://api.example.com/admin").
		Nil(srv.Description)

	a.True(doc.tagExists("tag1")).
		False(doc.tagExists("not-exists"))

	a.True(doc.serverExists("admin")).
		False(doc.serverExists("not-exists"))

	a.Equal(2, len(doc.Mimetypes)).
		Equal("application/xml", doc.Mimetypes[0].Content.Value)

	// api
	a.Equal(1, len(doc.APIs))
}

func loadAPI(a *assert.Assertion) *API {
	doc := loadAPIDoc(a)

	data, err := ioutil.ReadFile("./testdata/api.xml")
	a.NotError(err).NotNil(data)

	rslt := messagetest.NewMessageHandler()
	doc.Parse(rslt.Handler, core.Block{Data: data})
	rslt.Handler.Stop()
	a.Empty(rslt.Errors)
	return doc.APIs[0]
}

func TestAPI(t *testing.T) {
	a := assert.New(t)
	api := loadAPI(a)

	a.Equal(api.Version.V(), "1.1.0").
		Equal(2, len(api.Tags))

	a.Equal(len(api.Responses), 2)
	resp := api.Responses[0]
	a.Equal(resp.Mimetype.V(), "json").
		Equal(resp.Status.V(), 200).
		Equal(resp.Type.V(), TypeObject).
		Equal(len(resp.Items), 3)
	sex := resp.Items[1]
	a.Equal(sex.Type.V(), TypeString).
		Equal(sex.Default.V(), "male").
		Equal(len(sex.Enums), 2)
	example := resp.Examples[0]
	a.Equal(example.Mimetype.V(), "json").
		NotEmpty(example.Content.Value.Value)

	a.Equal(1, len(api.Requests))
	req := api.Requests[0]
	a.Equal(req.Mimetype.V(), "json").
		Equal(req.Headers[0].Name.V(), "authorization")

	// callback
	cb := api.Callback
	a.Equal(cb.Method.V(), "POST").
		Equal(cb.Requests[0].Type.V(), TypeObject).
		Equal(cb.Requests[0].Mimetype.V(), "json").
		Equal(cb.Responses[0].Status.V(), 200)
}

func TestAPIDoc_DeleteURI(t *testing.T) {
	a := assert.New(t)

	d := &APIDoc{}
	d.APIDoc = &APIDocVersionAttribute{Value: token.String{Value: "1.0.0"}}
	d.URI = core.URI("uri1")
	d.APIs = []*API{
		{
			URI: core.URI("uri1"),
		},
		{
			URI: core.URI("uri2"),
		},
		{
			URI: core.URI("uri3"),
		},
	}

	d.DeleteURI(core.URI("uri3"))
	a.Equal(2, len(d.APIs)).NotNil(d.APIDoc)

	d.DeleteURI(core.URI("uri1"))
	a.Equal(1, len(d.APIs)).Nil(d.APIDoc)

	d.DeleteURI(core.URI("uri2"))
	a.Equal(0, len(d.APIs)).Nil(d.APIDoc)
}

func TestRequest_Param(t *testing.T) {
	a := assert.New(t)

	var req *Request
	a.Nil(req.Param())

	req = &Request{Type: &TypeAttribute{Value: token.String{Value: TypeObject}}}
	param := req.Param()
	a.Equal(req.Type, param.Type)
}
<|endoftext|>


<commit_before>package clip

import (
	"testing"

	"github.com/tidwall/geojson"
	"github.com/tidwall/geojson/geometry"
)

func LO(points []geometry.Point) *geojson.LineString {
	return geojson.NewLineString(geometry.NewLine(points, nil))
}

func RO(minX, minY, maxX, maxY float64) *geojson.Rect {
	return geojson.NewRect(geometry.Rect{
		Min: geometry.Point{X: minX, Y: minY},
		Max: geometry.Point{X: maxX, Y: maxY},
	})
}

func PPO(exterior []geometry.Point, holes [][]geometry.Point) *geojson.Polygon {
	return geojson.NewPolygon(geometry.NewPoly(exterior, holes, nil))
}

func TestClipLineStringSimple(t *testing.T) {
	ls := LO([]geometry.Point{
		{X: 1, Y: 1},
		{X: 2, Y: 2},
		{X: 3, Y: 1}})
	clipped := Clip(ls, RO(1.5, 0.5, 2.5, 1.8), nil)
	cl, ok := clipped.(*geojson.MultiLineString)
	if !ok {
		t.Fatal("wrong type")
	}
	if len(cl.Children()) != 2 {
		t.Fatal("result must have two parts in MultiString")
	}
}

func TestClipPolygonSimple(t *testing.T) {
	exterior := []geometry.Point{
		{X: 2, Y: 2},
		{X: 1, Y: 2},
		{X: 1.5, Y: 1.5},
		{X: 1, Y: 1},
		{X: 2, Y: 1},
		{X: 2, Y: 2},
	}
	holes := [][]geometry.Point{
		[]geometry.Point{
			{X: 1.9, Y: 1.9},
			{X: 1.2, Y: 1.9},
			{X: 1.45, Y: 1.65},
			{X: 1.9, Y: 1.5},
			{X: 1.9, Y: 1.9},
		},
	}
	polygon := PPO(exterior, holes)
	clipped := Clip(polygon, RO(1.3, 1.3, 1.4, 2.15), nil)
	cp, ok := clipped.(*geojson.Polygon)
	if !ok {
		t.Fatal("wrong type")
	}
	if cp.Base().Exterior.Empty() {
		t.Fatal("Empty result.")
	}
	if len(cp.Base().Holes) != 1 {
		t.Fatal("result must be a two-ring Polygon")
	}
}

func TestClipPolygon2(t *testing.T) {
	exterior := []geometry.Point{
		{X: 2, Y: 2},
		{X: 1, Y: 2},
		{X: 1.5, Y: 1.5},
		{X: 1, Y: 1},
		{X: 2, Y: 1},
		{X: 2, Y: 2},
	}
	holes := [][]geometry.Point{
		[]geometry.Point{
			{X: 1.9, Y: 1.9},
			{X: 1.2, Y: 1.9},
			{X: 1.45, Y: 1.65},
			{X: 1.9, Y: 1.5},
			{X: 1.9, Y: 1.9},
		},
	}
	polygon := PPO(exterior, holes)
	clipped := Clip(polygon, RO(1.1, 0.8, 1.15, 2.1))
	cp, ok := clipped.(*geojson.Polygon)
	if !ok {
		t.Fatal("wrong type")
	}
	if cp.Base().Exterior.Empty() {
		t.Fatal("Empty result.")
	}
	if len(cp.Base().Holes) != 0 {
		t.Fatal("result must be a single-ring Polygon")
	}
}

// func TestClipLineString(t *testing.T) {
// 	featuresJSON := `
// 		{"type": "FeatureCollection","features": [
// 			{"type": "Feature","properties":{},"geometry": {"type": "LineString","coordinates": [[-71.46537780761717,42.594290856363344],[-71.37714385986328,42.600861802789524],[-71.37508392333984,42.538156868495555],[-71.43756866455078,42.535374141307415],[-71.44683837890625,42.466018925787495],[-71.334228515625,42.465005871175755],[-71.32736206054688,42.52424199254517]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.49284362792969,42.527784255084676],[-71.35791778564453,42.527784255084676],[-71.35791778564453,42.61096959812047],[-71.49284362792969,42.61096959812047],[-71.49284362792969,42.527784255084676]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.47396087646484,42.48247876554176],[-71.30744934082031,42.48247876554176],[-71.30744934082031,42.576596402826894],[-71.47396087646484,42.576596402826894],[-71.47396087646484,42.48247876554176]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.33491516113281,42.613496290695196],[-71.29920959472656,42.613496290695196],[-71.29920959472656,42.643556064374536],[-71.33491516113281,42.643556064374536],[-71.33491516113281,42.613496290695196]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.37130737304686,42.530061317794775],[-71.3287353515625,42.530061317794775],[-71.3287353515625,42.60414701616359],[-71.37130737304686,42.60414701616359],[-71.37130737304686,42.530061317794775]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.52889251708984,42.564460160624115],[-71.45713806152342,42.54043355305221],[-71.53266906738281,42.49969365675931],[-71.36547088623047,42.508552415528634],[-71.43962860107422,42.58999409368092],[-71.52889251708984,42.564460160624115]]]}},
// 			{"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.33079528808594,42.55940269610327]}},
// 			{"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.27208709716797,42.53107331902133]}}
// 		]}
// 	`
// 	rectJSON := `{"type": "Feature","properties": {},"geometry": {"type": "Polygon","coordinates": [[[-71.44065856933594,42.51740991900762],[-71.29131317138672,42.51740991900762],[-71.29131317138672,42.62663343969058],[-71.44065856933594,42.62663343969058],[-71.44065856933594,42.51740991900762]]]}}`
// 	features := expectJSON(t, featuresJSON, nil)
// 	rect := expectJSON(t, rectJSON, nil)
// 	clipped := features.Clipped(rect)
// 	println(clipped.String())

// }
<commit_msg>Fixed clip test #558<commit_after>package clip

import (
	"testing"

	"github.com/tidwall/geojson"
	"github.com/tidwall/geojson/geometry"
)

func LO(points []geometry.Point) *geojson.LineString {
	return geojson.NewLineString(geometry.NewLine(points, nil))
}

func RO(minX, minY, maxX, maxY float64) *geojson.Rect {
	return geojson.NewRect(geometry.Rect{
		Min: geometry.Point{X: minX, Y: minY},
		Max: geometry.Point{X: maxX, Y: maxY},
	})
}

func PPO(exterior []geometry.Point, holes [][]geometry.Point) *geojson.Polygon {
	return geojson.NewPolygon(geometry.NewPoly(exterior, holes, nil))
}

func TestClipLineStringSimple(t *testing.T) {
	ls := LO([]geometry.Point{
		{X: 1, Y: 1},
		{X: 2, Y: 2},
		{X: 3, Y: 1}})
	clipped := Clip(ls, RO(1.5, 0.5, 2.5, 1.8), nil)
	cl, ok := clipped.(*geojson.MultiLineString)
	if !ok {
		t.Fatal("wrong type")
	}
	if len(cl.Children()) != 2 {
		t.Fatal("result must have two parts in MultiString")
	}
}

func TestClipPolygonSimple(t *testing.T) {
	exterior := []geometry.Point{
		{X: 2, Y: 2},
		{X: 1, Y: 2},
		{X: 1.5, Y: 1.5},
		{X: 1, Y: 1},
		{X: 2, Y: 1},
		{X: 2, Y: 2},
	}
	holes := [][]geometry.Point{
		[]geometry.Point{
			{X: 1.9, Y: 1.9},
			{X: 1.2, Y: 1.9},
			{X: 1.45, Y: 1.65},
			{X: 1.9, Y: 1.5},
			{X: 1.9, Y: 1.9},
		},
	}
	polygon := PPO(exterior, holes)
	clipped := Clip(polygon, RO(1.3, 1.3, 1.4, 2.15), nil)
	cp, ok := clipped.(*geojson.Polygon)
	if !ok {
		t.Fatal("wrong type")
	}
	if cp.Base().Exterior.Empty() {
		t.Fatal("Empty result.")
	}
	if len(cp.Base().Holes) != 1 {
		t.Fatal("result must be a two-ring Polygon")
	}
}

func TestClipPolygon2(t *testing.T) {
	exterior := []geometry.Point{
		{X: 2, Y: 2},
		{X: 1, Y: 2},
		{X: 1.5, Y: 1.5},
		{X: 1, Y: 1},
		{X: 2, Y: 1},
		{X: 2, Y: 2},
	}
	holes := [][]geometry.Point{
		[]geometry.Point{
			{X: 1.9, Y: 1.9},
			{X: 1.2, Y: 1.9},
			{X: 1.45, Y: 1.65},
			{X: 1.9, Y: 1.5},
			{X: 1.9, Y: 1.9},
		},
	}
	polygon := PPO(exterior, holes)
	clipped := Clip(polygon, RO(1.1, 0.8, 1.15, 2.1), nil)
	cp, ok := clipped.(*geojson.Polygon)
	if !ok {
		t.Fatal("wrong type")
	}
	if cp.Base().Exterior.Empty() {
		t.Fatal("Empty result.")
	}
	if len(cp.Base().Holes) != 0 {
		t.Fatal("result must be a single-ring Polygon")
	}
}

// func TestClipLineString(t *testing.T) {
// 	featuresJSON := `
// 		{"type": "FeatureCollection","features": [
// 			{"type": "Feature","properties":{},"geometry": {"type": "LineString","coordinates": [[-71.46537780761717,42.594290856363344],[-71.37714385986328,42.600861802789524],[-71.37508392333984,42.538156868495555],[-71.43756866455078,42.535374141307415],[-71.44683837890625,42.466018925787495],[-71.334228515625,42.465005871175755],[-71.32736206054688,42.52424199254517]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.49284362792969,42.527784255084676],[-71.35791778564453,42.527784255084676],[-71.35791778564453,42.61096959812047],[-71.49284362792969,42.61096959812047],[-71.49284362792969,42.527784255084676]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.47396087646484,42.48247876554176],[-71.30744934082031,42.48247876554176],[-71.30744934082031,42.576596402826894],[-71.47396087646484,42.576596402826894],[-71.47396087646484,42.48247876554176]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.33491516113281,42.613496290695196],[-71.29920959472656,42.613496290695196],[-71.29920959472656,42.643556064374536],[-71.33491516113281,42.643556064374536],[-71.33491516113281,42.613496290695196]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.37130737304686,42.530061317794775],[-71.3287353515625,42.530061317794775],[-71.3287353515625,42.60414701616359],[-71.37130737304686,42.60414701616359],[-71.37130737304686,42.530061317794775]]]}},
// 			{"type": "Feature","properties":{},"geometry": {"type": "Polygon","coordinates": [[[-71.52889251708984,42.564460160624115],[-71.45713806152342,42.54043355305221],[-71.53266906738281,42.49969365675931],[-71.36547088623047,42.508552415528634],[-71.43962860107422,42.58999409368092],[-71.52889251708984,42.564460160624115]]]}},
// 			{"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.33079528808594,42.55940269610327]}},
// 			{"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [-71.27208709716797,42.53107331902133]}}
// 		]}
// 	`
// 	rectJSON := `{"type": "Feature","properties": {},"geometry": {"type": "Polygon","coordinates": [[[-71.44065856933594,42.51740991900762],[-71.29131317138672,42.51740991900762],[-71.29131317138672,42.62663343969058],[-71.44065856933594,42.62663343969058],[-71.44065856933594,42.51740991900762]]]}}`
// 	features := expectJSON(t, featuresJSON, nil)
// 	rect := expectJSON(t, rectJSON, nil)
// 	clipped := features.Clipped(rect)
// 	println(clipped.String())

// }
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

// LogRequest middleware logs details of request.
func LogRequest(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var start time.Time
		if zerolog.GlobalLevel() <= zerolog.DebugLevel {
			start = time.Now()
		}
		h.ServeHTTP(w, r)
		if zerolog.GlobalLevel() <= zerolog.DebugLevel {
			addr := r.Header.Get("X-Real-IP")
			if addr == "" {
				addr = r.Header.Get("X-Forwarded-For")
				if addr == "" {
					addr = r.RemoteAddr
				}
			}
			log.Debug().Str("method", r.Method).Str("path", r.URL.Path).Str("addr", addr).Dur("duration", time.Since(start)).Msgf("http request")
		}
		return
	})
}
<commit_msg>log request status code on debug log level (#314)<commit_after>package middleware

import (
	"bufio"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

// LogRequest middleware logs details of request.
func LogRequest(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if zerolog.GlobalLevel() <= zerolog.DebugLevel {
			start := time.Now()
			lrw := &logResponseWriter{w, 0}
			h.ServeHTTP(lrw, r)
			addr := r.Header.Get("X-Real-IP")
			if addr == "" {
				addr = r.Header.Get("X-Forwarded-For")
				if addr == "" {
					addr = r.RemoteAddr
				}
			}
			log.Debug().Str("method", r.Method).Int("status", lrw.Status()).Str("path", r.URL.Path).Str("addr", addr).Dur("duration", time.Since(start)).Msgf("http request")
		} else {
			h.ServeHTTP(w, r)
		}
		return
	})
}

type logResponseWriter struct {
	http.ResponseWriter
	status int
}

// WriteHeader allows us to save status code.
func (lrw *logResponseWriter) WriteHeader(status int) {
	lrw.status = status
	lrw.ResponseWriter.WriteHeader(status)
}

// Status code allows to get saved status code after handler finished its work.
func (lrw *logResponseWriter) Status() int {
	if lrw.status == 0 {
		return http.StatusOK
	}
	return lrw.status
}

// Hijack as we need it for Websocket.
func (lrw *logResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	lrw.status = http.StatusSwitchingProtocols
	hijacker, ok := lrw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, fmt.Errorf("ResponseWriter doesn't support Hijacker interface")
	}
	return hijacker.Hijack()
}

// Flush as SockJS uses http.Flusher.
func (lrw *logResponseWriter) Flush() {
	lrw.ResponseWriter.(http.Flusher).Flush()
}

// CloseNotify as SockJS uses http.CloseNotifier.
func (lrw *logResponseWriter) CloseNotify() <-chan bool {
	return lrw.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package modules

import (
	"errors"
	"fmt"
	"path/filepath"
	"plugin"
	"runtime"

	"github.com/issue9/web/module"
)

// 插件中的初始化函数名称，必须为可导出的函数名称
const moduleInitFuncName = "Init"

// 指定支持 plugin 模式的系统类型，需要保持该值与 gen.go 中的 +build 指令中的值一致
var pluginOS = []string{"linux", "darwin"}

func isPluginOS() bool {
	for _, os := range pluginOS {
		if os == runtime.GOOS {
			return true
		}
	}

	return false
}

// 加载所有的插件
//
// 如果 glob 为空，则不会加载任何内容，返回空值
func loadPlugins(glob string) ([]*module.Module, error) {
	if !isPluginOS() {
		return nil, errors.New("windows 平台并未实现插件功能！")
	}

	fs, err := filepath.Glob(glob)
	if err != nil {
		return nil, err
	}

	modules := make([]*module.Module, 0, len(fs))
	for _, path := range fs {
		m, err := loadPlugin(path)
		if err != nil {
			return nil, err
		}

		modules = append(modules, m)
	}

	return modules, nil
}

func loadPlugin(path string) (*module.Module, error) {
	p, err := plugin.Open(path)
	if err != nil {
		return nil, err
	}

	symbol, err := p.Lookup(moduleInitFuncName)
	if err != nil {
		return nil, err
	}
	init := symbol.(func(*module.Module))

	m := module.New(module.TypePlugin, "", "")
	init(m)

	if m.Name == "" || m.Description == "" {
		return nil, fmt.Errorf("插件 %s 未指定插件名称或描述内容", path)
	}

	return m, nil
}
<commit_msg>调整错误提示<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package modules

import (
	"errors"
	"fmt"
	"path/filepath"
	"plugin"
	"runtime"

	"github.com/issue9/web/module"
)

// 插件中的初始化函数名称，必须为可导出的函数名称
const moduleInitFuncName = "Init"

// 指定支持 plugin 模式的系统类型，需要保持该值与 gen.go 中的 +build 指令中的值一致
var pluginOS = []string{"linux", "darwin"}

func isPluginOS() bool {
	for _, os := range pluginOS {
		if os == runtime.GOOS {
			return true
		}
	}

	return false
}

// 加载所有的插件
//
// 如果 glob 为空，则不会加载任何内容，返回空值
func loadPlugins(glob string) ([]*module.Module, error) {
	if !isPluginOS() {
		return nil, errors.New("当前平台并未实现插件功能！")
	}

	fs, err := filepath.Glob(glob)
	if err != nil {
		return nil, err
	}

	modules := make([]*module.Module, 0, len(fs))
	for _, path := range fs {
		m, err := loadPlugin(path)
		if err != nil {
			return nil, err
		}

		modules = append(modules, m)
	}

	return modules, nil
}

func loadPlugin(path string) (*module.Module, error) {
	p, err := plugin.Open(path)
	if err != nil {
		return nil, err
	}

	symbol, err := p.Lookup(moduleInitFuncName)
	if err != nil {
		return nil, err
	}
	init := symbol.(func(*module.Module))

	m := module.New(module.TypePlugin, "", "")
	init(m)

	if m.Name == "" || m.Description == "" {
		return nil, fmt.Errorf("插件 %s 未指定插件名称或描述内容", path)
	}

	return m, nil
}
<|endoftext|>