<commit_before>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package task

import "testing"

func newTaskOrFatal(t *testing.T, title string) *Task {
	task, err := NewTask("learn Go")
	if err != nil {
		t.Fatalf("new task: %v", err)
	}
	return task
}

func TestNewTask(t *testing.T) {
	title := "learn Go"
	task := newTaskOrFatal(t, title)
	if task.Title != title {
		t.Errorf("expected title %q, got %q", title, task.Title)
	}
	if task.Done {
		t.Errorf("new task is done")
	}
}

func TestNewTaskEmptyTitle(t *testing.T) {
	_, err := NewTask("")
	if err == nil {
		t.Errorf("expected 'empty title' error, got nil")
	}
}

func TestSaveTaskAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")

	m := NewTaskManager()
	m.Save(task)

	all := m.All()
	if len(all) != 1 {
		t.Errorf("expected 1 task, got %v", len(all))
	}
	if *all[0] != *task {
		t.Errorf("expected %v, got %v", task, all[0])
	}
}

func TestSaveAndRetrieveTwoTasks(t *testing.T) {
	learnGo := newTaskOrFatal(t, "learn Go")
	learnTDD := newTaskOrFatal(t, "learn TDD")

	m := NewTaskManager()
	m.Save(learnGo)
	m.Save(learnTDD)

	all := m.All()
	if len(all) != 2 {
		t.Errorf("expected 2 tasks, got %v", len(all))
	}
	if *all[0] != *learnGo && *all[1] != *learnGo {
		t.Errorf("missing task: %v", learnGo)
	}
	if *all[0] != *learnTDD && *all[1] != *learnTDD {
		t.Errorf("missing task: %v", learnTDD)
	}
}

func TestSaveModifyAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)

	task.Done = true
	if m.All()[0].Done {
		t.Errorf("saved task wasn't done")
	}
}

func TestSaveTwiceAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)
	m.Save(task)

	all := m.All()
	if len(all) != 1 {
		t.Errorf("expected 1 task, got %v", len(all))
	}
	if *all[0] != *task {
		t.Errorf("expected task %v, got %v", task, all[0])
	}
}

func TestSaveAndFind(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)

	nt, ok := m.Find(task.ID)
	if !ok {
		t.Errorf("dIDn't find task")
	}
	if *task != *nt {
		t.Errorf("expected %v, got %v", task, nt)
	}
}
<commit_msg>Fixed typo<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package task

import "testing"

func newTaskOrFatal(t *testing.T, title string) *Task {
	task, err := NewTask("learn Go")
	if err != nil {
		t.Fatalf("new task: %v", err)
	}
	return task
}

func TestNewTask(t *testing.T) {
	title := "learn Go"
	task := newTaskOrFatal(t, title)
	if task.Title != title {
		t.Errorf("expected title %q, got %q", title, task.Title)
	}
	if task.Done {
		t.Errorf("new task is done")
	}
}

func TestNewTaskEmptyTitle(t *testing.T) {
	_, err := NewTask("")
	if err == nil {
		t.Errorf("expected 'empty title' error, got nil")
	}
}

func TestSaveTaskAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")

	m := NewTaskManager()
	m.Save(task)

	all := m.All()
	if len(all) != 1 {
		t.Errorf("expected 1 task, got %v", len(all))
	}
	if *all[0] != *task {
		t.Errorf("expected %v, got %v", task, all[0])
	}
}

func TestSaveAndRetrieveTwoTasks(t *testing.T) {
	learnGo := newTaskOrFatal(t, "learn Go")
	learnTDD := newTaskOrFatal(t, "learn TDD")

	m := NewTaskManager()
	m.Save(learnGo)
	m.Save(learnTDD)

	all := m.All()
	if len(all) != 2 {
		t.Errorf("expected 2 tasks, got %v", len(all))
	}
	if *all[0] != *learnGo && *all[1] != *learnGo {
		t.Errorf("missing task: %v", learnGo)
	}
	if *all[0] != *learnTDD && *all[1] != *learnTDD {
		t.Errorf("missing task: %v", learnTDD)
	}
}

func TestSaveModifyAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)

	task.Done = true
	if m.All()[0].Done {
		t.Errorf("saved task wasn't done")
	}
}

func TestSaveTwiceAndRetrieve(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)
	m.Save(task)

	all := m.All()
	if len(all) != 1 {
		t.Errorf("expected 1 task, got %v", len(all))
	}
	if *all[0] != *task {
		t.Errorf("expected task %v, got %v", task, all[0])
	}
}

func TestSaveAndFind(t *testing.T) {
	task := newTaskOrFatal(t, "learn Go")
	m := NewTaskManager()
	m.Save(task)

	nt, ok := m.Find(task.ID)
	if !ok {
		t.Errorf("didn't find task")
	}
	if *task != *nt {
		t.Errorf("expected %v, got %v", task, nt)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/msbranco/goconfig"
	"github.com/vaughan0/go-zmq"
	"log"
	"log/syslog"
	"os"
	"strings"
)

func main() {

	// Syslog
		syslogLog, err :=syslog.New(syslog.LOG_INFO, "turtledq/server")
		if nil != err {
			log.Fatal("Crap on a stick, could not open up syslog!")
/////////// RETURN
			return
		}

	//DEBUG
	syslogLog.Notice("=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=")
	syslogLog.Notice("TurtleDQ Server BEGIN.")

	// Config
		//DEBUG
		syslogLog.Notice("    Settings BEGIN.")

		cmdPath := os.Args[0]
		cmdPathLastSlash := strings.LastIndexFunc(cmdPath, func(c rune) bool {
			return '/' == c
		})

		cmdDirPath := cmdPath[0:cmdPathLastSlash]

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("    command path: [%v]", cmdPath)  )
		syslogLog.Notice( fmt.Sprintf("    command path last slash: [%v]", cmdPathLastSlash) )
		syslogLog.Notice( fmt.Sprintf("    command dir path: [%v]", cmdDirPath) )

		confRelativePath := "turtledq.ini"
		confPath := cmdDirPath + "/" + confRelativePath

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("    settings file relative path: [%v]", confRelativePath) )
		syslogLog.Notice( fmt.Sprintf("    settings file absolute path: [%v]", confPath) )

		//c, err := configfile.ReadConfigFile(confPath);
		c, err := goconfig.ReadConfigFile(confPath);
		if nil != err {
			errMsg := fmt.Sprintf("Error when trying to read config file: err = [%v]", err)
			syslogLog.Err(errMsg)
			log.Fatal(errMsg)
/////////// RETURN
			return
		}

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("config file: [%v]", c) )

		//DEBUG
		syslogLog.Notice("    Settings END.")

	// Go!
		go dequeue(syslogLog)
		go enqueue(syslogLog)

	// Create ZeroMQ context.
		ctx, err := zmq.NewContext()
		if err != nil {
			panic(err)
		}
		defer ctx.Close()

	// Create ZeroMQ socket (from ZeroMQ context).
		sock, err := ctx.Socket(zmq.Router)
		if err != nil {
			panic(err)
		}
		defer sock.Close()

		if err = sock.Bind("tcp://*:5555"); err != nil {
			panic(err)
		}

	// Handle input from ZeroMQ (via channels).
		chans := sock.Channels()
		defer chans.Close()

		for {
			select {

				case msg := <-chans.In():
					go func() {
						resp := handleRequest(syslogLog, msg)
						chans.Out() <- resp
					}()

				case err := <-chans.Errors():
					panic(err)

			} // select
		}

	//DEBUG
	syslogLog.Notice("TurtleDQ Server END.")
}

func handleRequest(syslogLog *syslog.Writer, msg [][]byte) [][]byte {

//@TODO
	syslogLog.Notice( fmt.Sprintf("[handleRequest] msg = [%v]", msg) )

	// Return
//@TODO
		return [][]byte{
			[]byte("apple banana cherry"),
		}
}
<commit_msg>should be more careful about what I copy and paste<commit_after>package main

import (
	"fmt"
	"github.com/msbranco/goconfig"
	"github.com/vaughan0/go-zmq"
	"log"
	"log/syslog"
	"os"
	"strings"
)

func main() {

	// Syslog
		syslogLog, err :=syslog.New(syslog.LOG_INFO, "turtledq/server")
		if nil != err {
			log.Fatal("Big problem, could not open up syslog!")
/////////// RETURN
			return
		}

	//DEBUG
	syslogLog.Notice("=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=-<>-=")
	syslogLog.Notice("TurtleDQ Server BEGIN.")

	// Config
		//DEBUG
		syslogLog.Notice("    Settings BEGIN.")

		cmdPath := os.Args[0]
		cmdPathLastSlash := strings.LastIndexFunc(cmdPath, func(c rune) bool {
			return '/' == c
		})

		cmdDirPath := cmdPath[0:cmdPathLastSlash]

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("    command path: [%v]", cmdPath)  )
		syslogLog.Notice( fmt.Sprintf("    command path last slash: [%v]", cmdPathLastSlash) )
		syslogLog.Notice( fmt.Sprintf("    command dir path: [%v]", cmdDirPath) )

		confRelativePath := "turtledq.ini"
		confPath := cmdDirPath + "/" + confRelativePath

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("    settings file relative path: [%v]", confRelativePath) )
		syslogLog.Notice( fmt.Sprintf("    settings file absolute path: [%v]", confPath) )

		//c, err := configfile.ReadConfigFile(confPath);
		c, err := goconfig.ReadConfigFile(confPath);
		if nil != err {
			errMsg := fmt.Sprintf("Error when trying to read config file: err = [%v]", err)
			syslogLog.Err(errMsg)
			log.Fatal(errMsg)
/////////// RETURN
			return
		}

		//DEBUG
		syslogLog.Notice( fmt.Sprintf("config file: [%v]", c) )

		//DEBUG
		syslogLog.Notice("    Settings END.")

	// Go!
		go dequeue(syslogLog)
		go enqueue(syslogLog)

	// Create ZeroMQ context.
		ctx, err := zmq.NewContext()
		if err != nil {
			panic(err)
		}
		defer ctx.Close()

	// Create ZeroMQ socket (from ZeroMQ context).
		sock, err := ctx.Socket(zmq.Router)
		if err != nil {
			panic(err)
		}
		defer sock.Close()

		if err = sock.Bind("tcp://*:5555"); err != nil {
			panic(err)
		}

	// Handle input from ZeroMQ (via channels).
		chans := sock.Channels()
		defer chans.Close()

		for {
			select {

				case msg := <-chans.In():
					go func() {
						resp := handleRequest(syslogLog, msg)
						chans.Out() <- resp
					}()

				case err := <-chans.Errors():
					panic(err)

			} // select
		}

	//DEBUG
	syslogLog.Notice("TurtleDQ Server END.")
}

func handleRequest(syslogLog *syslog.Writer, msg [][]byte) [][]byte {

//@TODO
	syslogLog.Notice( fmt.Sprintf("[handleRequest] msg = [%v]", msg) )

	// Return
//@TODO
		return [][]byte{
			[]byte("apple banana cherry"),
		}
}
<|endoftext|>


<commit_before>package scbe

// go standard for all the structures in the project.

type LoginResponse struct {
	Token string `json:"token"`
}

type ScbeStorageService struct {
	Id                                 string `json:"id"`
	UniqueIdentifier                   string `json:"unique_identifier"`
	Name                               string `json:"name"`
	Description                        string `json:"description"`
	Container                          string `json:"container"`
	CapabilityValues                   string `json:"capability_values"`
	Type                               string `json:"type"`
	PhysicalSize                       int    `json:""`
	Logical_size                       int    `json:"logical_size"`
	Physical_free                      int    `json:"physical_free"`
	Logical_free                       int    `json:"logical_free"`
	Total_capacity                     int    `json:"total_capacity"`
	Used_capacity                      int    `json:"used_capacity"`
	MaxResourceLogicalFree             int    `json:"max_resource_logical_free"`
	MaxResourceFreeSizeForProvisioning int    `json:"max_resource_free_size_for_provisioning"`
	NumVolumes                         int    `json:"num_volumes"`
	HasAdmin                           bool   `json:"has_admin"`
	QosMaxIops                         int    `json:"qos_max_iops"`
	QosMaxMbps                         int    `json:"qos_max_mbps"`
}

/*
Example of services response from SCBE
[
  {
    "id": "cc4c1254-d551-4a51-81f5-ffffffffffff",
    "unique_identifier": "cc4c1254-d551-4a51-81f5-ffffffffffff",
    "name": "gold",
    "description": " ",
    "container": "23c380fc-fe1e-4c02-9d1e-ffffffffffff",
    "capability_values": "",
    "type": "regular",
    "physical_size": 413457711104,
    "logical_size": 413457711104,
    "physical_free": 310093283328,
    "logical_free": 310093283328,
    "total_capacity": 413457711104,
    "used_capacity": 103364427776,
    "max_resource_logical_free": 310093283328,
    "max_resource_free_size_for_provisioning": 310093283328,
    "num_volumes": 0,
    "has_admin": true,
    "qos_max_iops": 0,
    "qos_max_mbps": 0
  }
]
*/

