<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/Nitro/sidecar-executor/container"
	"github.com/fsouza/go-dockerclient"
	log "github.com/sirupsen/logrus"
)

// copyLogs will copy the Docker container logs to stdout and stderr so we can
// capture some failure information in the Mesos logs. Then tooling can fetch
// crash info from the Mesos API.
func (exec *sidecarExecutor) copyLogs(containerId string) {
	startTimeEpoch := time.Now().UTC().Add(0 - exec.config.LogsSince).Unix()

	container.GetLogs(
		exec.client, containerId, startTimeEpoch, os.Stdout, os.Stderr,
	)
}

// handleContainerLogs will, if configured to do it, watch and relay container
// logs to syslog.
func (exec *sidecarExecutor) handleContainerLogs(containerId string,
	labels map[string]string) {

	if exec.config.RelaySyslog {
		var output io.Writer
		if exec.config.ContainerLogsStdout {
			output = os.Stdout
		} else {
			output = ioutil.Discard
		}

		exec.logsQuitChan = make(chan struct{})
		go exec.relayLogs(exec.logsQuitChan, containerId, labels, output)
	}
}

// getMasterHostname talks to the local worker endpoint and discovers the
// Mesos master hostname.
func (exec *sidecarExecutor) getMasterHostname() (string, error) {
	envEndpoint := os.Getenv("MESOS_AGENT_ENDPOINT")

	if len(envEndpoint) < 1 { // Did we get anything in the env var?
		return "", fmt.Errorf("Can't get MESOS_AGENT_ENDPOINT from env! Won't provide Sidecar seeds.")
	}
	localEndpoint := "http://" + envEndpoint + "/state"

	localStruct := struct {
		MasterHostname string `json:"master_hostname"`
	}{}

	// Let's find out the Mesos master's hostname
	resp, err := exec.fetcher.Get(localEndpoint)
	if err != nil {
		return "", fmt.Errorf("Unable to fetch Mesos master info from worker endpoint: %s", err)
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Error reading response body from Mesos worker! '%s'", err)
	}

	err = json.Unmarshal(body, &localStruct)
	if err != nil {
		return "", fmt.Errorf("Error parsing response body from Mesos worker! '%s'", err)
	}

	return localStruct.MasterHostname, nil
}

// getWorkerHostnames returns a slice of all the current worker hostnames
func (exec *sidecarExecutor) getWorkerHostnames(masterHostname string) ([]string, error) {
	masterAddr := masterHostname
	if exec.config.MesosMasterPort != "" {
		masterAddr += ":" + exec.config.MesosMasterPort
	}
	masterEndpoint := "http://" + masterAddr + "/slaves"

	type workersStruct struct {
		Hostname string `json:"hostname"`
	}

	masterStruct := struct {
		Slaves []workersStruct `json:"slaves"`
	}{}

	// Let's find out the Mesos master's hostname
	resp, err := exec.fetcher.Get(masterEndpoint)
	if err != nil {
		return nil, fmt.Errorf("Unable to fetch info from master endpoint: %s", err)
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Error reading response body from Mesos master! '%s'", err)
	}

	err = json.Unmarshal(body, &masterStruct)
	if err != nil {
		return nil, fmt.Errorf("Error parsing response body from Mesos master! '%s'", err)
	}

	var workers []string
	for _, worker := range masterStruct.Slaves {
		workers = append(workers, worker.Hostname)
	}

	return workers, nil
}

// addSidecarSeeds mutates the passed slice and inserts an env var formatted
// string (FOO=BAR_1) containing the list of Sidecar seeds that should be
// used to bootstrap a Sidecar instance.
func (exec *sidecarExecutor) addSidecarSeeds(envVars []string) []string {
	masterHostname, err := exec.getMasterHostname()
	if err != nil {
		log.Error(err.Error())
		return envVars
	}

	workerNames, err := exec.getWorkerHostnames(masterHostname)
	if err != nil {
		log.Error(err.Error())
		return envVars
	}

	return append(envVars, "SIDECAR_SEEDS="+strings.Join(workerNames, ","))
}

// notifyDrain instructs Sidecar to set the current service's status to DRAINING
func (exec *sidecarExecutor) notifyDrain() {
	// Check if draining is required
	if !shouldCheckSidecar(exec.containerConfig) ||
		exec.config.SidecarDrainingDuration == 0 {
		return
	}

	// NB: Unfortunately, since exec.config.SidecarUrl points to
	// `state.json`, we need to extract the Host from it first.
	sidecarUrl, err := url.Parse(exec.config.SidecarUrl)
	if err != nil {
		log.Errorf("Error parsing Sidercar URL: %s", err)
		return
	}

	if exec.containerID == "" {
		log.Error("Attempted to drain service with empty container ID")
		return
	}

	// URL.Host contains the port as well, if present
	sidecarDrainServiceUrl := url.URL{
		Scheme: sidecarUrl.Scheme,
		Host:   sidecarUrl.Host,
		Path:   fmt.Sprintf("/api/services/%s/drain", exec.containerID[:12]),
	}

	drainer := func() (int, error) {
		resp, err := exec.fetcher.Post(sidecarDrainServiceUrl.String(), "", nil)
		if err != nil {
			return 0, err
		}

		defer resp.Body.Close()

		return resp.StatusCode, nil
	}

	log.Warnf("Setting service ID %q status to DRAINING in Sidecar", exec.containerID[:12])

	// Bridge the watcher waitgroup to a channel
	watcherDoneChan := make(chan struct{})
	go func() {
		exec.watcherWg.Wait()
		close(watcherDoneChan)
	}()

RETRIES:
	// Try several times to instruct Sidecar to set this service to DRAINING
	for i := 0; i <= exec.config.SidecarRetryCount; i++ {
		status, err := drainer()

		if err == nil && status == 202 {
			break
		}

		log.Warnf("Failed %d attempts to set service to DRAINING in Sidecar!", i+1)

		select {
		case <-watcherDoneChan:
			break RETRIES
		default:
		}

		time.Sleep(exec.config.SidecarRetryDelay)
	}
	ticker := time.NewTicker(exec.config.SidecarDrainingDuration)
	defer ticker.Stop()
	select {
	case <-ticker.C:
		// Finished waiting SidecarDrainingDuration
	case <-watcherDoneChan:
		// Bail out early if the watcher exits in the mean time
	}
}

// Check if it should check Sidecar status, assuming enabled by default
func shouldCheckSidecar(containerConfig *docker.CreateContainerOptions) bool {
	value, ok := containerConfig.Config.Labels["SidecarDiscover"]
	if !ok {
		return true
	}

	if enabled, err := strconv.ParseBool(value); err == nil {
		return enabled
	}

	return true
}
<commit_msg>Allow RelaySyslogStartupOnly to stand on its own<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/Nitro/sidecar-executor/container"
	"github.com/fsouza/go-dockerclient"
	log "github.com/sirupsen/logrus"
)

// copyLogs will copy the Docker container logs to stdout and stderr so we can
// capture some failure information in the Mesos logs. Then tooling can fetch
// crash info from the Mesos API.
func (exec *sidecarExecutor) copyLogs(containerId string) {
	startTimeEpoch := time.Now().UTC().Add(0 - exec.config.LogsSince).Unix()

	container.GetLogs(
		exec.client, containerId, startTimeEpoch, os.Stdout, os.Stderr,
	)
}

// handleContainerLogs will, if configured to do it, watch and relay container
// logs to syslog.
func (exec *sidecarExecutor) handleContainerLogs(containerId string,
	labels map[string]string) {

	if exec.config.RelaySyslog || exec.config.RelaySyslogStartupOnly {
		var output io.Writer
		if exec.config.ContainerLogsStdout {
			output = os.Stdout
		} else {
			output = ioutil.Discard
		}

		exec.logsQuitChan = make(chan struct{})
		go exec.relayLogs(exec.logsQuitChan, containerId, labels, output)
	}
}

// getMasterHostname talks to the local worker endpoint and discovers the
// Mesos master hostname.
func (exec *sidecarExecutor) getMasterHostname() (string, error) {
	envEndpoint := os.Getenv("MESOS_AGENT_ENDPOINT")

	if len(envEndpoint) < 1 { // Did we get anything in the env var?
		return "", fmt.Errorf("Can't get MESOS_AGENT_ENDPOINT from env! Won't provide Sidecar seeds.")
	}
	localEndpoint := "http://" + envEndpoint + "/state"

	localStruct := struct {
		MasterHostname string `json:"master_hostname"`
	}{}

	// Let's find out the Mesos master's hostname
	resp, err := exec.fetcher.Get(localEndpoint)
	if err != nil {
		return "", fmt.Errorf("Unable to fetch Mesos master info from worker endpoint: %s", err)
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Error reading response body from Mesos worker! '%s'", err)
	}

	err = json.Unmarshal(body, &localStruct)
	if err != nil {
		return "", fmt.Errorf("Error parsing response body from Mesos worker! '%s'", err)
	}

	return localStruct.MasterHostname, nil
}

