<commit_before>// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package seccomp

import (
	"fmt"
	"syscall"
	"testing"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"gvisor.dev/gvisor/pkg/binary"
	"gvisor.dev/gvisor/pkg/bpf"
)

type seccompData struct {
	nr                 uint32
	arch               uint32
	instructionPointer uint64
	args               [6]uint64
}

// asInput converts a seccompData to a bpf.Input.
func asInput(d seccompData) bpf.Input {
	return bpf.InputBytes{binary.Marshal(nil, binary.LittleEndian, d), binary.LittleEndian}
}

// testInput creates an Input struct with given seccomp input values.
func testInput(arch uint32, syscallName string, args *[6]uint64) bpf.Input {
	syscallNo, err := lookupSyscallNo(arch, syscallName)
	if err != nil {
		// Assume tests set valid syscall names.
		panic(err)
	}

	if args == nil {
		argArray := [6]uint64{0, 0, 0, 0, 0, 0}
		args = &argArray
	}

	data := seccompData{
		nr:   syscallNo,
		arch: arch,
		args: *args,
	}

	return asInput(data)
}

// testCase holds a seccomp test case.
type testCase struct {
	name     string
	config   specs.LinuxSeccomp
	input    bpf.Input
	expected uint32
}

var (
	// seccompTests is a list of speccomp test cases.
	seccompTests = []testCase{
		{
			name: "default_allow",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
			},
			input:    testInput(nativeArchAuditNo, "read", nil),
			expected: uint32(allowAction),
		},
		{
			name: "default_deny",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActErrno,
			},
			input:    testInput(nativeArchAuditNo, "read", nil),
			expected: uint32(errnoAction),
		},
		{
			name: "deny_arch",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
						},
						Action: specs.ActErrno,
					},
				},
			},
			// Syscall matches but the arch is AUDIT_ARCH_X86 so the return
			// value is the bad arch action.
			input:    asInput(seccompData{nr: 183, arch: 0x40000003}), //
			expected: uint32(killThreadAction),
		},
		{
			name: "match_name_errno",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getcwd", nil),
			expected: uint32(errnoAction),
		},
		{
			name: "match_name_trace",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "write", nil),
			expected: uint32(traceAction),
		},
		{
			name: "no_match_name_allow",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "open", nil),
			expected: uint32(allowAction),
		},
		{
			name: "simple_match_args",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(errnoAction),
		},
		{
			name: "match_args_or",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 0,
								Value: syscall.CLONE_VM,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(errnoAction),
		},
		{
			name: "match_args_and",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getsockopt",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 1,
								Value: syscall.SOL_SOCKET,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 2,
								Value: syscall.SO_PEERCRED,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET, syscall.SO_PEERCRED}),
			expected: uint32(errnoAction),
		},
		{
			name: "no_match_args_and",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getsockopt",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 1,
								Value: syscall.SOL_SOCKET,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 2,
								Value: syscall.SO_PEERCRED,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET}),
			expected: uint32(allowAction),
		},
		{
			name: "Simple args (no match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_VM}),
			expected: uint32(allowAction),
		},
		{
			name: "OpMaskedEqual (match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    syscall.CLONE_FS,
								ValueTwo: syscall.CLONE_FS,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS | syscall.CLONE_VM}),
			expected: uint32(errnoAction),
		},
		{
			name: "OpMaskedEqual (no match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    syscall.CLONE_FS | syscall.CLONE_VM,
								ValueTwo: syscall.CLONE_FS | syscall.CLONE_VM,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(allowAction),
		},
		{
			name: "OpMaskedEqual (clone)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActErrno,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						// This comes from the Docker default seccomp
						// profile for clone.
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    0x7e020000,
								ValueTwo: 0x0,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActAllow,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{0x50f00}),
			expected: uint32(allowAction),
		},
	}
)

// TestRunscSeccomp generates seccomp programs from OCI config and executes
// them using runsc's library, comparing against expected results.
func TestRunscSeccomp(t *testing.T) {
	for _, tc := range seccompTests {
		t.Run(tc.name, func(t *testing.T) {
			runscProgram, err := BuildProgram(&tc.config)
			if err != nil {
				t.Fatalf("generating runsc BPF: %v", err)
			}

			if err := checkProgram(runscProgram, tc.input, tc.expected); err != nil {
				t.Fatalf("running runsc BPF: %v", err)
			}
		})
	}
}

// checkProgram runs the given program over the given input and checks the
// result against the expected output.
func checkProgram(p bpf.Program, in bpf.Input, expected uint32) error {
	result, err := bpf.Exec(p, in)
	if err != nil {
		return err
	}

	if result != expected {
		// Include a decoded version of the program in output for debugging purposes.
		decoded, _ := bpf.DecodeProgram(p)
		return fmt.Errorf("Unexpected result: got: %d, expected: %d\nBPF Program\n%s", result, expected, decoded)
	}

	return nil
}
<commit_msg>fix seccomp test for ARM64<commit_after>// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package seccomp

import (
	"fmt"
	"syscall"
	"testing"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"gvisor.dev/gvisor/pkg/binary"
	"gvisor.dev/gvisor/pkg/bpf"
)

type seccompData struct {
	nr                 uint32
	arch               uint32
	instructionPointer uint64
	args               [6]uint64
}

// asInput converts a seccompData to a bpf.Input.
func asInput(d seccompData) bpf.Input {
	return bpf.InputBytes{binary.Marshal(nil, binary.LittleEndian, d), binary.LittleEndian}
}

// testInput creates an Input struct with given seccomp input values.
func testInput(arch uint32, syscallName string, args *[6]uint64) bpf.Input {
	syscallNo, err := lookupSyscallNo(arch, syscallName)
	if err != nil {
		// Assume tests set valid syscall names.
		panic(err)
	}

	if args == nil {
		argArray := [6]uint64{0, 0, 0, 0, 0, 0}
		args = &argArray
	}

	data := seccompData{
		nr:   syscallNo,
		arch: arch,
		args: *args,
	}

	return asInput(data)
}

// testCase holds a seccomp test case.
type testCase struct {
	name     string
	config   specs.LinuxSeccomp
	input    bpf.Input
	expected uint32
}

var (
	// seccompTests is a list of speccomp test cases.
	seccompTests = []testCase{
		{
			name: "default_allow",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
			},
			input:    testInput(nativeArchAuditNo, "read", nil),
			expected: uint32(allowAction),
		},
		{
			name: "default_deny",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActErrno,
			},
			input:    testInput(nativeArchAuditNo, "read", nil),
			expected: uint32(errnoAction),
		},
		{
			name: "deny_arch",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
						},
						Action: specs.ActErrno,
					},
				},
			},
			// Syscall matches but the arch is AUDIT_ARCH_X86 so the return
			// value is the bad arch action.
			input:    asInput(seccompData{nr: 183, arch: 0x40000003}), //
			expected: uint32(killThreadAction),
		},
		{
			name: "match_name_errno",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getcwd", nil),
			expected: uint32(errnoAction),
		},
		{
			name: "match_name_trace",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "write", nil),
			expected: uint32(traceAction),
		},
		{
			name: "no_match_name_allow",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getcwd",
							"chmod",
						},
						Action: specs.ActErrno,
					},
					{
						Names: []string{
							"write",
						},
						Action: specs.ActTrace,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "openat", nil),
			expected: uint32(allowAction),
		},
		{
			name: "simple_match_args",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(errnoAction),
		},
		{
			name: "match_args_or",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 0,
								Value: syscall.CLONE_VM,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(errnoAction),
		},
		{
			name: "match_args_and",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getsockopt",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 1,
								Value: syscall.SOL_SOCKET,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 2,
								Value: syscall.SO_PEERCRED,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET, syscall.SO_PEERCRED}),
			expected: uint32(errnoAction),
		},
		{
			name: "no_match_args_and",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"getsockopt",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 1,
								Value: syscall.SOL_SOCKET,
								Op:    specs.OpEqualTo,
							},
							{
								Index: 2,
								Value: syscall.SO_PEERCRED,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "getsockopt", &[6]uint64{0, syscall.SOL_SOCKET}),
			expected: uint32(allowAction),
		},
		{
			name: "Simple args (no match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index: 0,
								Value: syscall.CLONE_FS,
								Op:    specs.OpEqualTo,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_VM}),
			expected: uint32(allowAction),
		},
		{
			name: "OpMaskedEqual (match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    syscall.CLONE_FS,
								ValueTwo: syscall.CLONE_FS,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS | syscall.CLONE_VM}),
			expected: uint32(errnoAction),
		},
		{
			name: "OpMaskedEqual (no match)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActAllow,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    syscall.CLONE_FS | syscall.CLONE_VM,
								ValueTwo: syscall.CLONE_FS | syscall.CLONE_VM,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActErrno,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{syscall.CLONE_FS}),
			expected: uint32(allowAction),
		},
		{
			name: "OpMaskedEqual (clone)",
			config: specs.LinuxSeccomp{
				DefaultAction: specs.ActErrno,
				Syscalls: []specs.LinuxSyscall{
					{
						Names: []string{
							"clone",
						},
						// This comes from the Docker default seccomp
						// profile for clone.
						Args: []specs.LinuxSeccompArg{
							{
								Index:    0,
								Value:    0x7e020000,
								ValueTwo: 0x0,
								Op:       specs.OpMaskedEqual,
							},
						},
						Action: specs.ActAllow,
					},
				},
			},
			input:    testInput(nativeArchAuditNo, "clone", &[6]uint64{0x50f00}),
			expected: uint32(allowAction),
		},
	}
)

// TestRunscSeccomp generates seccomp programs from OCI config and executes
// them using runsc's library, comparing against expected results.
func TestRunscSeccomp(t *testing.T) {
	for _, tc := range seccompTests {
		t.Run(tc.name, func(t *testing.T) {
			runscProgram, err := BuildProgram(&tc.config)
			if err != nil {
				t.Fatalf("generating runsc BPF: %v", err)
			}

			if err := checkProgram(runscProgram, tc.input, tc.expected); err != nil {
				t.Fatalf("running runsc BPF: %v", err)
			}
		})
	}
}

// checkProgram runs the given program over the given input and checks the
// result against the expected output.
func checkProgram(p bpf.Program, in bpf.Input, expected uint32) error {
	result, err := bpf.Exec(p, in)
	if err != nil {
		return err
	}

	if result != expected {
		// Include a decoded version of the program in output for debugging purposes.
		decoded, _ := bpf.DecodeProgram(p)
		return fmt.Errorf("Unexpected result: got: %d, expected: %d\nBPF Program\n%s", result, expected, decoded)
	}

	return nil
}
<|endoftext|>


<commit_before>package dinheiro

import (
	"errors"
	"math"
	"strconv"
)

const (
	negativeValueError    = "Não é possível transformar números negativos."
	unsupportedValueError = "Número muito grande para ser transformado em extenso."

	andSeparator = " e "

	currencyCentavo  = "centavo"
	currencyCentavos = "centavos"
	currencyReal     = "real"
	currencyReais    = "reais"
)

var (
	numbers = [20]string{
		"zero",
		"um",
		"dois",
		"três",
		"quatro",
		"cinco",
		"seis",
		"sete",
		"oito",
		"nove",
		"dez",
		"onze",
		"doze",
		"treze",
		"quatorze",
		"quinze",
		"dezesseis",
		"dezessete",
		"dezoito",
		"dezenove"}

	tens = [8]string{
		"vinte",
		"trinta",
		"quarenta",
		"cinquenta",
		"sessenta",
		"setenta",
		"oitenta",
		"noventa",
	}

	hundreds = [9]string{
		"cento",
		"duzentos",
		"trezentos",
		"quatrocentos",
		"quinhentos",
		"seiscentos",
		"setecentos",
		"oitocentos",
		"novecentos",
	}
	hundred  = "cem"
	thousand = "mil"
	million  = "milhão"
	millions = "milhões"
	billion  = "bilhão"
	billions = "bilhões"
)

