<commit_before>package utils

import (
	"math/rand"
	"net"
	"reflect"
	"sort"
	"testing"
)

// ------------------------------------------
// Test setup with mock services

type MockAddr struct {
	NetworkAttr string
	StringAttr  string
}

func (addr MockAddr) Network() string {
	return addr.NetworkAttr
}

func (addr MockAddr) String() string {
	return addr.StringAttr
}

func TestGetIp(t *testing.T) {
	if ip, _ := GetIP([]string{}); ip == "" {
		t.Errorf("Expected default interface to yield an IP, but got nothing.")
	}
	if ip, _ := GetIP(nil); ip == "" {
		t.Errorf("Expected default interface to yield an IP, but got nothing.")
	}
	if ip, _ := GetIP([]string{"eth0"}); ip == "" {
		t.Errorf("Expected to find IP for eth0, but found nothing.")
	}
	if ip, _ := GetIP([]string{"eth0", "lo"}); ip == "127.0.0.1" {
		t.Errorf("Expected to find eth0 ip, but found loopback instead")
	}
	if ip, _ := GetIP([]string{"lo", "eth0"}); ip != "127.0.0.1" {
		t.Errorf("Expected to find loopback ip, but found: %s", ip)
	}
	if ip, err := GetIP([]string{"interface-does-not-exist"}); err == nil {
		t.Errorf("Expected interface not found, but instead got an IP: %s", ip)
	}
}

func TestInterfaceIpsLoopback(t *testing.T) {
	interfaces := make([]net.Interface, 1)

	interfaces[0] = net.Interface{
		Index: 1,
		MTU:   65536,
		Name:  "lo",
		Flags: net.FlagUp | net.FlagLoopback,
	}

	interfaceIps, err := getinterfaceIPs(interfaces)

	if err != nil {
		t.Error(err)
		return
	}

	/* Because we are testing inside of Docker we can expect that the loopback
	 * interface to always be on the IPv4 address 127.0.0.1 and to be at
	 * index 1 */

	for _, ip := range interfaceIps {
		if ip.Name != "lo" {
			t.Errorf("Expecting loopback interface, but got: %s", ip.Name)
		}
		if ip.IsIPv4() && ip.IPString() != "127.0.0.1" {
			t.Errorf("Expecting loopback interface [127.0.0.1] to be returned, got %s", ip.IPString())
		}
		if !ip.IsIPv4() && ip.IPString() != "::1" {
			t.Errorf("Expecting loopback interface [::1] to be returned, got %s", ip.IPString())
		}
	}
}

func TestInterfaceIpsError(t *testing.T) {
	interfaces := make([]net.Interface, 2)

	interfaces[0] = net.Interface{
		Index: 1,
		MTU:   65536,
		Name:  "lo",
		Flags: net.FlagUp | net.FlagLoopback,
	}
	interfaces[1] = net.Interface{
		Index:        -1,
		MTU:          65536,
		Name:         "barf",
		Flags:        net.FlagUp | net.FlagBroadcast | net.FlagMulticast,
		HardwareAddr: []byte{0x10, 0xC3, 0x7B, 0x45, 0xA2, 0xFF},
	}

	interfaceIps, err := getinterfaceIPs(interfaces)

	if err != nil {
		t.Error(err)
		return
	}

	/* We expect to get only a single valid ip address back because the second
	 * value is junk. */

	if len(interfaceIps) == 0 {
		t.Error("No IPs were parsed from interface. Expecting: 127.0.0.1")
	}

	for _, ip := range interfaceIps {
		if ip.Name != "lo" {
			t.Errorf("Expecting loopback interface, but got: %s", ip.Name)
		}
		if ip.IsIPv4() && ip.IPString() != "127.0.0.1" {
			t.Errorf("Expecting loopback interface [127.0.0.1] to be returned, got %s", ip.IPString())
		}
		if !ip.IsIPv4() && ip.IPString() != "::1" {
			t.Errorf("Expecting loopback interface [::1] to be returned, got %s", ip.IPString())
		}
	}
}

func TestInterfaceSpecParse(t *testing.T) {
	// Test Error Cases
	testSpecError(t, "")              // Nothing
	testSpecError(t, "!")             // Nonsense
	testSpecError(t, "127.0.0.1")     // No Network
	testSpecError(t, "eth0:inet5")    // Invalid IP Version
	testSpecError(t, "eth0[-1]")      // Invalid Index
	testSpecError(t, "static:abcdef") // Invalid IP

	// Test Interface Case
	testSpecInterfaceName(t, "eth0", "eth0", false, -1)
	testSpecInterfaceName(t, "eth0:inet6", "eth0", true, -1)
	testSpecInterfaceName(t, "eth0[1]", "eth0", false, 1)
	testSpecInterfaceName(t, "eth0[2]", "eth0", false, 2)
	testSpecInterfaceName(t, "inet", "*", false, -1)
	testSpecInterfaceName(t, "inet6", "*", true, -1)

	// Test CIDR Case
	testSpecCIDR(t, "10.0.0.0/16")
	testSpecCIDR(t, "fdc6:238c:c4bc::/48")
}

func testSpecError(t *testing.T, specStr string) {
	if spec, err := parseInterfaceSpec(specStr); err == nil {
		t.Errorf("Expected error but got %s", spec)
	}
}

func testSpecInterfaceName(t *testing.T, specStr string, name string, ipv6 bool, index int) {
	spec, err := parseInterfaceSpec(specStr)
	if err != nil {
		t.Errorf("Expected parse to succeed, but got error: %s", err)
	}
	if index < 0 {
		inetSpec, ok := spec.(inetInterfaceSpec)
		if !ok {
			t.Errorf("Expected %s to parse as inetInterfaceSpec", spec)
			return
		}
		if inetSpec.Name != name {
			t.Errorf("Expected to parse interface name %s but got %s", name, inetSpec.Name)
		}
		if inetSpec.IPv6 != ipv6 {
			if ipv6 {
				t.Errorf("Expected spec %s to be IPv6", spec)
			} else {
				t.Errorf("Expected spec %s to be IPv4", spec)
			}
		}
		return
	}
	indexSpec, ok := spec.(indexInterfaceSpec)
	if !ok {
		t.Errorf("Expected %s to parse as indexInterfaceSpec", spec)
		return
	}
	if indexSpec.Name != name {
		t.Errorf("Expected to parse interface name %s but got %s", name, indexSpec.Name)
	}
	if indexSpec.Index != index {
		t.Errorf("Expected index to be %d but was %d", index, indexSpec.Index)
	}
}

func testSpecCIDR(t *testing.T, specStr string) {
	spec, err := parseInterfaceSpec(specStr)
	if err != nil {
		t.Errorf("Expected parse to succeed, but got error: %s", err)
	}
	cidrSpec, ok := spec.(cidrInterfaceSpec)
	if !ok {
		t.Errorf("Expected %s to parse as cidrInterfaceSpec", spec)
		return
	}
	if cidrSpec.Network == nil {
		t.Errorf("Expected spec to be a network CIDR")
	}
}

func TestFindIPWithSpecs(t *testing.T) {
	iips := getTestIPs()

	// Loopback
	testIPSpec(t, iips, "127.0.0.1", "lo")
	testIPSpec(t, iips, "::1", "lo:inet6")

	// Interface Name
	testIPSpec(t, iips, "10.2.0.1", "eth0")
	testIPSpec(t, iips, "10.2.0.1", "eth0:inet")
	testIPSpec(t, iips, "", "eth0:inet6")

	// Indexes
	testIPSpec(t, iips, "192.168.1.100", "eth0[1]")
	testIPSpec(t, iips, "", "eth0[2]")

	// IPv4 CIDR
	testIPSpec(t, iips, "10.0.0.100", "10.0.0.0/16")
	testIPSpec(t, iips, "10.1.0.200", "10.1.0.0/16")
	testIPSpec(t, iips, "10.2.0.1", "10.0.0.0/8")

	// IPv6 CIDR
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "eth2:inet6")

	// First IPv4
	testIPSpec(t, iips, "10.2.0.1", "inet")

	// First IPv6
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "inet6")

	// Test Multiple
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "eth3", "fdc6:238c:c4bc::/48", "inet", "inet6")
	testIPSpec(t, iips, "10.0.0.100", "eth3", "10.0.0.0/16", "inet", "inet6", "fdc6:238c:c4bc::/48")

	// Test that inet and inet6 will never find the loopback address
	loopback := []interfaceIP{
		newInterfaceIP("lo", "::1"),
		newInterfaceIP("lo", "127.0.0.1"),
	}
	testIPSpec(t, loopback, "", "inet")
	testIPSpec(t, loopback, "", "inet6")
}

func testIPSpec(t *testing.T, iips []interfaceIP, expectedIP string, specList ...string) {
	specs, err := parseInterfaceSpecs(specList)
	if err != nil {
		t.Fatalf("Fatal parse error of spec list: %s, %s", specList, err)
	}
	foundIP, err := findIPWithSpecs(specs, iips)
	if err != nil && expectedIP != "" {
		t.Errorf("Expected to find an IP, but got an error instead: %s", err)
	}
	if foundIP != expectedIP {
		t.Errorf("Expected to find IP %s but found %s instead", expectedIP, foundIP)
	}
}

func TestInterfaceIPSorting(t *testing.T) {
	sortedIIPs := getTestIPs()
	var unsortedIIPs = make([]interfaceIP, len(sortedIIPs))
	copy(unsortedIIPs, sortedIIPs)

	// Shuffle unsortedIIPs
	rand.Seed(1) // Deterministic
	for i := range unsortedIIPs {
		j := rand.Intn(i + 1)
		unsortedIIPs[i], unsortedIIPs[j] = unsortedIIPs[j], unsortedIIPs[i]
	}

	// Do Stable Sort
	sort.Stable(ByInterfaceThenIP(unsortedIIPs))

	if !reflect.DeepEqual(unsortedIIPs, sortedIIPs) {
		t.Errorf("Interface IPs are not sorted as expected")

		t.Log("=== EXPECTED ===")
		for _, ip := range sortedIIPs {
			t.Logf("%s: %s", ip.Name, ip)
		}

		t.Log("=== ACTUAL ===")
		for _, ip := range unsortedIIPs {
			t.Logf("%s: %s", ip.Name, ip)
		}
	}
}

// -------- Helper Functions

func getTestIPs() []interfaceIP {
	return []interfaceIP{
		newInterfaceIP("eth0", "10.2.0.1"),
		newInterfaceIP("eth0", "192.168.1.100"),
		newInterfaceIP("eth1", "10.0.0.100"),
		newInterfaceIP("eth1", "10.0.0.200"),
		newInterfaceIP("eth2", "10.1.0.200"),
		newInterfaceIP("eth2", "fdc6:238c:c4bc::1"),
		newInterfaceIP("lo", "::1"),
		newInterfaceIP("lo", "127.0.0.1"),
	}
}

func newInterfaceIP(name string, ip string) interfaceIP {
	return interfaceIP{
		Name: name,
		IP:   net.ParseIP(ip),
	}
}
<commit_msg>add unit tests for static:x.x.x.x interface spec<commit_after>package utils

import (
	"math/rand"
	"net"
	"reflect"
	"sort"
	"testing"
)

// ------------------------------------------
// Test setup with mock services

type MockAddr struct {
	NetworkAttr string
	StringAttr  string
}

func (addr MockAddr) Network() string {
	return addr.NetworkAttr
}

func (addr MockAddr) String() string {
	return addr.StringAttr
}

func TestGetIp(t *testing.T) {
	if ip, _ := GetIP([]string{}); ip == "" {
		t.Errorf("Expected default interface to yield an IP, but got nothing.")
	}
	if ip, _ := GetIP(nil); ip == "" {
		t.Errorf("Expected default interface to yield an IP, but got nothing.")
	}
	if ip, _ := GetIP([]string{"eth0"}); ip == "" {
		t.Errorf("Expected to find IP for eth0, but found nothing.")
	}
	if ip, _ := GetIP([]string{"eth0", "lo"}); ip == "127.0.0.1" {
		t.Errorf("Expected to find eth0 ip, but found loopback instead")
	}
	if ip, _ := GetIP([]string{"lo", "eth0"}); ip != "127.0.0.1" {
		t.Errorf("Expected to find loopback ip, but found: %s", ip)
	}
	if ip, err := GetIP([]string{"interface-does-not-exist"}); err == nil {
		t.Errorf("Expected interface not found, but instead got an IP: %s", ip)
	}
	if ip, _ := GetIP([]string{"static:192.168.1.100", "lo"}); ip != "192.168.1.100" {
		t.Errorf("Expected to find static ip 192.168.1.100, but found: %s", ip)
	}
}