type ScbeResponseVolume struct {
	ScsiIdentifier     string `json:"scsi_identifier"`
	ArrayType          string `json:"array_type"`
	Array              string `json:"array"`
	ArrayName          string `json:"array_name"`
	Id                 string `json:"id"`
	PoolName           string `json:"pool_name"`
	MaxExtendable_size int    `json:"max_extendable_size"`
	ServiceCompliance  string `json:"service_compliance"`
	DomainName         string `json:"domain_name"`
	ServiceName        string `json:"service_name"`
	ContainerName      string `json:"container_name"`
	ServiceId          string `json:"service_id"`
	ContainerId        string `json:"container_id"`
	StorageModel       string `json:""`
	VolumeId           string `json:"volume_id"`
	Name               string `json:"name"`
	LogicalCapacity    int    `json:"logical_capacity"`
	PhysicalCapacity   int    `json:"physical_capacity"`
	UsedCapacity       int    `json:"used_capacity"`
	LastUpdateTime     string `json:"last_update_time"`
	IsPendingDeletion  bool   `json:"is_pending_deletion"`
	Pool               int    `json:"pool"`

	// A9000 related attrs
	Serial        string `json:"serial"`
	CgId          string `json:"cg_id"`
	Snapshot_time string `json:"snapshot_time"` //not sure about the type
	Compressed    bool   `json:"compressed"`
	//Ratio                   int `json:"ratio"` // for A9000 its int but for v9000 its string "N/A", so comment out to avoid marshaling issue
	Saving                  int    `json:"cg_id"`
	ThinProvisioningSavings string `json:"thin_provisioning_savings"`
	EstCompressionFactor    string `json:"est_compression_factor"`
	PerfClass               string `json:"perf_class"`

	// V9000 related attrs
	Status           string `json:"status"`
	CopyCount        string `json:"copy_count"`
	FcMapCount       int    `json:"fc_map_count"`
	FastWriteState   string `json:"fast_write_state"`
	OwnerType        string `json:"owner_type"`
	FlashcopyTime    string `json:"flashcopy_time"`
	ThrottleId       string `json:"throttle_id"`
	IOPsLimit        string `json:"IOPs_limit"`
	BandwidthLimitMB string `json:"bandwidth_limit_MB"`
	IoGrp            int    `json:"io_grp"`
}

/*
Example of SCBE response on A9000 volume
    {
        "scsi_identifier": "6001738CFC9035E8000000000091D081",
        "array_type": "2810XIV",
        "array": "9835-415-6013800",
        "array_name": "XIV Gen4d-67a",
        "id": "6001738CFC9035E8000000000091D081",
        "pool_name": "gold_pool_ubiquity_scbe_9.151.162.17",
        "pool_id": "5b819800004",
        "max_extendable_size": 108364427776,
        "service_compliance": "True",
        "domain_name": "ilyak_domain",
        "service_name": "gold",
        "container_name": "Default_Space",
        "service_id": "cc4c1254-d551-4a51-81f5-4732f9e0833e",
        "container_id": "23c380fc-fe1e-4c02-9d1e-0781e9442394",
        "storage_model": "FlashSystem A9000R",
        "ratio": 0, // on SVC\V9000 it returns string "N/A"
        "update_status": "",
        "volume_id": "478319e0012e",
        "name": "u_ubiquity_instance1_vol1",
        "logical_capacity": 5000000000,
        "physical_capacity": 5117050880,
        "used_capacity": 0,
        "last_update_time": "2017-06-16T11:14:16.876891",
        "is_pending_deletion": false,
        "pool": 28, // was below
    // From here its specific A9000
        "serial": "",
        "cg_id": "0",
        "snapshot_time": null,
        "compressed": true,
        "saving": 0,
        "thin_provisioning_savings": "100",
        "est_compression_factor": "1.00",
        "pool": 274,
        "perf_class": null
    // From here its specific V9000 related
        "status": "online",
        "copy_count": 1,
        "fc_map_count": 0,
        "fast_write_state": "empty",
        "owner_type": null,
        "flashcopy_time": null,
        "throttle_id": null,
        "IOPs_limit": null,
        "bandwidth_limit_MB": null,
        "io_grp": 1
    }
*/

type ScbeCreateVolumePostParams struct {
	Service  string `json:"service"`
	Name     string `json:"name"`
	Size     int    `json:"size"`
	SizeUnit string `json:"size_unit"`
}

type ScbeMapVolumePostParams struct {
	VolumeId string `json:"volume_id"`
	HostId   int    `json:"host_id"`
}

// Its the same as ScbeMapVolumePostParams but later on we may add more attr to the map struct
type ScbeUnMapVolumePostParams struct {
	VolumeId string `json:"volume_id"`
	HostId   int    `json:"host_id"`
}

type ScbeResponseMapping struct {
	Id        int    `json:"id"`
	Volume    string `json:"volume"`
	Host      int    `json:"host"`
	LunNumber int    `json:"lun_number"`
}

type ScbeResponseMappings struct {
	Mappings []ScbeResponseMapping `json:"mappings"`
}

/*
"{\"mappings\":[{\"id\":1,\"volume\":\"6001738CFC9035E8000000000091XXXX\",\"host\":2,\"lun_number\":1}]}"
*/

type ScbeVolumeInfo struct {
	Name        string
	Wwn         string
	ServiceName string
	// TODO later on we will want also size and maybe other stuff
}

type ScbeResponseHost struct {
	Id             int    `json:"id"`
	ArrayType      string `json:"array_type"`
	Array          string `json:"array"`
	HostId         string `json:"host_id"`
	Name           string `json:"name"`
	StorageCluster string `json:"storage_cluster"`
	PhysicalHost   int    `json:"physical_host"`
}
<commit_msg>#44 : V9000 unmarshel issue due to wrong type of copy_count(int instead of string)<commit_after>package scbe

// go standard for all the structures in the project.

type LoginResponse struct {
	Token string `json:"token"`
}

type ScbeStorageService struct {
	Id                                 string `json:"id"`
	UniqueIdentifier                   string `json:"unique_identifier"`
	Name                               string `json:"name"`
	Description                        string `json:"description"`
	Container                          string `json:"container"`
	CapabilityValues                   string `json:"capability_values"`
	Type                               string `json:"type"`
	PhysicalSize                       int    `json:""`
	Logical_size                       int    `json:"logical_size"`
	Physical_free                      int    `json:"physical_free"`
	Logical_free                       int    `json:"logical_free"`
	Total_capacity                     int    `json:"total_capacity"`
	Used_capacity                      int    `json:"used_capacity"`
	MaxResourceLogicalFree             int    `json:"max_resource_logical_free"`
	MaxResourceFreeSizeForProvisioning int    `json:"max_resource_free_size_for_provisioning"`
	NumVolumes                         int    `json:"num_volumes"`
	HasAdmin                           bool   `json:"has_admin"`
	QosMaxIops                         int    `json:"qos_max_iops"`
	QosMaxMbps                         int    `json:"qos_max_mbps"`
}

/*
Example of services response from SCBE
[
  {
    "id": "cc4c1254-d551-4a51-81f5-ffffffffffff",
    "unique_identifier": "cc4c1254-d551-4a51-81f5-ffffffffffff",
    "name": "gold",
    "description": " ",
    "container": "23c380fc-fe1e-4c02-9d1e-ffffffffffff",
    "capability_values": "",
    "type": "regular",
    "physical_size": 413457711104,
    "logical_size": 413457711104,
    "physical_free": 310093283328,
    "logical_free": 310093283328,
    "total_capacity": 413457711104,
    "used_capacity": 103364427776,
    "max_resource_logical_free": 310093283328,
    "max_resource_free_size_for_provisioning": 310093283328,
    "num_volumes": 0,
    "has_admin": true,
    "qos_max_iops": 0,
    "qos_max_mbps": 0
  }
]
*/

type ScbeResponseVolume struct {
	ScsiIdentifier     string `json:"scsi_identifier"`
	ArrayType          string `json:"array_type"`
	Array              string `json:"array"`
	ArrayName          string `json:"array_name"`
	Id                 string `json:"id"`
	PoolName           string `json:"pool_name"`
	MaxExtendable_size int    `json:"max_extendable_size"`
	ServiceCompliance  string `json:"service_compliance"`
	DomainName         string `json:"domain_name"`
	ServiceName        string `json:"service_name"`
	ContainerName      string `json:"container_name"`
	ServiceId          string `json:"service_id"`
	ContainerId        string `json:"container_id"`
	StorageModel       string `json:""`
	VolumeId           string `json:"volume_id"`
	Name               string `json:"name"`
	LogicalCapacity    int    `json:"logical_capacity"`
	PhysicalCapacity   int    `json:"physical_capacity"`
	UsedCapacity       int    `json:"used_capacity"`
	LastUpdateTime     string `json:"last_update_time"`
	IsPendingDeletion  bool   `json:"is_pending_deletion"`
	Pool               int    `json:"pool"`

	// A9000 related attrs
	Serial        string `json:"serial"`
	CgId          string `json:"cg_id"`
	Snapshot_time string `json:"snapshot_time"` //not sure about the type
	Compressed    bool   `json:"compressed"`
	//Ratio                   int `json:"ratio"` // for A9000 its int but for v9000 its string "N/A", so comment out to avoid marshaling issue
	Saving                  int    `json:"cg_id"`
	ThinProvisioningSavings string `json:"thin_provisioning_savings"`
	EstCompressionFactor    string `json:"est_compression_factor"`
	PerfClass               string `json:"perf_class"`

	// V9000 related attrs
	Status           string `json:"status"`
	CopyCount        int    `json:"copy_count"`
	FcMapCount       int    `json:"fc_map_count"`
	FastWriteState   string `json:"fast_write_state"`
	OwnerType        string `json:"owner_type"`
	FlashcopyTime    string `json:"flashcopy_time"`
	ThrottleId       string `json:"throttle_id"`
	IOPsLimit        string `json:"IOPs_limit"`
	BandwidthLimitMB string `json:"bandwidth_limit_MB"`
	IoGrp            int    `json:"io_grp"`
}

/*
Example of SCBE response on A9000 volume
    {
        "scsi_identifier": "6001738CFC9035E8000000000091D081",
        "array_type": "2810XIV",
        "array": "9835-415-6013800",
        "array_name": "XIV Gen4d-67a",
        "id": "6001738CFC9035E8000000000091D081",
        "pool_name": "gold_pool_ubiquity_scbe_9.151.162.17",
        "pool_id": "5b819800004",
        "max_extendable_size": 108364427776,
        "service_compliance": "True",
        "domain_name": "ilyak_domain",
        "service_name": "gold",
        "container_name": "Default_Space",
        "service_id": "cc4c1254-d551-4a51-81f5-4732f9e0833e",
        "container_id": "23c380fc-fe1e-4c02-9d1e-0781e9442394",
        "storage_model": "FlashSystem A9000R",
        "ratio": 0, // on SVC\V9000 it returns string "N/A"
        "update_status": "",
        "volume_id": "478319e0012e",
        "name": "u_ubiquity_instance1_vol1",
        "logical_capacity": 5000000000,
        "physical_capacity": 5117050880,
        "used_capacity": 0,
        "last_update_time": "2017-06-16T11:14:16.876891",
        "is_pending_deletion": false,
        "pool": 28, // was below
    // From here its specific A9000
        "serial": "",
        "cg_id": "0",
        "snapshot_time": null,
        "compressed": true,
        "saving": 0,
        "thin_provisioning_savings": "100",
        "est_compression_factor": "1.00",
        "pool": 274,
        "perf_class": null
    // From here its specific V9000 related
        "status": "online",
        "copy_count": 1,
        "fc_map_count": 0,
        "fast_write_state": "empty",
        "owner_type": null,
        "flashcopy_time": null,
        "throttle_id": null,
        "IOPs_limit": null,
        "bandwidth_limit_MB": null,
        "io_grp": 1
    }
*/

type ScbeCreateVolumePostParams struct {
	Service  string `json:"service"`
	Name     string `json:"name"`
	Size     int    `json:"size"`
	SizeUnit string `json:"size_unit"`
}

type ScbeMapVolumePostParams struct {
	VolumeId string `json:"volume_id"`
	HostId   int    `json:"host_id"`
}

// Its the same as ScbeMapVolumePostParams but later on we may add more attr to the map struct
type ScbeUnMapVolumePostParams struct {
	VolumeId string `json:"volume_id"`
	HostId   int    `json:"host_id"`
}

type ScbeResponseMapping struct {
	Id        int    `json:"id"`
	Volume    string `json:"volume"`
	Host      int    `json:"host"`
	LunNumber int    `json:"lun_number"`
}

