<commit_before>// Copyright (c) 2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package dcrjson_test

import (
	"bytes"
	"encoding/hex"
	"reflect"
	"testing"

	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/dcrjson"
	"strings"
)

func decodeHash(reversedHash string) chainhash.Hash {
	h, err := chainhash.NewHashFromStr(reversedHash)
	if err != nil {
		panic(err)
	}
	return *h
}

func TestEncodeConcatenatedHashes(t *testing.T) {
	// Input Hash slice. Data taken from Decred's first three mainnet blocks.
	hashSlice := []chainhash.Hash{
		decodeHash("298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980"),
		decodeHash("000000000000437482b6d47f82f374cde539440ddb108b0a76886f0d87d126b9"),
		decodeHash("000000000000c41019872ff7db8fd2e9bfa05f42d3f8fee8e895e8c1e5b8dcba"),
	}
	hashLen := hex.EncodedLen(len(hashSlice[0]))

	// Expected output. The string representations of the underlying byte arrays
	// in the input []chainhash.Hash
	blockHashes := []string{
		"80d9212bf4ceb066ded2866b39d4ed89e0ab60f335c11df8e7bf85d9c35c8e29",
		"b926d1870d6f88760a8b10db0d4439e5cd74f3827fd4b6827443000000000000",
		"badcb8e5c1e895e8e8fef8d3425fa0bfe9d28fdbf72f871910c4000000000000",
	}
	concatenatedHashes := strings.Join(blockHashes, "")

	// Test from 0 to N of the hashes
	for j := 0; j < len(hashSlice)+1; j++ {
		// Expected output string
		concatRef := concatenatedHashes[:j*hashLen]

		// Encode to string
		concatenated := dcrjson.EncodeConcatenatedHashes(hashSlice[:j])
		// Verify output
		if concatenated != concatRef {
			t.Fatalf("EncodeConcatenatedHashes failed (%v!=%v)",
				concatenated, concatRef)
		}
	}
}

func TestDecodeConcatenatedHashes(t *testing.T) {
	// Test data taken from Decred's first three mainnet blocks
	testHashes := []chainhash.Hash{
		decodeHash("298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980"),
		decodeHash("000000000000437482b6d47f82f374cde539440ddb108b0a76886f0d87d126b9"),
		decodeHash("000000000000c41019872ff7db8fd2e9bfa05f42d3f8fee8e895e8c1e5b8dcba"),
	}
	var concatenatedHashBytes []byte
	for _, h := range testHashes {
		concatenatedHashBytes = append(concatenatedHashBytes, h[:]...)
	}
	concatenatedHashes := hex.EncodeToString(concatenatedHashBytes)
	decodedHashes, err := dcrjson.DecodeConcatenatedHashes(concatenatedHashes)
	if err != nil {
		t.Fatalf("Decode failed: %v", err)
	}
	if len(testHashes) != len(decodedHashes) {
		t.Fatalf("Got wrong number of decoded hashes (%v)", len(decodedHashes))
	}
	for i, expected := range testHashes {
		if expected != decodedHashes[i] {
			t.Fatalf("Decoded hash %d `%v` does not match expected `%v`",
				i, decodedHashes[i], expected)
		}
	}
}

func TestEncodeConcatenatedVoteBits(t *testing.T) {
	testVbs := []stake.VoteBits{
		{Bits: 0, ExtendedBits: []byte{}},
		{Bits: 0, ExtendedBits: []byte{0x00}},
		{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
		{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
	}
	encodedResults, err := dcrjson.EncodeConcatenatedVoteBits(testVbs)
	if err != nil {
		t.Fatalf("Encode failed: %v", err)
	}

	expectedEncoded := []byte{
		0x02, 0x00, 0x00, 0x03,
		0x00, 0x00, 0x00, 0x06,
		0x23, 0x12, 0x01, 0x02,
		0x03, 0x04, 0x07, 0xaa,
		0xaa, 0x01, 0x02, 0x03,
		0x04, 0x05,
	}

	encodedResultsStr, _ := hex.DecodeString(encodedResults)
	if !bytes.Equal(expectedEncoded, encodedResultsStr) {
		t.Fatalf("Encoded votebits `%x` does not match expected `%x`",
			encodedResults, expectedEncoded)
	}

	// Test too long voteBits extended.
	testVbs = []stake.VoteBits{
		{Bits: 0, ExtendedBits: bytes.Repeat([]byte{0x00}, 74)},
	}
	_, err = dcrjson.EncodeConcatenatedVoteBits(testVbs)
	if err == nil {
		t.Fatalf("expected too long error")
	}
}

func TestDecodeConcatenatedVoteBits(t *testing.T) {
	encodedBytes := []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05,
	}
	encodedBytesStr := hex.EncodeToString(encodedBytes)

	expectedVbs := []stake.VoteBits{
		{Bits: 0, ExtendedBits: []byte{0x00}},
		{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
		{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
	}

	decodedSlice, err :=
		dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err != nil {
		t.Fatalf("unexpected error decoding votebits: %v", err.Error())
	}

	if !reflect.DeepEqual(expectedVbs, decodedSlice) {
		t.Fatalf("Decoded votebits `%v` does not match expected `%v`",
			decodedSlice, expectedVbs)
	}

	// Test short read.
	encodedBytes = []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected short read error")
	}

	// Test too long read.
	encodedBytes = []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x06,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected corruption error")
	}

	// Test invalid length.
	encodedBytes = []byte{
		0x01, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x06,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected corruption error")
	}
}
<commit_msg>dcrjson: Add negative DecodeConcatenatedHashes tests (#423)<commit_after>// Copyright (c) 2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package dcrjson_test

import (
	"bytes"
	"encoding/hex"
	"reflect"
	"testing"

	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/dcrjson"
	"strings"
)

func decodeHash(reversedHash string) chainhash.Hash {
	h, err := chainhash.NewHashFromStr(reversedHash)
	if err != nil {
		panic(err)
	}
	return *h
}

func TestEncodeConcatenatedHashes(t *testing.T) {
	// Input Hash slice. Data taken from Decred's first three mainnet blocks.
	hashSlice := []chainhash.Hash{
		decodeHash("298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980"),
		decodeHash("000000000000437482b6d47f82f374cde539440ddb108b0a76886f0d87d126b9"),
		decodeHash("000000000000c41019872ff7db8fd2e9bfa05f42d3f8fee8e895e8c1e5b8dcba"),
	}
	hashLen := hex.EncodedLen(len(hashSlice[0]))

	// Expected output. The string representations of the underlying byte arrays
	// in the input []chainhash.Hash
	blockHashes := []string{
		"80d9212bf4ceb066ded2866b39d4ed89e0ab60f335c11df8e7bf85d9c35c8e29",
		"b926d1870d6f88760a8b10db0d4439e5cd74f3827fd4b6827443000000000000",
		"badcb8e5c1e895e8e8fef8d3425fa0bfe9d28fdbf72f871910c4000000000000",
	}
	concatenatedHashes := strings.Join(blockHashes, "")

	// Test from 0 to N of the hashes
	for j := 0; j < len(hashSlice)+1; j++ {
		// Expected output string
		concatRef := concatenatedHashes[:j*hashLen]

		// Encode to string
		concatenated := dcrjson.EncodeConcatenatedHashes(hashSlice[:j])
		// Verify output
		if concatenated != concatRef {
			t.Fatalf("EncodeConcatenatedHashes failed (%v!=%v)",
				concatenated, concatRef)
		}
	}
}

func TestDecodeConcatenatedHashes(t *testing.T) {
	// Test data taken from Decred's first three mainnet blocks
	testHashes := []chainhash.Hash{
		decodeHash("298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980"),
		decodeHash("000000000000437482b6d47f82f374cde539440ddb108b0a76886f0d87d126b9"),
		decodeHash("000000000000c41019872ff7db8fd2e9bfa05f42d3f8fee8e895e8c1e5b8dcba"),
	}
	var concatenatedHashBytes []byte
	for _, h := range testHashes {
		concatenatedHashBytes = append(concatenatedHashBytes, h[:]...)
	}
	concatenatedHashes := hex.EncodeToString(concatenatedHashBytes)
	decodedHashes, err := dcrjson.DecodeConcatenatedHashes(concatenatedHashes)
	if err != nil {
		t.Fatalf("Decode failed: %v", err)
	}
	if len(testHashes) != len(decodedHashes) {
		t.Fatalf("Got wrong number of decoded hashes (%v)", len(decodedHashes))
	}
	for i, expected := range testHashes {
		if expected != decodedHashes[i] {
			t.Fatalf("Decoded hash %d `%v` does not match expected `%v`",
				i, decodedHashes[i], expected)
		}
	}
}

func TestEncodeConcatenatedVoteBits(t *testing.T) {
	testVbs := []stake.VoteBits{
		{Bits: 0, ExtendedBits: []byte{}},
		{Bits: 0, ExtendedBits: []byte{0x00}},
		{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
		{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
	}
	encodedResults, err := dcrjson.EncodeConcatenatedVoteBits(testVbs)
	if err != nil {
		t.Fatalf("Encode failed: %v", err)
	}

	expectedEncoded := []byte{
		0x02, 0x00, 0x00, 0x03,
		0x00, 0x00, 0x00, 0x06,
		0x23, 0x12, 0x01, 0x02,
		0x03, 0x04, 0x07, 0xaa,
		0xaa, 0x01, 0x02, 0x03,
		0x04, 0x05,
	}

	encodedResultsStr, _ := hex.DecodeString(encodedResults)
	if !bytes.Equal(expectedEncoded, encodedResultsStr) {
		t.Fatalf("Encoded votebits `%x` does not match expected `%x`",
			encodedResults, expectedEncoded)
	}

	// Test too long voteBits extended.
	testVbs = []stake.VoteBits{
		{Bits: 0, ExtendedBits: bytes.Repeat([]byte{0x00}, 74)},
	}
	_, err = dcrjson.EncodeConcatenatedVoteBits(testVbs)
	if err == nil {
		t.Fatalf("expected too long error")
	}
}

func TestDecodeConcatenatedVoteBits(t *testing.T) {
	encodedBytes := []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05,
	}
	encodedBytesStr := hex.EncodeToString(encodedBytes)

	expectedVbs := []stake.VoteBits{
		{Bits: 0, ExtendedBits: []byte{0x00}},
		{Bits: 0x1223, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04}},
		{Bits: 0xaaaa, ExtendedBits: []byte{0x01, 0x02, 0x03, 0x04, 0x05}},
	}

	decodedSlice, err :=
		dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err != nil {
		t.Fatalf("unexpected error decoding votebits: %v", err.Error())
	}

	if !reflect.DeepEqual(expectedVbs, decodedSlice) {
		t.Fatalf("Decoded votebits `%v` does not match expected `%v`",
			decodedSlice, expectedVbs)
	}

	// Test short read.
	encodedBytes = []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected short read error")
	}

	// Test too long read.
	encodedBytes = []byte{
		0x03, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x06,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected corruption error")
	}

	// Test invalid length.
	encodedBytes = []byte{
		0x01, 0x00, 0x00, 0x00,
		0x06, 0x23, 0x12, 0x01,
		0x02, 0x03, 0x04, 0x07,
		0xaa, 0xaa, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x06,
	}
	encodedBytesStr = hex.EncodeToString(encodedBytes)

	decodedSlice, err = dcrjson.DecodeConcatenatedVoteBits(encodedBytesStr)
	if err == nil {
		t.Fatalf("expected corruption error")
	}
}

func TestInvalidDecodeConcatenatedHashes(t *testing.T) {
	testStrings := []struct {
		str string
		err dcrjson.RPCError
	}{
		{
			// length of 1
			"0",
			dcrjson.RPCError{
				Code: dcrjson.ErrRPCInvalidParameter,
			},
		}, {
			// not hex
			"ffffgfffffffffffffffffffffffffff" +
				"ffffffffffffffffffffffffffffffff",
			dcrjson.RPCError{
				Code: dcrjson.ErrRPCDecodeHexString,
			},
		}, {
			// invalid length
			"298e5cc3d985bfe811edd4396b86d2de66b0cef4" +
				"2b21d980096b86d2de96b86d2",
			dcrjson.RPCError{
				Code: dcrjson.ErrRPCInvalidParameter,
			},
		},
	}
	for _, str := range testStrings {
		_, err := dcrjson.DecodeConcatenatedHashes(str.str)
		if err == nil {
			t.Fatalf("DecodeConcatenatedHashes passed on '%s' "+
				"when it should have failed", str)
		}
		rpcError, ok := err.(*dcrjson.RPCError)
		if !ok {
			t.Fatalf("DecodeConcatenatedHashes error is not "+
				"expected type *dcrjson.RPCError: %T", err)
		}
		if rpcError.Code != str.err.Code {
			t.Fatalf("DecodeConcatenatedHashes returned "+
				"unexpected error code: want %v, got %v",
				str.err.Code, rpcError.Code)
		}
	}
}
<|endoftext|>


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

package raw

import (
	"flag"
	"fmt"

	"github.com/golang/glog"
	"github.com/google/cadvisor/container"
	"github.com/google/cadvisor/container/libcontainer"
	"github.com/google/cadvisor/fs"
	info "github.com/google/cadvisor/info/v1"
)

var dockerOnly = flag.Bool("docker_only", false, "Only report docker containers in addition to root stats")

type rawFactory struct {
	// Factory for machine information.
	machineInfoFactory info.MachineInfoFactory

	// Information about the cgroup subsystems.
	cgroupSubsystems *libcontainer.CgroupSubsystems

	// Information about mounted filesystems.
	fsInfo fs.FsInfo

	// Watcher for inotify events.
	watcher *InotifyWatcher
}

func (self *rawFactory) String() string {
	return "raw"
}

func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHandler, error) {
	return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher)
}