func TestInterfaceIpsLoopback(t *testing.T) {
	interfaces := make([]net.Interface, 1)

	interfaces[0] = net.Interface{
		Index: 1,
		MTU:   65536,
		Name:  "lo",
		Flags: net.FlagUp | net.FlagLoopback,
	}

	interfaceIps, err := getinterfaceIPs(interfaces)

	if err != nil {
		t.Error(err)
		return
	}

	/* Because we are testing inside of Docker we can expect that the loopback
	 * interface to always be on the IPv4 address 127.0.0.1 and to be at
	 * index 1 */

	for _, ip := range interfaceIps {
		if ip.Name != "lo" {
			t.Errorf("Expecting loopback interface, but got: %s", ip.Name)
		}
		if ip.IsIPv4() && ip.IPString() != "127.0.0.1" {
			t.Errorf("Expecting loopback interface [127.0.0.1] to be returned, got %s", ip.IPString())
		}
		if !ip.IsIPv4() && ip.IPString() != "::1" {
			t.Errorf("Expecting loopback interface [::1] to be returned, got %s", ip.IPString())
		}
	}
}

func TestInterfaceIpsError(t *testing.T) {
	interfaces := make([]net.Interface, 2)

	interfaces[0] = net.Interface{
		Index: 1,
		MTU:   65536,
		Name:  "lo",
		Flags: net.FlagUp | net.FlagLoopback,
	}
	interfaces[1] = net.Interface{
		Index:        -1,
		MTU:          65536,
		Name:         "barf",
		Flags:        net.FlagUp | net.FlagBroadcast | net.FlagMulticast,
		HardwareAddr: []byte{0x10, 0xC3, 0x7B, 0x45, 0xA2, 0xFF},
	}

	interfaceIps, err := getinterfaceIPs(interfaces)

	if err != nil {
		t.Error(err)
		return
	}

	/* We expect to get only a single valid ip address back because the second
	 * value is junk. */

	if len(interfaceIps) == 0 {
		t.Error("No IPs were parsed from interface. Expecting: 127.0.0.1")
	}

	for _, ip := range interfaceIps {
		if ip.Name != "lo" {
			t.Errorf("Expecting loopback interface, but got: %s", ip.Name)
		}
		if ip.IsIPv4() && ip.IPString() != "127.0.0.1" {
			t.Errorf("Expecting loopback interface [127.0.0.1] to be returned, got %s", ip.IPString())
		}
		if !ip.IsIPv4() && ip.IPString() != "::1" {
			t.Errorf("Expecting loopback interface [::1] to be returned, got %s", ip.IPString())
		}
	}
}

func TestInterfaceSpecParse(t *testing.T) {
	// Test Error Cases
	testSpecError(t, "")              // Nothing
	testSpecError(t, "!")             // Nonsense
	testSpecError(t, "127.0.0.1")     // No Network
	testSpecError(t, "eth0:inet5")    // Invalid IP Version
	testSpecError(t, "eth0[-1]")      // Invalid Index
	testSpecError(t, "static:abcdef") // Invalid IP

	// Test Interface Case
	testSpecInterfaceName(t, "eth0", "eth0", false, -1)
	testSpecInterfaceName(t, "eth0:inet6", "eth0", true, -1)
	testSpecInterfaceName(t, "eth0[1]", "eth0", false, 1)
	testSpecInterfaceName(t, "eth0[2]", "eth0", false, 2)
	testSpecInterfaceName(t, "inet", "*", false, -1)
	testSpecInterfaceName(t, "inet6", "*", true, -1)
	testSpecInterfaceName(t, "static:192.168.1.100", "static", false, 1)

	// Test CIDR Case
	testSpecCIDR(t, "10.0.0.0/16")
	testSpecCIDR(t, "fdc6:238c:c4bc::/48")
}

func testSpecError(t *testing.T, specStr string) {
	if spec, err := parseInterfaceSpec(specStr); err == nil {
		t.Errorf("Expected error but got %s", spec)
	}
}

func testSpecInterfaceName(t *testing.T, specStr string, name string, ipv6 bool, index int) {
	spec, err := parseInterfaceSpec(specStr)
	if err != nil {
		t.Errorf("Expected parse to succeed, but got error: %s", err)
	}
	if name == "static" {
		staticSpec, ok := spec.(staticInterfaceSpec)
		if !ok {
			t.Errorf("Expected %s to parse as staticInterfaceSpec", spec)
			return
		}
		if staticSpec.Name != "static" {
			t.Errorf("Expected to parse interface name static but got %s", staticSpec.Name)
		}
		return
	}
	if index < 0 {
		inetSpec, ok := spec.(inetInterfaceSpec)
		if !ok {
			t.Errorf("Expected %s to parse as inetInterfaceSpec", spec)
			return
		}
		if inetSpec.Name != name {
			t.Errorf("Expected to parse interface name %s but got %s", name, inetSpec.Name)
		}
		if inetSpec.IPv6 != ipv6 {
			if ipv6 {
				t.Errorf("Expected spec %s to be IPv6", spec)
			} else {
				t.Errorf("Expected spec %s to be IPv4", spec)
			}
		}
		return
	}
	indexSpec, ok := spec.(indexInterfaceSpec)
	if !ok {
		t.Errorf("Expected %s to parse as indexInterfaceSpec", spec)
		return
	}
	if indexSpec.Name != name {
		t.Errorf("Expected to parse interface name %s but got %s", name, indexSpec.Name)
	}
	if indexSpec.Index != index {
		t.Errorf("Expected index to be %d but was %d", index, indexSpec.Index)
	}
}

func testSpecCIDR(t *testing.T, specStr string) {
	spec, err := parseInterfaceSpec(specStr)
	if err != nil {
		t.Errorf("Expected parse to succeed, but got error: %s", err)
	}
	cidrSpec, ok := spec.(cidrInterfaceSpec)
	if !ok {
		t.Errorf("Expected %s to parse as cidrInterfaceSpec", spec)
		return
	}
	if cidrSpec.Network == nil {
		t.Errorf("Expected spec to be a network CIDR")
	}
}

func TestFindIPWithSpecs(t *testing.T) {
	iips := getTestIPs()

	// Loopback
	testIPSpec(t, iips, "127.0.0.1", "lo")
	testIPSpec(t, iips, "::1", "lo:inet6")

	// Static
	testIPSpec(t, iips, "192.168.1.100", "static:192.168.1.100")

	// Interface Name
	testIPSpec(t, iips, "10.2.0.1", "eth0")
	testIPSpec(t, iips, "10.2.0.1", "eth0:inet")
	testIPSpec(t, iips, "", "eth0:inet6")

	// Indexes
	testIPSpec(t, iips, "192.168.1.100", "eth0[1]")
	testIPSpec(t, iips, "", "eth0[2]")

	// IPv4 CIDR
	testIPSpec(t, iips, "10.0.0.100", "10.0.0.0/16")
	testIPSpec(t, iips, "10.1.0.200", "10.1.0.0/16")
	testIPSpec(t, iips, "10.2.0.1", "10.0.0.0/8")

	// IPv6 CIDR
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "eth2:inet6")

	// First IPv4
	testIPSpec(t, iips, "10.2.0.1", "inet")

	// First IPv6
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "inet6")

	// Test Multiple
	testIPSpec(t, iips, "fdc6:238c:c4bc::1", "eth3", "fdc6:238c:c4bc::/48", "inet", "inet6")
	testIPSpec(t, iips, "10.0.0.100", "eth3", "10.0.0.0/16", "inet", "inet6", "fdc6:238c:c4bc::/48")

	// Test that inet and inet6 will never find the loopback address
	loopback := []interfaceIP{
		newInterfaceIP("lo", "::1"),
		newInterfaceIP("lo", "127.0.0.1"),
	}
	testIPSpec(t, loopback, "", "inet")
	testIPSpec(t, loopback, "", "inet6")
}

func testIPSpec(t *testing.T, iips []interfaceIP, expectedIP string, specList ...string) {
	specs, err := parseInterfaceSpecs(specList)
	if err != nil {
		t.Fatalf("Fatal parse error of spec list: %s, %s", specList, err)
	}
	foundIP, err := findIPWithSpecs(specs, iips)
	if err != nil && expectedIP != "" {
		t.Errorf("Expected to find an IP, but got an error instead: %s", err)
	}
	if foundIP != expectedIP {
		t.Errorf("Expected to find IP %s but found %s instead", expectedIP, foundIP)
	}
}

func TestInterfaceIPSorting(t *testing.T) {
	sortedIIPs := getTestIPs()
	var unsortedIIPs = make([]interfaceIP, len(sortedIIPs))
	copy(unsortedIIPs, sortedIIPs)

	// Shuffle unsortedIIPs
	rand.Seed(1) // Deterministic
	for i := range unsortedIIPs {
		j := rand.Intn(i + 1)
		unsortedIIPs[i], unsortedIIPs[j] = unsortedIIPs[j], unsortedIIPs[i]
	}

	// Do Stable Sort
	sort.Stable(ByInterfaceThenIP(unsortedIIPs))

	if !reflect.DeepEqual(unsortedIIPs, sortedIIPs) {
		t.Errorf("Interface IPs are not sorted as expected")

		t.Log("=== EXPECTED ===")
		for _, ip := range sortedIIPs {
			t.Logf("%s: %s", ip.Name, ip)
		}

		t.Log("=== ACTUAL ===")
		for _, ip := range unsortedIIPs {
			t.Logf("%s: %s", ip.Name, ip)
		}
	}
}

// -------- Helper Functions

func getTestIPs() []interfaceIP {
	return []interfaceIP{
		newInterfaceIP("eth0", "10.2.0.1"),
		newInterfaceIP("eth0", "192.168.1.100"),
		newInterfaceIP("eth1", "10.0.0.100"),
		newInterfaceIP("eth1", "10.0.0.200"),
		newInterfaceIP("eth2", "10.1.0.200"),
		newInterfaceIP("eth2", "fdc6:238c:c4bc::1"),
		newInterfaceIP("lo", "::1"),
		newInterfaceIP("lo", "127.0.0.1"),
		newInterfaceIP("static:192.168.1.100", "192.168.1.100"),
	}
}

func newInterfaceIP(name string, ip string) interfaceIP {
	return interfaceIP{
		Name: name,
		IP:   net.ParseIP(ip),
	}
}
<|endoftext|>


<commit_before>package vault

import (
	"log"
	"sync/atomic"
	"time"

	"github.com/hashicorp/vault/logical"
)

// RollbackManager is responsible for performing rollbacks of partial
// secrets within logical backends.
//
// During normal operations, it is possible for logical backends to
// error partially through an operation. These are called "partial secrets":
// they are never sent back to a user, but they do need to be cleaned up.
// This manager handles that by periodically (on a timer) requesting that the
// backends clean up.
//
// The RollbackManager periodically (according to the Period option)
// initiates a logical.RollbackOperation on every mounted logical backend.
// It ensures that only one rollback operation is in-flight at any given
// time within a single seal/unseal phase.
type RollbackManager struct {
	// NOTE: This must always be at the top of the struct to avoid
	// atomic alignment issues. Go bug.
	running uint32

	Logger *log.Logger
	Mounts *MountTable
	Router *Router

	Period time.Duration // time between rollback calls
}

// Start starts the rollback manager. This will block until Stop is called
// so it should be executed within a goroutine.
func (m *RollbackManager) Start() {
	// If we're already running, then don't start again
	if !atomic.CompareAndSwapUint32(&m.running, 0, 1) {
		return
	}

	m.Logger.Printf("[INFO] rollback: starting rollback manager")

	var mounts map[string]*uint32
	tick := time.NewTicker(m.Period)
	defer tick.Stop()
	for {
		// Wait for the tick
		<-tick.C

		// If we're quitting, then stop
		if atomic.LoadUint32(&m.running) != 1 {
			m.Logger.Printf("[INFO] rollback: stopping rollback manager")
			return
		}

		// Get the list of paths that we should rollback and setup our
		// mounts mapping. Mounts that have since been unmounted will
		// just "fall off" naturally: they aren't in our new mount mapping
		// and when their goroutine ends they'll naturally lose the reference.
		newMounts := make(map[string]*uint32)
		m.Mounts.RLock()
		for _, e := range m.Mounts.Entries {
			if s, ok := mounts[e.Path]; ok {
				newMounts[e.Path] = s
			} else {
				newMounts[e.Path] = new(uint32)
			}
		}
		m.Mounts.RUnlock()
		mounts = newMounts

		// Go through the mounts and start the rollback if we can
		for path, status := range mounts {
			// If we can change the status from 0 to 1, we can start it
			if !atomic.CompareAndSwapUint32(status, 0, 1) {
				continue
			}

			go m.rollback(path, status)
		}
	}
}

// Stop stops the running manager. This will not halt any in-flight
// rollbacks.
func (m *RollbackManager) Stop() {
	atomic.StoreUint32(&m.running, 0)
}