type ScbeResponseMappings struct {
	Mappings []ScbeResponseMapping `json:"mappings"`
}

/*
"{\"mappings\":[{\"id\":1,\"volume\":\"6001738CFC9035E8000000000091XXXX\",\"host\":2,\"lun_number\":1}]}"
*/

type ScbeVolumeInfo struct {
	Name        string
	Wwn         string
	ServiceName string
	// TODO later on we will want also size and maybe other stuff
}

type ScbeResponseHost struct {
	Id             int    `json:"id"`
	ArrayType      string `json:"array_type"`
	Array          string `json:"array"`
	HostId         string `json:"host_id"`
	Name           string `json:"name"`
	StorageCluster string `json:"storage_cluster"`
	PhysicalHost   int    `json:"physical_host"`
}
<|endoftext|>


<commit_before>package vm

import (
	"github.com/fatih/structs"
	"github.com/gorilla/mux"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"unicode"
)

type response struct {
	status      int
	body        string
	contentType string
}

type request struct {
	Method           string
	Body             string
	URL              string
	Path             string
	Host             string
	Protocol         string
	Headers          map[string][]string
	ContentLength    int64
	TransferEncoding []string
}

func initSimpleServerClass(vm *VM) {
	initHTTPClass(vm)
	net := vm.loadConstant("Net", true)
	simpleServer := vm.initializeClass("SimpleServer", false)
	simpleServer.setBuiltInMethods(builtinSimpleServerInstanceMethods(), false)
	net.setClassConstant(simpleServer)

	vm.execGobyLib("net/simple_server.gb")
}

func builtinSimpleServerInstanceMethods() []*BuiltInMethodObject {
	router := mux.NewRouter()

	return []*BuiltInMethodObject{
		{
			Name: "mount",
			Fn: func(receiver Object) builtinMethodBody {
				return func(t *thread, args []Object, blockFrame *callFrame) Object {
					path := args[0].(*StringObject).value
					method := args[1].(*StringObject).value
					router.HandleFunc(path, newHandler(t, blockFrame)).Methods(method)

					return receiver
				}
			},
		},
		{
			Name: "start",
			Fn: func(receiver Object) builtinMethodBody {
				return func(t *thread, args []Object, blockFrame *callFrame) Object {
					var port string
					var serveStatic bool
					server := receiver.(*RObject)

					portVar, ok := server.InstanceVariables.get("@port")

					if !ok {
						port = "8080"
					} else {
						port = portVar.(*StringObject).value
					}

					log.Println("SimpleServer start listening on port: " + port)

					c := make(chan os.Signal, 1)
					signal.Notify(c, os.Interrupt)

					go func() {
						for range c {
							log.Println("SimpleServer gracefully stopped")
							os.Exit(0)
						}
					}()

					fileRoot, serveStatic := server.InstanceVariables.get("@file_root")

					if serveStatic {
						fr := fileRoot.(*StringObject).value
						currentDir, _ := os.Getwd()
						fp := filepath.Join(currentDir, fr)
						fs := http.FileServer(http.Dir(fp))
						http.Handle("/", fs)
					} else {
						http.Handle("/", router)
					}

					err := http.ListenAndServe(":"+port, nil)

					if err != http.ErrServerClosed { // HL
						log.Fatalf("listen: %s\n", err)
					}

					return receiver
				}
			},
		},
	}
}

func newHandler(t *thread, blockFrame *callFrame) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		// Go creates one goroutine per request, so we also need to create a new Goby thread for every request.
		thread := t.vm.newThread()
		res := httpResponseClass.initializeInstance()
		req := initRequest(t, w, r)
		result := thread.builtInMethodYield(blockFrame, req, res)

		if err, ok := result.Target.(*Error); ok {
			log.Printf("Error: %s", err.Message)
			res.instanceVariableSet("@status", t.vm.initIntegerObject(500))
		}

		thread = nil
		setupResponse(w, r, res)
	}
}

func initRequest(t *thread, w http.ResponseWriter, req *http.Request) *RObject {
	r := request{}
	reqObj := httpRequestClass.initializeInstance()

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		http.Error(w, "can't read body", http.StatusBadRequest)
		return reqObj
	}

	r.Method = req.Method
	r.Protocol = req.Proto
	r.Headers = req.Header
	r.Body = string(body)
	r.ContentLength = req.ContentLength
	r.TransferEncoding = req.TransferEncoding
	r.Host = req.Host
	r.Path = req.URL.Path
	r.URL = req.RequestURI

	m := structs.Map(r)

	for k, v := range m {
		varName := "@" + toSnakeCase(k)
		reqObj.InstanceVariables.set(varName, t.vm.initObjectFromGoType(v))
	}

	return reqObj
}

func setupResponse(w http.ResponseWriter, req *http.Request, res *RObject) {
	r := &response{}

	resStatus, ok := res.instanceVariableGet("@status")

	if ok {
		r.status = resStatus.(*IntegerObject).value
	} else {
		r.status = http.StatusOK
	}

	resBody, ok := res.instanceVariableGet("@body")

	if !ok {
		r.body = ""
	} else {
		r.body = resBody.(*StringObject).value
	}

	h, ok := res.instanceVariableGet("@headers")

	if headers, isHashObject := h.(*HashObject); ok && isHashObject {
		for k, v := range headers.Pairs {
			w.Header().Set(k, v.(*StringObject).value)
		}
	} else {
		r.contentType = "text/plain; charset=utf-8"
		w.Header().Set("Content-Type", r.contentType) // normal header
	}

	w.WriteHeader(r.status)

	io.WriteString(w, r.body)
	log.Printf("%s %s %s %d\n", req.Method, req.URL.Path, req.Proto, r.status)
}

func toSnakeCase(in string) string {
	runes := []rune(in)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}
<commit_msg>Check if file_root is nil.<commit_after>package vm

import (
	"github.com/fatih/structs"
	"github.com/gorilla/mux"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"unicode"
)

type response struct {
	status      int
	body        string
	contentType string
}

type request struct {
	Method           string
	Body             string
	URL              string
	Path             string
	Host             string
	Protocol         string
	Headers          map[string][]string
	ContentLength    int64
	TransferEncoding []string
}

func initSimpleServerClass(vm *VM) {
	initHTTPClass(vm)
	net := vm.loadConstant("Net", true)
	simpleServer := vm.initializeClass("SimpleServer", false)
	simpleServer.setBuiltInMethods(builtinSimpleServerInstanceMethods(), false)
	net.setClassConstant(simpleServer)

	vm.execGobyLib("net/simple_server.gb")
}

func builtinSimpleServerInstanceMethods() []*BuiltInMethodObject {
	router := mux.NewRouter()

	return []*BuiltInMethodObject{
		{
			Name: "mount",
			Fn: func(receiver Object) builtinMethodBody {
				return func(t *thread, args []Object, blockFrame *callFrame) Object {
					path := args[0].(*StringObject).value
					method := args[1].(*StringObject).value
					router.HandleFunc(path, newHandler(t, blockFrame)).Methods(method)

					return receiver
				}
			},
		},
		{
			Name: "start",
			Fn: func(receiver Object) builtinMethodBody {
				return func(t *thread, args []Object, blockFrame *callFrame) Object {
					var port string
					var serveStatic bool
					server := receiver.(*RObject)

					portVar, ok := server.InstanceVariables.get("@port")

					if !ok {
						port = "8080"
					} else {
						port = portVar.(*StringObject).value
					}

					log.Println("SimpleServer start listening on port: " + port)

					c := make(chan os.Signal, 1)
					signal.Notify(c, os.Interrupt)

					go func() {
						for range c {
							log.Println("SimpleServer gracefully stopped")
							os.Exit(0)
						}
					}()

					fileRoot, serveStatic := server.InstanceVariables.get("@file_root")

					if serveStatic && fileRoot.Class() != t.vm.objectClass.getClassConstant(nullClass) {
						fr := fileRoot.(*StringObject).value
						currentDir, _ := os.Getwd()
						fp := filepath.Join(currentDir, fr)
						fs := http.FileServer(http.Dir(fp))
						http.Handle("/", fs)
					} else {
						http.Handle("/", router)
					}

					err := http.ListenAndServe(":"+port, nil)

					if err != http.ErrServerClosed { // HL
						log.Fatalf("listen: %s\n", err)
					}

					return receiver
				}
			},
		},
	}
}

func newHandler(t *thread, blockFrame *callFrame) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		// Go creates one goroutine per request, so we also need to create a new Goby thread for every request.
		thread := t.vm.newThread()
		res := httpResponseClass.initializeInstance()
		req := initRequest(t, w, r)
		result := thread.builtInMethodYield(blockFrame, req, res)

		if err, ok := result.Target.(*Error); ok {
			log.Printf("Error: %s", err.Message)
			res.instanceVariableSet("@status", t.vm.initIntegerObject(500))
		}

		thread = nil
		setupResponse(w, r, res)
	}
}

func initRequest(t *thread, w http.ResponseWriter, req *http.Request) *RObject {
	r := request{}
	reqObj := httpRequestClass.initializeInstance()

	body, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Printf("Error reading body: %v", err)
		http.Error(w, "can't read body", http.StatusBadRequest)
		return reqObj
	}

	r.Method = req.Method
	r.Protocol = req.Proto
	r.Headers = req.Header
	r.Body = string(body)
	r.ContentLength = req.ContentLength
	r.TransferEncoding = req.TransferEncoding
	r.Host = req.Host
	r.Path = req.URL.Path
	r.URL = req.RequestURI

	m := structs.Map(r)

	for k, v := range m {
		varName := "@" + toSnakeCase(k)
		reqObj.InstanceVariables.set(varName, t.vm.initObjectFromGoType(v))
	}

	return reqObj
}

func setupResponse(w http.ResponseWriter, req *http.Request, res *RObject) {
	r := &response{}

	resStatus, ok := res.instanceVariableGet("@status")

	if ok {
		r.status = resStatus.(*IntegerObject).value
	} else {
		r.status = http.StatusOK
	}

	resBody, ok := res.instanceVariableGet("@body")

	if !ok {
		r.body = ""
	} else {
		r.body = resBody.(*StringObject).value
	}

	h, ok := res.instanceVariableGet("@headers")

	if headers, isHashObject := h.(*HashObject); ok && isHashObject {
		for k, v := range headers.Pairs {
			w.Header().Set(k, v.(*StringObject).value)
		}
	} else {
		r.contentType = "text/plain; charset=utf-8"
		w.Header().Set("Content-Type", r.contentType) // normal header
	}

	w.WriteHeader(r.status)

	io.WriteString(w, r.body)
	log.Printf("%s %s %s %d\n", req.Method, req.URL.Path, req.Proto, r.status)
}

func toSnakeCase(in string) string {
	runes := []rune(in)
	length := len(runes)

	var out []rune
	for i := 0; i < length; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}
<|endoftext|>


<commit_before>//Lower level stuff happens here, should not be used directly
package couchdb

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

//represents a couchdb 'connection'
type connection struct {
	url      string
	client   *http.Client
	username string
	password string
}

//Adds HTTP Basic Authentication headers to a request
func addBasicAuthHeaders(username string, password string, req *http.Request) {
	authString := []byte(username + ":" + password)
	header := "Basic " + base64.StdEncoding.EncodeToString(authString)
	req.Header.Set("Authorization", string(header))
}

//processes a request
func (conn *connection) request(method, path string,
	body io.Reader, headers map[string]string) (*http.Response, error) {
	req, err := http.NewRequest(method, conn.url+path, body)
	//set headers
	for k, v := range headers {
		req.Header.Set(k, v)
	}
	if err != nil {
		return nil, err
	}
	if conn.username != "" && conn.password != "" {
		addBasicAuthHeaders(conn.username, conn.password, req)
	}
	resp, err := conn.client.Do(req)
	if err != nil {
		return nil, err
	} else if resp.StatusCode >= 400 {
		return resp, parseError(resp)
	} else {
		return resp, nil
	}
}

type Error struct {
	StatusCode int
	URL        string
	Method     string
	ErrorCode  string //empty for HEAD requests
	Reason     string //empty for HEAD requests
}

//stringify the error
func (err *Error) Error() string {
	return fmt.Sprintf("[Error] %v %v: %v %v %v",
		err.Method, err.URL, err.StatusCode, err.ErrorCode, err.Reason)
}

//extracts rev code from header
func getRevInfo(resp *http.Response) (string, error) {
	if rev := resp.Header.Get("ETag"); rev == "" {
		return "", fmt.Errorf("CouchDB did not return rev info")
	} else {
		return rev[1 : len(rev)-1], nil
	}
}

//unmarshalls a JSON Response Body
func parseBody(resp *http.Response, o interface{}) error {
	err := json.NewDecoder(resp.Body).Decode(&o)
	if err != nil {
		resp.Body.Close()
		return err
	} else {
		return resp.Body.Close()
	}
}