// The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored.
func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
	accept := name == "/" || !*dockerOnly
	return true, accept, nil
}

func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
	cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
	if err != nil {
		return fmt.Errorf("failed to get cgroup subsystems: %v", err)
	}
	if len(cgroupSubsystems.Mounts) == 0 {
		return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
	}

	watcher, err := NewInotifyWatcher()
	if err != nil {
		return err
	}

	glog.Infof("Registering Raw factory")
	factory := &rawFactory{
		machineInfoFactory: machineInfoFactory,
		fsInfo:             fsInfo,
		cgroupSubsystems:   &cgroupSubsystems,
		watcher:            watcher,
	}
	container.RegisterContainerHandlerFactory(factory)
	return nil
}
<commit_msg>Add inotify watches as debug information<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package raw

import (
	"flag"
	"fmt"

	"github.com/golang/glog"
	"github.com/google/cadvisor/container"
	"github.com/google/cadvisor/container/libcontainer"
	"github.com/google/cadvisor/fs"
	info "github.com/google/cadvisor/info/v1"
)

var dockerOnly = flag.Bool("docker_only", false, "Only report docker containers in addition to root stats")

type rawFactory struct {
	// Factory for machine information.
	machineInfoFactory info.MachineInfoFactory

	// Information about the cgroup subsystems.
	cgroupSubsystems *libcontainer.CgroupSubsystems

	// Information about mounted filesystems.
	fsInfo fs.FsInfo

	// Watcher for inotify events.
	watcher *InotifyWatcher
}

func (self *rawFactory) String() string {
	return "raw"
}

func (self *rawFactory) NewContainerHandler(name string) (container.ContainerHandler, error) {
	return newRawContainerHandler(name, self.cgroupSubsystems, self.machineInfoFactory, self.fsInfo, self.watcher)
}

// The raw factory can handle any container. If --docker_only is set to false, non-docker containers are ignored.
func (self *rawFactory) CanHandleAndAccept(name string) (bool, bool, error) {
	accept := name == "/" || !*dockerOnly
	return true, accept, nil
}

func (self *rawFactory) DebugInfo() map[string][]string {
	out := make(map[string][]string)

	// Get information about inotify watches.
	watches := self.watcher.GetWatches()
	lines := make([]string, 0, len(watches))
	for containerName, cgroupWatches := range watches {
		lines = append(lines, fmt.Sprintf("%s:", containerName))
		for _, cg := range cgroupWatches {
			lines = append(lines, fmt.Sprintf("\t%s", cg))
		}
	}
	out["Inotify watches"] = lines

	return out
}

func Register(machineInfoFactory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
	cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
	if err != nil {
		return fmt.Errorf("failed to get cgroup subsystems: %v", err)
	}
	if len(cgroupSubsystems.Mounts) == 0 {
		return fmt.Errorf("failed to find supported cgroup mounts for the raw factory")
	}

	watcher, err := NewInotifyWatcher()
	if err != nil {
		return err
	}

	glog.Infof("Registering Raw factory")
	factory := &rawFactory{
		machineInfoFactory: machineInfoFactory,
		fsInfo:             fsInfo,
		cgroupSubsystems:   &cgroupSubsystems,
		watcher:            watcher,
	}
	container.RegisterContainerHandlerFactory(factory)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/pkg/apis/core/v1/helper"
)

const (
	provisionerName    = "ceph.com/cephfs"
	provisionCmd       = "/usr/local/bin/cephfs_provisioner"
	provisionerIDAnn   = "cephFSProvisionerIdentity"
	cephShareAnn       = "cephShare"
	provisionerNameKey = "PROVISIONER_NAME"
	secretNamespaceKey = "PROVISIONER_SECRET_NAMESPACE"
)

type provisionOutput struct {
	Path   string `json:"path"`
	User   string `json:"user"`
	Secret string `json:"auth"`
}

type cephFSProvisioner struct {
	// Kubernetes Client. Use to retrieve Ceph admin secret
	client kubernetes.Interface
	// Identity of this cephFSProvisioner, generated. Used to identify "this"
	// provisioner's PVs.
	identity string
	// Namespace secrets will be created in. If empty, secrets will be created in each PVC's namespace.
	secretNamespace string
}

func newCephFSProvisioner(client kubernetes.Interface, id string, secretNamespace string) controller.Provisioner {
	return &cephFSProvisioner{
		client:          client,
		identity:        id,
		secretNamespace: secretNamespace,
	}
}

var _ controller.Provisioner = &cephFSProvisioner{}

func generateSecretName(user string) string {
	return "ceph-" + user + "-secret"
}

func getClaimRefNamespace(pv *v1.PersistentVolume) string {
	if pv.Spec.ClaimRef != nil {
		return pv.Spec.ClaimRef.Namespace
	}
	return ""
}

// getSecretFromCephFSPersistentVolume gets secret reference from CephFS PersistentVolume.
// It fallbacks to use ClaimRef.Namespace if SecretRef.Namespace is
// empty. See https://github.com/kubernetes/kubernetes/pull/49502.
func getSecretFromCephFSPersistentVolume(pv *v1.PersistentVolume) (*v1.SecretReference, error) {
	source := &pv.Spec.PersistentVolumeSource
	if source.CephFS == nil {
		return nil, errors.New("pv.Spec.PersistentVolumeSource.CephFS is nil")
	}
	if source.CephFS.SecretRef == nil {
		return nil, errors.New("pv.Spec.PersistentVolumeSource.CephFS.SecretRef is nil")
	}
	if len(source.CephFS.SecretRef.Namespace) > 0 {
		return source.CephFS.SecretRef, nil
	}
	ns := getClaimRefNamespace(pv)
	if len(ns) <= 0 {
		return nil, errors.New("both pv.Spec.SecretRef.Namespace and pv.Spec.ClaimRef.Namespace are empty")
	}
	return &v1.SecretReference{
		Name:      source.CephFS.SecretRef.Name,
		Namespace: ns,
	}, nil
}

// Provision creates a storage asset and returns a PV object representing it.
func (p *cephFSProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("claim Selector is not supported")
	}
	cluster, adminID, adminSecret, mon, err := p.parseParameters(options.Parameters)
	if err != nil {
		return nil, err
	}
	// create random share name
	share := fmt.Sprintf("kubernetes-dynamic-pvc-%s", uuid.NewUUID())
	// create random user id
	user := fmt.Sprintf("kubernetes-dynamic-user-%s", uuid.NewUUID())
	// provision share
	// create cmd
	cmd := exec.Command(provisionCmd, "-n", share, "-u", user)
	// set env
	cmd.Env = []string{
		"CEPH_CLUSTER_NAME=" + cluster,
		"CEPH_MON=" + strings.Join(mon[:], ","),
		"CEPH_AUTH_ID=" + adminID,
		"CEPH_AUTH_KEY=" + adminSecret}

	output, cmdErr := cmd.CombinedOutput()
	if cmdErr != nil {
		glog.Errorf("failed to provision share %q for %q, err: %v, output: %v", share, user, cmdErr, string(output))
		return nil, cmdErr
	}
	// validate output
	res := &provisionOutput{}
	json.Unmarshal([]byte(output), &res)
	if res.User == "" || res.Secret == "" || res.Path == "" {
		return nil, fmt.Errorf("invalid provisioner output")
	}
	nameSpace := p.secretNamespace
	if nameSpace == "" {
		// if empty, create secret in PVC's namespace
		nameSpace = options.PVC.Namespace
	}
	secretName := generateSecretName(user)
	secret := &v1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: nameSpace,
			Name:      secretName,
		},
		Data: map[string][]byte{
			"key": []byte(res.Secret),
		},
		Type: "Opaque",
	}

	_, err = p.client.CoreV1().Secrets(nameSpace).Create(secret)
	if err != nil {
		glog.Errorf("Cephfs Provisioner: create volume failed, err: %v", err)
		return nil, fmt.Errorf("failed to create secret")
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: options.PVName,
			Annotations: map[string]string{
				provisionerIDAnn: p.identity,
				cephShareAnn:     share,
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{ //FIXME: kernel cephfs doesn't enforce quota, capacity is not meaningless here.
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				CephFS: &v1.CephFSPersistentVolumeSource{
					Monitors: mon,
					Path:     res.Path[strings.Index(res.Path, "/"):],
					SecretRef: &v1.SecretReference{
						Name: secretName,
						// TODO https://github.com/kubernetes-incubator/external-storage/issues/309
						Namespace: nameSpace,
					},
					User: user,
				},
			},
		},
	}

	glog.Infof("successfully created CephFS share %+v", pv.Spec.PersistentVolumeSource.CephFS)

	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *cephFSProvisioner) Delete(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations[provisionerIDAnn]
	if !ok {
		return errors.New("identity annotation not found on PV")
	}
	if ann != p.identity {
		return &controller.IgnoredError{Reason: "identity annotation on PV does not match ours"}
	}
	share, ok := volume.Annotations[cephShareAnn]
	if !ok {
		return errors.New("ceph share annotation not found on PV")
	}
	// delete CephFS
	// TODO when beta is removed, have to check kube version and pick v1/beta
	// accordingly: maybe the controller lib should offer a function for that
	class, err := p.client.StorageV1beta1().StorageClasses().Get(helper.GetPersistentVolumeClass(volume), metav1.GetOptions{})
	if err != nil {
		return err
	}
	cluster, adminID, adminSecret, mon, err := p.parseParameters(class.Parameters)
	if err != nil {
		return err
	}
	user := volume.Spec.PersistentVolumeSource.CephFS.User
	// create cmd
	cmd := exec.Command(provisionCmd, "-r", "-n", share, "-u", user)
	// set env
	cmd.Env = []string{
		"CEPH_CLUSTER_NAME=" + cluster,
		"CEPH_MON=" + strings.Join(mon[:], ","),
		"CEPH_AUTH_ID=" + adminID,
		"CEPH_AUTH_KEY=" + adminSecret}

	output, cmdErr := cmd.CombinedOutput()
	if cmdErr != nil {
		glog.Errorf("failed to delete share %q for %q, err: %v, output: %v", share, user, cmdErr, string(output))
		return cmdErr
	}

	// Remove dynamic user secret
	secretRef, err := getSecretFromCephFSPersistentVolume(volume)
	if err != nil {
		glog.Errorf("failed to get secret references, err: %v", err)
		return err
	}
	err = p.client.CoreV1().Secrets(secretRef.Namespace).Delete(secretRef.Name, &metav1.DeleteOptions{})
	if err != nil {
		glog.Errorf("Cephfs Provisioner: delete secret failed, err: %v", err)
		return fmt.Errorf("failed to delete secret")
	}

	return nil
}