func (m *RollbackManager) rollback(path string, state *uint32) {
	defer atomic.StoreUint32(state, 0)

	m.Logger.Printf(
		"[DEBUG] rollback: starting rollback for %s",
		path)
	req := &logical.Request{
		Operation: logical.RollbackOperation,
		Path:      path,
	}
	if _, err := m.Router.Route(req); err != nil {
		// If the error is an unsupported operation, then it doesn't
		// matter, the backend doesn't support it.
		if err == logical.ErrUnsupportedOperation {
			return
		}

		m.Logger.Printf(
			"[ERR] rollback: error rolling back %s: %s",
			path, err)
	}
}

// The methods below are the hooks from core that are called pre/post seal.

func (c *Core) startRollback() error {
	// Ensure if we had a rollback it was stopped. This should never
	// be the case but it doesn't hurt to check.
	if c.rollback != nil {
		c.rollback.Stop()
	}

	c.rollback = &RollbackManager{
		Logger: c.logger,
		Router: c.router,
		Mounts: c.mounts,
		Period: 1 * time.Minute,
	}
	go c.rollback.Start()

	return nil
}

func (c *Core) stopRollback() error {
	if c.rollback != nil {
		c.rollback.Stop()
		c.rollback = nil
	}

	return nil
}
<commit_msg>vault: comment mounts mapping in rollback manager<commit_after>package vault

import (
	"log"
	"sync/atomic"
	"time"

	"github.com/hashicorp/vault/logical"
)

// RollbackManager is responsible for performing rollbacks of partial
// secrets within logical backends.
//
// During normal operations, it is possible for logical backends to
// error partially through an operation. These are called "partial secrets":
// they are never sent back to a user, but they do need to be cleaned up.
// This manager handles that by periodically (on a timer) requesting that the
// backends clean up.
//
// The RollbackManager periodically (according to the Period option)
// initiates a logical.RollbackOperation on every mounted logical backend.
// It ensures that only one rollback operation is in-flight at any given
// time within a single seal/unseal phase.
type RollbackManager struct {
	// NOTE: This must always be at the top of the struct to avoid
	// atomic alignment issues. Go bug.
	running uint32

	Logger *log.Logger
	Mounts *MountTable
	Router *Router

	Period time.Duration // time between rollback calls
}

// Start starts the rollback manager. This will block until Stop is called
// so it should be executed within a goroutine.
func (m *RollbackManager) Start() {
	// If we're already running, then don't start again
	if !atomic.CompareAndSwapUint32(&m.running, 0, 1) {
		return
	}

	m.Logger.Printf("[INFO] rollback: starting rollback manager")

	// mounts is a mapping of a mount path (i.e. "sys") to a uint32 pointer
	// we can do atomic operations on. The purpose of this map is to ensure
	// we only ever have one RollbackOperation request in-flight for each
	// path.
	//
	// When a RollbackOperation is started, the pointer is changed to 0 to 1
	// atomically. When the operation completes, it is atomatically loaded
	// to 0 (from anything). Before we start a rollback operation, we use a
	// CAS 0 to 1 and only start a rollback if that succeeds.
	//
	// As a result, we only ever get one in-flight request at one time.
	var mounts map[string]*uint32

	tick := time.NewTicker(m.Period)
	defer tick.Stop()
	for {
		// Wait for the tick
		<-tick.C

		// If we're quitting, then stop
		if atomic.LoadUint32(&m.running) != 1 {
			m.Logger.Printf("[INFO] rollback: stopping rollback manager")
			return
		}

		// Get the list of paths that we should rollback and setup our
		// mounts mapping. Mounts that have since been unmounted will
		// just "fall off" naturally: they aren't in our new mount mapping
		// and when their goroutine ends they'll naturally lose the reference.
		//
		// The reason we make a new mapping is so that unmounted paths
		// are automatically removed. If a mount path was in the last mapping
		// we copy the uint32 pointer. So the result of the copy is: new
		// mount paths get a new uint32 pointer, unmounted paths are removed
		// from the map, and existing mount paths nothing changes.
		//
		// The purpose of the map is documented above where mounts is defined.
		newMounts := make(map[string]*uint32)
		m.Mounts.RLock()
		for _, e := range m.Mounts.Entries {
			if s, ok := mounts[e.Path]; ok {
				newMounts[e.Path] = s
			} else {
				newMounts[e.Path] = new(uint32)
			}
		}
		m.Mounts.RUnlock()
		mounts = newMounts

		// Go through the mounts and start the rollback if we can
		for path, status := range mounts {
			// If we can change the status from 0 to 1, we can start it
			if !atomic.CompareAndSwapUint32(status, 0, 1) {
				continue
			}

			go m.rollback(path, status)
		}
	}
}

// Stop stops the running manager. This will not halt any in-flight
// rollbacks.
func (m *RollbackManager) Stop() {
	atomic.StoreUint32(&m.running, 0)
}

func (m *RollbackManager) rollback(path string, state *uint32) {
	defer atomic.StoreUint32(state, 0)

	m.Logger.Printf(
		"[DEBUG] rollback: starting rollback for %s",
		path)
	req := &logical.Request{
		Operation: logical.RollbackOperation,
		Path:      path,
	}
	if _, err := m.Router.Route(req); err != nil {
		// If the error is an unsupported operation, then it doesn't
		// matter, the backend doesn't support it.
		if err == logical.ErrUnsupportedOperation {
			return
		}

		m.Logger.Printf(
			"[ERR] rollback: error rolling back %s: %s",
			path, err)
	}
}

// The methods below are the hooks from core that are called pre/post seal.

func (c *Core) startRollback() error {
	// Ensure if we had a rollback it was stopped. This should never
	// be the case but it doesn't hurt to check.
	if c.rollback != nil {
		c.rollback.Stop()
	}

	c.rollback = &RollbackManager{
		Logger: c.logger,
		Router: c.router,
		Mounts: c.mounts,
		Period: 1 * time.Minute,
	}
	go c.rollback.Start()

	return nil
}

func (c *Core) stopRollback() error {
	if c.rollback != nil {
		c.rollback.Stop()
		c.rollback = nil
	}

	return nil
}
<|endoftext|>


<commit_before>// Package filewatcher 包用于监听监听文件变化
//
// 安装:
//    go get github.com/scofieldpeng/go-libs/filewatcher
//
// 使用方法:
//     import github.com/scofieldpeng/go-libs/filewatcher
//
// 1. 初始化
//     filewatcher.FWatcher.Init()
//
// 2. 添加某文件监听事件,第一个参数为要监听的文件/文件夹的绝对路径,第二个参数为要回调的函数名称,
//    注意,如果是文件夹,那么只能监听该文件夹下的第一级文件的变化
//     filewatcher.FWatcher.AddFile("filePath",callbackName,callback)     /* 监听文件 */
//     filewatcher.FWatcher.AddFolder("folderPath",callbackName,callback) /* 监听文件夹 */
//
// 3. 取消文件/文件夹监听
//     filewatcher.FWatcher.RmFile("filePath",callbackName) /* 移除对文件的监听 */
//     filewatcher.FWatcher.RmFolder("folderPath",callbackName) /* 移除对文件夹的监听 */
package filewatcher

import (
	"errors"
	"github.com/fsnotify/fsnotify"
	"log"
)

type (
	// fWatcher fWatcher结构体,用于实现文件监听的主要功能
	fWatcher struct {
		fileEvents   map[string]map[string]func(string) error // 文件监听事件回调
		folderEvents map[string]map[string]func(string) error // 文件夹监听事件回调
		status       int                                      // 当前监听程序的状态,init(初始化完毕),run(运行)
		watcher      *fsnotify.Watcher                        // fsnotify监听对象
		debug        bool                                     // 是否是调试模式
	}
)

var (
	STATUS_STOP int = 0 // 停止状态
	STATUS_INIT int = 1 // 初始化的状态
	STATUS_RUN  int = 2 // 运行的状态
)

var (
	FWatcher fWatcher
)

func init() {
	FWatcher = newFWatcher()
}

// newFWatcher 新建一个fWatcher对象结构体
func newFWatcher() fWatcher {
	return fWatcher{}
}

// Init 初始化文件监听
func (fw *fWatcher) Init(debug ...bool) error {
	var err error = nil

	if len(debug) > 0 {
		debug = make([]bool,1)
		debug[0] = false
	}

	fw.status = STATUS_INIT
	fw.watcher,err = fsnotify.NewWatcher()
	fw.debug = debug[0]
	if err != nil {
		return errors.New("初始化filewatcher失败,失败原因:" + err.Error())
	}
	return nil
}

// Run 开始文件监听
func (fw *fWatcher) Run() {
	defer fw.watcher.Close()
	fw.status = STATUS_RUN
	for{
		select {
		case event := <-fw.watcher.Events:
			if fw.debug {
				log.Println("本次发现变化文件:",event.String())
			}

			go fw.watcherTrigger(event.Name)
		}
	}
}

// watcherTrigger TODO 事件监听触发函数,该函数将便利用户设置的监听文件/文件夹,如果有对变化文件/文件夹的监听,那么一一进行用户回调函数的触发
func (fw *fWatcher) watcherTrigger(changedPath string) {

}

// Status 获取当前程序运行状态
func (fw *fWatcher) Status() int {
	return fw.status
}

// AddFile 添加文件监听
func (fw *fWatcher) AddFile(filePath, callbackName string, callback func(string) error) {
	if _, ok := fw.fileEvents[filePath]; !ok {
		fw.fileEvents[filePath] = make(map[string]func(string) error)
	}
	fw.fileEvents[filePath][callbackName] = callback
}

// AddFloder 添加文件夹变化
func (fw *fWatcher) AddFolder(folderPath, callbackName string, callback func(string) error) {
	if _, ok := fw.fileEvents[folderPath]; !ok {
		fw.fileEvents[folderPath] = make(map[string]func(string) error)
	}
	fw.fileEvents[folderPath][callbackName] = callback
}

// RmFile 移除对某文件的事件监听
func (fw *fWatcher) RmFile(filePath, callbackName string) error {
	var errNotFound error = errors.New("没有设置对文件:" + filePath + "为" + callbackName + "的监听")
	filePathCallbacks, ok := fw.fileEvents[filePath]
	if !ok {
		return errNotFound
	}
	if _, ok := filePathCallbacks[callbackName]; !ok {
		return errNotFound
	}

	delete(fw.fileEvents[filePath], callbackName)
	return nil
}

// RmFolder 移除对某文件夹的事件监听
func (fw *fWatcher) RmFolder(folderPath, callbackName string) error {
	var errNotFound error = errors.New("没有设置对文件:" + folderPath + "为" + callbackName + "的监听")
	folderPathCallbacks, ok := fw.folderEvents[folderPath]
	if !ok {
		return errNotFound
	}
	if _, ok := folderPathCallbacks[callbackName]; !ok {
		return errNotFound
	}

	delete(fw.folderEvents[folderPath], callbackName)
	return nil
}
<commit_msg>finish filewatcher<commit_after>// Package filewatcher 包用于监听监听文件变化
//
// 安装:
//    go get github.com/scofieldpeng/go-libs/filewatcher
//
// 使用方法:
//     import github.com/scofieldpeng/go-libs/filewatcher
//
// 1. 初始化
//     filewatcher.FWatcher.Init()
//
// 2. 添加某文件监听事件,第一个参数为要监听的文件/文件夹的绝对路径,第二个参数为要回调的函数名称,
//    注意,如果是文件夹,那么只能监听该文件夹下的第一级文件的变化
//     filewatcher.FWatcher.AddFile("filePath",callbackName,callback)     /* 监听文件 */
//     filewatcher.FWatcher.AddFolder("folderPath",callbackName,callback) /* 监听文件夹 */
//
// 3. 取消文件/文件夹监听
//     filewatcher.FWatcher.RmFile("filePath",callbackName) /* 移除对文件的监听 */
//     filewatcher.FWatcher.RmFolder("folderPath",callbackName) /* 移除对文件夹的监听 */
package filewatcher

import (
	"errors"
	"github.com/fsnotify/fsnotify"
	"log"
	"path/filepath"
)

type (
	// fWatcher fWatcher结构体,用于实现文件监听的主要功能
	fWatcher struct {
		fileEvents   map[string]map[string]func(string) error // 文件监听事件回调
		folderEvents map[string]map[string]func(string) error // 文件夹监听事件回调
		status       int                                      // 当前监听程序的状态,init(初始化完毕),run(运行)
		watcher      *fsnotify.Watcher                        // fsnotify监听对象
		debug        bool                                     // 是否是调试模式
	}
)

var (
	STATUS_STOP int = 0 // 停止状态
	STATUS_INIT int = 1 // 初始化的状态
	STATUS_RUN  int = 2 // 运行的状态
)

var (
	FWatcher fWatcher
)

func init() {
	FWatcher = newFWatcher()
}

// newFWatcher 新建一个fWatcher对象结构体
func newFWatcher() fWatcher {
	return fWatcher{}
}

