<commit_before>// Package dnsimple provides a client for the DNSimple API.
// In order to use this package you will need a DNSimple account.
package dnsimple

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
)

const (
	// libraryVersion identifies the current library version.
	// This is a pro-forma convention given that Go dependencies
	// tends to be fetched directly from the repo.
	// It is also used in the user-agent identify the client.
	Version = "0.13.0"

	// defaultBaseURL to the DNSimple production API.
	defaultBaseURL = "https://api.dnsimple.com"

	// userAgent represents the default user agent used
	// when no other user agent is set.
	defaultUserAgent = "dnsimple-go/" + Version

	apiVersion = "v2"
)

// Client represents a client to the DNSimple API.
type Client struct {
	// HttpClient is the underlying HTTP client
	// used to communicate with the API.
	HttpClient *http.Client

	// Credentials used for accessing the DNSimple API
	Credentials Credentials

	// BaseURL for API requests.
	// Defaults to the public DNSimple API, but can be set to a different endpoint (e.g. the sandbox).
	BaseURL string

	// UserAgent used when communicating with the DNSimple API.
	UserAgent string

	// Services used for talking to different parts of the DNSimple API.
	Identity          *IdentityService
	Accounts          *AccountsService
	Certificates      *CertificatesService
	Contacts          *ContactsService
	Domains           *DomainsService
	Oauth             *OauthService
	Registrar         *RegistrarService
	Services          *ServicesService
	Templates         *TemplatesService
	Tlds              *TldsService
	VanityNameServers *VanityNameServersService
	Webhooks          *WebhooksService
	Zones             *ZonesService

	// Set to true to output debugging logs during API calls
	Debug bool
}

// ListOptions contains the common options you can pass to a List method
// in order to control parameters such as paginations and page number.
type ListOptions struct {
	// The page to return
	Page int `url:"page,omitempty"`

	// The number of entries to return per page
	PerPage int `url:"per_page,omitempty"`

	// The order criteria to sort the results.
	// The value is a comma-separated list of field[:direction],
	// eg. name | name:desc | name:desc,expiration:desc
	Sort string `url:"sort,omitempty"`
}

// NewClient returns a new DNSimple API client using the given credentials.
func NewClient(credentials Credentials) *Client {
	c := &Client{Credentials: credentials, HttpClient: &http.Client{}, BaseURL: defaultBaseURL}
	c.Identity = &IdentityService{client: c}
	c.Accounts = &AccountsService{client: c}
	c.Certificates = &CertificatesService{client: c}
	c.Contacts = &ContactsService{client: c}
	c.Domains = &DomainsService{client: c}
	c.Oauth = &OauthService{client: c}
	c.Registrar = &RegistrarService{client: c}
	c.Services = &ServicesService{client: c}
	c.Templates = &TemplatesService{client: c}
	c.Tlds = &TldsService{client: c}
	c.VanityNameServers = &VanityNameServersService{client: c}
	c.Webhooks = &WebhooksService{client: c}
	c.Zones = &ZonesService{client: c}
	return c
}

// NewRequest creates an API request.
// The path is expected to be a relative path and will be resolved
// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
	url := c.BaseURL + path

	body := new(bytes.Buffer)
	if payload != nil {
		err := json.NewEncoder(body).Encode(payload)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", formatUserAgent(c.UserAgent))
	for key, value := range c.Credentials.Headers() {
		req.Header.Add(key, value)
	}

	return req, nil
}

// formatUserAgent builds the final user agent to use for HTTP requests.
//
// If no custom user agent is provided, the default user agent is used.
//
//     dnsimple-go/1.0
//
// If a custom user agent is provided, the final user agent is the combination of the custom user agent
// prepended by the default user agent.
//
//     dnsimple-go/1.0 customAgentFlag
//
func formatUserAgent(customUserAgent string) string {
	if customUserAgent == "" {
		return defaultUserAgent
	}

	return fmt.Sprintf("%s %s", defaultUserAgent, customUserAgent)
}

func versioned(path string) string {
	return fmt.Sprintf("/%s/%s", apiVersion, strings.Trim(path, "/"))
}

func (c *Client) get(path string, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) post(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("POST", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) put(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("PUT", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) patch(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("PATCH", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) delete(path string, payload interface{}, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("DELETE", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

// Do sends an API request and returns the API response.
//
// The API response is JSON decoded and stored in the value pointed by obj,
// or returned as an error if an API error has occurred.
// If obj implements the io.Writer interface, the raw response body will be written to obj,
// without attempting to decode it.
func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error) {
	if c.Debug {
		log.Printf("Executing request (%v): %#v", req.URL, req)
	}

	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if c.Debug {
		log.Printf("Response received: %#v", resp)
	}

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	// If obj implements the io.Writer,
	// the response body is decoded into v.
	if obj != nil {
		if w, ok := obj.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(obj)
		}
	}

	return resp, err
}

// A Response represents an API response.
type Response struct {
	// HTTP response
	HttpResponse *http.Response

	// If the response is paginated, the Pagination will store them.
	Pagination *Pagination `json:"pagination"`
}

// RateLimit returns the maximum amount of requests this account can send in an hour.
func (r *Response) RateLimit() int {
	value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Limit"))
	return value
}

// RateLimitRemaining returns the remaining amount of requests this account can send within this hour window.
func (r *Response) RateLimitRemaining() int {
	value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Remaining"))
	return value
}

// RateLimitReset returns when the throttling window will be reset for this account.
func (r *Response) RateLimitReset() time.Time {
	value, _ := strconv.ParseInt(r.HttpResponse.Header.Get("X-RateLimit-Reset"), 10, 64)
	return time.Unix(value, 0)
}

// If the response is paginated, Pagination represents the pagination information.
type Pagination struct {
	CurrentPage  int `json:"current_page"`
	PerPage      int `json:"per_page"`
	TotalPages   int `json:"total_pages"`
	TotalEntries int `json:"total_entries"`
}

// An ErrorResponse represents an API response that generated an error.
type ErrorResponse struct {
	Response

	// human-readable message
	Message string `json:"message"`
}

// Error implements the error interface.
func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %v %v",
		r.HttpResponse.Request.Method, r.HttpResponse.Request.URL,
		r.HttpResponse.StatusCode, r.Message)
}

// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if the status code is different than 2xx. Specific requests
// may have additional requirements, but this is sufficient in most of the cases.
func CheckResponse(resp *http.Response) error {
	if code := resp.StatusCode; 200 <= code && code <= 299 {
		return nil
	}

	errorResponse := &ErrorResponse{}
	errorResponse.HttpResponse = resp

	err := json.NewDecoder(resp.Body).Decode(errorResponse)
	if err != nil {
		return err
	}

	return errorResponse
}

// addOptions adds the parameters in opt as URL query parameters to s.  opt
// must be a struct whose fields may contain "url" tags.
func addURLQueryOptions(path string, options interface{}) (string, error) {
	opt := reflect.ValueOf(options)

	// options is a pointer
	// return if the value of the pointer is nil,
	if opt.Kind() == reflect.Ptr && opt.IsNil() {
		return path, nil
	}

	// append the options to the URL
	u, err := url.Parse(path)
	if err != nil {
		return path, err
	}

	qs, err := query.Values(options)
	if err != nil {
		return path, err
	}

	uqs := u.Query()
	for k, _ := range qs {
		uqs.Set(k, qs.Get(k))
	}
	u.RawQuery = uqs.Encode()

	return u.String(), nil
}
<commit_msg>Release 0.14.0<commit_after>// Package dnsimple provides a client for the DNSimple API.
// In order to use this package you will need a DNSimple account.
package dnsimple

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
)

const (
	// libraryVersion identifies the current library version.
	// This is a pro-forma convention given that Go dependencies
	// tends to be fetched directly from the repo.
	// It is also used in the user-agent identify the client.
	Version = "0.14.0"

	// defaultBaseURL to the DNSimple production API.
	defaultBaseURL = "https://api.dnsimple.com"

	// userAgent represents the default user agent used
	// when no other user agent is set.
	defaultUserAgent = "dnsimple-go/" + Version

	apiVersion = "v2"
)

// Client represents a client to the DNSimple API.
type Client struct {
	// HttpClient is the underlying HTTP client
	// used to communicate with the API.
	HttpClient *http.Client

	// Credentials used for accessing the DNSimple API
	Credentials Credentials

	// BaseURL for API requests.
	// Defaults to the public DNSimple API, but can be set to a different endpoint (e.g. the sandbox).
	BaseURL string

	// UserAgent used when communicating with the DNSimple API.
	UserAgent string

	// Services used for talking to different parts of the DNSimple API.
	Identity          *IdentityService
	Accounts          *AccountsService
	Certificates      *CertificatesService
	Contacts          *ContactsService
	Domains           *DomainsService
	Oauth             *OauthService
	Registrar         *RegistrarService
	Services          *ServicesService
	Templates         *TemplatesService
	Tlds              *TldsService
	VanityNameServers *VanityNameServersService
	Webhooks          *WebhooksService
	Zones             *ZonesService

	// Set to true to output debugging logs during API calls
	Debug bool
}

// ListOptions contains the common options you can pass to a List method
// in order to control parameters such as paginations and page number.
type ListOptions struct {
	// The page to return
	Page int `url:"page,omitempty"`

	// The number of entries to return per page
	PerPage int `url:"per_page,omitempty"`

	// The order criteria to sort the results.
	// The value is a comma-separated list of field[:direction],
	// eg. name | name:desc | name:desc,expiration:desc
	Sort string `url:"sort,omitempty"`
}

// NewClient returns a new DNSimple API client using the given credentials.
func NewClient(credentials Credentials) *Client {
	c := &Client{Credentials: credentials, HttpClient: &http.Client{}, BaseURL: defaultBaseURL}
	c.Identity = &IdentityService{client: c}
	c.Accounts = &AccountsService{client: c}
	c.Certificates = &CertificatesService{client: c}
	c.Contacts = &ContactsService{client: c}
	c.Domains = &DomainsService{client: c}
	c.Oauth = &OauthService{client: c}
	c.Registrar = &RegistrarService{client: c}
	c.Services = &ServicesService{client: c}
	c.Templates = &TemplatesService{client: c}
	c.Tlds = &TldsService{client: c}
	c.VanityNameServers = &VanityNameServersService{client: c}
	c.Webhooks = &WebhooksService{client: c}
	c.Zones = &ZonesService{client: c}
	return c
}

// NewRequest creates an API request.
// The path is expected to be a relative path and will be resolved
// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
	url := c.BaseURL + path

	body := new(bytes.Buffer)
	if payload != nil {
		err := json.NewEncoder(body).Encode(payload)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", formatUserAgent(c.UserAgent))
	for key, value := range c.Credentials.Headers() {
		req.Header.Add(key, value)
	}

	return req, nil
}

// formatUserAgent builds the final user agent to use for HTTP requests.
//
// If no custom user agent is provided, the default user agent is used.
//
//     dnsimple-go/1.0
//
// If a custom user agent is provided, the final user agent is the combination of the custom user agent
// prepended by the default user agent.
//
//     dnsimple-go/1.0 customAgentFlag
//
func formatUserAgent(customUserAgent string) string {
	if customUserAgent == "" {
		return defaultUserAgent
	}

	return fmt.Sprintf("%s %s", defaultUserAgent, customUserAgent)
}

func versioned(path string) string {
	return fmt.Sprintf("/%s/%s", apiVersion, strings.Trim(path, "/"))
}