// getWorkerHostnames returns a slice of all the current worker hostnames
func (exec *sidecarExecutor) getWorkerHostnames(masterHostname string) ([]string, error) {
	masterAddr := masterHostname
	if exec.config.MesosMasterPort != "" {
		masterAddr += ":" + exec.config.MesosMasterPort
	}
	masterEndpoint := "http://" + masterAddr + "/slaves"

	type workersStruct struct {
		Hostname string `json:"hostname"`
	}

	masterStruct := struct {
		Slaves []workersStruct `json:"slaves"`
	}{}

	// Let's find out the Mesos master's hostname
	resp, err := exec.fetcher.Get(masterEndpoint)
	if err != nil {
		return nil, fmt.Errorf("Unable to fetch info from master endpoint: %s", err)
	}
	if resp.Body != nil {
		defer resp.Body.Close()
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Error reading response body from Mesos master! '%s'", err)
	}

	err = json.Unmarshal(body, &masterStruct)
	if err != nil {
		return nil, fmt.Errorf("Error parsing response body from Mesos master! '%s'", err)
	}

	var workers []string
	for _, worker := range masterStruct.Slaves {
		workers = append(workers, worker.Hostname)
	}

	return workers, nil
}

// addSidecarSeeds mutates the passed slice and inserts an env var formatted
// string (FOO=BAR_1) containing the list of Sidecar seeds that should be
// used to bootstrap a Sidecar instance.
func (exec *sidecarExecutor) addSidecarSeeds(envVars []string) []string {
	masterHostname, err := exec.getMasterHostname()
	if err != nil {
		log.Error(err.Error())
		return envVars
	}

	workerNames, err := exec.getWorkerHostnames(masterHostname)
	if err != nil {
		log.Error(err.Error())
		return envVars
	}

	return append(envVars, "SIDECAR_SEEDS="+strings.Join(workerNames, ","))
}

// notifyDrain instructs Sidecar to set the current service's status to DRAINING
func (exec *sidecarExecutor) notifyDrain() {
	// Check if draining is required
	if !shouldCheckSidecar(exec.containerConfig) ||
		exec.config.SidecarDrainingDuration == 0 {
		return
	}

	// NB: Unfortunately, since exec.config.SidecarUrl points to
	// `state.json`, we need to extract the Host from it first.
	sidecarUrl, err := url.Parse(exec.config.SidecarUrl)
	if err != nil {
		log.Errorf("Error parsing Sidercar URL: %s", err)
		return
	}

	if exec.containerID == "" {
		log.Error("Attempted to drain service with empty container ID")
		return
	}

	// URL.Host contains the port as well, if present
	sidecarDrainServiceUrl := url.URL{
		Scheme: sidecarUrl.Scheme,
		Host:   sidecarUrl.Host,
		Path:   fmt.Sprintf("/api/services/%s/drain", exec.containerID[:12]),
	}

	drainer := func() (int, error) {
		resp, err := exec.fetcher.Post(sidecarDrainServiceUrl.String(), "", nil)
		if err != nil {
			return 0, err
		}

		defer resp.Body.Close()

		return resp.StatusCode, nil
	}

	log.Warnf("Setting service ID %q status to DRAINING in Sidecar", exec.containerID[:12])

	// Bridge the watcher waitgroup to a channel
	watcherDoneChan := make(chan struct{})
	go func() {
		exec.watcherWg.Wait()
		close(watcherDoneChan)
	}()

RETRIES:
	// Try several times to instruct Sidecar to set this service to DRAINING
	for i := 0; i <= exec.config.SidecarRetryCount; i++ {
		status, err := drainer()

		if err == nil && status == 202 {
			break
		}

		log.Warnf("Failed %d attempts to set service to DRAINING in Sidecar!", i+1)

		select {
		case <-watcherDoneChan:
			break RETRIES
		default:
		}

		time.Sleep(exec.config.SidecarRetryDelay)
	}
	ticker := time.NewTicker(exec.config.SidecarDrainingDuration)
	defer ticker.Stop()
	select {
	case <-ticker.C:
		// Finished waiting SidecarDrainingDuration
	case <-watcherDoneChan:
		// Bail out early if the watcher exits in the mean time
	}
}

// Check if it should check Sidecar status, assuming enabled by default
func shouldCheckSidecar(containerConfig *docker.CreateContainerOptions) bool {
	value, ok := containerConfig.Config.Labels["SidecarDiscover"]
	if !ok {
		return true
	}

	if enabled, err := strconv.ParseBool(value); err == nil {
		return enabled
	}

	return true
}
<|endoftext|>


<commit_before>package paystack

import (
	"fmt"
	"net/url"
)

// TransferService handles operations related to the transfer
// For more details see https://developers.paystack.co/v1.0/reference#create-transfer
type TransferService service

// TransferRequest represents a request to create a transfer.
type TransferRequest struct {
	Source    string  `json:"source,omitempty"`
	Amount    float32 `json:"amount,omitempty"`
	Currency  string  `json:"currency,omitempty"`
	Reason    string  `json:"reason,omitempty"`
	Recipient string  `json:"recipient,omitempty"`
}

// Transfer is the resource representing your Paystack transfer.
// For more details see https://developers.paystack.co/v1.0/reference#initiate-transfer
type Transfer struct {
	ID           int     `json:"id,omitempty"`
	CreatedAt    string  `json:"createdAt,omitempty"`
	UpdatedAt    string  `json:"updatedAt,omitempty"`
	Domain       string  `json:"domain,omitempty"`
	Integration  int     `json:"integration,omitempty"`
	Source       string  `json:"source,omitempty"`
	Amount       float32 `json:"amount,omitempty"`
	Currency     string  `json:"currency,omitempty"`
	Reason       string  `json:"reason,omitempty"`
	TransferCode string  `json:"transfer_code,omitempty"`
	// Initiate returns recipient ID as recipient value, Fetch returns recipient object
	Recipient interface{} `json:"recipient,omitempty"`
	Status    string      `json:"status,omitempty"`
	// confirm types for source_details and failures
	SourceDetails interface{} `json:"source_details,omitempty"`
	Failures      interface{} `json:"failures,omitempty"`
	TransferredAt string      `json:"transferred_at,omitempty"`
	TitanCode     string      `json:"titan_code,omitempty"`
}

// TransferRecipient represents a Paystack transfer recipient
// For more details see https://developers.paystack.co/v1.0/reference#create-transfer-recipient
type TransferRecipient struct {
	ID            int                    `json:"id,omitempty"`
	CreatedAt     string                 `json:"createdAt,omitempty"`
	UpdatedAt     string                 `json:"updatedAt,omitempty"`
	Type          string                 `json:",omitempty"`
	Name          string                 `json:"name,omitempty"`
	Metadata      Metadata               `json:"metadata,omitempty"`
	AccountNumber string                 `json:"account_number,omitempty"`
	BankCode      string                 `json:"bank_code,omitempty"`
	Currency      string                 `json:"currency,omitempty"`
	Description   string                 `json:"description,omitempty"`
	Active        bool                   `json:"active,omitempty"`
	Details       map[string]interface{} `json:"details,omitempty"`
	Domain        string                 `json:"domain,omitempty"`
	RecipientCode string                 `json:"recipient_code,omitempty"`
}

// BulkTransfer represents a Paystack bulk transfer
// You need to disable the Transfers OTP requirement to use this endpoint
type BulkTransfer struct {
	Currency  string                   `json:"currency,omitempty"`
	Source    string                   `json:"source,omitempty"`
	Transfers []map[string]interface{} `json:"transfers,omitempty"`
}

// TransferList is a list object for transfers.
type TransferList struct {
	Meta   ListMeta
	Values []Transfer `json:"data,omitempty"`
}

// TransferRecipientList is a list object for transfer recipient.
type TransferRecipientList struct {
	Meta   ListMeta
	Values []TransferRecipient `json:"data,omitempty"`
}

// Initiate initiates a new transfer
// For more details see https://developers.paystack.co/v1.0/reference#initiate-transfer
func (s *TransferService) Initiate(req *TransferRequest) (*Transfer, error) {
	transfer := &Transfer{}
	err := s.client.Call("POST", "/transfer", req, transfer)
	return transfer, err
}

// Finalize completes a transfer request
// For more details see https://developers.paystack.co/v1.0/reference#finalize-transfer
func (s *TransferService) Finalize(code, otp string) (Response, error) {
	u := fmt.Sprintf("/transfer/finalize_transfer")
	req := url.Values{}
	req.Add("transfer_code", code)
	req.Add("otp", otp)
	resp := Response{}
	err := s.client.Call("POST", u, req, &resp)
	return resp, err
}

// MakeBulkTransfer initiates a new bulk transfer request
// You need to disable the Transfers OTP requirement to use this endpoint
// For more details see https://developers.paystack.co/v1.0/reference#initiate-bulk-transfer
func (s *TransferService) MakeBulkTransfer(req *BulkTransfer) (Response, error) {
	u := fmt.Sprintf("/transfer")
	resp := Response{}
	err := s.client.Call("POST", u, req, &resp)
	return resp, err
}

// Get returns the details of a transfer.
// For more details see https://developers.paystack.co/v1.0/reference#fetch-transfer
func (s *TransferService) Get(idCode string) (*Transfer, error) {
	u := fmt.Sprintf("/transfer/%s", idCode)
	transfer := &Transfer{}
	err := s.client.Call("GET", u, nil, transfer)
	return transfer, err
}

// List returns a list of transfers.
// For more details see https://developers.paystack.co/v1.0/reference#list-transfers
func (s *TransferService) List() (*TransferList, error) {
	return s.ListN(10, 0)
}

// ListN returns a list of transfers
// For more details see https://developers.paystack.co/v1.0/reference#list-transfers
func (s *TransferService) ListN(count, offset int) (*TransferList, error) {
	u := paginateURL("/transfer", count, offset)
	transfers := &TransferList{}
	err := s.client.Call("GET", u, nil, transfers)
	return transfers, err
}