func (p *cephFSProvisioner) parseParameters(parameters map[string]string) (string, string, string, []string, error) {
	var (
		err                                                                  error
		mon                                                                  []string
		cluster, adminID, adminSecretName, adminSecretNamespace, adminSecret string
	)

	adminSecretNamespace = "default"
	adminID = "admin"
	cluster = "ceph"

	for k, v := range parameters {
		switch strings.ToLower(k) {
		case "cluster":
			cluster = v
		case "monitors":
			arr := strings.Split(v, ",")
			for _, m := range arr {
				mon = append(mon, m)
			}
		case "adminid":
			adminID = v
		case "adminsecretname":
			adminSecretName = v
		case "adminsecretnamespace":
			adminSecretNamespace = v
		default:
			return "", "", "", nil, fmt.Errorf("invalid option %q", k)
		}
	}
	// sanity check
	if adminSecretName == "" {
		return "", "", "", nil, fmt.Errorf("missing Ceph admin secret name")
	}
	if adminSecret, err = p.parsePVSecret(adminSecretNamespace, adminSecretName); err != nil {
		return "", "", "", nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
	}
	if len(mon) < 1 {
		return "", "", "", nil, fmt.Errorf("missing Ceph monitors")
	}
	return cluster, adminID, adminSecret, mon, nil
}

func (p *cephFSProvisioner) parsePVSecret(namespace, secretName string) (string, error) {
	if p.client == nil {
		return "", fmt.Errorf("Cannot get kube client")
	}
	secrets, err := p.client.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
	if err != nil {
		return "", err
	}
	for _, data := range secrets.Data {
		return string(data), nil
	}

	// If not found, the last secret in the map wins as done before
	return "", fmt.Errorf("no secret found")
}

var (
	master          = flag.String("master", "", "Master URL")
	kubeconfig      = flag.String("kubeconfig", "", "Absolute path to the kubeconfig")
	id              = flag.String("id", "", "Unique provisioner identity")
	secretNamespace = flag.String("secret-namespace", "", "Namespace secrets will be created in (default: '', created in each PVC's namespace)")
)

func main() {
	flag.Parse()
	flag.Set("logtostderr", "true")

	var config *rest.Config
	var err error
	if *master != "" || *kubeconfig != "" {
		config, err = clientcmd.BuildConfigFromFlags(*master, *kubeconfig)
	} else {
		config, err = rest.InClusterConfig()
	}
	if err != nil {
		glog.Fatalf("Failed to create config: %v", err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Failed to create client: %v", err)
	}

	prName := provisionerName
	prNameFromEnv := os.Getenv(provisionerNameKey)
	if prNameFromEnv != "" {
		prName = prNameFromEnv
	}

	// By default, we use provisioner name as provisioner identity.
	// User may specify their own identity with `-id` flag to distinguish each
	// others, if they deploy more than one CephFS provisioners under same provisioner name.
	prID := prName
	if *id != "" {
		prID = *id
	}

	secretNamespaceFromEnv := os.Getenv(secretNamespaceKey)
	if secretNamespaceFromEnv != "" {
		*secretNamespace = secretNamespaceFromEnv
	}

	// The controller needs to know what the server version is because out-of-tree
	// provisioners aren't officially supported until 1.5
	serverVersion, err := clientset.Discovery().ServerVersion()
	if err != nil {
		glog.Fatalf("Error getting server version: %v", err)
	}

	// Create the provisioner: it implements the Provisioner interface expected by
	// the controller
	glog.Infof("Creating CephFS provisioner %s with identity: %s, secret namespace: %s", prName, prID, *secretNamespace)
	cephFSProvisioner := newCephFSProvisioner(clientset, prID, *secretNamespace)

	// Start the provision controller which will dynamically provision cephFS
	// PVs
	pc := controller.NewProvisionController(
		clientset,
		prName,
		cephFSProvisioner,
		serverVersion.GitVersion,
	)

	pc.Run(wait.NeverStop)
}
<commit_msg>ceph/cephfs/cephfs-provisioner.go:  Added all the option handling for claimRoot in StorageClass parameters<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/golang/glog"
	"github.com/kubernetes-incubator/external-storage/lib/controller"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubernetes/pkg/apis/core/v1/helper"
)

const (
	provisionerName    = "ceph.com/cephfs"
	provisionCmd       = "/usr/local/bin/cephfs_provisioner"
	provisionerIDAnn   = "cephFSProvisionerIdentity"
	cephShareAnn       = "cephShare"
	provisionerNameKey = "PROVISIONER_NAME"
	secretNamespaceKey = "PROVISIONER_SECRET_NAMESPACE"
)

type provisionOutput struct {
	Path   string `json:"path"`
	User   string `json:"user"`
	Secret string `json:"auth"`
}

type cephFSProvisioner struct {
	// Kubernetes Client. Use to retrieve Ceph admin secret
	client kubernetes.Interface
	// Identity of this cephFSProvisioner, generated. Used to identify "this"
	// provisioner's PVs.
	identity string
	// Namespace secrets will be created in. If empty, secrets will be created in each PVC's namespace.
	secretNamespace string
}

func newCephFSProvisioner(client kubernetes.Interface, id string, secretNamespace string) controller.Provisioner {
	return &cephFSProvisioner{
		client:          client,
		identity:        id,
		secretNamespace: secretNamespace,
	}
}

var _ controller.Provisioner = &cephFSProvisioner{}

func generateSecretName(user string) string {
	return "ceph-" + user + "-secret"
}

func getClaimRefNamespace(pv *v1.PersistentVolume) string {
	if pv.Spec.ClaimRef != nil {
		return pv.Spec.ClaimRef.Namespace
	}
	return ""
}

// getSecretFromCephFSPersistentVolume gets secret reference from CephFS PersistentVolume.
// It fallbacks to use ClaimRef.Namespace if SecretRef.Namespace is
// empty. See https://github.com/kubernetes/kubernetes/pull/49502.
func getSecretFromCephFSPersistentVolume(pv *v1.PersistentVolume) (*v1.SecretReference, error) {
	source := &pv.Spec.PersistentVolumeSource
	if source.CephFS == nil {
		return nil, errors.New("pv.Spec.PersistentVolumeSource.CephFS is nil")
	}
	if source.CephFS.SecretRef == nil {
		return nil, errors.New("pv.Spec.PersistentVolumeSource.CephFS.SecretRef is nil")
	}
	if len(source.CephFS.SecretRef.Namespace) > 0 {
		return source.CephFS.SecretRef, nil
	}
	ns := getClaimRefNamespace(pv)
	if len(ns) <= 0 {
		return nil, errors.New("both pv.Spec.SecretRef.Namespace and pv.Spec.ClaimRef.Namespace are empty")
	}
	return &v1.SecretReference{
		Name:      source.CephFS.SecretRef.Name,
		Namespace: ns,
	}, nil
}

// Provision creates a storage asset and returns a PV object representing it.
func (p *cephFSProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
	if options.PVC.Spec.Selector != nil {
		return nil, fmt.Errorf("claim Selector is not supported")
	}
	cluster, adminID, adminSecret, pvc_root, mon, err := p.parseParameters(options.Parameters)
	if err != nil {
		return nil, err
	}
	// create random share name
	share := fmt.Sprintf("kubernetes-dynamic-pvc-%s", uuid.NewUUID())
	// create random user id
	user := fmt.Sprintf("kubernetes-dynamic-user-%s", uuid.NewUUID())
	// provision share
	// create cmd
	cmd := exec.Command(provisionCmd, "-n", share, "-u", user)
	// set env
	cmd.Env = []string{
		"CEPH_CLUSTER_NAME=" + cluster,
		"CEPH_MON=" + strings.Join(mon[:], ","),
		"CEPH_AUTH_ID=" + adminID,
		"CEPH_AUTH_KEY=" + adminSecret,
		"CEPH_VOLUME_ROOT=" + pvc_root}

	output, cmdErr := cmd.CombinedOutput()
	if cmdErr != nil {
		glog.Errorf("failed to provision share %q for %q, err: %v, output: %v", share, user, cmdErr, string(output))
		return nil, cmdErr
	}
	// validate output
	res := &provisionOutput{}
	json.Unmarshal([]byte(output), &res)
	if res.User == "" || res.Secret == "" || res.Path == "" {
		return nil, fmt.Errorf("invalid provisioner output")
	}
	nameSpace := p.secretNamespace
	if nameSpace == "" {
		// if empty, create secret in PVC's namespace
		nameSpace = options.PVC.Namespace
	}
	secretName := generateSecretName(user)
	secret := &v1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: nameSpace,
			Name:      secretName,
		},
		Data: map[string][]byte{
			"key": []byte(res.Secret),
		},
		Type: "Opaque",
	}

	_, err = p.client.CoreV1().Secrets(nameSpace).Create(secret)
	if err != nil {
		glog.Errorf("Cephfs Provisioner: create volume failed, err: %v", err)
		return nil, fmt.Errorf("failed to create secret")
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name: options.PVName,
			Annotations: map[string]string{
				provisionerIDAnn: p.identity,
				cephShareAnn:     share,
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy,
			AccessModes:                   options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{ //FIXME: kernel cephfs doesn't enforce quota, capacity is not meaningless here.
				v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				CephFS: &v1.CephFSPersistentVolumeSource{
					Monitors: mon,
					Path:     res.Path[strings.Index(res.Path, "/"):],
					SecretRef: &v1.SecretReference{
						Name: secretName,
						// TODO https://github.com/kubernetes-incubator/external-storage/issues/309
						Namespace: nameSpace,
					},
					User: user,
				},
			},
		},
	}

	glog.Infof("successfully created CephFS share %+v", pv.Spec.PersistentVolumeSource.CephFS)

	return pv, nil
}

// Delete removes the storage asset that was created by Provision represented
// by the given PV.
func (p *cephFSProvisioner) Delete(volume *v1.PersistentVolume) error {
	ann, ok := volume.Annotations[provisionerIDAnn]
	if !ok {
		return errors.New("identity annotation not found on PV")
	}
	if ann != p.identity {
		return &controller.IgnoredError{Reason: "identity annotation on PV does not match ours"}
	}
	share, ok := volume.Annotations[cephShareAnn]
	if !ok {
		return errors.New("ceph share annotation not found on PV")
	}
	// delete CephFS
	// TODO when beta is removed, have to check kube version and pick v1/beta
	// accordingly: maybe the controller lib should offer a function for that
	class, err := p.client.StorageV1beta1().StorageClasses().Get(helper.GetPersistentVolumeClass(volume), metav1.GetOptions{})
	if err != nil {
		return err
	}
	cluster, adminID, adminSecret, pvc_root, mon, err := p.parseParameters(class.Parameters)
	if err != nil {
		return err
	}
	user := volume.Spec.PersistentVolumeSource.CephFS.User
	// create cmd
	cmd := exec.Command(provisionCmd, "-r", "-n", share, "-u", user)
	// set env
	cmd.Env = []string{
		"CEPH_CLUSTER_NAME=" + cluster,
		"CEPH_MON=" + strings.Join(mon[:], ","),
		"CEPH_AUTH_ID=" + adminID,
		"CEPH_AUTH_KEY=" + adminSecret,
		"CEPH_VOLUME_ROOT=" + pvc_root}

	output, cmdErr := cmd.CombinedOutput()
	if cmdErr != nil {
		glog.Errorf("failed to delete share %q for %q, err: %v, output: %v", share, user, cmdErr, string(output))
		return cmdErr
	}

	// Remove dynamic user secret
	secretRef, err := getSecretFromCephFSPersistentVolume(volume)
	if err != nil {
		glog.Errorf("failed to get secret references, err: %v", err)
		return err
	}
	err = p.client.CoreV1().Secrets(secretRef.Namespace).Delete(secretRef.Name, &metav1.DeleteOptions{})
	if err != nil {
		glog.Errorf("Cephfs Provisioner: delete secret failed, err: %v", err)
		return fmt.Errorf("failed to delete secret")
	}

	return nil
}