func (c *Client) get(path string, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) post(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("POST", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) put(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("PUT", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) patch(path string, payload, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("PATCH", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

func (c *Client) delete(path string, payload interface{}, obj interface{}) (*http.Response, error) {
	req, err := c.NewRequest("DELETE", path, payload)
	if err != nil {
		return nil, err
	}

	return c.Do(req, obj)
}

// Do sends an API request and returns the API response.
//
// The API response is JSON decoded and stored in the value pointed by obj,
// or returned as an error if an API error has occurred.
// If obj implements the io.Writer interface, the raw response body will be written to obj,
// without attempting to decode it.
func (c *Client) Do(req *http.Request, obj interface{}) (*http.Response, error) {
	if c.Debug {
		log.Printf("Executing request (%v): %#v", req.URL, req)
	}

	resp, err := c.HttpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if c.Debug {
		log.Printf("Response received: %#v", resp)
	}

	err = CheckResponse(resp)
	if err != nil {
		return resp, err
	}

	// If obj implements the io.Writer,
	// the response body is decoded into v.
	if obj != nil {
		if w, ok := obj.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(obj)
		}
	}

	return resp, err
}

// A Response represents an API response.
type Response struct {
	// HTTP response
	HttpResponse *http.Response

	// If the response is paginated, the Pagination will store them.
	Pagination *Pagination `json:"pagination"`
}

// RateLimit returns the maximum amount of requests this account can send in an hour.
func (r *Response) RateLimit() int {
	value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Limit"))
	return value
}

// RateLimitRemaining returns the remaining amount of requests this account can send within this hour window.
func (r *Response) RateLimitRemaining() int {
	value, _ := strconv.Atoi(r.HttpResponse.Header.Get("X-RateLimit-Remaining"))
	return value
}

// RateLimitReset returns when the throttling window will be reset for this account.
func (r *Response) RateLimitReset() time.Time {
	value, _ := strconv.ParseInt(r.HttpResponse.Header.Get("X-RateLimit-Reset"), 10, 64)
	return time.Unix(value, 0)
}

// If the response is paginated, Pagination represents the pagination information.
type Pagination struct {
	CurrentPage  int `json:"current_page"`
	PerPage      int `json:"per_page"`
	TotalPages   int `json:"total_pages"`
	TotalEntries int `json:"total_entries"`
}

// An ErrorResponse represents an API response that generated an error.
type ErrorResponse struct {
	Response

	// human-readable message
	Message string `json:"message"`
}

// Error implements the error interface.
func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %v %v",
		r.HttpResponse.Request.Method, r.HttpResponse.Request.URL,
		r.HttpResponse.StatusCode, r.Message)
}

// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if the status code is different than 2xx. Specific requests
// may have additional requirements, but this is sufficient in most of the cases.
func CheckResponse(resp *http.Response) error {
	if code := resp.StatusCode; 200 <= code && code <= 299 {
		return nil
	}

	errorResponse := &ErrorResponse{}
	errorResponse.HttpResponse = resp

	err := json.NewDecoder(resp.Body).Decode(errorResponse)
	if err != nil {
		return err
	}

	return errorResponse
}

// addOptions adds the parameters in opt as URL query parameters to s.  opt
// must be a struct whose fields may contain "url" tags.
func addURLQueryOptions(path string, options interface{}) (string, error) {
	opt := reflect.ValueOf(options)

	// options is a pointer
	// return if the value of the pointer is nil,
	if opt.Kind() == reflect.Ptr && opt.IsNil() {
		return path, nil
	}

	// append the options to the URL
	u, err := url.Parse(path)
	if err != nil {
		return path, err
	}

	qs, err := query.Values(options)
	if err != nil {
		return path, err
	}

	uqs := u.Query()
	for k, _ := range qs {
		uqs.Set(k, qs.Get(k))
	}
	u.RawQuery = uqs.Encode()

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


<commit_before>package command

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

	"github.com/ForceCLI/force/config"
	. "github.com/ForceCLI/force/error"
	. "github.com/ForceCLI/force/lib"
)

var cmdExport = &Command{
	Run:   runExport,
	Usage: "export [options] [dir]",
	Short: "Export metadata to a local directory",
	Long: `
Export metadata to a local directory

Export Options
  -w, -warnings  # Display warnings about metadata that cannot be retrieved

Examples:

  force export

  force export org/schema
`,
}

var (
	showWarnings bool
)

func init() {
	cmdExport.Flag.BoolVar(&showWarnings, "w", false, "show warnings")
	cmdExport.Flag.BoolVar(&showWarnings, "warnings", false, "show warnings")
}

func runExport(cmd *Command, args []string) {
	// Get path from args if available
	var err error
	var root string
	if len(args) == 1 {
		root, err = filepath.Abs(args[0])
	}
	if err != nil {
		fmt.Printf("Error obtaining file path\n")
		ErrorAndExit(err.Error())
	}
	force, _ := ActiveForce()
	sobjects, err := force.ListSobjects()
	if err != nil {
		ErrorAndExit(err.Error())
	}
	stdObjects := make([]string, 1, len(sobjects)+1)
	stdObjects[0] = "*"
	for _, sobject := range sobjects {
		name := sobject["name"].(string)
		if !sobject["custom"].(bool) && !strings.HasSuffix(name, "__Tag") && !strings.HasSuffix(name, "__History") && !strings.HasSuffix(name, "__Share") {
			stdObjects = append(stdObjects, name)
		}
	}
	query := ForceMetadataQuery{
		{Name: []string{"AccountSettings"}, Members: []string{"*"}},
		{Name: []string{"ActivitiesSettings"}, Members: []string{"*"}},
		{Name: []string{"AddressSettings"}, Members: []string{"*"}},
		{Name: []string{"AnalyticSnapshot"}, Members: []string{"*"}},
		{Name: []string{"ApexClass"}, Members: []string{"*"}},
		{Name: []string{"ApexComponent"}, Members: []string{"*"}},
		{Name: []string{"ApexPage"}, Members: []string{"*"}},
		{Name: []string{"ApexTrigger"}, Members: []string{"*"}},
		{Name: []string{"ApprovalProcess"}, Members: []string{"*"}},
		{Name: []string{"AssignmentRules"}, Members: []string{"*"}},
		{Name: []string{"AuraDefinitionBundle"}, Members: []string{"*"}},
		{Name: []string{"AuthProvider"}, Members: []string{"*"}},
		{Name: []string{"AutoResponseRules"}, Members: []string{"*"}},
		{Name: []string{"BusinessHoursSettings"}, Members: []string{"*"}},
		{Name: []string{"BusinessProcess"}, Members: []string{"*"}},
		{Name: []string{"CallCenter"}, Members: []string{"*"}},
		{Name: []string{"CaseSettings"}, Members: []string{"*"}},
		{Name: []string{"ChatterAnswersSettings"}, Members: []string{"*"}},
		{Name: []string{"CompanySettings"}, Members: []string{"*"}},
		{Name: []string{"Community"}, Members: []string{"*"}},
		{Name: []string{"CompactLayout"}, Members: []string{"*"}},
		{Name: []string{"ConnectedApp"}, Members: []string{"*"}},
		{Name: []string{"ContractSettings"}, Members: []string{"*"}},
		{Name: []string{"CustomApplication"}, Members: []string{"*"}},
		{Name: []string{"CustomApplicationComponent"}, Members: []string{"*"}},
		{Name: []string{"CustomField"}, Members: []string{"*"}},
		{Name: []string{"CustomLabels"}, Members: []string{"*"}},
		{Name: []string{"CustomMetadata"}, Members: []string{"*"}},
		{Name: []string{"CustomObject"}, Members: stdObjects},
		{Name: []string{"CustomObjectTranslation"}, Members: []string{"*"}},
		{Name: []string{"CustomPageWebLink"}, Members: []string{"*"}},
		{Name: []string{"CustomPermission"}, Members: []string{"*"}},
		{Name: []string{"CustomSite"}, Members: []string{"*"}},
		{Name: []string{"CustomTab"}, Members: []string{"*"}},
		{Name: []string{"DataCategoryGroup"}, Members: []string{"*"}},
		{Name: []string{"DuplicateRule"}, Members: []string{"*"}},
		{Name: []string{"EntitlementProcess"}, Members: []string{"*"}},
		{Name: []string{"EntitlementSettings"}, Members: []string{"*"}},
		{Name: []string{"EntitlementTemplate"}, Members: []string{"*"}},
		{Name: []string{"ExternalDataSource"}, Members: []string{"*"}},
		{Name: []string{"FieldSet"}, Members: []string{"*"}},
		{Name: []string{"Flow"}, Members: []string{"*"}},
		{Name: []string{"FlowDefinition"}, Members: []string{"*"}},
		{Name: []string{"Folder"}, Members: []string{"*"}},
		{Name: []string{"ForecastingSettings"}, Members: []string{"*"}},
		{Name: []string{"Group"}, Members: []string{"*"}},
		{Name: []string{"HomePageComponent"}, Members: []string{"*"}},
		{Name: []string{"HomePageLayout"}, Members: []string{"*"}},
		{Name: []string{"IdeasSettings"}, Members: []string{"*"}},
		{Name: []string{"KnowledgeSettings"}, Members: []string{"*"}},
		{Name: []string{"Layout"}, Members: []string{"*"}},
		{Name: []string{"Letterhead"}, Members: []string{"*"}},
		{Name: []string{"ListView"}, Members: []string{"*"}},
		{Name: []string{"LiveAgentSettings"}, Members: []string{"*"}},
		{Name: []string{"LiveChatAgentConfig"}, Members: []string{"*"}},
		{Name: []string{"LiveChatButton"}, Members: []string{"*"}},
		{Name: []string{"LiveChatDeployment"}, Members: []string{"*"}},
		{Name: []string{"MatchingRules"}, Members: []string{"*"}},
		{Name: []string{"MilestoneType"}, Members: []string{"*"}},
		{Name: []string{"MobileSettings"}, Members: []string{"*"}},
		{Name: []string{"NamedFilter"}, Members: []string{"*"}},
		{Name: []string{"Network"}, Members: []string{"*"}},
		{Name: []string{"OpportunitySettings"}, Members: []string{"*"}},
		{Name: []string{"PermissionSet"}, Members: []string{"*"}},
		{Name: []string{"Portal"}, Members: []string{"*"}},
		{Name: []string{"PostTemplate"}, Members: []string{"*"}},
		{Name: []string{"ProductSettings"}, Members: []string{"*"}},
		{Name: []string{"Profile"}, Members: []string{"*"}},
		{Name: []string{"ProfileSessionSetting"}, Members: []string{"*"}},
		{Name: []string{"Queue"}, Members: []string{"*"}},
		{Name: []string{"QuickAction"}, Members: []string{"*"}},
		{Name: []string{"QuoteSettings"}, Members: []string{"*"}},
		{Name: []string{"RecordType"}, Members: []string{"*"}},
		{Name: []string{"RemoteSiteSetting"}, Members: []string{"*"}},
		{Name: []string{"ReportType"}, Members: []string{"*"}},
		{Name: []string{"Role"}, Members: []string{"*"}},
		{Name: []string{"SamlSsoConfig"}, Members: []string{"*"}},
		{Name: []string{"Scontrol"}, Members: []string{"*"}},
		{Name: []string{"SecuritySettings"}, Members: []string{"*"}},
		{Name: []string{"SharingReason"}, Members: []string{"*"}},
		{Name: []string{"SharingRules"}, Members: []string{"*"}},
		{Name: []string{"Skill"}, Members: []string{"*"}},
		{Name: []string{"StaticResource"}, Members: []string{"*"}},
		{Name: []string{"Territory"}, Members: []string{"*"}},
		{Name: []string{"Translations"}, Members: []string{"*"}},
		{Name: []string{"ValidationRule"}, Members: []string{"*"}},
		{Name: []string{"Workflow"}, Members: []string{"*"}},
	}

	folders, err := force.GetAllFolders()
	if err != nil {
		err = fmt.Errorf("Could not get folders: %s", err.Error())
		ErrorAndExit(err.Error())
	}
	for foldersType, foldersName := range folders {
		if foldersType == "Email" {
			foldersType = "EmailTemplate"
		}
		members, err := force.GetMetadataInFolders(foldersType, foldersName)
		if err != nil {
			err = fmt.Errorf("Could not get metadata in folders: %s", err.Error())
			ErrorAndExit(err.Error())
		}
		query = append(query, ForceMetadataQueryElement{Name: []string{string(foldersType)}, Members: members})
	}

	if root == "" {
		root, err = config.GetSourceDir()
		if err != nil {
			fmt.Printf("Error obtaining root directory\n")
			ErrorAndExit(err.Error())
		}
	}
	files, problems, err := force.Metadata.Retrieve(query)
	if err != nil {
		fmt.Printf("Encountered and error with retrieve...\n")
		ErrorAndExit(err.Error())
	}
	if showWarnings {
		for _, problem := range problems {
			fmt.Fprintln(os.Stderr, problem)
		}
	}
	for name, data := range files {
		file := filepath.Join(root, name)
		dir := filepath.Dir(file)
		if err := os.MkdirAll(dir, 0755); err != nil {
			ErrorAndExit(err.Error())
		}
		if err := ioutil.WriteFile(filepath.Join(root, name), data, 0644); err != nil {
			ErrorAndExit(err.Error())
		}
	}
	fmt.Printf("Exported to %s\n", root)
}
<commit_msg>Export FlexiPages<commit_after>package command

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

	"github.com/ForceCLI/force/config"
	. "github.com/ForceCLI/force/error"
	. "github.com/ForceCLI/force/lib"
)