// Init 初始化文件监听
func (fw *fWatcher) Init(debug ...bool) error {
	var err error = nil

	if len(debug) > 0 {
		debug = make([]bool,1)
		debug[0] = false
	}

	fw.status = STATUS_INIT
	fw.watcher,err = fsnotify.NewWatcher()
	fw.debug = debug[0]
	if err != nil {
		return errors.New("初始化filewatcher失败,失败原因:" + err.Error())
	}
	return nil
}

// Run 开始文件监听
func (fw *fWatcher) Run() {
	defer fw.watcher.Close()
	fw.status = STATUS_RUN
	for{
		select {
		case event := <-fw.watcher.Events:
			if fw.debug {
				log.Println("本次发现变化文件:",event.String())
			}

			go fw.watcherTrigger(event.Name)
		}
	}
}

// watcherTrigger TODO 事件监听触发函数,该函数将便利用户设置的监听文件/文件夹,如果有对变化文件/文件夹的监听,那么一一进行用户回调函数的触发
func (fw *fWatcher) watcherTrigger(changedPath string) {
	fileDir := filepath.Dir(changedPath)
	 // 遍历文件监听,是否有监听回调
	if events,ok := fw.folderEvents[fileDir];ok{
		for _,callback:=range events {
			go callback(fileDir)
		}
	}
	 // 便利文件夹监听事件,查看是否有监听回调
	if events,ok := fw.fileEvents[changedPath];ok{
		for _,callback := range events{
			go callback(changedPath)
		}
	}
}

// Status 获取当前程序运行状态
func (fw *fWatcher) Status() int {
	return fw.status
}

// AddFile 添加文件监听
func (fw *fWatcher) AddFile(filePath, callbackName string, callback func(string) error) {
	if _, ok := fw.fileEvents[filePath]; !ok {
		fw.fileEvents[filePath] = make(map[string]func(string) error)
	}
	fw.fileEvents[filePath][callbackName] = callback
}

// AddFloder 添加文件夹变化
func (fw *fWatcher) AddFolder(folderPath, callbackName string, callback func(string) error) {
	if _, ok := fw.fileEvents[folderPath]; !ok {
		fw.fileEvents[folderPath] = make(map[string]func(string) error)
	}
	fw.fileEvents[folderPath][callbackName] = callback
}

// RmFile 移除对某文件的事件监听
func (fw *fWatcher) RmFile(filePath, callbackName string) error {
	var errNotFound error = errors.New("没有设置对文件:" + filePath + "为" + callbackName + "的监听")
	filePathCallbacks, ok := fw.fileEvents[filePath]
	if !ok {
		return errNotFound
	}
	if _, ok := filePathCallbacks[callbackName]; !ok {
		return errNotFound
	}

	delete(fw.fileEvents[filePath], callbackName)
	return nil
}

// RmFolder 移除对某文件夹的事件监听
func (fw *fWatcher) RmFolder(folderPath, callbackName string) error {
	var errNotFound error = errors.New("没有设置对文件:" + folderPath + "为" + callbackName + "的监听")
	folderPathCallbacks, ok := fw.folderEvents[folderPath]
	if !ok {
		return errNotFound
	}
	if _, ok := folderPathCallbacks[callbackName]; !ok {
		return errNotFound
	}

	delete(fw.folderEvents[folderPath], callbackName)
	return nil
}
<|endoftext|>


<commit_before>/**
	* 1. Setup the server so cf can call it under main.
				e.g. `cf my-plugin` creates the callable server. now we can call the Run command
	* 2. Implement Run that is the actual code of the plugin!
	* 3. Return an error
**/

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/plugin"
)

type Test1 struct {
}

func (c *Test1) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "new-api" {
		token, _ := cliConnection.AccessToken()
		fmt.Println("Access Token:", token)
		fmt.Println("")

		app, err := cliConnection.GetApp("test_app")
		fmt.Println("err for test_app", err)
		fmt.Println("test_app is: ", app)

		hasOrg, _ := cliConnection.HasOrganization()
		fmt.Println("Has Organization Targeted:", hasOrg)
		currentOrg, _ := cliConnection.GetCurrentOrg()
		fmt.Println("Current Org:", currentOrg)
		org, _ := cliConnection.GetOrg(currentOrg.Name)
		fmt.Println(currentOrg.Name, " Org:", org)
		orgs, _ := cliConnection.GetOrgs()
		fmt.Println("Orgs:", orgs)
		hasSpace, _ := cliConnection.HasSpace()
		fmt.Println("Has Space Targeted:", hasSpace)
		currentSpace, _ := cliConnection.GetCurrentSpace()
		fmt.Println("Current space:", currentSpace)
		space, _ := cliConnection.GetSpace(currentSpace.Name)
		fmt.Println("Space:", space)

		loggregator, _ := cliConnection.LoggregatorEndpoint()
		fmt.Println("Loggregator Endpoint:", loggregator)
		dopplerEndpoint, _ := cliConnection.DopplerEndpoint()
		fmt.Println("Doppler Endpoint:", dopplerEndpoint)

		user, _ := cliConnection.Username()
		fmt.Println("Current user:", user)
		userGuid, _ := cliConnection.UserGuid()
		fmt.Println("Current user guid:", userGuid)
		email, _ := cliConnection.UserEmail()
		fmt.Println("Current user email:", email)

		hasAPI, _ := cliConnection.HasAPIEndpoint()
		fmt.Println("Has API Endpoint:", hasAPI)
		api, _ := cliConnection.ApiEndpoint()
		fmt.Println("Current api:", api)
		version, _ := cliConnection.ApiVersion()
		fmt.Println("Current api version:", version)

		loggedIn, _ := cliConnection.IsLoggedIn()
		fmt.Println("Is Logged In:", loggedIn)
		isSSLDisabled, _ := cliConnection.IsSSLDisabled()
		fmt.Println("Is SSL Disabled:", isSSLDisabled)
	} else if args[0] == "test_1_cmd1" {
		theFirstCmd()
	} else if args[0] == "test_1_cmd2" {
		theSecondCmd()
	} else if args[0] == "CLI-MESSAGE-UNINSTALL" {
		uninstalling()
	}
}

func (c *Test1) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "Test1",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 2,
			Build: 4,
		},
		MinCliVersion: plugin.VersionType{
			Major: 5,
			Minor: 0,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "test_1_cmd1",
				Alias:    "test_1_cmd1_alias",
				HelpText: "help text for test_1_cmd1",
				UsageDetails: plugin.Usage{
					Usage: "Test plugin command\n   cf test_1_cmd1 [-a] [-b] [--no-ouput]",
					Options: map[string]string{
						"a":         "flag to do nothing",
						"b":         "another flag to do nothing",
						"no-output": "example option with no use",
					},
				},
			},
			{
				Name:     "test_1_cmd2",
				HelpText: "help text for test_1_cmd2",
			},
			{
				Name:     "new-api",
				HelpText: "test new api for plugins",
			},
		},
	}
}

func theFirstCmd() {
	fmt.Println("You called cmd1 in test_1")
}

func theSecondCmd() {
	fmt.Println("You called cmd2 in test_1")
}

func uninstalling() {
	os.Remove(filepath.Join(os.TempDir(), "uninstall-test-file-for-test_1.exe"))
}

func main() {
	plugin.Start(new(Test1))
}
<commit_msg>added GetSpaces to api test plugin<commit_after>/**
	* 1. Setup the server so cf can call it under main.
				e.g. `cf my-plugin` creates the callable server. now we can call the Run command
	* 2. Implement Run that is the actual code of the plugin!
	* 3. Return an error
**/

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/cloudfoundry/cli/plugin"
)

type Test1 struct {
}

func (c *Test1) Run(cliConnection plugin.CliConnection, args []string) {
	if args[0] == "new-api" {
		token, _ := cliConnection.AccessToken()
		fmt.Println("Access Token:", token)
		fmt.Println("")

		app, err := cliConnection.GetApp("test_app")
		fmt.Println("err for test_app", err)
		fmt.Println("test_app is: ", app)

		hasOrg, _ := cliConnection.HasOrganization()
		fmt.Println("Has Organization Targeted:", hasOrg)
		currentOrg, _ := cliConnection.GetCurrentOrg()
		fmt.Println("Current Org:", currentOrg)
		org, _ := cliConnection.GetOrg(currentOrg.Name)
		fmt.Println(currentOrg.Name, " Org:", org)
		orgs, _ := cliConnection.GetOrgs()
		fmt.Println("Orgs:", orgs)
		hasSpace, _ := cliConnection.HasSpace()
		fmt.Println("Has Space Targeted:", hasSpace)
		currentSpace, _ := cliConnection.GetCurrentSpace()
		fmt.Println("Current space:", currentSpace)
		space, _ := cliConnection.GetSpace(currentSpace.Name)
		fmt.Println("Space:", space)
		spaces, _ := cliConnection.GetSpaces()
		fmt.Println("Spaces:", spaces)
		loggregator, _ := cliConnection.LoggregatorEndpoint()
		fmt.Println("Loggregator Endpoint:", loggregator)
		dopplerEndpoint, _ := cliConnection.DopplerEndpoint()
		fmt.Println("Doppler Endpoint:", dopplerEndpoint)

		user, _ := cliConnection.Username()
		fmt.Println("Current user:", user)
		userGuid, _ := cliConnection.UserGuid()
		fmt.Println("Current user guid:", userGuid)
		email, _ := cliConnection.UserEmail()
		fmt.Println("Current user email:", email)

		hasAPI, _ := cliConnection.HasAPIEndpoint()
		fmt.Println("Has API Endpoint:", hasAPI)
		api, _ := cliConnection.ApiEndpoint()
		fmt.Println("Current api:", api)
		version, _ := cliConnection.ApiVersion()
		fmt.Println("Current api version:", version)

		loggedIn, _ := cliConnection.IsLoggedIn()
		fmt.Println("Is Logged In:", loggedIn)
		isSSLDisabled, _ := cliConnection.IsSSLDisabled()
		fmt.Println("Is SSL Disabled:", isSSLDisabled)
	} else if args[0] == "test_1_cmd1" {
		theFirstCmd()
	} else if args[0] == "test_1_cmd2" {
		theSecondCmd()
	} else if args[0] == "CLI-MESSAGE-UNINSTALL" {
		uninstalling()
	}
}

func (c *Test1) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "Test1",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 2,
			Build: 4,
		},
		MinCliVersion: plugin.VersionType{
			Major: 5,
			Minor: 0,
			Build: 0,
		},
		Commands: []plugin.Command{
			{
				Name:     "test_1_cmd1",
				Alias:    "test_1_cmd1_alias",
				HelpText: "help text for test_1_cmd1",
				UsageDetails: plugin.Usage{
					Usage: "Test plugin command\n   cf test_1_cmd1 [-a] [-b] [--no-ouput]",
					Options: map[string]string{
						"a":         "flag to do nothing",
						"b":         "another flag to do nothing",
						"no-output": "example option with no use",
					},
				},
			},
			{
				Name:     "test_1_cmd2",
				HelpText: "help text for test_1_cmd2",
			},
			{
				Name:     "new-api",
				HelpText: "test new api for plugins",
			},
		},
	}
}

func theFirstCmd() {
	fmt.Println("You called cmd1 in test_1")
}

func theSecondCmd() {
	fmt.Println("You called cmd2 in test_1")
}

func uninstalling() {
	os.Remove(filepath.Join(os.TempDir(), "uninstall-test-file-for-test_1.exe"))
}

func main() {
	plugin.Start(new(Test1))
}
<|endoftext|>


<commit_before>package spatialite

import (
	"database/sql"
	"errors"

	"github.com/mattn/go-sqlite3"
)

type entrypoint struct {
	lib  string
	proc string
}

var LibNames = []entrypoint{
	{"mod_spatialite", "sqlite3_modspatialite_init"},
	{"libspatialite7", "sqlite3_spatialite_init"},
	{"libspatialite5", "sqlite3_spatialite_init"},
	{"libspatialite3", "sqlite3_spatialite_init"},
	{"libspatialite", "sqlite3_modspatialite_init"},
}

var ErrSpatialiteNotFound = errors.New("shaxbee/go-spatialite: spatialite extension not found.")

func init() {
	sql.Register("spatialite", &sqlite3.SQLiteDriver{
		ConnectHook: func(conn *sqlite3.SQLiteConn) error {
			for _, v := range LibNames {
				if err := conn.LoadExtension(v.lib, v.proc); err == nil {
					return nil
				}
			}
			return ErrSpatialiteNotFound
		},
	})
}
<commit_msg>Fix entrypoints on Debian<commit_after>package spatialite

import (
	"database/sql"
	"errors"

	"github.com/mattn/go-sqlite3"
)

type entrypoint struct {
	lib  string
	proc string
}

var LibNames = []entrypoint{
	{"mod_spatialite", "sqlite3_modspatialite_init"},
	{"libspatialite.so", "sqlite3_modspatialite_init"},
}