func (p *cephFSProvisioner) parseParameters(parameters map[string]string) (string, string, string, string, []string, error) {
	var (
		err                                                                            error
		mon                                                                            []string
		cluster, adminID, adminSecretName, adminSecretNamespace, adminSecret, pvc_root string
	)

	adminSecretNamespace = "default"
	adminID = "admin"
	cluster = "ceph"
	pvc_root = "/volumes/kubernetes"

	for k, v := range parameters {
		switch strings.ToLower(k) {
		case "cluster":
			cluster = v
		case "monitors":
			arr := strings.Split(v, ",")
			for _, m := range arr {
				mon = append(mon, m)
			}
		case "adminid":
			adminID = v
		case "adminsecretname":
			adminSecretName = v
		case "adminsecretnamespace":
			adminSecretNamespace = v
		case "claimroot":
			pvc_root = v
		default:
			return "", "", "", "", nil, fmt.Errorf("invalid option %q", k)
		}
	}
	// sanity check
	if adminSecretName == "" {
		return "", "", "", "", nil, fmt.Errorf("missing Ceph admin secret name")
	}
	if adminSecret, err = p.parsePVSecret(adminSecretNamespace, adminSecretName); err != nil {
		return "", "", "", "", nil, fmt.Errorf("failed to get admin secret from [%q/%q]: %v", adminSecretNamespace, adminSecretName, err)
	}
	if len(mon) < 1 {
		return "", "", "", "", nil, fmt.Errorf("missing Ceph monitors")
	}
	return cluster, adminID, adminSecret, pvc_root, mon, nil
}

func (p *cephFSProvisioner) parsePVSecret(namespace, secretName string) (string, error) {
	if p.client == nil {
		return "", fmt.Errorf("Cannot get kube client")
	}
	secrets, err := p.client.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
	if err != nil {
		return "", err
	}
	for _, data := range secrets.Data {
		return string(data), nil
	}

	// If not found, the last secret in the map wins as done before
	return "", fmt.Errorf("no secret found")
}

var (
	master          = flag.String("master", "", "Master URL")
	kubeconfig      = flag.String("kubeconfig", "", "Absolute path to the kubeconfig")
	id              = flag.String("id", "", "Unique provisioner identity")
	secretNamespace = flag.String("secret-namespace", "", "Namespace secrets will be created in (default: '', created in each PVC's namespace)")
)

func main() {
	flag.Parse()
	flag.Set("logtostderr", "true")

	var config *rest.Config
	var err error
	if *master != "" || *kubeconfig != "" {
		config, err = clientcmd.BuildConfigFromFlags(*master, *kubeconfig)
	} else {
		config, err = rest.InClusterConfig()
	}
	if err != nil {
		glog.Fatalf("Failed to create config: %v", err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Fatalf("Failed to create client: %v", err)
	}

	prName := provisionerName
	prNameFromEnv := os.Getenv(provisionerNameKey)
	if prNameFromEnv != "" {
		prName = prNameFromEnv
	}

	// By default, we use provisioner name as provisioner identity.
	// User may specify their own identity with `-id` flag to distinguish each
	// others, if they deploy more than one CephFS provisioners under same provisioner name.
	prID := prName
	if *id != "" {
		prID = *id
	}

	secretNamespaceFromEnv := os.Getenv(secretNamespaceKey)
	if secretNamespaceFromEnv != "" {
		*secretNamespace = secretNamespaceFromEnv
	}

	// The controller needs to know what the server version is because out-of-tree
	// provisioners aren't officially supported until 1.5
	serverVersion, err := clientset.Discovery().ServerVersion()
	if err != nil {
		glog.Fatalf("Error getting server version: %v", err)
	}

	// Create the provisioner: it implements the Provisioner interface expected by
	// the controller
	glog.Infof("Creating CephFS provisioner %s with identity: %s, secret namespace: %s", prName, prID, *secretNamespace)
	cephFSProvisioner := newCephFSProvisioner(clientset, prID, *secretNamespace)

	// Start the provision controller which will dynamically provision cephFS
	// PVs
	pc := controller.NewProvisionController(
		clientset,
		prName,
		cephFSProvisioner,
		serverVersion.GitVersion,
	)

	pc.Run(wait.NeverStop)
}
<|endoftext|>


<commit_before>package delmo

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mitchellh/cli"
)

type TestCommand struct {
	Ui cli.Ui
}

func (t *TestCommand) Help() string {
	helpText := `
Usage: delmo test [options]

  Run a test :-)
`
	return strings.TrimSpace(helpText)
}

func (t *TestCommand) Run(args []string) int {
	flags := flag.FlagSet{
		Usage: func() { t.Help() },
	}

	var path string
	flags.StringVar(&path, "f", "delmo.yml", "")
	if err := flags.Parse(args); err != nil {
		t.Ui.Error(fmt.Sprintf("Error parsing arguments\n%s", err))
		return 2
	}

	config, err := LoadConfig(path)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Error reading configuration\n%s", err))
		return 2
	}

	hostDir, err := t.prepareDockerHost(path, config.System)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Cloud not setup docker-machine\n%s", err))
		return 2
	}

	context := GlobalContext{
		DockerHostSyncDir: hostDir,
	}
	suite, err := NewSuite(config, context)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Could not initialize suite %s"))
		return 2
	}
	result := suite.Run(t.Ui)
	return result

}

func (t *TestCommand) Synopsis() string {
	return "Run some tests"
}

func (t *TestCommand) prepareDockerHost(path string, system SystemConfig) (string, error) {
	rawCmd, err := exec.LookPath("docker-machine")
	if err != nil {
		return "", err
	}
	hostDir := fmt.Sprintf(".delmo/%s", system.Name)

	args := []string{
		"ssh",
		system.MachineName,
		"rm",
		"-rf",
		hostDir,
	}
	cmd := exec.Command(rawCmd, args...)
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("Could not delete dir %s\n%s", hostDir, err)
	}

	args = []string{
		"ssh",
		system.MachineName,
		"mkdir",
		"-p",
		hostDir,
	}
	cmd = exec.Command(rawCmd, args...)
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("Could not create dir %s\n%s", hostDir, err)
	}

	dir := filepath.Dir(path)
	files, err := ioutil.ReadDir(dir)
	for _, f := range files {
		file := filepath.Join(dir, f.Name())
		args = []string{
			"scp",
			"-r",
			file,
			fmt.Sprintf("%s:%s", system.MachineName, hostDir),
		}
		cmd = exec.Command(rawCmd, args...)
		err = cmd.Run()
		if err != nil {
			return "", fmt.Errorf("Could not upload file %s\n%s", f.Name(), err)
		}
	}

	return hostDir, nil
}
<commit_msg>Output uploading files<commit_after>package delmo

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mitchellh/cli"
)

type TestCommand struct {
	Ui cli.Ui
}

func (t *TestCommand) Help() string {
	helpText := `
Usage: delmo test [options]

  Run a test :-)
`
	return strings.TrimSpace(helpText)
}

func (t *TestCommand) Run(args []string) int {
	flags := flag.FlagSet{
		Usage: func() { t.Help() },
	}

	var path string
	flags.StringVar(&path, "f", "delmo.yml", "")
	if err := flags.Parse(args); err != nil {
		t.Ui.Error(fmt.Sprintf("Error parsing arguments\n%s", err))
		return 2
	}

	config, err := LoadConfig(path)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Error reading configuration\n%s", err))
		return 2
	}

	hostDir, err := t.prepareDockerHost(path, config.System)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Cloud not setup docker-machine\n%s", err))
		return 2
	}

	context := GlobalContext{
		DockerHostSyncDir: hostDir,
	}
	suite, err := NewSuite(config, context)
	if err != nil {
		t.Ui.Error(fmt.Sprintf("Could not initialize suite %s"))
		return 2
	}
	result := suite.Run(t.Ui)
	return result

}

func (t *TestCommand) Synopsis() string {
	return "Run some tests"
}

func (t *TestCommand) prepareDockerHost(path string, system SystemConfig) (string, error) {
	rawCmd, err := exec.LookPath("docker-machine")
	if err != nil {
		return "", err
	}
	hostDir := fmt.Sprintf(".delmo/%s", system.Name)

	t.Ui.Info("Preparing host machine")
	args := []string{
		"ssh",
		system.MachineName,
		"rm",
		"-rf",
		hostDir,
	}
	cmd := exec.Command(rawCmd, args...)
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("Could not delete dir %s\n%s", hostDir, err)
	}

	args = []string{
		"ssh",
		system.MachineName,
		"mkdir",
		"-p",
		hostDir,
	}
	cmd = exec.Command(rawCmd, args...)
	err = cmd.Run()
	if err != nil {
		return "", fmt.Errorf("Could not create dir %s\n%s", hostDir, err)
	}

	t.Ui.Info("Uploading files")
	dir := filepath.Dir(path)
	files, err := ioutil.ReadDir(dir)
	for _, f := range files {
		file := filepath.Join(dir, f.Name())
		t.Ui.Info(fmt.Sprintf("file: %s", file))
		args = []string{
			"scp",
			"-r",
			file,
			fmt.Sprintf("%s:%s", system.MachineName, hostDir),
		}
		cmd = exec.Command(rawCmd, args...)
		err = cmd.Run()
		if err != nil {
			return "", fmt.Errorf("Could not upload file %s\n%s", f.Name(), err)
		}
	}

	return hostDir, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/golang/oauth2"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"time"
)