// Real é a moeda corrente no Brasil
// en: Real is the present-day currency of Brazil
type Real float64

// PorExtenso Retorna o value por extenso do dinheiro
// en: Returns the value into words
func (real Real) PorExtenso() (string, error) {
	var value string

	integer, fractional := math.Modf(float64(real))

	if integer != 0 || fractional == 0 {
		numberIntoWords, err := convertNumberIntoWords(integer)
		if err != nil {
			return "", err
		}
		value = numberIntoWords + " " + getIntegerUnit(integer)
	}

	if fractional > 0 {
		fractional := round(math.Abs(fractional) * 100)
		numberIntoWords, err := convertNumberIntoWords(fractional)
		if err != nil {
			return "", err
		}
		if integer > 0 {
			value += andSeparator
		}
		value += numberIntoWords + " " + getDecimalUnit(fractional)
	}

	return value, nil
}

func (real Real) String() (string, error) {
	return real.PorExtenso()
}

func convertNumberIntoWords(f float64) (string, error) {
	switch {
	case f < 0:
		return "", errors.New(negativeValueError)
	case f < 20:
		return numbers[int(f)], nil
	case f < 100:
		value := tens[int((f-20)/10)]
		mod := math.Mod(f, 10)
		if mod != 0 {
			value += andSeparator + numbers[int(mod)]
		}
		return value, nil
	case f == 100:
		return hundred, nil
	case f < 1000:
		value := hundreds[int(f/100-1)]
		mod := math.Mod(f, 100)
		if mod != 0 {
			remaining, _ := convertNumberIntoWords(mod)
			value += andSeparator + remaining
		}
		return value, nil
	case f == 1000:
		return thousand, nil
	case f < 1000000:
		s := strconv.Itoa(int(f))
		t1, _ := strconv.Atoi(s[:len(s)-3])
		t2, _ := strconv.Atoi(s[len(s)-3:])
		value, _ := convertNumberIntoWords(float64(t1))
		value += " " + thousand
		if t2 > 0 {
			t2IntoWords, _ := convertNumberIntoWords(float64(t2))
			value += andSeparator + t2IntoWords
		}
		return value, nil
	default:
		return "", errors.New(unsupportedValueError)
	}
}

func getIntegerUnit(f float64) string {
	if f >= 0 && f < 2 {
		return currencyReal
	}
	return currencyReais
}

func getDecimalUnit(f float64) string {
	if f == 1 {
		return currencyCentavo
	}
	return currencyCentavos
}

func round(val float64) float64 {
	const (
		roundOn = .5
		places  = 2
	)

	var round float64
	pow := math.Pow(10, float64(places))
	digit := pow * val
	_, div := math.Modf(digit)
	if div >= roundOn {
		round = math.Ceil(digit)
	} else {
		round = math.Floor(digit)
	}
	return round / pow
}
<commit_msg>Extract method<commit_after>package dinheiro

import (
	"errors"
	"math"
	"strconv"
)

const (
	negativeValueError    = "Não é possível transformar números negativos."
	unsupportedValueError = "Número muito grande para ser transformado em extenso."

	andSeparator = " e "

	currencyCentavo  = "centavo"
	currencyCentavos = "centavos"
	currencyReal     = "real"
	currencyReais    = "reais"
)

var (
	numbers = [20]string{
		"zero",
		"um",
		"dois",
		"três",
		"quatro",
		"cinco",
		"seis",
		"sete",
		"oito",
		"nove",
		"dez",
		"onze",
		"doze",
		"treze",
		"quatorze",
		"quinze",
		"dezesseis",
		"dezessete",
		"dezoito",
		"dezenove"}

	tens = [8]string{
		"vinte",
		"trinta",
		"quarenta",
		"cinquenta",
		"sessenta",
		"setenta",
		"oitenta",
		"noventa",
	}

	hundreds = [9]string{
		"cento",
		"duzentos",
		"trezentos",
		"quatrocentos",
		"quinhentos",
		"seiscentos",
		"setecentos",
		"oitocentos",
		"novecentos",
	}
	hundred  = "cem"
	thousand = "mil"
	million  = "milhão"
	millions = "milhões"
	billion  = "bilhão"
	billions = "bilhões"
)

// Real é a moeda corrente no Brasil
// en: Real is the present-day currency of Brazil
type Real float64

// PorExtenso Retorna o value por extenso do dinheiro
// en: Returns the value into words
func (real Real) PorExtenso() (string, error) {
	var value string

	integer, fractional := math.Modf(float64(real))

	if integer != 0 || fractional == 0 {
		numberIntoWords, err := convertNumberIntoWords(integer)
		if err != nil {
			return "", err
		}
		value = numberIntoWords + " " + getIntegerUnit(integer)
	}

	if fractional > 0 {
		fractional := round(math.Abs(fractional) * 100)
		numberIntoWords, err := convertNumberIntoWords(fractional)
		if err != nil {
			return "", err
		}
		if integer > 0 {
			value += andSeparator
		}
		value += numberIntoWords + " " + getDecimalUnit(fractional)
	}

	return value, nil
}

func (real Real) String() (string, error) {
	return real.PorExtenso()
}

func convertNumberIntoWords(f float64) (string, error) {
	switch {
	case f < 0:
		return "", errors.New(negativeValueError)
	case f < 20:
		return numbers[int(f)], nil
	case f < 100:
		return getNumberUnderHundred(f)
	case f == 100:
		return hundred, nil
	case f < 1000:
		return getNumberUnderThousand(f)
	case f == 1000:
		return thousand, nil
	case f < 1000000:
		return getNumberUnderMillion(f)
	default:
		return "", errors.New(unsupportedValueError)
	}
}

func getNumberUnderHundred(f float64) (string, error) {
	value := tens[int((f-20)/10)]
	mod := math.Mod(f, 10)
	if mod != 0 {
		value += andSeparator + numbers[int(mod)]
	}
	return value, nil
}

func getNumberUnderThousand(f float64) (string, error) {
	value := hundreds[int(f/100-1)]
	mod := math.Mod(f, 100)
	if mod != 0 {
		remaining, _ := convertNumberIntoWords(mod)
		value += andSeparator + remaining
	}
	return value, nil
}

func getNumberUnderMillion(f float64) (string, error) {
	s := strconv.Itoa(int(f))
	t1, _ := strconv.Atoi(s[:len(s)-3])
	t2, _ := strconv.Atoi(s[len(s)-3:])

	value, _ := convertNumberIntoWords(float64(t1))
	value += " " + thousand
	if t2 > 0 {
		t2IntoWords, _ := convertNumberIntoWords(float64(t2))
		value += andSeparator + t2IntoWords
	}
	return value, nil
}

func getIntegerUnit(f float64) string {
	if f >= 0 && f < 2 {
		return currencyReal
	}
	return currencyReais
}

func getDecimalUnit(f float64) string {
	if f == 1 {
		return currencyCentavo
	}
	return currencyCentavos
}

func round(val float64) float64 {
	const (
		roundOn = .5
		places  = 2
	)

	var round float64
	pow := math.Pow(10, float64(places))
	digit := pow * val
	_, div := math.Modf(digit)
	if div >= roundOn {
		round = math.Ceil(digit)
	} else {
		round = math.Floor(digit)
	}
	return round / pow
}
<|endoftext|>


<commit_before>package ds_store

import (
	"errors"
	"bytes"
	"encoding/binary"
	"reflect"
	"unicode/utf16"
	"unicode/utf8"
	"unsafe"
)

type Block struct {
	Allocator 	*Allocator
	Offset 		uint32
	Size		uint32
	Data		[]byte
	Pos			uint32
}

type Allocator struct {
	Data	[]byte
	Pos		uint32
	Root	*Block
	Offsets	[]uint32
	Toc		map[string]uint32
	FreeList	map[uint32][]uint32
}

func NewBlock(a *Allocator, pos uint32, size uint32) (block *Block, err error) {
	block = &Block{Size: size, Allocator: a, Data: a.Data[pos+0x4:pos+0x4+size]}
	return block, nil
}