//encodes a struct to JSON and returns it as a buffer
func encodeData(o interface{}) (io.Reader, error) {
	if o == nil {
		return nil, nil
	}
	buf, err := json.Marshal(&o)
	if err != nil {
		return nil, err
	} else {
		return bytes.NewReader(buf), nil
	}
}

//Parse a CouchDB error response
func parseError(resp *http.Response) error {
	var couchReply struct{ Error, Reason string }
	if resp.Request.Method != "HEAD" {
		err := parseBody(resp, couchReply)
		if err != nil {
			return fmt.Errorf("Unknown error accessing CouchDB: %v", err)
		}
	}
	return &Error{
		StatusCode: resp.StatusCode,
		URL:        resp.Request.URL.String(),
		Method:     resp.Request.Method,
		ErrorCode:  couchReply.Error,
		Reason:     couchReply.Reason,
	}
}

//smooshes url segments together
func buildString(pathSegments []string) string {
	urlString := ""
	for _, pathSegment := range pathSegments {
		urlString += "/"
		urlString += url.QueryEscape(pathSegment)
	}
	return urlString
}

//Build Url
func buildUrl(pathSegments ...string) (string, error) {
	var Url *url.URL
	urlString := buildString(pathSegments)
	Url, err := url.Parse(urlString)
	if err != nil {
		return "", err
	}
	return Url.String(), nil
}

//Build Url with query arguments
func buildParamUrl(params url.Values, pathSegments ...string) (string, error) {
	var Url *url.URL
	urlString := buildString(pathSegments)
	Url, err := url.Parse(urlString)
	if err != nil {
		return "", err
	}
	Url.RawQuery = params.Encode()

	return Url.String(), nil
}
<commit_msg>one more thing<commit_after>package couchdb

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
)

//represents a couchdb 'connection'
type connection struct {
	url      string
	client   *http.Client
	username string
	password string
}

//Adds HTTP Basic Authentication headers to a request
func addBasicAuthHeaders(username string, password string, req *http.Request) {
	authString := []byte(username + ":" + password)
	header := "Basic " + base64.StdEncoding.EncodeToString(authString)
	req.Header.Set("Authorization", string(header))
}

//processes a request
func (conn *connection) request(method, path string,
	body io.Reader, headers map[string]string) (*http.Response, error) {
	req, err := http.NewRequest(method, conn.url+path, body)
	//set headers
	for k, v := range headers {
		req.Header.Set(k, v)
	}
	if err != nil {
		return nil, err
	}
	if conn.username != "" && conn.password != "" {
		addBasicAuthHeaders(conn.username, conn.password, req)
	}
	resp, err := conn.client.Do(req)
	if err != nil {
		return nil, err
	} else if resp.StatusCode >= 400 {
		return resp, parseError(resp)
	} else {
		return resp, nil
	}
}

type Error struct {
	StatusCode int
	URL        string
	Method     string
	ErrorCode  string //empty for HEAD requests
	Reason     string //empty for HEAD requests
}

//stringify the error
func (err *Error) Error() string {
	return fmt.Sprintf("[Error] %v %v: %v %v %v",
		err.Method, err.URL, err.StatusCode, err.ErrorCode, err.Reason)
}

//extracts rev code from header
func getRevInfo(resp *http.Response) (string, error) {
	if rev := resp.Header.Get("ETag"); rev == "" {
		return "", fmt.Errorf("CouchDB did not return rev info")
	} else {
		return rev[1 : len(rev)-1], nil
	}
}

//unmarshalls a JSON Response Body
func parseBody(resp *http.Response, o interface{}) error {
	err := json.NewDecoder(resp.Body).Decode(&o)
	if err != nil {
		resp.Body.Close()
		return err
	} else {
		return resp.Body.Close()
	}
}

//encodes a struct to JSON and returns it as a buffer
func encodeData(o interface{}) (io.Reader, error) {
	if o == nil {
		return nil, nil
	}
	buf, err := json.Marshal(&o)
	if err != nil {
		return nil, err
	} else {
		return bytes.NewReader(buf), nil
	}
}

//Parse a CouchDB error response
func parseError(resp *http.Response) error {
	var couchReply struct{ Error, Reason string }
	if resp.Request.Method != "HEAD" {
		err := parseBody(resp, couchReply)
		if err != nil {
			return fmt.Errorf("Unknown error accessing CouchDB: %v", err)
		}
	}
	return &Error{
		StatusCode: resp.StatusCode,
		URL:        resp.Request.URL.String(),
		Method:     resp.Request.Method,
		ErrorCode:  couchReply.Error,
		Reason:     couchReply.Reason,
	}
}

//smooshes url segments together
func buildString(pathSegments []string) string {
	urlString := ""
	for _, pathSegment := range pathSegments {
		urlString += "/"
		urlString += url.QueryEscape(pathSegment)
	}
	return urlString
}

//Build Url
func buildUrl(pathSegments ...string) (string, error) {
	var Url *url.URL
	urlString := buildString(pathSegments)
	Url, err := url.Parse(urlString)
	if err != nil {
		return "", err
	}
	return Url.String(), nil
}

//Build Url with query arguments
func buildParamUrl(params url.Values, pathSegments ...string) (string, error) {
	var Url *url.URL
	urlString := buildString(pathSegments)
	Url, err := url.Parse(urlString)
	if err != nil {
		return "", err
	}
	Url.RawQuery = params.Encode()

	return Url.String(), nil
}
<|endoftext|>


<commit_before>package tao

import (
  "bytes"
  "log"
  "net"
  "encoding/binary"
  "sync"
  "time"
  "io"
)

const (
  NTYPE = 4
  NLEN = 4
  MAXLEN = 1 << 23  // 8M
)

type TCPConnection struct {
  netid int64
  Owner *TCPServer
  conn net.Conn
  name string
  closeOnce sync.Once
  wg *sync.WaitGroup
  timing *TimingWheel
  messageSendChan chan Message
  handlerRecvChan chan MessageHandler
  closeConnChan chan struct{}
  timeOutChan chan *OnTimeOut
  heartBeat int64
  pendingTimers []int64
  onConnect onConnectFunc
  onMessage onMessageFunc
  onClose onCloseFunc
  onError onErrorFunc
}

func ClientTCPConnection(id int64, c net.Conn, t *TimingWheel, keepAlive bool) *TCPConnection {
  tcpConn := &TCPConnection {
    netid: id,
    conn: c,
    wg: &sync.WaitGroup{},
    timing: t,
    messageSendChan: make(chan Message, 1024),
    handlerRecvChan: make(chan MessageHandler, 1024),
    closeConnChan: make(chan struct{}),
    timeOutChan: make(chan *OnTimeOut),
    heartBeat: time.Now().UnixNano(),
    pendingTimers: []int64{},
  }
  if keepAlive {
    tcpConn.activateKeepAlive()
  }
  return tcpConn
}

func ServerTCPConnection(id int64, s *TCPServer, c net.Conn, keepAlive bool) *TCPConnection {
  tcpConn := &TCPConnection {
    netid: id,
    Owner: s,
    conn: c,
    wg: &sync.WaitGroup{},
    timing: s.timing,
    messageSendChan: make(chan Message, 1024),
    handlerRecvChan: make(chan MessageHandler, 1024),
    closeConnChan: make(chan struct{}),
    timeOutChan: make(chan *OnTimeOut),
    heartBeat: time.Now().UnixNano(),
    pendingTimers: []int64{},
  }
  tcpConn.SetOnConnectCallback(s.onConnect)
  tcpConn.SetOnMessageCallback(s.onMessage)
  tcpConn.SetOnErrorCallback(s.onError)
  tcpConn.SetOnCloseCallback(s.onClose)

  if keepAlive {
    tcpConn.activateKeepAlive()
  }
  return tcpConn
}

func (client *TCPConnection)activateKeepAlive() {
  if client.isServerMode() { // server mode, check
    MessageMap.Register(HeartBeatMessage{}.MessageNumber(), UnmarshalFunctionType(UnmarshalHeartBeatMessage))
    HandlerMap.Register(HeartBeatMessage{}.MessageNumber(), NewHandlerFunctionType(NewHeartBeatMessageHandler))
    timerId := client.RunEvery(HEART_BEAT_PERIOD, func(now time.Time) {
      log.Printf("Checking client %d at %s", client.netid, time.Now())
      last := client.heartBeat
      period := HEART_BEAT_PERIOD.Nanoseconds()
      if last < now.UnixNano() - 2 * period {
        log.Printf("Client %s netid %d timeout, close it\n", client, client.netid)
        client.Close()
      }
    })
    client.pendingTimers = append(client.pendingTimers, timerId)
  } else { // client mode, send
    client.RunEvery(HEART_BEAT_PERIOD, func(now time.Time) {
      msg := HeartBeatMessage {
        Timestamp: now.UnixNano(),
      }
      log.Printf("Sending heart beat at %s, timestamp %d\n", now, msg.Timestamp)
      client.Write(msg)
    })
  }
}

func (client *TCPConnection)isServerMode() bool {
  return client.Owner != nil
}

func (client *TCPConnection)SetOnConnectCallback(cb func(*TCPConnection) bool) {
  if cb != nil {
    client.onConnect = onConnectFunc(cb)
  }
}

func (client *TCPConnection)SetOnMessageCallback(cb func(Message, *TCPConnection)) {
  if cb != nil {
    client.onMessage = onMessageFunc(cb)
  }
}

func (client *TCPConnection)SetOnErrorCallback(cb func()) {
  if cb != nil {
    client.onError = onErrorFunc(cb)
  }
}

func (client *TCPConnection)Wait() {
  client.wg.Wait()
}

func (client *TCPConnection)SetOnCloseCallback(cb func(*TCPConnection)) {
  if cb != nil {
    client.onClose = onCloseFunc(cb)
  }
}

func (client *TCPConnection)RemoteAddr() net.Addr {
  return client.conn.RemoteAddr()
}

func (client *TCPConnection)SetName(n string) {
  client.name = n
}

func (client *TCPConnection)String() string {
  return client.name
}

func (client *TCPConnection)Close() {
  client.closeOnce.Do(func() {
    close(client.closeConnChan)
    close(client.messageSendChan)
    close(client.handlerRecvChan)
    close(client.timeOutChan)
    client.conn.Close()
    if (client.onClose != nil) {
      client.onClose(client)
    }
    if client.isServerMode() {
      client.Owner.connections.Remove(client.netid)
    }

    for _, id := range client.pendingTimers {
      client.CancelTimer(id)
    }
  })
}

func (client *TCPConnection)Write(msg Message) (err error) {
  select {
  case client.messageSendChan<- msg:
    return nil
  default:
    return ErrorWouldBlock
  }
}

/* If onConnect() returns true, start three go-routines for each client:
readLoop(), writeLoop() and handleLoop() */
func (client *TCPConnection)Do() {
  if client.onConnect != nil && !client.onConnect(client) {
    log.Fatalln("Error onConnect()\n")
  }

  // start read, write and handle loop
  client.startLoop(client.readLoop)
  client.startLoop(client.writeLoop)
  client.startLoop(client.handleLoop)
}

func (client *TCPConnection)RunAt(t time.Time, cb func(t time.Time)) int64 {
  timeout := NewOnTimeOut(client.netid, cb)
  var id int64
  if client.timing != nil {
    id = client.timing.AddTimer(t, 0, timeout)
  }
  return id
}

func (client *TCPConnection)RunAfter(d time.Duration, cb func(t time.Time)) int64 {
  delay := time.Now().Add(d)
  var id int64
  if client.timing != nil {
    id = client.RunAt(delay, cb)
  }
  return id
}

func (client *TCPConnection)RunEvery(i time.Duration, cb func(t time.Time)) int64 {
  delay := time.Now().Add(i)
  timeout := NewOnTimeOut(client.netid, cb)
  var id int64
  if client.timing != nil {
    id = client.timing.AddTimer(delay, i, timeout)
  }
  return id
}

func (client *TCPConnection)CancelTimer(timerId int64) {
  client.timing.CancelTimer(timerId)
}

func (client *TCPConnection)startLoop(looper func()) {
  client.wg.Add(1)
  go func() {
    looper()
    client.wg.Done()
  }()
}