var cmdExport = &Command{
	Run:   runExport,
	Usage: "export [options] [dir]",
	Short: "Export metadata to a local directory",
	Long: `
Export metadata to a local directory

Export Options
  -w, -warnings  # Display warnings about metadata that cannot be retrieved

Examples:

  force export

  force export org/schema
`,
}

var (
	showWarnings bool
)

func init() {
	cmdExport.Flag.BoolVar(&showWarnings, "w", false, "show warnings")
	cmdExport.Flag.BoolVar(&showWarnings, "warnings", false, "show warnings")
}

func runExport(cmd *Command, args []string) {
	// Get path from args if available
	var err error
	var root string
	if len(args) == 1 {
		root, err = filepath.Abs(args[0])
	}
	if err != nil {
		fmt.Printf("Error obtaining file path\n")
		ErrorAndExit(err.Error())
	}
	force, _ := ActiveForce()
	sobjects, err := force.ListSobjects()
	if err != nil {
		ErrorAndExit(err.Error())
	}
	stdObjects := make([]string, 1, len(sobjects)+1)
	stdObjects[0] = "*"
	for _, sobject := range sobjects {
		name := sobject["name"].(string)
		if !sobject["custom"].(bool) && !strings.HasSuffix(name, "__Tag") && !strings.HasSuffix(name, "__History") && !strings.HasSuffix(name, "__Share") {
			stdObjects = append(stdObjects, name)
		}
	}
	query := ForceMetadataQuery{
		{Name: []string{"AccountSettings"}, Members: []string{"*"}},
		{Name: []string{"ActivitiesSettings"}, Members: []string{"*"}},
		{Name: []string{"AddressSettings"}, Members: []string{"*"}},
		{Name: []string{"AnalyticSnapshot"}, Members: []string{"*"}},
		{Name: []string{"ApexClass"}, Members: []string{"*"}},
		{Name: []string{"ApexComponent"}, Members: []string{"*"}},
		{Name: []string{"ApexPage"}, Members: []string{"*"}},
		{Name: []string{"ApexTrigger"}, Members: []string{"*"}},
		{Name: []string{"ApprovalProcess"}, Members: []string{"*"}},
		{Name: []string{"AssignmentRules"}, Members: []string{"*"}},
		{Name: []string{"AuraDefinitionBundle"}, Members: []string{"*"}},
		{Name: []string{"AuthProvider"}, Members: []string{"*"}},
		{Name: []string{"AutoResponseRules"}, Members: []string{"*"}},
		{Name: []string{"BusinessHoursSettings"}, Members: []string{"*"}},
		{Name: []string{"BusinessProcess"}, Members: []string{"*"}},
		{Name: []string{"CallCenter"}, Members: []string{"*"}},
		{Name: []string{"CaseSettings"}, Members: []string{"*"}},
		{Name: []string{"ChatterAnswersSettings"}, Members: []string{"*"}},
		{Name: []string{"CompanySettings"}, Members: []string{"*"}},
		{Name: []string{"Community"}, Members: []string{"*"}},
		{Name: []string{"CompactLayout"}, Members: []string{"*"}},
		{Name: []string{"ConnectedApp"}, Members: []string{"*"}},
		{Name: []string{"ContractSettings"}, Members: []string{"*"}},
		{Name: []string{"CustomApplication"}, Members: []string{"*"}},
		{Name: []string{"CustomApplicationComponent"}, Members: []string{"*"}},
		{Name: []string{"CustomField"}, Members: []string{"*"}},
		{Name: []string{"CustomLabels"}, Members: []string{"*"}},
		{Name: []string{"CustomMetadata"}, Members: []string{"*"}},
		{Name: []string{"CustomObject"}, Members: stdObjects},
		{Name: []string{"CustomObjectTranslation"}, Members: []string{"*"}},
		{Name: []string{"CustomPageWebLink"}, Members: []string{"*"}},
		{Name: []string{"CustomPermission"}, Members: []string{"*"}},
		{Name: []string{"CustomSite"}, Members: []string{"*"}},
		{Name: []string{"CustomTab"}, Members: []string{"*"}},
		{Name: []string{"DataCategoryGroup"}, Members: []string{"*"}},
		{Name: []string{"DuplicateRule"}, Members: []string{"*"}},
		{Name: []string{"EntitlementProcess"}, Members: []string{"*"}},
		{Name: []string{"EntitlementSettings"}, Members: []string{"*"}},
		{Name: []string{"EntitlementTemplate"}, Members: []string{"*"}},
		{Name: []string{"ExternalDataSource"}, Members: []string{"*"}},
		{Name: []string{"FieldSet"}, Members: []string{"*"}},
		{Name: []string{"FlexiPage"}, Members: []string{"*"}},
		{Name: []string{"Flow"}, Members: []string{"*"}},
		{Name: []string{"FlowDefinition"}, Members: []string{"*"}},
		{Name: []string{"Folder"}, Members: []string{"*"}},
		{Name: []string{"ForecastingSettings"}, Members: []string{"*"}},
		{Name: []string{"Group"}, Members: []string{"*"}},
		{Name: []string{"HomePageComponent"}, Members: []string{"*"}},
		{Name: []string{"HomePageLayout"}, Members: []string{"*"}},
		{Name: []string{"IdeasSettings"}, Members: []string{"*"}},
		{Name: []string{"KnowledgeSettings"}, Members: []string{"*"}},
		{Name: []string{"Layout"}, Members: []string{"*"}},
		{Name: []string{"Letterhead"}, Members: []string{"*"}},
		{Name: []string{"ListView"}, Members: []string{"*"}},
		{Name: []string{"LiveAgentSettings"}, Members: []string{"*"}},
		{Name: []string{"LiveChatAgentConfig"}, Members: []string{"*"}},
		{Name: []string{"LiveChatButton"}, Members: []string{"*"}},
		{Name: []string{"LiveChatDeployment"}, Members: []string{"*"}},
		{Name: []string{"MatchingRules"}, Members: []string{"*"}},
		{Name: []string{"MilestoneType"}, Members: []string{"*"}},
		{Name: []string{"MobileSettings"}, Members: []string{"*"}},
		{Name: []string{"NamedFilter"}, Members: []string{"*"}},
		{Name: []string{"Network"}, Members: []string{"*"}},
		{Name: []string{"OpportunitySettings"}, Members: []string{"*"}},
		{Name: []string{"PermissionSet"}, Members: []string{"*"}},
		{Name: []string{"Portal"}, Members: []string{"*"}},
		{Name: []string{"PostTemplate"}, Members: []string{"*"}},
		{Name: []string{"ProductSettings"}, Members: []string{"*"}},
		{Name: []string{"Profile"}, Members: []string{"*"}},
		{Name: []string{"ProfileSessionSetting"}, Members: []string{"*"}},
		{Name: []string{"Queue"}, Members: []string{"*"}},
		{Name: []string{"QuickAction"}, Members: []string{"*"}},
		{Name: []string{"QuoteSettings"}, Members: []string{"*"}},
		{Name: []string{"RecordType"}, Members: []string{"*"}},
		{Name: []string{"RemoteSiteSetting"}, Members: []string{"*"}},
		{Name: []string{"ReportType"}, Members: []string{"*"}},
		{Name: []string{"Role"}, Members: []string{"*"}},
		{Name: []string{"SamlSsoConfig"}, Members: []string{"*"}},
		{Name: []string{"Scontrol"}, Members: []string{"*"}},
		{Name: []string{"SecuritySettings"}, Members: []string{"*"}},
		{Name: []string{"SharingReason"}, Members: []string{"*"}},
		{Name: []string{"SharingRules"}, Members: []string{"*"}},
		{Name: []string{"Skill"}, Members: []string{"*"}},
		{Name: []string{"StaticResource"}, Members: []string{"*"}},
		{Name: []string{"Territory"}, Members: []string{"*"}},
		{Name: []string{"Translations"}, Members: []string{"*"}},
		{Name: []string{"ValidationRule"}, Members: []string{"*"}},
		{Name: []string{"Workflow"}, Members: []string{"*"}},
	}

	folders, err := force.GetAllFolders()
	if err != nil {
		err = fmt.Errorf("Could not get folders: %s", err.Error())
		ErrorAndExit(err.Error())
	}
	for foldersType, foldersName := range folders {
		if foldersType == "Email" {
			foldersType = "EmailTemplate"
		}
		members, err := force.GetMetadataInFolders(foldersType, foldersName)
		if err != nil {
			err = fmt.Errorf("Could not get metadata in folders: %s", err.Error())
			ErrorAndExit(err.Error())
		}
		query = append(query, ForceMetadataQueryElement{Name: []string{string(foldersType)}, Members: members})
	}

	if root == "" {
		root, err = config.GetSourceDir()
		if err != nil {
			fmt.Printf("Error obtaining root directory\n")
			ErrorAndExit(err.Error())
		}
	}
	files, problems, err := force.Metadata.Retrieve(query)
	if err != nil {
		fmt.Printf("Encountered and error with retrieve...\n")
		ErrorAndExit(err.Error())
	}
	if showWarnings {
		for _, problem := range problems {
			fmt.Fprintln(os.Stderr, problem)
		}
	}
	for name, data := range files {
		file := filepath.Join(root, name)
		dir := filepath.Dir(file)
		if err := os.MkdirAll(dir, 0755); err != nil {
			ErrorAndExit(err.Error())
		}
		if err := ioutil.WriteFile(filepath.Join(root, name), data, 0644); err != nil {
			ErrorAndExit(err.Error())
		}
	}
	fmt.Printf("Exported to %s\n", root)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/posener/complete"
)

var _ cli.Command = (*KVPutCommand)(nil)
var _ cli.CommandAutocomplete = (*KVPutCommand)(nil)

type KVPutCommand struct {
	*BaseCommand

	flagCAS   int
	testStdin io.Reader // for tests
}

func (c *KVPutCommand) Synopsis() string {
	return "Sets or updates data in the KV store"
}

func (c *KVPutCommand) Help() string {
	helpText := `
Usage: vault kv put [options] KEY [DATA]

  Writes the data to the given path in the key-value store. The data can be of
  any type.

      $ vault kv put secret/foo bar=baz

  The data can also be consumed from a file on disk by prefixing with the "@"
  symbol. For example:

      $ vault kv put secret/foo @data.json

  Or it can be read from stdin using the "-" symbol:

      $ echo "abcd1234" | vault kv put secret/foo bar=-

  To perform a Check-And-Set operation, specify the -cas flag with the
  appropriate version number corresponding to the key you want to perform
  the CAS operation on:

      $ vault kv put -cas=1 secret/foo bar=baz

  Additional flags and more advanced use cases are detailed below.

` + c.Flags().Help()
	return strings.TrimSpace(helpText)
}