// ResendOTP generates a new OTP and sends to customer in the event they are having trouble receiving one.
// For more details see https://developers.paystack.co/v1.0/reference#resend-otp-for-transfer
func (s *TransferService) ResendOTP(transferCode, reason string) (Response, error) {
	data := url.Values{}
	data.Add("transfer_code", transferCode)
	data.Add("reason", reason)
	resp := Response{}
	err := s.client.Call("POST", "/transfer/resend_otp", data, &resp)
	return resp, err
}

// EnableOTP enables OTP requirement for Transfers
// In the event that a customer wants to stop being able to complete
// transfers programmatically, this endpoint helps turn OTP requirement back on.
// No arguments required.
func (s *TransferService) EnableOTP() (Response, error) {
	resp := Response{}
	err := s.client.Call("POST", "/transfer/enable_otp", nil, &resp)
	return resp, err
}

// DisableOTP disables OTP requirement for Transfers
// In the event that you want to be able to complete transfers
// programmatically without use of OTPs, this endpoint helps disable that….
// with an OTP. No arguments required. You will get an OTP.
func (s *TransferService) DisableOTP() (Response, error) {
	resp := Response{}
	err := s.client.Call("POST", "/transfer/disable_otp", nil, &resp)
	return resp, err
}

// FinalizeOTPDisable finalizes disabling of OTP requirement for Transfers
// For more details see https://developers.paystack.co/v1.0/reference#finalize-disabling-of-otp-requirement-for-transfers
func (s *TransferService) FinalizeOTPDisable(otp string) (Response, error) {
	data := url.Values{}
	data.Add("otp", otp)
	resp := Response{}
	err := s.client.Call("POST", "/transfer/disable_otp_finalize", data, &resp)
	return resp, err
}

// CreateRecipient creates a new transfer recipient
// For more details see https://developers.paystack.co/v1.0/reference#create-transferrecipient
func (s *TransferService) CreateRecipient(recipient *TransferRecipient) (*TransferRecipient, error) {
	recipient1 := &TransferRecipient{}
	err := s.client.Call("POST", "/transferrecipient", recipient, recipient1)
	return recipient1, err
}

// ListRecipients returns a list of transfer recipients.
// For more details see https://developers.paystack.co/v1.0/reference#list-transferrecipients
func (s *TransferService) ListRecipients() (*TransferRecipientList, error) {
	return s.ListRecipientsN(10, 0)
}

// ListRecipientsN returns a list of transfer recipients
// For more details see https://developers.paystack.co/v1.0/reference#list-transferrecipients
func (s *TransferService) ListRecipientsN(count, offset int) (*TransferRecipientList, error) {
	u := paginateURL("/transferrecipient", count, offset)
	resp := &TransferRecipientList{}
	err := s.client.Call("GET", u, nil, &resp)
	return resp, err
}
<commit_msg>Page parameter is 1 indexed<commit_after>package paystack

import (
	"fmt"
	"net/url"
)

// TransferService handles operations related to the transfer
// For more details see https://developers.paystack.co/v1.0/reference#create-transfer
type TransferService service

// TransferRequest represents a request to create a transfer.
type TransferRequest struct {
	Source    string  `json:"source,omitempty"`
	Amount    float32 `json:"amount,omitempty"`
	Currency  string  `json:"currency,omitempty"`
	Reason    string  `json:"reason,omitempty"`
	Recipient string  `json:"recipient,omitempty"`
}

// Transfer is the resource representing your Paystack transfer.
// For more details see https://developers.paystack.co/v1.0/reference#initiate-transfer
type Transfer struct {
	ID           int     `json:"id,omitempty"`
	CreatedAt    string  `json:"createdAt,omitempty"`
	UpdatedAt    string  `json:"updatedAt,omitempty"`
	Domain       string  `json:"domain,omitempty"`
	Integration  int     `json:"integration,omitempty"`
	Source       string  `json:"source,omitempty"`
	Amount       float32 `json:"amount,omitempty"`
	Currency     string  `json:"currency,omitempty"`
	Reason       string  `json:"reason,omitempty"`
	TransferCode string  `json:"transfer_code,omitempty"`
	// Initiate returns recipient ID as recipient value, Fetch returns recipient object
	Recipient interface{} `json:"recipient,omitempty"`
	Status    string      `json:"status,omitempty"`
	// confirm types for source_details and failures
	SourceDetails interface{} `json:"source_details,omitempty"`
	Failures      interface{} `json:"failures,omitempty"`
	TransferredAt string      `json:"transferred_at,omitempty"`
	TitanCode     string      `json:"titan_code,omitempty"`
}

// TransferRecipient represents a Paystack transfer recipient
// For more details see https://developers.paystack.co/v1.0/reference#create-transfer-recipient
type TransferRecipient struct {
	ID            int                    `json:"id,omitempty"`
	CreatedAt     string                 `json:"createdAt,omitempty"`
	UpdatedAt     string                 `json:"updatedAt,omitempty"`
	Type          string                 `json:",omitempty"`
	Name          string                 `json:"name,omitempty"`
	Metadata      Metadata               `json:"metadata,omitempty"`
	AccountNumber string                 `json:"account_number,omitempty"`
	BankCode      string                 `json:"bank_code,omitempty"`
	Currency      string                 `json:"currency,omitempty"`
	Description   string                 `json:"description,omitempty"`
	Active        bool                   `json:"active,omitempty"`
	Details       map[string]interface{} `json:"details,omitempty"`
	Domain        string                 `json:"domain,omitempty"`
	RecipientCode string                 `json:"recipient_code,omitempty"`
}

// BulkTransfer represents a Paystack bulk transfer
// You need to disable the Transfers OTP requirement to use this endpoint
type BulkTransfer struct {
	Currency  string                   `json:"currency,omitempty"`
	Source    string                   `json:"source,omitempty"`
	Transfers []map[string]interface{} `json:"transfers,omitempty"`
}

// TransferList is a list object for transfers.
type TransferList struct {
	Meta   ListMeta
	Values []Transfer `json:"data,omitempty"`
}

// TransferRecipientList is a list object for transfer recipient.
type TransferRecipientList struct {
	Meta   ListMeta
	Values []TransferRecipient `json:"data,omitempty"`
}

// Initiate initiates a new transfer
// For more details see https://developers.paystack.co/v1.0/reference#initiate-transfer
func (s *TransferService) Initiate(req *TransferRequest) (*Transfer, error) {
	transfer := &Transfer{}
	err := s.client.Call("POST", "/transfer", req, transfer)
	return transfer, err
}

// Finalize completes a transfer request
// For more details see https://developers.paystack.co/v1.0/reference#finalize-transfer
func (s *TransferService) Finalize(code, otp string) (Response, error) {
	u := fmt.Sprintf("/transfer/finalize_transfer")
	req := url.Values{}
	req.Add("transfer_code", code)
	req.Add("otp", otp)
	resp := Response{}
	err := s.client.Call("POST", u, req, &resp)
	return resp, err
}

// MakeBulkTransfer initiates a new bulk transfer request
// You need to disable the Transfers OTP requirement to use this endpoint
// For more details see https://developers.paystack.co/v1.0/reference#initiate-bulk-transfer
func (s *TransferService) MakeBulkTransfer(req *BulkTransfer) (Response, error) {
	u := fmt.Sprintf("/transfer")
	resp := Response{}
	err := s.client.Call("POST", u, req, &resp)
	return resp, err
}

// Get returns the details of a transfer.
// For more details see https://developers.paystack.co/v1.0/reference#fetch-transfer
func (s *TransferService) Get(idCode string) (*Transfer, error) {
	u := fmt.Sprintf("/transfer/%s", idCode)
	transfer := &Transfer{}
	err := s.client.Call("GET", u, nil, transfer)
	return transfer, err
}

// List returns a list of transfers.
// For more details see https://developers.paystack.co/v1.0/reference#list-transfers
func (s *TransferService) List() (*TransferList, error) {
	return s.ListN(10, 0)
}

// ListN returns a list of transfers
// For more details see https://developers.paystack.co/v1.0/reference#list-transfers
func (s *TransferService) ListN(count, offset int) (*TransferList, error) {
	u := paginateURL("/transfer", count, offset)
	transfers := &TransferList{}
	err := s.client.Call("GET", u, nil, transfers)
	return transfers, err
}

// ResendOTP generates a new OTP and sends to customer in the event they are having trouble receiving one.
// For more details see https://developers.paystack.co/v1.0/reference#resend-otp-for-transfer
func (s *TransferService) ResendOTP(transferCode, reason string) (Response, error) {
	data := url.Values{}
	data.Add("transfer_code", transferCode)
	data.Add("reason", reason)
	resp := Response{}
	err := s.client.Call("POST", "/transfer/resend_otp", data, &resp)
	return resp, err
}

// EnableOTP enables OTP requirement for Transfers
// In the event that a customer wants to stop being able to complete
// transfers programmatically, this endpoint helps turn OTP requirement back on.
// No arguments required.
func (s *TransferService) EnableOTP() (Response, error) {
	resp := Response{}
	err := s.client.Call("POST", "/transfer/enable_otp", nil, &resp)
	return resp, err
}

// DisableOTP disables OTP requirement for Transfers
// In the event that you want to be able to complete transfers
// programmatically without use of OTPs, this endpoint helps disable that….
// with an OTP. No arguments required. You will get an OTP.
func (s *TransferService) DisableOTP() (Response, error) {
	resp := Response{}
	err := s.client.Call("POST", "/transfer/disable_otp", nil, &resp)
	return resp, err
}