var ErrSpatialiteNotFound = errors.New("shaxbee/go-spatialite: spatialite extension not found.")

func init() {
	sql.Register("spatialite", &sqlite3.SQLiteDriver{
		ConnectHook: func(conn *sqlite3.SQLiteConn) error {
			for _, v := range LibNames {
				if err := conn.LoadExtension(v.lib, v.proc); err == nil {
					return nil
				}
			}
			return ErrSpatialiteNotFound
		},
	})
}
<|endoftext|>


<commit_before>package udpsender

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"time"
)

type RecvService struct {
	key          string
	ch           chan *RecvBlock
	transactions map[string]*RecvTransaction
	sock         *net.UDPConn
	fn           RecvHandleFunc
	disk         bool
	dir          string
}

type RecvTransaction struct {
	num         int
	blocks      map[int]*RecvBlock
	lastTime    int64
	blockSize   int64
	Speed       float64
	completed   bool
	Addr        *net.UDPAddr
	Transaction []byte
}

type RecvBlock struct {
	transaction             []byte
	blockHash               []byte
	fileHash                []byte
	fileData                []byte
	num, id                 int32
	start, end, size, size1 int64
	time1                   int64
	addr                    *net.UDPAddr
}

type RecvHandleFunc func(service *RecvService, transaction *RecvTransaction)

func InitRecvService(key string, url string, fn RecvHandleFunc) (service *RecvService, err error) {
	service = new(RecvService)
	service.key = key
	service.fn = fn
	service.ch = make(chan *RecvBlock, 100)
	service.transactions = map[string]*RecvTransaction{}

	addr, err := net.ResolveUDPAddr("udp4", url)
	if err != nil {
		return nil, err
	}
	service.sock, err = net.ListenUDP("udp4", addr)
	if err != nil {
		return nil, err
	}
	go service.loop()

	return service, nil
}

func InitRecvTransaction(block *RecvBlock) *RecvTransaction {
	transaction := new(RecvTransaction)
	transaction.blocks = map[int]*RecvBlock{}
	transaction.lastTime = time.Now().UnixNano()
	transaction.blockSize = block.size1
	transaction.num = int(block.num)
	transaction.Addr = block.addr
	transaction.Transaction = block.transaction
	return transaction
}
func (service *RecvService) EnableFileStorage(dir string) {
	service.disk = true
	service.dir = dir
}
func (service *RecvService) processRecvBlock(block *RecvBlock) {
	stransaction := hex.EncodeToString(block.transaction)
	transaction, ok := service.transactions[stransaction]
	if !ok {
		fmt.Println("Creating transaction:", stransaction)
		transaction = InitRecvTransaction(block)
		service.transactions[stransaction] = transaction
	}
	if transaction.num != int(block.num) {
		// error
	}
	block.time1 = time.Now().UnixNano()
	fdtime := float64(block.time1 - transaction.lastTime)
	transaction.lastTime = block.time1
	speed := float64(len(block.fileData)) / fdtime * float64(time.Second)
	transaction.Speed = movingExpAvg(speed, transaction.Speed, fdtime, float64(time.Second))

	_, ok = transaction.blocks[int(block.id)]
	if ok {
		// dupe
		return
	}
	transaction.blocks[int(block.id)] = block
	// fmt.Printf("got %d %d %d\n%s\n", block.id, len(transaction.blocks), transaction.num, string(block.fileData))
	fmt.Printf("got %d %d %d %f\n", block.id, len(transaction.blocks), transaction.num, transaction.Speed)
	if len(transaction.blocks) == transaction.num {
		transaction.completed = true
		service.fn(service, transaction)

		// if service.disk {

		// 	os.MkdirAll(service.dir+"/"+stransaction, 0700)

		// 	f1, err := os.Create(service.dir + "/" + stransaction + "/file.out")
		// 	if err != nil {
		// 		// panic(err)
		// 		return
		// 	}
		// 	// defer f1.Close()

		// 	for i := 1; i <= transaction.num; i++ {
		// 		block1, ok := transaction.blocks[i]
		// 		if !ok {
		// 			// error
		// 			panic(errors.New(fmt.Sprintf("Not all blocks %d", i)))
		// 		}
		// 		f1.Write(block1.fileData)
		// 	}
		// 	f1.Close()
		// 	block1.fileData = nil
		// }

	}
}
func (service *RecvService) WriteTo(transaction *RecvTransaction, w io.Writer) error {
	if service.disk {
		for i := 1; i <= transaction.num; i++ {
			block1, ok := transaction.blocks[i]
			if !ok {
				// error
				return errors.New(fmt.Sprintf("Not all blocks %d", i))
			}
			// w.Write(block1.fileData)

			f1, err := os.Open(service.dir + "/" + hex.EncodeToString(block1.transaction) + "/" + fmt.Sprintf("%d.data", block1.id))
			if err != nil {
				// panic(err)
				return err
			}
			io.Copy(w, f1)
		}
	} else {
		for i := 1; i <= transaction.num; i++ {
			block1, ok := transaction.blocks[i]
			if !ok {
				// error
				return errors.New(fmt.Sprintf("Not all blocks %d", i))
			}
			w.Write(block1.fileData)
		}
	}

	return nil
}
func (service *RecvService) Stop() {
	service.sock.Close()
}
func (service *RecvService) Loop() error {

	for {
		// len, remote, err := sock.ReadFromUDP(buf[:])
		var buf [1 << 16]byte
		len, remote, err := service.sock.ReadFromUDP(buf[:])
		if err != nil {
			// 	(err)
			return err
		}
		service.processPacket(remote, buf[:len])
	}
}
func (service *RecvService) loop() {

	ticker := time.Tick(100 * time.Millisecond)
	for {

		select {
		case blck, ok := <-service.ch:
			if ok {
				service.processRecvBlock(blck)
			}
		case <-ticker:
			time1 := time.Now().UnixNano()
			for _, transaction := range service.transactions {
				if transaction.completed {
					continue
				}

				fdtime := float64(time1 - transaction.lastTime)
				transaction.lastTime = time1
				transaction.Speed = movingExpAvg(0.0, transaction.Speed, fdtime, float64(time.Second))

				//
				// find last_block
				last_block := -1
				var block *RecvBlock
				for i, _block := range transaction.blocks {
					if last_block < i {
						last_block = i
						block = _block
					}
				}
				if last_block == -1 {
					continue
				}
				// dtime  = Now - last_block.time
				dtime := time1 - block.time1
				// last_block + (dtime-100ms)*speed/blocksize
				shift := last_block + int((float64(dtime)-100.0*float64(time.Millisecond))*transaction.Speed/float64(transaction.blockSize))
				// get first 100 un recieved blocks

				req := []int{}

				for i := 1; i <= transaction.num; i++ {
					if i > shift {
						break
					}
					_, ok := transaction.blocks[i]
					if !ok {
						req = append(req, i)
						if len(req) >= 100 {
							break
						}
					}
				}
				// fmt.Printf(":%d %d#", len(req), shift)
				fmt.Println("@", shift, last_block, dtime/int64(time.Millisecond), dtime, transaction.Speed, float64(transaction.blockSize))

				if len(req) > 0 {
					service.rerequestPackets(transaction, req)
				}
			}
		}

	}

}
func (service *RecvService) rerequestPackets(transaction *RecvTransaction, req []int) error {
	fmt.Printf("rerequestPackets %#v\n", req)
	r1 := make([]byte, 16)
	_, err := rand.Read(r1)
	if err != nil {
		// panic(err)
		return err
	}

	data := append(transaction.Transaction, r1...)

	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.LittleEndian, int32(1))
	binary.Write(buffer, binary.LittleEndian, int32(len(req)))
	for _, i := range req {
		binary.Write(buffer, binary.LittleEndian, int32(i))
	}

	block, err := aes.NewCipher([]byte(service.key))
	if err != nil {
		// panic(err)
		return err
	}

	data1 := buffer.Bytes()

	stream := cipher.NewCFBEncrypter(block, r1)
	stream.XORKeyStream(data1, data1)

	data = append(data, data1...)

	service.sock.WriteToUDP(data, transaction.Addr)
	return nil
}
func (service *RecvService) processPacket(addr *net.UDPAddr, buf []byte) error {
	blck := new(RecvBlock)
	blck.transaction = buf[:16]
	blck.addr = addr
	r1 := buf[16:32]
	data2 := buf[32:]

	block, err := aes.NewCipher([]byte(service.key))
	if err != nil {
		// panic(err)
		return err
	}

	stream := cipher.NewCFBDecrypter(block, r1)

	stream.XORKeyStream(data2, data2)

	// fileHash := data2[0:20]

	buffer := bytes.NewBuffer(data2[20:52])
	binary.Read(buffer, binary.LittleEndian, &blck.id)
	binary.Read(buffer, binary.LittleEndian, &blck.num)
	binary.Read(buffer, binary.LittleEndian, &blck.start)
	binary.Read(buffer, binary.LittleEndian, &blck.end)
	binary.Read(buffer, binary.LittleEndian, &blck.size)
	blck.blockHash = data2[52:72]
	blck.size1 = int64(len(data2[72:]))
	bufCopy := make([]byte, blck.size1)
	copy(bufCopy[:], data2[72:])
	blck.fileData = bufCopy
	blockHash := sha1.Sum(blck.fileData)
	verified := bytes.Equal(blck.blockHash, blockHash[:])

	fmt.Println("processPacker:", blck.id, hex.EncodeToString(r1))

	if verified {
		if service.disk {
			os.MkdirAll(service.dir+"/"+hex.EncodeToString(blck.transaction), 0700)
			f1, err := os.Create(service.dir + "/" + hex.EncodeToString(blck.transaction) + "/" + fmt.Sprintf("%d.data", blck.id))
			if err != nil {
				// panic(err)
				return err
			}
			defer f1.Close()
			f1.Write(blck.fileData)
			blck.fileData = nil
		}
		service.ch <- blck
	} else {
		fmt.Println("wrong hash %#v %#v", blck.blockHash, blockHash[:])
	}
	return nil
}
<commit_msg>logging+<commit_after>package udpsender

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"time"
)

type RecvService struct {
	key          string
	ch           chan *RecvBlock
	transactions map[string]*RecvTransaction
	sock         *net.UDPConn
	fn           RecvHandleFunc
	disk         bool
	dir          string
}

type RecvTransaction struct {
	num         int
	blocks      map[int]*RecvBlock
	lastTime    int64
	blockSize   int64
	Speed       float64
	completed   bool
	Addr        *net.UDPAddr
	Transaction []byte
}

type RecvBlock struct {
	transaction             []byte
	blockHash               []byte
	fileHash                []byte
	fileData                []byte
	num, id                 int32
	start, end, size, size1 int64
	time1                   int64
	addr                    *net.UDPAddr
}

type RecvHandleFunc func(service *RecvService, transaction *RecvTransaction)

func InitRecvService(key string, url string, fn RecvHandleFunc) (service *RecvService, err error) {
	service = new(RecvService)
	service.key = key
	service.fn = fn
	service.ch = make(chan *RecvBlock, 100)
	service.transactions = map[string]*RecvTransaction{}

	addr, err := net.ResolveUDPAddr("udp4", url)
	if err != nil {
		return nil, err
	}
	service.sock, err = net.ListenUDP("udp4", addr)
	if err != nil {
		return nil, err
	}
	go service.loop()

	return service, nil
}