func (c *KVPutCommand) Flags() *FlagSets {
	set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)

	// Common Options
	f := set.NewFlagSet("Common Options")

	f.IntVar(&IntVar{
		Name:    "cas",
		Target:  &c.flagCAS,
		Default: -1,
		Usage: `Specifies to use a Check-And-Set operation. If not set the write
		will be allowed. If set to 0 a write will only be allowed if the key
		doesn’t exist. If the index is non-zero the write will only be allowed
		if the key’s current version matches the version specified in the cas
		parameter.`,
	})

	return set
}

func (c *KVPutCommand) AutocompleteArgs() complete.Predictor {
	return nil
}

func (c *KVPutCommand) AutocompleteFlags() complete.Flags {
	return c.Flags().Completions()
}

func (c *KVPutCommand) Run(args []string) int {
	f := c.Flags()

	if err := f.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	args = f.Args()
	// Pull our fake stdin if needed
	stdin := (io.Reader)(os.Stdin)
	if c.testStdin != nil {
		stdin = c.testStdin
	}

	var err error
	path := sanitizePath(args[0])
	path, err = addPrefixToVKVPath(path, "data")
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	data, err := parseArgsData(stdin, args[1:])
	if err != nil {
		c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
		return 1
	}

	data = map[string]interface{}{
		"data":    data,
		"options": map[string]interface{}{},
	}

	if c.flagCAS > -1 {
		data["options"].(map[string]interface{})["cas"] = c.flagCAS
	}

	client, err := c.Client()
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	secret, err := kvWriteRequest(client, path, data)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
		return 2
	}
	if secret == nil {
		// Don't output anything unless using the "table" format
		if Format(c.UI) == "table" {
			c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
		}
		return 0
	}

	return OutputSecret(c.UI, secret)
}
<commit_msg>Fix panic on kv put command with no arguments (#4389)<commit_after>package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/posener/complete"
)

var _ cli.Command = (*KVPutCommand)(nil)
var _ cli.CommandAutocomplete = (*KVPutCommand)(nil)

type KVPutCommand struct {
	*BaseCommand

	flagCAS   int
	testStdin io.Reader // for tests
}

func (c *KVPutCommand) Synopsis() string {
	return "Sets or updates data in the KV store"
}

func (c *KVPutCommand) Help() string {
	helpText := `
Usage: vault kv put [options] KEY [DATA]

  Writes the data to the given path in the key-value store. The data can be of
  any type.

      $ vault kv put secret/foo bar=baz

  The data can also be consumed from a file on disk by prefixing with the "@"
  symbol. For example:

      $ vault kv put secret/foo @data.json

  Or it can be read from stdin using the "-" symbol:

      $ echo "abcd1234" | vault kv put secret/foo bar=-

  To perform a Check-And-Set operation, specify the -cas flag with the
  appropriate version number corresponding to the key you want to perform
  the CAS operation on:

      $ vault kv put -cas=1 secret/foo bar=baz

  Additional flags and more advanced use cases are detailed below.

` + c.Flags().Help()
	return strings.TrimSpace(helpText)
}

func (c *KVPutCommand) Flags() *FlagSets {
	set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat)

	// Common Options
	f := set.NewFlagSet("Common Options")

	f.IntVar(&IntVar{
		Name:    "cas",
		Target:  &c.flagCAS,
		Default: -1,
		Usage: `Specifies to use a Check-And-Set operation. If not set the write
		will be allowed. If set to 0 a write will only be allowed if the key
		doesn’t exist. If the index is non-zero the write will only be allowed
		if the key’s current version matches the version specified in the cas
		parameter.`,
	})

	return set
}

func (c *KVPutCommand) AutocompleteArgs() complete.Predictor {
	return nil
}

func (c *KVPutCommand) AutocompleteFlags() complete.Flags {
	return c.Flags().Completions()
}

func (c *KVPutCommand) Run(args []string) int {
	f := c.Flags()

	if err := f.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	args = f.Args()
	// Pull our fake stdin if needed
	stdin := (io.Reader)(os.Stdin)
	if c.testStdin != nil {
		stdin = c.testStdin
	}

	if len(args) < 1 {
		c.UI.Error(fmt.Sprintf("Not enough arguments (expected 1, got %d)", len(args)))
		return 1
	}

	var err error
	path := sanitizePath(args[0])
	path, err = addPrefixToVKVPath(path, "data")
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	data, err := parseArgsData(stdin, args[1:])
	if err != nil {
		c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
		return 1
	}

	data = map[string]interface{}{
		"data":    data,
		"options": map[string]interface{}{},
	}

	if c.flagCAS > -1 {
		data["options"].(map[string]interface{})["cas"] = c.flagCAS
	}

	client, err := c.Client()
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	secret, err := kvWriteRequest(client, path, data)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err))
		return 2
	}
	if secret == nil {
		// Don't output anything unless using the "table" format
		if Format(c.UI) == "table" {
			c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path))
		}
		return 0
	}

	return OutputSecret(c.UI, secret)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"syscall"
	"strings"

	"github.com/wantedly/developers-account-mapper/store"
)

type SetenvCommand struct {
	Meta
}

func (c *SetenvCommand) Run(args []string) int {
	loginName := args[0]
	if args[1] != "exec" {
		log.Println("$ developers-account-mapper setenv <user> exec")
		return 1
	}

	s := store.NewDynamoDB()

	user, err := s.GetUserByLoginName(loginName)
	if err != nil {
		log.Println(err)
		return 1
	}

	envs := os.Environ()
	envs = append(envs, fmt.Sprintf("%s=%s", "GITHUB_USERNAME", user.GitHubUsername))

	execCmd := exec.Command(args[1], args[2:]...)
	execCmd.Env = envs
	execCmd.Stderr = os.Stderr
	execCmd.Stdout = os.Stdout
	err = execCmd.Run()

	if execCmd.Process == nil {
		log.Println(err)
	}

	os.Exit(execCmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus())

	return 0
}

func (c *SetenvCommand) Synopsis() string {
	return "Set account information as env vars and exec commands"
}

func (c *SetenvCommand) Help() string {
	helpText := `

`
	return strings.TrimSpace(helpText)
}
<commit_msg>Remove os.Exit<commit_after>package command

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/wantedly/developers-account-mapper/store"
)

type SetenvCommand struct {
	Meta
}

func (c *SetenvCommand) Run(args []string) int {
	loginName := args[0]
	if args[1] != "exec" {
		log.Println("$ developers-account-mapper setenv <user> exec")
		return 1
	}

	s := store.NewDynamoDB()

	user, err := s.GetUserByLoginName(loginName)
	if err != nil {
		log.Println(err)
		return 1
	}

	envs := os.Environ()
	envs = append(envs, fmt.Sprintf("%s=%s", "GITHUB_USERNAME", user.GitHubUsername))

	execCmd := exec.Command(args[2], args[3:]...)
	execCmd.Env = envs
	execCmd.Stderr = os.Stderr
	execCmd.Stdout = os.Stdout
	err = execCmd.Run()

	return 0
}

func (c *SetenvCommand) Synopsis() string {
	return "Set account information as env vars and exec commands"
}

func (c *SetenvCommand) Help() string {
	helpText := `

`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
)

type StatusCommand struct {
	Ui cli.Ui
}

func (c *StatusCommand) Help() string {
	helpText := `
Usage: nomad status [options] <job>

  Displays information about the given job. If no job ID
  is given, this command will dump a list of all jobs.

Options:

  -help
    Display this message

  -http-addr
    Address of the Nomad API to connect.
    Default = http://127.0.0.1:4646
`
	return strings.TrimSpace(helpText)
}

func (c *StatusCommand) Synopsis() string {
	return "Displays information about jobs"
}

func (c *StatusCommand) Run(args []string) int {
	var httpAddr *string

	flags := flag.NewFlagSet("status", flag.ContinueOnError)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	httpAddr = HttpAddrFlag(flags)

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we either got no jobs or exactly one.
	if len(flags.Args()) > 1 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Get the HTTP client
	client, err := HttpClient(*httpAddr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed initializing Nomad client: %s", err))
		return 1
	}

	// Invoke list mode if no job ID.
	if len(flags.Args()) == 0 {
		jobs, _, err := client.Jobs().List(nil)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Failed querying jobs: %s", err))
			return 1
		}

		out := []string{"ID|Type|Priority|AllAtOnce|Status"}
		for _, job := range jobs {
			out = append(out, fmt.Sprintf("%s|%s|%d|%v|%s",
				job.ID,
				job.Type,
				job.Priority,
				job.AllAtOnce,
				job.Status))
		}
		c.Ui.Output(columnize.SimpleFormat(out))
		return 0
	}

	// Try querying the job
	jobID := flags.Args()[0]
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed querying job: %s", err))
		return 1
	}

	// Format the job info
	basic := []string{
		fmt.Sprintf("ID | %s", job.ID),
		fmt.Sprintf("Name | %s", job.Name),
		fmt.Sprintf("Type | %s", job.Type),
		fmt.Sprintf("Priority | %d", job.Priority),
		fmt.Sprintf("AllAtOnce | %v", job.AllAtOnce),
		fmt.Sprintf("Datacenters | %s", strings.Join(job.Datacenters, ",")),
		fmt.Sprintf("Status | %s", job.Status),
		fmt.Sprintf("StatusDescription | %s", job.StatusDescription),
	}
	c.Ui.Output(columnize.SimpleFormat(basic))

	return 0
}
<commit_msg>command: adjustments for status<commit_after>package command

import (
	"flag"
	"fmt"
	"strings"

	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
)

type StatusCommand struct {
	Ui cli.Ui
}

func (c *StatusCommand) Help() string {
	helpText := `
Usage: nomad status [options] [job]

  Displays information about the given job. If no job ID is given,
  a list of all known jobs will be dumped.

Options:

  -help
    Display this message

  -http-addr
    Address of the Nomad API to connect. Can also be specified
    using the environment variable NOMAD_HTTP_ADDR.
    Default = http://127.0.0.1:4646
`
	return strings.TrimSpace(helpText)
}

func (c *StatusCommand) Synopsis() string {
	return "Displays information about jobs"
}

func (c *StatusCommand) Run(args []string) int {
	var httpAddr *string

	flags := flag.NewFlagSet("status", flag.ContinueOnError)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	httpAddr = HttpAddrFlag(flags)

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Check that we either got no jobs or exactly one.
	if len(flags.Args()) > 1 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Get the HTTP client
	client, err := HttpClient(*httpAddr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed initializing Nomad client: %s", err))
		return 1
	}

	// Invoke list mode if no job ID.
	if len(flags.Args()) == 0 {
		jobs, _, err := client.Jobs().List(nil)
		if err != nil {
			c.Ui.Error(fmt.Sprintf("Failed querying jobs: %s", err))
			return 1
		}

		// No output if we have no jobs
		if len(jobs) == 0 {
			return 0
		}

		out := []string{"ID|Type|Priority|AllAtOnce|Status"}
		for _, job := range jobs {
			out = append(out, fmt.Sprintf("%s|%s|%d|%v|%s",
				job.ID,
				job.Type,
				job.Priority,
				job.AllAtOnce,
				job.Status))
		}
		c.Ui.Output(columnize.SimpleFormat(out))
		return 0
	}

	// Try querying the job
	jobID := flags.Args()[0]
	job, _, err := client.Jobs().Info(jobID, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed querying job: %s", err))
		return 1
	}

	// Format the job info
	basic := []string{
		fmt.Sprintf("ID | %s", job.ID),
		fmt.Sprintf("Name | %s", job.Name),
		fmt.Sprintf("Type | %s", job.Type),
		fmt.Sprintf("Priority | %d", job.Priority),
		fmt.Sprintf("AllAtOnce | %v", job.AllAtOnce),
		fmt.Sprintf("Datacenters | %s", strings.Join(job.Datacenters, ",")),
		fmt.Sprintf("Status | %s", job.Status),
		fmt.Sprintf("StatusDescription | %s", job.StatusDescription),
	}
	c.Ui.Output(columnize.SimpleFormat(basic))

	return 0
}
<|endoftext|>