// FinalizeOTPDisable finalizes disabling of OTP requirement for Transfers
// For more details see https://developers.paystack.co/v1.0/reference#finalize-disabling-of-otp-requirement-for-transfers
func (s *TransferService) FinalizeOTPDisable(otp string) (Response, error) {
	data := url.Values{}
	data.Add("otp", otp)
	resp := Response{}
	err := s.client.Call("POST", "/transfer/disable_otp_finalize", data, &resp)
	return resp, err
}

// CreateRecipient creates a new transfer recipient
// For more details see https://developers.paystack.co/v1.0/reference#create-transferrecipient
func (s *TransferService) CreateRecipient(recipient *TransferRecipient) (*TransferRecipient, error) {
	recipient1 := &TransferRecipient{}
	err := s.client.Call("POST", "/transferrecipient", recipient, recipient1)
	return recipient1, err
}

// ListRecipients returns a list of transfer recipients.
// For more details see https://developers.paystack.co/v1.0/reference#list-transferrecipients
func (s *TransferService) ListRecipients() (*TransferRecipientList, error) {
	return s.ListRecipientsN(10, 1)
}

// ListRecipientsN returns a list of transfer recipients
// For more details see https://developers.paystack.co/v1.0/reference#list-transferrecipients
func (s *TransferService) ListRecipientsN(count, offset int) (*TransferRecipientList, error) {
	u := paginateURL("/transferrecipient", count, offset)
	resp := &TransferRecipientList{}
	err := s.client.Call("GET", u, nil, &resp)
	return resp, err
}
<|endoftext|>


<commit_before>package util

import (
	"github.com/viant/toolbox"
)

//NormalizeMap normalizes keyValuePairs from map or slice (map with preserved key order)
func NormalizeMap(keyValuePairs interface{}, deep bool) (map[string]interface{}, error) {
	var result = make(map[string]interface{})
	if keyValuePairs == nil {
		return result, nil
	}
	err := toolbox.ProcessMap(keyValuePairs, func(k, value interface{}) bool {
		var key = toolbox.AsString(k)
		//inline map key
		result[key] = value
		if deep {
			if normalized, err := toolbox.NormalizeKVPairs(value); err == nil {
				result[key] = normalized
			}

		}
		return true
	})
	return result, err
}

//AppendMap source to dest map
func Append(dest, source map[string]interface{}, override bool) {
	for k, v := range source {
		if _, ok := dest[k]; ok && !override {
			continue
		}
		dest[k] = v
	}
}
<commit_msg>added BuildLowerCaseMapping utility<commit_after>package util

import (
	"github.com/viant/toolbox"
	"strings"
)

//NormalizeMap normalizes keyValuePairs from map or slice (map with preserved key order)
func NormalizeMap(keyValuePairs interface{}, deep bool) (map[string]interface{}, error) {
	var result = make(map[string]interface{})
	if keyValuePairs == nil {
		return result, nil
	}
	err := toolbox.ProcessMap(keyValuePairs, func(k, value interface{}) bool {
		var key = toolbox.AsString(k)
		//inline map key
		result[key] = value
		if deep {
			if normalized, err := toolbox.NormalizeKVPairs(value); err == nil {
				result[key] = normalized
			}

		}
		return true
	})
	return result, err
}

//AppendMap source to dest map
func Append(dest, source map[string]interface{}, override bool) {
	for k, v := range source {
		if _, ok := dest[k]; ok && !override {
			continue
		}
		dest[k] = v
	}
}

//BuildLowerCaseMapping build lowercase key to key map mapping
func BuildLowerCaseMapping(aMap map[string]interface{}) map[string]string {
	var result = make(map[string]string)
	for k := range aMap {
		result[strings.ToLower(k)] = k
	}
	return result
}<|endoftext|>


<commit_before>package terraform

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var writeFile func(file string, data []byte, perm os.FileMode) error = ioutil.WriteFile
var readFile func(filename string) ([]byte, error) = ioutil.ReadFile

type Executor struct {
	cmd        terraformCmd
	stateStore stateStore
	debug      bool
}

type tfOutput struct {
	Sensitive bool
	Type      string
	Value     interface{}
}

type terraformCmd interface {
	Run(stdout io.Writer, workingDirectory string, args []string, debug bool) error
}

type stateStore interface {
	GetTerraformDir() (string, error)
	GetVarsDir() (string, error)
}

func NewExecutor(cmd terraformCmd, stateStore stateStore, debug bool) Executor {
	return Executor{
		cmd:        cmd,
		stateStore: stateStore,
		debug:      debug,
	}
}

func (e Executor) IsInitialized() bool {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return false // not tested
	}

	_, err = os.Stat(filepath.Join(varsDir, "terraform.tfstate"))
	if err != nil {
		return false
	}

	_, err = os.Stat(filepath.Join(varsDir, "terraform.tfvars"))
	if err != nil {
		return false
	}

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return false // not tested
	}

	_, err = os.Stat(filepath.Join(terraformDir, ".terraform"))
	if err != nil {
		return false
	}

	_, err = os.Stat(filepath.Join(terraformDir, "template.tf"))
	if err != nil {
		return false
	}

	return true
}

func (e Executor) Init(template, prevTFState string, input map[string]string) error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, "template.tf"), []byte(template), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write terraform template: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}

	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")
	if prevTFState != "" {
		err = writeFile(tfStatePath, []byte(prevTFState), os.ModePerm)
		if err != nil {
			return fmt.Errorf("Write previous terraform state: %s", err)
		}
	}

	err = os.MkdirAll(filepath.Join(terraformDir, ".terraform"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Create .terraform directory: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, ".terraform", ".gitignore"), []byte("*\n"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write .gitignore for terraform binaries: %s", err)
	}

	tfVarsPath := filepath.Join(varsDir, "terraform.tfvars")
	formattedVars := formatVars(input)
	err = writeFile(tfVarsPath, []byte(formattedVars), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write terraform vars: %s", err)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, []string{"init"}, e.debug)
	if err != nil {
		return fmt.Errorf("Run terraform init: %s", err)
	}

	return nil
}

func formatVars(inputs map[string]string) string {
	formattedVars := ""
	for name, value := range inputs {
		formattedVars = fmt.Sprintf("%s\n%s=\"%s\"", formattedVars, name, value)
	}
	return formattedVars
}

func (e Executor) Apply() (string, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}
	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}
	relativeStatePath, err := filepath.Rel(terraformDir, tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Get relative terraform state path: %s", err) //not tested
	}
	relativeVarsPath, err := filepath.Rel(terraformDir, filepath.Join(varsDir, "terraform.tfvars"))
	if err != nil {
		return "", fmt.Errorf("Get relative terraform vars path: %s", err) //not tested
	}

	args := []string{
		"apply",
		"-state", relativeStatePath,
		"-var-file", relativeVarsPath,
	}

	err = e.cmd.Run(os.Stdout, terraformDir, args, e.debug)
	if err != nil {
		return "", NewExecutorError(tfStatePath, err, e.debug)
	}

	tfState, err := readFile(tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Read terraform state: %s", err)
	}

	return string(tfState), nil
}

func (e Executor) Destroy(input map[string]string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")

	relativeStatePath, err := filepath.Rel(terraformDir, tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Get relative terraform state path: %s", err) //not tested
	}

	args := []string{
		"destroy",
		"-force",
		"-state", relativeStatePath,
	}
	for name, value := range input {
		tfVar := []string{"-var", fmt.Sprintf("%s=%s", name, value)}
		args = append(args, tfVar...)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, args, e.debug)
	if err != nil {
		return "", NewExecutorError(tfStatePath, err, e.debug)
	}

	tfState, err := readFile(tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Read terraform state: %s", err)
	}

	return string(tfState), nil
}

func (e Executor) Version() (string, error) {
	buffer := bytes.NewBuffer([]byte{})
	err := e.cmd.Run(buffer, "/tmp", []string{"version"}, true)
	if err != nil {
		return "", err
	}
	versionOutput := buffer.String()
	regex := regexp.MustCompile(`\d+.\d+.\d+`)

	version := regex.FindString(versionOutput)
	if version == "" {
		return "", errors.New("Terraform version could not be parsed")
	}

	return version, nil
}

func (e Executor) Output(tfState, outputName string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, "terraform.tfstate"), []byte(tfState), os.ModePerm)
	if err != nil {
		return "", fmt.Errorf("Write terraform state to terraform.tfstate in terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, []string{"init"}, e.debug)
	if err != nil {
		return "", fmt.Errorf("Run terraform init in terraform dir: %s", err)
	}

	args := []string{"output", outputName, "-state", filepath.Join(varsDir, "terraform.tfstate")}
	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, terraformDir, args, true)
	if err != nil {
		return "", fmt.Errorf("Run terraform output -state: %s", err)
	}

	return strings.TrimSuffix(buffer.String(), "\n"), nil
}