/* readLoop() blocking read from connection, deserialize bytes into message,
then find corresponding handler, put it into channel */
func (client *TCPConnection)readLoop() {
  defer func() {
    recover()
    client.Close()
  }()

  typeBytes := make([]byte, NTYPE)
  lengthBytes := make([]byte, NLEN)
  for {
    select {
    case <-client.closeConnChan:
      return

    default:
    }

    // use type-length-value format: |4 bytes|4 bytes|n bytes <= 8M|
    _, err := io.ReadFull(client.conn, typeBytes)
    if err != nil {
      log.Printf("Error: failed to read message type - %s", err)
      return
    }
    typeBuf := bytes.NewReader(typeBytes)
    var msgType int32
    if err := binary.Read(typeBuf, binary.BigEndian, &msgType); err != nil {
      log.Fatalln(err)
    }

    _, err = io.ReadFull(client.conn, lengthBytes)
    if err != nil {
      log.Printf("Error: failed to read message length - %s", err)
      return
    }
    lengthBuf := bytes.NewReader(lengthBytes)
    var msgLen uint32
    if err := binary.Read(lengthBuf, binary.BigEndian, &msgLen); err != nil {
      log.Fatalln(err)
    }
    if msgLen > MAXLEN {
      log.Printf("Error: more than 8M data: %d\n", msgLen)
      return
    }

    // read real application message
    msgBytes := make([]byte, msgLen)
    _, err = io.ReadFull(client.conn, msgBytes)
    if err != nil {
      log.Printf("Error: failed to read message value - %s", err)
      return
    }

    // deserialize message from bytes
    unmarshaler := MessageMap.get(msgType)
    if unmarshaler == nil {
      log.Printf("Error: undefined message %d\n", msgType)
      continue
    }
    var msg Message
    if msg, err = unmarshaler(msgBytes); err != nil {
      log.Printf("Error: unmarshal message %d - %s\n", msgType, err)
      continue
    }

    handlerFactory := HandlerMap.get(msgType)
    if handlerFactory == nil {
      if client.onMessage != nil {
        log.Printf("Message %d call onMessage()\n", msgType)
        client.onMessage(msg, client)
      } else {
        log.Printf("No handler or onMessage() found for message %d", msgType)
      }
      continue
    }

    // send handler to handleLoop
    handler := handlerFactory(msg)
    client.handlerRecvChan<- handler
  }
}

/* writeLoop() receive message from channel, serialize it into bytes,
then blocking write into connection */
func (client *TCPConnection)writeLoop() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case msg := <-client.messageSendChan:
      data, err := msg.MarshalBinary();
      if err != nil {
        log.Printf("Error serializing data\n")
        continue
      }
      buf := new(bytes.Buffer)
      binary.Write(buf, binary.BigEndian, msg.MessageNumber())
      binary.Write(buf, binary.BigEndian, int32(len(data)))
      binary.Write(buf, binary.BigEndian, data)
      packet := buf.Bytes()
      if _, err = client.conn.Write(packet); err != nil {
        log.Printf("Error writing data %s\n", err)
      }
    }
  }
}

/* handleLoop() handles business logic in server or client mode:
(1) server mode - put handler or timeout callback into worker go-routines
(2) client mode - run handler or timeout callback in handleLoop() go-routine */
func (client *TCPConnection)handleLoop() {
  if client.isServerMode() {
    client.handleServerMode()
  } else {
    client.handleClientMode()
  }
}

func (client *TCPConnection)handleServerMode() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case handler := <-client.handlerRecvChan:
      if !isNil(handler) {
        client.Owner.workerPool.Put(client.netid, func() {
          handler.Process(client)
        })
        // update heart beat timestamp
        client.heartBeat = time.Now().UnixNano()
      }

    case timeout := <-client.timeOutChan:
      if timeout != nil {
        client.Owner.workerPool.Put(client.netid, func() {
          timeout.Callback(time.Now())
        })
      }
    }
  }
}

func (client *TCPConnection)handleClientMode() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case handler := <-client.handlerRecvChan:
      if !isNil(handler) {
        // update heart beat timestamp
        client.heartBeat = time.Now().UnixNano()
        handler.Process(client)
      }

    case timeout := <-client.timing.TimeOutChan:
      // put callback into workers
      timeout.Callback(time.Now())
    }
  }
}
<commit_msg>bugfix nil pointer, add log for timeout<commit_after>package tao

import (
  "bytes"
  "log"
  "net"
  "encoding/binary"
  "sync"
  "time"
  "io"
)

const (
  NTYPE = 4
  NLEN = 4
  MAXLEN = 1 << 23  // 8M
)

type TCPConnection struct {
  netid int64
  Owner *TCPServer
  conn net.Conn
  name string
  closeOnce sync.Once
  wg *sync.WaitGroup
  timing *TimingWheel
  messageSendChan chan Message
  handlerRecvChan chan MessageHandler
  closeConnChan chan struct{}
  timeOutChan chan *OnTimeOut
  heartBeat int64
  pendingTimers []int64
  onConnect onConnectFunc
  onMessage onMessageFunc
  onClose onCloseFunc
  onError onErrorFunc
}

func ClientTCPConnection(id int64, c net.Conn, t *TimingWheel, keepAlive bool) *TCPConnection {
  tcpConn := &TCPConnection {
    netid: id,
    conn: c,
    wg: &sync.WaitGroup{},
    timing: t,
    messageSendChan: make(chan Message, 1024),
    handlerRecvChan: make(chan MessageHandler, 1024),
    closeConnChan: make(chan struct{}),
    timeOutChan: make(chan *OnTimeOut),
    heartBeat: time.Now().UnixNano(),
    pendingTimers: []int64{},
  }
  if keepAlive {
    tcpConn.activateKeepAlive()
  }
  return tcpConn
}

func ServerTCPConnection(id int64, s *TCPServer, c net.Conn, keepAlive bool) *TCPConnection {
  tcpConn := &TCPConnection {
    netid: id,
    Owner: s,
    conn: c,
    wg: &sync.WaitGroup{},
    timing: s.timing,
    messageSendChan: make(chan Message, 1024),
    handlerRecvChan: make(chan MessageHandler, 1024),
    closeConnChan: make(chan struct{}),
    timeOutChan: make(chan *OnTimeOut),
    heartBeat: time.Now().UnixNano(),
    pendingTimers: []int64{},
  }
  tcpConn.SetOnConnectCallback(s.onConnect)
  tcpConn.SetOnMessageCallback(s.onMessage)
  tcpConn.SetOnErrorCallback(s.onError)
  tcpConn.SetOnCloseCallback(s.onClose)

  if keepAlive {
    tcpConn.activateKeepAlive()
  }
  return tcpConn
}

func (client *TCPConnection)activateKeepAlive() {
  if client.isServerMode() { // server mode, check
    MessageMap.Register(HeartBeatMessage{}.MessageNumber(), UnmarshalFunctionType(UnmarshalHeartBeatMessage))
    HandlerMap.Register(HeartBeatMessage{}.MessageNumber(), NewHandlerFunctionType(NewHeartBeatMessageHandler))
    timerId := client.RunEvery(HEART_BEAT_PERIOD, func(now time.Time) {
      log.Printf("Checking client %d at %s", client.netid, time.Now())
      last := client.heartBeat
      period := HEART_BEAT_PERIOD.Nanoseconds()
      if last < now.UnixNano() - 2 * period {
        log.Printf("Client %s netid %d timeout, close it\n", client, client.netid)
        client.Close()
      }
    })
    client.pendingTimers = append(client.pendingTimers, timerId)
  } else { // client mode, send
    client.RunEvery(HEART_BEAT_PERIOD, func(now time.Time) {
      msg := HeartBeatMessage {
        Timestamp: now.UnixNano(),
      }
      log.Printf("Sending heart beat at %s, timestamp %d\n", now, msg.Timestamp)
      client.Write(msg)
    })
  }
}

func (client *TCPConnection)isServerMode() bool {
  return client.Owner != nil
}

func (client *TCPConnection)SetOnConnectCallback(cb func(*TCPConnection) bool) {
  if cb != nil {
    client.onConnect = onConnectFunc(cb)
  }
}

func (client *TCPConnection)SetOnMessageCallback(cb func(Message, *TCPConnection)) {
  if cb != nil {
    client.onMessage = onMessageFunc(cb)
  }
}

func (client *TCPConnection)SetOnErrorCallback(cb func()) {
  if cb != nil {
    client.onError = onErrorFunc(cb)
  }
}

func (client *TCPConnection)Wait() {
  client.wg.Wait()
}

func (client *TCPConnection)SetOnCloseCallback(cb func(*TCPConnection)) {
  if cb != nil {
    client.onClose = onCloseFunc(cb)
  }
}

func (client *TCPConnection)RemoteAddr() net.Addr {
  return client.conn.RemoteAddr()
}

func (client *TCPConnection)SetName(n string) {
  client.name = n
}

func (client *TCPConnection)String() string {
  return client.name
}

func (client *TCPConnection)Close() {
  client.closeOnce.Do(func() {
    close(client.closeConnChan)
    close(client.messageSendChan)
    close(client.handlerRecvChan)
    close(client.timeOutChan)
    client.conn.Close()
    if (client.onClose != nil) {
      client.onClose(client)
    }
    if client.isServerMode() {
      client.Owner.connections.Remove(client.netid)
    }

    for _, id := range client.pendingTimers {
      client.CancelTimer(id)
    }
  })
}

func (client *TCPConnection)Write(msg Message) (err error) {
  select {
  case client.messageSendChan<- msg:
    return nil
  default:
    return ErrorWouldBlock
  }
}

/* If onConnect() returns true, start three go-routines for each client:
readLoop(), writeLoop() and handleLoop() */
func (client *TCPConnection)Do() {
  if client.onConnect != nil && !client.onConnect(client) {
    log.Fatalln("Error onConnect()\n")
  }

  // start read, write and handle loop
  client.startLoop(client.readLoop)
  client.startLoop(client.writeLoop)
  client.startLoop(client.handleLoop)
}

func (client *TCPConnection)RunAt(t time.Time, cb func(t time.Time)) int64 {
  timeout := NewOnTimeOut(client.netid, cb)
  var id int64
  if client.timing != nil {
    id = client.timing.AddTimer(t, 0, timeout)
  }
  return id
}

func (client *TCPConnection)RunAfter(d time.Duration, cb func(t time.Time)) int64 {
  delay := time.Now().Add(d)
  var id int64
  if client.timing != nil {
    id = client.RunAt(delay, cb)
  }
  return id
}

func (client *TCPConnection)RunEvery(i time.Duration, cb func(t time.Time)) int64 {
  delay := time.Now().Add(i)
  timeout := NewOnTimeOut(client.netid, cb)
  var id int64
  if client.timing != nil {
    id = client.timing.AddTimer(delay, i, timeout)
  }
  return id
}

func (client *TCPConnection)CancelTimer(timerId int64) {
  client.timing.CancelTimer(timerId)
}

func (client *TCPConnection)startLoop(looper func()) {
  client.wg.Add(1)
  go func() {
    looper()
    client.wg.Done()
  }()
}

/* readLoop() blocking read from connection, deserialize bytes into message,
then find corresponding handler, put it into channel */
func (client *TCPConnection)readLoop() {
  defer func() {
    recover()
    client.Close()
  }()

  typeBytes := make([]byte, NTYPE)
  lengthBytes := make([]byte, NLEN)
  for {
    select {
    case <-client.closeConnChan:
      return

    default:
    }

    // use type-length-value format: |4 bytes|4 bytes|n bytes <= 8M|
    _, err := io.ReadFull(client.conn, typeBytes)
    if err != nil {
      log.Printf("Error: failed to read message type - %s", err)
      return
    }
    typeBuf := bytes.NewReader(typeBytes)
    var msgType int32
    if err := binary.Read(typeBuf, binary.BigEndian, &msgType); err != nil {
      log.Fatalln(err)
    }

    _, err = io.ReadFull(client.conn, lengthBytes)
    if err != nil {
      log.Printf("Error: failed to read message length - %s", err)
      return
    }
    lengthBuf := bytes.NewReader(lengthBytes)
    var msgLen uint32
    if err := binary.Read(lengthBuf, binary.BigEndian, &msgLen); err != nil {
      log.Fatalln(err)
    }
    if msgLen > MAXLEN {
      log.Printf("Error: more than 8M data: %d\n", msgLen)
      return
    }

    // read real application message
    msgBytes := make([]byte, msgLen)
    _, err = io.ReadFull(client.conn, msgBytes)
    if err != nil {
      log.Printf("Error: failed to read message value - %s", err)
      return
    }

    // deserialize message from bytes
    unmarshaler := MessageMap.get(msgType)
    if unmarshaler == nil {
      log.Printf("Error: undefined message %d\n", msgType)
      continue
    }
    var msg Message
    if msg, err = unmarshaler(msgBytes); err != nil {
      log.Printf("Error: unmarshal message %d - %s\n", msgType, err)
      continue
    }

    handlerFactory := HandlerMap.get(msgType)
    if handlerFactory == nil {
      if client.onMessage != nil {
        log.Printf("Message %d call onMessage()\n", msgType)
        client.onMessage(msg, client)
      } else {
        log.Printf("No handler or onMessage() found for message %d", msgType)
      }
      continue
    }

    // send handler to handleLoop
    handler := handlerFactory(msg)
    client.handlerRecvChan<- handler
  }
}