type (
	Config struct {
		RedirectURI  string `json:"redirect_uri"`
		ClientID     string `json:"client_id"`
		ClientSecret string `json:"client_secret"`
	}
	SimpleResult struct {
		Result bool `json:"result"`
	}
	Device struct {
		Id          bson.ObjectId `bson:"_id"`
		BuildingId  bson.ObjectId `bson:"building_id,omitempty"`
		DomainId    bson.ObjectId `bson:"domain_id,omitempty"`
		LastUpdate  time.Time     `bson:"last_update,omitempty"`
		FirstUpdate time.Time     `bson:"first_update,omitempty"`
	}
	DeviceEvent struct {
		When  time.Time     `bson:"when"`
		What  string        `bson:"what"`
		WhoId bson.ObjectId `bson:"who_id"`
	}
	Group struct {
		Id      bson.ObjectId   `bson:"_id"`
		Members []bson.ObjectId `bson:"members"`
	}
	Role struct {
		DomainAdmin     bool `bson:"domain_admin"`
		DomainSetRole   bool `bson:"domain_set_role"`
		DomainModDep    bool `bson:"domain_mod_dep"`
		DomainModBldg   bool `bson:"domain_mod_bldg"`
		DomainModUser   bool `bson:"domain_mod_user"`
		DomainModConfig bool `bson:"domain_mod_config"`
		BldgViewTicket  bool `bson:"bldg_view_tickets"`
	}
	Building struct {
		Id   bson.ObjectId `bson:"_id"`
		Name string        `bson:"name"`
	}
	EmailTemplate struct {
		SubmitTicket   string `bson:"submit_ticket"`   // When a user creates a ticket, what the submitter sees
		NewTicket      string `bson:"new_ticket"`      // When a user creates a ticket, what department sees
		AssignedTicket string `bson:"assigned_ticket"` // When a ticket has been assigned, what the user sees
		SolvedTicket   string `bson:"solved_ticket"`   // When a ticket has been solved, what the submitter sees
		NotedTicket    string `bson:"noted_ticket"`    // When a ticket has a public note, what the submitter sees
		DocumentAdded  string `bson:"document_added"`  // When a ticket has a new document added, universal
	}
	DomainDefault struct {
		UserRole Role `bson:"user_roles"`
	}
	DomainSettings struct {
		Id             bson.ObjectId `bson:"_id"`
		DomainId       bson.ObjectId `bson:"domain_id"`
		Defaults       DomainDefault `bson:"domain_defaults"`
		EmailTemplates EmailTemplate `bson:"email_templates"`
		KeepStats      bool          `bson:"keep_user_stats"`
	}
	Domain struct {
		Id          bson.ObjectId   `bson:"_id"`
		Name        string          `bson:"domain"`
		Buildings   []Building      `bson:"buildings"`
		Departments []bson.ObjectId `bson:"departments"`
	}
	Note struct {
		Id        bson.ObjectId `bson:"_id"`
		Public    bool          `bson:"public"`
		Submitter bson.ObjectId `bson:"submitter"`
		Created   time.Time     `bson:"created"`
		Detail    string        `bson:"details"`
	}
	Category struct {
		Id   bson.ObjectId `bson:"_id"`
		Name string        `bson:"name"`
	}
	DepartmentUser struct {
		UserId          bson.ObjectId `bson:"user_id"`
		DepAdmin        bool          `bson:"dep_admin"`
		DepAssignTicket bool          `bson:"dep_assign_ticket"`
		DepCloseTicket  bool          `bson:"dep_close_ticket"`
	}
	Department struct {
		Id                 bson.ObjectId    `bson:"_id"`
		Name               string           `bson:"name"`
		Category           []Category       `bson:"category,omitempty"`
		IsBuildingSpecific bool             `bson:"is_building_specific"`
		Building           bson.ObjectId    `bson:"visible_to,omitempty"`
		Users              []DepartmentUser `bson:"department_users,omitempty"`
	}
	Document struct {
		Id        bson.ObjectId `bson:"_id"`
		Created   time.Time     `bson:"created"`
		Submitter bson.ObjectId `bson:"submitter"`
		Name      string        `bson:"name"`
		Data      []byte        `bson:"data"`
		Mime      string        `bson:"mime"`
	}
	TicketStatus struct {
		Value string `bson:"value"`
		Name  string `bson:"name"`
	}
	Ticket struct {
		Id          bson.ObjectId `bson:"_id"`
		Submitter   bson.ObjectId `bson:"submitter,omitempty"`
		AssignedTo  bson.ObjectId `bson:"assigned_to,omitempty"`
		Building    bson.ObjectId `bson:"building,omitempty"`
		Department  bson.ObjectId `bson:"department,omitempty"`
		Category    bson.ObjectId `bson:"category,omitempty"`
		Target      bson.ObjectId `bson:"target,omitempty"`
		Subject     string        `bson:"subject"`
		Created     time.Time     `bson:"created"`
		Closed      time.Time     `bson:"closed"`
		Status      string        `bson:"status,omitempty"`
		Duration    time.Duration `bson:"duration"`
		Notes       []Note        `bson:"notes"`
		Documents   []Document    `bson:"document"`
		Description string        `bson:"description,omitempty"`
	}
	TicketCount struct {
		Day        int `bson:"day"`
		Month      int `bson:"month"`
		Year       int `bson:"year"`
		Submitted  int `bson:"submitted"`
		Closed     int `bson:"closed"`
		Assigned   int `bson:"assigned"`
		AssignedTo int `bson:"assigned_to"`
		Noted      int `bson:"noted"`
	}
	Session struct {
		Id     bson.ObjectId `bson:"_id,omitempty"`
		UserId bson.ObjectId `bson:"user_id,omitempty"`
		oauth2.Token
	}
	User struct {
		Id         bson.ObjectId   `bson:"_id,omitempty"`
		Domain     bson.ObjectId   `bson:"domain_id,omitempty"`
		Department []bson.ObjectId `bson:"department,omitempty"`
		Building   bson.ObjectId   `bson:"location,omitempty"`

		GoogleId  string `bson:"google_id"`
		Firstname string `bson:"firstname"`
		Lastname  string `bson:"lastname"`
		Email     string `bson:"email"`
		Picture   string `bson:"picture"`

		Enabled bool `bson:"enabled"`

		Roles Role `bson:"role"`

		FirstLogin time.Time `bson:"first_login"`
		LastLogin  time.Time `bson:"last_login"`
		RolesSet   time.Time `bson:"role_set"`

		TicketStats []TicketCount   `bson:"ticket_count"`
		Submitted   []bson.ObjectId `bson:"submitted"`
	}

	GoogleUserV2 struct {
		Id            string `json:"id,omitempty"`
		Email         string `json:"email,omitempty"`
		VerifiedEmail bool   `json:"verified_email,omitempty"`
		GivenName     string `json:"given_name,omitempty"`
		FamilyName    string `json:"family_name,omitempty"`
		Picture       string `json:"picture,omitempty"`
		Hd            string `json:"hd,omitempty"`
	}
)

func (d *Department) GetDepartment(id bson.ObjectId, db *mgo.Database) error {
	c := db.C(DepartmentsC)
	return c.Find(bson.M{"_id": id}).One(&d)
}
func (u User) Marshal() ([]byte, error) {
	ret, err := json.Marshal(u)
	return ret, err
}
func (u User) GetAssignedTickets(db *mgo.Database) ([]Ticket, error) {
	var t []Ticket
	c := db.C(TicketsC)
	err := c.Find(bson.M{"assigned_to": u.Id}).All(&t)
	return t, err
}
func (u User) InDepartment(id bson.ObjectId) bool {
	// If it is not a valid object id then return false
	if !id.Valid() {
		return false
	}

	// To limit the cpu work when retrieving a hex
	hex := id.Hex()

	// Iterate through the departments that the user is associated with
	for i := 0; i < len(u.Department); i++ {
		if hex == u.Department[i].Hex() {
			return true
		}
	}
	return false
}
func (u User) CanView(ticket Ticket) bool {
	if ticket.Submitter.Hex() == u.Id.Hex() {
		return true
	}

	if ticket.AssignedTo.Hex() == u.Id.Hex() {
		return true
	}

	if u.InDepartment(ticket.Department) {
		return true
	}

	if u.Roles.DomainAdmin {
		return true
	}

	if u.Roles.BldgViewTicket && ticket.Building.Hex() == u.Building.Hex() {
		return true
	}

	return false
}
func (u User) CanEdit(ticket Ticket) bool {
	if ticket.AssignedTo.Hex() == u.Id.Hex() {
		return true
	}

	if ticket.Submitter.Hex() == u.Id.Hex() {
		return true
	}

	if u.Roles.DomainAdmin {
		return true
	}

	return false
}
func (u User) CanDelete(ticket Ticket) bool {
	return false
}
func (u User) CanUpdate(ticket Ticket) bool {
	return false
}
<commit_msg>building on department functions<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/golang/oauth2"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
	"time"
)

type (
	Config struct {
		RedirectURI  string `json:"redirect_uri"`
		ClientID     string `json:"client_id"`
		ClientSecret string `json:"client_secret"`
	}
	SimpleResult struct {
		Result bool `json:"result"`
	}
	Device struct {
		Id          bson.ObjectId `bson:"_id"`
		BuildingId  bson.ObjectId `bson:"building_id,omitempty"`
		DomainId    bson.ObjectId `bson:"domain_id,omitempty"`
		LastUpdate  time.Time     `bson:"last_update,omitempty"`
		FirstUpdate time.Time     `bson:"first_update,omitempty"`
	}
	DeviceEvent struct {
		When  time.Time     `bson:"when"`
		What  string        `bson:"what"`
		WhoId bson.ObjectId `bson:"who_id"`
	}
	Group struct {
		Id      bson.ObjectId   `bson:"_id"`
		Members []bson.ObjectId `bson:"members"`
	}
	Role struct {
		DomainAdmin     bool `bson:"domain_admin"`
		DomainSetRole   bool `bson:"domain_set_role"`
		DomainModDep    bool `bson:"domain_mod_dep"`
		DomainModBldg   bool `bson:"domain_mod_bldg"`
		DomainModUser   bool `bson:"domain_mod_user"`
		DomainModConfig bool `bson:"domain_mod_config"`
		BldgViewTicket  bool `bson:"bldg_view_tickets"`
	}
	Building struct {
		Id   bson.ObjectId `bson:"_id"`
		Name string        `bson:"name"`
	}
	EmailTemplate struct {
		SubmitTicket   string `bson:"submit_ticket"`   // When a user creates a ticket, what the submitter sees
		NewTicket      string `bson:"new_ticket"`      // When a user creates a ticket, what department sees
		AssignedTicket string `bson:"assigned_ticket"` // When a ticket has been assigned, what the user sees
		SolvedTicket   string `bson:"solved_ticket"`   // When a ticket has been solved, what the submitter sees
		NotedTicket    string `bson:"noted_ticket"`    // When a ticket has a public note, what the submitter sees
		DocumentAdded  string `bson:"document_added"`  // When a ticket has a new document added, universal
	}
	DomainDefault struct {
		UserRole Role `bson:"user_roles"`
	}
	DomainSettings struct {
		Id             bson.ObjectId `bson:"_id"`
		DomainId       bson.ObjectId `bson:"domain_id"`
		Defaults       DomainDefault `bson:"domain_defaults"`
		EmailTemplates EmailTemplate `bson:"email_templates"`
		KeepStats      bool          `bson:"keep_user_stats"`
	}
	Domain struct {
		Id          bson.ObjectId   `bson:"_id"`
		Name        string          `bson:"domain"`
		Buildings   []Building      `bson:"buildings"`
		Departments []bson.ObjectId `bson:"departments"`
	}
	Note struct {
		Id        bson.ObjectId `bson:"_id"`
		Public    bool          `bson:"public"`
		Submitter bson.ObjectId `bson:"submitter"`
		Created   time.Time     `bson:"created"`
		Detail    string        `bson:"details"`
	}
	Category struct {
		Id   bson.ObjectId `bson:"_id"`
		Name string        `bson:"name"`
	}
	DepartmentUser struct {
		UserId          bson.ObjectId `bson:"user_id"`
		DepAdmin        bool          `bson:"dep_admin"`
		DepAssignTicket bool          `bson:"dep_assign_ticket"`
		DepCloseTicket  bool          `bson:"dep_close_ticket"`
	}
	Department struct {
		Id                 bson.ObjectId    `bson:"_id"`
		Name               string           `bson:"name"`
		Category           []Category       `bson:"category,omitempty"`
		IsBuildingSpecific bool             `bson:"is_building_specific"`
		Building           bson.ObjectId    `bson:"visible_to,omitempty"`
		Users              []DepartmentUser `bson:"department_users,omitempty"`
	}
	Document struct {
		Id        bson.ObjectId `bson:"_id"`
		Created   time.Time     `bson:"created"`
		Submitter bson.ObjectId `bson:"submitter"`
		Name      string        `bson:"name"`
		Data      []byte        `bson:"data"`
		Mime      string        `bson:"mime"`
	}
	TicketStatus struct {
		Value string `bson:"value"`
		Name  string `bson:"name"`
	}
	Ticket struct {
		Id          bson.ObjectId `bson:"_id"`
		Submitter   bson.ObjectId `bson:"submitter,omitempty"`
		AssignedTo  bson.ObjectId `bson:"assigned_to,omitempty"`
		Building    bson.ObjectId `bson:"building,omitempty"`
		Department  bson.ObjectId `bson:"department,omitempty"`
		Category    bson.ObjectId `bson:"category,omitempty"`
		Target      bson.ObjectId `bson:"target,omitempty"`
		Subject     string        `bson:"subject"`
		Created     time.Time     `bson:"created"`
		Closed      time.Time     `bson:"closed"`
		Status      string        `bson:"status,omitempty"`
		Duration    time.Duration `bson:"duration"`
		Notes       []Note        `bson:"notes"`
		Documents   []Document    `bson:"document"`
		Description string        `bson:"description,omitempty"`
	}
	TicketCount struct {
		Day        int `bson:"day"`
		Month      int `bson:"month"`
		Year       int `bson:"year"`
		Submitted  int `bson:"submitted"`
		Closed     int `bson:"closed"`
		Assigned   int `bson:"assigned"`
		AssignedTo int `bson:"assigned_to"`
		Noted      int `bson:"noted"`
	}
	Session struct {
		Id     bson.ObjectId `bson:"_id,omitempty"`
		UserId bson.ObjectId `bson:"user_id,omitempty"`
		oauth2.Token
	}
	User struct {
		Id         bson.ObjectId   `bson:"_id,omitempty"`
		Domain     bson.ObjectId   `bson:"domain_id,omitempty"`
		Department []bson.ObjectId `bson:"department,omitempty"`
		Building   bson.ObjectId   `bson:"location,omitempty"`

		GoogleId  string `bson:"google_id"`
		Firstname string `bson:"firstname"`
		Lastname  string `bson:"lastname"`
		Email     string `bson:"email"`
		Picture   string `bson:"picture"`

		Enabled bool `bson:"enabled"`

		Roles Role `bson:"role"`

		FirstLogin time.Time `bson:"first_login"`
		LastLogin  time.Time `bson:"last_login"`
		RolesSet   time.Time `bson:"role_set"`

		TicketStats []TicketCount   `bson:"ticket_count"`
		Submitted   []bson.ObjectId `bson:"submitted"`
	}

	GoogleUserV2 struct {
		Id            string `json:"id,omitempty"`
		Email         string `json:"email,omitempty"`
		VerifiedEmail bool   `json:"verified_email,omitempty"`
		GivenName     string `json:"given_name,omitempty"`
		FamilyName    string `json:"family_name,omitempty"`
		Picture       string `json:"picture,omitempty"`
		Hd            string `json:"hd,omitempty"`
	}
)