func (e Executor) Outputs(tfState string) (map[string]interface{}, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Get vars dir: %s", err)
	}

	err = writeFile(filepath.Join(varsDir, "terraform.tfstate"), []byte(tfState), os.ModePerm)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Write terraform state to terraform.tfstate: %s", err)
	}

	err = e.cmd.Run(os.Stdout, varsDir, []string{"init"}, false)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform init in vars dir: %s", err)
	}

	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, varsDir, []string{"output", "--json"}, true)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform output --json in vars dir: %s", err)
	}

	tfOutputs := map[string]tfOutput{}
	err = json.Unmarshal(buffer.Bytes(), &tfOutputs)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Unmarshal terraform output: %s", err)
	}

	outputs := map[string]interface{}{}
	for tfKey, tfValue := range tfOutputs {
		outputs[tfKey] = tfValue.Value
	}

	return outputs, nil
}
<commit_msg>Don't expect TFState to exist after plan<commit_after>package terraform

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var writeFile func(file string, data []byte, perm os.FileMode) error = ioutil.WriteFile
var readFile func(filename string) ([]byte, error) = ioutil.ReadFile

type Executor struct {
	cmd        terraformCmd
	stateStore stateStore
	debug      bool
}

type tfOutput struct {
	Sensitive bool
	Type      string
	Value     interface{}
}

type terraformCmd interface {
	Run(stdout io.Writer, workingDirectory string, args []string, debug bool) error
}

type stateStore interface {
	GetTerraformDir() (string, error)
	GetVarsDir() (string, error)
}

func NewExecutor(cmd terraformCmd, stateStore stateStore, debug bool) Executor {
	return Executor{
		cmd:        cmd,
		stateStore: stateStore,
		debug:      debug,
	}
}

func (e Executor) IsInitialized() bool {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return false // not tested
	}

	_, err = os.Stat(filepath.Join(varsDir, "terraform.tfvars"))
	if err != nil {
		return false
	}

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return false // not tested
	}

	_, err = os.Stat(filepath.Join(terraformDir, ".terraform"))
	if err != nil {
		return false
	}

	_, err = os.Stat(filepath.Join(terraformDir, "template.tf"))
	if err != nil {
		return false
	}

	return true
}

func (e Executor) Init(template, prevTFState string, input map[string]string) error {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return fmt.Errorf("Get terraform dir: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, "template.tf"), []byte(template), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write terraform template: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return fmt.Errorf("Get vars dir: %s", err)
	}

	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")
	if prevTFState != "" {
		err = writeFile(tfStatePath, []byte(prevTFState), os.ModePerm)
		if err != nil {
			return fmt.Errorf("Write previous terraform state: %s", err)
		}
	}

	err = os.MkdirAll(filepath.Join(terraformDir, ".terraform"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Create .terraform directory: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, ".terraform", ".gitignore"), []byte("*\n"), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write .gitignore for terraform binaries: %s", err)
	}

	tfVarsPath := filepath.Join(varsDir, "terraform.tfvars")
	formattedVars := formatVars(input)
	err = writeFile(tfVarsPath, []byte(formattedVars), os.ModePerm)
	if err != nil {
		return fmt.Errorf("Write terraform vars: %s", err)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, []string{"init"}, e.debug)
	if err != nil {
		return fmt.Errorf("Run terraform init: %s", err)
	}

	return nil
}

func formatVars(inputs map[string]string) string {
	formattedVars := ""
	for name, value := range inputs {
		formattedVars = fmt.Sprintf("%s\n%s=\"%s\"", formattedVars, name, value)
	}
	return formattedVars
}

func (e Executor) Apply() (string, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}
	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")

	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}
	relativeStatePath, err := filepath.Rel(terraformDir, tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Get relative terraform state path: %s", err) //not tested
	}
	relativeVarsPath, err := filepath.Rel(terraformDir, filepath.Join(varsDir, "terraform.tfvars"))
	if err != nil {
		return "", fmt.Errorf("Get relative terraform vars path: %s", err) //not tested
	}

	args := []string{
		"apply",
		"-state", relativeStatePath,
		"-var-file", relativeVarsPath,
	}

	err = e.cmd.Run(os.Stdout, terraformDir, args, e.debug)
	if err != nil {
		return "", NewExecutorError(tfStatePath, err, e.debug)
	}

	tfState, err := readFile(tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Read terraform state: %s", err)
	}

	return string(tfState), nil
}

func (e Executor) Destroy(input map[string]string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	tfStatePath := filepath.Join(varsDir, "terraform.tfstate")

	relativeStatePath, err := filepath.Rel(terraformDir, tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Get relative terraform state path: %s", err) //not tested
	}

	args := []string{
		"destroy",
		"-force",
		"-state", relativeStatePath,
	}
	for name, value := range input {
		tfVar := []string{"-var", fmt.Sprintf("%s=%s", name, value)}
		args = append(args, tfVar...)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, args, e.debug)
	if err != nil {
		return "", NewExecutorError(tfStatePath, err, e.debug)
	}

	tfState, err := readFile(tfStatePath)
	if err != nil {
		return "", fmt.Errorf("Read terraform state: %s", err)
	}

	return string(tfState), nil
}

func (e Executor) Version() (string, error) {
	buffer := bytes.NewBuffer([]byte{})
	err := e.cmd.Run(buffer, "/tmp", []string{"version"}, true)
	if err != nil {
		return "", err
	}
	versionOutput := buffer.String()
	regex := regexp.MustCompile(`\d+.\d+.\d+`)

	version := regex.FindString(versionOutput)
	if version == "" {
		return "", errors.New("Terraform version could not be parsed")
	}

	return version, nil
}

func (e Executor) Output(tfState, outputName string) (string, error) {
	terraformDir, err := e.stateStore.GetTerraformDir()
	if err != nil {
		return "", fmt.Errorf("Get terraform dir: %s", err)
	}

	err = writeFile(filepath.Join(terraformDir, "terraform.tfstate"), []byte(tfState), os.ModePerm)
	if err != nil {
		return "", fmt.Errorf("Write terraform state to terraform.tfstate in terraform dir: %s", err)
	}

	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return "", fmt.Errorf("Get vars dir: %s", err)
	}

	err = e.cmd.Run(os.Stdout, terraformDir, []string{"init"}, e.debug)
	if err != nil {
		return "", fmt.Errorf("Run terraform init in terraform dir: %s", err)
	}

	args := []string{"output", outputName, "-state", filepath.Join(varsDir, "terraform.tfstate")}
	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, terraformDir, args, true)
	if err != nil {
		return "", fmt.Errorf("Run terraform output -state: %s", err)
	}

	return strings.TrimSuffix(buffer.String(), "\n"), nil
}

func (e Executor) Outputs(tfState string) (map[string]interface{}, error) {
	varsDir, err := e.stateStore.GetVarsDir()
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Get vars dir: %s", err)
	}

	err = writeFile(filepath.Join(varsDir, "terraform.tfstate"), []byte(tfState), os.ModePerm)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Write terraform state to terraform.tfstate: %s", err)
	}

	err = e.cmd.Run(os.Stdout, varsDir, []string{"init"}, false)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform init in vars dir: %s", err)
	}

	buffer := bytes.NewBuffer([]byte{})
	err = e.cmd.Run(buffer, varsDir, []string{"output", "--json"}, true)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Run terraform output --json in vars dir: %s", err)
	}

	tfOutputs := map[string]tfOutput{}
	err = json.Unmarshal(buffer.Bytes(), &tfOutputs)
	if err != nil {
		return map[string]interface{}{}, fmt.Errorf("Unmarshal terraform output: %s", err)
	}

	outputs := map[string]interface{}{}
	for tfKey, tfValue := range tfOutputs {
		outputs[tfKey] = tfValue.Value
	}

	return outputs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/url"

	"github.com/codegangsta/cli"
	"gopkg.in/resty.v0"
)

func registerDatacenters(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			// datacenters
			{
				Name:  "datacenters",
				Usage: "SUBCOMMANDS for datacenters",
				Subcommands: []cli.Command{
					{
						Name:   "add",
						Usage:  "Register a new datacenter",
						Action: runtime(cmdDatacentersAdd),
					},
					{
						Name:   "remove",
						Usage:  "Remove an existing datacenter",
						Action: runtime(cmdDatacentersRemove),
					},
					{
						Name:   "rename",
						Usage:  "Rename an existing datacenter",
						Action: runtime(cmdDatacentersRename),
					},
					{
						Name:   "list",
						Usage:  "List all datacenters",
						Action: runtime(cmdDatacentersList),
					},
					{
						Name:   "show",
						Usage:  "Show information about a specific datacenter",
						Action: runtime(cmdDatacentersShow),
					},
					{
						Name:   "groupadd",
						Usage:  "Add a datacenter to a datacenter group",
						Action: runtime(cmdDatacentersAddToGroup),
					},
					{
						Name:   "groupdel",
						Usage:  "Remove a datacenter from a datacenter group",
						Action: runtime(cmdDatacentersRemoveFromGroup),
					},
					{
						Name:   "grouplist",
						Usage:  "List all datacenter groups",
						Action: runtime(cmdDatacentersListGroups),
					},
					{
						Name:   "groupshow",
						Usage:  "Show information about a datacenter group",
						Action: runtime(cmdDatacentersShowGroup),
					},
				},
			}, // end datacenters
		}...,
	)
	return &app
}

func cmdDatacentersAdd(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}
	url.Path = "/datacenters"

	a := c.Args()
	datacenter := a.First()
	if datacenter == "" {
		log.Fatal("Syntax error")
	}
	log.Printf("Command: add datacenter [%s]", datacenter)

	var req proto.Request
	req.Datacenter = &proto.Datacenter{}
	req.Datacenter.Locode = datacenter

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		SetBody(req).
		Post(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s\n", resp.Status())
	return nil
}