/* writeLoop() receive message from channel, serialize it into bytes,
then blocking write into connection */
func (client *TCPConnection)writeLoop() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case msg := <-client.messageSendChan:
      data, err := msg.MarshalBinary();
      if err != nil {
        log.Printf("Error serializing data\n")
        continue
      }
      buf := new(bytes.Buffer)
      binary.Write(buf, binary.BigEndian, msg.MessageNumber())
      binary.Write(buf, binary.BigEndian, int32(len(data)))
      binary.Write(buf, binary.BigEndian, data)
      packet := buf.Bytes()
      if _, err = client.conn.Write(packet); err != nil {
        log.Printf("Error writing data %s\n", err)
      }
    }
  }
}

/* handleLoop() handles business logic in server or client mode:
(1) server mode - put handler or timeout callback into worker go-routines
(2) client mode - run handler or timeout callback in handleLoop() go-routine */
func (client *TCPConnection)handleLoop() {
  if client.isServerMode() {
    client.handleServerMode()
  } else {
    client.handleClientMode()
  }
}

func (client *TCPConnection)handleServerMode() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case handler := <-client.handlerRecvChan:
      if !isNil(handler) {
        client.Owner.workerPool.Put(client.netid, func() {
          handler.Process(client)
        })
        // update heart beat timestamp
        client.heartBeat = time.Now().UnixNano()
      }

    case timeout := <-client.timeOutChan:
      if timeout != nil {
        extraData := timeout.ExtraData.(int64)
        if extraData != client.netid {
          log.Printf("[Warn] time out of %d running on client %d", extraData, client.netid)
        }
        client.Owner.workerPool.Put(client.netid, func() {
          timeout.Callback(time.Now())
        })
      }
    }
  }
}

func (client *TCPConnection)handleClientMode() {
  defer func() {
    recover()
    client.Close()
  }()

  for {
    select {
    case <-client.closeConnChan:
      return

    case handler := <-client.handlerRecvChan:
      if !isNil(handler) {
        // update heart beat timestamp
        client.heartBeat = time.Now().UnixNano()
        handler.Process(client)
      }

    case timeout := <-client.timing.TimeOutChan:
      if timeout != nil {
        extraData := timeout.ExtraData.(int64)
        if extraData != client.netid {
          log.Printf("[Warn] time out of %d running on client %d", extraData, client.netid)
        }
        timeout.Callback(time.Now())
      }
    }
  }
}
<|endoftext|>


<commit_before>package main

import (
	"net"

	"golang.org/x/crypto/ssh"
)

type connContext struct {
	ssh.ConnMetadata
	cfg            *config
	noMoreSessions bool
}

type channelContext struct {
	connContext
	channelID int
}

var channelHandlers = map[string]func(newChannel ssh.NewChannel, context channelContext) error{
	"session":      handleSessionChannel,
	"direct-tcpip": handleDirectTCPIPChannel,
}

func handleConnection(conn net.Conn, cfg *config) {
	serverConn, newChannels, requests, err := ssh.NewServerConn(conn, cfg.sshConfig)
	if err != nil {
		warningLogger.Printf("Failed to establish SSH connection: %v", err)
		conn.Close()
		return
	}
	channelsDone := []chan interface{}{}
	context := connContext{ConnMetadata: serverConn, cfg: cfg}
	defer func() {
		serverConn.Close()
		for _, channelDone := range channelsDone {
			<-channelDone
		}
		context.logEvent(connectionCloseLog{})
	}()

	context.logEvent(connectionLog{
		ClientVersion: string(serverConn.ClientVersion()),
	})

	if _, _, err := serverConn.SendRequest("hostkeys-00@openssh.com", false, createHostkeysRequestPayload(cfg.parsedHostKeys)); err != nil {
		warningLogger.Printf("Failed to send hostkeys-00@openssh.com request: %v", err)
		return
	}

	channelID := 0
	for requests != nil || newChannels != nil {
		select {
		case request, ok := <-requests:
			if !ok {
				requests = nil
				continue
			}
			context.logEvent(debugGlobalRequestLog{
				RequestType: request.Type,
				WantReply:   request.WantReply,
				Payload:     string(request.Payload),
			})
			if err := handleGlobalRequest(request, &context); err != nil {
				warningLogger.Printf("Failed to handle global request: %v", err)
				requests = nil
				continue
			}
		case newChannel, ok := <-newChannels:
			if !ok {
				newChannels = nil
				continue
			}
			context.logEvent(debugChannelLog{
				channelLog:  channelLog{ChannelID: channelID},
				ChannelType: newChannel.ChannelType(),
				ExtraData:   string(newChannel.ExtraData()),
			})
			channelType := newChannel.ChannelType()
			handler := channelHandlers[channelType]
			if handler == nil {
				warningLogger.Printf("Unsupported channel type %v", channelType)
				if err := newChannel.Reject(ssh.ConnectionFailed, "open failed"); err != nil {
					warningLogger.Printf("Failed to reject channel: %v", err)
					newChannels = nil
					continue
				}
				continue
			}
			go func(context channelContext) {
				channelDone := make(chan interface{})
				channelsDone = append(channelsDone, channelDone)
				defer func() { channelDone <- nil }()
				if err := handler(newChannel, context); err != nil {
					warningLogger.Printf("Failed to handle new channel: %v", err)
					serverConn.Close()
				}
			}(channelContext{context, channelID})
			channelID++
		}
	}
}
<commit_msg>Use waitgroups instead of empty channels to flag finished channels<commit_after>package main

import (
	"net"
	"sync"

	"golang.org/x/crypto/ssh"
)

type connContext struct {
	ssh.ConnMetadata
	cfg            *config
	noMoreSessions bool
}

type channelContext struct {
	connContext
	channelID int
}

var channelHandlers = map[string]func(newChannel ssh.NewChannel, context channelContext) error{
	"session":      handleSessionChannel,
	"direct-tcpip": handleDirectTCPIPChannel,
}

func handleConnection(conn net.Conn, cfg *config) {
	serverConn, newChannels, requests, err := ssh.NewServerConn(conn, cfg.sshConfig)
	if err != nil {
		warningLogger.Printf("Failed to establish SSH connection: %v", err)
		conn.Close()
		return
	}
	var wg sync.WaitGroup
	context := connContext{ConnMetadata: serverConn, cfg: cfg}
	defer func() {
		serverConn.Close()
		wg.Wait()
		context.logEvent(connectionCloseLog{})
	}()

	context.logEvent(connectionLog{
		ClientVersion: string(serverConn.ClientVersion()),
	})

	if _, _, err := serverConn.SendRequest("hostkeys-00@openssh.com", false, createHostkeysRequestPayload(cfg.parsedHostKeys)); err != nil {
		warningLogger.Printf("Failed to send hostkeys-00@openssh.com request: %v", err)
		return
	}

	channelID := 0
	for requests != nil || newChannels != nil {
		select {
		case request, ok := <-requests:
			if !ok {
				requests = nil
				continue
			}
			context.logEvent(debugGlobalRequestLog{
				RequestType: request.Type,
				WantReply:   request.WantReply,
				Payload:     string(request.Payload),
			})
			if err := handleGlobalRequest(request, &context); err != nil {
				warningLogger.Printf("Failed to handle global request: %v", err)
				requests = nil
				continue
			}
		case newChannel, ok := <-newChannels:
			if !ok {
				newChannels = nil
				continue
			}
			context.logEvent(debugChannelLog{
				channelLog:  channelLog{ChannelID: channelID},
				ChannelType: newChannel.ChannelType(),
				ExtraData:   string(newChannel.ExtraData()),
			})
			channelType := newChannel.ChannelType()
			handler := channelHandlers[channelType]
			if handler == nil {
				warningLogger.Printf("Unsupported channel type %v", channelType)
				if err := newChannel.Reject(ssh.ConnectionFailed, "open failed"); err != nil {
					warningLogger.Printf("Failed to reject channel: %v", err)
					newChannels = nil
					continue
				}
				continue
			}
			wg.Add(1)
			go func(context channelContext) {
				defer wg.Done()
				if err := handler(newChannel, context); err != nil {
					warningLogger.Printf("Failed to handle new channel: %v", err)
					serverConn.Close()
				}
			}(channelContext{context, channelID})
			channelID++
		}
	}
}
<|endoftext|>


<commit_before>/*
Package dexcom provides functions to access a Dexcom CGM receiver
over a USB or BLE connection.

Based on the Python version at github.com/bewest/decoding-dexcom
*/
package dexcom

import (
	"log"
)

type Connection interface {
	Send([]byte) error
	Receive([]byte) error
	Close()
}

type Cgm struct {
	conn Connection
	err  error
}

// Open first attempts to open a USB connection;
// if that fails it tries a BLE connection.
func Open() *Cgm {
	cgm := &Cgm{}
	cgm.conn, cgm.err = OpenUSB()
	if cgm.err == nil {
		return cgm
	}
	log.Println("USB:", cgm.err)
	cgm.conn, cgm.err = OpenBLE()
	return cgm
}

func (cgm *Cgm) Error() error {
	return cgm.err
}

func (cgm *Cgm) SetError(err error) {
	cgm.err = err
}
<commit_msg>Don't print error for USB device not found<commit_after>/*
Package dexcom provides functions to access a Dexcom CGM receiver
over a USB or BLE connection.

Based on the Python version at github.com/bewest/decoding-dexcom
*/
package dexcom

import (
	"log"

	"github.com/ecc1/usbserial"
)

type Connection interface {
	Send([]byte) error
	Receive([]byte) error
	Close()
}

type Cgm struct {
	conn Connection
	err  error
}

// Open first attempts to open a USB connection;
// if that fails it tries a BLE connection.
func Open() *Cgm {
	cgm := &Cgm{}
	cgm.conn, cgm.err = OpenUSB()
	if cgm.err == nil {
		return cgm
	}
	_, ok := cgm.err.(usbserial.DeviceNotFoundError)
	if !ok {
		log.Print(cgm.err)
	}
	cgm.conn, cgm.err = OpenBLE()
	return cgm
}

func (cgm *Cgm) Error() error {
	return cgm.err
}

func (cgm *Cgm) SetError(err error) {
	cgm.err = err
}
<|endoftext|>


<commit_before>package providers

import (
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/zquestz/s/launcher"
	"golang.org/x/text/language"
)

const (
	defaultLanguage = "en"
	defaultRegion   = "US"
)

var (
	enableBlacklist = false
	enableWhitelist = false
	clientLocale    = ""
	blacklist       = make(map[string]interface{})
	whitelist       = make(map[string]interface{})
)

// Provider interface provides a way to build the URI
// for each provider.
type Provider interface {
	BuildURI(string) string
	Tags() []string
}

// Providers tracks loaded providers.
var Providers map[string]Provider

func init() {
	Providers = make(map[string]Provider)
}

// AddProvider should be called within your provider's init() func.
// This will register the provider so it can be used.
func AddProvider(name string, provider Provider) {
	Providers[name] = provider
}

// SetBlacklist filters out unneeded providers.
func SetBlacklist(b []string) {
	for _, v := range b {
		blacklist[v] = nil
	}

	if len(blacklist) > 0 {
		enableBlacklist = true
	}
}

// SetWhitelist sets an exact list of supported providers.
func SetWhitelist(w []string) {
	for _, v := range w {
		whitelist[v] = nil
	}

	if len(whitelist) > 0 {
		enableWhitelist = true
	}
}

// Search builds a search URL and opens it in your browser.
func Search(binary string, p string, t string, q string, userProvider bool, outputOnly bool, verbose bool) error {
	prov, err := ExpandProvider(p)
	if err != nil {
		return err
	}

	builders := []Provider{}

	if t == "" || userProvider {
		builders = append(builders, Providers[prov])
	}

	if t != "" {
		tag, err := ExpandTag(t)
		if err != nil {
			return err
		}

		builders = append(builders, GetProvidersByTag(tag)...)
	}

	var success bool

	for _, builder := range builders {
		if builder != nil {
			url := builder.BuildURI(q)

			if verbose || outputOnly {
				fmt.Printf("%s\n", url)
			}

			if !outputOnly {
				err = launcher.OpenURI(binary, url)
				if err != nil {
					return err
				}
			}

			success = true
		}
	}

	if !success {
		return fmt.Errorf("No providers found for tag %q.", t)
	}

	return nil
}

// GetProvidersByTag gets all providers with a specific tag.
func GetProvidersByTag(tag string) []Provider {
	provs := []Provider{}

	for _, providerName := range ProviderNames(false) {
		provider := Providers[providerName]
		for _, providerTag := range provider.Tags() {
			if providerTag == tag {
				provs = append(provs, provider)
			}
		}
	}

	return provs
}

// DisplayProviders displays all the loaded providers.
func DisplayProviders(verbose bool) string {
	names := ProviderNames(verbose)

	return fmt.Sprintf("%s\n", strings.Join(names, "\n"))
}

// DisplayTags displays all the available tags.
func DisplayTags(verbose bool) string {
	names := TagNames(verbose)

	return fmt.Sprintf("%s\n", strings.Join(names, "\n"))
}