func (block *Block) readUint32() (value uint32, err error) {
	if block.Size - block.Pos < 4 {
		return 0, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	binary.Read(data, binary.BigEndian, &value)
	block.Pos += 4
	return value, nil
}

func (block *Block) readByte() (value byte, err error) {
	if block.Size - block.Pos < 1 {
		return 0, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	binary.Read(data, binary.BigEndian, &value)
	block.Pos += 1
	return value, nil
}

func (block *Block) readBuf(length int) (buf []byte, err error) {
	if int(block.Size) - int(block.Pos) < length {
		return nil, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	buf = make([]byte, length)
	binary.Read(data, binary.BigEndian, &buf)
	block.Pos += uint32(length)
	return buf, nil
}

func (block *Block) readFileName() (name string, err error) {
	length, err := block.readUint32()
	if err != nil {
		return "", err
	}
	buf, err := block.readBuf(int(2 * length))
	if err != nil {
		return "", err
	}

	/*
	sid, err := block.readUint32()
	if err != nil {
		return "", err
	}
	*/
	block.skip(4)

	stype, err := block.readBuf(4)
	if err != nil {
		return "", err
	}
	t := string(stype)
	switch {
		case t == "bool":
			block.skip(1)
			break
		case t == "type" ||t == "long" || t == "shor":
			block.skip(4)
			break
		case t == "comp" || t == "dutc":
			block.skip(8)
			break
		case t == "blob":
			blen, err := block.readUint32()
			if err != nil {
				return "", err
			}
			block.skip(blen)
			break
		case t == "ustr":
			blen, err := block.readUint32()
			if err != nil {
				return "", err
			}
			block.skip(2 * blen)
		default:
			panic("Unknown file format.")
	}

	name = utf16be2utf8(buf)
	return name, nil
}

func (block *Block) skip(i uint32) {
	block.Pos += i
}

func NewAllocator(data []byte) (a *Allocator, err error) {
	a = &Allocator{Data:  data}//bytes.NewBuffer(data)}
	a.Toc = make(map[string]uint32)
	a.FreeList = make(map[uint32][]uint32)

	offset, size, err := a.readHeader()
	if err != nil {
		return nil, err
	}

	a.Root,_  = NewBlock(a, offset, size)

	err = a.readOffsets()
	if err != nil {
		return nil, err
	}

	err = a.readToc()
	if err != nil {
		return nil, err
	}

	err = a.readFreeList()
	if err != nil {
		return nil, err
	}

	return a, err
}

func (a *Allocator) GetBlock(bid uint32) (block *Block, err error) {
	addr := a.Offsets[bid]
	offset := int(addr) & ^0x1f
	size := 1 << (uint(addr) & 0x1f)

	block, err = NewBlock(a, uint32(offset), uint32(size)) ///+4??
	if err != nil {
		return nil, errors.New("Cannot create/read block")
	}
	return block, nil
}

func (a *Allocator) TraverseFromRootNode() (filenames []string, err error) {
	rootBlk,err := a.GetBlock(a.Toc["DSDB"])
	if err != nil {
		return nil, err
	}
	rootNode, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	/*height, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	recordsCount, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	nodesCount, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	blksize, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}*/
	rootBlk.skip(4*4)

	return a.Traverse(rootNode)
}

func (a *Allocator) Traverse(bid uint32) (filenames []string, err error) {
	node, err := a.GetBlock(bid)
	if err != nil {
		return nil, err
	}
	nextPtr, err := node.readUint32()
	if err != nil {
		return nil, err
	}
	count, err := node.readUint32()
	if err != nil {
		return nil, err
	}
	if nextPtr > 0{
		//This may be broken
		for i := 0; i < int(count); i++ {
			next, err := node.readUint32()
			if err != nil {
				return nil, err
			}
			files, err := a.Traverse(next)
			if err != nil {
				return nil, err
			}
			for _,f := range files {
				filenames = append(filenames,f)
			}
			f, err := node.readFileName()
			if err != nil {
				return nil, err
			}
			filenames = append(filenames, f)
		}
		files, err := a.Traverse(nextPtr)
		if err != nil {
			return nil, err
		}
		for _,f := range files {
			filenames = append(filenames,f)
		}
	} else {
		for i := 0; i < int(count); i++ {
			f, err := node.readFileName()
			if err != nil {
				return nil, err
			}
			filenames = append(filenames, f)
		}
	}

	return filenames, nil
}

func (a *Allocator) readFreeList() error {
	for i:=0; i < 32; i++ {
		blkcount, err := a.Root.readUint32()
		if err != nil {
			return err
		}
		if blkcount == 0 {
			continue
		}
		a.FreeList[uint32(i)] = make([]uint32, 0)
 		for k:=0; k < int(blkcount); k++ {
 			val, err := a.Root.readUint32()
 			if err != nil {
 				return err
 			}
 			if val == 0 {
 				continue
 			}
 			a.FreeList[uint32(i)] = append(a.FreeList[uint32(i)],val)
		}
	}
	return nil
}

func (a *Allocator) readToc() error {
	toccount, err := a.Root.readUint32()
	if err != nil {
		return err
	}
	for i := toccount; i > 0; i-- {
		tlen, err := a.Root.readByte()
		if err != nil {
			return err
		}
		name, err := a.Root.readBuf(int(tlen))
		if err != nil {
			return err
		}
		value, err := a.Root.readUint32()
		if err != nil {
			return err
		}
		a.Toc[string(name)] = value
	}
	return nil
}

func (a *Allocator) readOffsets() error {
	count, err := a.Root.readUint32()
	if err != nil {
		return err
	}
	a.Root.skip(4)

	for offcount := int(count); offcount > 0; offcount -= 256 {
		for i := 0; i < 256; i++ {
			val, err := a.Root.readUint32()
			if err != nil {
				return err
			}
			if val == 0 {
				continue
			}
			a.Offsets = append(a.Offsets, val)
		}
	}
	return nil
}

func (a *Allocator) readHeader() (offset uint32, size uint32, err error) {
	data := bytes.NewBuffer(a.Data)
	if data.Len() < 32 {
		return offset, size, errors.New("Header not long enough")
	}
	var magic1, magic, offset2 uint32

	binary.Read(data, binary.BigEndian, &magic1)
	if magic1 != 1 {
		return offset, size, errors.New("Wrong magic bytes")
	}
	a.Pos += 4

	binary.Read(data, binary.BigEndian, &magic)
	if magic != 0x42756431 {
		return offset, size, errors.New("Wrong magic bytes")
	}
	a.Pos += 4

	binary.Read(data, binary.BigEndian, &offset)
	a.Pos += 4
	binary.Read(data, binary.BigEndian, &size)
	a.Pos += 4
	binary.Read(data, binary.BigEndian, &offset2)
	if offset != offset2 {
		return offset, size, errors.New("Offets do not match")
	}
	a.Pos += 4

	return offset, size, nil
}

func utf16be2utf8(utf16be []byte) string {
	//Taken from http://play.golang.org/p/xtG1e9iqA1
	n := len(utf16be)
	// Convert to []uint16
	// hop through unsafe to skip any actual allocation/copying
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&utf16be))
	header.Len /= 2
	shorts := *(*[]uint16)(unsafe.Pointer(&header))
	// shorts may need byte-swapping
	for i := 0; i < n; i += 2 {
		shorts[i/2] = (uint16(utf16be[i]) << 8) | uint16(utf16be[i+1])
	}

	// Convert to []byte
	count := 0
	for i := 0; i < len(shorts); i++ {
		r := rune(shorts[i])
		if utf16.IsSurrogate(r) {
			i++
			r = utf16.DecodeRune(r, rune(shorts[i]))
		}
		count += utf8.RuneLen(r)
	}
	buf := make([]byte, count)
	bi := 0
	for i := 0; i < len(shorts); i++ {
		r := rune(shorts[i])
		if utf16.IsSurrogate(r) {
			i++
			r = utf16.DecodeRune(r, rune(shorts[i]))
		}
		bi += utf8.EncodeRune(buf[bi:], r)
	}
	return string(buf)
}<commit_msg>go fmt<commit_after>package ds_store

import (
	"bytes"
	"encoding/binary"
	"errors"
	"reflect"
	"unicode/utf16"
	"unicode/utf8"
	"unsafe"
)

type Block struct {
	Allocator *Allocator
	Offset    uint32
	Size      uint32
	Data      []byte
	Pos       uint32
}

type Allocator struct {
	Data     []byte
	Pos      uint32
	Root     *Block
	Offsets  []uint32
	Toc      map[string]uint32
	FreeList map[uint32][]uint32
}

func NewBlock(a *Allocator, pos uint32, size uint32) (block *Block, err error) {
	block = &Block{Size: size, Allocator: a, Data: a.Data[pos+0x4 : pos+0x4+size]}
	return block, nil
}

func (block *Block) readUint32() (value uint32, err error) {
	if block.Size-block.Pos < 4 {
		return 0, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	binary.Read(data, binary.BigEndian, &value)
	block.Pos += 4
	return value, nil
}

func (block *Block) readByte() (value byte, err error) {
	if block.Size-block.Pos < 1 {
		return 0, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	binary.Read(data, binary.BigEndian, &value)
	block.Pos += 1
	return value, nil
}

func (block *Block) readBuf(length int) (buf []byte, err error) {
	if int(block.Size)-int(block.Pos) < length {
		return nil, errors.New("Not enough bytes to read")
	}
	data := bytes.NewBuffer(block.Data)
	data.Next(int(block.Pos))
	buf = make([]byte, length)
	binary.Read(data, binary.BigEndian, &buf)
	block.Pos += uint32(length)
	return buf, nil
}

func (block *Block) readFileName() (name string, err error) {
	length, err := block.readUint32()
	if err != nil {
		return "", err
	}
	buf, err := block.readBuf(int(2 * length))
	if err != nil {
		return "", err
	}

	/*
		sid, err := block.readUint32()
		if err != nil {
			return "", err
		}
	*/
	block.skip(4)

	stype, err := block.readBuf(4)
	if err != nil {
		return "", err
	}
	t := string(stype)
	switch {
	case t == "bool":
		block.skip(1)
		break
	case t == "type" || t == "long" || t == "shor":
		block.skip(4)
		break
	case t == "comp" || t == "dutc":
		block.skip(8)
		break
	case t == "blob":
		blen, err := block.readUint32()
		if err != nil {
			return "", err
		}
		block.skip(blen)
		break
	case t == "ustr":
		blen, err := block.readUint32()
		if err != nil {
			return "", err
		}
		block.skip(2 * blen)
	default:
		panic("Unknown file format.")
	}

	name = utf16be2utf8(buf)
	return name, nil
}

func (block *Block) skip(i uint32) {
	block.Pos += i
}

func NewAllocator(data []byte) (a *Allocator, err error) {
	a = &Allocator{Data: data} //bytes.NewBuffer(data)}
	a.Toc = make(map[string]uint32)
	a.FreeList = make(map[uint32][]uint32)

	offset, size, err := a.readHeader()
	if err != nil {
		return nil, err
	}

	a.Root, _ = NewBlock(a, offset, size)

	err = a.readOffsets()
	if err != nil {
		return nil, err
	}

	err = a.readToc()
	if err != nil {
		return nil, err
	}

	err = a.readFreeList()
	if err != nil {
		return nil, err
	}

	return a, err
}

func (a *Allocator) GetBlock(bid uint32) (block *Block, err error) {
	addr := a.Offsets[bid]
	offset := int(addr) & ^0x1f
	size := 1 << (uint(addr) & 0x1f)

	block, err = NewBlock(a, uint32(offset), uint32(size)) ///+4??
	if err != nil {
		return nil, errors.New("Cannot create/read block")
	}
	return block, nil
}

func (a *Allocator) TraverseFromRootNode() (filenames []string, err error) {
	rootBlk, err := a.GetBlock(a.Toc["DSDB"])
	if err != nil {
		return nil, err
	}
	rootNode, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	/*height, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	recordsCount, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	nodesCount, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}
	blksize, err := rootBlk.readUint32()
	if err != nil {
		return nil, err
	}*/
	rootBlk.skip(4 * 4)

	return a.Traverse(rootNode)
}

func (a *Allocator) Traverse(bid uint32) (filenames []string, err error) {
	node, err := a.GetBlock(bid)
	if err != nil {
		return nil, err
	}
	nextPtr, err := node.readUint32()
	if err != nil {
		return nil, err
	}
	count, err := node.readUint32()
	if err != nil {
		return nil, err
	}
	if nextPtr > 0 {
		//This may be broken
		for i := 0; i < int(count); i++ {
			next, err := node.readUint32()
			if err != nil {
				return nil, err
			}
			files, err := a.Traverse(next)
			if err != nil {
				return nil, err
			}
			for _, f := range files {
				filenames = append(filenames, f)
			}
			f, err := node.readFileName()
			if err != nil {
				return nil, err
			}
			filenames = append(filenames, f)
		}
		files, err := a.Traverse(nextPtr)
		if err != nil {
			return nil, err
		}
		for _, f := range files {
			filenames = append(filenames, f)
		}
	} else {
		for i := 0; i < int(count); i++ {
			f, err := node.readFileName()
			if err != nil {
				return nil, err
			}
			filenames = append(filenames, f)
		}
	}

	return filenames, nil
}

func (a *Allocator) readFreeList() error {
	for i := 0; i < 32; i++ {
		blkcount, err := a.Root.readUint32()
		if err != nil {
			return err
		}
		if blkcount == 0 {
			continue
		}
		a.FreeList[uint32(i)] = make([]uint32, 0)
		for k := 0; k < int(blkcount); k++ {
			val, err := a.Root.readUint32()
			if err != nil {
				return err
			}
			if val == 0 {
				continue
			}
			a.FreeList[uint32(i)] = append(a.FreeList[uint32(i)], val)
		}
	}
	return nil
}

func (a *Allocator) readToc() error {
	toccount, err := a.Root.readUint32()
	if err != nil {
		return err
	}
	for i := toccount; i > 0; i-- {
		tlen, err := a.Root.readByte()
		if err != nil {
			return err
		}
		name, err := a.Root.readBuf(int(tlen))
		if err != nil {
			return err
		}
		value, err := a.Root.readUint32()
		if err != nil {
			return err
		}
		a.Toc[string(name)] = value
	}
	return nil
}

func (a *Allocator) readOffsets() error {
	count, err := a.Root.readUint32()
	if err != nil {
		return err
	}
	a.Root.skip(4)

	for offcount := int(count); offcount > 0; offcount -= 256 {
		for i := 0; i < 256; i++ {
			val, err := a.Root.readUint32()
			if err != nil {
				return err
			}
			if val == 0 {
				continue
			}
			a.Offsets = append(a.Offsets, val)
		}
	}
	return nil
}

func (a *Allocator) readHeader() (offset uint32, size uint32, err error) {
	data := bytes.NewBuffer(a.Data)
	if data.Len() < 32 {
		return offset, size, errors.New("Header not long enough")
	}
	var magic1, magic, offset2 uint32

	binary.Read(data, binary.BigEndian, &magic1)
	if magic1 != 1 {
		return offset, size, errors.New("Wrong magic bytes")
	}
	a.Pos += 4

	binary.Read(data, binary.BigEndian, &magic)
	if magic != 0x42756431 {
		return offset, size, errors.New("Wrong magic bytes")
	}
	a.Pos += 4

	binary.Read(data, binary.BigEndian, &offset)
	a.Pos += 4
	binary.Read(data, binary.BigEndian, &size)
	a.Pos += 4
	binary.Read(data, binary.BigEndian, &offset2)
	if offset != offset2 {
		return offset, size, errors.New("Offets do not match")
	}
	a.Pos += 4

	return offset, size, nil
}

func utf16be2utf8(utf16be []byte) string {
	//Taken from http://play.golang.org/p/xtG1e9iqA1
	n := len(utf16be)
	// Convert to []uint16
	// hop through unsafe to skip any actual allocation/copying
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&utf16be))
	header.Len /= 2
	shorts := *(*[]uint16)(unsafe.Pointer(&header))
	// shorts may need byte-swapping
	for i := 0; i < n; i += 2 {
		shorts[i/2] = (uint16(utf16be[i]) << 8) | uint16(utf16be[i+1])
	}

	// Convert to []byte
	count := 0
	for i := 0; i < len(shorts); i++ {
		r := rune(shorts[i])
		if utf16.IsSurrogate(r) {
			i++
			r = utf16.DecodeRune(r, rune(shorts[i]))
		}
		count += utf8.RuneLen(r)
	}
	buf := make([]byte, count)
	bi := 0
	for i := 0; i < len(shorts); i++ {
		r := rune(shorts[i])
		if utf16.IsSurrogate(r) {
			i++
			r = utf16.DecodeRune(r, rune(shorts[i]))
		}
		bi += utf8.EncodeRune(buf[bi:], r)
	}
	return string(buf)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Red Hat, Inc.

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 volume

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/guelfey/go.dbus"
	"k8s.io/client-go/1.4/pkg/api/v1"
)

// Delete removes the directory that was created by Provision backing the given
// PV.
func (p *nfsProvisioner) Delete(volume *v1.PersistentVolume) error {
	// TODO quota, something better than just directories

	path := fmt.Sprintf(p.exportDir+"%s", volume.ObjectMeta.Name)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return fmt.Errorf("Delete called on a volume that doesn't exist, presumably because this provisioner never created it")
	}

	if err := os.RemoveAll(path); err != nil {
		return fmt.Errorf("error deleting volume by removing its path: %v", err)
	}

	var err error
	if p.useGanesha {
		err = p.ganeshaUnexport(volume)
	} else {
		err = p.kernelUnexport(volume)
	}
	if err != nil {
		return fmt.Errorf("removed the path backing the volume but error unexporting it: %v", err)
	}

	return nil
}