<commit_before>package common

import (
	"bytes"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// write writes an encrypted/compressed object and signature to an S3 datastore
func (info *s3Info) write(key string, data []byte) error {
	var s3Client *s3.S3                     // aws s3 client
	var bucketRequest *s3.CreateBucketInput // aws create bucket request
	var buf *bytes.Buffer                   // data buffer
	var err error                           // general error holder
	var awsErr awserr.Error                 // aws framework error
	var ok bool                             // assert check

	// init s3 client
	s3Client = s3.New(session.Must(session.NewSession(info.awsConfig)))

	// build create bucket request
	bucketRequest = &s3.CreateBucketInput{
		Bucket: aws.String(info.bucket),
	}

	// add location constraint if needed
	// http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
	if info.awsConfig.Region != nil && aws.StringValue(info.awsConfig.Region) != "us-east-1" {
		bucketRequest.CreateBucketConfiguration = &s3.CreateBucketConfiguration{
			LocationConstraint: info.awsConfig.Region,
		}
	}

	// attempt to create bucket
	if _, err = s3Client.CreateBucket(bucketRequest); err != nil {
		// ignore non-fatal creation errors
		if awsErr, ok = err.(awserr.Error); ok {
			if awsErr.Code() != s3.ErrCodeBucketAlreadyExists &&
				awsErr.Code() != s3.ErrCodeBucketAlreadyOwnedByYou &&
				awsErr.Code() != "AccessDenied" {
				// not something we catch - return the error
				return err
			}
		} else {
			// other failure - return the error
			return err
		}
	}

	// init byte buffer
	buf = new(bytes.Buffer)

	// populate byte buffer with encrypted/compressed data
	if err = writeBytes(buf, key, data); err != nil {
		return err
	}

	// upload data object
	if _, err = s3Client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(info.bucket),
		Key:    aws.String(info.key),
		Body:   bytes.NewReader(buf.Bytes()),
	}); err != nil {
		return err
	}

	// reset and reuse our buf
	buf.Reset()

	// calculate data checksum
	if err = writeChecksum(buf, key, data); err != nil {
		return err
	}

	// upload signature object
	if _, err = s3Client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(info.bucket),
		Key:    aws.String(info.key + ".sig"),
		Body:   bytes.NewReader(buf.Bytes()),
	}); err != nil {
		return err
	}

	// all good
	return nil
}
<commit_msg>small comment tweak<commit_after>package common

import (
	"bytes"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// write writes an encrypted/compressed object and signature to an S3 datastore
func (info *s3Info) write(key string, data []byte) error {
	var s3Client *s3.S3                     // aws s3 client
	var bucketRequest *s3.CreateBucketInput // aws create bucket request
	var buf *bytes.Buffer                   // data buffer
	var err error                           // general error holder
	var awsErr awserr.Error                 // aws framework error
	var ok bool                             // assert check

	// init s3 client
	s3Client = s3.New(session.Must(session.NewSession(info.awsConfig)))

	// build create bucket request
	bucketRequest = &s3.CreateBucketInput{
		Bucket: aws.String(info.bucket),
	}

	// only add location constraint if needed
	// http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
	if info.awsConfig.Region != nil && aws.StringValue(info.awsConfig.Region) != "us-east-1" {
		bucketRequest.CreateBucketConfiguration = &s3.CreateBucketConfiguration{
			LocationConstraint: info.awsConfig.Region,
		}
	}

	// attempt to create bucket
	if _, err = s3Client.CreateBucket(bucketRequest); err != nil {
		// ignore non-fatal creation errors
		if awsErr, ok = err.(awserr.Error); ok {
			if awsErr.Code() != s3.ErrCodeBucketAlreadyExists &&
				awsErr.Code() != s3.ErrCodeBucketAlreadyOwnedByYou &&
				awsErr.Code() != "AccessDenied" {
				// not something we catch - return the error
				return err
			}
		} else {
			// other failure - return the error
			return err
		}
	}

	// init byte buffer
	buf = new(bytes.Buffer)

	// populate byte buffer with encrypted/compressed data
	if err = writeBytes(buf, key, data); err != nil {
		return err
	}

	// upload data object
	if _, err = s3Client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(info.bucket),
		Key:    aws.String(info.key),
		Body:   bytes.NewReader(buf.Bytes()),
	}); err != nil {
		return err
	}

	// reset and reuse our buf
	buf.Reset()

	// calculate data checksum
	if err = writeChecksum(buf, key, data); err != nil {
		return err
	}

	// upload signature object
	if _, err = s3Client.PutObject(&s3.PutObjectInput{
		Bucket: aws.String(info.bucket),
		Key:    aws.String(info.key + ".sig"),
		Body:   bytes.NewReader(buf.Bytes()),
	}); err != nil {
		return err
	}

	// all good
	return nil
}
<|endoftext|>


<commit_before>package ssh

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type comm struct {
	client *ssh.ClientConn
	config *Config
	conn   net.Conn
}

// Config is the structure used to configure the SSH communicator.
type Config struct {
	// The configuration of the Go SSH connection
	SSHConfig *ssh.ClientConfig

	// Connection returns a new connection. The current connection
	// in use will be closed as part of the Close method, or in the
	// case an error occurs.
	Connection func() (net.Conn, error)

	// NoPty, if true, will not request a pty from the remote end.
	NoPty bool
}

// Creates a new packer.Communicator implementation over SSH. This takes
// an already existing TCP connection and SSH configuration.
func New(config *Config) (result *comm, err error) {
	// Establish an initial connection and connect
	result = &comm{
		config: config,
	}

	if err = result.reconnect(); err != nil {
		result = nil
		return
	}

	return
}

func (c *comm) Start(cmd *packer.RemoteCmd) (err error) {
	session, err := c.newSession()
	if err != nil {
		return
	}

	// Setup our session
	session.Stdin = cmd.Stdin
	session.Stdout = cmd.Stdout
	session.Stderr = cmd.Stderr

	if !c.config.NoPty {
		// Request a PTY
		termModes := ssh.TerminalModes{
			ssh.ECHO:          0,     // do not echo
			ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
			ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
		}

		if err = session.RequestPty("xterm", 80, 40, termModes); err != nil {
			return
		}
	}

	log.Printf("starting remote command: %s", cmd.Command)
	err = session.Start(cmd.Command + "\n")
	if err != nil {
		return
	}

	// A channel to keep track of our done state
	doneCh := make(chan struct{})
	sessionLock := new(sync.Mutex)
	timedOut := false

	// Start a goroutine to wait for the session to end and set the
	// exit boolean and status.
	go func() {
		defer session.Close()

		err := session.Wait()
		exitStatus := 0
		if err != nil {
			exitErr, ok := err.(*ssh.ExitError)
			if ok {
				exitStatus = exitErr.ExitStatus()
			}
		}

		sessionLock.Lock()
		defer sessionLock.Unlock()

		if timedOut {
			// We timed out, so set the exit status to -1
			exitStatus = -1
		}

		log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command)
		cmd.SetExited(exitStatus)
		close(doneCh)
	}()

	go func() {
		failures := 0
		for {
			dummy, err := c.config.Connection()
			if err == nil {
				dummy.Close()
			}

			select {
			case <-doneCh:
				return
			default:
			}

			if err != nil {
				log.Printf("background SSH connection checker failure: %s", err)
				failures += 1
			}

			if failures < 5 {
				time.Sleep(5 * time.Second)
				continue
			}

			// Acquire a lock in order to modify session state
			sessionLock.Lock()
			defer sessionLock.Unlock()

			// Kill the connection and mark that we timed out.
			log.Printf("Too many SSH connection failures. Killing it!")
			c.conn.Close()
			timedOut = true

			return
		}
	}()

	return
}

func (c *comm) Upload(path string, input io.Reader) error {
	// The target directory and file for talking the SCP protocol
	target_dir := filepath.Dir(path)
	target_file := filepath.Base(path)

	// On windows, filepath.Dir uses backslash seperators (ie. "\tmp").
	// This does not work when the target host is unix.  Switch to forward slash
	// which works for unix and windows
	target_dir = filepath.ToSlash(target_dir)

	scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error {
		return scpUploadFile(target_file, input, w, stdoutR)
	}

	return c.scpSession("scp -vt "+target_dir, scpFunc)
}

func (c *comm) UploadDir(dst string, src string, excl []string) error {
	log.Printf("Upload dir '%s' to '%s'", src, dst)
	scpFunc := func(w io.Writer, r *bufio.Reader) error {
		uploadEntries := func() error {
			f, err := os.Open(src)
			if err != nil {
				return err
			}
			defer f.Close()

			entries, err := f.Readdir(-1)
			if err != nil {
				return err
			}

			return scpUploadDir(src, entries, w, r)
		}

		if src[len(src)-1] != '/' {
			log.Printf("No trailing slash, creating the source directory name")
			return scpUploadDirProtocol(filepath.Base(src), w, r, uploadEntries)
		} else {
			// Trailing slash, so only upload the contents
			return uploadEntries()
		}
	}

	return c.scpSession("scp -rvt "+dst, scpFunc)
}

func (c *comm) Download(string, io.Writer) error {
	panic("not implemented yet")
}

func (c *comm) newSession() (session *ssh.Session, err error) {
	log.Println("opening new ssh session")
	if c.client == nil {
		err = errors.New("client not available")
	} else {
		session, err = c.client.NewSession()
	}

	if err != nil {
		log.Printf("ssh session open error: '%s', attempting reconnect", err)
		if err := c.reconnect(); err != nil {
			return nil, err
		}

		return c.client.NewSession()
	}

	return session, nil
}

func (c *comm) reconnect() (err error) {
	if c.conn != nil {
		c.conn.Close()
	}

	// Set the conn and client to nil since we'll recreate it
	c.conn = nil
	c.client = nil

	log.Printf("reconnecting to TCP connection for SSH")
	c.conn, err = c.config.Connection()
	if err != nil {
		log.Printf("reconnection error: %s", err)
		return
	}

	log.Printf("handshaking with SSH")
	c.client, err = ssh.Client(c.conn, c.config.SSHConfig)
	if err != nil {
		log.Printf("handshake error: %s", err)
	}

	return
}

func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) error) error {
	session, err := c.newSession()
	if err != nil {
		return err
	}
	defer session.Close()

	// Get a pipe to stdin so that we can send data down
	stdinW, err := session.StdinPipe()
	if err != nil {
		return err
	}

	// We only want to close once, so we nil w after we close it,
	// and only close in the defer if it hasn't been closed already.
	defer func() {
		if stdinW != nil {
			stdinW.Close()
		}
	}()

	// Get a pipe to stdout so that we can get responses back
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}
	stdoutR := bufio.NewReader(stdoutPipe)

	// Set stderr to a bytes buffer
	stderr := new(bytes.Buffer)
	session.Stderr = stderr

	// Start the sink mode on the other side
	// TODO(mitchellh): There are probably issues with shell escaping the path
	log.Println("Starting remote scp process: %s", scpCommand)
	if err := session.Start(scpCommand); err != nil {
		return err
	}

	// Call our callback that executes in the context of SCP
	log.Println("Started SCP session, beginning transfers...")
	if err := f(stdinW, stdoutR); err != nil {
		return err
	}

	// Close the stdin, which sends an EOF, and then set w to nil so that
	// our defer func doesn't close it again since that is unsafe with
	// the Go SSH package.
	log.Println("SCP session complete, closing stdin pipe.")
	stdinW.Close()
	stdinW = nil

	// Wait for the SCP connection to close, meaning it has consumed all
	// our data and has completed. Or has errored.
	log.Println("Waiting for SSH session to complete.")
	err = session.Wait()
	if err != nil {
		if exitErr, ok := err.(*ssh.ExitError); ok {
			// Otherwise, we have an ExitErorr, meaning we can just read
			// the exit status
			log.Printf("non-zero exit status: %d", exitErr.ExitStatus())

			// If we exited with status 127, it means SCP isn't available.
			// Return a more descriptive error for that.
			if exitErr.ExitStatus() == 127 {
				return errors.New(
					"SCP failed to start. This usually means that SCP is not\n" +
						"properly installed on the remote system.")
			}
		}

		return err
	}

	log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String())
	return nil
}

