<commit_before>package golhttpclient

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

func getURL(baseURL string, getParams map[string]string) (url *url.URL) {
	var getParamsURI string
	for key, val := range getParams {
		if getParamsURI == "" {
			getParamsURI = fmt.Sprintf("%s=%s", key, val)
		} else {
			getParamsURI = fmt.Sprintf("%s&%s=%s", getParamsURI, key, val)
		}
	}
	request_url := fmt.Sprintf("%s?%s", baseURL, getParamsURI)
	url, err := url.Parse(request_url)

	if err != nil {
		log.Println(err)
		return
	}
	return
}

func setHttpHeaders(req *http.Request, httpHeaders map[string]string) (err error) {
	basicAuth := strings.Split(httpHeaders["basicAuth"], ":")
	if len(basicAuth) > 1 {
		apiUsername, apiPassword := basicAuth[0], strings.Join(basicAuth[1:], ":")
		req.SetBasicAuth(apiUsername, apiPassword)
	}
	return
}

func httpResponse(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (resp http.Response, err error) {
	httpClient := &http.Client{
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   10 * time.Second,
			ResponseHeaderTimeout: 10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	req, err := http.NewRequest(httpMethod, "", nil)
	if err != nil {
		return
	}
	req.URL = getURL(baseURL, getParams)
	setHttpHeaders(req, httpHeaders)

	resp, err = httpClient.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	return
}

func httpResponseBody(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	resp, err := httpResponse(httpMethod, baseURL, getParams, httpHeaders)
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err == nil {
		body = string(bodyText)
	} else {
		log.Println(err)
	}

	return
}

func Http(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (resp http.Response, err error) {
	return httpResponse(httpMethod, baseURL, getParams, httpHeaders)
}

func HttpGet(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	body, err = httpResponseBody("GET", baseURL, getParams, httpHeaders)
	return
}

func HttpPut(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	// need to handle PUT body content
	body, err = httpResponseBody("PUT", baseURL, getParams, httpHeaders)
	return
}

func HttpPost(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	// need to handle POST body content
	body, err = httpResponseBody("POST", baseURL, getParams, httpHeaders)
	return
}

func HttpDelete(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	body, err = httpResponseBody("DELETE", baseURL, getParams, httpHeaders)
	return
}
<commit_msg>[golhttpclient] correcting return type for http.Response<commit_after>package golhttpclient

import (
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"strings"
	"time"
)

func getURL(baseURL string, getParams map[string]string) (url *url.URL) {
	var getParamsURI string
	for key, val := range getParams {
		if getParamsURI == "" {
			getParamsURI = fmt.Sprintf("%s=%s", key, val)
		} else {
			getParamsURI = fmt.Sprintf("%s&%s=%s", getParamsURI, key, val)
		}
	}
	request_url := fmt.Sprintf("%s?%s", baseURL, getParamsURI)
	url, err := url.Parse(request_url)

	if err != nil {
		log.Println(err)
		return
	}
	return
}

func setHttpHeaders(req *http.Request, httpHeaders map[string]string) (err error) {
	basicAuth := strings.Split(httpHeaders["basicAuth"], ":")
	if len(basicAuth) > 1 {
		apiUsername, apiPassword := basicAuth[0], strings.Join(basicAuth[1:], ":")
		req.SetBasicAuth(apiUsername, apiPassword)
	}
	return
}

func httpResponse(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (resp *http.Response, err error) {
	httpClient := &http.Client{
		Transport: &http.Transport{
			Dial: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
			}).Dial,
			TLSHandshakeTimeout:   10 * time.Second,
			ResponseHeaderTimeout: 10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
	}

	req, err := http.NewRequest(httpMethod, "", nil)
	if err != nil {
		return
	}
	req.URL = getURL(baseURL, getParams)
	setHttpHeaders(req, httpHeaders)

	resp, err = httpClient.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	return
}

func httpResponseBody(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	resp, err := httpResponse(httpMethod, baseURL, getParams, httpHeaders)
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err == nil {
		body = string(bodyText)
	} else {
		log.Println(err)
	}

	return
}

func Http(httpMethod string, baseURL string, getParams map[string]string, httpHeaders map[string]string) (resp *http.Response, err error) {
	return httpResponse(httpMethod, baseURL, getParams, httpHeaders)
}

func HttpGet(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	body, err = httpResponseBody("GET", baseURL, getParams, httpHeaders)
	return
}

func HttpPut(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	// need to handle PUT body content
	body, err = httpResponseBody("PUT", baseURL, getParams, httpHeaders)
	return
}

func HttpPost(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	// need to handle POST body content
	body, err = httpResponseBody("POST", baseURL, getParams, httpHeaders)
	return
}

func HttpDelete(baseURL string, getParams map[string]string, httpHeaders map[string]string) (body string, err error) {
	body, err = httpResponseBody("DELETE", baseURL, getParams, httpHeaders)
	return
}
<|endoftext|>


<commit_before>package hcn

import (
	"encoding/json"
	"fmt"
	"math"

	"github.com/Microsoft/hcsshim/internal/hcserror"
	"github.com/Microsoft/hcsshim/internal/interop"
	"github.com/sirupsen/logrus"
)

// Globals are all global properties of the HCN Service.
type Globals struct {
	Version Version `json:"Version"`
}

// Version is the HCN Service version.
type Version struct {
	Major int `json:"Major"`
	Minor int `json:"Minor"`
}

type VersionRange struct {
	MinVersion Version
	MaxVersion Version
}

type VersionRanges []VersionRange

var (
	// HNSVersion1803 added ACL functionality.
	HNSVersion1803 = VersionRanges{VersionRange{MinVersion: Version{Major: 7, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// V2ApiSupport allows the use of V2 Api calls and V2 Schema.
	V2ApiSupport = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// Remote Subnet allows for Remote Subnet policies on Overlay networks
	RemoteSubnetVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// A Host Route policy allows for local container to local host communication Overlay networks
	HostRouteVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// HNS 10.2 allows for Direct Server Return for loadbalancing
	DSRVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 10, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// HNS 9.3 through 10.0 (not included) and, 10.4+ provide support for configuring endpoints with /32 prefixes
	Slash32EndpointPrefixesVersion = VersionRanges{
		VersionRange{MinVersion: Version{Major: 9, Minor: 3}, MaxVersion: Version{Major: 9, Minor: math.MaxInt32}},
		VersionRange{MinVersion: Version{Major: 10, Minor: 4}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}},
	}
	// HNS 9.3 through 10.0 (not included) and, 10.4+ allow for HNS ACL Policies to support protocol 252 for VXLAN
	AclSupportForProtocol252Version = VersionRanges{
		VersionRange{MinVersion: Version{Major: 9, Minor: 3}, MaxVersion: Version{Major: 9, Minor: math.MaxInt32}},
		VersionRange{MinVersion: Version{Major: 10, Minor: 4}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}},
	}
	// HNS 11.10 allows for session affinity for loadbalancing
	SessionAffinityVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 11, Minor: 10}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
)

// GetGlobals returns the global properties of the HCN Service.
func GetGlobals() (*Globals, error) {
	var version Version
	err := hnsCall("GET", "/globals/version", "", &version)
	if err != nil {
		return nil, err
	}

	globals := &Globals{
		Version: version,
	}

	return globals, nil
}

type hnsResponse struct {
	Success bool
	Error   string
	Output  json.RawMessage
}

func hnsCall(method, path, request string, returnResponse interface{}) error {
	var responseBuffer *uint16
	logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request)

	err := _hnsCall(method, path, request, &responseBuffer)
	if err != nil {
		return hcserror.New(err, "hnsCall ", "")
	}
	response := interop.ConvertAndFreeCoTaskMemString(responseBuffer)

	hnsresponse := &hnsResponse{}
	if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil {
		return err
	}

	if !hnsresponse.Success {
		return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error)
	}

	if len(hnsresponse.Output) == 0 {
		return nil
	}

	logrus.Debugf("Network Response : %s", hnsresponse.Output)
	err = json.Unmarshal(hnsresponse.Output, returnResponse)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Updating session affinity version check<commit_after>package hcn

import (
	"encoding/json"
	"fmt"
	"math"

	"github.com/Microsoft/hcsshim/internal/hcserror"
	"github.com/Microsoft/hcsshim/internal/interop"
	"github.com/sirupsen/logrus"
)

// Globals are all global properties of the HCN Service.
type Globals struct {
	Version Version `json:"Version"`
}

// Version is the HCN Service version.
type Version struct {
	Major int `json:"Major"`
	Minor int `json:"Minor"`
}

type VersionRange struct {
	MinVersion Version
	MaxVersion Version
}

type VersionRanges []VersionRange

var (
	// HNSVersion1803 added ACL functionality.
	HNSVersion1803 = VersionRanges{VersionRange{MinVersion: Version{Major: 7, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// V2ApiSupport allows the use of V2 Api calls and V2 Schema.
	V2ApiSupport = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// Remote Subnet allows for Remote Subnet policies on Overlay networks
	RemoteSubnetVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// A Host Route policy allows for local container to local host communication Overlay networks
	HostRouteVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 9, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// HNS 10.2 allows for Direct Server Return for loadbalancing
	DSRVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 10, Minor: 2}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
	// HNS 9.3 through 10.0 (not included) and, 10.4+ provide support for configuring endpoints with /32 prefixes
	Slash32EndpointPrefixesVersion = VersionRanges{
		VersionRange{MinVersion: Version{Major: 9, Minor: 3}, MaxVersion: Version{Major: 9, Minor: math.MaxInt32}},
		VersionRange{MinVersion: Version{Major: 10, Minor: 4}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}},
	}
	// HNS 9.3 through 10.0 (not included) and, 10.4+ allow for HNS ACL Policies to support protocol 252 for VXLAN
	AclSupportForProtocol252Version = VersionRanges{
		VersionRange{MinVersion: Version{Major: 9, Minor: 3}, MaxVersion: Version{Major: 9, Minor: math.MaxInt32}},
		VersionRange{MinVersion: Version{Major: 10, Minor: 4}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}},
	}
	// HNS 12.0 allows for session affinity for loadbalancing
	SessionAffinityVersion = VersionRanges{VersionRange{MinVersion: Version{Major: 12, Minor: 0}, MaxVersion: Version{Major: math.MaxInt32, Minor: math.MaxInt32}}}
)

// GetGlobals returns the global properties of the HCN Service.
func GetGlobals() (*Globals, error) {
	var version Version
	err := hnsCall("GET", "/globals/version", "", &version)
	if err != nil {
		return nil, err
	}

	globals := &Globals{
		Version: version,
	}

	return globals, nil
}

type hnsResponse struct {
	Success bool
	Error   string
	Output  json.RawMessage
}

func hnsCall(method, path, request string, returnResponse interface{}) error {
	var responseBuffer *uint16
	logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request)

	err := _hnsCall(method, path, request, &responseBuffer)
	if err != nil {
		return hcserror.New(err, "hnsCall ", "")
	}
	response := interop.ConvertAndFreeCoTaskMemString(responseBuffer)

	hnsresponse := &hnsResponse{}
	if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil {
		return err
	}

	if !hnsresponse.Success {
		return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error)
	}

	if len(hnsresponse.Output) == 0 {
		return nil
	}

	logrus.Debugf("Network Response : %s", hnsresponse.Output)
	err = json.Unmarshal(hnsresponse.Output, returnResponse)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>/*
* Copyright (c) 2014 Santiago Arias | Remy Jourde
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package helpers

import (
	"reflect"
)

type arrayOfStrings []string

func (a arrayOfStrings) Contains(s string) bool {
	for _, e := range a {
		if e == s {
			return true
		}
	}
	return false
}

// from two structures source and destination.
// structures defined as follows:
// both structures have the same fields
// destination structure field types are pointers of the source types
// example:
// type TA struct {
// 	Field1      string
// 	Field2      string
// 	IsSomething bool
// }
// type TB struct {
// 	Field1      *string `json:",omitempty"`
// 	Field2      *string `json:",omitempty"`
// 	IsSomething *bool   `json:",omitempty"`
// }
// use source structure to build destination structure
func CopyToPointerStructure(tSrc interface{}, tDest interface{}) {
	s1 := reflect.ValueOf(tSrc).Elem()
	s2 := reflect.ValueOf(tDest).Elem()
	for i := 0; i < s1.NumField(); i++ {
		f1 := s1.Field(i)
		f2 := s2.Field(i)
		if f2.CanSet() {
			s2.Field(i).Set(f1.Addr())
		}
	}
}

// Set to nil all fields of t structure not present in array
// we suppose that t is a structure of pointer types and fieldsToKeep is an array of the fields you wish to keep.
func KeepFields(t interface{}, fieldsToKeep arrayOfStrings) {
	s := reflect.ValueOf(t).Elem()
	typeOfT := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		if !fieldsToKeep.Contains(typeOfT.Field(i).Name) && f.CanSet() {
			s.Field(i).Set(reflect.Zero(typeOfT.Field(i).Type))
		}
	}
}

// from two structures, source and destination.
// structures defined as follows:
// both structures have the same fields
// destination structure field types are pointers of the source types
// example:
// type TA struct {
// 	Field1      string
// 	Field2      string
// 	IsSomething bool
// }
// type TB struct {
// 	Field1      *string `json:",omitempty"`
// 	Field2      *string `json:",omitempty"`
// 	IsSomething *bool   `json:",omitempty"`
// }
// use source structure to build destination structure
// and at the same time set to nil all fields of Destination structure not present in array fieldsToKeep
// we suppose that pDest is a structure of pointer types and fieldsToKeep is an array of the fields you wish to keep.
func InitPointerStructure(pSrc interface{}, pDest interface{}, fieldsToKeep arrayOfStrings) {
	s1 := reflect.ValueOf(pSrc).Elem()
	s2 := reflect.ValueOf(pDest).Elem()
	for i := 0; i < s1.NumField(); i++ {
		f1 := s1.Field(i)
		f2 := s2.Field(i)
		if f2.CanSet() {
			if fieldsToKeep.Contains(s2.Type().Field(i).Name) {
				s2.Field(i).Set(f1.Addr())
			} else {
				s2.Field(i).Set(reflect.Zero(s2.Type().Field(i).Type))
			}
		}
	}
}

// works like InitPointerStructure but for arrays
// source array has values
func TransformFromArrayOfPointers(pArraySrc interface{}, pArrayDest interface{}, fieldsToKeep arrayOfStrings) {
	arraySrc := reflect.ValueOf(pArraySrc).Elem()
	arrayDest := reflect.ValueOf(pArrayDest).Elem()
	for i := 0; i < arraySrc.Len(); i++ {
		src := arraySrc.Index(i).Elem() // as we are working with array of pointers get true value
		dest := arrayDest.Index(i)
		for j := 0; j < src.NumField(); j++ {
			srcField := src.Field(j)
			destField := dest.Field(j)
			if destField.CanSet() {
				if fieldsToKeep.Contains(dest.Type().Field(j).Name) {
					destField.Set(srcField.Addr())
				} else {
					destField.Set(reflect.Zero(dest.Type().Field(j).Type))
				}
			}
		}
	}
}

// works like InitPointerStructure but for arrays
// source array has pointers
func TransformFromArrayOfValues(pArraySrc interface{}, pArrayDest interface{}, fieldsToKeep arrayOfStrings) {
	arraySrc := reflect.ValueOf(pArraySrc).Elem()
	arrayDest := reflect.ValueOf(pArrayDest).Elem()
	for i := 0; i < arraySrc.Len(); i++ {
		src := arraySrc.Index(i)
		dest := arrayDest.Index(i)
		for j := 0; j < src.NumField(); j++ {
			srcField := src.Field(j)
			destField := dest.Field(j)
			if destField.CanSet() {
				if fieldsToKeep.Contains(dest.Type().Field(j).Name) {
					destField.Set(srcField.Addr())
				} else {
					destField.Set(reflect.Zero(dest.Type().Field(j).Type))
				}
			}
		}
	}
}
<commit_msg>go lint helpers/struct.go<commit_after>/*
* Copyright (c) 2014 Santiago Arias | Remy Jourde
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package helpers

import (
	"reflect"
)

type arrayOfStrings []string

func (a arrayOfStrings) Contains(s string) bool {
	for _, e := range a {
		if e == s {
			return true
		}
	}
	return false
}

// CopyToPointerStructure copies structure fields to structure pointer fields.
// from two structures source and destination.
// structures defined as follows:
// both structures have the same fields
// destination structure field types are pointers of the source types
// example:
// type TA struct {
// 	Field1      string
// 	Field2      string
// 	IsSomething bool
// }
// type TB struct {
// 	Field1      *string `json:",omitempty"`
// 	Field2      *string `json:",omitempty"`
// 	IsSomething *bool   `json:",omitempty"`
// }
// use source structure to build destination structure
func CopyToPointerStructure(tSrc interface{}, tDest interface{}) {
	s1 := reflect.ValueOf(tSrc).Elem()
	s2 := reflect.ValueOf(tDest).Elem()
	for i := 0; i < s1.NumField(); i++ {
		f1 := s1.Field(i)
		f2 := s2.Field(i)
		if f2.CanSet() {
			s2.Field(i).Set(f1.Addr())
		}
	}
}

// KeepFields sets to nil all fields of t structure not present in array.
// We suppose that t is a structure of pointer types and fieldsToKeep is an array of the fields you wish to keep.
//
func KeepFields(t interface{}, fieldsToKeep arrayOfStrings) {
	s := reflect.ValueOf(t).Elem()
	typeOfT := s.Type()
	for i := 0; i < s.NumField(); i++ {
		f := s.Field(i)
		if !fieldsToKeep.Contains(typeOfT.Field(i).Name) && f.CanSet() {
			s.Field(i).Set(reflect.Zero(typeOfT.Field(i).Type))
		}
	}
}

// InitPointerStructure initializes structure with pointer fields
// from two structures, source and destination.
// structures defined as follows:
// both structures have the same fields
// destination structure field types are pointers of the source types
// example:
// type TA struct {
// 	Field1      string
// 	Field2      string
// 	IsSomething bool
// }
// type TB struct {
// 	Field1      *string `json:",omitempty"`
// 	Field2      *string `json:",omitempty"`
// 	IsSomething *bool   `json:",omitempty"`
// }
// use source structure to build destination structure
// and at the same time set to nil all fields of Destination structure not present in array fieldsToKeep
// we suppose that pDest is a structure of pointer types and fieldsToKeep is an array of the fields you wish to keep.
func InitPointerStructure(pSrc interface{}, pDest interface{}, fieldsToKeep arrayOfStrings) {
	s1 := reflect.ValueOf(pSrc).Elem()
	s2 := reflect.ValueOf(pDest).Elem()
	for i := 0; i < s1.NumField(); i++ {
		f1 := s1.Field(i)
		f2 := s2.Field(i)
		if f2.CanSet() {
			if fieldsToKeep.Contains(s2.Type().Field(i).Name) {
				s2.Field(i).Set(f1.Addr())
			} else {
				s2.Field(i).Set(reflect.Zero(s2.Type().Field(i).Type))
			}
		}
	}
}

// TransformFromArrayOfPointers works like InitPointerStructure but for arrays.
// source array has values.
//
func TransformFromArrayOfPointers(pArraySrc interface{}, pArrayDest interface{}, fieldsToKeep arrayOfStrings) {
	arraySrc := reflect.ValueOf(pArraySrc).Elem()
	arrayDest := reflect.ValueOf(pArrayDest).Elem()
	for i := 0; i < arraySrc.Len(); i++ {
		src := arraySrc.Index(i).Elem() // as we are working with array of pointers get true value
		dest := arrayDest.Index(i)
		for j := 0; j < src.NumField(); j++ {
			srcField := src.Field(j)
			destField := dest.Field(j)
			if destField.CanSet() {
				if fieldsToKeep.Contains(dest.Type().Field(j).Name) {
					destField.Set(srcField.Addr())
				} else {
					destField.Set(reflect.Zero(dest.Type().Field(j).Type))
				}
			}
		}
	}
}

// TransformFromArrayOfValues works like InitPointerStructure but for arrays.
// source array has pointers.
//
func TransformFromArrayOfValues(pArraySrc interface{}, pArrayDest interface{}, fieldsToKeep arrayOfStrings) {
	arraySrc := reflect.ValueOf(pArraySrc).Elem()
	arrayDest := reflect.ValueOf(pArrayDest).Elem()
	for i := 0; i < arraySrc.Len(); i++ {
		src := arraySrc.Index(i)
		dest := arrayDest.Index(i)
		for j := 0; j < src.NumField(); j++ {
			srcField := src.Field(j)
			destField := dest.Field(j)
			if destField.CanSet() {
				if fieldsToKeep.Contains(dest.Type().Field(j).Name) {
					destField.Set(srcField.Addr())
				} else {
					destField.Set(reflect.Zero(dest.Type().Field(j).Type))
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/tls"
)

// TLSConfig holds the TLS configuration.
type TLSConfig struct {
	Scheme string
	Server tls.Config
	Client tls.Config
}
<commit_msg>doc(server): some basic docs on the tls_config object<commit_after>package server

import (
	"crypto/tls"
)

// TLSConfig holds the TLS configuration.
type TLSConfig struct {
	Scheme string     // http or https
	Server tls.Config // Used by the Raft or etcd Server transporter.
	Client tls.Config // Used by the Raft peer client.
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"net/http"
	"testing"
	"time"
)

type mockHandler struct {
	mock.Mock
}

func (m *mockHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	m.Called(response, request)
}

func TestWebPADefaults(t *testing.T) {
	assert := assert.New(t)
	for _, webPA := range []*WebPA{nil, new(WebPA)} {
		assert.Equal(DefaultName, webPA.name())
		assert.Equal(DefaultAddress, webPA.address())
		assert.Equal(DefaultHealthAddress, webPA.healthAddress())
		assert.Equal("", webPA.pprofAddress())
		assert.Equal(DefaultHealthLogInterval, webPA.healthLogInterval())
		assert.Equal(DefaultLogConnectionState, webPA.logConnectionState())
		assert.Equal("", webPA.certificateFile())
		assert.Equal("", webPA.keyFile())
	}
}

func TestWebPAAccessors(t *testing.T) {
	const healthLogInterval time.Duration = 46 * time.Minute

	var (
		assert = assert.New(t)
		webPA  = WebPA{
			Name:               "Custom Name",
			CertificateFile:    "custom.cert",
			KeyFile:            "custom.key",
			LogConnectionState: !DefaultLogConnectionState,
			Address:            "localhost:15001",
			HealthAddress:      "localhost:55",
			HealthLogInterval:  healthLogInterval,
			PprofAddress:       "foobar:7273",
		}
	)

	assert.Equal("Custom Name", webPA.name())
	assert.Equal("custom.cert", webPA.certificateFile())
	assert.Equal("custom.key", webPA.keyFile())
	assert.Equal(!DefaultLogConnectionState, webPA.logConnectionState())
	assert.Equal("localhost:15001", webPA.address())
	assert.Equal("localhost:55", webPA.healthAddress())
	assert.Equal(healthLogInterval, webPA.healthLogInterval())
	assert.Equal("foobar:7273", webPA.pprofAddress())
}

func TestNewPrimaryServer(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:    "TestNewPrimaryServer",
			Address: ":6007",
		}

		primaryServer = webPA.NewPrimaryServer(logger, handler)
	)

	require.NotNil(primaryServer)
	assert.Equal(":6007", primaryServer.Addr)
	assert.Equal(handler, primaryServer.Handler)
	assert.Nil(primaryServer.ConnState)
	assertErrorLog(assert, verify, "TestNewPrimaryServer", primaryServer.ErrorLog)

	handler.AssertExpectations(t)
}

func TestNewPrimaryServerLogConnectionState(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:               "TestNewPrimaryServer",
			Address:            ":6007",
			LogConnectionState: true,
		}

		primaryServer = webPA.NewPrimaryServer(logger, handler)
	)

	require.NotNil(primaryServer)
	assert.Equal(":6007", primaryServer.Addr)
	assert.Equal(handler, primaryServer.Handler)
	assertErrorLog(assert, verify, "TestNewPrimaryServer", primaryServer.ErrorLog)
	assertConnState(assert, verify, primaryServer.ConnState)

	handler.AssertExpectations(t)
}
<commit_msg>Added health and pprof tests<commit_after>package server

import (
	"github.com/Comcast/webpa-common/health"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	"net/http"
	"testing"
	"time"
)

type mockHandler struct {
	mock.Mock
}

func (m *mockHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	m.Called(response, request)
}

func TestWebPADefaults(t *testing.T) {
	assert := assert.New(t)
	for _, webPA := range []*WebPA{nil, new(WebPA)} {
		assert.Equal(DefaultName, webPA.name())
		assert.Equal(DefaultAddress, webPA.address())
		assert.Equal(DefaultHealthAddress, webPA.healthAddress())
		assert.Equal("", webPA.pprofAddress())
		assert.Equal(DefaultHealthLogInterval, webPA.healthLogInterval())
		assert.Equal(DefaultLogConnectionState, webPA.logConnectionState())
		assert.Equal("", webPA.certificateFile())
		assert.Equal("", webPA.keyFile())
	}
}

func TestWebPAAccessors(t *testing.T) {
	const healthLogInterval time.Duration = 46 * time.Minute

	var (
		assert = assert.New(t)
		webPA  = WebPA{
			Name:               "Custom Name",
			CertificateFile:    "custom.cert",
			KeyFile:            "custom.key",
			LogConnectionState: !DefaultLogConnectionState,
			Address:            "localhost:15001",
			HealthAddress:      "localhost:55",
			HealthLogInterval:  healthLogInterval,
			PprofAddress:       "foobar:7273",
		}
	)

	assert.Equal("Custom Name", webPA.name())
	assert.Equal("custom.cert", webPA.certificateFile())
	assert.Equal("custom.key", webPA.keyFile())
	assert.Equal(!DefaultLogConnectionState, webPA.logConnectionState())
	assert.Equal("localhost:15001", webPA.address())
	assert.Equal("localhost:55", webPA.healthAddress())
	assert.Equal(healthLogInterval, webPA.healthLogInterval())
	assert.Equal("foobar:7273", webPA.pprofAddress())
}

func TestNewPrimaryServer(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:    "TestNewPrimaryServer",
			Address: ":6007",
		}

		primaryServer = webPA.NewPrimaryServer(logger, handler)
	)

	require.NotNil(primaryServer)
	assert.Equal(":6007", primaryServer.Addr)
	assert.Equal(handler, primaryServer.Handler)
	assert.Nil(primaryServer.ConnState)
	assertErrorLog(assert, verify, "TestNewPrimaryServer", primaryServer.ErrorLog)

	handler.AssertExpectations(t)
}

func TestNewPrimaryServerLogConnectionState(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:               "TestNewPrimaryServerLogConnectionState",
			Address:            ":331",
			LogConnectionState: true,
		}

		primaryServer = webPA.NewPrimaryServer(logger, handler)
	)

	require.NotNil(primaryServer)
	assert.Equal(":331", primaryServer.Addr)
	assert.Equal(handler, primaryServer.Handler)
	assertErrorLog(assert, verify, "TestNewPrimaryServerLogConnectionState", primaryServer.ErrorLog)
	assertConnState(assert, verify, primaryServer.ConnState)

	handler.AssertExpectations(t)
}

func TestNewHealthServer(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		options = []health.Option{health.Stat("option1"), health.Stat("option2")}

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:              "TestNewHealthServer",
			HealthAddress:     ":7181",
			HealthLogInterval: 15 * time.Second,
		}

		healthHandler, healthServer = webPA.NewHealthServer(logger, options...)
	)

	require.NotNil(healthHandler)
	require.NotNil(healthServer)
	assert.Equal(":7181", healthServer.Addr)
	assert.Equal(healthHandler, healthServer.Handler)
	assertErrorLog(assert, verify, "TestNewHealthServer", healthServer.ErrorLog)
	assert.Nil(healthServer.ConnState)
}

func TestNewHealthServerLogConnectionState(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		options = []health.Option{health.Stat("option1"), health.Stat("option2")}

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:               "TestNewHealthServerLogConnectionState",
			HealthAddress:      ":165",
			HealthLogInterval:  45 * time.Minute,
			LogConnectionState: true,
		}

		healthHandler, healthServer = webPA.NewHealthServer(logger, options...)
	)

	require.NotNil(healthHandler)
	require.NotNil(healthServer)
	assert.Equal(":165", healthServer.Addr)
	assert.Equal(healthHandler, healthServer.Handler)
	assertErrorLog(assert, verify, "TestNewHealthServerLogConnectionState", healthServer.ErrorLog)
	assertConnState(assert, verify, healthServer.ConnState)
}

func TestNewPprofServer(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:         "TestNewPprofServer",
			PprofAddress: ":996",
		}

		pprofServer = webPA.NewPprofServer(logger, handler)
	)

	require.NotNil(pprofServer)
	assert.Equal(":996", pprofServer.Addr)
	assert.Equal(handler, pprofServer.Handler)
	assert.Nil(pprofServer.ConnState)
	assertErrorLog(assert, verify, "TestNewPprofServer", pprofServer.ErrorLog)

	handler.AssertExpectations(t)
}

func TestNewPprofServerDefaultHandler(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:         "TestNewPprofServerDefaultHandler",
			PprofAddress: ":1299",
		}

		pprofServer = webPA.NewPprofServer(logger, nil)
	)

	require.NotNil(pprofServer)
	assert.Equal(":1299", pprofServer.Addr)
	assert.Equal(http.DefaultServeMux, pprofServer.Handler)
	assert.Nil(pprofServer.ConnState)
	assertErrorLog(assert, verify, "TestNewPprofServerDefaultHandler", pprofServer.ErrorLog)
}

func TestNewPprofServerNoPprofAddress(t *testing.T) {
	var (
		assert = assert.New(t)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name: "TestNewPprofServerNoPprofAddress",
		}

		pprofServer = webPA.NewPprofServer(logger, nil)
	)

	assert.Nil(pprofServer)
	assert.Empty(verify.String())
}

func TestNewPprofServerLogConnectionState(t *testing.T) {
	var (
		assert  = assert.New(t)
		require = require.New(t)
		handler = new(mockHandler)

		verify, logger = newTestLogger()
		webPA          = WebPA{
			Name:               "TestNewPprofServerLogConnectionState",
			PprofAddress:       ":16077",
			LogConnectionState: true,
		}

		pprofServer = webPA.NewPprofServer(logger, handler)
	)

	require.NotNil(pprofServer)
	assert.Equal(":16077", pprofServer.Addr)
	assert.Equal(handler, pprofServer.Handler)
	assertErrorLog(assert, verify, "TestNewPprofServerLogConnectionState", pprofServer.ErrorLog)
	assertConnState(assert, verify, pprofServer.ConnState)

	handler.AssertExpectations(t)
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/utils/featureflag"

	"github.com/juju/juju/feature"
	"github.com/juju/juju/service/common"
	"github.com/juju/juju/version"
)

// This exists to allow patching during tests.
var getVersion = func() version.Binary {
	return version.Current
}

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, err := discoverInitSystem()
	if err != nil {
		return nil, errors.Trace(err)
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

func discoverInitSystem() (string, error) {
	initName, err := discoverLocalInitSystem()
	if errors.IsNotFound(err) {
		// Fall back to checking the juju version.
		jujuVersion := getVersion()
		versionInitName, ok := VersionInitSystem(jujuVersion)
		if !ok {
			// The key error is the one from discoverLocalInitSystem so
			// that is what we return. However, we at least log the
			// failed fallback attempt.
			logger.Errorf("could not identify init system from %v", jujuVersion)
			return "", errors.Trace(err)
		}
		initName = versionInitName
	} else if err != nil {
		return "", errors.Trace(err)
	}
	return initName, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		case "":
			return "", false
		default:
			// Check for pre-precise releases.
			os, _ := version.GetOSFromSeries(vers.Series)
			if os == version.Unknown {
				return "", false
			}
			// vivid and later
			if featureflag.Enabled(feature.LegacyUpstart) {
				return InitSystemUpstart, true
			}
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

// pid1 is the path to the "file" that contains the path to the init
// system executable on linux.
const pid1 = "/proc/1/cmdline"

// These exist to allow patching during tests.
var (
	runtimeOS    = func() string { return runtime.GOOS }
	pid1Filename = func() string { return pid1 }

	initExecutable = func() (string, error) {
		pid1File := pid1Filename()
		data, err := ioutil.ReadFile(pid1File)
		if os.IsNotExist(err) {
			return "", errors.NotFoundf("init system (via %q)", pid1File)
		}
		if err != nil {
			return "", errors.Annotatef(err, "failed to identify init system (via %q)", pid1File)
		}
		executable := strings.Split(string(data), "\x00")[0]
		return executable, nil
	}
)

func discoverLocalInitSystem() (string, error) {
	if runtimeOS() == "windows" {
		return InitSystemWindows, nil
	}

	executable, err := initExecutable()
	if err != nil {
		return "", errors.Trace(err)
	}

	initName, ok := identifyInitSystem(executable)
	if !ok {
		return "", errors.NotFoundf("init system (based on %q)", executable)
	}
	logger.Debugf("discovered init system %q from executable %q", initName, executable)
	return initName, nil
}

func identifyInitSystem(executable string) (string, bool) {
	initSystem, ok := identifyExecutable(executable)
	if ok {
		return initSystem, true
	}

	if _, err := os.Stat(executable); os.IsNotExist(err) {
		return "", false
	} else if err != nil {
		logger.Errorf("failed to find %q: %v", executable, err)
		// The stat check is just an optimization so we go on anyway.
	}

	// TODO(ericsnow) First fall back to following symlinks?

	// Fall back to checking the "version" text.
	cmd := exec.Command(executable, "--version")
	out, err := cmd.CombinedOutput()
	if err != nil {
		logger.Errorf(`"%s --version" failed (%v): %s`, executable, err, out)
		return "", false
	}

	verText := string(out)
	switch {
	case strings.Contains(verText, "upstart"):
		return InitSystemUpstart, true
	case strings.Contains(verText, "systemd"):
		return InitSystemSystemd, true
	}

	// uh-oh
	return "", false
}

func identifyExecutable(executable string) (string, bool) {
	switch {
	case strings.Contains(executable, "upstart"):
		return InitSystemUpstart, true
	case strings.Contains(executable, "systemd"):
		return InitSystemSystemd, true
	default:
		return "", false
	}
}

// TODO(ericsnow) Build this script more dynamically (using shell.Renderer).
// TODO(ericsnow) Use a case statement in the script?

// DiscoverInitSystemScript is the shell script to use when
// discovering the local init system.
const DiscoverInitSystemScript = `#!/usr/bin/env bash

function checkInitSystem() {
    if [[ $@ == *"systemd"* ]]; then
        echo -n systemd
        exit $?
    elif [[ $@ == *"upstart"* ]]; then
        echo -n upstart
        exit $?
    fi
}

# Find the executable.
executable=$(cat /proc/1/cmdline | awk -F"\0" '{print $1}')
if [[ ! $? ]]; then
    exit 1
fi

# Check the executable.
checkInitSystem $executable

# First fall back to following symlinks.
if [[ -L $executable ]]; then
    linked=$(readlink "$(executable)")
    if [[ $? ]]; then
        executable=$linked

        # Check the linked executable.
        checkInitSystem $linked
    fi
fi

# Fall back to checking the "version" text.
verText=$("${executable}" --version)
if [[ $? ]]; then
    checkInitSystem $verText
fi

# uh-oh
exit 1
`

func writeDiscoverInitSystemScript(filename string) []string {
	// TODO(ericsnow) Use utils.shell.Renderer.WriteScript.
	return []string{
		fmt.Sprintf(`
cat > %s << 'EOF'
%s
EOF`[1:], filename, DiscoverInitSystemScript),
		"chmod 0755 " + filename,
	}
}

const caseLine = "%sif [[ $%s == %q ]]; then %s\n"

// newShellSelectCommand creates a bash if statement with an if
// (or elif) clause for each of the executables in linuxExecutables.
// The body of each clause comes from calling the provided handler with
// the init system name. If the handler does not support the args then
// it returns a false "ok" value.
func newShellSelectCommand(envVarName string, handler func(string) (string, bool)) string {
	// TODO(ericsnow) Build the command in a better way?
	// TODO(ericsnow) Use a case statement?

	prefix := ""
	lines := ""
	for _, initSystem := range linuxInitSystems {
		cmd, ok := handler(initSystem)
		if !ok {
			continue
		}
		lines += fmt.Sprintf(caseLine, prefix, envVarName, initSystem, cmd)

		if prefix != "el" {
			prefix = "el"
		}
	}
	if lines != "" {
		lines += "" +
			"else exit 1\n" +
			"fi"
	}
	return lines
}
<commit_msg>Use $1 instead of $@.<commit_after>package service

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/utils/featureflag"

	"github.com/juju/juju/feature"
	"github.com/juju/juju/service/common"
	"github.com/juju/juju/version"
)

// This exists to allow patching during tests.
var getVersion = func() version.Binary {
	return version.Current
}

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, err := discoverInitSystem()
	if err != nil {
		return nil, errors.Trace(err)
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

func discoverInitSystem() (string, error) {
	initName, err := discoverLocalInitSystem()
	if errors.IsNotFound(err) {
		// Fall back to checking the juju version.
		jujuVersion := getVersion()
		versionInitName, ok := VersionInitSystem(jujuVersion)
		if !ok {
			// The key error is the one from discoverLocalInitSystem so
			// that is what we return. However, we at least log the
			// failed fallback attempt.
			logger.Errorf("could not identify init system from %v", jujuVersion)
			return "", errors.Trace(err)
		}
		initName = versionInitName
	} else if err != nil {
		return "", errors.Trace(err)
	}
	return initName, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		case "":
			return "", false
		default:
			// Check for pre-precise releases.
			os, _ := version.GetOSFromSeries(vers.Series)
			if os == version.Unknown {
				return "", false
			}
			// vivid and later
			if featureflag.Enabled(feature.LegacyUpstart) {
				return InitSystemUpstart, true
			}
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

// pid1 is the path to the "file" that contains the path to the init
// system executable on linux.
const pid1 = "/proc/1/cmdline"

// These exist to allow patching during tests.
var (
	runtimeOS    = func() string { return runtime.GOOS }
	pid1Filename = func() string { return pid1 }

	initExecutable = func() (string, error) {
		pid1File := pid1Filename()
		data, err := ioutil.ReadFile(pid1File)
		if os.IsNotExist(err) {
			return "", errors.NotFoundf("init system (via %q)", pid1File)
		}
		if err != nil {
			return "", errors.Annotatef(err, "failed to identify init system (via %q)", pid1File)
		}
		executable := strings.Split(string(data), "\x00")[0]
		return executable, nil
	}
)

func discoverLocalInitSystem() (string, error) {
	if runtimeOS() == "windows" {
		return InitSystemWindows, nil
	}

	executable, err := initExecutable()
	if err != nil {
		return "", errors.Trace(err)
	}

	initName, ok := identifyInitSystem(executable)
	if !ok {
		return "", errors.NotFoundf("init system (based on %q)", executable)
	}
	logger.Debugf("discovered init system %q from executable %q", initName, executable)
	return initName, nil
}

func identifyInitSystem(executable string) (string, bool) {
	initSystem, ok := identifyExecutable(executable)
	if ok {
		return initSystem, true
	}

	if _, err := os.Stat(executable); os.IsNotExist(err) {
		return "", false
	} else if err != nil {
		logger.Errorf("failed to find %q: %v", executable, err)
		// The stat check is just an optimization so we go on anyway.
	}

	// TODO(ericsnow) First fall back to following symlinks?

	// Fall back to checking the "version" text.
	cmd := exec.Command(executable, "--version")
	out, err := cmd.CombinedOutput()
	if err != nil {
		logger.Errorf(`"%s --version" failed (%v): %s`, executable, err, out)
		return "", false
	}

	verText := string(out)
	switch {
	case strings.Contains(verText, "upstart"):
		return InitSystemUpstart, true
	case strings.Contains(verText, "systemd"):
		return InitSystemSystemd, true
	}

	// uh-oh
	return "", false
}

func identifyExecutable(executable string) (string, bool) {
	switch {
	case strings.Contains(executable, "upstart"):
		return InitSystemUpstart, true
	case strings.Contains(executable, "systemd"):
		return InitSystemSystemd, true
	default:
		return "", false
	}
}

// TODO(ericsnow) Build this script more dynamically (using shell.Renderer).
// TODO(ericsnow) Use a case statement in the script?

// DiscoverInitSystemScript is the shell script to use when
// discovering the local init system.
const DiscoverInitSystemScript = `#!/usr/bin/env bash

function checkInitSystem() {
    if [[ $1 == *"systemd"* ]]; then
        echo -n systemd
        exit $?
    elif [[ $1 == *"upstart"* ]]; then
        echo -n upstart
        exit $?
    fi
}

# Find the executable.
executable=$(cat /proc/1/cmdline | awk -F"\0" '{print $1}')
if [[ ! $? ]]; then
    exit 1
fi

# Check the executable.
checkInitSystem "$executable"

# First fall back to following symlinks.
if [[ -L $executable ]]; then
    linked=$(readlink "$(executable)")
    if [[ $? ]]; then
        executable=$linked

        # Check the linked executable.
        checkInitSystem "$linked"
    fi
fi

# Fall back to checking the "version" text.
verText=$("${executable}" --version)
if [[ $? ]]; then
    checkInitSystem "$verText"
fi

# uh-oh
exit 1
`

func writeDiscoverInitSystemScript(filename string) []string {
	// TODO(ericsnow) Use utils.shell.Renderer.WriteScript.
	return []string{
		fmt.Sprintf(`
cat > %s << 'EOF'
%s
EOF`[1:], filename, DiscoverInitSystemScript),
		"chmod 0755 " + filename,
	}
}

const caseLine = "%sif [[ $%s == %q ]]; then %s\n"

// newShellSelectCommand creates a bash if statement with an if
// (or elif) clause for each of the executables in linuxExecutables.
// The body of each clause comes from calling the provided handler with
// the init system name. If the handler does not support the args then
// it returns a false "ok" value.
func newShellSelectCommand(envVarName string, handler func(string) (string, bool)) string {
	// TODO(ericsnow) Build the command in a better way?
	// TODO(ericsnow) Use a case statement?

	prefix := ""
	lines := ""
	for _, initSystem := range linuxInitSystems {
		cmd, ok := handler(initSystem)
		if !ok {
			continue
		}
		lines += fmt.Sprintf(caseLine, prefix, envVarName, initSystem, cmd)

		if prefix != "el" {
			prefix = "el"
		}
	}
	if lines != "" {
		lines += "" +
			"else exit 1\n" +
			"fi"
	}
	return lines
}
<|endoftext|>


<commit_before>package filesystem

import (
	"syscall"

	"github.com/pkg/errors"

	"golang.org/x/text/unicode/norm"
)

func normalizeDirectoryNames(path string, names []string) error {
	// Grab statistics on the filesystem at this path.
	var fsStats syscall.Statfs_t
	if err := syscall.Statfs(path, &fsStats); err != nil {
		return errors.Wrap(err, "unable to load filesystem information")
	}

	// Check if the filesystem is some variant of HFS. If not, then no
	// normalization is required.
	//
	// We perform this check by checking if the filesystem type name starts with
	// "hfs". This is not the ideal way of checking for HFS volumes, but
	// unfortunately macOS' statvfs and statfs implementations are a bit
	// terrible. According to the man pages, the f_fsid field of the statvfs
	// structure is not meaningful, and it is seemingly not populated. The man
	// pages also say that the f_type field of the statfs structure is reserved,
	// but there is no documentation of its value. Before macOS 10.12, its value
	// was 17 for all HFS variants, but then it changed to 23. The only place
	// this value is available is in the XNU sources (xnu/bsd/vfs/vfs_conf.c),
	// and those aren't even available for 10.12 yet.
	//
	// Other people have solved this by checking for both:
	//  http://stackoverflow.com/questions/39350259
	//  https://trac.macports.org/ticket/52463
	//  https://github.com/jrk/afsctool/commit/1146c90
	//
	// But this doesn't seem ideal, especially with APFS coming soon. Thus, the
	// only sensible recourse is to use f_fstypename field, which is BARELY
	// documented. I suspect this is what's being used by NSWorkspace's
	// getFileSystemInfoForPath... method.
	//
	// This check should cover all HFS variants.
	isHFS := fsStats.Fstypename[0] == 'h' &&
		fsStats.Fstypename[1] == 'f' &&
		fsStats.Fstypename[2] == 's'
	if !isHFS {
		return nil
	}

	// If this is an HFS volume, then we need to convert names to NFC
	// normalization.
	//
	// This conversion might not be perfect, because HFS actually uses a custom
	// variant of NFD, but my understanding is that it's just NFD with certain
	// CJK characters not decomposed. The exact normalization has evolved a lot
	// over time and is way under-documented, so it's difficult to say.
	//
	// This link has a lot of information:
	// 	https://bugzilla.mozilla.org/show_bug.cgi?id=703161
	//
	// In any case, converting to NFC should be a fairly decent approximation
	// because most text will be have been in NFC normalization before HFS
	// forcefully decomposed it. It's admittedly not perfect, though.
	//
	// Once Apple decides to take HFS out behind the shed and shoot it, this
	// should be less of an issue. The new Apple File System (APFS) is a bit
	// better - it just treats file names as bags-of-bytes (just like other
	// filesystems). The one problem is that Apple is doing in-place conversion
	// of HFS volumes as they're rolling out APFS, and it's not clear if they're
	// converting to NFC as part of that (I seriously doubt it). If they're not,
	// we'll probably still need to perform this normalization.
	//
	// TODO: Once APFS rolls out, if the converter doesn't perform NFC
	// normalization, then I'd just make the NFC normalization unconditional on
	// macOS until HFS has become a thing of the past (at which point I'd remove
	// the normalization). Perhaps in the intermediate period, we can make the
	// normalization configurable.
	for i, n := range names {
		names[i] = norm.NFC.String(n)
	}

	// Success.
	return nil
}
<commit_msg>Updated Darwin filesystem name normalization documentation.<commit_after>package filesystem

import (
	"syscall"

	"github.com/pkg/errors"

	"golang.org/x/text/unicode/norm"
)

func normalizeDirectoryNames(path string, names []string) error {
	// Grab statistics on the filesystem at this path.
	var fsStats syscall.Statfs_t
	if err := syscall.Statfs(path, &fsStats); err != nil {
		return errors.Wrap(err, "unable to load filesystem information")
	}

	// Check if the filesystem is some variant of HFS. If not, then no
	// normalization is required.
	//
	// Well, that's not entirely true, but we have to take that position.
	// Apple's new APFS is normalization-preserving (while being normalization-
	// insensitive), which is great, except for cases where people convert HFS
	// volumes to APFS in-place (the default behavior for macOS 10.12 -> 10.13
	// upgrades), thus "locking-in" the decomposed normalization that HFS
	// enforced. Unfortunately there's not really a good heuristic for
	// determining which decomposed filenames on an APFS volume are due to HFS'
	// behavior. We could assume that all decomposed filenames on APFS volume at
	// like that for this reason, but (a) that's a pretty wild assumption,
	// especially as time goes on, and (b) as HFS dies off and more people
	// switch to APFS (likely though new volume creation), the cross-section of
	// cases where some HFS-induced decomposition is still haunting cross-
	// platform synchronization is going to become vanishingly small. People can
	// also just fix the problem by fixing the file name normalization once and
	// relying on APFS to preserve it.
	//
	// For a complete accounting of APFS' behavior, see the following article:
	// 	https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
	// Search for "How does Apple File System handle filenames?" The behavior
	// was a little inconsistent and crazy during the initial deployment on iOS
	// 10.3, but it's pretty much settled down now, and was always sane on
	// macOS, where its deployment occurred later. Even during the crazy periods
	// though, the above logic regarding not doing normalization on APFS still
	// stands.
	//
	// Anyway, we perform this check by checking if the filesystem type name
	// starts with "hfs". This is not the ideal way of checking for HFS volumes,
	// but unfortunately macOS' statvfs and statfs implementations are a bit
	// terrible. According to the man pages, the f_fsid field of the statvfs
	// structure is not meaningful, and it is seemingly not populated. The man
	// pages also say that the f_type field of the statfs structure is reserved,
	// but there is no documentation of its value. Before macOS 10.12, its value
	// was 17 for all HFS variants, but then it changed to 23. The only place
	// this value is available is in the XNU sources (xnu/bsd/vfs/vfs_conf.c),
	// and those aren't even available for 10.12 yet.
	//
	// Other people have solved this by checking for both:
	//  http://stackoverflow.com/questions/39350259
	//  https://trac.macports.org/ticket/52463
	//  https://github.com/jrk/afsctool/commit/1146c90
	//
	// But this is not robust, because it can break at any time with OS updates.
	// Thus, the only sensible recourse is to use f_fstypename field, which is
	// BARELY documented. I suspect this is what's being used by NSWorkspace's
	// getFileSystemInfoForPath... method.
	//
	// This check should cover all HFS variants.
	isHFS := fsStats.Fstypename[0] == 'h' &&
		fsStats.Fstypename[1] == 'f' &&
		fsStats.Fstypename[2] == 's'
	if !isHFS {
		return nil
	}

	// If this is an HFS volume, then we need to convert names to NFC
	// normalization.
	//
	// This conversion might not be perfect, because HFS actually uses a custom
	// variant of NFD, but my understanding is that it's just NFD with certain
	// CJK characters not decomposed. The exact normalization has evolved a lot
	// over time and is way under-documented, so it's difficult to say.
	//
	// This link has a lot of information:
	// 	https://bugzilla.mozilla.org/show_bug.cgi?id=703161
	//
	// In any case, converting to NFC should be a fairly decent approximation
	// because most text will be have been in NFC normalization before HFS
	// forcefully decomposed it. At the end of the day, though, it's just a
	// heuristic.
	for i, n := range names {
		names[i] = norm.NFC.String(n)
	}

	// Success.
	return nil
}
<|endoftext|>


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

package flag_test

import (
	. "launchpad.net/~rogpeppe/gnuflag/flag"
	"fmt"
	"os"
	"sort"
	"testing"
	"time"
)

var (
	test_bool     = Bool("test_bool", false, "bool value")
	test_int      = Int("test_int", 0, "int value")
	test_int64    = Int64("test_int64", 0, "int64 value")
	test_uint     = Uint("test_uint", 0, "uint value")
	test_uint64   = Uint64("test_uint64", 0, "uint64 value")
	test_string   = String("test_string", "0", "string value")
	test_float64  = Float64("test_float64", 0, "float64 value")
	test_duration = Duration("test_duration", 0, "time.Duration value")
)

func boolString(s string) string {
	if s == "0" {
		return "false"
	}
	return "true"
}

func TestEverything(t *testing.T) {
	m := make(map[string]*Flag)
	desired := "0"
	visitor := func(f *Flag) {
		if len(f.Name) > 5 && f.Name[0:5] == "test_" {
			m[f.Name] = f
			ok := false
			switch {
			case f.Value.String() == desired:
				ok = true
			case f.Name == "test_bool" && f.Value.String() == boolString(desired):
				ok = true
			case f.Name == "test_duration" && f.Value.String() == desired+"s":
				ok = true
			}
			if !ok {
				t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
			}
		}
	}
	VisitAll(visitor)
	if len(m) != 8 {
		t.Error("VisitAll misses some flags")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	m = make(map[string]*Flag)
	Visit(visitor)
	if len(m) != 0 {
		t.Errorf("Visit sees unset flags")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	// Now set all flags
	Set("test_bool", "true")
	Set("test_int", "1")
	Set("test_int64", "1")
	Set("test_uint", "1")
	Set("test_uint64", "1")
	Set("test_string", "1")
	Set("test_float64", "1")
	Set("test_duration", "1s")
	desired = "1"
	Visit(visitor)
	if len(m) != 8 {
		t.Error("Visit fails after set")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	// Now test they're visited in sort order.
	var flagNames []string
	Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
	if !sort.StringsAreSorted(flagNames) {
		t.Errorf("flag names not sorted: %v", flagNames)
	}
}

func TestUsage(t *testing.T) {
	called := false
	ResetForTesting(func() { called = true })
	if CommandLine().Parse([]string{"-x"}) == nil {
		t.Error("parse did not fail for unknown flag")
	}
	if !called {
		t.Error("did not call Usage for unknown flag")
	}
}

var gnuTests = []struct {
	intersperse bool
	args []string
	vals map[string]interface{}
	remaining []string
} {{
	true,
	[]string{
		"-a",
		"-",
		"-bc",
		"2",
		"-de1s",
		"-f2s",
		"-g", "3s",
		"--h",
		"--long",
		"--long2", "-4s",
		"3",
		"4",
		"--", "-5",
	},
	map[string]interface{} {
		"a": true,
		"b": true,
		"c": true,
		"d": true,
		"e": "1s",
		"f": "2s",
		"g": "3s",
		"h": true,
		"long": true,
		"long2": "-4s",
	},
	[]string{
		"-",
		"2",
		"3",
		"4",
		"-5",
	},
}, {
	true,
	[]string{
		"-a",
		"--",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"-b",
	},
}, {
	false,
	[]string{
		"-a",
		"foo",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"foo",
		"-b",
	},
},
{
	false,
	[]string{
		"-a",
		"--",
		"foo",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"foo",
		"-b",
	},
}}

func TestGnuParse(t *testing.T) {
	for i, g := range gnuTests {
		f := NewFlagSet("gnu test", ContinueOnError)
		flags := make(map[string]interface{})
		for name, val := range g.vals {
			switch val.(type) {
			case bool:
				flags[name] = f.Bool(name, false, "bool value "+name)
			case string:
				flags[name] = f.String(name, "", "string value "+name)
			}
		}
		err := f.ParseGnu(g.intersperse, g.args)
		if err != nil {
			t.Fatal(err)
		}
		for name, val := range g.vals {
			var actual interface{}
			switch val.(type) {
			case bool:
				actual = *(flags[name].(*bool))
			case string:
				actual = *(flags[name].(*string))
			}
			if val != actual {
				t.Errorf("test %d: flag %q, expected %v got %v", i, name, val, actual)
			}
		}
		if len(f.Args()) != len(g.remaining) {
			t.Fatalf("test %d: remaining args, expected %q got %q", i, g.remaining, f.Args())
		}
		for j, a := range f.Args() {
			if a != g.remaining[j] {
				t.Errorf("test %d: arg %d, expected %q got %q", i, j, g.remaining[i], a)
			}
		}
	}
}

func testParse(f *FlagSet, t *testing.T) {
	if f.Parsed() {
		t.Error("f.Parse() = true before Parse")
	}
	boolFlag := f.Bool("bool", false, "bool value")
	bool2Flag := f.Bool("bool2", false, "bool2 value")
	intFlag := f.Int("int", 0, "int value")
	int64Flag := f.Int64("int64", 0, "int64 value")
	uintFlag := f.Uint("uint", 0, "uint value")
	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
	stringFlag := f.String("string", "0", "string value")
	float64Flag := f.Float64("float64", 0, "float64 value")
	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
	extra := "one-extra-argument"
	args := []string{
		"-bool",
		"-bool2=true",
		"--int", "22",
		"--int64", "0x23",
		"-uint", "24",
		"--uint64", "25",
		"-string", "hello",
		"-float64", "2718e28",
		"-duration", "2m",
		extra,
	}
	if err := f.Parse(args); err != nil {
		t.Fatal(err)
	}
	if !f.Parsed() {
		t.Error("f.Parse() = false after Parse")
	}
	if *boolFlag != true {
		t.Error("bool flag should be true, is ", *boolFlag)
	}
	if *bool2Flag != true {
		t.Error("bool2 flag should be true, is ", *bool2Flag)
	}
	if *intFlag != 22 {
		t.Error("int flag should be 22, is ", *intFlag)
	}
	if *int64Flag != 0x23 {
		t.Error("int64 flag should be 0x23, is ", *int64Flag)
	}
	if *uintFlag != 24 {
		t.Error("uint flag should be 24, is ", *uintFlag)
	}
	if *uint64Flag != 25 {
		t.Error("uint64 flag should be 25, is ", *uint64Flag)
	}
	if *stringFlag != "hello" {
		t.Error("string flag should be `hello`, is ", *stringFlag)
	}
	if *float64Flag != 2718e28 {
		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
	}
	if *durationFlag != 2*time.Minute {
		t.Error("duration flag should be 2m, is ", *durationFlag)
	}
	if len(f.Args()) != 1 {
		t.Error("expected one argument, got", len(f.Args()))
	} else if f.Args()[0] != extra {
		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
	}
}

func TestParse(t *testing.T) {
	ResetForTesting(func() { t.Error("bad parse") })
	testParse(CommandLine(), t)
}

func TestFlagSetParse(t *testing.T) {
	testParse(NewFlagSet("test", ContinueOnError), t)
}

// Declare a user-defined flag type.
type flagVar []string

func (f *flagVar) String() string {
	return fmt.Sprint([]string(*f))
}

func (f *flagVar) Set(value string) error {
	*f = append(*f, value)
	return nil
}

func TestUserDefined(t *testing.T) {
	var flags FlagSet
	flags.Init("test", ContinueOnError)
	var v flagVar
	flags.Var(&v, "v", "usage")
	if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
		t.Error(err)
	}
	if len(v) != 3 {
		t.Fatal("expected 3 args; got ", len(v))
	}
	expect := "[1 2 3]"
	if v.String() != expect {
		t.Errorf("expected value %q got %q", expect, v.String())
	}
}

// This tests that one can reset the flags. This still works but not well, and is
// superseded by FlagSet.
func TestChangingArgs(t *testing.T) {
	ResetForTesting(func() { t.Fatal("bad parse") })
	oldArgs := os.Args
	defer func() { os.Args = oldArgs }()
	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
	before := Bool("before", false, "")
	if err := CommandLine().Parse(os.Args[1:]); err != nil {
		t.Fatal(err)
	}
	cmd := Arg(0)
	os.Args = Args()
	after := Bool("after", false, "")
	Parse()
	args := Args()

	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
	}
}

// Test that -help invokes the usage message and returns ErrHelp.
func TestHelp(t *testing.T) {
	var helpCalled = false
	fs := NewFlagSet("help test", ContinueOnError)
	fs.Usage = func() { helpCalled = true }
	var flag bool
	fs.BoolVar(&flag, "flag", false, "regular flag")
	// Regular flag invocation should work
	err := fs.Parse([]string{"-flag=true"})
	if err != nil {
		t.Fatal("expected no error; got ", err)
	}
	if !flag {
		t.Error("flag was not set by -flag")
	}
	if helpCalled {
		t.Error("help called for regular flag")
		helpCalled = false // reset for next test
	}
	// Help flag should work as expected.
	err = fs.Parse([]string{"-help"})
	if err == nil {
		t.Fatal("error expected")
	}
	if err != ErrHelp {
		t.Fatal("expected ErrHelp; got ", err)
	}
	if !helpCalled {
		t.Fatal("help was not called")
	}
	// If we define a help flag, that should override.
	var help bool
	fs.BoolVar(&help, "help", false, "help flag")
	helpCalled = false
	err = fs.Parse([]string{"-help"})
	if err != nil {
		t.Fatal("expected no error for defined -help; got ", err)
	}
	if helpCalled {
		t.Fatal("help was called; should not have been for defined help flag")
	}
}

func TestPrintDefaults(t *testing.T) {
	f := NewFlagSet("print test", ContinueOnError)
	var b bool
	var c int
	var d string
	var e float64
	f.IntVar(&c, "claptrap", 99, "usage not shown")
	f.IntVar(&c, "c", 99, "c usage")

	f.BoolVar(&b, "bal", false, "usage not shown")
	f.BoolVar(&b, "b", false, "b usage")
	f.BoolVar(&b, "balalaika", false, "usage not shown")

	f.StringVar(&d, "d", "d default", "d usage")

	f.Float64Var(&e, "elephant", 3.14, "elephant usage")

	got := f.DefaultsString()
	expect :=
`-b, --bal, --balalaika  (= false)
    b usage
-c, --claptrap  (= 99)
    c usage
-d (= "d default")
    d usage
--elephant  (= 3.14)
    elephant usage
`
	if got != expect {
		t.Error("expect %q got %q", expect, got)
	}
}
<commit_msg>better test<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package flag_test

import (
	. "launchpad.net/~rogpeppe/gnuflag/flag"
	"fmt"
	"os"
	"sort"
	"testing"
	"time"
)

var (
	test_bool     = Bool("test_bool", false, "bool value")
	test_int      = Int("test_int", 0, "int value")
	test_int64    = Int64("test_int64", 0, "int64 value")
	test_uint     = Uint("test_uint", 0, "uint value")
	test_uint64   = Uint64("test_uint64", 0, "uint64 value")
	test_string   = String("test_string", "0", "string value")
	test_float64  = Float64("test_float64", 0, "float64 value")
	test_duration = Duration("test_duration", 0, "time.Duration value")
)

func boolString(s string) string {
	if s == "0" {
		return "false"
	}
	return "true"
}

func TestEverything(t *testing.T) {
	m := make(map[string]*Flag)
	desired := "0"
	visitor := func(f *Flag) {
		if len(f.Name) > 5 && f.Name[0:5] == "test_" {
			m[f.Name] = f
			ok := false
			switch {
			case f.Value.String() == desired:
				ok = true
			case f.Name == "test_bool" && f.Value.String() == boolString(desired):
				ok = true
			case f.Name == "test_duration" && f.Value.String() == desired+"s":
				ok = true
			}
			if !ok {
				t.Error("Visit: bad value", f.Value.String(), "for", f.Name)
			}
		}
	}
	VisitAll(visitor)
	if len(m) != 8 {
		t.Error("VisitAll misses some flags")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	m = make(map[string]*Flag)
	Visit(visitor)
	if len(m) != 0 {
		t.Errorf("Visit sees unset flags")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	// Now set all flags
	Set("test_bool", "true")
	Set("test_int", "1")
	Set("test_int64", "1")
	Set("test_uint", "1")
	Set("test_uint64", "1")
	Set("test_string", "1")
	Set("test_float64", "1")
	Set("test_duration", "1s")
	desired = "1"
	Visit(visitor)
	if len(m) != 8 {
		t.Error("Visit fails after set")
		for k, v := range m {
			t.Log(k, *v)
		}
	}
	// Now test they're visited in sort order.
	var flagNames []string
	Visit(func(f *Flag) { flagNames = append(flagNames, f.Name) })
	if !sort.StringsAreSorted(flagNames) {
		t.Errorf("flag names not sorted: %v", flagNames)
	}
}

func TestUsage(t *testing.T) {
	called := false
	ResetForTesting(func() { called = true })
	if CommandLine().Parse([]string{"-x"}) == nil {
		t.Error("parse did not fail for unknown flag")
	}
	if !called {
		t.Error("did not call Usage for unknown flag")
	}
}

var gnuTests = []struct {
	intersperse bool
	args []string
	vals map[string]interface{}
	remaining []string
} {{
	true,
	[]string{
		"-a",
		"-",
		"-bc",
		"2",
		"-de1s",
		"-f2s",
		"-g", "3s",
		"--h",
		"--long",
		"--long2", "-4s",
		"3",
		"4",
		"--", "-5",
	},
	map[string]interface{} {
		"a": true,
		"b": true,
		"c": true,
		"d": true,
		"e": "1s",
		"f": "2s",
		"g": "3s",
		"h": true,
		"long": true,
		"long2": "-4s",
	},
	[]string{
		"-",
		"2",
		"3",
		"4",
		"-5",
	},
}, {
	true,
	[]string{
		"-a",
		"--",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"-b",
	},
}, {
	false,
	[]string{
		"-a",
		"foo",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"foo",
		"-b",
	},
},
{
	false,
	[]string{
		"-a",
		"--",
		"foo",
		"-b",
	},
	map[string]interface{} {
		"a": true,
		"b": false,
	},
	[]string{
		"foo",
		"-b",
	},
}}

func TestGnuParse(t *testing.T) {
	for i, g := range gnuTests {
		f := NewFlagSet("gnu test", ContinueOnError)
		flags := make(map[string]interface{})
		for name, val := range g.vals {
			switch val.(type) {
			case bool:
				flags[name] = f.Bool(name, false, "bool value "+name)
			case string:
				flags[name] = f.String(name, "", "string value "+name)
			}
		}
		err := f.ParseGnu(g.intersperse, g.args)
		if err != nil {
			t.Fatal(err)
		}
		for name, val := range g.vals {
			var actual interface{}
			switch val.(type) {
			case bool:
				actual = *(flags[name].(*bool))
			case string:
				actual = *(flags[name].(*string))
			}
			if val != actual {
				t.Errorf("test %d: flag %q, expected %v got %v", i, name, val, actual)
			}
		}
		if len(f.Args()) != len(g.remaining) {
			t.Fatalf("test %d: remaining args, expected %q got %q", i, g.remaining, f.Args())
		}
		for j, a := range f.Args() {
			if a != g.remaining[j] {
				t.Errorf("test %d: arg %d, expected %q got %q", i, j, g.remaining[i], a)
			}
		}
	}
}

func testParse(f *FlagSet, t *testing.T) {
	if f.Parsed() {
		t.Error("f.Parse() = true before Parse")
	}
	boolFlag := f.Bool("bool", false, "bool value")
	bool2Flag := f.Bool("bool2", false, "bool2 value")
	intFlag := f.Int("int", 0, "int value")
	int64Flag := f.Int64("int64", 0, "int64 value")
	uintFlag := f.Uint("uint", 0, "uint value")
	uint64Flag := f.Uint64("uint64", 0, "uint64 value")
	stringFlag := f.String("string", "0", "string value")
	float64Flag := f.Float64("float64", 0, "float64 value")
	durationFlag := f.Duration("duration", 5*time.Second, "time.Duration value")
	extra := "one-extra-argument"
	args := []string{
		"-bool",
		"-bool2=true",
		"--int", "22",
		"--int64", "0x23",
		"-uint", "24",
		"--uint64", "25",
		"-string", "hello",
		"-float64", "2718e28",
		"-duration", "2m",
		extra,
	}
	if err := f.Parse(args); err != nil {
		t.Fatal(err)
	}
	if !f.Parsed() {
		t.Error("f.Parse() = false after Parse")
	}
	if *boolFlag != true {
		t.Error("bool flag should be true, is ", *boolFlag)
	}
	if *bool2Flag != true {
		t.Error("bool2 flag should be true, is ", *bool2Flag)
	}
	if *intFlag != 22 {
		t.Error("int flag should be 22, is ", *intFlag)
	}
	if *int64Flag != 0x23 {
		t.Error("int64 flag should be 0x23, is ", *int64Flag)
	}
	if *uintFlag != 24 {
		t.Error("uint flag should be 24, is ", *uintFlag)
	}
	if *uint64Flag != 25 {
		t.Error("uint64 flag should be 25, is ", *uint64Flag)
	}
	if *stringFlag != "hello" {
		t.Error("string flag should be `hello`, is ", *stringFlag)
	}
	if *float64Flag != 2718e28 {
		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
	}
	if *durationFlag != 2*time.Minute {
		t.Error("duration flag should be 2m, is ", *durationFlag)
	}
	if len(f.Args()) != 1 {
		t.Error("expected one argument, got", len(f.Args()))
	} else if f.Args()[0] != extra {
		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
	}
}

func TestParse(t *testing.T) {
	ResetForTesting(func() { t.Error("bad parse") })
	testParse(CommandLine(), t)
}

func TestFlagSetParse(t *testing.T) {
	testParse(NewFlagSet("test", ContinueOnError), t)
}

// Declare a user-defined flag type.
type flagVar []string

func (f *flagVar) String() string {
	return fmt.Sprint([]string(*f))
}

func (f *flagVar) Set(value string) error {
	*f = append(*f, value)
	return nil
}

func TestUserDefined(t *testing.T) {
	var flags FlagSet
	flags.Init("test", ContinueOnError)
	var v flagVar
	flags.Var(&v, "v", "usage")
	if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
		t.Error(err)
	}
	if len(v) != 3 {
		t.Fatal("expected 3 args; got ", len(v))
	}
	expect := "[1 2 3]"
	if v.String() != expect {
		t.Errorf("expected value %q got %q", expect, v.String())
	}
}

// This tests that one can reset the flags. This still works but not well, and is
// superseded by FlagSet.
func TestChangingArgs(t *testing.T) {
	ResetForTesting(func() { t.Fatal("bad parse") })
	oldArgs := os.Args
	defer func() { os.Args = oldArgs }()
	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
	before := Bool("before", false, "")
	if err := CommandLine().Parse(os.Args[1:]); err != nil {
		t.Fatal(err)
	}
	cmd := Arg(0)
	os.Args = Args()
	after := Bool("after", false, "")
	Parse()
	args := Args()

	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
	}
}

// Test that -help invokes the usage message and returns ErrHelp.
func TestHelp(t *testing.T) {
	var helpCalled = false
	fs := NewFlagSet("help test", ContinueOnError)
	fs.Usage = func() { helpCalled = true }
	var flag bool
	fs.BoolVar(&flag, "flag", false, "regular flag")
	// Regular flag invocation should work
	err := fs.Parse([]string{"-flag=true"})
	if err != nil {
		t.Fatal("expected no error; got ", err)
	}
	if !flag {
		t.Error("flag was not set by -flag")
	}
	if helpCalled {
		t.Error("help called for regular flag")
		helpCalled = false // reset for next test
	}
	// Help flag should work as expected.
	err = fs.Parse([]string{"-help"})
	if err == nil {
		t.Fatal("error expected")
	}
	if err != ErrHelp {
		t.Fatal("expected ErrHelp; got ", err)
	}
	if !helpCalled {
		t.Fatal("help was not called")
	}
	// If we define a help flag, that should override.
	var help bool
	fs.BoolVar(&help, "help", false, "help flag")
	helpCalled = false
	err = fs.Parse([]string{"-help"})
	if err != nil {
		t.Fatal("expected no error for defined -help; got ", err)
	}
	if helpCalled {
		t.Fatal("help was called; should not have been for defined help flag")
	}
}

func TestPrintDefaults(t *testing.T) {
	f := NewFlagSet("print test", ContinueOnError)
	var b bool
	var c int
	var d string
	var e float64
	f.IntVar(&c, "trapclap", 99, "usage not shown")
	f.IntVar(&c, "c", 99, "c usage")

	f.BoolVar(&b, "bal", false, "usage not shown")
	f.BoolVar(&b, "b", false, "b usage")
	f.BoolVar(&b, "balalaika", false, "usage not shown")

	f.StringVar(&d, "d", "d default", "d usage")

	f.Float64Var(&e, "elephant", 3.14, "elephant usage")

	got := f.DefaultsString()
	expect :=
`-b, --bal, --balalaika  (= false)
    b usage
-c, --trapclap  (= 99)
    c usage
-d (= "d default")
    d usage
--elephant  (= 3.14)
    elephant usage
`
	if got != expect {
		t.Error("expect %q got %q", expect, got)
	}
}
<|endoftext|>


<commit_before>package reader

import (
	"goposm/cache"
	"goposm/element"
	"goposm/mapping"
	"goposm/parser"
	"goposm/stats"
	"os"
	"runtime"
	"sync"
)

var skipCoords, skipNodes, skipWays bool

func init() {
	if os.Getenv("GOPOSM_SKIP_COORDS") != "" {
		skipCoords = true
	}
	if os.Getenv("GOPOSM_SKIP_NODES") != "" {
		skipNodes = true
	}
	if os.Getenv("GOPOSM_SKIP_WAYS") != "" {
		skipWays = true
	}
}

func ReadPbf(cache *cache.OSMCache, progress *stats.Statistics, tagmapping *mapping.Mapping, filename string) {
	nodes := make(chan []element.Node, 4)
	coords := make(chan []element.Node, 4)
	ways := make(chan []element.Way, 4)
	relations := make(chan []element.Relation, 4)

	positions := parser.PBFBlockPositions(filename)

	waitParser := sync.WaitGroup{}
	for i := 0; i < runtime.NumCPU(); i++ {
		waitParser.Add(1)
		go func() {
			for pos := range positions {
				parser.ParseBlock(
					pos,
					coords,
					nodes,
					ways,
					relations,
				)
			}
			waitParser.Done()
		}()
	}

	waitWriter := sync.WaitGroup{}

	for i := 0; i < runtime.NumCPU(); i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.WayTagFilter()
			for ws := range ways {
				if skipWays {
					continue
				}
				for i, _ := range ws {
					m.Filter(&ws[i].Tags)
				}
				cache.Ways.PutWays(ws)
				progress.AddWays(len(ws))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; i < runtime.NumCPU(); i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.RelationTagFilter()
			for rels := range relations {
				for i, _ := range rels {
					m.Filter(&rels[i].Tags)
				}
				cache.Relations.PutRelations(rels)
				progress.AddRelations(len(rels))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; i < runtime.NumCPU(); i++ {
		waitWriter.Add(1)
		go func() {
			for nds := range coords {
				if skipCoords {
					continue
				}
				cache.Coords.PutCoords(nds)
				progress.AddCoords(len(nds))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; i < runtime.NumCPU(); i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.NodeTagFilter()
			for nds := range nodes {
				for i, _ := range nds {
					m.Filter(&nds[i].Tags)
				}
				cache.Nodes.PutNodes(nds)
				progress.AddNodes(len(nds))
			}
			waitWriter.Done()
		}()
	}

	waitParser.Wait()
	close(coords)
	close(nodes)
	close(ways)
	close(relations)
	waitWriter.Wait()
}
<commit_msg>make number of reader procs configurable<commit_after>package reader

import (
	"goposm/cache"
	"goposm/element"
	"goposm/mapping"
	"goposm/parser"
	"goposm/stats"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

var skipCoords, skipNodes, skipWays bool
var nParser, nWays, nRels, nNodes, nCoords int64

func init() {
	if os.Getenv("GOPOSM_SKIP_COORDS") != "" {
		skipCoords = true
	}
	if os.Getenv("GOPOSM_SKIP_NODES") != "" {
		skipNodes = true
	}
	if os.Getenv("GOPOSM_SKIP_WAYS") != "" {
		skipWays = true
	}
	nParser = int64(runtime.NumCPU())
	nWays = int64(runtime.NumCPU())
	nRels = int64(runtime.NumCPU())
	nNodes = int64(runtime.NumCPU())
	nCoords = int64(runtime.NumCPU())
	if procConf := os.Getenv("GOPOSM_READ_PROCS"); procConf != "" {
		parts := strings.Split(procConf, ":")
		nParser, _ = strconv.ParseInt(parts[0], 10, 32)
		nRels, _ = strconv.ParseInt(parts[1], 10, 32)
		nWays, _ = strconv.ParseInt(parts[2], 10, 32)
		nNodes, _ = strconv.ParseInt(parts[3], 10, 32)
		nCoords, _ = strconv.ParseInt(parts[3], 10, 32)
	}

}

func ReadPbf(cache *cache.OSMCache, progress *stats.Statistics, tagmapping *mapping.Mapping, filename string) {
	nodes := make(chan []element.Node, 4)
	coords := make(chan []element.Node, 4)
	ways := make(chan []element.Way, 4)
	relations := make(chan []element.Relation, 4)

	positions := parser.PBFBlockPositions(filename)

	waitParser := sync.WaitGroup{}
	for i := 0; int64(i) < nParser; i++ {
		waitParser.Add(1)
		go func() {
			for pos := range positions {
				parser.ParseBlock(
					pos,
					coords,
					nodes,
					ways,
					relations,
				)
			}
			waitParser.Done()
		}()
	}

	waitWriter := sync.WaitGroup{}

	for i := 0; int64(i) < nWays; i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.WayTagFilter()
			for ws := range ways {
				if skipWays {
					continue
				}
				for i, _ := range ws {
					m.Filter(&ws[i].Tags)
				}
				cache.Ways.PutWays(ws)
				progress.AddWays(len(ws))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; int64(i) < nRels; i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.RelationTagFilter()
			for rels := range relations {
				for i, _ := range rels {
					m.Filter(&rels[i].Tags)
				}
				cache.Relations.PutRelations(rels)
				progress.AddRelations(len(rels))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; int64(i) < nCoords; i++ {
		waitWriter.Add(1)
		go func() {
			for nds := range coords {
				if skipCoords {
					continue
				}
				cache.Coords.PutCoords(nds)
				progress.AddCoords(len(nds))
			}
			waitWriter.Done()
		}()
	}

	for i := 0; int64(i) < nNodes; i++ {
		waitWriter.Add(1)
		go func() {
			m := tagmapping.NodeTagFilter()
			for nds := range nodes {
				for i, _ := range nds {
					m.Filter(&nds[i].Tags)
				}
				cache.Nodes.PutNodes(nds)
				progress.AddNodes(len(nds))
			}
			waitWriter.Done()
		}()
	}

	waitParser.Wait()
	close(coords)
	close(nodes)
	close(ways)
	close(relations)
	waitWriter.Wait()
}
<|endoftext|>