func (p *nfsProvisioner) ganeshaUnexport(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations[annExportId]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, can't remove the export from the server", annExportId)
	}
	exportId, _ := strconv.ParseUint(ann, 10, 16)
	delete(p.exportIds, uint16(exportId))

	// Call RemoveExport using dbus
	conn, err := dbus.SystemBus()
	if err != nil {
		return fmt.Errorf("error getting dbus session bus: %v", err)
	}
	obj := conn.Object("org.ganesha.nfsd", "/org/ganesha/nfsd/ExportMgr")
	call := obj.Call("org.ganesha.nfsd.exportmgr.RemoveExport", 0, uint16(exportId))
	if call.Err != nil {
		return fmt.Errorf("error calling org.ganesha.nfsd.exportmgr.RemoveExport: %v", call.Err)
	}

	// Error removing the EXPORT block from file is not really an error, ganesha
	// will just not export it next time around because the dir doesn't exist
	block, ok := volume.Annotations[annBlock]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, removed the export from the server but can't remove the export from the config file", annBlock)
	}
	if err := p.removeFromFile(p.ganeshaConfig, block); err != nil {
		return fmt.Errorf("removed the export from the server but error removing the export from the config file: %v", err)
	}

	return nil

}

func (p *nfsProvisioner) kernelUnexport(volume *v1.PersistentVolume) error {
	if ann, ok := volume.Annotations[annExportId]; ok {
		// If PV doesn't have this annotation it's no big deal for knfs
		exportId, _ := strconv.ParseUint(ann, 10, 16)
		delete(p.exportIds, uint16(exportId))
	}

	line, ok := volume.Annotations[annLine]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, can't remove the export from /etc/exports", annLine)
	}
	if err := p.removeFromFile("/etc/exports", line); err != nil {
		return fmt.Errorf("error removing the export from /etc/exports: %v", err)
	}

	// Execute exportfs
	cmd := exec.Command("exportfs", "-r")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("exportfs -r failed with error: %v, output: %s", err, out)
	}

	return nil
}
<commit_msg>Lock deletion from map just in case...<commit_after>/*
Copyright 2016 Red Hat, Inc.

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 volume

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/guelfey/go.dbus"
	"k8s.io/client-go/1.4/pkg/api/v1"
)

// Delete removes the directory that was created by Provision backing the given
// PV.
func (p *nfsProvisioner) Delete(volume *v1.PersistentVolume) error {
	// TODO quota, something better than just directories

	path := fmt.Sprintf(p.exportDir+"%s", volume.ObjectMeta.Name)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return fmt.Errorf("Delete called on a volume that doesn't exist, presumably because this provisioner never created it")
	}

	if err := os.RemoveAll(path); err != nil {
		return fmt.Errorf("error deleting volume by removing its path: %v", err)
	}

	var err error
	if p.useGanesha {
		err = p.ganeshaUnexport(volume)
	} else {
		err = p.kernelUnexport(volume)
	}
	if err != nil {
		return fmt.Errorf("removed the path backing the volume but error unexporting it: %v", err)
	}

	return nil
}

func (p *nfsProvisioner) ganeshaUnexport(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations[annExportId]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, can't remove the export from the server", annExportId)
	}
	exportId, _ := strconv.ParseUint(ann, 10, 16)
	p.mapMutex.Lock()
	delete(p.exportIds, uint16(exportId))
	p.mapMutex.Unlock()

	// Call RemoveExport using dbus
	conn, err := dbus.SystemBus()
	if err != nil {
		return fmt.Errorf("error getting dbus session bus: %v", err)
	}
	obj := conn.Object("org.ganesha.nfsd", "/org/ganesha/nfsd/ExportMgr")
	call := obj.Call("org.ganesha.nfsd.exportmgr.RemoveExport", 0, uint16(exportId))
	if call.Err != nil {
		return fmt.Errorf("error calling org.ganesha.nfsd.exportmgr.RemoveExport: %v", call.Err)
	}

	// Error removing the EXPORT block from file is not really an error, ganesha
	// will just not export it next time around because the dir doesn't exist
	block, ok := volume.Annotations[annBlock]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, removed the export from the server but can't remove the export from the config file", annBlock)
	}
	if err := p.removeFromFile(p.ganeshaConfig, block); err != nil {
		return fmt.Errorf("removed the export from the server but error removing the export from the config file: %v", err)
	}

	return nil

}

func (p *nfsProvisioner) kernelUnexport(volume *v1.PersistentVolume) error {
	if ann, ok := volume.Annotations[annExportId]; ok {
		// If PV doesn't have this annotation it's no big deal for knfs
		exportId, _ := strconv.ParseUint(ann, 10, 16)
		p.mapMutex.Lock()
		delete(p.exportIds, uint16(exportId))
		p.mapMutex.Unlock()
	}

	line, ok := volume.Annotations[annLine]
	if !ok {
		return fmt.Errorf("PV doesn't have an annotation %s, can't remove the export from /etc/exports", annLine)
	}
	if err := p.removeFromFile("/etc/exports", line); err != nil {
		return fmt.Errorf("error removing the export from /etc/exports: %v", err)
	}

	// Execute exportfs
	cmd := exec.Command("exportfs", "-r")
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("exportfs -r failed with error: %v, output: %s", err, out)
	}

	return nil
}
<|endoftext|>


<commit_before>package volume

import (
	"fmt"
	"os"
	"strings"
	"syscall"

	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/docker/pkg/system"
	"github.com/opencontainers/runc/libcontainer/label"
)

// DefaultDriverName is the driver name used for the driver
// implemented in the local package.
const DefaultDriverName = "local"

// Scopes define if a volume has is cluster-wide (global) or local only.
// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
const (
	LocalScope  = "local"
	GlobalScope = "global"
)

// Driver is for creating and removing volumes.
type Driver interface {
	// Name returns the name of the volume driver.
	Name() string
	// Create makes a new volume with the given id.
	Create(name string, opts map[string]string) (Volume, error)
	// Remove deletes the volume.
	Remove(vol Volume) (err error)
	// List lists all the volumes the driver has
	List() ([]Volume, error)
	// Get retrieves the volume with the requested name
	Get(name string) (Volume, error)
	// Scope returns the scope of the driver (e.g. `golbal` or `local`).
	// Scope determines how the driver is handled at a cluster level
	Scope() string
}

// Capability defines a set of capabilities that a driver is able to handle.
type Capability struct {
	// Scope is the scope of the driver, `global` or `local`
	// A `global` scope indicates that the driver manages volumes across the cluster
	// A `local` scope indicates that the driver only manages volumes resources local to the host
	// Scope is declared by the driver
	Scope string
}

// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
type Volume interface {
	// Name returns the name of the volume
	Name() string
	// DriverName returns the name of the driver which owns this volume.
	DriverName() string
	// Path returns the absolute path to the volume.
	Path() string
	// Mount mounts the volume and returns the absolute path to
	// where it can be consumed.
	Mount(id string) (string, error)
	// Unmount unmounts the volume when it is no longer in use.
	Unmount(id string) error
	// Status returns low-level status information about a volume
	Status() map[string]interface{}
}

// LabeledVolume wraps a Volume with user-defined labels
type LabeledVolume interface {
	Labels() map[string]string
	Volume
}

// ScopedVolume wraps a volume with a cluster scope (e.g., `local` or `global`)
type ScopedVolume interface {
	Scope() string
	Volume
}

// MountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type MountPoint struct {
	Source      string // Container host directory
	Destination string // Inside the container
	RW          bool   // True if writable
	Name        string // Name set by user
	Driver      string // Volume driver to use
	Volume      Volume `json:"-"`

	// Note Mode is not used on Windows
	Mode string `json:"Relabel"` // Originally field was `Relabel`"

	// Note Propagation is not used on Windows
	Propagation string // Mount propagation string
	Named       bool   // specifies if the mountpoint was specified by name

	// Specifies if data should be copied from the container before the first mount
	// Use a pointer here so we can tell if the user set this value explicitly
	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
	CopyData bool `json:"-"`
	// ID is the opaque ID used to pass to the volume driver.
	// This should be set by calls to `Mount` and unset by calls to `Unmount`
	ID string
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *MountPoint) Setup(mountLabel string) (string, error) {
	if m.Volume != nil {
		if m.ID == "" {
			m.ID = stringid.GenerateNonCryptoID()
		}
		return m.Volume.Mount(m.ID)
	}
	if len(m.Source) == 0 {
		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
	}
	// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
	if err := system.MkdirAll(m.Source, 0755); err != nil {
		if perr, ok := err.(*os.PathError); ok {
			if perr.Err != syscall.ENOTDIR {
				return "", err
			}
		}
	}
	if label.RelabelNeeded(m.Mode) {
		if err := label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
			return "", err
		}
	}
	return m.Source, nil
}

// Path returns the path of a volume in a mount point.
func (m *MountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}
	return m.Source
}

// Type returns the type of mount point
func (m *MountPoint) Type() string {
	if m.Name != "" {
		return "VOLUME"
	}
	if m.Source != "" {
		return "BIND"
	}
	return "EPHEMERAL"
}

// ParseVolumesFrom ensures that the supplied volumes-from is valid.
func ParseVolumesFrom(spec string) (string, string, error) {
	if len(spec) == 0 {
		return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
	}

	specParts := strings.SplitN(spec, ":", 2)
	id := specParts[0]
	mode := "rw"

	if len(specParts) == 2 {
		mode = specParts[1]
		if !ValidMountMode(mode) {
			return "", "", errInvalidMode(mode)
		}
		// For now don't allow propagation properties while importing
		// volumes from data container. These volumes will inherit
		// the same propagation property as of the original volume
		// in data container. This probably can be relaxed in future.
		if HasPropagation(mode) {
			return "", "", errInvalidMode(mode)
		}
		// Do not allow copy modes on volumes-from
		if _, isSet := getCopyMode(mode); isSet {
			return "", "", errInvalidMode(mode)
		}
	}
	return id, mode, nil
}

func errInvalidMode(mode string) error {
	return fmt.Errorf("invalid mode: %v", mode)
}

func errInvalidSpec(spec string) error {
	return fmt.Errorf("Invalid volume specification: '%s'", spec)
}
<commit_msg>fixes minor typo in comment<commit_after>package volume

import (
	"fmt"
	"os"
	"strings"
	"syscall"

	"github.com/docker/docker/pkg/stringid"
	"github.com/docker/docker/pkg/system"
	"github.com/opencontainers/runc/libcontainer/label"
)

// DefaultDriverName is the driver name used for the driver
// implemented in the local package.
const DefaultDriverName = "local"

// Scopes define if a volume has is cluster-wide (global) or local only.
// Scopes are returned by the volume driver when it is queried for capabilities and then set on a volume
const (
	LocalScope  = "local"
	GlobalScope = "global"
)

// Driver is for creating and removing volumes.
type Driver interface {
	// Name returns the name of the volume driver.
	Name() string
	// Create makes a new volume with the given id.
	Create(name string, opts map[string]string) (Volume, error)
	// Remove deletes the volume.
	Remove(vol Volume) (err error)
	// List lists all the volumes the driver has
	List() ([]Volume, error)
	// Get retrieves the volume with the requested name
	Get(name string) (Volume, error)
	// Scope returns the scope of the driver (e.g. `global` or `local`).
	// Scope determines how the driver is handled at a cluster level
	Scope() string
}

// Capability defines a set of capabilities that a driver is able to handle.
type Capability struct {
	// Scope is the scope of the driver, `global` or `local`
	// A `global` scope indicates that the driver manages volumes across the cluster
	// A `local` scope indicates that the driver only manages volumes resources local to the host
	// Scope is declared by the driver
	Scope string
}

// Volume is a place to store data. It is backed by a specific driver, and can be mounted.
type Volume interface {
	// Name returns the name of the volume
	Name() string
	// DriverName returns the name of the driver which owns this volume.
	DriverName() string
	// Path returns the absolute path to the volume.
	Path() string
	// Mount mounts the volume and returns the absolute path to
	// where it can be consumed.
	Mount(id string) (string, error)
	// Unmount unmounts the volume when it is no longer in use.
	Unmount(id string) error
	// Status returns low-level status information about a volume
	Status() map[string]interface{}
}

// LabeledVolume wraps a Volume with user-defined labels
type LabeledVolume interface {
	Labels() map[string]string
	Volume
}

// ScopedVolume wraps a volume with a cluster scope (e.g., `local` or `global`)
type ScopedVolume interface {
	Scope() string
	Volume
}

// MountPoint is the intersection point between a volume and a container. It
// specifies which volume is to be used and where inside a container it should
// be mounted.
type MountPoint struct {
	Source      string // Container host directory
	Destination string // Inside the container
	RW          bool   // True if writable
	Name        string // Name set by user
	Driver      string // Volume driver to use
	Volume      Volume `json:"-"`

	// Note Mode is not used on Windows
	Mode string `json:"Relabel"` // Originally field was `Relabel`"

	// Note Propagation is not used on Windows
	Propagation string // Mount propagation string
	Named       bool   // specifies if the mountpoint was specified by name

	// Specifies if data should be copied from the container before the first mount
	// Use a pointer here so we can tell if the user set this value explicitly
	// This allows us to error out when the user explicitly enabled copy but we can't copy due to the volume being populated
	CopyData bool `json:"-"`
	// ID is the opaque ID used to pass to the volume driver.
	// This should be set by calls to `Mount` and unset by calls to `Unmount`
	ID string
}

// Setup sets up a mount point by either mounting the volume if it is
// configured, or creating the source directory if supplied.
func (m *MountPoint) Setup(mountLabel string) (string, error) {
	if m.Volume != nil {
		if m.ID == "" {
			m.ID = stringid.GenerateNonCryptoID()
		}
		return m.Volume.Mount(m.ID)
	}
	if len(m.Source) == 0 {
		return "", fmt.Errorf("Unable to setup mount point, neither source nor volume defined")
	}
	// system.MkdirAll() produces an error if m.Source exists and is a file (not a directory),
	if err := system.MkdirAll(m.Source, 0755); err != nil {
		if perr, ok := err.(*os.PathError); ok {
			if perr.Err != syscall.ENOTDIR {
				return "", err
			}
		}
	}
	if label.RelabelNeeded(m.Mode) {
		if err := label.Relabel(m.Source, mountLabel, label.IsShared(m.Mode)); err != nil {
			return "", err
		}
	}
	return m.Source, nil
}

// Path returns the path of a volume in a mount point.
func (m *MountPoint) Path() string {
	if m.Volume != nil {
		return m.Volume.Path()
	}
	return m.Source
}

// Type returns the type of mount point
func (m *MountPoint) Type() string {
	if m.Name != "" {
		return "VOLUME"
	}
	if m.Source != "" {
		return "BIND"
	}
	return "EPHEMERAL"
}

// ParseVolumesFrom ensures that the supplied volumes-from is valid.
func ParseVolumesFrom(spec string) (string, string, error) {
	if len(spec) == 0 {
		return "", "", fmt.Errorf("malformed volumes-from specification: %s", spec)
	}

	specParts := strings.SplitN(spec, ":", 2)
	id := specParts[0]
	mode := "rw"

	if len(specParts) == 2 {
		mode = specParts[1]
		if !ValidMountMode(mode) {
			return "", "", errInvalidMode(mode)
		}
		// For now don't allow propagation properties while importing
		// volumes from data container. These volumes will inherit
		// the same propagation property as of the original volume
		// in data container. This probably can be relaxed in future.
		if HasPropagation(mode) {
			return "", "", errInvalidMode(mode)
		}
		// Do not allow copy modes on volumes-from
		if _, isSet := getCopyMode(mode); isSet {
			return "", "", errInvalidMode(mode)
		}
	}
	return id, mode, nil
}

func errInvalidMode(mode string) error {
	return fmt.Errorf("invalid mode: %v", mode)
}

func errInvalidSpec(spec string) error {
	return fmt.Errorf("Invalid volume specification: '%s'", spec)
}
<|endoftext|>


<commit_before>package buildkite

import "fmt"
import "strings"

// You can overriden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/buildkite.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "1.0-beta.10"
var buildVersion string = ""

func Version() string {
	// Only output the build version if a pre-release
	if strings.Contains(baseVersion, "beta") || strings.Contains(baseVersion, "alpha") {
		// Use a default buildVersion if one doesn't exist
		actualBuildVersion := buildVersion
		if actualBuildVersion == "" {
			actualBuildVersion = "x"
		}

		return fmt.Sprintf("%s.%s", baseVersion, actualBuildVersion)
	} else {
		return baseVersion
	}
}
<commit_msg>Bumping to version 1.0-beta.11<commit_after>package buildkite

import "fmt"
import "strings"

// You can overriden buildVersion at compile time by using:
//
//  go run -ldflags "-X github.com/buildkite/agent/buildkite.buildVersion abc" *.go --version
//
// On CI, the binaries are always build with the buildVersion variable set.

var baseVersion string = "1.0-beta.11"
var buildVersion string = ""

func Version() string {
	// Only output the build version if a pre-release
	if strings.Contains(baseVersion, "beta") || strings.Contains(baseVersion, "alpha") {
		// Use a default buildVersion if one doesn't exist
		actualBuildVersion := buildVersion
		if actualBuildVersion == "" {
			actualBuildVersion = "x"
		}

		return fmt.Sprintf("%s.%s", baseVersion, actualBuildVersion)
	} else {
		return baseVersion
	}
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/sampler"
)

// peersData encapsulate all peers known to Network.
// peers in peersData can be retrieved by their id and can be iterated over
// via getList() method, returning a slice of peers.
// Index associated with peers MAY CHANGE following removal of other peers

type peersData struct {
	peersIdxes map[ids.ShortID]int // peerID -> *peer index in peersList
	peersList  []*peer             // invariant: len(peersList) == len(peersIdxes)
}

func (p *peersData) initialize() {
	p.peersIdxes = make(map[ids.ShortID]int)
	p.peersList = make([]*peer, 0)
}

func (p *peersData) reset() {
	p.initialize()
}

func (p *peersData) add(peer *peer) {
	if _, ok := p.getByID(peer.nodeID); !ok { // new insertion
		p.peersList = append(p.peersList, peer)
		p.peersIdxes[peer.nodeID] = len(p.peersList) - 1
	} else { // update
		p.peersList[p.peersIdxes[peer.nodeID]] = peer
	}
}

func (p *peersData) remove(peer *peer) {
	if _, ok := p.getByID(peer.nodeID); !ok {
		return
	}

	// Drop p by replacing it with last peer in peersList.
	// if p is already the last peer, simply drop it.
	// Keep peersIdxes synced. peersList order is not preserved
	idToDrop := peer.nodeID
	idxToReplace := p.peersIdxes[idToDrop]

	if idxToReplace != len(p.peersList)-1 {
		lastPeer := p.peersList[len(p.peersList)-1]
		p.peersList[idxToReplace] = lastPeer
		p.peersIdxes[lastPeer.nodeID] = idxToReplace
	}
	p.peersList = p.peersList[:len(p.peersList)-1]
	delete(p.peersIdxes, idToDrop)
}

func (p *peersData) getByID(id ids.ShortID) (*peer, bool) {
	if idx, ok := p.peersIdxes[id]; ok {
		return p.peersList[idx], ok
	}
	return nil, false
}

func (p *peersData) getByIdx(idx int) (*peer, bool) {
	// peer index may change following other peers removal
	// since upon removal order is not guaranteed.
	if idx < 0 || idx >= len(p.peersList) {
		return nil, false
	}
	return p.peersList[idx], true
}

func (p *peersData) size() int {
	return len(p.peersList)
}

// Randomly sample [n] peers that have finished the handshake.
// If < [n] peers have finished the handshake, returns < [n] peers.
// If [n] > [p.size()], returns <= [p.size()] peers.
// [n] must be >= 0.
// The returned list has no nil elements.
// [p] must not be modified while this method is executing.
func (p *peersData) sample(n int) ([]*peer, error) {
	numPeers := p.size()
	if numPeers < n {
		n = numPeers
	}
	s := sampler.NewUniform()
	if err := s.Initialize(uint64(numPeers)); err != nil {
		return nil, err
	}
	peers := make([]*peer, 0, n) // peers we'll gossip to
	for len(peers) < n {
		idx, err := s.Next()
		if err != nil {
			// all peers have been sampled and not enough valid ones found.
			// return what we have
			return peers, nil
		}
		peer, found := p.getByIdx(int(idx))
		if !found {
			// This should never happen
			return nil, fmt.Errorf("no peer at index %d", idx)
		}
		if !peer.finishedHandshake.GetValue() {
			continue
		}
		peers = append(peers, peer)
	}
	return peers, nil
}
<commit_msg>short-circuit if n == 0 or numPeers == 0<commit_after>package network

import (
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/sampler"
)

// peersData encapsulate all peers known to Network.
// peers in peersData can be retrieved by their id and can be iterated over
// via getList() method, returning a slice of peers.
// Index associated with peers MAY CHANGE following removal of other peers

type peersData struct {
	peersIdxes map[ids.ShortID]int // peerID -> *peer index in peersList
	peersList  []*peer             // invariant: len(peersList) == len(peersIdxes)
}

func (p *peersData) initialize() {
	p.peersIdxes = make(map[ids.ShortID]int)
	p.peersList = make([]*peer, 0)
}

func (p *peersData) reset() {
	p.initialize()
}

func (p *peersData) add(peer *peer) {
	if _, ok := p.getByID(peer.nodeID); !ok { // new insertion
		p.peersList = append(p.peersList, peer)
		p.peersIdxes[peer.nodeID] = len(p.peersList) - 1
	} else { // update
		p.peersList[p.peersIdxes[peer.nodeID]] = peer
	}
}

func (p *peersData) remove(peer *peer) {
	if _, ok := p.getByID(peer.nodeID); !ok {
		return
	}

	// Drop p by replacing it with last peer in peersList.
	// if p is already the last peer, simply drop it.
	// Keep peersIdxes synced. peersList order is not preserved
	idToDrop := peer.nodeID
	idxToReplace := p.peersIdxes[idToDrop]

	if idxToReplace != len(p.peersList)-1 {
		lastPeer := p.peersList[len(p.peersList)-1]
		p.peersList[idxToReplace] = lastPeer
		p.peersIdxes[lastPeer.nodeID] = idxToReplace
	}
	p.peersList = p.peersList[:len(p.peersList)-1]
	delete(p.peersIdxes, idToDrop)
}

func (p *peersData) getByID(id ids.ShortID) (*peer, bool) {
	if idx, ok := p.peersIdxes[id]; ok {
		return p.peersList[idx], ok
	}
	return nil, false
}

func (p *peersData) getByIdx(idx int) (*peer, bool) {
	// peer index may change following other peers removal
	// since upon removal order is not guaranteed.
	if idx < 0 || idx >= len(p.peersList) {
		return nil, false
	}
	return p.peersList[idx], true
}

func (p *peersData) size() int {
	return len(p.peersList)
}

// Randomly sample [n] peers that have finished the handshake.
// If < [n] peers have finished the handshake, returns < [n] peers.
// If [n] > [p.size()], returns <= [p.size()] peers.
// [n] must be >= 0.
// [p] must not be modified while this method is executing.
func (p *peersData) sample(n int) ([]*peer, error) {
	numPeers := p.size()
	if numPeers < n {
		n = numPeers
	}
	if n == 0 {
		return nil, nil
	}
	s := sampler.NewUniform()
	if err := s.Initialize(uint64(numPeers)); err != nil {
		return nil, err
	}
	peers := make([]*peer, 0, n) // peers we'll gossip to
	for len(peers) < n {
		idx, err := s.Next()
		if err != nil {
			// all peers have been sampled and not enough valid ones found.
			// return what we have
			return peers, nil
		}
		peer, found := p.getByIdx(int(idx))
		if !found {
			// This should never happen
			return nil, fmt.Errorf("no peer at index %d", idx)
		}
		if !peer.finishedHandshake.GetValue() {
			continue
		}
		peers = append(peers, peer)
	}
	return peers, nil
}
<|endoftext|>


<commit_before>package network

import (
	"flag"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"os"
)

// Returns an S3 session for this objectList.
func GetS3Session(awsRegion, accessKeyId, secretAccessKey string) (*session.Session, error) {
	testsAreRunning := flag.Lookup("test.v") != nil
	if !testsAreRunning && (os.Getenv("AWS_ACCESS_KEY_ID") == "" || os.Getenv("AWS_SECRET_ACCESS_KEY") == "") {
		panic("AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY not set in environment")
	}
	creds := credentials.NewEnvCredentials()
	if accessKeyId != "" && secretAccessKey != "" {
		creds = credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "")
	}
	_session := session.New(&aws.Config{
		Region:      aws.String(awsRegion),
		Credentials: creds,
	})
	if _session == nil {
		return nil, fmt.Errorf("AWS Session returned nil")
	}
	return _session, nil
}
<commit_msg>Don't panic if AWS env vars are not set<commit_after>package network

import (
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
)

// Returns an S3 session for this objectList.
func GetS3Session(awsRegion, accessKeyId, secretAccessKey string) (*session.Session, error) {
	creds := credentials.NewEnvCredentials()
	if accessKeyId != "" && secretAccessKey != "" {
		creds = credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "")
	}
	_session := session.New(&aws.Config{
		Region:      aws.String(awsRegion),
		Credentials: creds,
	})
	if _session == nil {
		return nil, fmt.Errorf("AWS Session returned nil")
	}
	return _session, nil
}
<|endoftext|>


<commit_before>package kamino_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "github.com/modcloth/kamino"
)

var _ = Describe("NewGenome()", func() {

	var opts map[string]string

	BeforeEach(func() {
		opts = map[string]string{
			"depth":   "50",
			"token":   "abc123",
			"account": "modcloth",
			"repo":    "kamino",
			"cache":   "",
			"ref":     "123",
		}
	})

	It("assigns token from the provided options", func() {
		ret, _ := NewGenome(opts)

		Expect(ret.APIToken).To(Equal("abc123"))
	})

	Context("with a non-integer depth", func() {
		It("returns an error", func() {
			opts["depth"] = "foo"
			ret, err := NewGenome(opts)

			Expect(ret).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no account specified", func() {
		It("returns an error", func() {
			opts["account"] = ""
			ret, err := NewGenome(opts)

			Expect(ret).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no repo specified", func() {
		It("returns an error", func() {
			opts["repo"] = ""
			ret, err := NewGenome(opts)

			Expect(ret).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with an empty cache option", func() {
		It("defaults the cache option to `no`", func() {
			ret, _ := NewGenome(opts)

			Expect(ret.UseCache).To(Equal("no"))
		})
	})

	Context("with an invalid cache option", func() {
		It("returns an error", func() {
			opts["cache"] = "foo"
			ret, err := NewGenome(opts)

			Expect(ret).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no ref specified", func() {
		It("returns an error", func() {
			opts["ref"] = ""
			ret, err := NewGenome(opts)

			Expect(ret).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})
})
<commit_msg>Changing "ret" to "subject" in genome test<commit_after>package kamino_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "github.com/modcloth/kamino"
)

var _ = Describe("NewGenome()", func() {

	var opts map[string]string

	BeforeEach(func() {
		opts = map[string]string{
			"depth":   "50",
			"token":   "abc123",
			"account": "modcloth",
			"repo":    "kamino",
			"cache":   "",
			"ref":     "123",
		}
	})

	It("assigns token from the provided options", func() {
		subject, _ := NewGenome(opts)

		Expect(subject.APIToken).To(Equal("abc123"))
	})

	Context("with a non-integer depth", func() {
		It("returns an error", func() {
			opts["depth"] = "foo"
			subject, err := NewGenome(opts)

			Expect(subject).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no account specified", func() {
		It("returns an error", func() {
			opts["account"] = ""
			subject, err := NewGenome(opts)

			Expect(subject).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no repo specified", func() {
		It("returns an error", func() {
			opts["repo"] = ""
			subject, err := NewGenome(opts)

			Expect(subject).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with an empty cache option", func() {
		It("defaults the cache option to `no`", func() {
			subject, _ := NewGenome(opts)

			Expect(subject.UseCache).To(Equal("no"))
		})
	})

	Context("with an invalid cache option", func() {
		It("returns an error", func() {
			opts["cache"] = "foo"
			subject, err := NewGenome(opts)

			Expect(subject).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})

	Context("with no ref specified", func() {
		It("returns an error", func() {
			opts["ref"] = ""
			subject, err := NewGenome(opts)

			Expect(subject).To(BeNil())
			Expect(err).ToNot(BeNil())
		})
	})
})
<|endoftext|>


<commit_before>package dnscache

import (
	"time"

	"github.com/miekg/dns"
)

// Cache provides a proactive and expiring caching layer for DNS queries.
// All public methods of Cache are threadsafe.
type Cache struct {
	requestChan    chan Request
	responseChan   chan response
	expirationChan chan cacheKey
	clearChan      chan struct{}
	stopChan       chan struct{}
	cacheMaxTTL    time.Duration
	cacheMissTTL   time.Duration
	lookup         func(dns.Question) []dns.RR
}

// Request defines a DNS request to be processed by a Cache object
type Request struct {
	Question     dns.Question
	Start        time.Time
	ResponseChan chan []dns.RR
}

// New creates a DNS cache with the given DNS lookup function
func New(bufferSize int, cacheMaxTTL, cacheMissTTL time.Duration, lookup func(dns.Question) []dns.RR) *Cache {
	c := &Cache{
		requestChan:    make(chan Request, bufferSize),
		responseChan:   make(chan response, bufferSize),
		expirationChan: make(chan cacheKey, bufferSize),
		clearChan:      make(chan struct{}, bufferSize),
		stopChan:       make(chan struct{}, bufferSize),
		cacheMaxTTL:    cacheMaxTTL,
		cacheMissTTL:   cacheMissTTL,
		lookup:         lookup,
	}
	go c.process()
	return c
}

// Lookup will retrieve an answer for the given request from the cache if it
// is present and unexpired, otherwise it will attempt to retrieve the value via
// the cache's lookup function and cache the returned value
func (c *Cache) Lookup(r Request) {
	c.requestChan <- r
}

// Insert will insert the given resource records into the cache as a response
// to the given question
func (c *Cache) Insert(q dns.Question, rr []dns.RR) {
	c.responseChan <- response{Key: cacheKey{q}, RR: rr}
}

// Expire will remove any answers to the given question from the cache
func (c *Cache) Expire(q dns.Question) {
	c.expirationChan <- cacheKey{q}
}

// Clear will remove all recorded answers from the cache
func (c *Cache) Clear() {
	c.clearChan <- struct{}{}
}

// Stop will shut down the cache's processor
func (c *Cache) Stop() {
	c.stopChan <- struct{}{}
}

type response struct {
	Key cacheKey
	RR  []dns.RR
}

type cacheKey struct {
	dns.Question
}

type cacheValue struct {
	Expiration time.Time
	Creation   time.Time
	HitCount   uint
	Timer      *time.Timer
	RR         []dns.RR
}

func (c *Cache) process() {
	data := make(map[cacheKey]*cacheValue)
	pending := make(map[cacheKey][]Request)

	for {
		select {
		case req := <-c.requestChan:
			key := cacheKey{req.Question}
			now := time.Now()
			if entry, ok := data[key]; ok && entry.Expiration.After(now) {
				elapsed := now.Sub(entry.Creation)
				entry.HitCount++
				if entry.HitCount == 1 {
					// This is the first cache hit since this entry was last updated
					// Update the timer so that it will proactively refresh the cache
					duration := entry.Expiration.Sub(entry.Creation)
					refresh := cacheRefreshDuration(duration, elapsed)
					entry.Timer.Reset(refresh)
				}
				rr := cacheCopy(entry.RR)
				cacheElapse(rr, uint32(elapsed/time.Second))
				//fmt.Printf("DNSCACHE HIT:         \t%v\t#%d\n", key, entry.HitCount)
				go func() {
					// Send responses via a separate goroutine so that we don't deadlock
					req.ResponseChan <- rr
				}()
			} else {
				if ok {
					//fmt.Printf("DNSCACHE EXPIRED: %v\n", key)
				} else {
					//fmt.Printf("DNSCACHE MISS: %v\n", key)
				}
				requests, running := pending[key]
				pending[key] = append(requests, req)
				if !running {
					go func() {
						rr := c.lookup(key.Question)
						c.responseChan <- response{Key: key, RR: rr}
					}()
				}
			}
		case resp := <-c.responseChan:
			key := resp.Key
			now := time.Now()
			duration := cacheDuration(resp.RR, c.cacheMaxTTL, c.cacheMissTTL)
			if duration > 0 {
				if entry, ok := data[key]; ok {
					entry.Expiration = now.Add(duration)
					entry.Creation = now
					entry.HitCount = 0
					entry.Timer.Reset(duration)
					entry.RR = resp.RR
				} else {
					data[key] = &cacheValue{
						Expiration: now.Add(duration),
						Creation:   now,
						HitCount:   0,
						Timer: time.AfterFunc(duration, func() {
							c.expirationChan <- key
						}),
						RR: resp.RR,
					}
				}
			}
			requests := pending[key]
			delete(pending, key)
			n := len(requests)
			if n > 0 {
				output := cacheCopy(resp.RR) // Keep clients from reaching into cached data
				// Send responses via a separate goroutine so that we don't deadlock
				go func() {
					if n == 1 {
						requests[0].ResponseChan <- output
					} else {
						for _, req := range requests {
							req.ResponseChan <- cacheCopy(output) // Keep requestors from reaching into each other's data
						}
					}
				}()
			}
		case key := <-c.expirationChan:
			now := time.Now()
			if entry, ok := data[key]; ok {
				if entry.Expiration.After(now) {
					entry.Timer.Stop()
					delete(data, key)
				} else {
					entry.Timer.Reset(entry.Expiration.Sub(now))
				}
				if entry.HitCount > 0 {
					_, running := pending[key]
					if !running {
						pending[key] = make([]Request, 0)
						go func() {
							rr := c.lookup(key.Question)
							c.responseChan <- response{Key: key, RR: rr}
						}()
					}
				}
			}
		case <-c.clearChan:
			for _, entry := range data {
				entry.Timer.Stop()
			}
			data = make(map[cacheKey]*cacheValue)
		case <-c.stopChan:
			// FIXME: Clean up outstanding requests somehow?
			for _, entry := range data {
				entry.Timer.Stop()
			}
			data = nil
			return
		}
	}
}

// cacheCopy performs a deep copy of the given resource records
func cacheCopy(rr []dns.RR) []dns.RR {
	clone := make([]dns.RR, len(rr))
	for i := range rr {
		clone[i] = dns.Copy(rr[i])
	}
	return clone
}

// cacheElapse subtracts the given number of seconds from the TTL of each
// resource record provided
func cacheElapse(rr []dns.RR, seconds uint32) {
	for i := range rr {
		hdr := rr[i].Header()
		if seconds < hdr.Ttl {
			hdr.Ttl -= seconds
		} else {
			hdr.Ttl = 0
		}
	}
}

// cacheDuration determines how long an entry should be cached
func cacheDuration(rr []dns.RR, max time.Duration, empty time.Duration) time.Duration {
	if len(rr) == 0 {
		if max < empty {
			return max
		}
		return empty
	}
	ttl := rr[0].Header().Ttl
	for i := 1; i < len(rr); i++ {
		hdr := rr[i].Header()
		if hdr.Ttl < ttl {
			ttl = hdr.Ttl
		}
	}
	duration := time.Second * time.Duration(ttl)
	if duration > max {
		return max
	}
	return duration
}

// cacheRefreshDuration determines how long to wait until a cache refresh should occur
func cacheRefreshDuration(duration, elapsed time.Duration) time.Duration {
	remaining := duration - elapsed
	if remaining >= time.Second {
		return remaining / 2
	}
	return remaining
}
<commit_msg>DNS lookup functions are now provided additional context<commit_after>package dnscache

import (
	"time"

	"github.com/miekg/dns"
)

// Cache provides a proactive and expiring caching layer for DNS queries.
// All public methods of Cache are threadsafe.
type Cache struct {
	requestChan    chan Request
	responseChan   chan response
	expirationChan chan cacheKey
	clearChan      chan struct{}
	stopChan       chan struct{}
	cacheMaxTTL    time.Duration
	cacheMissTTL   time.Duration
	lookup         func(Context, dns.Question) []dns.RR
}

// Request defines a DNS request to be processed by a Cache object.
type Request struct {
	Question     dns.Question
	Start        time.Time
	Data         interface{}
	ResponseChan chan []dns.RR
}

// Event identifies what kind of event has triggered the lookup.
type Event uint8

const (
	// Query indicates that this event is the result of an external query.
	Query Event = iota

	// Renewal indicates that this event is the result of proactive record
	// renewal.
	Renewal
)

// Context provides contextual information about a DNS lookup, including what
// event triggered the lookup, when the looup was started, and any additional
// data that was provided in the original request.
type Context struct {
	Event Event
	Start time.Time
	Data  interface{}
}

// New creates a DNS cache with the given DNS lookup function
func New(bufferSize int, cacheMaxTTL, cacheMissTTL time.Duration, lookup func(Context, dns.Question) []dns.RR) *Cache {
	c := &Cache{
		requestChan:    make(chan Request, bufferSize),
		responseChan:   make(chan response, bufferSize),
		expirationChan: make(chan cacheKey, bufferSize),
		clearChan:      make(chan struct{}, bufferSize),
		stopChan:       make(chan struct{}, bufferSize),
		cacheMaxTTL:    cacheMaxTTL,
		cacheMissTTL:   cacheMissTTL,
		lookup:         lookup,
	}
	go c.process()
	return c
}

// Lookup will retrieve an answer for the given request from the cache if it
// is present and unexpired, otherwise it will attempt to retrieve the value via
// the cache's lookup function and cache the returned value.
func (c *Cache) Lookup(r Request) {
	c.requestChan <- r
}

// Insert will insert the given resource records into the cache as a response.
// to the given question
func (c *Cache) Insert(q dns.Question, rr []dns.RR) {
	c.responseChan <- response{Key: cacheKey{q}, RR: rr}
}

// Expire will remove any answers to the given question from the cache.
func (c *Cache) Expire(q dns.Question) {
	c.expirationChan <- cacheKey{q}
}

// Clear will remove all recorded answers from the cache.
func (c *Cache) Clear() {
	c.clearChan <- struct{}{}
}

// Stop will shut down the cache's processor.
func (c *Cache) Stop() {
	c.stopChan <- struct{}{}
}

type response struct {
	Key cacheKey
	RR  []dns.RR
}

type cacheKey struct {
	dns.Question
}

type cacheValue struct {
	Expiration time.Time
	Creation   time.Time
	HitCount   uint
	Timer      *time.Timer
	RR         []dns.RR
}

func (c *Cache) process() {
	data := make(map[cacheKey]*cacheValue)
	pending := make(map[cacheKey][]Request)

	for {
		select {
		case req := <-c.requestChan:
			key := cacheKey{req.Question}
			now := time.Now()
			if entry, ok := data[key]; ok && entry.Expiration.After(now) {
				elapsed := now.Sub(entry.Creation)
				entry.HitCount++
				if entry.HitCount == 1 {
					// This is the first cache hit since this entry was last updated
					// Update the timer so that it will proactively refresh the cache
					duration := entry.Expiration.Sub(entry.Creation)
					refresh := cacheRefreshDuration(duration, elapsed)
					entry.Timer.Reset(refresh)
				}
				rr := cacheCopy(entry.RR)
				cacheElapse(rr, uint32(elapsed/time.Second))
				//fmt.Printf("DNSCACHE HIT:         \t%v\t#%d\n", key, entry.HitCount)
				go func() {
					// Send responses via a separate goroutine so that we don't deadlock
					req.ResponseChan <- rr
				}()
			} else {
				if ok {
					//fmt.Printf("DNSCACHE EXPIRED: %v\n", key)
				} else {
					//fmt.Printf("DNSCACHE MISS: %v\n", key)
				}
				requests, running := pending[key]
				pending[key] = append(requests, req)
				if !running {
					go func() {
						rr := c.lookup(Context{Event: Query, Start: bestTime(req.Start, now), Data: req.Data}, key.Question)
						c.responseChan <- response{Key: key, RR: rr}
					}()
				}
			}
		case resp := <-c.responseChan:
			key := resp.Key
			now := time.Now()
			duration := cacheDuration(resp.RR, c.cacheMaxTTL, c.cacheMissTTL)
			if duration > 0 {
				if entry, ok := data[key]; ok {
					entry.Expiration = now.Add(duration)
					entry.Creation = now
					entry.HitCount = 0
					entry.Timer.Reset(duration)
					entry.RR = resp.RR
				} else {
					data[key] = &cacheValue{
						Expiration: now.Add(duration),
						Creation:   now,
						HitCount:   0,
						Timer: time.AfterFunc(duration, func() {
							c.expirationChan <- key
						}),
						RR: resp.RR,
					}
				}
			}
			requests := pending[key]
			delete(pending, key)
			n := len(requests)
			if n > 0 {
				output := cacheCopy(resp.RR) // Keep clients from reaching into cached data
				// Send responses via a separate goroutine so that we don't deadlock
				go func() {
					if n == 1 {
						requests[0].ResponseChan <- output
					} else {
						for _, req := range requests {
							req.ResponseChan <- cacheCopy(output) // Keep requestors from reaching into each other's data
						}
					}
				}()
			}
		case key := <-c.expirationChan:
			now := time.Now()
			if entry, ok := data[key]; ok {
				if entry.Expiration.After(now) {
					entry.Timer.Stop()
					delete(data, key)
				} else {
					entry.Timer.Reset(entry.Expiration.Sub(now))
				}
				if entry.HitCount > 0 {
					_, running := pending[key]
					if !running {
						pending[key] = make([]Request, 0)
						go func() {
							rr := c.lookup(Context{Event: Renewal, Start: now}, key.Question)
							c.responseChan <- response{Key: key, RR: rr}
						}()
					}
				}
			}
		case <-c.clearChan:
			for _, entry := range data {
				entry.Timer.Stop()
			}
			data = make(map[cacheKey]*cacheValue)
		case <-c.stopChan:
			// FIXME: Clean up outstanding requests somehow?
			for _, entry := range data {
				entry.Timer.Stop()
			}
			data = nil
			return
		}
	}
}

// cacheCopy performs a deep copy of the given resource records
func cacheCopy(rr []dns.RR) []dns.RR {
	clone := make([]dns.RR, len(rr))
	for i := range rr {
		clone[i] = dns.Copy(rr[i])
	}
	return clone
}

// cacheElapse subtracts the given number of seconds from the TTL of each
// resource record provided
func cacheElapse(rr []dns.RR, seconds uint32) {
	for i := range rr {
		hdr := rr[i].Header()
		if seconds < hdr.Ttl {
			hdr.Ttl -= seconds
		} else {
			hdr.Ttl = 0
		}
	}
}

// cacheDuration determines how long an entry should be cached
func cacheDuration(rr []dns.RR, max time.Duration, empty time.Duration) time.Duration {
	if len(rr) == 0 {
		if max < empty {
			return max
		}
		return empty
	}
	ttl := rr[0].Header().Ttl
	for i := 1; i < len(rr); i++ {
		hdr := rr[i].Header()
		if hdr.Ttl < ttl {
			ttl = hdr.Ttl
		}
	}
	duration := time.Second * time.Duration(ttl)
	if duration > max {
		return max
	}
	return duration
}

// cacheRefreshDuration determines how long to wait until a cache refresh should occur
func cacheRefreshDuration(duration, elapsed time.Duration) time.Duration {
	remaining := duration - elapsed
	if remaining >= time.Second {
		return remaining / 2
	}
	return remaining
}

// bestTime	returns the most appropriate time that marks the start of
// something, given a user-provided start time and the current time. If the
// user-provided time is zero (not provided) then the current time is used.
func bestTime(start, now time.Time) time.Time {
	if start.IsZero() {
		return now
	}
	return start
}
<|endoftext|>


<commit_before>package loggenrunner

import (
	"bytes"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
)

// DataStore holds all the data info for a given simulated log line
type DataStore struct {
	Text string `json:"text"`
}

// RunLogLineParams holds all the data to be passed to RunLogLine
type RunLogLineParams struct {
	HTTPLoc        string
	PostBody       string
	IntervalSecs   int
	IntervalStdDev float64
}

// randomizeString takes a string, looks for the random tokens (int and string), and replaces them
func randomizeString(text string, timeformat string) string {
	// Bail if we can't get any randomizers
	goodstring, err := regexp.MatchString(`\$\[[^\]]+\]`, text)
	if err != nil {
		log.Error("Something broke on parsing the text string with a regular expression")
	}

	//Return original string if no randomizers
	if !goodstring {
		return text
	}

	// Find all randomizing tokens
	re := regexp.MustCompile(`\$\[[^\]]+\]`)
	randos := re.FindAllString(text, -1)
	log.Debug("Random tokens: ", randos)

	// Create a list of new strings to be inserted where the tokens were
	var newstrings []string
	replacer := strings.NewReplacer("$[", "", "]", "")

	// Append the properly randomized values to the newstrings slice
	for _, rando := range randos {
		// Take off the leading and trailing formatting
		tempstring := replacer.Replace(rando)
		log.Debug("tempstring: ", tempstring)

		// Split the rnadomizer into individual items
		tempstrings := strings.Split(tempstring, ",")
		log.Debug("tempstrings: ", tempstrings)

		// Numeric ranges will only have two items for an upper and lower bound, timestamps have "time" and "stamp", all the rest are string groups
		var randType string
		num0, err := strconv.Atoi(string(tempstrings[0]))
		num1, err2 := strconv.Atoi(string(tempstrings[1]))
		log.Debug("num0 parsed: ", num0, err)
		log.Debug("num1 parsed: ", num1, err2)
		log.Debug("Length of tempstrings: ", len(tempstrings))
		log.Debug("Numbers?: ", len(tempstrings) == 2 && err == nil && err2 == nil)
		log.Debug("Timestamp?: ", tempstrings[0] == "time" && tempstrings[1] == "stamp")

		switch {
		case len(tempstrings) == 2 && err == nil && err2 == nil:
			randType = "Number"
		case tempstrings[0] == "time" && tempstrings[1] == "stamp":
			randType = "Timestamp"
		default:
			randType = "Category"
		}

		switch randType {
		case "Category":
			log.Debug("Treating as Category")
			newstrings = append(newstrings, tempstrings[rand.Intn(len(tempstrings))])
		case "Number":
			log.Debug("Treating as Number")

			// Get a random number in the range
			diff := num1 - num0
			log.Debug("diff parsed: ", diff)
			tempnum := rand.Intn(diff)
			log.Debug("random number: ", tempnum)
			log.Debug("random number as string: ", strconv.Itoa(tempnum+num0))
			newstrings = append(newstrings, strconv.Itoa(tempnum+num0))
		case "Timestamp":
			t := time.Now()
			log.Debug("Current time: ", t)
			timeformatted := t.Format(timeformat)
			log.Debug("Formatted time: ", timeformatted)

			newstrings = append(newstrings, timeformatted)
		}
	}

	nonRandomStrings := re.Split(text, -1)
	var newLogLine []string

	for i := 0; i < len(nonRandomStrings); i++ {
		newLogLine = append(newLogLine, nonRandomStrings[i])
		if i != len(nonRandomStrings)-1 {
			newLogLine = append(newLogLine, newstrings[i])
		}
	}

	log.Info("Randomization complete. New string: ", strings.Join(newLogLine, ""))

	return strings.Join(newLogLine, "")
}

// RunLogLine makes repeated calls to an endpoint given the configs of the log line
func RunLogLine(HTTPLoc string, PostBody string, IntervalSecs int, IntervalStdDev float64, TimeFormat string, SumoCategory string, SumoHost string, SumoName string) {
	log.Info("Starting log runner")

	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	client := &http.Client{}

	// Begin loop to post the value until we're done
	for {
		// Randomize the post body if need be
		var stringBody = []byte(randomizeString(PostBody, TimeFormat))

		// Post to Sumo
		log.Info("Sending log to Sumo: ", stringBody)
		req, err := http.NewRequest("POST", HTTPLoc, bytes.NewBuffer(stringBody))
		req.Header.Add("X-Sumo-Category", SumoCategory)
		req.Header.Add("X-Sumo-Host", SumoHost)
		req.Header.Add("X-Sumo-Name", SumoName)
		resp, err := client.Do(req)
		if err != nil {
			log.Error("something went amiss on submitting to Sumo")
			return
		}
		defer resp.Body.Close()
		log.Debug("Response from Sumo: ", resp)

		// Sleep until the next run
		// Randomize the sleep by specifying the std dev and adding the desired mean... targeting 3%
		milliseconds := IntervalSecs * 1000
		stdDevMilli := IntervalStdDev * 1000.0
		nextInterval := int(r.NormFloat64()*stdDevMilli + float64(milliseconds))
		time.Sleep(time.Duration(nextInterval) * time.Millisecond)
	}

}
<commit_msg>Logging improvements<commit_after>package loggenrunner

import (
	"bytes"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
)

// DataStore holds all the data info for a given simulated log line
type DataStore struct {
	Text string `json:"text"`
}

// RunLogLineParams holds all the data to be passed to RunLogLine
type RunLogLineParams struct {
	HTTPLoc        string
	PostBody       string
	IntervalSecs   int
	IntervalStdDev float64
}

// randomizeString takes a string, looks for the random tokens (int and string), and replaces them
func randomizeString(text string, timeformat string) string {
	// Bail if we can't get any randomizers
	goodstring, err := regexp.MatchString(`\$\[[^\]]+\]`, text)
	if err != nil {
		log.Error("Something broke on parsing the text string with a regular expression")
	}

	//Return original string if no randomizers
	if !goodstring {
		log.Debug("Found no random tokens: ", text)
		return text
	}

	// Find all randomizing tokens
	re := regexp.MustCompile(`\$\[[^\]]+\]`)
	randos := re.FindAllString(text, -1)
	log.Debug("Random tokens: ", randos)

	// Create a list of new strings to be inserted where the tokens were
	var newstrings []string
	replacer := strings.NewReplacer("$[", "", "]", "")

	// Append the properly randomized values to the newstrings slice
	for _, rando := range randos {
		// Take off the leading and trailing formatting
		tempstring := replacer.Replace(rando)
		log.Debug("tempstring: ", tempstring)

		// Split the rnadomizer into individual items
		tempstrings := strings.Split(tempstring, ",")
		log.Debug("tempstrings: ", tempstrings)

		// Numeric ranges will only have two items for an upper and lower bound, timestamps have "time" and "stamp", all the rest are string groups
		var randType string
		num0, err := strconv.Atoi(string(tempstrings[0]))
		num1, err2 := strconv.Atoi(string(tempstrings[1]))
		log.Debug("num0 parsed: ", num0, err)
		log.Debug("num1 parsed: ", num1, err2)
		log.Debug("Length of tempstrings: ", len(tempstrings))
		log.Debug("Numbers?: ", len(tempstrings) == 2 && err == nil && err2 == nil)
		log.Debug("Timestamp?: ", tempstrings[0] == "time" && tempstrings[1] == "stamp")

		switch {
		case len(tempstrings) == 2 && err == nil && err2 == nil:
			randType = "Number"
		case tempstrings[0] == "time" && tempstrings[1] == "stamp":
			randType = "Timestamp"
		default:
			randType = "Category"
		}

		switch randType {
		case "Category":
			log.Debug("Treating as Category")
			newstrings = append(newstrings, tempstrings[rand.Intn(len(tempstrings))])
		case "Number":
			log.Debug("Treating as Number")

			// Get a random number in the range
			diff := num1 - num0
			log.Debug("diff parsed: ", diff)
			tempnum := rand.Intn(diff)
			log.Debug("random number: ", tempnum)
			log.Debug("random number as string: ", strconv.Itoa(tempnum+num0))
			newstrings = append(newstrings, strconv.Itoa(tempnum+num0))
		case "Timestamp":
			t := time.Now()
			log.Debug("Current time: ", t)
			timeformatted := t.Format(timeformat)
			log.Debug("Formatted time: ", timeformatted)

			newstrings = append(newstrings, timeformatted)
		}
	}

	nonRandomStrings := re.Split(text, -1)
	var newLogLine []string

	for i := 0; i < len(nonRandomStrings); i++ {
		newLogLine = append(newLogLine, nonRandomStrings[i])
		if i != len(nonRandomStrings)-1 {
			newLogLine = append(newLogLine, newstrings[i])
		}
	}

	log.Info("Randomization complete. New string: ", strings.Join(newLogLine, ""))

	return strings.Join(newLogLine, "")
}

// RunLogLine makes repeated calls to an endpoint given the configs of the log line
func RunLogLine(HTTPLoc string, PostBody string, IntervalSecs int, IntervalStdDev float64, TimeFormat string, SumoCategory string, SumoHost string, SumoName string) {
	log.Info("Starting log runner for logline: ", PostBody)

	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	client := &http.Client{}

	// Begin loop to post the value until we're done
	for {
		// Randomize the post body if need be
		var stringBody = []byte(randomizeString(PostBody, TimeFormat))

		// Post to Sumo
		log.Info("Sending log to Sumo: ", string(stringBody))
		req, err := http.NewRequest("POST", HTTPLoc, bytes.NewBuffer(stringBody))
		req.Header.Add("X-Sumo-Category", SumoCategory)
		req.Header.Add("X-Sumo-Host", SumoHost)
		req.Header.Add("X-Sumo-Name", SumoName)
		resp, err := client.Do(req)
		if err != nil {
			log.Error("something went amiss on submitting to Sumo")
			return
		}
		defer resp.Body.Close()
		//log.Debug("Response from Sumo: ", resp)

		// Sleep until the next run
		// Randomize the sleep by specifying the std dev and adding the desired mean... targeting 3%
		milliseconds := IntervalSecs * 1000
		stdDevMilli := IntervalStdDev * 1000.0
		nextInterval := int(r.NormFloat64()*stdDevMilli + float64(milliseconds))
		time.Sleep(time.Duration(nextInterval) * time.Millisecond)
	}

}
<|endoftext|>