func (d Department) GetMember(id bson.ObjectId) (*DepartmentUser, error) {
	for i := 0; i < len(d.Users); i++ {
		if d.Users[i].UserId == id {
			return &d.Users[i], nil
		}
	}
	return nil, fmt.Errorf("Department: user %s is not a member of \"%s\"", id.Hex(), d.Name)
}

func (d *Department) GetDepartment(id bson.ObjectId, db *mgo.Database) error {
	c := db.C(DepartmentsC)
	return c.Find(bson.M{"_id": id}).One(&d)
}
func (d Department) CanEditTicket(u User, t Ticket) bool {
	var depUser int = -1

	// Is the user in the department on their list?
	if !u.InDepartment(t.Department) {
		return false
	}

	// Find the user in the department to get their roles
	for i := 0; i < len(d.Users); i++ {
		if d.Users[i].UserId.Hex() == u.Id.Hex() {
			depUser = i
			break
		}
	}

	// Can't find them? then they can only view... maybe
	if depUser == -1 {
		return false
	}

	//
	return true
}

func (u User) Marshal() ([]byte, error) {
	ret, err := json.Marshal(u)
	return ret, err
}
func (u User) GetAssignedTickets(db *mgo.Database) ([]Ticket, error) {
	var t []Ticket
	c := db.C(TicketsC)
	err := c.Find(bson.M{"assigned_to": u.Id}).All(&t)
	return t, err
}
func (u User) InDepartment(id bson.ObjectId) bool {
	// If it is not a valid object id then return false
	if !id.Valid() {
		return false
	}

	// To limit the cpu work when retrieving a hex
	hex := id.Hex()

	// Iterate through the departments that the user is associated with
	for i := 0; i < len(u.Department); i++ {
		if hex == u.Department[i].Hex() {
			return true
		}
	}
	return false
}
func (u User) CanViewTicket(ticket Ticket) bool {
	if ticket.Submitter.Hex() == u.Id.Hex() {
		return true
	}

	if ticket.AssignedTo.Hex() == u.Id.Hex() {
		return true
	}

	if u.InDepartment(ticket.Department) {
		return true
	}

	if u.Roles.DomainAdmin {
		return true
	}

	if u.Roles.BldgViewTicket && ticket.Building.Hex() == u.Building.Hex() {
		return true
	}

	return false
}
func (u User) CanEditTicket(ticket Ticket) bool {
	if ticket.AssignedTo.Hex() == u.Id.Hex() {
		return true
	}

	if ticket.Submitter.Hex() == u.Id.Hex() {
		return true
	}

	if u.Roles.DomainAdmin {
		return true
	}

	return false
}
func (u User) CanDelete(ticket Ticket) bool {
	return false
}
func (u User) CanUpdate(ticket Ticket) bool {
	return false
}
<|endoftext|>


<commit_before>package dbr

import (
	"bytes"
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"time"
)

//
// Your app can use these Null types instead of the defaults. The sole benefit you get is a MarshalJSON method that is not retarded.
//

// NullString is a type that can be null or a string
type NullString struct {
	sql.NullString
}

// NullFloat64 is a type that can be null or a float64
type NullFloat64 struct {
	sql.NullFloat64
}

// NullInt64 is a type that can be null or an int
type NullInt64 struct {
	sql.NullInt64
}

// NullTime is a type that can be null or a time
type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (n *NullTime) Scan(value interface{}) error {
	n.Time, n.Valid = value.(time.Time)
	return nil
}

// Value implements the driver Valuer interface.
func (n NullTime) Value() (driver.Value, error) {
	if !n.Valid {
		return nil, nil
	}
	return n.Time, nil
}

// NullBool is a type that can be null or a bool
type NullBool struct {
	sql.NullBool
}

var nullString = []byte("null")

// MarshalJSON correctly serializes a NullString to JSON
func (n NullString) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.String)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullInt64 to JSON
func (n NullInt64) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Int64)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullFloat64 to JSON
func (n NullFloat64) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Float64)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullTime to JSON
func (n NullTime) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Time)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullBool to JSON
func (n NullBool) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Bool)
	}
	return nullString, nil
}

// UnmarshalJSON correctly deserializes a NullString from JSON
func (n *NullString) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullInt64 from JSON
func (n *NullInt64) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullFloat64 from JSON
func (n *NullFloat64) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullTime from JSON
func (n *NullTime) UnmarshalJSON(b []byte) error {
	// scan for null
	if bytes.Equal(b, nullString) {
		return n.Scan(nil)
	}
	// scan for JSON timestamp
	var t time.Time
	if err := json.Unmarshal(b, &t); err != nil {
		return err
	}
	return n.Scan(t)
}

// UnmarshalJSON correctly deserializes a NullBool from JSON
func (n *NullBool) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}
<commit_msg>FEATURE: add null type init helpers<commit_after>package dbr

import (
	"bytes"
	"database/sql"
	"database/sql/driver"
	"encoding/json"
	"time"
)

//
// Your app can use these Null types instead of the defaults. The sole benefit you get is a MarshalJSON method that is not retarded.
//

// NullString is a type that can be null or a string
type NullString struct {
	sql.NullString
}

// NullFloat64 is a type that can be null or a float64
type NullFloat64 struct {
	sql.NullFloat64
}

// NullInt64 is a type that can be null or an int
type NullInt64 struct {
	sql.NullInt64
}

// NullTime is a type that can be null or a time
type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

// Scan implements the Scanner interface.
func (n *NullTime) Scan(value interface{}) error {
	n.Time, n.Valid = value.(time.Time)
	return nil
}

// Value implements the driver Valuer interface.
func (n NullTime) Value() (driver.Value, error) {
	if !n.Valid {
		return nil, nil
	}
	return n.Time, nil
}

// NullBool is a type that can be null or a bool
type NullBool struct {
	sql.NullBool
}

var nullString = []byte("null")

// MarshalJSON correctly serializes a NullString to JSON
func (n NullString) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.String)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullInt64 to JSON
func (n NullInt64) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Int64)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullFloat64 to JSON
func (n NullFloat64) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Float64)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullTime to JSON
func (n NullTime) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Time)
	}
	return nullString, nil
}

// MarshalJSON correctly serializes a NullBool to JSON
func (n NullBool) MarshalJSON() ([]byte, error) {
	if n.Valid {
		return json.Marshal(n.Bool)
	}
	return nullString, nil
}

// UnmarshalJSON correctly deserializes a NullString from JSON
func (n *NullString) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullInt64 from JSON
func (n *NullInt64) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullFloat64 from JSON
func (n *NullFloat64) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

// UnmarshalJSON correctly deserializes a NullTime from JSON
func (n *NullTime) UnmarshalJSON(b []byte) error {
	// scan for null
	if bytes.Equal(b, nullString) {
		return n.Scan(nil)
	}
	// scan for JSON timestamp
	var t time.Time
	if err := json.Unmarshal(b, &t); err != nil {
		return err
	}
	return n.Scan(t)
}

// UnmarshalJSON correctly deserializes a NullBool from JSON
func (n *NullBool) UnmarshalJSON(b []byte) error {
	var s interface{}
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	return n.Scan(s)
}

func NewNullInt64(v interface{}) (n NullInt64) {
	n.Scan(v)
	return
}

func NewNullFloat64(v interface{}) (n NullFloat64) {
	n.Scan(v)
	return
}

func NewNullString(v interface{}) (n NullString) {
	n.Scan(v)
	return
}

func NewNullTime(v interface{}) (n NullTime) {
	n.Scan(v)
	return
}

func NewNullBool(v interface{}) (n NullBool) {
	n.Scan(v)
	return
}
<|endoftext|>


<commit_before>package blockchain

import (
	"math"
	"sync"
	"time"

	//	"github.com/blockchain/types"
	"github.com/bytom/protocol/bc/legacy"
	. "github.com/tendermint/tmlibs/common"
	flow "github.com/tendermint/tmlibs/flowrate"
	"github.com/tendermint/tmlibs/log"
)

const (
	requestIntervalMS         = 250
	maxTotalRequesters        = 300
	maxPendingRequests        = maxTotalRequesters
	maxPendingRequestsPerPeer = 75
	minRecvRate               = 10240 // 10Kb/s
)

var peerTimeoutSeconds = time.Duration(15) // not const so we can override with tests

type BlockPool struct {
	BaseService
	startTime time.Time

	mtx sync.Mutex
	// block requests
	requesters map[uint64]*bpRequester
	height     uint64 // the lowest key in requesters.
	numPending int32  // number of requests pending assignment or block response
	// peers
	peers map[string]*bpPeer

	requestsCh chan<- BlockRequest
	timeoutsCh chan<- string
}

func NewBlockPool(start uint64, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool {
	bp := &BlockPool{
		peers: make(map[string]*bpPeer),

		requesters: make(map[uint64]*bpRequester),
		height:     start,
		numPending: 0,

		requestsCh: requestsCh,
		timeoutsCh: timeoutsCh,
	}
	bp.BaseService = *NewBaseService(nil, "BlockPool", bp)
	return bp
}

func (pool *BlockPool) OnStart() error {
	go pool.makeRequestersRoutine()
	pool.startTime = time.Now()
	return nil
}

func (pool *BlockPool) OnStop() {
	pool.BaseService.OnStop()
}

// Run spawns requesters as needed.
func (pool *BlockPool) makeRequestersRoutine() {
	for {
		if !pool.IsRunning() {
			break
		}
		_, numPending, lenRequesters := pool.GetStatus()
		if numPending >= maxPendingRequests {
			// sleep for a bit.
			time.Sleep(requestIntervalMS * time.Millisecond)
			// check for timed out peers
			pool.removeTimedoutPeers()
		} else if lenRequesters >= maxTotalRequesters {
			// sleep for a bit.
			time.Sleep(requestIntervalMS * time.Millisecond)
			// check for timed out peers
			pool.removeTimedoutPeers()
		} else {
			// request for more blocks.
			pool.makeNextRequester()
		}
	}
}

func (pool *BlockPool) removeTimedoutPeers() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	for _, peer := range pool.peers {
		if !peer.didTimeout && peer.numPending > 0 {
			curRate := peer.recvMonitor.Status().CurRate
			// XXX remove curRate != 0
			if curRate != 0 && curRate < minRecvRate {
				pool.sendTimeout(peer.id)
				pool.Logger.Error("SendTimeout", "peer", peer.id, "reason", "curRate too low")
				peer.didTimeout = true
			}
		}
		if peer.didTimeout {
			pool.removePeer(peer.id)
		}
	}
}