// ExpandProvider expands the passed in provider to the full value.
func ExpandProvider(provider string) (string, error) {
	names := ProviderNames(false)
	r := regexp.MustCompile(`^` + provider)

	validProviders := []string{}
	for _, n := range names {
		// Exact match returns immediately.
		if n == provider {
			return n, nil
		}

		if r.Match([]byte(n)) {
			validProviders = append(validProviders, n)
		}
	}

	switch len(validProviders) {
	case 0:
		return "", fmt.Errorf("No provider found for %q", provider)
	case 1:
		return validProviders[0], nil
	default:
		return "", fmt.Errorf("Multiple providers matched %q: %v", provider, validProviders)
	}
}

// ExpandTag expands the passed in tag to the full value.
func ExpandTag(tag string) (string, error) {
	names := TagNames(false)
	r := regexp.MustCompile(`^` + tag)

	validTags := []string{}
	for _, n := range names {
		// Exact match returns immediately.
		if n == tag {
			return n, nil
		}

		if r.Match([]byte(n)) {
			validTags = append(validTags, n)
		}
	}

	switch len(validTags) {
	case 0:
		return "", fmt.Errorf("No tag found for %q", tag)
	case 1:
		return validTags[0], nil
	default:
		return "", fmt.Errorf("Multiple tags matched %q: %v", tag, validTags)
	}
}

// ProviderNames returns a sorted slice of provider names
// applying both the whitelist and then the blacklist.
func ProviderNames(verbose bool) []string {
	names := []string{}

	for key, p := range Providers {
		if enableWhitelist {
			_, ok := whitelist[key]
			if !ok {
				continue
			}
		}

		if enableBlacklist {
			_, ok := blacklist[key]
			if ok {
				continue
			}
		}

		if verbose {
			tags := p.Tags()

			if len(tags) > 0 {
				sort.Strings(tags)
				names = append(names, fmt.Sprintf("%s (%s)", key, strings.Join(tags, ", ")))
				continue
			}
		}

		names = append(names, key)
	}

	sort.Strings(names)
	return names
}

// TagNames returns a deduped and sorted slice of tag names.
func TagNames(verbose bool) []string {
	tags := make(map[string][]string)

	for name, p := range Providers {
		for _, t := range p.Tags() {
			tags[t] = append(tags[t], name)
		}
	}

	tagList := []string{}

	for k, providers := range tags {
		if verbose {
			sort.Strings(providers)
			tagList = append(tagList, fmt.Sprintf("%s (%s)", k, strings.Join(providers, ", ")))
		} else {
			tagList = append(tagList, k)
		}
	}

	sort.Strings(tagList)

	return tagList
}

// Region returns the users region code.
// Eg. "US", "GB", etc
func Region() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultRegion
	}

	region, _ := tag.Region()

	return region.String()
}

// Language returns the users language code.
// Eg. "en", "es", etc
func Language() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultLanguage
	}

	base, _ := tag.Base()

	return base.String()
}

// SetClientLocale sets the locale of the client
// connecting to the web server.
func SetClientLocale(locale string) {
	clientLocale = locale
}

// locale gets the locale from the LANG env var if not set.
func locale() string {
	if clientLocale != "" {
		return clientLocale
	}

	lang := os.Getenv("LANG")
	if lang == "" {
		return ""
	}

	locale := strings.Split(lang, ".")[0]

	return locale
}
<commit_msg>Update --list-tags to be provider aware<commit_after>package providers

import (
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/zquestz/s/launcher"
	"golang.org/x/text/language"
)

const (
	defaultLanguage = "en"
	defaultRegion   = "US"
)

var (
	enableBlacklist = false
	enableWhitelist = false
	clientLocale    = ""
	blacklist       = make(map[string]interface{})
	whitelist       = make(map[string]interface{})
)

// Provider interface provides a way to build the URI
// for each provider.
type Provider interface {
	BuildURI(string) string
	Tags() []string
}

// Providers tracks loaded providers.
var Providers map[string]Provider

func init() {
	Providers = make(map[string]Provider)
}

// AddProvider should be called within your provider's init() func.
// This will register the provider so it can be used.
func AddProvider(name string, provider Provider) {
	Providers[name] = provider
}

// SetBlacklist filters out unneeded providers.
func SetBlacklist(b []string) {
	for _, v := range b {
		blacklist[v] = nil
	}

	if len(blacklist) > 0 {
		enableBlacklist = true
	}
}

// SetWhitelist sets an exact list of supported providers.
func SetWhitelist(w []string) {
	for _, v := range w {
		whitelist[v] = nil
	}

	if len(whitelist) > 0 {
		enableWhitelist = true
	}
}

// Search builds a search URL and opens it in your browser.
func Search(binary string, p string, t string, q string, userProvider bool, outputOnly bool, verbose bool) error {
	prov, err := ExpandProvider(p)
	if err != nil {
		return err
	}

	builders := []Provider{}

	if t == "" || userProvider {
		builders = append(builders, Providers[prov])
	}

	if t != "" {
		tag, err := ExpandTag(t)
		if err != nil {
			return err
		}

		builders = append(builders, GetProvidersByTag(tag)...)
	}

	var success bool

	for _, builder := range builders {
		if builder != nil {
			url := builder.BuildURI(q)

			if verbose || outputOnly {
				fmt.Printf("%s\n", url)
			}

			if !outputOnly {
				err = launcher.OpenURI(binary, url)
				if err != nil {
					return err
				}
			}

			success = true
		}
	}

	if !success {
		return fmt.Errorf("No providers found for tag %q.", t)
	}

	return nil
}

// GetProvidersByTag gets all providers with a specific tag.
func GetProvidersByTag(tag string) []Provider {
	provs := []Provider{}

	for _, providerName := range ProviderNames(false) {
		provider := Providers[providerName]
		for _, providerTag := range provider.Tags() {
			if providerTag == tag {
				provs = append(provs, provider)
			}
		}
	}

	return provs
}

// DisplayProviders displays all the loaded providers.
func DisplayProviders(verbose bool) string {
	names := ProviderNames(verbose)

	return fmt.Sprintf("%s\n", strings.Join(names, "\n"))
}

// DisplayTags displays all the available tags.
func DisplayTags(verbose bool) string {
	names := TagNames(verbose)

	return fmt.Sprintf("%s\n", strings.Join(names, "\n"))
}

// ExpandProvider expands the passed in provider to the full value.
func ExpandProvider(provider string) (string, error) {
	names := ProviderNames(false)
	r := regexp.MustCompile(`^` + provider)

	validProviders := []string{}
	for _, n := range names {
		// Exact match returns immediately.
		if n == provider {
			return n, nil
		}

		if r.Match([]byte(n)) {
			validProviders = append(validProviders, n)
		}
	}

	switch len(validProviders) {
	case 0:
		return "", fmt.Errorf("No provider found for %q", provider)
	case 1:
		return validProviders[0], nil
	default:
		return "", fmt.Errorf("Multiple providers matched %q: %v", provider, validProviders)
	}
}

// ExpandTag expands the passed in tag to the full value.
func ExpandTag(tag string) (string, error) {
	names := TagNames(false)
	r := regexp.MustCompile(`^` + tag)

	validTags := []string{}
	for _, n := range names {
		// Exact match returns immediately.
		if n == tag {
			return n, nil
		}

		if r.Match([]byte(n)) {
			validTags = append(validTags, n)
		}
	}

	switch len(validTags) {
	case 0:
		return "", fmt.Errorf("No tag found for %q", tag)
	case 1:
		return validTags[0], nil
	default:
		return "", fmt.Errorf("Multiple tags matched %q: %v", tag, validTags)
	}
}

// ProviderNames returns a sorted slice of provider names
// applying both the whitelist and then the blacklist.
func ProviderNames(verbose bool) []string {
	names := []string{}

	for key, p := range Providers {
		if enableWhitelist {
			_, ok := whitelist[key]
			if !ok {
				continue
			}
		}

		if enableBlacklist {
			_, ok := blacklist[key]
			if ok {
				continue
			}
		}

		if verbose {
			tags := p.Tags()

			if len(tags) > 0 {
				sort.Strings(tags)
				names = append(names, fmt.Sprintf("%s (%s)", key, strings.Join(tags, ", ")))
				continue
			}
		}

		names = append(names, key)
	}

	sort.Strings(names)
	return names
}

// TagNames returns a deduped and sorted slice of tag names.
func TagNames(verbose bool) []string {
	tags := make(map[string][]string)

	for name, p := range Providers {
		if enableWhitelist {
			_, ok := whitelist[name]
			if !ok {
				continue
			}
		}

		if enableBlacklist {
			_, ok := blacklist[name]
			if ok {
				continue
			}
		}

		for _, t := range p.Tags() {
			tags[t] = append(tags[t], name)
		}
	}

	tagList := []string{}

	for k, providers := range tags {
		if verbose {
			sort.Strings(providers)
			tagList = append(tagList, fmt.Sprintf("%s (%s)", k, strings.Join(providers, ", ")))
		} else {
			tagList = append(tagList, k)
		}
	}

	sort.Strings(tagList)

	return tagList
}

// Region returns the users region code.
// Eg. "US", "GB", etc
func Region() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultRegion
	}

	region, _ := tag.Region()

	return region.String()
}

// Language returns the users language code.
// Eg. "en", "es", etc
func Language() string {
	l := locale()

	tag, err := language.Parse(l)
	if err != nil {
		return defaultLanguage
	}

	base, _ := tag.Base()

	return base.String()
}

// SetClientLocale sets the locale of the client
// connecting to the web server.
func SetClientLocale(locale string) {
	clientLocale = locale
}

// locale gets the locale from the LANG env var if not set.
func locale() string {
	if clientLocale != "" {
		return clientLocale
	}

	lang := os.Getenv("LANG")
	if lang == "" {
		return ""
	}

	locale := strings.Split(lang, ".")[0]

	return locale
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"golang.org/x/crypto/ssh"
)

type connContext struct {
	ssh.ConnMetadata
	cfg            *config
	noMoreSessions bool
}

type channelContext struct {
	connContext
	channelID int
}

var channelHandlers = map[string]func(newChannel ssh.NewChannel, context channelContext) error{
	"session":      handleSessionChannel,
	"direct-tcpip": handleDirectTCPIPChannel,
}

var (
	tcpConnectionsMetric = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sshesame_tcp_connections_total",
		Help: "Total number of TCP connections",
	})
	activeTCPConnectionsMetric = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sshesame_active_tcp_connections",
		Help: "Number of active TCP connections",
	})
	sshConnectionsMetric = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sshesame_ssh_connections_total",
		Help: "Total number of SSH connections",
	})
	activeSSHConnectionsMetric = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sshesame_active_ssh_connections",
		Help: "Number of active SSH connections",
	})
)

func handleConnection(conn net.Conn, cfg *config) {
	tcpConnectionsMetric.Inc()
	activeTCPConnectionsMetric.Inc()
	defer activeTCPConnectionsMetric.Dec()
	serverConn, newChannels, requests, err := ssh.NewServerConn(conn, cfg.sshConfig)
	if err != nil {
		warningLogger.Printf("Failed to establish SSH connection: %v", err)
		conn.Close()
		return
	}
	sshConnectionsMetric.Inc()
	activeSSHConnectionsMetric.Inc()
	defer activeSSHConnectionsMetric.Dec()
	var channels sync.WaitGroup
	context := connContext{ConnMetadata: serverConn, cfg: cfg}
	defer func() {
		serverConn.Close()
		channels.Wait()
		context.logEvent(connectionCloseLog{})
	}()

	context.logEvent(connectionLog{
		ClientVersion: string(serverConn.ClientVersion()),
	})

	if _, _, err := serverConn.SendRequest("hostkeys-00@openssh.com", false, createHostkeysRequestPayload(cfg.parsedHostKeys)); err != nil {
		warningLogger.Printf("Failed to send hostkeys-00@openssh.com request: %v", err)
		return
	}

	channelID := 0
	for requests != nil || newChannels != nil {
		select {
		case request, ok := <-requests:
			if !ok {
				requests = nil
				continue
			}
			if err := handleGlobalRequest(request, &context); err != nil {
				warningLogger.Printf("Failed to handle global request: %v", err)
				requests = nil
				continue
			}
		case newChannel, ok := <-newChannels:
			if !ok {
				newChannels = nil
				continue
			}
			channelType := newChannel.ChannelType()
			handler := channelHandlers[channelType]
			if handler == nil {
				warningLogger.Printf("Unsupported channel type %v", channelType)
				if err := newChannel.Reject(ssh.ConnectionFailed, "open failed"); err != nil {
					warningLogger.Printf("Failed to reject channel: %v", err)
					newChannels = nil
					continue
				}
				continue
			}
			channels.Add(1)
			go func(context channelContext) {
				defer channels.Done()
				if err := handler(newChannel, context); err != nil {
					warningLogger.Printf("Failed to handle new channel: %v", err)
					serverConn.Close()
				}
			}(channelContext{context, channelID})
			channelID++
		}
	}
}
<commit_msg>Export metrics about unknown channels<commit_after>package main