func InitRecvTransaction(block *RecvBlock) *RecvTransaction {
	transaction := new(RecvTransaction)
	transaction.blocks = map[int]*RecvBlock{}
	transaction.lastTime = time.Now().UnixNano()
	transaction.blockSize = block.size1
	transaction.num = int(block.num)
	transaction.Addr = block.addr
	transaction.Transaction = block.transaction
	return transaction
}
func (service *RecvService) EnableFileStorage(dir string) {
	service.disk = true
	service.dir = dir
}
func (service *RecvService) processRecvBlock(block *RecvBlock) {
	stransaction := hex.EncodeToString(block.transaction)
	transaction, ok := service.transactions[stransaction]
	if !ok {
		fmt.Println("Creating transaction:", stransaction)
		transaction = InitRecvTransaction(block)
		service.transactions[stransaction] = transaction
	}
	if transaction.num != int(block.num) {
		// error
	}
	block.time1 = time.Now().UnixNano()
	fdtime := float64(block.time1 - transaction.lastTime)
	transaction.lastTime = block.time1
	speed := float64(len(block.fileData)) / fdtime * float64(time.Second)
	transaction.Speed = movingExpAvg(speed, transaction.Speed, fdtime, float64(time.Second))

	_, ok = transaction.blocks[int(block.id)]
	if ok {
		// dupe
		return
	}
	transaction.blocks[int(block.id)] = block
	// fmt.Printf("got %d %d %d\n%s\n", block.id, len(transaction.blocks), transaction.num, string(block.fileData))
	fmt.Printf("got %d %d %d %f\n", block.id, len(transaction.blocks), transaction.num, transaction.Speed)
	if len(transaction.blocks) == transaction.num {
		transaction.completed = true
		service.fn(service, transaction)

		// if service.disk {

		// 	os.MkdirAll(service.dir+"/"+stransaction, 0700)

		// 	f1, err := os.Create(service.dir + "/" + stransaction + "/file.out")
		// 	if err != nil {
		// 		// panic(err)
		// 		return
		// 	}
		// 	// defer f1.Close()

		// 	for i := 1; i <= transaction.num; i++ {
		// 		block1, ok := transaction.blocks[i]
		// 		if !ok {
		// 			// error
		// 			panic(errors.New(fmt.Sprintf("Not all blocks %d", i)))
		// 		}
		// 		f1.Write(block1.fileData)
		// 	}
		// 	f1.Close()
		// 	block1.fileData = nil
		// }

	}
}
func (service *RecvService) WriteTo(transaction *RecvTransaction, w io.Writer) error {
	if service.disk {
		for i := 1; i <= transaction.num; i++ {
			block1, ok := transaction.blocks[i]
			if !ok {
				// error
				return errors.New(fmt.Sprintf("Not all blocks %d", i))
			}
			// w.Write(block1.fileData)

			f1, err := os.Open(service.dir + "/" + hex.EncodeToString(block1.transaction) + "/" + fmt.Sprintf("%d.data", block1.id))
			if err != nil {
				// panic(err)
				return err
			}
			io.Copy(w, f1)
		}
	} else {
		for i := 1; i <= transaction.num; i++ {
			block1, ok := transaction.blocks[i]
			if !ok {
				// error
				return errors.New(fmt.Sprintf("Not all blocks %d", i))
			}
			w.Write(block1.fileData)
		}
	}

	return nil
}
func (service *RecvService) Stop() {
	service.sock.Close()
}
func (service *RecvService) Loop() error {

	for {
		// len, remote, err := sock.ReadFromUDP(buf[:])
		var buf [1 << 16]byte
		len, remote, err := service.sock.ReadFromUDP(buf[:])
		if err != nil {
			// 	(err)
			return err
		}
		fmt.Print("+")
		service.processPacket(remote, buf[:len])
	}
}
func (service *RecvService) loop() {

	ticker := time.Tick(100 * time.Millisecond)
	for {

		select {
		case blck, ok := <-service.ch:
			if ok {
				service.processRecvBlock(blck)
			}
		case <-ticker:
			time1 := time.Now().UnixNano()
			for _, transaction := range service.transactions {
				if transaction.completed {
					continue
				}

				fdtime := float64(time1 - transaction.lastTime)
				transaction.lastTime = time1
				transaction.Speed = movingExpAvg(0.0, transaction.Speed, fdtime, float64(time.Second))

				//
				// find last_block
				last_block := -1
				var block *RecvBlock
				for i, _block := range transaction.blocks {
					if last_block < i {
						last_block = i
						block = _block
					}
				}
				if last_block == -1 {
					continue
				}
				// dtime  = Now - last_block.time
				dtime := time1 - block.time1
				// last_block + (dtime-100ms)*speed/blocksize
				shift := last_block + int((float64(dtime)-100.0*float64(time.Millisecond))*transaction.Speed/float64(transaction.blockSize))
				// get first 100 un recieved blocks

				req := []int{}

				for i := 1; i <= transaction.num; i++ {
					if i > shift {
						break
					}
					_, ok := transaction.blocks[i]
					if !ok {
						req = append(req, i)
						if len(req) >= 100 {
							break
						}
					}
				}
				// fmt.Printf(":%d %d#", len(req), shift)
				fmt.Println("@", shift, last_block, dtime/int64(time.Millisecond), dtime, transaction.Speed, float64(transaction.blockSize))

				if len(req) > 0 {
					service.rerequestPackets(transaction, req)
				}
			}
		}

	}

}
func (service *RecvService) rerequestPackets(transaction *RecvTransaction, req []int) error {
	fmt.Printf("rerequestPackets %#v\n", req)
	r1 := make([]byte, 16)
	_, err := rand.Read(r1)
	if err != nil {
		// panic(err)
		return err
	}

	data := append(transaction.Transaction, r1...)

	buffer := new(bytes.Buffer)
	binary.Write(buffer, binary.LittleEndian, int32(1))
	binary.Write(buffer, binary.LittleEndian, int32(len(req)))
	for _, i := range req {
		binary.Write(buffer, binary.LittleEndian, int32(i))
	}

	block, err := aes.NewCipher([]byte(service.key))
	if err != nil {
		// panic(err)
		return err
	}

	data1 := buffer.Bytes()

	stream := cipher.NewCFBEncrypter(block, r1)
	stream.XORKeyStream(data1, data1)

	data = append(data, data1...)

	service.sock.WriteToUDP(data, transaction.Addr)
	return nil
}
func (service *RecvService) processPacket(addr *net.UDPAddr, buf []byte) error {
	blck := new(RecvBlock)
	blck.transaction = buf[:16]
	blck.addr = addr
	r1 := buf[16:32]
	data2 := buf[32:]

	block, err := aes.NewCipher([]byte(service.key))
	if err != nil {
		// panic(err)
		return err
	}

	stream := cipher.NewCFBDecrypter(block, r1)

	stream.XORKeyStream(data2, data2)

	// fileHash := data2[0:20]

	buffer := bytes.NewBuffer(data2[20:52])
	binary.Read(buffer, binary.LittleEndian, &blck.id)
	binary.Read(buffer, binary.LittleEndian, &blck.num)
	binary.Read(buffer, binary.LittleEndian, &blck.start)
	binary.Read(buffer, binary.LittleEndian, &blck.end)
	binary.Read(buffer, binary.LittleEndian, &blck.size)
	blck.blockHash = data2[52:72]
	blck.size1 = int64(len(data2[72:]))
	bufCopy := make([]byte, blck.size1)
	copy(bufCopy[:], data2[72:])
	blck.fileData = bufCopy
	blockHash := sha1.Sum(blck.fileData)
	verified := bytes.Equal(blck.blockHash, blockHash[:])

	fmt.Println("processPacker:", blck.id, hex.EncodeToString(r1))

	if verified {
		if service.disk {
			os.MkdirAll(service.dir+"/"+hex.EncodeToString(blck.transaction), 0700)
			f1, err := os.Create(service.dir + "/" + hex.EncodeToString(blck.transaction) + "/" + fmt.Sprintf("%d.data", blck.id))
			if err != nil {
				// panic(err)
				return err
			}
			defer f1.Close()
			f1.Write(blck.fileData)
			blck.fileData = nil
		}
		service.ch <- blck
	} else {
		fmt.Println("wrong hash %#v %#v", blck.blockHash, blockHash[:])
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Package ring provides a simple implementation of a ring buffer.
*/
package ring

/*
The DefaultCapacity of an uninitialized Ring buffer.

Changing this value only affects ring buffers created after it is changed.
*/
var DefaultCapacity int = 10

/*
Type Ring implements a Circular Buffer.
The default value of the Ring struct is a valid (empty) Ring buffer with capacity DefaultCapacify.
*/
type Ring struct {
	head int // the most recent value written
	tail int // the least recent value written
	buff []interface{}
}

/*
Set the maximum size of the ring buffer.
*/
func (r *Ring) SetCapacity(size int) {
	r.checkInit()
	r.extend(size)
}

/*
Capacity returns the current capacity of the ring buffer.
*/
func (r Ring) Capacity() int {
	return len(r.buff)
}

/*
Enqueue a value into the Ring buffer.
*/
func (r *Ring) Enqueue(i interface{}) {
	r.checkInit()
	r.set(r.head+1, i)
	old := r.head
	r.head = r.mod(r.head+1)
	if old != -1 && r.head == r.tail {
		r.tail = r.mod(r.tail + 1)
	}
}

/*
Dequeue a value from the Ring buffer.

Returns nil if the ring buffer is empty.
*/
func (r *Ring) Dequeue() interface{} {
	r.checkInit()
	if r.head == -1 {
		return nil
	}
	v := r.get(r.tail)
	if r.tail == r.head {
		r.head = -1
		r.tail = 0
	} else {
		r.tail = r.mod(r.tail + 1)
	}
	return v
}

/*
Read the value that Dequeue would have dequeued without actually dequeuing it.

Returns nil if the ring buffer is empty.
*/
func (r *Ring) Peek() interface{} {
	r.checkInit()
	if r.head == -1 {
		return nil
	}
	return r.get(r.tail)
}

/*
Values returns a slice of all the values in the circular buffer without modifying them at all.
The returned slice can be modified independently of the circular buffer. However, the values inside the slice
are shared between the slice and circular buffer.
*/
func (r *Ring) Values() []interface{} {
	if r.head == -1 {
		return []interface{}{}
	}
	arr := make([]interface{}, 0, r.Capacity())
	for i := 0; i < r.Capacity(); i++ {
		idx := r.mod(i+r.tail)
		arr = append(arr, r.get(idx))
		if idx == r.head {
			break
		}
	}
	return arr
}

/**
*** Unexported methods beyond this point.
**/

// sets a value at the given unmodified index and returns the modified index of the value
func (r *Ring) set(p int, v interface{}) {
	r.buff[r.mod(p)] = v
}

// gets a value based at a given unmodified index
func (r *Ring) get(p int) interface{} {
	return r.buff[r.mod(p)]
}

// returns the modified index of an unmodified index
func (r *Ring) mod(p int) int {
	return p % len(r.buff)
}

func (r *Ring) checkInit() {
	if r.buff == nil {
		r.buff = make([]interface{}, DefaultCapacity)
		for i := range r.buff {
			r.buff[i] = nil
		}
		r.head, r.tail = -1, 0
	}
}

func (r *Ring) extend(size int) {
	if size == len(r.buff) {
		return
	} else if size < len(r.buff) {
		r.buff = r.buff[0:size]
	}
	newb := make([]interface{}, size-len(r.buff))
	for i := range newb {
		newb[i] = nil
	}
	r.buff = append(r.buff, newb...)
}
<commit_msg>go fmt<commit_after>/*
Package ring provides a simple implementation of a ring buffer.
*/
package ring

/*
The DefaultCapacity of an uninitialized Ring buffer.

Changing this value only affects ring buffers created after it is changed.
*/
var DefaultCapacity int = 10

/*
Type Ring implements a Circular Buffer.
The default value of the Ring struct is a valid (empty) Ring buffer with capacity DefaultCapacify.
*/
type Ring struct {
	head int // the most recent value written
	tail int // the least recent value written
	buff []interface{}
}

/*
Set the maximum size of the ring buffer.
*/
func (r *Ring) SetCapacity(size int) {
	r.checkInit()
	r.extend(size)
}

/*
Capacity returns the current capacity of the ring buffer.
*/
func (r Ring) Capacity() int {
	return len(r.buff)
}

/*
Enqueue a value into the Ring buffer.
*/
func (r *Ring) Enqueue(i interface{}) {
	r.checkInit()
	r.set(r.head+1, i)
	old := r.head
	r.head = r.mod(r.head + 1)
	if old != -1 && r.head == r.tail {
		r.tail = r.mod(r.tail + 1)
	}
}

/*
Dequeue a value from the Ring buffer.

Returns nil if the ring buffer is empty.
*/
func (r *Ring) Dequeue() interface{} {
	r.checkInit()
	if r.head == -1 {
		return nil
	}
	v := r.get(r.tail)
	if r.tail == r.head {
		r.head = -1
		r.tail = 0
	} else {
		r.tail = r.mod(r.tail + 1)
	}
	return v
}

/*
Read the value that Dequeue would have dequeued without actually dequeuing it.

Returns nil if the ring buffer is empty.
*/
func (r *Ring) Peek() interface{} {
	r.checkInit()
	if r.head == -1 {
		return nil
	}
	return r.get(r.tail)
}

/*
Values returns a slice of all the values in the circular buffer without modifying them at all.
The returned slice can be modified independently of the circular buffer. However, the values inside the slice
are shared between the slice and circular buffer.
*/
func (r *Ring) Values() []interface{} {
	if r.head == -1 {
		return []interface{}{}
	}
	arr := make([]interface{}, 0, r.Capacity())
	for i := 0; i < r.Capacity(); i++ {
		idx := r.mod(i + r.tail)
		arr = append(arr, r.get(idx))
		if idx == r.head {
			break
		}
	}
	return arr
}

/**
*** Unexported methods beyond this point.
**/

// sets a value at the given unmodified index and returns the modified index of the value
func (r *Ring) set(p int, v interface{}) {
	r.buff[r.mod(p)] = v
}

// gets a value based at a given unmodified index
func (r *Ring) get(p int) interface{} {
	return r.buff[r.mod(p)]
}

// returns the modified index of an unmodified index
func (r *Ring) mod(p int) int {
	return p % len(r.buff)
}

func (r *Ring) checkInit() {
	if r.buff == nil {
		r.buff = make([]interface{}, DefaultCapacity)
		for i := range r.buff {
			r.buff[i] = nil
		}
		r.head, r.tail = -1, 0
	}
}

func (r *Ring) extend(size int) {
	if size == len(r.buff) {
		return
	} else if size < len(r.buff) {
		r.buff = r.buff[0:size]
	}
	newb := make([]interface{}, size-len(r.buff))
	for i := range newb {
		newb[i] = nil
	}
	r.buff = append(r.buff, newb...)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
	"github.com/libgit2/git2go"
	"github.com/unrolled/render"

	"github.com/wantedly/risu/registry"
	"github.com/wantedly/risu/schema"
)

const (
	SourceBasePath = "/var/risu/src/github.com/"
	CacheBasePath  = "/var/risu/cache"
)

var ren = render.New()
var reg = registry.NewRegistry(os.Getenv("REGISTRY_BACKEND"), os.Getenv("REGISTRY_ENDPOINT"))

func create(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	defer r.Body.Close()
	var opts schema.BuildCreateOpts
	err := json.NewDecoder(r.Body).Decode(&opts)
	if err != nil {
		log.Fatal(err)
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}

	if opts.SourceBranch == "" {
		opts.SourceBranch = "master"
	}

	if opts.Dockerfile == "" {
		opts.Dockerfile = "Dockerfile"
	}

	currentTime := time.Now()
	build := schema.Build{
		ID:           uuid.NewUUID(),
		SourceRepo:   opts.SourceRepo,
		SourceBranch: opts.SourceBranch,
		Name:         opts.Name,
		Dockerfile:   opts.Dockerfile,
		Status:       "building",
		CreatedAt:    currentTime,
		UpdatedAt:    currentTime,
	}

	err = reg.Set(build)
	if err != nil {
		log.Fatal(err)
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}
	ren.JSON(w, http.StatusAccepted, build)

	go func() {
		if err := gitClone(build); err != nil {
			return
		}

		if err := dockerBuild(build); err != nil {
			return
		}

		go dockerPush(build)
		go pushCache(build)
	}()
}

func root(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	ren.JSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	builds, err := reg.List()
	if err != nil {
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}
	ren.JSON(w, http.StatusOK, builds)
}

func show(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	id := ps.ByName("id")
	uuid := uuid.Parse(id)
	build, err := reg.Get(uuid)
	if err != nil {
		ren.JSON(w, http.StatusNotFound, map[string]string{"status": "not found"})
		return
	}
	ren.JSON(w, http.StatusOK, build)
}

// Clone run "git clone <repository_URL>" and "git checkout branch"
func gitClone(build schema.Build) error {
	if _, err := os.Stat(SourceBasePath); err != nil {
		os.MkdirAll(SourceBasePath, 0755)
	}

	// htpps://<token>@github.com/<SourceRepo>.git
	cloneURL := "https://" + os.Getenv("GITHUB_ACCESS_TOKEN") + "@github.com/" + build.SourceRepo + ".git"

	// debug
	fmt.Println(cloneURL)

	clonePath := SourceBasePath + build.SourceRepo

	// debug
	fmt.Println(clonePath)

	_, err := git.Clone(cloneURL, clonePath, &git.CloneOptions{CheckoutBranch: build.SourceBranch})
	if err != nil {
		return err
	}

func dockerBuild(build schema.Build) error {
	// TODO (@dtan4)
	return nil
}

func dockerPush(build schema.Build) error {
	// TODO (@koudaii)
	return nil
}

func pushCache(build schema.Build) error {
	// TODO (@dtan4)
	return nil
}

func setUpServer() *negroni.Negroni {
	router := httprouter.New()
	router.GET("/", root)
	router.GET("/builds", index)
	router.GET("/builds/:id", show)
	router.POST("/builds", create)

	n := negroni.Classic()
	n.UseHandler(router)
	return n
}

func main() {
	if os.Getenv("GITHUB_ACCESS_TOKEN") == "" {
		log.Fatal("Please provide 'GITHUB_ACCESS_TOKEN' through environment")
	}
	n := setUpServer()
	n.Run(":8080")
}
<commit_msg>Fix build error<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/negroni"
	"github.com/julienschmidt/httprouter"
	"github.com/libgit2/git2go"
	"github.com/unrolled/render"

	"github.com/wantedly/risu/registry"
	"github.com/wantedly/risu/schema"
)

const (
	SourceBasePath = "/var/risu/src/github.com/"
	CacheBasePath  = "/var/risu/cache"
)

var ren = render.New()
var reg = registry.NewRegistry(os.Getenv("REGISTRY_BACKEND"), os.Getenv("REGISTRY_ENDPOINT"))

func create(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	defer r.Body.Close()
	var opts schema.BuildCreateOpts
	err := json.NewDecoder(r.Body).Decode(&opts)
	if err != nil {
		log.Fatal(err)
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}

	if opts.SourceBranch == "" {
		opts.SourceBranch = "master"
	}

	if opts.Dockerfile == "" {
		opts.Dockerfile = "Dockerfile"
	}

	currentTime := time.Now()
	build := schema.Build{
		ID:           uuid.NewUUID(),
		SourceRepo:   opts.SourceRepo,
		SourceBranch: opts.SourceBranch,
		Name:         opts.Name,
		Dockerfile:   opts.Dockerfile,
		Status:       "building",
		CreatedAt:    currentTime,
		UpdatedAt:    currentTime,
	}

	err = reg.Set(build)
	if err != nil {
		log.Fatal(err)
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}
	ren.JSON(w, http.StatusAccepted, build)

	go func() {
		if err := gitClone(build); err != nil {
			return
		}

		if err := dockerBuild(build); err != nil {
			return
		}

		go dockerPush(build)
		go pushCache(build)
	}()
}

func root(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	ren.JSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	builds, err := reg.List()
	if err != nil {
		ren.JSON(w, http.StatusInternalServerError, map[string]string{"status": "internal server error"})
		return
	}
	ren.JSON(w, http.StatusOK, builds)
}

func show(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	id := ps.ByName("id")
	uuid := uuid.Parse(id)
	build, err := reg.Get(uuid)
	if err != nil {
		ren.JSON(w, http.StatusNotFound, map[string]string{"status": "not found"})
		return
	}
	ren.JSON(w, http.StatusOK, build)
}

// Clone run "git clone <repository_URL>" and "git checkout branch"
func gitClone(build schema.Build) error {
	if _, err := os.Stat(SourceBasePath); err != nil {
		os.MkdirAll(SourceBasePath, 0755)
	}

	// htpps://<token>@github.com/<SourceRepo>.git
	cloneURL := "https://" + os.Getenv("GITHUB_ACCESS_TOKEN") + "@github.com/" + build.SourceRepo + ".git"

	// debug
	fmt.Println(cloneURL)

	clonePath := SourceBasePath + build.SourceRepo

	// debug
	fmt.Println(clonePath)

	_, err := git.Clone(cloneURL, clonePath, &git.CloneOptions{CheckoutBranch: build.SourceBranch})
	if err != nil {
		return err
	}
}

func dockerBuild(build schema.Build) error {
	// TODO (@dtan4)
	return nil
}

func dockerPush(build schema.Build) error {
	// TODO (@koudaii)
	return nil
}

func pushCache(build schema.Build) error {
	// TODO (@dtan4)
	return nil
}

func setUpServer() *negroni.Negroni {
	router := httprouter.New()
	router.GET("/", root)
	router.GET("/builds", index)
	router.GET("/builds/:id", show)
	router.POST("/builds", create)

	n := negroni.Classic()
	n.UseHandler(router)
	return n
}

func main() {
	if os.Getenv("GITHUB_ACCESS_TOKEN") == "" {
		log.Fatal("Please provide 'GITHUB_ACCESS_TOKEN' through environment")
	}
	n := setUpServer()
	n.Run(":8080")
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strconv"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/hyperhq/runv/driverloader"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/term"

	"github.com/opencontainers/specs"
)

const shortLen = 12

func TruncateID(id string) string {
	trimTo := shortLen
	if len(id) < shortLen {
		trimTo = len(id)
	}
	return id[:trimTo]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 32)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
			continue
		}
		return value
	}
}