func cmdDatacentersAddToGroup(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}

	a := c.Args()
	// we expected exactly 3 arguments
	if len(a) != 3 {
		log.Fatal("Syntax error")
	}
	// second arg must be `to`
	if a.Get(1) != "group" {
		log.Fatal("Syntax error")
	}
	log.Printf("Command: add datacenter [%s] to group [%s]", a.Get(0), a.Get(2))

	var req proto.Request
	req.Datacenter = &proto.Datacenter{}
	req.Datacenter.Locode = a.Get(0)
	url.Path = fmt.Sprintf("/datacentergroups/%s", a.Get(2))

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		SetBody(req).
		Patch(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s", resp.Status())
	return nil
}

func cmdDatacentersRemove(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}

	a := c.Args()
	datacenter := a.First()
	if datacenter == "" {
		log.Fatal("Syntax error")
	}
	log.Printf("Command: remove datacenter [%s]", datacenter)
	url.Path = fmt.Sprintf("/datacenters/%s", datacenter)

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Delete(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s\n", resp.Status())
	return nil
}

func cmdDatacentersRemoveFromGroup(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}

	a := c.Args()
	// we expected exactly 3 arguments
	if len(a) != 3 {
		log.Fatal("Syntax error")
	}
	// second arg must be `to`
	if a.Get(1) != "group" {
		log.Fatal("Syntax error")
	}
	log.Printf("Command: remove datacenter [%s] from group [%s]", a.Get(0), a.Get(2))

	var req proto.Request
	req.Datacenter = &proto.Datacenter{}
	req.Datacenter.Locode = a.Get(0)
	url.Path = fmt.Sprintf("/datacentergroups/%s", a.Get(2))

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		SetBody(req).
		Delete(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s", resp.Status())
	return nil
}

func cmdDatacentersRename(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}

	a := c.Args()
	// we expected exactly 3 arguments
	if len(a) != 3 {
		log.Fatal("Syntax error")
	}
	// second arg must be `to`
	if a.Get(1) != "to" {
		log.Fatal("Syntax error")
	}
	log.Printf("Command: rename datacenter [%s] to [%s]", a.Get(0), a.Get(2))

	var req proto.Request
	req.Datacenter = &proto.Datacenter{}
	req.Datacenter.Locode = a.Get(2)
	url.Path = fmt.Sprintf("/datacenters/%s", a.Get(0))

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		SetBody(req).
		Put(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s\n", resp.Status())
	return nil
}

func cmdDatacentersList(c *cli.Context) error {
	url, err := url.Parse(Cfg.Api)
	if err != nil {
		log.Fatal(err)
	}
	url.Path = "/datacenters"

	a := c.Args()
	if len(a) != 0 {
		log.Fatal("Syntax error")
	}

	resp, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Response: %s\n", resp.Status())
	return nil
}

func cmdDatacentersListGroups(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest(Client, "/datacentergroups/")
	fmt.Println(resp)
	return nil
}

func cmdDatacentersShowGroup(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/datacentergroups/%s", c.Args().First())
	resp := utl.GetRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdDatacentersShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/datacenters/%s", c.Args().First())
	resp := utl.GetRequest(Client, path)
	fmt.Println(resp)
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Modernize command_datacenters, new runtime<commit_after>package main

import (
	"fmt"

	"github.com/codegangsta/cli"
)

func registerDatacenters(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			// datacenters
			{
				Name:  "datacenters",
				Usage: "SUBCOMMANDS for datacenters",
				Subcommands: []cli.Command{
					{
						Name:   "add",
						Usage:  "Register a new datacenter",
						Action: runtime(cmdDatacentersAdd),
					},
					{
						Name:   "remove",
						Usage:  "Remove an existing datacenter",
						Action: runtime(cmdDatacentersRemove),
					},
					{
						Name:   "rename",
						Usage:  "Rename an existing datacenter",
						Action: runtime(cmdDatacentersRename),
					},
					{
						Name:   "list",
						Usage:  "List all datacenters",
						Action: runtime(cmdDatacentersList),
					},
					{
						Name:   "show",
						Usage:  "Show information about a specific datacenter",
						Action: runtime(cmdDatacentersShow),
					},
					/*
						{
							Name:   "groupadd",
							Usage:  "Add a datacenter to a datacenter group",
							Action: runtime(cmdDatacentersAddToGroup),
						},
						{
							Name:   "groupdel",
							Usage:  "Remove a datacenter from a datacenter group",
							Action: runtime(cmdDatacentersRemoveFromGroup),
						},
						{
							Name:   "grouplist",
							Usage:  "List all datacenter groups",
							Action: runtime(cmdDatacentersListGroups),
						},
						{
							Name:   "groupshow",
							Usage:  "Show information about a datacenter group",
							Action: runtime(cmdDatacentersShowGroup),
						},
					*/
				},
			}, // end datacenters
		}...,
	)
	return &app
}

func cmdDatacentersAdd(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	req := proto.NewDatacenterRequest()
	req.Datacenter.Locode = c.Args().First()

	resp := utl.PostRequestWithBody(Client, req, `/datacenters/`)
	fmt.Println(resp)
	return nil
}

func cmdDatacentersAddToGroup(c *cli.Context) error {
	/*
		url, err := url.Parse(Cfg.Api)
		if err != nil {
			log.Fatal(err)
		}

		a := c.Args()
		// we expected exactly 3 arguments
		if len(a) != 3 {
			log.Fatal("Syntax error")
		}
		// second arg must be `to`
		if a.Get(1) != "group" {
			log.Fatal("Syntax error")
		}
		log.Printf("Command: add datacenter [%s] to group [%s]", a.Get(0), a.Get(2))

		var req proto.Request
		req.Datacenter = &proto.Datacenter{}
		req.Datacenter.Locode = a.Get(0)
		url.Path = fmt.Sprintf("/datacentergroups/%s", a.Get(2))

		resp, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Patch(url.String())
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Response: %s", resp.Status())
	*/
	return nil
}

func cmdDatacentersRemove(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/datacenters/%s", c.Args().First())

	resp := utl.DeleteRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdDatacentersRemoveFromGroup(c *cli.Context) error {
	/*
		url, err := url.Parse(Cfg.Api)
		if err != nil {
			log.Fatal(err)
		}

		a := c.Args()
		// we expected exactly 3 arguments
		if len(a) != 3 {
			log.Fatal("Syntax error")
		}
		// second arg must be `to`
		if a.Get(1) != "group" {
			log.Fatal("Syntax error")
		}
		log.Printf("Command: remove datacenter [%s] from group [%s]", a.Get(0), a.Get(2))

		var req proto.Request
		req.Datacenter = &proto.Datacenter{}
		req.Datacenter.Locode = a.Get(0)
		url.Path = fmt.Sprintf("/datacentergroups/%s", a.Get(2))

		resp, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Delete(url.String())
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Response: %s", resp.Status())
	*/
	return nil
}

func cmdDatacentersRename(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 3)
	key := []string{`to`}

	opts := utl.ParseVariadicArguments(key, key, key, c.Args().Tail())

	req := proto.NewDatacenterRequest()
	req.Datacenter.Locode = opts[`to`][0]

	path := fmt.Sprintf("/datacenters/%s", c.Args().First())

	resp := utl.PutRequestWithBody(Client, req, path)
	fmt.Println(resp)
	return nil
}

func cmdDatacentersList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)
	resp := utl.GetRequest(Client, `/datacenters/`)
	fmt.Println(resp)
	return nil
}

func cmdDatacentersListGroups(c *cli.Context) error {
	/*
		utl.ValidateCliArgumentCount(c, 0)

		resp := utl.GetRequest(Client, "/datacentergroups/")
		fmt.Println(resp)
	*/
	return nil
}

func cmdDatacentersShowGroup(c *cli.Context) error {
	/*
		utl.ValidateCliArgumentCount(c, 1)

		path := fmt.Sprintf("/datacentergroups/%s", c.Args().First())
		resp := utl.GetRequest(Client, path)
		fmt.Println(resp)
	*/
	return nil
}

func cmdDatacentersShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/datacenters/%s", c.Args().First())
	resp := utl.GetRequest(Client, path)
	fmt.Println(resp)
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>/*
This is a simple client
*/
package main

import (
	"bufio"
	"crypto/rand"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"mateusbraga/freestore/client"
)

func main() {
	sizes := []int{1, 256, 512, 1024}

	for _, size := range sizes {
		var times []time.Duration

		log.Println("Start size: ", size)

		data := make([]byte, size)

		n, err := io.ReadFull(rand.Reader, data)
		if n != len(data) || err != nil {
			log.Fatalln("error to generate data:", err)
			return
		}

		err = client.Write(data)
		if err != nil {
			log.Fatalln(err)
		}

		for i := 0; i < 1000; i++ {

			startRead := time.Now()
			_, err = client.Read()
			endRead := time.Now()
			if err != nil {
				log.Fatalln(err)
			}

			//fmt.Printf("Read %v (%v)-> Write (%v)\n", finalValue, endRead.Sub(startRead), endWrite.Sub(startWrite))
			times = append(times, endRead.Sub(startRead))
			time.Sleep(300 * time.Millisecond)
		}
		saveTime(times, size)
	}
}

func saveTime(times []time.Duration, size int) {
	file, err := os.Create(fmt.Sprintf("/home/mateus/read-latency-%v.txt", size))
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()

	w := bufio.NewWriter(file)
	defer w.Flush()

	for _, t := range times {
		if _, err := w.Write([]byte(fmt.Sprintf("%v\n", t))); err != nil {
			log.Fatalln(err)
		}
	}
}
<commit_msg>Print nanoseconds to file<commit_after>/*
This is a simple client
*/
package main