// checkSCPStatus checks that a prior command sent to SCP completed
// successfully. If it did not complete successfully, an error will
// be returned.
func checkSCPStatus(r *bufio.Reader) error {
	code, err := r.ReadByte()
	if err != nil {
		return err
	}

	if code != 0 {
		// Treat any non-zero (really 1 and 2) as fatal errors
		message, _, err := r.ReadLine()
		if err != nil {
			return fmt.Errorf("Error reading error message: %s", err)
		}

		return errors.New(string(message))
	}

	return nil
}

func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) error {
	// Determine the length of the upload content by copying it
	// into an in-memory buffer. Note that this means what we upload
	// must fit into memory.
	log.Println("Copying input data into in-memory buffer so we can get the length")
	inputBuf := new(bytes.Buffer)
	if _, err := io.Copy(inputBuf, src); err != nil {
		return err
	}

	// Start the protocol
	log.Println("Beginning file upload...")
	fmt.Fprintln(w, "C0644", inputBuf.Len(), dst)
	err := checkSCPStatus(r)
	if err != nil {
		return err
	}

	if _, err := io.Copy(w, inputBuf); err != nil {
		return err
	}

	fmt.Fprint(w, "\x00")
	err = checkSCPStatus(r)
	if err != nil {
		return err
	}

	return nil
}

func scpUploadDirProtocol(name string, w io.Writer, r *bufio.Reader, f func() error) error {
	log.Printf("SCP: starting directory upload: %s", name)
	fmt.Fprintln(w, "D0755 0", name)
	err := checkSCPStatus(r)
	if err != nil {
		return err
	}

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

	fmt.Fprintln(w, "E")
	if err != nil {
		return err
	}

	return nil
}