func resizeTty(vm *hypervisor.Vm, tag string, outFd uintptr, isTerminalOut bool) {
	height, width := getTtySize(outFd, isTerminalOut)
	if height == 0 && width == 0 {
		return
	}

	vm.Tty(tag, height, width)
}

func monitorTtySize(vm *hypervisor.Vm, tag string, outFd uintptr, isTerminalOut bool) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGWINCH)
	go func() {
		for range sigchan {
			resizeTty(vm, tag, outFd, isTerminalOut)
		}
	}()
}

func getTtySize(outFd uintptr, isTerminalOut bool) (int, int) {
	if !isTerminalOut {
		return 0, 0
	}
	ws, err := term.GetWinsize(outFd)
	if err != nil {
		fmt.Printf("Error getting size: %s", err.Error())
		if ws == nil {
			return 0, 0
		}
	}
	return int(ws.Height), int(ws.Width)
}

func getDefaultID() string {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return filepath.Base(cwd)
}

func removeState(podId, root string, sock net.Listener) {
	os.RemoveAll(path.Join(root, podId))
	sock.Close()
}

func saveState(vmId, podId, root string) (net.Listener, error) {
	podPath := path.Join(root, podId)

	_, err := os.Stat(podPath)
	if err == nil {
		return nil, fmt.Errorf("Container %s exists\n", podId)
	}

	err = os.MkdirAll(podPath, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	defer func() {
		if err != nil {
			os.RemoveAll(podPath)
		}
	}()

	pwd, err := filepath.Abs(".")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	state := specs.State{
		Version: specs.Version,
		ID:      vmId,
		Pid:     -1,
		Root:    pwd,
	}

	stateData, err := json.MarshalIndent(&state, "", "\t")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	err = ioutil.WriteFile(path.Join(podPath, "state.json"), stateData, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	sock, err := net.Listen("unix", path.Join(podPath, "runv.sock"))
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return sock, nil
}

func parseUserPod(context *cli.Context) (*pod.UserPod, error) {
	ocffile := context.String("config-file")
	runtimefile := context.String("runtime-file")

	if _, err := os.Stat(ocffile); os.IsNotExist(err) {
		fmt.Printf("Please specify ocffile or put config.json under current working directory\n")
		return nil, err
	}

	ocfData, err := ioutil.ReadFile(ocffile)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	var runtimeData []byte = nil
	_, err = os.Stat(runtimefile)
	if err != nil {
		if !os.IsNotExist(err) {
			fmt.Printf("Fail to stat %s, %s\n", runtimefile, err.Error())
			return nil, err
		}
	} else {
		runtimeData, err = ioutil.ReadFile(runtimefile)
		if err != nil {
			fmt.Printf("Fail to readfile %s, %s\n", runtimefile, err.Error())
			return nil, err
		}
	}

	userPod, err := pod.OCFConvert2Pod(ocfData, runtimeData)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return userPod, nil
}

func preparePod(mypod *hypervisor.PodStatus, userPod *pod.UserPod, vmId string) ([]*hypervisor.ContainerInfo, error) {
	var (
		containerInfoList []*hypervisor.ContainerInfo
		containerId       string
	)

	sharedDir := path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag)

	for _, c := range userPod.Containers {
		var root string
		var err error

		containerId = GenerateRandomID()
		rootDir := path.Join(sharedDir, containerId)
		os.MkdirAll(rootDir, 0755)

		rootDir = path.Join(rootDir, "rootfs")

		if !filepath.IsAbs(c.Image) {
			root, err = filepath.Abs(c.Image)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				return nil, err
			}
		} else {
			root = c.Image
		}

		err = mount(root, rootDir)
		if err != nil {
			fmt.Printf("mount %s to %s failed: %s\n", root, rootDir, err.Error())
			return nil, err
		}

		containerInfo := &hypervisor.ContainerInfo{
			Id:     containerId,
			Rootfs: "rootfs",
			Image:  containerId,
			Fstype: "dir",
		}

		containerInfoList = append(containerInfoList, containerInfo)
		mypod.AddContainer(containerId, mypod.Id, "", []string{}, types.S_POD_CREATED)
	}

	return containerInfoList, nil
}