import (
	"bufio"
	"crypto/rand"
	"fmt"
	"io"
	"log"
	"os"
	"time"

	"mateusbraga/freestore/client"
)

func main() {
	sizes := []int{1, 256, 512, 1024}

	for _, size := range sizes {
		var times []time.Duration

		log.Println("Start size: ", size)

		data := make([]byte, size)

		n, err := io.ReadFull(rand.Reader, data)
		if n != len(data) || err != nil {
			log.Fatalln("error to generate data:", err)
			return
		}

		err = client.Write(data)
		if err != nil {
			log.Fatalln(err)
		}

		for i := 0; i < 1000; i++ {

			startRead := time.Now()
			_, err = client.Read()
			endRead := time.Now()
			if err != nil {
				log.Fatalln(err)
			}

			//fmt.Printf("Read %v (%v)-> Write (%v)\n", finalValue, endRead.Sub(startRead), endWrite.Sub(startWrite))
			times = append(times, endRead.Sub(startRead))
			time.Sleep(300 * time.Millisecond)
		}
		saveTime(times, size)
	}
}

func saveTime(times []time.Duration, size int) {
	file, err := os.Create(fmt.Sprintf("/home/mateus/read-latency-%v.txt", size))
	if err != nil {
		log.Fatalln(err)
	}
	defer file.Close()

	w := bufio.NewWriter(file)
	defer w.Flush()

	for _, t := range times {
		if _, err := w.Write([]byte(fmt.Sprintf("%d\n", t))); err != nil {
			log.Fatalln(err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package samples_test

import (
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/gcsfuse/timeutil"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestHelloFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type HelloFSTest struct {
	clock timeutil.SimulatedClock
	mfs   *fuse.MountedFileSystem
}

var _ SetUpInterface = &HelloFSTest{}
var _ TearDownInterface = &HelloFSTest{}

func init() { RegisterTestSuite(&HelloFSTest{}) }

func (t *HelloFSTest) SetUp(ti *TestInfo) {
	var err error

	// Set up a fixed, non-zero time.
	t.clock.AdvanceTime(time.Now().Sub(t.clock.Now()))

	// Set up a temporary directory for mounting.
	mountPoint, err := ioutil.TempDir("", "hello_fs_test")
	if err != nil {
		panic("ioutil.TempDir: " + err.Error())
	}

	// Mount a file system.
	fs := &samples.HelloFS{
		Clock: &t.clock,
	}

	if t.mfs, err = fuse.Mount(mountPoint, fs); err != nil {
		panic("Mount: " + err.Error())
	}

	if err = t.mfs.WaitForReady(context.Background()); err != nil {
		panic("MountedFileSystem.WaitForReady: " + err.Error())
	}
}

func (t *HelloFSTest) TearDown() {
	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := t.mfs.Unmount()
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		panic("MountedFileSystem.Unmount: " + err.Error())
	}

	if err := t.mfs.Join(context.Background()); err != nil {
		panic("MountedFileSystem.Join: " + err.Error())
	}
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *HelloFSTest) ReadDir_Root() {
	entries, err := ioutil.ReadDir(t.mfs.Dir())

	AssertEq(nil, err)
	AssertEq(2, len(entries))
	var fi os.FileInfo

	// dir
	fi = entries[0]
	ExpectEq("dir", fi.Name())
	ExpectEq(0, fi.Size())
	ExpectEq(os.ModeDir|0500, fi.Mode())
	ExpectEq(t.clock.Now(), fi.ModTime())
	ExpectTrue(fi.IsDir())

	// hello
	fi = entries[1]
	ExpectEq("hello", fi.Name())
	ExpectEq(len("Hello, world!"), fi.Size())
	ExpectEq(0400, fi.Mode())
	ExpectEq(t.clock.Now(), fi.ModTime())
	ExpectFalse(fi.IsDir())
}

func (t *HelloFSTest) ReadDir_Dir() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) ReadDir_NonExistent() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_Hello() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_Dir() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_World() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_NonExistent() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Read_Hello() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Read_World() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Open_NonExistent() {
	AssertTrue(false, "TODO")
}
<commit_msg>Fixed some test bugs.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package samples_test

import (
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/samples"
	"github.com/jacobsa/gcsfuse/timeutil"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestHelloFS(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type HelloFSTest struct {
	clock timeutil.SimulatedClock
	mfs   *fuse.MountedFileSystem
}

var _ SetUpInterface = &HelloFSTest{}
var _ TearDownInterface = &HelloFSTest{}

func init() { RegisterTestSuite(&HelloFSTest{}) }

func (t *HelloFSTest) SetUp(ti *TestInfo) {
	var err error

	// Set up a fixed, non-zero time.
	t.clock.AdvanceTime(time.Now().Sub(t.clock.Now()))

	// Set up a temporary directory for mounting.
	mountPoint, err := ioutil.TempDir("", "hello_fs_test")
	if err != nil {
		panic("ioutil.TempDir: " + err.Error())
	}

	// Mount a file system.
	fs := &samples.HelloFS{
		Clock: &t.clock,
	}

	if t.mfs, err = fuse.Mount(mountPoint, fs); err != nil {
		panic("Mount: " + err.Error())
	}

	if err = t.mfs.WaitForReady(context.Background()); err != nil {
		panic("MountedFileSystem.WaitForReady: " + err.Error())
	}
}

func (t *HelloFSTest) TearDown() {
	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := t.mfs.Unmount()
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		panic("MountedFileSystem.Unmount: " + err.Error())
	}

	if err := t.mfs.Join(context.Background()); err != nil {
		panic("MountedFileSystem.Join: " + err.Error())
	}
}

////////////////////////////////////////////////////////////////////////
// Test functions
////////////////////////////////////////////////////////////////////////

func (t *HelloFSTest) ReadDir_Root() {
	entries, err := ioutil.ReadDir(t.mfs.Dir())

	AssertEq(nil, err)
	AssertEq(2, len(entries))
	var fi os.FileInfo

	// dir
	fi = entries[0]
	ExpectEq("dir", fi.Name())
	ExpectEq(0, fi.Size())
	ExpectEq(os.ModeDir|0500, fi.Mode())
	ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
	ExpectTrue(fi.IsDir())

	// hello
	fi = entries[1]
	ExpectEq("hello", fi.Name())
	ExpectEq(len("Hello, world!"), fi.Size())
	ExpectEq(0400, fi.Mode())
	ExpectEq(0, t.clock.Now().Sub(fi.ModTime()), "ModTime: %v", fi.ModTime())
	ExpectFalse(fi.IsDir())
}

func (t *HelloFSTest) ReadDir_Dir() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) ReadDir_NonExistent() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_Hello() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_Dir() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_World() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Stat_NonExistent() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Read_Hello() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Read_World() {
	AssertTrue(false, "TODO")
}

func (t *HelloFSTest) Open_NonExistent() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package waveguide

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"sort"
	"sync"

	"appengine"
	"appengine/urlfetch"
)

func init() {
	http.HandleFunc("/", handleRoot)
}

var tmpl = template.Must(template.New("main").Parse(`

		<title>Waveguide</title>

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.Name}}
					{{.Rating}}
					{{.Details}}

				{{end}}

		{{end}}

		{{if .Errs}}

		Errors:

			{{range .Errs}}

				{{.Loc.Name}}
				{{.Err}}

			{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

type Conditions struct {
	Loc     *Location
	Rating  string
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

type ConditionsOrError struct {
	Cond *Conditions
	Err  *Error
}

var locations = []Location{
	Location{"Bay Area: Lindamar-Pacifica", "/Linda-Mar-Pacifica-Surf-Report/819/"},
	Location{"Bay Area: Stinson Beach", "/Stinson-Beach-Surf-Report/4216/"},
	Location{"Bay Area: Ocean Beach SF", "/Ocean-Beach-Surf-Report/255/"},
	Location{"Bay Area: Princeton Jetty", "/Princeton-Jetty-Surf-Report/3679/"},
	Location{"Bali: Kuta Beach", "/Kuta-Beach-Surf-Report/566/"},
	Location{"Bolinas", "/Bolinas-Surf-Report/4221/"},
	Location{"Bolinas Jetty", "/Bolinas-Jetty-Surf-Report/4215/"},
	Location{"Cairns: Sunshine Beach", "/Sunshine-Beach-Surf-Report/1004/"},
	Location{"Oahu: Waikiki Beach", "/Queens-Canoes-Waikiki-Surf-Report/662/"},
	Location{"Kauai: Hanalei Bay", "/Hanalei-Bay-Surf-Report/3051/"},
	Location{"Kauai: Polihale", "/Polihale-Surf-Report/3080/"},
	Location{"Maui: Lahaina", "/Lahaina-Harbor-Breakwall-Surf-Report/4287/"},
	Location{"Oahu: Laniakea", "/Laniakea-Surf-Report/3672/"},
	Location{"Oahu: Pipeline", "/Pipeline-Backdoor-Surf-Report/616/"},
	Location{"Oahu: Sunset", "/Sunset-Surf-Report/657/"},
	Location{"Sydney: Manly Beach", "/Sydney-Manly-Surf-Report/526/"},
	Location{"Sydney: Bodi Beach", "/Sydney-Bondi-Surf-Report/996/"},
	Location{"New Zealand: Dunedin: Martins Bay", "/Martins-Bay-Surf-Report/3913/"},
	Location{"New Zealand: Dunedin: Papatowai", "/Papatowai-Surf-Report/124/"},
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	client := urlfetch.Client(ctx)

	// Spawn requests to get the conditions.
	ch := make(chan *ConditionsOrError)
	var wg sync.WaitGroup
	for _, loc := range locations {
		loc := loc
		wg.Add(1)
		go func() {
			defer wg.Done()
			cond, err := loc.GetConditions(client)
			coe := &ConditionsOrError{}
			if err != nil {
				coe.Err = &Error{
					Loc: &loc,
					Err: err,
				}
			} else {
				coe.Cond = cond
			}
			ch <- coe
		}()
	}

	go func() {
		wg.Wait()
		close(ch)
	}()

	// Gather conditions and errors.
	conds := make([]*Conditions, 0, len(locations))
	errs := make([]*Error, 0, len(locations))
	for coe := range ch {
		if coe.Err != nil {
			errs = append(errs, coe.Err)
			continue
		}
		conds = append(conds, coe.Cond)
	}

	// Sort locations by rating and name.
	sort.Sort(ByRating(conds))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Errs  []*Error
	}{Conds: conds, Errs: errs}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	stars := starRx.FindAll(body, -1)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	rating := fmt.Sprintf("%d/5 stars", len(stars))
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}
<commit_msg>Use unicode stars instead of "3/5 stars"<commit_after>package waveguide

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"sort"
	"sync"

	"appengine"
	"appengine/urlfetch"
)

func init() {
	http.HandleFunc("/", handleRoot)
}

var tmpl = template.Must(template.New("main").Parse(`

		<title>Waveguide</title>

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.Name}}
					{{.Stars}}
					{{.Details}}

				{{end}}

		{{end}}

		{{if .Errs}}

		Errors:

			{{range .Errs}}

				{{.Loc.Name}}
				{{.Err}}

			{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

type Conditions struct {
	Loc     *Location
	Rating  int
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

type ConditionsOrError struct {
	Cond *Conditions
	Err  *Error
}

var locations = []Location{
	Location{"Bay Area: Lindamar-Pacifica", "/Linda-Mar-Pacifica-Surf-Report/819/"},
	Location{"Bay Area: Stinson Beach", "/Stinson-Beach-Surf-Report/4216/"},
	Location{"Bay Area: Ocean Beach SF", "/Ocean-Beach-Surf-Report/255/"},
	Location{"Bay Area: Princeton Jetty", "/Princeton-Jetty-Surf-Report/3679/"},
	Location{"Bali: Kuta Beach", "/Kuta-Beach-Surf-Report/566/"},
	Location{"Bolinas", "/Bolinas-Surf-Report/4221/"},
	Location{"Bolinas Jetty", "/Bolinas-Jetty-Surf-Report/4215/"},
	Location{"Cairns: Sunshine Beach", "/Sunshine-Beach-Surf-Report/1004/"},
	Location{"Oahu: Waikiki Beach", "/Queens-Canoes-Waikiki-Surf-Report/662/"},
	Location{"Kauai: Hanalei Bay", "/Hanalei-Bay-Surf-Report/3051/"},
	Location{"Kauai: Polihale", "/Polihale-Surf-Report/3080/"},
	Location{"Maui: Lahaina", "/Lahaina-Harbor-Breakwall-Surf-Report/4287/"},
	Location{"Oahu: Laniakea", "/Laniakea-Surf-Report/3672/"},
	Location{"Oahu: Pipeline", "/Pipeline-Backdoor-Surf-Report/616/"},
	Location{"Oahu: Sunset", "/Sunset-Surf-Report/657/"},
	Location{"Sydney: Manly Beach", "/Sydney-Manly-Surf-Report/526/"},
	Location{"Sydney: Bodi Beach", "/Sydney-Bondi-Surf-Report/996/"},
	Location{"New Zealand: Dunedin: Martins Bay", "/Martins-Bay-Surf-Report/3913/"},
	Location{"New Zealand: Dunedin: Papatowai", "/Papatowai-Surf-Report/124/"},
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	client := urlfetch.Client(ctx)

	// Spawn requests to get the conditions.
	ch := make(chan *ConditionsOrError)
	var wg sync.WaitGroup
	for _, loc := range locations {
		loc := loc
		wg.Add(1)
		go func() {
			defer wg.Done()
			cond, err := loc.GetConditions(client)
			coe := &ConditionsOrError{}
			if err != nil {
				coe.Err = &Error{
					Loc: &loc,
					Err: err,
				}
			} else {
				coe.Cond = cond
			}
			ch <- coe
		}()
	}

	go func() {
		wg.Wait()
		close(ch)
	}()

	// Gather conditions and errors.
	conds := make([]*Conditions, 0, len(locations))
	errs := make([]*Error, 0, len(locations))
	for coe := range ch {
		if coe.Err != nil {
			errs = append(errs, coe.Err)
			continue
		}
		conds = append(conds, coe.Cond)
	}

	// Sort locations by rating and name.
	sort.Sort(ByRating(conds))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Errs  []*Error
	}{Conds: conds, Errs: errs}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	foundStars := starRx.FindAll(body, -1)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	rating := len(foundStars)
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}

func (c *Conditions) Stars() string {
	runes := make([]rune, 0, 5)
	for i := 0; i < c.Rating; i++ {
		runes = append(runes, '★')
	}
	for i := 0; i < 5-c.Rating; i++ {
		runes = append(runes, '☆')
	}
	return string(runes)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
)

type ARWServer struct {
	sessions  SessionManager
	listenner net.Listener
	events    ARWEvents
}

func (arw *ARWServer) SendResponceToUser(arwObject *ARWObject, user *User) (err error) {

	return nil
}

func (arw *ARWServer) Initialize() {

	ln, err := net.Listen("tcp", ":8081")

	if err != nil {
		fmt.Println("Error listening...")
		os.Exit(1)
	}
	fmt.Print("Initialize Success... \n\n")

	arw.listenner = ln
}

func (arw *ARWServer) ProcessEvents() {
	for {
		conn, acceptErr := arw.listenner.Accept()
		// timeoutDuration := 1 * time.Millisecond

		// conn.SetReadDeadline(time.Now().Add(timeoutDuration))
		if acceptErr != nil {
			fmt.Println("Error Accepting :", acceptErr)
			os.Exit(1)
		}

		go arw.HandleRequests(conn)
	}
}

func (arw *ARWServer) PrivateConnection(conn net.Conn) {
	arw.sessions.StartSession(&conn)
}

func (arw *ARWServer) HandleRequests(conn net.Conn) {
	requestBytes := make([]byte, 1024)

	_, err := conn.Read(requestBytes)
	// requestBytes, err := bufio.NewReader(conn).ReadBytes('\n')
	if err != nil {
		if err != io.EOF {
			println("Read to server failed:", err.Error())
			os.Exit(1)
		}
	}

	requestBytes = bytes.Trim(requestBytes, "\x00")

	var arwObj ARWObject
	arwObj.Extract(requestBytes)

	fmt.Println("===> ", string(requestBytes))

	if arwObj.requestName == "ConnectionSuccess" {
		fmt.Println("Connection Success")
		conn.Write(arwObj.Compress())
	} else if arwObj.requestName == "LoginEvent" {
		fmt.Println("Login Event")
	}
}

func main() {
	var arwServer ARWServer
	arwServer.Initialize()

	arwServer.ProcessEvents()
}
<commit_msg>Connection Read Fixed<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
)

type ARWServer struct {
	sessions  SessionManager
	listenner net.Listener
	events    ARWEvents
}

func (arw *ARWServer) SendResponceToUser(arwObject *ARWObject, user *User) (err error) {

	return nil
}

func (arw *ARWServer) Initialize() {

	ln, err := net.Listen("tcp", ":8081")

	if err != nil {
		fmt.Println("Error listening...")
		os.Exit(1)
	}
	fmt.Print("Initialize Success... \n\n")

	arw.listenner = ln
}

func (arw *ARWServer) ProcessEvents() {
	for {
		conn, acceptErr := arw.listenner.Accept()
		// timeoutDuration := 1 * time.Millisecond

		// conn.SetReadDeadline(time.Now().Add(timeoutDuration))
		if acceptErr != nil {
			fmt.Println("Error Accepting :", acceptErr)
			os.Exit(1)
		}

		go arw.HandleRequests(conn)
	}
}

func (arw *ARWServer) PrivateConnection(conn net.Conn) {
	arw.sessions.StartSession(&conn)
}

func (arw *ARWServer) HandleRequests(conn net.Conn) {
	for {
		requestBytes := make([]byte, 1024)

		_, err := conn.Read(requestBytes)
		// requestBytes, err := bufio.NewReader(conn).ReadBytes('\n')
		if err != nil {
			if err != io.EOF {
				println("Read to server failed:", err.Error())
				os.Exit(1)
			} else {
				println("EOF Fail")
			}
		}

		requestBytes = bytes.Trim(requestBytes, "\x00")

		var arwObj ARWObject
		arwObj.Extract(requestBytes)

		fmt.Println("===> ", string(requestBytes))

		if arwObj.requestName == "ConnectionSuccess" {
			fmt.Println("Connection Success")
			conn.Write(arwObj.Compress())
		} else if arwObj.requestName == "LoginEvent" {
			fmt.Println("Login Event")
		}
	}

}

func main() {
	var arwServer ARWServer
	arwServer.Initialize()

	arwServer.ProcessEvents()
}
<|endoftext|>