func (pool *BlockPool) GetStatus() (height uint64, numPending int32, lenRequesters int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	return pool.height, pool.numPending, len(pool.requesters)
}

// TODO: relax conditions, prevent abuse.
func (pool *BlockPool) IsCaughtUp() bool {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	height := pool.height

	// Need at least 1 peer to be considered caught up.
	if len(pool.peers) == 0 && time.Now().Sub(pool.startTime) > 60*time.Second {
		pool.Logger.Debug("Blockpool has no peers")
		return false
	}

	var maxPeerHeight uint64 = 0
	for _, peer := range pool.peers {
		maxPeerHeight = MaxUint64(maxPeerHeight, peer.height)
	}

	isCaughtUp := (height > 0 || time.Now().Sub(pool.startTime) > 5*time.Second) && (maxPeerHeight == 0 || height >= maxPeerHeight)
	pool.Logger.Info(Fmt("IsCaughtUp: %v", isCaughtUp), "height", height, "maxPeerHeight", maxPeerHeight)
	return isCaughtUp
}

// We need to see the second block's Commit to validate the first block.
// So we peek two blocks at a time.
// The caller will verify the commit.
func (pool *BlockPool) PeekTwoBlocks() (first *legacy.Block, second *legacy.Block) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	if r := pool.requesters[pool.height]; r != nil {
		first = r.getBlock()
	}
	if r := pool.requesters[pool.height+1]; r != nil {
		second = r.getBlock()
	}
	return
}

// Pop the first block at pool.height
// It must have been validated by 'second'.Commit from PeekTwoBlocks().
func (pool *BlockPool) PopRequest() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	if r := pool.requesters[pool.height]; r != nil {
		/*  The block can disappear at any time, due to removePeer().
		if r := pool.requesters[pool.height]; r == nil || r.block == nil {
			PanicSanity("PopRequest() requires a valid block")
		}
		*/
		r.Stop()
		delete(pool.requesters, pool.height)
		pool.height++
	} else {
		PanicSanity(Fmt("Expected requester to pop, got nothing at height %v", pool.height))
	}
}

// Invalidates the block at pool.height,
// Remove the peer and redo request from others.
func (pool *BlockPool) RedoRequest(height uint64) {
	pool.mtx.Lock()
	request := pool.requesters[height]
	pool.mtx.Unlock()

	if request.block == nil {
		PanicSanity("Expected block to be non-nil")
	}
	// RemovePeer will redo all requesters associated with this peer.
	// TODO: record this malfeasance
	pool.RemovePeer(request.peerID)
}

// TODO: ensure that blocks come in order for each peer.
func (pool *BlockPool) AddBlock(peerID string, block *legacy.Block, blockSize int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	requester := pool.requesters[block.Height]
	if requester == nil {
		return
	}

	if requester.setBlock(block, peerID) {
		pool.numPending--
		peer := pool.peers[peerID]
		peer.decrPending(blockSize)
	} else {
		// Bad peer?
	}
}

// Sets the peer's alleged blockchain height.
func (pool *BlockPool) SetPeerHeight(peerID string, height uint64) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	peer := pool.peers[peerID]
	if peer != nil {
		peer.height = height
	} else {
		peer = newBPPeer(pool, peerID, height)
		peer.setLogger(pool.Logger.With("peer", peerID))
		pool.peers[peerID] = peer
	}
}

func (pool *BlockPool) RemovePeer(peerID string) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	pool.removePeer(peerID)
}

func (pool *BlockPool) removePeer(peerID string) {
	for _, requester := range pool.requesters {
		if requester.getPeerID() == peerID {
			if requester.getBlock() != nil {
				pool.numPending++
			}
			go requester.redo() // pick another peer and ...
		}
	}
	delete(pool.peers, peerID)
}

// Pick an available peer with at least the given minHeight.
// If no peers are available, returns nil.
func (pool *BlockPool) pickIncrAvailablePeer(minHeight uint64) *bpPeer {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	for _, peer := range pool.peers {
		if peer.didTimeout {
			pool.removePeer(peer.id)
			continue
		} else {
		}
		if peer.numPending >= maxPendingRequestsPerPeer {
			continue
		}
		if peer.height < minHeight {
			continue
		}
		peer.incrPending()
		return peer
	}
	return nil
}

func (pool *BlockPool) makeNextRequester() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	nextHeight := pool.height + uint64(len(pool.requesters))
	request := newBPRequester(pool, nextHeight)
	request.SetLogger(pool.Logger.With("height", nextHeight))

	pool.requesters[nextHeight] = request
	pool.numPending++

	request.Start()
}

func (pool *BlockPool) sendRequest(height uint64, peerID string) {
	if !pool.IsRunning() {
		return
	}
	pool.requestsCh <- BlockRequest{height, peerID}
}

func (pool *BlockPool) sendTimeout(peerID string) {
	if !pool.IsRunning() {
		return
	}
	pool.timeoutsCh <- peerID
}

func (pool *BlockPool) debug() string {
	pool.mtx.Lock() // Lock
	defer pool.mtx.Unlock()

	str := ""
	var h uint64
	for h = pool.height; h < pool.height+uint64(len(pool.requesters)); h++ {
		if pool.requesters[h] == nil {
			str += Fmt("H(%v):X ", h)
		} else {
			str += Fmt("H(%v):", h)
			str += Fmt("B?(%v) ", pool.requesters[h].block != nil)
		}
	}
	return str
}

//-------------------------------------

type bpPeer struct {
	pool        *BlockPool
	id          string
	recvMonitor *flow.Monitor

	mtx        sync.Mutex
	height     uint64
	numPending int32
	timeout    *time.Timer
	didTimeout bool

	logger log.Logger
}

func newBPPeer(pool *BlockPool, peerID string, height uint64) *bpPeer {
	peer := &bpPeer{
		pool:       pool,
		id:         peerID,
		height:     height,
		numPending: 0,
		logger:     log.NewNopLogger(),
	}
	return peer
}

func (peer *bpPeer) setLogger(l log.Logger) {
	peer.logger = l
}

func (peer *bpPeer) resetMonitor() {
	peer.recvMonitor = flow.New(time.Second, time.Second*40)
	var initialValue = float64(minRecvRate) * math.E
	peer.recvMonitor.SetREMA(initialValue)
}

func (peer *bpPeer) resetTimeout() {
	if peer.timeout == nil {
		peer.timeout = time.AfterFunc(time.Second*peerTimeoutSeconds, peer.onTimeout)
	} else {
		peer.timeout.Reset(time.Second * peerTimeoutSeconds)
	}
}

func (peer *bpPeer) incrPending() {
	if peer.numPending == 0 {
		peer.resetMonitor()
		peer.resetTimeout()
	}
	peer.numPending++
}

func (peer *bpPeer) decrPending(recvSize int) {
	peer.numPending--
	if peer.numPending == 0 {
		peer.timeout.Stop()
	} else {
		peer.recvMonitor.Update(recvSize)
		peer.resetTimeout()
	}
}

func (peer *bpPeer) onTimeout() {
	peer.pool.mtx.Lock()
	defer peer.pool.mtx.Unlock()

	peer.pool.sendTimeout(peer.id)
	peer.logger.Error("SendTimeout", "reason", "onTimeout")
	peer.didTimeout = true
}

//-------------------------------------

type bpRequester struct {
	BaseService
	pool       *BlockPool
	height     uint64
	gotBlockCh chan struct{}
	redoCh     chan struct{}

	mtx    sync.Mutex
	peerID string
	block  *legacy.Block
}

func newBPRequester(pool *BlockPool, height uint64) *bpRequester {
	bpr := &bpRequester{
		pool:       pool,
		height:     height,
		gotBlockCh: make(chan struct{}),
		redoCh:     make(chan struct{}),

		peerID: "",
		block:  nil,
	}
	bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr)
	return bpr
}

func (bpr *bpRequester) OnStart() error {
	go bpr.requestRoutine()
	return nil
}

// Returns true if the peer matches
func (bpr *bpRequester) setBlock(block *legacy.Block, peerID string) bool {
	bpr.mtx.Lock()
	if bpr.block != nil || bpr.peerID != peerID {
		bpr.mtx.Unlock()
		return false
	}
	bpr.block = block
	bpr.mtx.Unlock()

	bpr.gotBlockCh <- struct{}{}
	return true
}

func (bpr *bpRequester) getBlock() *legacy.Block {
	bpr.mtx.Lock()
	defer bpr.mtx.Unlock()
	return bpr.block
}

func (bpr *bpRequester) getPeerID() string {
	bpr.mtx.Lock()
	defer bpr.mtx.Unlock()
	return bpr.peerID
}

func (bpr *bpRequester) reset() {
	bpr.mtx.Lock()
	bpr.peerID = ""
	bpr.block = nil
	bpr.mtx.Unlock()
}

// Tells bpRequester to pick another peer and try again.
// NOTE: blocking
func (bpr *bpRequester) redo() {
	bpr.redoCh <- struct{}{}
}

// Responsible for making more requests as necessary
// Returns only when a block is found (e.g. AddBlock() is called)
func (bpr *bpRequester) requestRoutine() {
OUTER_LOOP:
	for {
		// Pick a peer to send request to.
		var peer *bpPeer = nil
	PICK_PEER_LOOP:
		for {
			if !bpr.IsRunning() || !bpr.pool.IsRunning() {
				return
			}
			peer = bpr.pool.pickIncrAvailablePeer(bpr.height)
			if peer == nil {
				//log.Info("No peers available", "height", height)
				time.Sleep(requestIntervalMS * time.Millisecond)
				continue PICK_PEER_LOOP
			}
			break PICK_PEER_LOOP
		}
		bpr.mtx.Lock()
		bpr.peerID = peer.id
		bpr.mtx.Unlock()

		// Send request and wait.
		bpr.pool.sendRequest(bpr.height, peer.id)
		select {
		case <-bpr.pool.Quit:
			bpr.Stop()
			return
		case <-bpr.Quit:
			return
		case <-bpr.redoCh:
			bpr.reset()
			continue OUTER_LOOP // When peer is removed
		case <-bpr.gotBlockCh:
			// We got the block, now see if it's good.
			select {
			case <-bpr.pool.Quit:
				bpr.Stop()
				return
			case <-bpr.Quit:
				return
			case <-bpr.redoCh:
				bpr.reset()
				continue OUTER_LOOP
			}
		}
	}
}

//-------------------------------------