func startVContainer(context *cli.Context) {
	var (
		err error
		cpu = 1
		mem = 128
	)

	hypervisor.InterfaceCount = 0

	vbox := context.GlobalString("vbox")
	if _, err = os.Stat(vbox); err == nil {
		vbox, err = filepath.Abs(vbox)
		if err != nil {
			fmt.Printf("Cannot get abs path for vbox: %s\n", err.Error())
			return
		}
	}

	kernel := context.GlobalString("kernel")
	if _, err = os.Stat(kernel); err == nil {
		kernel, err = filepath.Abs(kernel)
		if err != nil {
			fmt.Printf("Cannot get abs path for kernel: %s\n", err.Error())
			return
		}
	}

	initrd := context.GlobalString("initrd")
	if _, err = os.Stat(initrd); err == nil {
		initrd, err = filepath.Abs(initrd)
		if err != nil {
			fmt.Printf("Cannot get abs path for initrd: %s\n", err.Error())
			return
		}
	}

	driver := context.GlobalString("driver")
	if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	root := context.GlobalString("root")

	podId := context.GlobalString("id")
	vmId := fmt.Sprintf("vm-%s", pod.RandStr(10, "alpha"))

	fmt.Printf("runv container id: %s\n", podId)
	sock, err := saveState(vmId, podId, root)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer removeState(podId, root, sock)

	userPod, err := parseUserPod(context)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	mypod := hypervisor.NewPod(podId, userPod)
	infoList, err := preparePod(mypod, userPod, vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer func() {
		tmpDir := path.Join(hypervisor.BaseDir, vmId)
		for _, c := range mypod.Containers {
			rootDir := path.Join(tmpDir, hypervisor.ShareDirTag, c.Id, "rootfs")
			umount(rootDir)
		}
		os.RemoveAll(tmpDir)
	}()

	if userPod.Resource.Vcpu > 0 {
		cpu = userPod.Resource.Vcpu
	}

	if userPod.Resource.Memory > 0 {
		mem = userPod.Resource.Memory
	}

	b := &hypervisor.BootConfig{
		Kernel: kernel,
		Initrd: initrd,
		Bios:   "",
		Cbfs:   "",
		Vbox:   vbox,
		CPU:    cpu,
		Memory: mem,
	}

	vm := hypervisor.NewVm(vmId, cpu, mem, false, types.VM_KEEP_NONE)
	err = vm.LaunchOCIVm(b, sock)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	inFd, _ := term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut := term.GetFdInfo(os.Stdout)

	oldState, err := term.SetRawTerminal(inFd)
	if err != nil {
		return
	}

	defer term.RestoreTerminal(inFd, oldState)

	tag := pod.RandStr(8, "alphanum")
	ttyCallback := make(chan *types.VmResponse, 1)

	// using pipes in vm.Attach to avoid the stdio to be closed
	fromStd, toVm := io.Pipe()
	fromVm, toStd := io.Pipe()
	go io.Copy(toVm, os.Stdin)
	go io.Copy(os.Stdout, fromVm)

	err = vm.Attach(fromStd, toStd, tag, mypod.Containers[0].Id, ttyCallback, nil)
	if err != nil {
		fmt.Printf("StartPod fail: fail to set up tty connection.\n")
		return
	}

	qemuResponse := vm.StartPod(mypod, userPod, infoList, nil)
	if qemuResponse.Data == nil {
		fmt.Printf("StartPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", qemuResponse.Code, qemuResponse.Cause)

	resizeTty(vm, tag, outFd, isTerminalOut)
	monitorTtySize(vm, tag, outFd, isTerminalOut)
	<-ttyCallback

	qemuResponse = vm.StopPod(mypod, "yes")

	if qemuResponse.Data == nil {
		fmt.Printf("StopPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", qemuResponse.Code, qemuResponse.Cause)
}

var startCommand = cli.Command{
	Name:  "start",
	Usage: "create and run a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "config-file, c",
			Value: "config.json",
			Usage: "path to spec config file",
		},
		cli.StringFlag{
			Name:  "runtime-file, r",
			Value: "runtime.json",
			Usage: "path to runtime config file",
		},
	},
	Action: startVContainer,
}
<commit_msg>remove word qemu<commit_after>package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"strconv"
	"syscall"

	"github.com/codegangsta/cli"
	"github.com/hyperhq/runv/driverloader"
	"github.com/hyperhq/runv/hypervisor"
	"github.com/hyperhq/runv/hypervisor/pod"
	"github.com/hyperhq/runv/hypervisor/types"
	"github.com/hyperhq/runv/lib/term"

	"github.com/opencontainers/specs"
)

const shortLen = 12

func TruncateID(id string) string {
	trimTo := shortLen
	if len(id) < shortLen {
		trimTo = len(id)
	}
	return id[:trimTo]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 32)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(TruncateID(value), 10, 64); err == nil {
			continue
		}
		return value
	}
}

func resizeTty(vm *hypervisor.Vm, tag string, outFd uintptr, isTerminalOut bool) {
	height, width := getTtySize(outFd, isTerminalOut)
	if height == 0 && width == 0 {
		return
	}

	vm.Tty(tag, height, width)
}

func monitorTtySize(vm *hypervisor.Vm, tag string, outFd uintptr, isTerminalOut bool) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGWINCH)
	go func() {
		for range sigchan {
			resizeTty(vm, tag, outFd, isTerminalOut)
		}
	}()
}

func getTtySize(outFd uintptr, isTerminalOut bool) (int, int) {
	if !isTerminalOut {
		return 0, 0
	}
	ws, err := term.GetWinsize(outFd)
	if err != nil {
		fmt.Printf("Error getting size: %s", err.Error())
		if ws == nil {
			return 0, 0
		}
	}
	return int(ws.Height), int(ws.Width)
}

func getDefaultID() string {
	cwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	return filepath.Base(cwd)
}

func removeState(podId, root string, sock net.Listener) {
	os.RemoveAll(path.Join(root, podId))
	sock.Close()
}

func saveState(vmId, podId, root string) (net.Listener, error) {
	podPath := path.Join(root, podId)

	_, err := os.Stat(podPath)
	if err == nil {
		return nil, fmt.Errorf("Container %s exists\n", podId)
	}

	err = os.MkdirAll(podPath, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	defer func() {
		if err != nil {
			os.RemoveAll(podPath)
		}
	}()

	pwd, err := filepath.Abs(".")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	state := specs.State{
		Version: specs.Version,
		ID:      vmId,
		Pid:     -1,
		Root:    pwd,
	}

	stateData, err := json.MarshalIndent(&state, "", "\t")
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	err = ioutil.WriteFile(path.Join(podPath, "state.json"), stateData, 0644)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	sock, err := net.Listen("unix", path.Join(podPath, "runv.sock"))
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return sock, nil
}

func parseUserPod(context *cli.Context) (*pod.UserPod, error) {
	ocffile := context.String("config-file")
	runtimefile := context.String("runtime-file")

	if _, err := os.Stat(ocffile); os.IsNotExist(err) {
		fmt.Printf("Please specify ocffile or put config.json under current working directory\n")
		return nil, err
	}

	ocfData, err := ioutil.ReadFile(ocffile)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	var runtimeData []byte = nil
	_, err = os.Stat(runtimefile)
	if err != nil {
		if !os.IsNotExist(err) {
			fmt.Printf("Fail to stat %s, %s\n", runtimefile, err.Error())
			return nil, err
		}
	} else {
		runtimeData, err = ioutil.ReadFile(runtimefile)
		if err != nil {
			fmt.Printf("Fail to readfile %s, %s\n", runtimefile, err.Error())
			return nil, err
		}
	}

	userPod, err := pod.OCFConvert2Pod(ocfData, runtimeData)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return nil, err
	}

	return userPod, nil
}

func preparePod(mypod *hypervisor.PodStatus, userPod *pod.UserPod, vmId string) ([]*hypervisor.ContainerInfo, error) {
	var (
		containerInfoList []*hypervisor.ContainerInfo
		containerId       string
	)

	sharedDir := path.Join(hypervisor.BaseDir, vmId, hypervisor.ShareDirTag)

	for _, c := range userPod.Containers {
		var root string
		var err error

		containerId = GenerateRandomID()
		rootDir := path.Join(sharedDir, containerId)
		os.MkdirAll(rootDir, 0755)

		rootDir = path.Join(rootDir, "rootfs")

		if !filepath.IsAbs(c.Image) {
			root, err = filepath.Abs(c.Image)
			if err != nil {
				fmt.Printf("%s\n", err.Error())
				return nil, err
			}
		} else {
			root = c.Image
		}

		err = mount(root, rootDir)
		if err != nil {
			fmt.Printf("mount %s to %s failed: %s\n", root, rootDir, err.Error())
			return nil, err
		}

		containerInfo := &hypervisor.ContainerInfo{
			Id:     containerId,
			Rootfs: "rootfs",
			Image:  containerId,
			Fstype: "dir",
		}

		containerInfoList = append(containerInfoList, containerInfo)
		mypod.AddContainer(containerId, mypod.Id, "", []string{}, types.S_POD_CREATED)
	}

	return containerInfoList, nil
}

func startVContainer(context *cli.Context) {
	var (
		err error
		cpu = 1
		mem = 128
	)

	hypervisor.InterfaceCount = 0

	vbox := context.GlobalString("vbox")
	if _, err = os.Stat(vbox); err == nil {
		vbox, err = filepath.Abs(vbox)
		if err != nil {
			fmt.Printf("Cannot get abs path for vbox: %s\n", err.Error())
			return
		}
	}

	kernel := context.GlobalString("kernel")
	if _, err = os.Stat(kernel); err == nil {
		kernel, err = filepath.Abs(kernel)
		if err != nil {
			fmt.Printf("Cannot get abs path for kernel: %s\n", err.Error())
			return
		}
	}

	initrd := context.GlobalString("initrd")
	if _, err = os.Stat(initrd); err == nil {
		initrd, err = filepath.Abs(initrd)
		if err != nil {
			fmt.Printf("Cannot get abs path for initrd: %s\n", err.Error())
			return
		}
	}

	driver := context.GlobalString("driver")
	if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	root := context.GlobalString("root")

	podId := context.GlobalString("id")
	vmId := fmt.Sprintf("vm-%s", pod.RandStr(10, "alpha"))

	fmt.Printf("runv container id: %s\n", podId)
	sock, err := saveState(vmId, podId, root)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer removeState(podId, root, sock)

	userPod, err := parseUserPod(context)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	mypod := hypervisor.NewPod(podId, userPod)
	infoList, err := preparePod(mypod, userPod, vmId)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	defer func() {
		tmpDir := path.Join(hypervisor.BaseDir, vmId)
		for _, c := range mypod.Containers {
			rootDir := path.Join(tmpDir, hypervisor.ShareDirTag, c.Id, "rootfs")
			umount(rootDir)
		}
		os.RemoveAll(tmpDir)
	}()

	if userPod.Resource.Vcpu > 0 {
		cpu = userPod.Resource.Vcpu
	}

	if userPod.Resource.Memory > 0 {
		mem = userPod.Resource.Memory
	}

	b := &hypervisor.BootConfig{
		Kernel: kernel,
		Initrd: initrd,
		Bios:   "",
		Cbfs:   "",
		Vbox:   vbox,
		CPU:    cpu,
		Memory: mem,
	}

	vm := hypervisor.NewVm(vmId, cpu, mem, false, types.VM_KEEP_NONE)
	err = vm.LaunchOCIVm(b, sock)
	if err != nil {
		fmt.Printf("%s\n", err.Error())
		return
	}

	inFd, _ := term.GetFdInfo(os.Stdin)
	outFd, isTerminalOut := term.GetFdInfo(os.Stdout)

	oldState, err := term.SetRawTerminal(inFd)
	if err != nil {
		return
	}

	defer term.RestoreTerminal(inFd, oldState)

	tag := pod.RandStr(8, "alphanum")
	ttyCallback := make(chan *types.VmResponse, 1)

	// using pipes in vm.Attach to avoid the stdio to be closed
	fromStd, toVm := io.Pipe()
	fromVm, toStd := io.Pipe()
	go io.Copy(toVm, os.Stdin)
	go io.Copy(os.Stdout, fromVm)

	err = vm.Attach(fromStd, toStd, tag, mypod.Containers[0].Id, ttyCallback, nil)
	if err != nil {
		fmt.Printf("StartPod fail: fail to set up tty connection.\n")
		return
	}

	Response := vm.StartPod(mypod, userPod, infoList, nil)
	if Response.Data == nil {
		fmt.Printf("StartPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)

	resizeTty(vm, tag, outFd, isTerminalOut)
	monitorTtySize(vm, tag, outFd, isTerminalOut)
	<-ttyCallback

	Response = vm.StopPod(mypod, "yes")

	if Response.Data == nil {
		fmt.Printf("StopPod fail: QEMU response data is nil\n")
		return
	}
	fmt.Printf("result: code %d %s\n", Response.Code, Response.Cause)
}

var startCommand = cli.Command{
	Name:  "start",
	Usage: "create and run a container",
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:  "config-file, c",
			Value: "config.json",
			Usage: "path to spec config file",
		},
		cli.StringFlag{
			Name:  "runtime-file, r",
			Value: "runtime.json",
			Usage: "path to runtime config file",
		},
	},
	Action: startVContainer,
}
<|endoftext|>