func scpUploadDir(root string, fs []os.FileInfo, w io.Writer, r *bufio.Reader) error {
	for _, fi := range fs {
		realPath := filepath.Join(root, fi.Name())

		if !fi.IsDir() {
			// It is a regular file, just upload it
			f, err := os.Open(realPath)
			if err != nil {
				return err
			}

			err = func() error {
				defer f.Close()
				return scpUploadFile(fi.Name(), f, w, r)
			}()

			if err != nil {
				return err
			}

			continue
		}

		// It is a directory, recursively upload
		err := scpUploadDirProtocol(fi.Name(), w, r, func() error {
			f, err := os.Open(realPath)
			if err != nil {
				return err
			}
			defer f.Close()

			entries, err := f.Readdir(-1)
			if err != nil {
				return err
			}

			return scpUploadDir(realPath, entries, w, r)
		})
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>communicator/ssh: Reset failure count if heartbeat works<commit_after>package ssh

import (
	"bufio"
	"bytes"
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/packer/packer"
	"io"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type comm struct {
	client *ssh.ClientConn
	config *Config
	conn   net.Conn
}

// Config is the structure used to configure the SSH communicator.
type Config struct {
	// The configuration of the Go SSH connection
	SSHConfig *ssh.ClientConfig

	// Connection returns a new connection. The current connection
	// in use will be closed as part of the Close method, or in the
	// case an error occurs.
	Connection func() (net.Conn, error)

	// NoPty, if true, will not request a pty from the remote end.
	NoPty bool
}

// Creates a new packer.Communicator implementation over SSH. This takes
// an already existing TCP connection and SSH configuration.
func New(config *Config) (result *comm, err error) {
	// Establish an initial connection and connect
	result = &comm{
		config: config,
	}

	if err = result.reconnect(); err != nil {
		result = nil
		return
	}

	return
}

func (c *comm) Start(cmd *packer.RemoteCmd) (err error) {
	session, err := c.newSession()
	if err != nil {
		return
	}

	// Setup our session
	session.Stdin = cmd.Stdin
	session.Stdout = cmd.Stdout
	session.Stderr = cmd.Stderr

	if !c.config.NoPty {
		// Request a PTY
		termModes := ssh.TerminalModes{
			ssh.ECHO:          0,     // do not echo
			ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
			ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
		}

		if err = session.RequestPty("xterm", 80, 40, termModes); err != nil {
			return
		}
	}

	log.Printf("starting remote command: %s", cmd.Command)
	err = session.Start(cmd.Command + "\n")
	if err != nil {
		return
	}

	// A channel to keep track of our done state
	doneCh := make(chan struct{})
	sessionLock := new(sync.Mutex)
	timedOut := false

	// Start a goroutine to wait for the session to end and set the
	// exit boolean and status.
	go func() {
		defer session.Close()

		err := session.Wait()
		exitStatus := 0
		if err != nil {
			exitErr, ok := err.(*ssh.ExitError)
			if ok {
				exitStatus = exitErr.ExitStatus()
			}
		}

		sessionLock.Lock()
		defer sessionLock.Unlock()

		if timedOut {
			// We timed out, so set the exit status to -1
			exitStatus = -1
		}

		log.Printf("remote command exited with '%d': %s", exitStatus, cmd.Command)
		cmd.SetExited(exitStatus)
		close(doneCh)
	}()

	go func() {
		failures := 0
		for {
			dummy, err := c.config.Connection()
			if err == nil {
				failures = 0
				dummy.Close()
			}

			select {
			case <-doneCh:
				return
			default:
			}

			if err != nil {
				log.Printf("background SSH connection checker failure: %s", err)
				failures += 1
			}

			if failures < 5 {
				time.Sleep(5 * time.Second)
				continue
			}

			// Acquire a lock in order to modify session state
			sessionLock.Lock()
			defer sessionLock.Unlock()

			// Kill the connection and mark that we timed out.
			log.Printf("Too many SSH connection failures. Killing it!")
			c.conn.Close()
			timedOut = true

			return
		}
	}()

	return
}

func (c *comm) Upload(path string, input io.Reader) error {
	// The target directory and file for talking the SCP protocol
	target_dir := filepath.Dir(path)
	target_file := filepath.Base(path)

	// On windows, filepath.Dir uses backslash seperators (ie. "\tmp").
	// This does not work when the target host is unix.  Switch to forward slash
	// which works for unix and windows
	target_dir = filepath.ToSlash(target_dir)

	scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error {
		return scpUploadFile(target_file, input, w, stdoutR)
	}

	return c.scpSession("scp -vt "+target_dir, scpFunc)
}

func (c *comm) UploadDir(dst string, src string, excl []string) error {
	log.Printf("Upload dir '%s' to '%s'", src, dst)
	scpFunc := func(w io.Writer, r *bufio.Reader) error {
		uploadEntries := func() error {
			f, err := os.Open(src)
			if err != nil {
				return err
			}
			defer f.Close()

			entries, err := f.Readdir(-1)
			if err != nil {
				return err
			}

			return scpUploadDir(src, entries, w, r)
		}

		if src[len(src)-1] != '/' {
			log.Printf("No trailing slash, creating the source directory name")
			return scpUploadDirProtocol(filepath.Base(src), w, r, uploadEntries)
		} else {
			// Trailing slash, so only upload the contents
			return uploadEntries()
		}
	}

	return c.scpSession("scp -rvt "+dst, scpFunc)
}

func (c *comm) Download(string, io.Writer) error {
	panic("not implemented yet")
}

func (c *comm) newSession() (session *ssh.Session, err error) {
	log.Println("opening new ssh session")
	if c.client == nil {
		err = errors.New("client not available")
	} else {
		session, err = c.client.NewSession()
	}

	if err != nil {
		log.Printf("ssh session open error: '%s', attempting reconnect", err)
		if err := c.reconnect(); err != nil {
			return nil, err
		}

		return c.client.NewSession()
	}

	return session, nil
}

func (c *comm) reconnect() (err error) {
	if c.conn != nil {
		c.conn.Close()
	}

	// Set the conn and client to nil since we'll recreate it
	c.conn = nil
	c.client = nil

	log.Printf("reconnecting to TCP connection for SSH")
	c.conn, err = c.config.Connection()
	if err != nil {
		log.Printf("reconnection error: %s", err)
		return
	}

	log.Printf("handshaking with SSH")
	c.client, err = ssh.Client(c.conn, c.config.SSHConfig)
	if err != nil {
		log.Printf("handshake error: %s", err)
	}

	return
}

func (c *comm) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) error) error {
	session, err := c.newSession()
	if err != nil {
		return err
	}
	defer session.Close()

	// Get a pipe to stdin so that we can send data down
	stdinW, err := session.StdinPipe()
	if err != nil {
		return err
	}

	// We only want to close once, so we nil w after we close it,
	// and only close in the defer if it hasn't been closed already.
	defer func() {
		if stdinW != nil {
			stdinW.Close()
		}
	}()

	// Get a pipe to stdout so that we can get responses back
	stdoutPipe, err := session.StdoutPipe()
	if err != nil {
		return err
	}
	stdoutR := bufio.NewReader(stdoutPipe)

	// Set stderr to a bytes buffer
	stderr := new(bytes.Buffer)
	session.Stderr = stderr

	// Start the sink mode on the other side
	// TODO(mitchellh): There are probably issues with shell escaping the path
	log.Println("Starting remote scp process: %s", scpCommand)
	if err := session.Start(scpCommand); err != nil {
		return err
	}

	// Call our callback that executes in the context of SCP
	log.Println("Started SCP session, beginning transfers...")
	if err := f(stdinW, stdoutR); err != nil {
		return err
	}

	// Close the stdin, which sends an EOF, and then set w to nil so that
	// our defer func doesn't close it again since that is unsafe with
	// the Go SSH package.
	log.Println("SCP session complete, closing stdin pipe.")
	stdinW.Close()
	stdinW = nil

	// Wait for the SCP connection to close, meaning it has consumed all
	// our data and has completed. Or has errored.
	log.Println("Waiting for SSH session to complete.")
	err = session.Wait()
	if err != nil {
		if exitErr, ok := err.(*ssh.ExitError); ok {
			// Otherwise, we have an ExitErorr, meaning we can just read
			// the exit status
			log.Printf("non-zero exit status: %d", exitErr.ExitStatus())

			// If we exited with status 127, it means SCP isn't available.
			// Return a more descriptive error for that.
			if exitErr.ExitStatus() == 127 {
				return errors.New(
					"SCP failed to start. This usually means that SCP is not\n" +
						"properly installed on the remote system.")
			}
		}

		return err
	}

	log.Printf("scp stderr (length %d): %s", stderr.Len(), stderr.String())
	return nil
}

// checkSCPStatus checks that a prior command sent to SCP completed
// successfully. If it did not complete successfully, an error will
// be returned.
func checkSCPStatus(r *bufio.Reader) error {
	code, err := r.ReadByte()
	if err != nil {
		return err
	}

	if code != 0 {
		// Treat any non-zero (really 1 and 2) as fatal errors
		message, _, err := r.ReadLine()
		if err != nil {
			return fmt.Errorf("Error reading error message: %s", err)
		}

		return errors.New(string(message))
	}

	return nil
}

func scpUploadFile(dst string, src io.Reader, w io.Writer, r *bufio.Reader) error {
	// Determine the length of the upload content by copying it
	// into an in-memory buffer. Note that this means what we upload
	// must fit into memory.
	log.Println("Copying input data into in-memory buffer so we can get the length")
	inputBuf := new(bytes.Buffer)
	if _, err := io.Copy(inputBuf, src); err != nil {
		return err
	}

	// Start the protocol
	log.Println("Beginning file upload...")
	fmt.Fprintln(w, "C0644", inputBuf.Len(), dst)
	err := checkSCPStatus(r)
	if err != nil {
		return err
	}

	if _, err := io.Copy(w, inputBuf); err != nil {
		return err
	}

	fmt.Fprint(w, "\x00")
	err = checkSCPStatus(r)
	if err != nil {
		return err
	}

	return nil
}

func scpUploadDirProtocol(name string, w io.Writer, r *bufio.Reader, f func() error) error {
	log.Printf("SCP: starting directory upload: %s", name)
	fmt.Fprintln(w, "D0755 0", name)
	err := checkSCPStatus(r)
	if err != nil {
		return err
	}

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

	fmt.Fprintln(w, "E")
	if err != nil {
		return err
	}

	return nil
}

func scpUploadDir(root string, fs []os.FileInfo, w io.Writer, r *bufio.Reader) error {
	for _, fi := range fs {
		realPath := filepath.Join(root, fi.Name())

		if !fi.IsDir() {
			// It is a regular file, just upload it
			f, err := os.Open(realPath)
			if err != nil {
				return err
			}

			err = func() error {
				defer f.Close()
				return scpUploadFile(fi.Name(), f, w, r)
			}()

			if err != nil {
				return err
			}

			continue
		}

		// It is a directory, recursively upload
		err := scpUploadDirProtocol(fi.Name(), w, r, func() error {
			f, err := os.Open(realPath)
			if err != nil {
				return err
			}
			defer f.Close()

			entries, err := f.Readdir(-1)
			if err != nil {
				return err
			}

			return scpUploadDir(realPath, entries, w, r)
		})
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package logic

import (
	"fmt"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/jonaz/astrotime"

	"code.google.com/p/go-uuid/uuid"
)

func TestTaskSetUuid(t *testing.T) {
	task := &task{}
	task.SetUuid("test")
	if task.Uuid() == "test" {
		return
	}
	t.Errorf("Uuid wrong expected: %s got %s", "test", task.Uuid())
}

func TestTaskRunAndAddActions(t *testing.T) {
	task := &task{}
	actionRunCount := 0
	action := NewRuleActionStub(&actionRunCount)
	task.AddAction(action)
	task.AddAction(action)
	task.Run()
	task.Run()
	if actionRunCount == 4 {
		return
	}
	t.Errorf("actionRunCount wrong expected: %d got %d", 4, actionRunCount)
}
func TestSchedulerCalculateSun(t *testing.T) {

	sunset := astrotime.NextSunset(time.Now(), float64(56.878333), float64(14.809167))
	sunrise := astrotime.NextSunrise(time.Now(), float64(56.878333), float64(14.809167))
	dusk := astrotime.NextDusk(time.Now(), float64(56.878333), float64(14.809167), astrotime.CIVIL_DUSK)
	dawn := astrotime.NextDawn(time.Now(), float64(56.878333), float64(14.809167), astrotime.CIVIL_DAWN)

	when := ""
	task := &task{Name_: "Test", Uuid_: uuid.New()}

	when = task.CalculateSun("* sunset sunset * * *")
	fmt.Println("sunset:", when)
	s := strings.Split(when, " ")
	if s[1] != strconv.Itoa(sunset.Minute()) || s[2] != strconv.Itoa(sunset.Hour()) {
		t.Errorf("Sunset does not match %s %s", s[1], sunset.Minute())
	}

	when = task.CalculateSun("* sunrise sunrise * * *")
	fmt.Println("sunrise:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(sunrise.Minute()) || s[2] != strconv.Itoa(sunrise.Hour()) {
		t.Errorf("Sunrise does not match %s %s", s[1], sunrise.Minute())
	}

	when = task.CalculateSun("* dawn dawn * * *")
	fmt.Println("dawn:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(dawn.Minute()) || s[2] != strconv.Itoa(dawn.Hour()) {
		t.Errorf("Dawn does not match %s %s", s[1], dawn.Minute())
	}

	when = task.CalculateSun("* dusk dusk * * *")
	fmt.Println("dusk:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(dusk.Minute()) || s[2] != strconv.Itoa(dusk.Hour()) {
		t.Errorf("Dusk does not match %s %s", s[1], dusk.Minute())
	}

	//t.Errorf("actionRunCount wrong expected cron to have ran 4 times got %d", actionRunCount)
}
<commit_msg>use t.Log<commit_after>package logic

import (
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/jonaz/astrotime"

	"code.google.com/p/go-uuid/uuid"
)

func TestTaskSetUuid(t *testing.T) {
	task := &task{}
	task.SetUuid("test")
	if task.Uuid() == "test" {
		return
	}
	t.Errorf("Uuid wrong expected: %s got %s", "test", task.Uuid())
}

func TestTaskRunAndAddActions(t *testing.T) {
	task := &task{}
	actionRunCount := 0
	action := NewRuleActionStub(&actionRunCount)
	task.AddAction(action)
	task.AddAction(action)
	task.Run()
	task.Run()
	if actionRunCount == 4 {
		return
	}
	t.Errorf("actionRunCount wrong expected: %d got %d", 4, actionRunCount)
}
func TestSchedulerCalculateSun(t *testing.T) {

	sunset := astrotime.NextSunset(time.Now(), float64(56.878333), float64(14.809167))
	sunrise := astrotime.NextSunrise(time.Now(), float64(56.878333), float64(14.809167))
	dusk := astrotime.NextDusk(time.Now(), float64(56.878333), float64(14.809167), astrotime.CIVIL_DUSK)
	dawn := astrotime.NextDawn(time.Now(), float64(56.878333), float64(14.809167), astrotime.CIVIL_DAWN)

	when := ""
	task := &task{Name_: "Test", Uuid_: uuid.New()}

	when = task.CalculateSun("* sunset sunset * * *")
	t.Log("sunset:", when)
	s := strings.Split(when, " ")
	if s[1] != strconv.Itoa(sunset.Minute()) || s[2] != strconv.Itoa(sunset.Hour()) {
		t.Errorf("Sunset does not match %s %s", s[1], sunset.Minute())
	}

	when = task.CalculateSun("* sunrise sunrise * * *")
	t.Log("sunrise:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(sunrise.Minute()) || s[2] != strconv.Itoa(sunrise.Hour()) {
		t.Errorf("Sunrise does not match %s %s", s[1], sunrise.Minute())
	}

	when = task.CalculateSun("* dawn dawn * * *")
	t.Log("dawn:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(dawn.Minute()) || s[2] != strconv.Itoa(dawn.Hour()) {
		t.Errorf("Dawn does not match %s %s", s[1], dawn.Minute())
	}

	when = task.CalculateSun("* dusk dusk * * *")
	t.Log("dusk:", when)
	s = strings.Split(when, " ")
	if s[1] != strconv.Itoa(dusk.Minute()) || s[2] != strconv.Itoa(dusk.Hour()) {
		t.Errorf("Dusk does not match %s %s", s[1], dusk.Minute())
	}

	//t.Errorf("actionRunCount wrong expected cron to have ran 4 times got %d", actionRunCount)
}
<|endoftext|>


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

package config

import (
	"errors"
	"time"

	"github.com/corestoreio/csfw/storage/csdb"
	"github.com/corestoreio/csfw/storage/dbr"
	"github.com/corestoreio/csfw/utils/cast"
	"github.com/corestoreio/csfw/utils/log"
	"github.com/spf13/viper"
)

const (
	LeftDelim  = "{{"
	RightDelim = "}}"

	// PathCSBaseURL main CoreStore base URL, used if no configuration on a store level can be found.
	PathCSBaseURL = "web/corestore/base_url"
	CSBaseURL     = "http://localhost:9500/"
)

const (
	URLTypeAbsent URLType = iota
	// UrlTypeWeb defines the ULR type to generate the main base URL.
	URLTypeWeb
	// UrlTypeStatic defines the url to the static assets like css, js or theme images
	URLTypeStatic
	// UrlTypeLink hmmm
	// UrlTypeLink
	// UrlTypeMedia defines the ULR type for generating URLs to product photos
	URLTypeMedia
)

type (
	// UrlType defines the type of the URL. Used in const declaration.
	// @see https://github.com/magento/magento2/blob/0.74.0-beta7/lib/internal/Magento/Framework/UrlInterface.php#L13
	URLType uint8

	Reader interface {
		// GetString returns a string from the manager. Example usage:
		// Default value: GetString(config.Path("general/locale/timezone"))
		// Website value: GetString(config.Path("general/locale/timezone"), config.ScopeWebsite(w))
		// Store   value: GetString(config.Path("general/locale/timezone"), config.ScopeStore(s))
		GetString(...ScopeOption) string

		// GetBool returns bool from the manager. Example usage see GetString.
		GetBool(...ScopeOption) bool
	}

	Writer interface {
		// Write puts a value back into the manager. Example usage:
		// Default Scope: Write(config.Path("currency", "option", "base"), config.Value("USD"))
		// Website Scope: Write(config.Path("currency", "option", "base"), config.Value("EUR"), config.ScopeWebsite(w))
		// Store   Scope: Write(config.Path("currency", "option", "base"), config.ValueReader(resp.Body), config.ScopeStore(s))
		Write(...ScopeOption) error
	}

	// Manager main configuration struct
	Manager struct {
		// why is Viper private? Because it can maybe replaced by something else ...
		v *viper.Viper
	}
)

var (
	_ Reader = (*Manager)(nil)
	_ Writer = (*Manager)(nil)

	// TableCollection handles all tables and its columns. init() in generated Go file will set the value.
	TableCollection csdb.TableStructureSlice
	ErrNoArguments  = errors.New("No arguments provided")
	DefaultManager  *Manager
)

func init() {
	DefaultManager = NewManager()
}

// NewManager creates the main new configuration for all scopes: default, website and store
func NewManager() *Manager {
	s := &Manager{
		v: viper.New(),
	}
	s.v.Set(newArg(Path(PathCSBaseURL)).scopePath(), CSBaseURL)
	return s
}

// ApplyDefaults reads the map and applies the keys and values to the default configuration
func (m *Manager) ApplyDefaults(ss Sectioner) *Manager {
	for k, v := range ss.Defaults() {
		if log.IsDebug() {
			log.Debug("Scope=ApplyDefaults", k, v)
		}
		m.v.Set(k, v)
	}
	return m
}

// ApplyCoreConfigData reads the table core_config_data into the Manager and overrides
// existing values. If the column value is NULL entry will be ignored.
func (m *Manager) ApplyCoreConfigData(dbrSess dbr.SessionRunner) error {
	var ccd TableCoreConfigDataSlice
	rows, err := csdb.LoadSlice(dbrSess, TableCollection, TableIndexCoreConfigData, &ccd)
	if log.IsDebug() {
		log.Debug("Manager=ApplyCoreConfigData", "rows", rows)
	}
	if err != nil {
		return log.Error("Manager=ApplyCoreConfigData", "err", err)
	}

	for _, cd := range ccd {
		if cd.Value.Valid {
			// ScopeID(cd.ScopeID) because cd.ScopeID is a struct field and cannot satisfy interface ScopeIDer
			m.Write(Path(cd.Path), Scope(GetScopeGroup(cd.Scope), ScopeID(cd.ScopeID)))
		}
	}
	return nil
}

// Write puts a value back into the manager. Example usage:
// Default Scope: Write(config.Path("currency", "option", "base"), config.Value("USD"))
// Website Scope: Write(config.Path("currency", "option", "base"), config.Value("EUR"), config.ScopeWebsite(w))
// Store   Scope: Write(config.Path("currency", "option", "base"), config.ValueReader(resp.Body), config.ScopeStore(s))
func (m *Manager) Write(o ...ScopeOption) error {
	a := newArg(o...)
	if a == nil {
		return ErrNoArguments
	}
	if a.isBubbling() {
		if log.IsDebug() {
			log.Debug("Manager=Write", "path", a.scopePathDefault(), "bubble", a.isBubbling(), "val", a.v)
		}
		m.v.Set(a.scopePathDefault(), a.v)
	}

	if log.IsDebug() {
		log.Debug("Manager=Write", "path", a.scopePath(), "val", a.v)
	}
	m.v.Set(a.scopePath(), a.v)

	return nil
}

func (m *Manager) get(o ...ScopeOption) interface{} {
	a := newArg(o...)
	vs := m.v.Get(a.scopePath()) // vs = value scope
	if vs == nil && a.isBubbling() {
		vs = m.v.Get(a.scopePathDefault())
	}
	return vs
}

// GetString returns a string from the manager. Example usage:
// Default value: GetString(config.Path("general/locale/timezone"))
// Website value: GetString(config.Path("general/locale/timezone"), config.ScopeWebsite(w))
// Store   value: GetString(config.Path("general/locale/timezone"), config.ScopeStore(s))
func (m *Manager) GetString(o ...ScopeOption) string {
	vs := m.get(o...)
	if vs == nil {
		return ""
	}
	return cast.ToString(vs)
}

// @todo use the backend model of a config value. most/all magento string slices are comma lists.
func (m *Manager) GetStringSlice(o ...ScopeOption) []string {
	return nil
	//	return m.v.GetStringSlice(newArg(o...))
}

// GetBool returns bool from the manager. Example usage see GetString.
func (m *Manager) GetBool(o ...ScopeOption) bool {
	vs := m.get(o...)
	if vs == nil {
		return false
	}
	return cast.ToBool(vs)
}

// GetFloat64 returns a float64 from the manager. Example usage see GetString.
func (m *Manager) GetFloat64(o ...ScopeOption) float64 {
	vs := m.get(o...)
	if vs == nil {
		return 0.0
	}
	return cast.ToFloat64(vs)
}

// GetInt returns an int from the manager. Example usage see GetString.
func (m *Manager) GetInt(o ...ScopeOption) int {
	vs := m.get(o...)
	if vs == nil {
		return 0
	}
	return cast.ToInt(vs)
}

// GetDateTime returns a date and time object from the manager. Example usage see GetString.
func (m *Manager) GetDateTime(o ...ScopeOption) time.Time {
	vs := m.get(o...)
	t, err := cast.ToTimeE(vs)
	if err != nil {
		log.Error("Manager=GetDateTime", "err", err, "val", vs)
	}
	return t
}

// @todo consider adding other Get* from the viper package

// AllKeys return all keys regardless where they are set
func (m *Manager) AllKeys() []string { return m.v.AllKeys() }

var _ Reader = (*mockReader)(nil)

// MockScopeReader used for testing
type mockReader struct {
	s func(path string) string
	b func(path string) bool
}

// NewMockReader used for testing
func NewMockReader(
	s func(path string) string,
	b func(path string) bool,
) *mockReader {

	return &mockReader{
		s: s,
		b: b,
	}
}

func (sr mockReader) GetString(opts ...ScopeOption) string {
	if sr.s == nil {
		return ""
	}
	return sr.s(newArg(opts...).scopePath())
}

func (sr mockReader) GetBool(opts ...ScopeOption) bool {
	if sr.b == nil {
		return false
	}
	return sr.b(newArg(opts...).scopePath())
}
<commit_msg>Package config: Manager add IsSet() to check if key exists.<commit_after>// Copyright 2015 CoreStore Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"errors"
	"time"

	"github.com/corestoreio/csfw/storage/csdb"
	"github.com/corestoreio/csfw/storage/dbr"
	"github.com/corestoreio/csfw/utils/cast"
	"github.com/corestoreio/csfw/utils/log"
	"github.com/spf13/viper"
)

const (
	LeftDelim  = "{{"
	RightDelim = "}}"

	// PathCSBaseURL main CoreStore base URL, used if no configuration on a store level can be found.
	PathCSBaseURL = "web/corestore/base_url"
	CSBaseURL     = "http://localhost:9500/"
)

const (
	URLTypeAbsent URLType = iota
	// UrlTypeWeb defines the ULR type to generate the main base URL.
	URLTypeWeb
	// UrlTypeStatic defines the url to the static assets like css, js or theme images
	URLTypeStatic
	// UrlTypeLink hmmm
	// UrlTypeLink
	// UrlTypeMedia defines the ULR type for generating URLs to product photos
	URLTypeMedia
)

type (
	// UrlType defines the type of the URL. Used in const declaration.
	// @see https://github.com/magento/magento2/blob/0.74.0-beta7/lib/internal/Magento/Framework/UrlInterface.php#L13
	URLType uint8

	Reader interface {
		// GetString returns a string from the manager. Example usage:
		// Default value: GetString(config.Path("general/locale/timezone"))
		// Website value: GetString(config.Path("general/locale/timezone"), config.ScopeWebsite(w))
		// Store   value: GetString(config.Path("general/locale/timezone"), config.ScopeStore(s))
		GetString(...ScopeOption) string

		// GetBool returns bool from the manager. Example usage see GetString.
		GetBool(...ScopeOption) bool
	}

	Writer interface {
		// Write puts a value back into the manager. Example usage:
		// Default Scope: Write(config.Path("currency", "option", "base"), config.Value("USD"))
		// Website Scope: Write(config.Path("currency", "option", "base"), config.Value("EUR"), config.ScopeWebsite(w))
		// Store   Scope: Write(config.Path("currency", "option", "base"), config.ValueReader(resp.Body), config.ScopeStore(s))
		Write(...ScopeOption) error
	}

	// Manager main configuration struct
	Manager struct {
		// why is Viper private? Because it can maybe replaced by something else ...
		v *viper.Viper
	}
)

var (
	_ Reader = (*Manager)(nil)
	_ Writer = (*Manager)(nil)

	// TableCollection handles all tables and its columns. init() in generated Go file will set the value.
	TableCollection csdb.TableStructureSlice
	ErrNoArguments  = errors.New("No arguments provided")
	DefaultManager  *Manager
)

func init() {
	DefaultManager = NewManager()
}

// NewManager creates the main new configuration for all scopes: default, website and store
func NewManager() *Manager {
	s := &Manager{
		v: viper.New(),
	}
	s.v.Set(newArg(Path(PathCSBaseURL)).scopePath(), CSBaseURL)
	return s
}

// ApplyDefaults reads the map and applies the keys and values to the default configuration
func (m *Manager) ApplyDefaults(ss Sectioner) *Manager {
	for k, v := range ss.Defaults() {
		if log.IsDebug() {
			log.Debug("Scope=ApplyDefaults", k, v)
		}
		m.v.Set(k, v)
	}
	return m
}

// ApplyCoreConfigData reads the table core_config_data into the Manager and overrides
// existing values. If the column value is NULL entry will be ignored.
func (m *Manager) ApplyCoreConfigData(dbrSess dbr.SessionRunner) error {
	var ccd TableCoreConfigDataSlice
	rows, err := csdb.LoadSlice(dbrSess, TableCollection, TableIndexCoreConfigData, &ccd)
	if log.IsDebug() {
		log.Debug("Manager=ApplyCoreConfigData", "rows", rows)
	}
	if err != nil {
		return log.Error("Manager=ApplyCoreConfigData", "err", err)
	}

	for _, cd := range ccd {
		if cd.Value.Valid {
			// ScopeID(cd.ScopeID) because cd.ScopeID is a struct field and cannot satisfy interface ScopeIDer
			m.Write(Path(cd.Path), Scope(GetScopeGroup(cd.Scope), ScopeID(cd.ScopeID)))
		}
	}
	return nil
}

// Write puts a value back into the manager. Example usage:
// Default Scope: Write(config.Path("currency", "option", "base"), config.Value("USD"))
// Website Scope: Write(config.Path("currency", "option", "base"), config.Value("EUR"), config.ScopeWebsite(w))
// Store   Scope: Write(config.Path("currency", "option", "base"), config.ValueReader(resp.Body), config.ScopeStore(s))
func (m *Manager) Write(o ...ScopeOption) error {
	a := newArg(o...)
	if a == nil {
		return ErrNoArguments
	}
	if a.isBubbling() {
		if log.IsDebug() {
			log.Debug("Manager=Write", "path", a.scopePathDefault(), "bubble", a.isBubbling(), "val", a.v)
		}
		m.v.Set(a.scopePathDefault(), a.v)
	}

	if log.IsDebug() {
		log.Debug("Manager=Write", "path", a.scopePath(), "val", a.v)
	}
	m.v.Set(a.scopePath(), a.v)

	return nil
}

// get generic getter ... not sure if this should be public ...
func (m *Manager) get(o ...ScopeOption) interface{} {
	a := newArg(o...)
	vs := m.v.Get(a.scopePath()) // vs = value scope
	if vs == nil && a.isBubbling() {
		vs = m.v.Get(a.scopePathDefault())
	}
	return vs
}

// GetString returns a string from the manager. Example usage:
// Default value: GetString(config.Path("general/locale/timezone"))
// Website value: GetString(config.Path("general/locale/timezone"), config.ScopeWebsite(w))
// Store   value: GetString(config.Path("general/locale/timezone"), config.ScopeStore(s))
func (m *Manager) GetString(o ...ScopeOption) string {
	vs := m.get(o...)
	if vs == nil {
		return ""
	}
	return cast.ToString(vs)
}

// @todo use the backend model of a config value. most/all magento string slices are comma lists.
func (m *Manager) GetStringSlice(o ...ScopeOption) []string {
	return nil
	//	return m.v.GetStringSlice(newArg(o...))
}

// GetBool returns bool from the manager. Example usage see GetString.
func (m *Manager) GetBool(o ...ScopeOption) bool {
	vs := m.get(o...)
	if vs == nil {
		return false
	}
	return cast.ToBool(vs)
}

// GetFloat64 returns a float64 from the manager. Example usage see GetString.
func (m *Manager) GetFloat64(o ...ScopeOption) float64 {
	vs := m.get(o...)
	if vs == nil {
		return 0.0
	}
	return cast.ToFloat64(vs)
}

// GetInt returns an int from the manager. Example usage see GetString.
func (m *Manager) GetInt(o ...ScopeOption) int {
	vs := m.get(o...)
	if vs == nil {
		return 0
	}
	return cast.ToInt(vs)
}

// GetDateTime returns a date and time object from the manager. Example usage see GetString.
func (m *Manager) GetDateTime(o ...ScopeOption) time.Time {
	vs := m.get(o...)
	t, err := cast.ToTimeE(vs)
	if err != nil {
		log.Error("Manager=GetDateTime", "err", err, "val", vs)
	}
	return t
}

// @todo consider adding other Get* from the viper package

// AllKeys return all keys regardless where they are set
func (m *Manager) AllKeys() []string { return m.v.AllKeys() }

// IsSet checks if a key is in the config. Does not bubble.
func (m *Manager) IsSet(o ...ScopeOption) bool {
	return m.v.IsSet(newArg(o...).scopePath())
}

var _ Reader = (*mockReader)(nil)

// MockScopeReader used for testing
type mockReader struct {
	s func(path string) string
	b func(path string) bool
}

// NewMockReader used for testing
func NewMockReader(
	s func(path string) string,
	b func(path string) bool,
) *mockReader {

	return &mockReader{
		s: s,
		b: b,
	}
}

func (sr mockReader) GetString(opts ...ScopeOption) string {
	if sr.s == nil {
		return ""
	}
	return sr.s(newArg(opts...).scopePath())
}

func (sr mockReader) GetBool(opts ...ScopeOption) bool {
	if sr.b == nil {
		return false
	}
	return sr.b(newArg(opts...).scopePath())
}
<|endoftext|>