type BlockRequest struct {
	Height uint64
	PeerID string
}
<commit_msg>Eliminate tendermint log in pool.go (#68)<commit_after>package blockchain

import (
	"math"
	"sync"
	"time"

	//	"github.com/blockchain/types"
	"github.com/bytom/protocol/bc/legacy"
	log "github.com/sirupsen/logrus"
	. "github.com/tendermint/tmlibs/common"
	flow "github.com/tendermint/tmlibs/flowrate"
)

const (
	requestIntervalMS         = 250
	maxTotalRequesters        = 300
	maxPendingRequests        = maxTotalRequesters
	maxPendingRequestsPerPeer = 75
	minRecvRate               = 10240 // 10Kb/s
)

var peerTimeoutSeconds = time.Duration(15) // not const so we can override with tests

type BlockPool struct {
	BaseService
	startTime time.Time

	mtx sync.Mutex
	// block requests
	requesters map[uint64]*bpRequester
	height     uint64 // the lowest key in requesters.
	numPending int32  // number of requests pending assignment or block response
	// peers
	peers map[string]*bpPeer

	requestsCh chan<- BlockRequest
	timeoutsCh chan<- string
}

func NewBlockPool(start uint64, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool {
	bp := &BlockPool{
		peers: make(map[string]*bpPeer),

		requesters: make(map[uint64]*bpRequester),
		height:     start,
		numPending: 0,

		requestsCh: requestsCh,
		timeoutsCh: timeoutsCh,
	}
	bp.BaseService = *NewBaseService(nil, "BlockPool", bp)
	return bp
}

func (pool *BlockPool) OnStart() error {
	go pool.makeRequestersRoutine()
	pool.startTime = time.Now()
	return nil
}

func (pool *BlockPool) OnStop() {
	pool.BaseService.OnStop()
}

// Run spawns requesters as needed.
func (pool *BlockPool) makeRequestersRoutine() {
	for {
		if !pool.IsRunning() {
			break
		}
		_, numPending, lenRequesters := pool.GetStatus()
		if numPending >= maxPendingRequests {
			// sleep for a bit.
			time.Sleep(requestIntervalMS * time.Millisecond)
			// check for timed out peers
			pool.removeTimedoutPeers()
		} else if lenRequesters >= maxTotalRequesters {
			// sleep for a bit.
			time.Sleep(requestIntervalMS * time.Millisecond)
			// check for timed out peers
			pool.removeTimedoutPeers()
		} else {
			// request for more blocks.
			pool.makeNextRequester()
		}
	}
}

func (pool *BlockPool) removeTimedoutPeers() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	for _, peer := range pool.peers {
		if !peer.didTimeout && peer.numPending > 0 {
			curRate := peer.recvMonitor.Status().CurRate
			// XXX remove curRate != 0
			if curRate != 0 && curRate < minRecvRate {
				pool.sendTimeout(peer.id)
				log.WithFields(log.Fields{
					"peer":   peer.id,
					"reason": "curRate too low",
				}).Error("SendTimeout")
				peer.didTimeout = true
			}
		}
		if peer.didTimeout {
			pool.removePeer(peer.id)
		}
	}
}

func (pool *BlockPool) GetStatus() (height uint64, numPending int32, lenRequesters int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	return pool.height, pool.numPending, len(pool.requesters)
}

// TODO: relax conditions, prevent abuse.
func (pool *BlockPool) IsCaughtUp() bool {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	height := pool.height

	// Need at least 1 peer to be considered caught up.
	if len(pool.peers) == 0 && time.Now().Sub(pool.startTime) > 60*time.Second {
		log.Debug("Blockpool has no peers")
		return false
	}

	var maxPeerHeight uint64 = 0
	for _, peer := range pool.peers {
		maxPeerHeight = MaxUint64(maxPeerHeight, peer.height)
	}

	isCaughtUp := (height > 0 || time.Now().Sub(pool.startTime) > 5*time.Second) && (maxPeerHeight == 0 || height >= maxPeerHeight)
	log.WithFields(log.Fields{
		"height":        height,
		"maxPeerHeight": maxPeerHeight,
	}).Infof("IsCaughtUp: %v", isCaughtUp)
	return isCaughtUp
}

// We need to see the second block's Commit to validate the first block.
// So we peek two blocks at a time.
// The caller will verify the commit.
func (pool *BlockPool) PeekTwoBlocks() (first *legacy.Block, second *legacy.Block) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	if r := pool.requesters[pool.height]; r != nil {
		first = r.getBlock()
	}
	if r := pool.requesters[pool.height+1]; r != nil {
		second = r.getBlock()
	}
	return
}

// Pop the first block at pool.height
// It must have been validated by 'second'.Commit from PeekTwoBlocks().
func (pool *BlockPool) PopRequest() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	if r := pool.requesters[pool.height]; r != nil {
		/*  The block can disappear at any time, due to removePeer().
		if r := pool.requesters[pool.height]; r == nil || r.block == nil {
			PanicSanity("PopRequest() requires a valid block")
		}
		*/
		r.Stop()
		delete(pool.requesters, pool.height)
		pool.height++
	} else {
		PanicSanity(Fmt("Expected requester to pop, got nothing at height %v", pool.height))
	}
}

// Invalidates the block at pool.height,
// Remove the peer and redo request from others.
func (pool *BlockPool) RedoRequest(height uint64) {
	pool.mtx.Lock()
	request := pool.requesters[height]
	pool.mtx.Unlock()

	if request.block == nil {
		PanicSanity("Expected block to be non-nil")
	}
	// RemovePeer will redo all requesters associated with this peer.
	// TODO: record this malfeasance
	pool.RemovePeer(request.peerID)
}

// TODO: ensure that blocks come in order for each peer.
func (pool *BlockPool) AddBlock(peerID string, block *legacy.Block, blockSize int) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	requester := pool.requesters[block.Height]
	if requester == nil {
		return
	}

	if requester.setBlock(block, peerID) {
		pool.numPending--
		peer := pool.peers[peerID]
		peer.decrPending(blockSize)
	} else {
		// Bad peer?
	}
}

// Sets the peer's alleged blockchain height.
func (pool *BlockPool) SetPeerHeight(peerID string, height uint64) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	peer := pool.peers[peerID]
	if peer != nil {
		peer.height = height
	} else {
		peer = newBPPeer(pool, peerID, height)
		pool.peers[peerID] = peer
	}
}

func (pool *BlockPool) RemovePeer(peerID string) {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	pool.removePeer(peerID)
}

func (pool *BlockPool) removePeer(peerID string) {
	for _, requester := range pool.requesters {
		if requester.getPeerID() == peerID {
			if requester.getBlock() != nil {
				pool.numPending++
			}
			go requester.redo() // pick another peer and ...
		}
	}
	delete(pool.peers, peerID)
}

// Pick an available peer with at least the given minHeight.
// If no peers are available, returns nil.
func (pool *BlockPool) pickIncrAvailablePeer(minHeight uint64) *bpPeer {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	for _, peer := range pool.peers {
		if peer.didTimeout {
			pool.removePeer(peer.id)
			continue
		} else {
		}
		if peer.numPending >= maxPendingRequestsPerPeer {
			continue
		}
		if peer.height < minHeight {
			continue
		}
		peer.incrPending()
		return peer
	}
	return nil
}

func (pool *BlockPool) makeNextRequester() {
	pool.mtx.Lock()
	defer pool.mtx.Unlock()

	nextHeight := pool.height + uint64(len(pool.requesters))
	request := newBPRequester(pool, nextHeight)
	request.SetLogger(pool.Logger.With("height", nextHeight))

	pool.requesters[nextHeight] = request
	pool.numPending++

	request.Start()
}

func (pool *BlockPool) sendRequest(height uint64, peerID string) {
	if !pool.IsRunning() {
		return
	}
	pool.requestsCh <- BlockRequest{height, peerID}
}

func (pool *BlockPool) sendTimeout(peerID string) {
	if !pool.IsRunning() {
		return
	}
	pool.timeoutsCh <- peerID
}

func (pool *BlockPool) debug() string {
	pool.mtx.Lock() // Lock
	defer pool.mtx.Unlock()

	str := ""
	var h uint64
	for h = pool.height; h < pool.height+uint64(len(pool.requesters)); h++ {
		if pool.requesters[h] == nil {
			str += Fmt("H(%v):X ", h)
		} else {
			str += Fmt("H(%v):", h)
			str += Fmt("B?(%v) ", pool.requesters[h].block != nil)
		}
	}
	return str
}

//-------------------------------------

type bpPeer struct {
	pool        *BlockPool
	id          string
	recvMonitor *flow.Monitor

	mtx        sync.Mutex
	height     uint64
	numPending int32
	timeout    *time.Timer
	didTimeout bool
}

func newBPPeer(pool *BlockPool, peerID string, height uint64) *bpPeer {
	peer := &bpPeer{
		pool:       pool,
		id:         peerID,
		height:     height,
		numPending: 0,
	}
	return peer
}

func (peer *bpPeer) resetMonitor() {
	peer.recvMonitor = flow.New(time.Second, time.Second*40)
	var initialValue = float64(minRecvRate) * math.E
	peer.recvMonitor.SetREMA(initialValue)
}

func (peer *bpPeer) resetTimeout() {
	if peer.timeout == nil {
		peer.timeout = time.AfterFunc(time.Second*peerTimeoutSeconds, peer.onTimeout)
	} else {
		peer.timeout.Reset(time.Second * peerTimeoutSeconds)
	}
}

func (peer *bpPeer) incrPending() {
	if peer.numPending == 0 {
		peer.resetMonitor()
		peer.resetTimeout()
	}
	peer.numPending++
}

func (peer *bpPeer) decrPending(recvSize int) {
	peer.numPending--
	if peer.numPending == 0 {
		peer.timeout.Stop()
	} else {
		peer.recvMonitor.Update(recvSize)
		peer.resetTimeout()
	}
}

func (peer *bpPeer) onTimeout() {
	peer.pool.mtx.Lock()
	defer peer.pool.mtx.Unlock()

	peer.pool.sendTimeout(peer.id)
	log.WithField("error", "onTimeout").Error("SendTimeout")
	peer.didTimeout = true
}

//-------------------------------------

type bpRequester struct {
	BaseService
	pool       *BlockPool
	height     uint64
	gotBlockCh chan struct{}
	redoCh     chan struct{}

	mtx    sync.Mutex
	peerID string
	block  *legacy.Block
}

func newBPRequester(pool *BlockPool, height uint64) *bpRequester {
	bpr := &bpRequester{
		pool:       pool,
		height:     height,
		gotBlockCh: make(chan struct{}),
		redoCh:     make(chan struct{}),

		peerID: "",
		block:  nil,
	}
	bpr.BaseService = *NewBaseService(nil, "bpRequester", bpr)
	return bpr
}

func (bpr *bpRequester) OnStart() error {
	go bpr.requestRoutine()
	return nil
}

// Returns true if the peer matches
func (bpr *bpRequester) setBlock(block *legacy.Block, peerID string) bool {
	bpr.mtx.Lock()
	if bpr.block != nil || bpr.peerID != peerID {
		bpr.mtx.Unlock()
		return false
	}
	bpr.block = block
	bpr.mtx.Unlock()

	bpr.gotBlockCh <- struct{}{}
	return true
}

func (bpr *bpRequester) getBlock() *legacy.Block {
	bpr.mtx.Lock()
	defer bpr.mtx.Unlock()
	return bpr.block
}

func (bpr *bpRequester) getPeerID() string {
	bpr.mtx.Lock()
	defer bpr.mtx.Unlock()
	return bpr.peerID
}

func (bpr *bpRequester) reset() {
	bpr.mtx.Lock()
	bpr.peerID = ""
	bpr.block = nil
	bpr.mtx.Unlock()
}

// Tells bpRequester to pick another peer and try again.
// NOTE: blocking
func (bpr *bpRequester) redo() {
	bpr.redoCh <- struct{}{}
}

// Responsible for making more requests as necessary
// Returns only when a block is found (e.g. AddBlock() is called)
func (bpr *bpRequester) requestRoutine() {
OUTER_LOOP:
	for {
		// Pick a peer to send request to.
		var peer *bpPeer = nil
	PICK_PEER_LOOP:
		for {
			if !bpr.IsRunning() || !bpr.pool.IsRunning() {
				return
			}
			peer = bpr.pool.pickIncrAvailablePeer(bpr.height)
			if peer == nil {
				time.Sleep(requestIntervalMS * time.Millisecond)
				continue PICK_PEER_LOOP
			}
			break PICK_PEER_LOOP
		}
		bpr.mtx.Lock()
		bpr.peerID = peer.id
		bpr.mtx.Unlock()

		// Send request and wait.
		bpr.pool.sendRequest(bpr.height, peer.id)
		select {
		case <-bpr.pool.Quit:
			bpr.Stop()
			return
		case <-bpr.Quit:
			return
		case <-bpr.redoCh:
			bpr.reset()
			continue OUTER_LOOP // When peer is removed
		case <-bpr.gotBlockCh:
			// We got the block, now see if it's good.
			select {
			case <-bpr.pool.Quit:
				bpr.Stop()
				return
			case <-bpr.Quit:
				return
			case <-bpr.redoCh:
				bpr.reset()
				continue OUTER_LOOP
			}
		}
	}
}

//-------------------------------------

type BlockRequest struct {
	Height uint64
	PeerID string
}
<|endoftext|>