import (
	"net"
	"sync"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
	"golang.org/x/crypto/ssh"
)

type connContext struct {
	ssh.ConnMetadata
	cfg            *config
	noMoreSessions bool
}

type channelContext struct {
	connContext
	channelID int
}

var channelHandlers = map[string]func(newChannel ssh.NewChannel, context channelContext) error{
	"session":      handleSessionChannel,
	"direct-tcpip": handleDirectTCPIPChannel,
}

var (
	tcpConnectionsMetric = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sshesame_tcp_connections_total",
		Help: "Total number of TCP connections",
	})
	activeTCPConnectionsMetric = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sshesame_active_tcp_connections",
		Help: "Number of active TCP connections",
	})
	sshConnectionsMetric = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sshesame_ssh_connections_total",
		Help: "Total number of SSH connections",
	})
	activeSSHConnectionsMetric = promauto.NewGauge(prometheus.GaugeOpts{
		Name: "sshesame_active_ssh_connections",
		Help: "Number of active SSH connections",
	})
	unknownChannelsMetric = promauto.NewCounter(prometheus.CounterOpts{
		Name: "sshesame_unknown_channels_total",
		Help: "Total number of unknown channels",
	})
)

func handleConnection(conn net.Conn, cfg *config) {
	tcpConnectionsMetric.Inc()
	activeTCPConnectionsMetric.Inc()
	defer activeTCPConnectionsMetric.Dec()
	serverConn, newChannels, requests, err := ssh.NewServerConn(conn, cfg.sshConfig)
	if err != nil {
		warningLogger.Printf("Failed to establish SSH connection: %v", err)
		conn.Close()
		return
	}
	sshConnectionsMetric.Inc()
	activeSSHConnectionsMetric.Inc()
	defer activeSSHConnectionsMetric.Dec()
	var channels sync.WaitGroup
	context := connContext{ConnMetadata: serverConn, cfg: cfg}
	defer func() {
		serverConn.Close()
		channels.Wait()
		context.logEvent(connectionCloseLog{})
	}()

	context.logEvent(connectionLog{
		ClientVersion: string(serverConn.ClientVersion()),
	})

	if _, _, err := serverConn.SendRequest("hostkeys-00@openssh.com", false, createHostkeysRequestPayload(cfg.parsedHostKeys)); err != nil {
		warningLogger.Printf("Failed to send hostkeys-00@openssh.com request: %v", err)
		return
	}

	channelID := 0
	for requests != nil || newChannels != nil {
		select {
		case request, ok := <-requests:
			if !ok {
				requests = nil
				continue
			}
			if err := handleGlobalRequest(request, &context); err != nil {
				warningLogger.Printf("Failed to handle global request: %v", err)
				requests = nil
				continue
			}
		case newChannel, ok := <-newChannels:
			if !ok {
				newChannels = nil
				continue
			}
			channelType := newChannel.ChannelType()
			handler := channelHandlers[channelType]
			if handler == nil {
				unknownChannelsMetric.Inc()
				warningLogger.Printf("Unsupported channel type %v", channelType)
				if err := newChannel.Reject(ssh.ConnectionFailed, "open failed"); err != nil {
					warningLogger.Printf("Failed to reject channel: %v", err)
					newChannels = nil
					continue
				}
				continue
			}
			channels.Add(1)
			go func(context channelContext) {
				defer channels.Done()
				if err := handler(newChannel, context); err != nil {
					warningLogger.Printf("Failed to handle new channel: %v", err)
					serverConn.Close()
				}
			}(channelContext{context, channelID})
			channelID++
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	a "github.com/nak1114/goutil/assert"
	"github.com/nak1114/goutil/cp"
)

func TestMakeExecFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	envShell = "bash"
	envCommand = "cmd.bat"
	cmd := "cmd"
	fname := "./misc/test/uexec/MakeExecFile_cfg.txt"
	envShell = "bash"
	if runtime.GOOS == `windows` {
		envShell = "windows"
	}
	ofile := filepath.Join(envHome, "tmp", envCommand)
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeExecFile_" + envShell + ".txt")

	a.Set(t, "MakeExecFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	m := NewConfig(fname)
	MakeExecFile(&m, cmd, fname)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)
}
func TestMakeShimsFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	envShell = "bash"
	envExt = ""
	cmd := "hoge"
	ofile := filepath.Join(envHome, "shims", cmd+envExt)
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeShimsFile.txt")

	a.Set(t, "MakeShimFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	MakeShimsFile(cmd)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)

}

func TestMakeExecFileSystem(t *testing.T) {
	envHome = "./misc/tmp/item"
	envCommand = "fuga.bat"
	cmd := "fuga"
	ofile := filepath.Join(envHome, "tmp", envCommand)
	plist := []string{
		"/bin",
		filepath.Join(envHome, "shims"),
		"/sbin",
	}
	os.Setenv("PATH", strings.Join(plist, string(os.PathListSeparator)))
	envShell = "bash"
	if runtime.GOOS == `windows` {
		envShell = "windows"
	}
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeExecFileSystem_" + envShell + ".txt")

	a.Set(t, "MakeExecFileSystem")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	MakeExecFileSystem(cmd)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)

}

func TestMakeArgsFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	ofile := filepath.Join(envHome, "files", "__args__")

	a.Set(t, "MakeArgsFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	os.Args = []string{"dcenv", "install", "--list", "hoge fuga"}
	MakeArgsFile(2)
	a.FileContent(ofile, ` --list "hoge fuga"`)

	os.RemoveAll(envHome)
}
<commit_msg>Add test code.<commit_after>package main

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"

	a "github.com/nak1114/goutil/assert"
	"github.com/nak1114/goutil/cp"
)

func TestShowExecFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	envShell = "bash"
	envCommand = "cmd.bat"
	cmd := "cmd"
	fname := "./misc/test/uexec/MakeExecFile_cfg.txt"
	envShell = "bash"
	if runtime.GOOS == `windows` {
		envShell = "windows"
	}
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeExecFile_" + envShell + ".txt")

	a.Set(t, "ShowExecFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	m := NewConfig(fname)
	bufr, _ := a.StubIO("", func() {
		ShowExecFile(&m, cmd, fname)
	})
	a.Eq(bufr, string(buf))

	os.RemoveAll(envHome)
}

func TestMakeExecFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	envShell = "bash"
	envCommand = "cmd.bat"
	cmd := "cmd"
	fname := "./misc/test/uexec/MakeExecFile_cfg.txt"
	envShell = "bash"
	if runtime.GOOS == `windows` {
		envShell = "windows"
	}
	ofile := filepath.Join(envHome, "tmp", envCommand)
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeExecFile_" + envShell + ".txt")

	a.Set(t, "MakeExecFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	m := NewConfig(fname)
	MakeExecFile(&m, cmd, fname)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)
}

func TestMakeShimsFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	envShell = "bash"
	envExt = ""
	cmd := "hoge"
	ofile := filepath.Join(envHome, "shims", cmd+envExt)
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeShimsFile.txt")

	a.Set(t, "MakeShimFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	MakeShimsFile(cmd)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)

}

func TestMakeExecFileSystem(t *testing.T) {
	envHome = "./misc/tmp/item"
	envCommand = "fuga.bat"
	cmd := "fuga"
	ofile := filepath.Join(envHome, "tmp", envCommand)
	plist := []string{
		"/bin",
		filepath.Join(envHome, "shims"),
		"/sbin",
	}
	os.Setenv("PATH", strings.Join(plist, string(os.PathListSeparator)))
	envShell = "bash"
	if runtime.GOOS == `windows` {
		envShell = "windows"
	}
	buf, _ := ioutil.ReadFile("./misc/test/uexec/MakeExecFileSystem_" + envShell + ".txt")

	a.Set(t, "MakeExecFileSystem")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	MakeExecFileSystem(cmd)
	a.FileContent(ofile, string(buf))

	os.RemoveAll(envHome)

}

func TestMakeArgsFile(t *testing.T) {
	envHome = "./misc/tmp/item"
	ofile := filepath.Join(envHome, "files", "__args__")

	a.Set(t, "MakeArgsFile")
	os.RemoveAll(envHome)
	cp.Dir("./misc/item", envHome)

	os.Args = []string{"dcenv", "install", "--list", "hoge fuga"}
	MakeArgsFile(2)
	a.FileContent(ofile, ` --list "hoge fuga"`)

	os.RemoveAll(envHome)
}
<|endoftext|>


<commit_before>package main

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

	"cloud.google.com/go/storage"
	hotmaze "github.com/Deleplace/hot-maze/B3"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
)

const (
	projectID               = "hot-maze"
	backendBaseURL          = "https://hot-maze-udtcugbenq-uc.a.run.app"
	storageServiceAccountID = "ephemeral-storage@hot-maze.iam.gserviceaccount.com"
	bucket                  = "hot-maze.appspot.com"
	fileDeleteAfter         = 9 * time.Minute
)

func main() {
	ctx := context.Background()

	log.Println("GOOGLE_APPLICATION_CREDENTIALS =", os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))
	// log.Println(os.Environ())
	cred, errCred := google.FindDefaultCredentials(ctx)
	log.Println("FindDefaultCredentials =", string(cred.JSON), errCred)

	storageClient, err := storage.NewClient(ctx)
	if err != nil {
		// log.Fatal("Couldn't create Storage client:", err)
		log.Println("Couldn't create Storage client:", err)
	}
	sa, errSA := storageClient.ServiceAccount(ctx, "hot-maze")
	log.Println("storageClient.ServiceAccount is", sa, errSA)

	storagePrivateKey, errSecret := hotmaze.AccessSecretVersion("projects/230384242501/secrets/B3-storage-private-key/versions/latest")
	if errSecret != nil {
		// log.Fatal("Couldn't read Storage service account private key:", err)
		log.Println("Couldn't read Storage service account private key:", errSecret)
	}

	server := hotmaze.Server{
		GCPProjectID:        projectID,
		BackendBaseURL:      backendBaseURL,
		StorageClient:       storageClient,
		StorageAccountID:    storageServiceAccountID,
		StoragePrivateKey:   storagePrivateKey,
		StorageBucket:       bucket,
		StorageFileTTL:      fileDeleteAfter,
		CloudTasksQueuePath: "projects/hot-maze/locations/us-central1/queues/b3-file-expiry",
	}
	server.RegisterHandlers()

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	log.Printf("Listening on port %s", port)
	err = http.ListenAndServe(fmt.Sprintf(":%s", port), nil)
	log.Fatal(err)
}
<commit_msg>B3: fix NPE.<commit_after>package main

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

	"cloud.google.com/go/storage"
	hotmaze "github.com/Deleplace/hot-maze/B3"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
)

const (
	projectID               = "hot-maze"
	backendBaseURL          = "https://hot-maze-udtcugbenq-uc.a.run.app"
	storageServiceAccountID = "ephemeral-storage@hot-maze.iam.gserviceaccount.com"
	bucket                  = "hot-maze.appspot.com"
	fileDeleteAfter         = 9 * time.Minute
)

func main() {
	ctx := context.Background()

	log.Println("GOOGLE_APPLICATION_CREDENTIALS =", os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"))
	// log.Println(os.Environ())
	cred, errCred := google.FindDefaultCredentials(ctx)
	log.Println("errCred =", errCred)
	if cred != nil {
		log.Println("FindDefaultCredentials =", string(cred.JSON))
	}

	storageClient, err := storage.NewClient(ctx)
	if err != nil {
		// log.Fatal("Couldn't create Storage client:", err)
		log.Println("Couldn't create Storage client:", err)
	}
	sa, errSA := storageClient.ServiceAccount(ctx, "hot-maze")
	log.Println("storageClient.ServiceAccount is", sa, errSA)

	storagePrivateKey, errSecret := hotmaze.AccessSecretVersion("projects/230384242501/secrets/B3-storage-private-key/versions/latest")
	if errSecret != nil {
		// log.Fatal("Couldn't read Storage service account private key:", err)
		log.Println("Couldn't read Storage service account private key:", errSecret)
	}

	server := hotmaze.Server{
		GCPProjectID:        projectID,
		BackendBaseURL:      backendBaseURL,
		StorageClient:       storageClient,
		StorageAccountID:    storageServiceAccountID,
		StoragePrivateKey:   storagePrivateKey,
		StorageBucket:       bucket,
		StorageFileTTL:      fileDeleteAfter,
		CloudTasksQueuePath: "projects/hot-maze/locations/us-central1/queues/b3-file-expiry",
	}
	server.RegisterHandlers()

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	log.Printf("Listening on port %s", port)
	err = http.ListenAndServe(fmt.Sprintf(":%s", port), nil)
	log.Fatal(err)
}
<|endoftext|>