<commit_before>package i18n

import (
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/qor/admin"
	"github.com/qor/cache"
	"github.com/qor/cache/memory"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

// Default default locale for i18n
var Default = "en-US"

// I18n struct that hold all translations
type I18n struct {
	scope        string
	value        string
	isInlineEdit bool
	Backends     []Backend
	CacheStore   cache.CacheStoreInterface
}

// ResourceName change display name in qor admin
func (I18n) ResourceName() string {
	return "Translation"
}

// Backend defined methods that needs for translation backend
type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation) error
	DeleteTranslation(*Translation) error
}

// Translation is a struct for translations, including Translation Key, Locale, Value
type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

func (translation Translation) cacheKey() string {
	return fmt.Sprintf("%v::%v", translation.Locale, translation.Key)
}

// New initialize I18n with backends
func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, CacheStore: memory.New()}
	for i := len(backends) - 1; i >= 0; i-- {
		var backend = backends[i]
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTranslation(translation)
		}
	}
	return i18n
}

func (i18n *I18n) LoadTranslations() map[string]map[string]*Translation {
	var translations = map[string]map[string]*Translation{}

	for _, backend := range i18n.Backends {
		for _, translation := range backend.LoadTranslations() {
			if translations[translation.Locale] == nil {
				translations[translation.Locale] = map[string]*Translation{}
			}
			translations[translation.Locale][translation.Key] = translation
		}
	}
	return translations
}

// AddTranslation add translation
func (i18n *I18n) AddTranslation(translation *Translation) {
	i18n.CacheStore.Set(translation.cacheKey(), translation)
}

// SaveTranslation save translation
func (i18n *I18n) SaveTranslation(translation *Translation) error {
	for _, backend := range i18n.Backends {
		if backend.SaveTranslation(translation) == nil {
			i18n.AddTranslation(translation)
		}
	}

	return errors.New("failed to save translation")
}

// DeleteTranslation delete translation
func (i18n *I18n) DeleteTranslation(translation *Translation) (err error) {
	for _, backend := range i18n.Backends {
		backend.DeleteTranslation(translation)
	}

	return i18n.CacheStore.Delete(translation.cacheKey())
}

// EnableInlineEdit enable inline edit, return HTML used to edit the translation
func (i18n *I18n) EnableInlineEdit(isInlineEdit bool) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: i18n.scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: isInlineEdit}
}

// Scope i18n scope
func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// Default default value of translation if key is missing
func (i18n *I18n) Default(value string) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: i18n.scope, value: value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// T translate with locale, key and arguments
func (i18n *I18n) T(locale, key string, args ...interface{}) template.HTML {
	var value = i18n.value
	var translationKey = key
	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	var translation Translation
	if err := i18n.CacheStore.Unmarshal(fmt.Sprintf("%v::%v", locale, key), &translation); err != nil || translation.Value == "" {
		// Get default translation if not translated
		if err := i18n.CacheStore.Unmarshal(fmt.Sprintf("%v::%v", Default, key), &translation); err != nil || translation.Value == "" {
			// If not initialized
			translation = Translation{Key: translationKey, Value: key, Locale: Default, Backend: i18n.Backends[0]}

			// Save translation
			i18n.SaveTranslation(&translation)
		}
	}

	if translation.Value != "" {
		value = translation.Value
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		value = str
	}

	if i18n.isInlineEdit {
		var editType string
		if len(value) > 25 {
			editType = "data-type=\"textarea\""
		}
		value = fmt.Sprintf("%s", editType, locale, key, value)
	}

	return template.HTML(value)
}

// RenderInlineEditAssets render inline edit html, it is using: http://vitalets.github.io/x-editable/index.html
// You could use Bootstrap or JQuery UI by set isIncludeExtendAssetLib to false and load files by yourself
func RenderInlineEditAssets(isIncludeJQuery bool, isIncludeExtendAssetLib bool) (template.HTML, error) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		var content string
		var hasError bool

		if isIncludeJQuery {
			content = `<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>`
		}

		if isIncludeExtendAssetLib {
			if extendLib, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/inline-edit-libs.tmpl")); err == nil {
				content += string(extendLib)
			} else {
				hasError = true
			}

			if css, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/stylesheets/i18n-inline.css")); err == nil {
				content += fmt.Sprintf("", string(css))
			} else {
				hasError = true
			}

		}

		if js, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/javascripts/i18n-inline.js")); err == nil {
			content += fmt.Sprintf("", string(js))
		} else {
			hasError = true
		}

		if !hasError {
			return template.HTML(content), nil
		}
	}

	return template.HTML(""), errors.New("templates not found")
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

// ConfigureQorResource configure qor resource for qor admin
func (i18n *I18n) ConfigureQorResource(res resource.Resourcer) {
	if res, ok := res.(*admin.Resource); ok {
		res.UseTheme("i18n")
		res.GetAdmin().I18n = i18n
		res.SearchAttrs("value") // generate search handler for i18n

		res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
			var translation Translation
			if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err == nil && translation.Value != "" {
				return translation.Value
			}

			if withDefault {
				if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err == nil && translation.Value != "" {
					return translation.Value
				}
			}

			return ""
		})

		var getPrimaryLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("primary_locale"); locale != "" {
				return locale
			}
			if availableLocales := getAvailableLocales(context.Request, context.CurrentUser); len(availableLocales) > 0 {
				return availableLocales[0]
			}
			return ""
		}

		var getEditingLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("to_locale"); locale != "" {
				return locale
			}
			return getLocaleFromContext(context.Context)
		}

		res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
			var (
				translations  = i18n.LoadTranslations()
				keysMap       = map[string]bool{}
				keyword       = strings.ToLower(context.Request.URL.Query().Get("keyword"))
				primaryLocale = getPrimaryLocale(context)
				editingLocale = getEditingLocale(context)
			)

			var filterTranslations = func(translations map[string]*Translation) {
				if translations != nil {
					for key, translation := range translations {
						if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), keyword) != -1 ||
							strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
							if _, ok := keysMap[key]; !ok {
								keysMap[key] = true
								keys = append(keys, key)
							}
						}
					}
				}
			}

			filterTranslations(translations[getPrimaryLocale(context)])
			if primaryLocale != editingLocale {
				filterTranslations(translations[getEditingLocale(context)])
			}

			sort.Strings(keys)

			pagination := context.Searcher.Pagination
			pagination.Total = len(keys)
			pagination.PrePage = 25
			pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
			if pagination.CurrentPage == 0 {
				pagination.CurrentPage = 1
			}
			if pagination.CurrentPage > 0 {
				pagination.Pages = pagination.Total / pagination.PrePage
			}
			context.Searcher.Pagination = pagination

			if pagination.CurrentPage == -1 {
				return keys
			}

			lastIndex := pagination.CurrentPage * pagination.PrePage
			if pagination.Total < lastIndex {
				lastIndex = pagination.Total
			}

			startIndex := (pagination.CurrentPage - 1) * pagination.PrePage
			if lastIndex >= startIndex {
				return keys[startIndex:lastIndex]
			}
			return []string{}
		})

		res.GetAdmin().RegisterFuncMap("i18n_primary_locale", getPrimaryLocale)

		res.GetAdmin().RegisterFuncMap("i18n_editing_locale", getEditingLocale)

		res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
			return getAvailableLocales(context.Request, context.CurrentUser)
		})

		res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
			return getEditableLocales(context.Request, context.CurrentUser)
		})

		controller := i18nController{i18n}
		router := res.GetAdmin().GetRouter()
		router.Get(res.ToParam(), controller.Index)
		router.Post(res.ToParam(), controller.Update)
		router.Put(res.ToParam(), controller.Update)

		admin.RegisterViewPath("github.com/qor/i18n/views")
	}
}

func cacheKey(strs ...string) string {
	return strings.Join(strs, "::")
}
<commit_msg>Fix create default translation<commit_after>package i18n

import (
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"

	"github.com/qor/admin"
	"github.com/qor/cache"
	"github.com/qor/cache/memory"
	"github.com/qor/qor"
	"github.com/qor/qor/resource"
	"github.com/qor/qor/utils"
	"github.com/theplant/cldr"
)

// Default default locale for i18n
var Default = "en-US"

// I18n struct that hold all translations
type I18n struct {
	scope        string
	value        string
	isInlineEdit bool
	Backends     []Backend
	CacheStore   cache.CacheStoreInterface
}

// ResourceName change display name in qor admin
func (I18n) ResourceName() string {
	return "Translation"
}

// Backend defined methods that needs for translation backend
type Backend interface {
	LoadTranslations() []*Translation
	SaveTranslation(*Translation) error
	DeleteTranslation(*Translation) error
}

// Translation is a struct for translations, including Translation Key, Locale, Value
type Translation struct {
	Key     string
	Locale  string
	Value   string
	Backend Backend
}

func (translation Translation) cacheKey() string {
	return fmt.Sprintf("%v::%v", translation.Locale, translation.Key)
}

// New initialize I18n with backends
func New(backends ...Backend) *I18n {
	i18n := &I18n{Backends: backends, CacheStore: memory.New()}
	for i := len(backends) - 1; i >= 0; i-- {
		var backend = backends[i]
		for _, translation := range backend.LoadTranslations() {
			translation.Backend = backend
			i18n.AddTranslation(translation)
		}
	}
	return i18n
}

func (i18n *I18n) LoadTranslations() map[string]map[string]*Translation {
	var translations = map[string]map[string]*Translation{}

	for _, backend := range i18n.Backends {
		for _, translation := range backend.LoadTranslations() {
			if translations[translation.Locale] == nil {
				translations[translation.Locale] = map[string]*Translation{}
			}
			translations[translation.Locale][translation.Key] = translation
		}
	}
	return translations
}

// AddTranslation add translation
func (i18n *I18n) AddTranslation(translation *Translation) {
	i18n.CacheStore.Set(translation.cacheKey(), translation)
}

// SaveTranslation save translation
func (i18n *I18n) SaveTranslation(translation *Translation) error {
	for _, backend := range i18n.Backends {
		if backend.SaveTranslation(translation) == nil {
			i18n.AddTranslation(translation)
		}
	}

	return errors.New("failed to save translation")
}

// DeleteTranslation delete translation
func (i18n *I18n) DeleteTranslation(translation *Translation) (err error) {
	for _, backend := range i18n.Backends {
		backend.DeleteTranslation(translation)
	}

	return i18n.CacheStore.Delete(translation.cacheKey())
}

// EnableInlineEdit enable inline edit, return HTML used to edit the translation
func (i18n *I18n) EnableInlineEdit(isInlineEdit bool) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: i18n.scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: isInlineEdit}
}

// Scope i18n scope
func (i18n *I18n) Scope(scope string) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: scope, value: i18n.value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// Default default value of translation if key is missing
func (i18n *I18n) Default(value string) admin.I18n {
	return &I18n{CacheStore: i18n.CacheStore, scope: i18n.scope, value: value, Backends: i18n.Backends, isInlineEdit: i18n.isInlineEdit}
}

// T translate with locale, key and arguments
func (i18n *I18n) T(locale, key string, args ...interface{}) template.HTML {
	var (
		value          = i18n.value
		translationKey = key
	)

	if i18n.scope != "" {
		translationKey = strings.Join([]string{i18n.scope, key}, ".")
	}

	var translation Translation
	if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err != nil || translation.Value == "" {
		// Get default translation if not translated
		if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err != nil || translation.Value == "" {
			// If not initialized
			translation = Translation{Key: translationKey, Value: value, Locale: Default, Backend: i18n.Backends[0]}

			// Save translation
			i18n.SaveTranslation(&translation)
		}
	}

	if translation.Value != "" {
		value = translation.Value
	}

	if str, err := cldr.Parse(locale, value, args...); err == nil {
		value = str
	}

	if i18n.isInlineEdit {
		var editType string
		if len(value) > 25 {
			editType = "data-type=\"textarea\""
		}
		value = fmt.Sprintf("%s", editType, locale, key, value)
	}

	return template.HTML(value)
}

// RenderInlineEditAssets render inline edit html, it is using: http://vitalets.github.io/x-editable/index.html
// You could use Bootstrap or JQuery UI by set isIncludeExtendAssetLib to false and load files by yourself
func RenderInlineEditAssets(isIncludeJQuery bool, isIncludeExtendAssetLib bool) (template.HTML, error) {
	for _, gopath := range strings.Split(os.Getenv("GOPATH"), ":") {
		var content string
		var hasError bool

		if isIncludeJQuery {
			content = `<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>`
		}

		if isIncludeExtendAssetLib {
			if extendLib, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/inline-edit-libs.tmpl")); err == nil {
				content += string(extendLib)
			} else {
				hasError = true
			}

			if css, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/stylesheets/i18n-inline.css")); err == nil {
				content += fmt.Sprintf("", string(css))
			} else {
				hasError = true
			}

		}

		if js, err := ioutil.ReadFile(path.Join(gopath, "src/github.com/qor/i18n/views/themes/i18n/assets/javascripts/i18n-inline.js")); err == nil {
			content += fmt.Sprintf("", string(js))
		} else {
			hasError = true
		}

		if !hasError {
			return template.HTML(content), nil
		}
	}

	return template.HTML(""), errors.New("templates not found")
}

func getLocaleFromContext(context *qor.Context) string {
	if locale := utils.GetLocale(context); locale != "" {
		return locale
	}

	return Default
}

type availableLocalesInterface interface {
	AvailableLocales() []string
}

type viewableLocalesInterface interface {
	ViewableLocales() []string
}

type editableLocalesInterface interface {
	EditableLocales() []string
}

func getAvailableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(viewableLocalesInterface); ok {
		return user.ViewableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

func getEditableLocales(req *http.Request, currentUser qor.CurrentUser) []string {
	if user, ok := currentUser.(editableLocalesInterface); ok {
		return user.EditableLocales()
	}

	if user, ok := currentUser.(availableLocalesInterface); ok {
		return user.AvailableLocales()
	}
	return []string{Default}
}

// ConfigureQorResource configure qor resource for qor admin
func (i18n *I18n) ConfigureQorResource(res resource.Resourcer) {
	if res, ok := res.(*admin.Resource); ok {
		res.UseTheme("i18n")
		res.GetAdmin().I18n = i18n
		res.SearchAttrs("value") // generate search handler for i18n

		res.GetAdmin().RegisterFuncMap("lt", func(locale, key string, withDefault bool) string {
			var translation Translation
			if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err == nil && translation.Value != "" {
				return translation.Value
			}

			if withDefault {
				if err := i18n.CacheStore.Unmarshal(cacheKey(locale, key), &translation); err == nil && translation.Value != "" {
					return translation.Value
				}
			}

			return ""
		})

		var getPrimaryLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("primary_locale"); locale != "" {
				return locale
			}
			if availableLocales := getAvailableLocales(context.Request, context.CurrentUser); len(availableLocales) > 0 {
				return availableLocales[0]
			}
			return ""
		}

		var getEditingLocale = func(context *admin.Context) string {
			if locale := context.Request.Form.Get("to_locale"); locale != "" {
				return locale
			}
			return getLocaleFromContext(context.Context)
		}

		res.GetAdmin().RegisterFuncMap("i18n_available_keys", func(context *admin.Context) (keys []string) {
			var (
				translations  = i18n.LoadTranslations()
				keysMap       = map[string]bool{}
				keyword       = strings.ToLower(context.Request.URL.Query().Get("keyword"))
				primaryLocale = getPrimaryLocale(context)
				editingLocale = getEditingLocale(context)
			)

			var filterTranslations = func(translations map[string]*Translation) {
				if translations != nil {
					for key, translation := range translations {
						if (keyword == "") || (strings.Index(strings.ToLower(translation.Key), keyword) != -1 ||
							strings.Index(strings.ToLower(translation.Value), keyword) != -1) {
							if _, ok := keysMap[key]; !ok {
								keysMap[key] = true
								keys = append(keys, key)
							}
						}
					}
				}
			}

			filterTranslations(translations[getPrimaryLocale(context)])
			if primaryLocale != editingLocale {
				filterTranslations(translations[getEditingLocale(context)])
			}

			sort.Strings(keys)

			pagination := context.Searcher.Pagination
			pagination.Total = len(keys)
			pagination.PrePage = 25
			pagination.CurrentPage, _ = strconv.Atoi(context.Request.URL.Query().Get("page"))
			if pagination.CurrentPage == 0 {
				pagination.CurrentPage = 1
			}
			if pagination.CurrentPage > 0 {
				pagination.Pages = pagination.Total / pagination.PrePage
			}
			context.Searcher.Pagination = pagination

			if pagination.CurrentPage == -1 {
				return keys
			}

			lastIndex := pagination.CurrentPage * pagination.PrePage
			if pagination.Total < lastIndex {
				lastIndex = pagination.Total
			}

			startIndex := (pagination.CurrentPage - 1) * pagination.PrePage
			if lastIndex >= startIndex {
				return keys[startIndex:lastIndex]
			}
			return []string{}
		})

		res.GetAdmin().RegisterFuncMap("i18n_primary_locale", getPrimaryLocale)

		res.GetAdmin().RegisterFuncMap("i18n_editing_locale", getEditingLocale)

		res.GetAdmin().RegisterFuncMap("i18n_viewable_locales", func(context admin.Context) []string {
			return getAvailableLocales(context.Request, context.CurrentUser)
		})

		res.GetAdmin().RegisterFuncMap("i18n_editable_locales", func(context admin.Context) []string {
			return getEditableLocales(context.Request, context.CurrentUser)
		})

		controller := i18nController{i18n}
		router := res.GetAdmin().GetRouter()
		router.Get(res.ToParam(), controller.Index)
		router.Post(res.ToParam(), controller.Update)
		router.Put(res.ToParam(), controller.Update)

		admin.RegisterViewPath("github.com/qor/i18n/views")
	}
}

func cacheKey(strs ...string) string {
	return strings.Join(strs, "::")
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package sidecar

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"testing"

	"k8s.io/test-infra/prow/entrypoint"
	"k8s.io/test-infra/prow/pod-utils/wrapper"

	"k8s.io/apimachinery/pkg/api/equality"
	"k8s.io/apimachinery/pkg/util/diff"
	"k8s.io/apimachinery/pkg/util/sets"
)

func TestWait(t *testing.T) {
	aborted := strconv.Itoa(entrypoint.AbortedErrorCode)
	skip := strconv.Itoa(entrypoint.PreviousErrorCode)
	const (
		pass = "0"
		fail = "1"
	)
	cases := []struct {
		name         string
		markers      []string
		abort        bool
		pass         bool
		accessDenied bool
		missing      bool
		failures     int
	}{
		{
			name:    "pass, not abort when 1 item passes",
			markers: []string{pass},
			pass:    true,
		},
		{
			name:    "pass when all items pass",
			markers: []string{pass, pass, pass},
			pass:    true,
		},
		{
			name:     "fail, not abort when 1 item fails",
			markers:  []string{fail},
			failures: 1,
		},
		{
			name:     "fail when any item fails",
			markers:  []string{pass, fail, pass},
			failures: 1,
		},
		{
			name:     "abort and fail when 1 item aborts",
			markers:  []string{aborted},
			abort:    true,
			failures: 1,
		},
		{
			name:     "abort when any item aborts",
			markers:  []string{pass, aborted, fail},
			abort:    true,
			failures: 2,
		},
		{
			name:     "fail when marker cannot be read",
			markers:  []string{pass, "not-an-exit-code", pass},
			failures: 1,
		},
		{
			name:     "fail when marker does not exist",
			markers:  []string{pass},
			missing:  true,
			failures: 1,
		},
		{
			name:     "count all failures",
			markers:  []string{pass, fail, aborted, skip, fail, pass},
			abort:    true,
			failures: 3,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()

			var entries []wrapper.Options

			for i, m := range tc.markers {
				p := path.Join(tmpDir, fmt.Sprintf("marker-%d.txt", i))
				var opt wrapper.Options
				opt.MarkerFile = p
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create marker %d: %v", i, err)
				}
				entries = append(entries, opt)
			}

			ctx, cancel := context.WithCancel(context.Background())
			if tc.missing {
				entries = append(entries, wrapper.Options{MarkerFile: "missing-marker.txt"})
				go cancel()
			}

			pass, abort, failures := wait(ctx, entries)
			cancel()
			if pass != tc.pass {
				t.Errorf("expected pass %t != actual %t", tc.pass, pass)
			}
			if abort != tc.abort {
				t.Errorf("expected abort %t != actual %t", tc.abort, abort)
			}
			if failures != tc.failures {
				t.Errorf("expected failures %d != actual %d", tc.failures, failures)
			}
		})
	}
}

func TestWaitParallelContainers(t *testing.T) {
	aborted := strconv.Itoa(entrypoint.AbortedErrorCode)
	const (
		pass = "0"
		fail = "1"
	)
	cases := []struct {
		name         string
		markers      []string
		abort        bool
		pass         bool
		accessDenied bool
		missing      bool
		failures     int
	}{
		{
			name:    "pass, not abort when 1 item passes",
			markers: []string{pass},
			pass:    true,
		},
		{
			name:    "pass when all items pass",
			markers: []string{pass, pass, pass},
			pass:    true,
		},
		{
			name:     "fail, not abort when 1 item fails",
			markers:  []string{fail},
			failures: 1,
		},
		{
			name:     "fail when any item fails",
			markers:  []string{pass, fail, pass},
			failures: 1,
		},
		{
			name:     "abort and fail when 1 item aborts",
			markers:  []string{aborted},
			abort:    true,
			failures: 1,
		},
		{
			name:     "abort when any item aborts",
			markers:  []string{pass, aborted, fail},
			abort:    true,
			failures: 2,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()

			var entries []wrapper.Options

			for i := range tc.markers {
				p := path.Join(tmpDir, fmt.Sprintf("marker-%d.txt", i))
				var opt wrapper.Options
				opt.MarkerFile = p
				entries = append(entries, opt)
			}

			ctx, cancel := context.WithCancel(context.Background())

			type WaitResult struct {
				pass     bool
				abort    bool
				failures int
			}

			waitResultsCh := make(chan WaitResult)

			go func() {
				pass, abort, failures := wait(ctx, entries)
				waitResultsCh <- WaitResult{pass, abort, failures}
			}()

			errCh := make(chan error, len(tc.markers))
			for i, m := range tc.markers {

				options := entries[i]
				entrypointOptions := entrypoint.Options{
					Options: &options,
				}
				marker, err := strconv.Atoi(m)
				if err != nil {
					errCh <- fmt.Errorf("invalid exit code: %v", err)
				}
				go func() {
					errCh <- entrypointOptions.Mark(marker)
				}()
			}

			for range tc.markers {
				if err := <-errCh; err != nil {
					t.Fatalf("could not create marker: %v", err)
				}
			}

			waitRes := <-waitResultsCh

			cancel()
			if waitRes.pass != tc.pass {
				t.Errorf("expected pass %t != actual %t", tc.pass, waitRes.pass)
			}
			if waitRes.abort != tc.abort {
				t.Errorf("expected abort %t != actual %t", tc.abort, waitRes.abort)
			}
			if waitRes.failures != tc.failures {
				t.Errorf("expected failures %d != actual %d", tc.failures, waitRes.failures)
			}
		})
	}
}

func TestCombineMetadata(t *testing.T) {
	cases := []struct {
		name     string
		pieces   []string
		expected map[string]interface{}
	}{
		{
			name:   "no problem when metadata file is not there",
			pieces: []string{"missing"},
		},
		{
			name:   "simple metadata",
			pieces: []string{`{"hello": "world"}`},
			expected: map[string]interface{}{
				"hello": "world",
			},
		},
		{
			name: "merge pieces",
			pieces: []string{
				`{"hello": "hello", "world": "world", "first": 1}`,
				`{"hello": "hola", "world": "world", "second": 2}`,
			},
			expected: map[string]interface{}{
				"hello":  "hola",
				"world":  "world",
				"first":  1.0,
				"second": 2.0,
			},
		},
		{
			name: "errors go into sidecar-errors",
			pieces: []string{
				`{"hello": "there"}`,
				"missing",
				"read-error",
				"json-error", // this is invalid json
				`{"world": "thanks"}`,
			},
			expected: map[string]interface{}{
				"hello": "there",
				"world": "thanks",
				errorKey: map[string]error{
					name(2): errors.New("read"),
					name(3): errors.New("json"),
				},
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()
			var entries []wrapper.Options

			for i, m := range tc.pieces {
				p := path.Join(tmpDir, fmt.Sprintf("metadata-%d.txt", i))
				var opt wrapper.Options
				opt.MetadataFile = p
				entries = append(entries, opt)
				if m == "missing" {
					continue
				} else if m == "read-error" {
					if err := os.Mkdir(p, 0700); err != nil {
						t.Fatalf("could not create %s: %v", p, err)
					}
					continue
				}
				// not-json is invalid json
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create metadata %d: %v", i, err)
				}
			}

			actual := combineMetadata(entries)
			expectedErrors, _ := tc.expected[errorKey].(map[string]error)
			actualErrors, _ := actual[errorKey].(map[string]error)
			delete(tc.expected, errorKey)
			delete(actual, errorKey)
			if !equality.Semantic.DeepEqual(tc.expected, actual) {
				t.Errorf("maps do not match:\n%s", diff.ObjectReflectDiff(tc.expected, actual))
			}

			if !equality.Semantic.DeepEqual(sets.StringKeySet(expectedErrors), sets.StringKeySet(actualErrors)) { // ignore the error values
				t.Errorf("errors do not match:\n%s", diff.ObjectReflectDiff(expectedErrors, actualErrors))
			}
		})
	}
}

func name(idx int) string {
	return nameEntry(idx, wrapper.Options{})
}

func TestLogReader(t *testing.T) {
	cases := []struct {
		name     string
		pieces   []string
		expected []string
	}{
		{
			name:     "basically works",
			pieces:   []string{"hello world"},
			expected: []string{"hello world"},
		},
		{
			name:   "multiple logging works",
			pieces: []string{"first", "second"},
			expected: []string{
				start(name(0)),
				"first",
				start(name(1)),
				"second",
			},
		},
		{
			name:   "note when a part has aproblem",
			pieces: []string{"first", "missing", "third"},
			expected: []string{
				start(name(0)),
				"first",
				start(name(1)),
				"Failed to open log-1.txt: whatever\n",
				start(name(2)),
				"third",
			},
		},
	}

	re := regexp.MustCompile(`(?m)(Failed to open) .*log-\d.txt: .*$`)
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()
			var entries []wrapper.Options

			for i, m := range tc.pieces {
				p := path.Join(tmpDir, fmt.Sprintf("log-%d.txt", i))
				var opt wrapper.Options
				opt.ProcessLog = p
				entries = append(entries, opt)
				if m == "missing" {
					continue
				}
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create log %d: %v", i, err)
				}
			}

			buf, err := ioutil.ReadAll(logReader(entries))
			if err != nil {
				t.Fatalf("failed to read all: %v", err)
			}
			const repl = "$1 <SNIP>"
			actual := re.ReplaceAllString(string(buf), repl)
			expected := re.ReplaceAllString(strings.Join(tc.expected, ""), repl)
			if !equality.Semantic.DeepEqual(expected, actual) {
				t.Errorf("maps do not match:\n%s", diff.ObjectReflectDiff(expected, actual))
			}
		})
	}

}
<commit_msg>added testcase to sidecar TestWaitParallelContainers to test missing marker file<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package sidecar

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"k8s.io/test-infra/prow/entrypoint"
	"k8s.io/test-infra/prow/pod-utils/wrapper"

	"k8s.io/apimachinery/pkg/api/equality"
	"k8s.io/apimachinery/pkg/util/diff"
	"k8s.io/apimachinery/pkg/util/sets"
)

func TestWait(t *testing.T) {
	aborted := strconv.Itoa(entrypoint.AbortedErrorCode)
	skip := strconv.Itoa(entrypoint.PreviousErrorCode)
	const (
		pass = "0"
		fail = "1"
	)
	cases := []struct {
		name         string
		markers      []string
		abort        bool
		pass         bool
		accessDenied bool
		missing      bool
		failures     int
	}{
		{
			name:    "pass, not abort when 1 item passes",
			markers: []string{pass},
			pass:    true,
		},
		{
			name:    "pass when all items pass",
			markers: []string{pass, pass, pass},
			pass:    true,
		},
		{
			name:     "fail, not abort when 1 item fails",
			markers:  []string{fail},
			failures: 1,
		},
		{
			name:     "fail when any item fails",
			markers:  []string{pass, fail, pass},
			failures: 1,
		},
		{
			name:     "abort and fail when 1 item aborts",
			markers:  []string{aborted},
			abort:    true,
			failures: 1,
		},
		{
			name:     "abort when any item aborts",
			markers:  []string{pass, aborted, fail},
			abort:    true,
			failures: 2,
		},
		{
			name:     "fail when marker cannot be read",
			markers:  []string{pass, "not-an-exit-code", pass},
			failures: 1,
		},
		{
			name:     "fail when marker does not exist",
			markers:  []string{pass},
			missing:  true,
			failures: 1,
		},
		{
			name:     "count all failures",
			markers:  []string{pass, fail, aborted, skip, fail, pass},
			abort:    true,
			failures: 3,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()

			var entries []wrapper.Options

			for i, m := range tc.markers {
				p := path.Join(tmpDir, fmt.Sprintf("marker-%d.txt", i))
				var opt wrapper.Options
				opt.MarkerFile = p
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create marker %d: %v", i, err)
				}
				entries = append(entries, opt)
			}

			ctx, cancel := context.WithCancel(context.Background())
			if tc.missing {
				entries = append(entries, wrapper.Options{MarkerFile: "missing-marker.txt"})
				go cancel()
			}

			pass, abort, failures := wait(ctx, entries)
			cancel()
			if pass != tc.pass {
				t.Errorf("expected pass %t != actual %t", tc.pass, pass)
			}
			if abort != tc.abort {
				t.Errorf("expected abort %t != actual %t", tc.abort, abort)
			}
			if failures != tc.failures {
				t.Errorf("expected failures %d != actual %d", tc.failures, failures)
			}
		})
	}
}

func TestWaitParallelContainers(t *testing.T) {
	aborted := strconv.Itoa(entrypoint.AbortedErrorCode)
	const (
		pass                 = "0"
		fail                 = "1"
		missingMarkerTimeout = time.Second
	)
	cases := []struct {
		name         string
		markers      []string
		abort        bool
		pass         bool
		accessDenied bool
		missing      bool
		failures     int
	}{
		{
			name:    "pass, not abort when 1 item passes",
			markers: []string{pass},
			pass:    true,
		},
		{
			name:    "pass when all items pass",
			markers: []string{pass, pass, pass},
			pass:    true,
		},
		{
			name:     "fail, not abort when 1 item fails",
			markers:  []string{fail},
			failures: 1,
		},
		{
			name:     "fail when any item fails",
			markers:  []string{pass, fail, pass},
			failures: 1,
		},
		{
			name:     "abort and fail when 1 item aborts",
			markers:  []string{aborted},
			abort:    true,
			failures: 1,
		},
		{
			name:     "abort when any item aborts",
			markers:  []string{pass, aborted, fail},
			abort:    true,
			failures: 2,
		},
		{
			name:     "fail when marker does not exist",
			markers:  []string{pass},
			missing:  true,
			failures: 1,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()

			var entries []wrapper.Options

			for i := range tc.markers {
				p := path.Join(tmpDir, fmt.Sprintf("marker-%d.txt", i))
				var opt wrapper.Options
				opt.MarkerFile = p
				entries = append(entries, opt)
			}

			if tc.missing {
				entries = append(entries, wrapper.Options{MarkerFile: "missing-marker.txt"})
			}

			ctx, cancel := context.WithCancel(context.Background())

			type WaitResult struct {
				pass     bool
				abort    bool
				failures int
			}

			waitResultsCh := make(chan WaitResult)

			go func() {
				pass, abort, failures := wait(ctx, entries)
				waitResultsCh <- WaitResult{pass, abort, failures}
			}()

			errCh := make(chan error, len(tc.markers))
			for i, m := range tc.markers {

				options := entries[i]

				entrypointOptions := entrypoint.Options{
					Options: &options,
				}
				marker, err := strconv.Atoi(m)
				if err != nil {
					errCh <- fmt.Errorf("invalid exit code: %v", err)
				}
				go func() {
					errCh <- entrypointOptions.Mark(marker)
				}()

			}

			if tc.missing {
				go func() {
					select {
					case <-time.After(missingMarkerTimeout):
						cancel()
						errCh <- nil
					}
				}()
			}

			for range tc.markers {
				if err := <-errCh; err != nil {
					t.Fatalf("could not create marker: %v", err)
				}
			}

			waitRes := <-waitResultsCh

			cancel()
			if waitRes.pass != tc.pass {
				t.Errorf("expected pass %t != actual %t", tc.pass, waitRes.pass)
			}
			if waitRes.abort != tc.abort {
				t.Errorf("expected abort %t != actual %t", tc.abort, waitRes.abort)
			}
			if waitRes.failures != tc.failures {
				t.Errorf("expected failures %d != actual %d", tc.failures, waitRes.failures)
			}
		})
	}
}

func TestCombineMetadata(t *testing.T) {
	cases := []struct {
		name     string
		pieces   []string
		expected map[string]interface{}
	}{
		{
			name:   "no problem when metadata file is not there",
			pieces: []string{"missing"},
		},
		{
			name:   "simple metadata",
			pieces: []string{`{"hello": "world"}`},
			expected: map[string]interface{}{
				"hello": "world",
			},
		},
		{
			name: "merge pieces",
			pieces: []string{
				`{"hello": "hello", "world": "world", "first": 1}`,
				`{"hello": "hola", "world": "world", "second": 2}`,
			},
			expected: map[string]interface{}{
				"hello":  "hola",
				"world":  "world",
				"first":  1.0,
				"second": 2.0,
			},
		},
		{
			name: "errors go into sidecar-errors",
			pieces: []string{
				`{"hello": "there"}`,
				"missing",
				"read-error",
				"json-error", // this is invalid json
				`{"world": "thanks"}`,
			},
			expected: map[string]interface{}{
				"hello": "there",
				"world": "thanks",
				errorKey: map[string]error{
					name(2): errors.New("read"),
					name(3): errors.New("json"),
				},
			},
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()
			var entries []wrapper.Options

			for i, m := range tc.pieces {
				p := path.Join(tmpDir, fmt.Sprintf("metadata-%d.txt", i))
				var opt wrapper.Options
				opt.MetadataFile = p
				entries = append(entries, opt)
				if m == "missing" {
					continue
				} else if m == "read-error" {
					if err := os.Mkdir(p, 0700); err != nil {
						t.Fatalf("could not create %s: %v", p, err)
					}
					continue
				}
				// not-json is invalid json
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create metadata %d: %v", i, err)
				}
			}

			actual := combineMetadata(entries)
			expectedErrors, _ := tc.expected[errorKey].(map[string]error)
			actualErrors, _ := actual[errorKey].(map[string]error)
			delete(tc.expected, errorKey)
			delete(actual, errorKey)
			if !equality.Semantic.DeepEqual(tc.expected, actual) {
				t.Errorf("maps do not match:\n%s", diff.ObjectReflectDiff(tc.expected, actual))
			}

			if !equality.Semantic.DeepEqual(sets.StringKeySet(expectedErrors), sets.StringKeySet(actualErrors)) { // ignore the error values
				t.Errorf("errors do not match:\n%s", diff.ObjectReflectDiff(expectedErrors, actualErrors))
			}
		})
	}
}

func name(idx int) string {
	return nameEntry(idx, wrapper.Options{})
}

func TestLogReader(t *testing.T) {
	cases := []struct {
		name     string
		pieces   []string
		expected []string
	}{
		{
			name:     "basically works",
			pieces:   []string{"hello world"},
			expected: []string{"hello world"},
		},
		{
			name:   "multiple logging works",
			pieces: []string{"first", "second"},
			expected: []string{
				start(name(0)),
				"first",
				start(name(1)),
				"second",
			},
		},
		{
			name:   "note when a part has aproblem",
			pieces: []string{"first", "missing", "third"},
			expected: []string{
				start(name(0)),
				"first",
				start(name(1)),
				"Failed to open log-1.txt: whatever\n",
				start(name(2)),
				"third",
			},
		},
	}

	re := regexp.MustCompile(`(?m)(Failed to open) .*log-\d.txt: .*$`)
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			tmpDir, err := ioutil.TempDir("", tc.name)
			if err != nil {
				t.Errorf("%s: error creating temp dir: %v", tc.name, err)
			}
			defer func() {
				if err := os.RemoveAll(tmpDir); err != nil {
					t.Errorf("%s: error cleaning up temp dir: %v", tc.name, err)
				}
			}()
			var entries []wrapper.Options

			for i, m := range tc.pieces {
				p := path.Join(tmpDir, fmt.Sprintf("log-%d.txt", i))
				var opt wrapper.Options
				opt.ProcessLog = p
				entries = append(entries, opt)
				if m == "missing" {
					continue
				}
				if err := ioutil.WriteFile(p, []byte(m), 0600); err != nil {
					t.Fatalf("could not create log %d: %v", i, err)
				}
			}

			buf, err := ioutil.ReadAll(logReader(entries))
			if err != nil {
				t.Fatalf("failed to read all: %v", err)
			}
			const repl = "$1 <SNIP>"
			actual := re.ReplaceAllString(string(buf), repl)
			expected := re.ReplaceAllString(strings.Join(tc.expected, ""), repl)
			if !equality.Semantic.DeepEqual(expected, actual) {
				t.Errorf("maps do not match:\n%s", diff.ObjectReflectDiff(expected, actual))
			}
		})
	}

}
<|endoftext|>


<commit_before>package proxy

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/cloudfoundry/gorouter/access_log"
	"github.com/cloudfoundry/gorouter/common"
	router_http "github.com/cloudfoundry/gorouter/common/http"
	"github.com/cloudfoundry/gorouter/route"
	steno "github.com/cloudfoundry/gosteno"
)

type RequestHandler struct {
	logger    *steno.Logger
	reporter  ProxyReporter
	logrecord *access_log.AccessLogRecord

	request  *http.Request
	response http.ResponseWriter
}

func NewRequestHandler(request *http.Request, response http.ResponseWriter, r ProxyReporter,
	alr *access_log.AccessLogRecord) RequestHandler {
	return RequestHandler{
		logger:    createLogger(request),
		reporter:  r,
		logrecord: alr,

		request:  request,
		response: response,
	}
}

func createLogger(request *http.Request) *steno.Logger {
	logger := steno.NewLogger("router.proxy.request-handler")

	logger.Set("RemoteAddr", request.RemoteAddr)
	logger.Set("Host", request.Host)
	logger.Set("Path", request.URL.Path)
	logger.Set("X-Forwarded-For", request.Header["X-Forwarded-For"])
	logger.Set("X-Forwarded-Proto", request.Header["X-Forwarded-Proto"])

	return logger
}

func (h *RequestHandler) Logger() *steno.Logger {
	return h.logger
}

func (h *RequestHandler) HandleHeartbeat() {
	h.logrecord.StatusCode = http.StatusOK
	h.response.WriteHeader(http.StatusOK)
	h.response.Write([]byte("ok\n"))
	h.request.Close = true
}

func (h *RequestHandler) HandleUnsupportedProtocol() {
	// must be hijacked, otherwise no response is sent back
	conn, buf, err := h.hijack()
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "Unsupported protocol")
		return
	}

	h.logrecord.StatusCode = http.StatusBadRequest
	fmt.Fprintf(buf, "HTTP/1.0 400 Bad Request\r\n\r\n")
	buf.Flush()
	conn.Close()
}

func (h *RequestHandler) HandleMissingRoute() {
	h.logger.Warnf("proxy.endpoint.not-found")

	h.response.Header().Set("X-Cf-RouterError", "unknown_route")
	message := fmt.Sprintf("Requested route ('%s') does not exist.", h.request.Host)
	h.writeStatus(http.StatusNotFound, message)
}

func (h *RequestHandler) HandleBadGateway(err error) {
	h.logger.Set("Error", err.Error())
	h.logger.Warnf("proxy.endpoint.failed")

	h.response.Header().Set("X-Cf-RouterError", "endpoint_failure")
	h.writeStatus(http.StatusBadGateway, "Registered endpoint failed to handle the request.")
}

func (h *RequestHandler) HandleTcpRequest(iter route.EndpointIterator) {
	h.logger.Set("Upgrade", "tcp")

	err := h.serveTcp(iter)
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "TCP forwarding to endpoint failed.")
	}
}

func (h *RequestHandler) HandleWebSocketRequest(iter route.EndpointIterator) {
	h.logger.Set("Upgrade", "websocket")

	err := h.serveWebSocket(iter)
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "WebSocket request to endpoint failed.")
	}
}

func (h *RequestHandler) writeStatus(code int, message string) {
	body := fmt.Sprintf("%d %s: %s", code, http.StatusText(code), message)

	h.logger.Warn(body)
	h.logrecord.StatusCode = code

	http.Error(h.response, body, code)
	if code > 299 {
		h.response.Header().Del("Connection")
	}
}

func (h *RequestHandler) serveTcp(iter route.EndpointIterator) error {
	var err error
	var connection net.Conn

	client, _, err := h.hijack()
	if err != nil {
		return err
	}

	defer func() {
		client.Close()
		if connection != nil {
			connection.Close()
		}
	}()

	retry := 0
	for {
		endpoint := iter.Next()
		if endpoint == nil {
			h.reporter.CaptureBadGateway(h.request)
			err = noEndpointsAvailable
			h.HandleBadGateway(err)
			return err
		}

		connection, err = net.DialTimeout("tcp", endpoint.CanonicalAddr(), 5*time.Second)
		if err == nil {
			break
		}

		iter.EndpointFailed()

		h.logger.Set("Error", err.Error())
		h.logger.Warn("proxy.tcp.failed")

		retry++
		if retry == retries {
			return err
		}
	}

	if connection != nil {
		forwardIO(client, connection)
	}

	return nil
}

func (h *RequestHandler) serveWebSocket(iter route.EndpointIterator) error {
	var err error
	var connection net.Conn

	client, _, err := h.hijack()
	if err != nil {
		return err
	}

	defer func() {
		client.Close()
		if connection != nil {
			connection.Close()
		}
	}()

	retry := 0
	for {
		endpoint := iter.Next()
		if endpoint == nil {
			h.reporter.CaptureBadGateway(h.request)
			err = noEndpointsAvailable
			h.HandleBadGateway(err)
			return err
		}

		connection, err = net.DialTimeout("tcp", endpoint.CanonicalAddr(), 5*time.Second)
		if err == nil {
			h.setupRequest(endpoint)
			break
		}

		iter.EndpointFailed()

		h.logger.Set("Error", err.Error())
		h.logger.Warn("proxy.websocket.failed")

		retry++
		if retry == retries {
			return err
		}
	}

	if connection != nil {
		err = h.request.Write(connection)
		if err != nil {
			return err
		}

		forwardIO(client, connection)
	}
	return nil
}

func (h *RequestHandler) setupRequest(endpoint *route.Endpoint) {
	h.setRequestURL(endpoint.CanonicalAddr())
	h.setRequestXForwardedFor()
	setRequestXRequestStart(h.request)
	setRequestXVcapRequestId(h.request, h.logger)
}

func (h *RequestHandler) setRequestURL(addr string) {
	h.request.URL.Scheme = "http"
	h.request.URL.Host = addr
}

func (h *RequestHandler) setRequestXForwardedFor() {
	if clientIP, _, err := net.SplitHostPort(h.request.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := h.request.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		h.request.Header.Set("X-Forwarded-For", clientIP)
	}
}

func setRequestXRequestStart(request *http.Request) {
	if _, ok := request.Header[http.CanonicalHeaderKey("X-Request-Start")]; !ok {
		request.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
	}
}

func setRequestXVcapRequestId(request *http.Request, logger *steno.Logger) {
	uuid, err := common.GenerateUUID()
	if err == nil {
		request.Header.Set(router_http.VcapRequestIdHeader, uuid)
		if logger != nil {
			logger.Set(router_http.VcapRequestIdHeader, uuid)
		}
	}
}

func setRequestXCfInstanceId(request *http.Request, endpoint *route.Endpoint) {
	value := endpoint.PrivateInstanceId
	if value == "" {
		value = endpoint.CanonicalAddr()
	}

	request.Header.Set(router_http.CfInstanceIdHeader, value)
}

func (h *RequestHandler) hijack() (client net.Conn, io *bufio.ReadWriter, err error) {
	hijacker, ok := h.response.(http.Hijacker)
	if !ok {
		return nil, nil, errors.New("response writer cannot hijack")
	}

	return hijacker.Hijack()
}

func forwardIO(a, b net.Conn) {
	done := make(chan bool, 2)

	copy := func(dst io.Writer, src io.Reader) {
		// don't care about errors here
		io.Copy(dst, src)
		done <- true
	}

	go copy(a, b)
	go copy(b, a)

	<-done
}
<commit_msg>Fix for log message in issue 37<commit_after>package proxy

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/cloudfoundry/gorouter/access_log"
	"github.com/cloudfoundry/gorouter/common"
	router_http "github.com/cloudfoundry/gorouter/common/http"
	"github.com/cloudfoundry/gorouter/route"
	steno "github.com/cloudfoundry/gosteno"
)

type RequestHandler struct {
	logger    *steno.Logger
	reporter  ProxyReporter
	logrecord *access_log.AccessLogRecord

	request  *http.Request
	response http.ResponseWriter
}

func NewRequestHandler(request *http.Request, response http.ResponseWriter, r ProxyReporter,
	alr *access_log.AccessLogRecord) RequestHandler {
	return RequestHandler{
		logger:    createLogger(request),
		reporter:  r,
		logrecord: alr,

		request:  request,
		response: response,
	}
}

func createLogger(request *http.Request) *steno.Logger {
	logger := steno.NewLogger("router.proxy.request-handler")

	logger.Set("RemoteAddr", request.RemoteAddr)
	logger.Set("Host", request.Host)
	logger.Set("Path", request.URL.Path)
	logger.Set("X-Forwarded-For", request.Header["X-Forwarded-For"])
	logger.Set("X-Forwarded-Proto", request.Header["X-Forwarded-Proto"])

	return logger
}

func (h *RequestHandler) Logger() *steno.Logger {
	return h.logger
}

func (h *RequestHandler) HandleHeartbeat() {
	h.logrecord.StatusCode = http.StatusOK
	h.response.WriteHeader(http.StatusOK)
	h.response.Write([]byte("ok\n"))
	h.request.Close = true
}

func (h *RequestHandler) HandleUnsupportedProtocol() {
	// must be hijacked, otherwise no response is sent back
	conn, buf, err := h.hijack()
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "Unsupported protocol")
		return
	}

	h.logrecord.StatusCode = http.StatusBadRequest
	fmt.Fprintf(buf, "HTTP/1.0 400 Bad Request\r\n\r\n")
	buf.Flush()
	conn.Close()
}

func (h *RequestHandler) HandleMissingRoute() {
	h.logger.Warnf("proxy.endpoint.not-found")

	h.response.Header().Set("X-Cf-RouterError", "unknown_route")
	message := fmt.Sprintf("Requested route ('%s') does not exist.", h.request.Host)
	h.writeStatus(http.StatusNotFound, message)
}

func (h *RequestHandler) HandleBadGateway(err error) {
	h.logger.Set("Error", err.Error())
	h.logger.Warnf("proxy.endpoint.failed")

	h.response.Header().Set("X-Cf-RouterError", "endpoint_failure")
	h.writeStatus(http.StatusBadGateway, "Registered endpoint failed to handle the request.")
}

func (h *RequestHandler) HandleTcpRequest(iter route.EndpointIterator) {
	h.logger.Set("Upgrade", "tcp")

	h.logrecord.StatusCode = http.StatusSwitchingProtocols

	err := h.serveTcp(iter)
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "TCP forwarding to endpoint failed.")
	}
}

func (h *RequestHandler) HandleWebSocketRequest(iter route.EndpointIterator) {
	h.logger.Set("Upgrade", "websocket")

	h.logrecord.StatusCode = http.StatusSwitchingProtocols

	err := h.serveWebSocket(iter)
	if err != nil {
		h.writeStatus(http.StatusBadRequest, "WebSocket request to endpoint failed.")
	}
}

func (h *RequestHandler) writeStatus(code int, message string) {
	body := fmt.Sprintf("%d %s: %s", code, http.StatusText(code), message)

	h.logger.Warn(body)
	h.logrecord.StatusCode = code

	http.Error(h.response, body, code)
	if code > 299 {
		h.response.Header().Del("Connection")
	}
}

func (h *RequestHandler) serveTcp(iter route.EndpointIterator) error {
	var err error
	var connection net.Conn

	client, _, err := h.hijack()
	if err != nil {
		return err
	}

	defer func() {
		client.Close()
		if connection != nil {
			connection.Close()
		}
	}()

	retry := 0
	for {
		endpoint := iter.Next()
		if endpoint == nil {
			h.reporter.CaptureBadGateway(h.request)
			err = noEndpointsAvailable
			h.HandleBadGateway(err)
			return err
		}

		connection, err = net.DialTimeout("tcp", endpoint.CanonicalAddr(), 5*time.Second)
		if err == nil {
			break
		}

		iter.EndpointFailed()

		h.logger.Set("Error", err.Error())
		h.logger.Warn("proxy.tcp.failed")

		retry++
		if retry == retries {
			return err
		}
	}

	if connection != nil {
		forwardIO(client, connection)
	}

	return nil
}

func (h *RequestHandler) serveWebSocket(iter route.EndpointIterator) error {
	var err error
	var connection net.Conn

	client, _, err := h.hijack()
	if err != nil {
		return err
	}

	defer func() {
		client.Close()
		if connection != nil {
			connection.Close()
		}
	}()

	retry := 0
	for {
		endpoint := iter.Next()
		if endpoint == nil {
			h.reporter.CaptureBadGateway(h.request)
			err = noEndpointsAvailable
			h.HandleBadGateway(err)
			return err
		}

		connection, err = net.DialTimeout("tcp", endpoint.CanonicalAddr(), 5*time.Second)
		if err == nil {
			h.setupRequest(endpoint)
			break
		}

		iter.EndpointFailed()

		h.logger.Set("Error", err.Error())
		h.logger.Warn("proxy.websocket.failed")

		retry++
		if retry == retries {
			return err
		}
	}

	if connection != nil {
		err = h.request.Write(connection)
		if err != nil {
			return err
		}

		forwardIO(client, connection)
	}
	return nil
}

func (h *RequestHandler) setupRequest(endpoint *route.Endpoint) {
	h.setRequestURL(endpoint.CanonicalAddr())
	h.setRequestXForwardedFor()
	setRequestXRequestStart(h.request)
	setRequestXVcapRequestId(h.request, h.logger)
}

func (h *RequestHandler) setRequestURL(addr string) {
	h.request.URL.Scheme = "http"
	h.request.URL.Host = addr
}

func (h *RequestHandler) setRequestXForwardedFor() {
	if clientIP, _, err := net.SplitHostPort(h.request.RemoteAddr); err == nil {
		// If we aren't the first proxy retain prior
		// X-Forwarded-For information as a comma+space
		// separated list and fold multiple headers into one.
		if prior, ok := h.request.Header["X-Forwarded-For"]; ok {
			clientIP = strings.Join(prior, ", ") + ", " + clientIP
		}
		h.request.Header.Set("X-Forwarded-For", clientIP)
	}
}

func setRequestXRequestStart(request *http.Request) {
	if _, ok := request.Header[http.CanonicalHeaderKey("X-Request-Start")]; !ok {
		request.Header.Set("X-Request-Start", strconv.FormatInt(time.Now().UnixNano()/1e6, 10))
	}
}

func setRequestXVcapRequestId(request *http.Request, logger *steno.Logger) {
	uuid, err := common.GenerateUUID()
	if err == nil {
		request.Header.Set(router_http.VcapRequestIdHeader, uuid)
		if logger != nil {
			logger.Set(router_http.VcapRequestIdHeader, uuid)
		}
	}
}

func setRequestXCfInstanceId(request *http.Request, endpoint *route.Endpoint) {
	value := endpoint.PrivateInstanceId
	if value == "" {
		value = endpoint.CanonicalAddr()
	}

	request.Header.Set(router_http.CfInstanceIdHeader, value)
}

func (h *RequestHandler) hijack() (client net.Conn, io *bufio.ReadWriter, err error) {
	hijacker, ok := h.response.(http.Hijacker)
	if !ok {
		return nil, nil, errors.New("response writer cannot hijack")
	}

	return hijacker.Hijack()
}

func forwardIO(a, b net.Conn) {
	done := make(chan bool, 2)

	copy := func(dst io.Writer, src io.Reader) {
		// don't care about errors here
		io.Copy(dst, src)
		done <- true
	}

	go copy(a, b)
	go copy(b, a)

	<-done
}
<|endoftext|>


<commit_before><commit_msg>Change error message<commit_after><|endoftext|>


<commit_before><commit_msg>Extract toLines<commit_after><|endoftext|>


<commit_before><commit_msg>Resolving merge conflicts<commit_after><|endoftext|>


<commit_before><commit_msg>Switch to main/run style, return error code on exit<commit_after><|endoftext|>


<commit_before><commit_msg>refactor<commit_after><|endoftext|>


<commit_before><commit_msg>Cleaning up TODOs<commit_after><|endoftext|>


<commit_before><commit_msg>Make id and delkey required for sql query<commit_after><|endoftext|>


<commit_before><commit_msg>Add ghetto expiry fix<commit_after><|endoftext|>


<commit_before><commit_msg>Fix serviceVersion<commit_after><|endoftext|>


<commit_before><commit_msg>Adds Clients support<commit_after><|endoftext|>


<commit_before><commit_msg>formatting and one comment; need to add a check for either future X,Y positioning or for diagonal placement/movement handling/correction<commit_after><|endoftext|>


<commit_before><commit_msg>Redirect on root<commit_after><|endoftext|>


<commit_before><commit_msg>output success message to stderr when configtest succeed<commit_after><|endoftext|>


<commit_before><commit_msg>:wrench: Adiciona variáveis de ambiente para ambiente de dev<commit_after><|endoftext|>


<commit_before><commit_msg>Switch user, conditional on being root.<commit_after><|endoftext|>


<commit_before><commit_msg>Add feedback in stdout<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"os"
	"strings"
)

type source struct {
	myToken *oauth2.Token
}

func (t source) Token() (*oauth2.Token, error) {
	return t.myToken, nil
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("missing github access token as avg 1\n")
		return
	}

	var NoContext oauth2.Context = nil
	src := source{
		myToken: &oauth2.Token{AccessToken: os.Args[1]},
	}

	client := oauth2.NewClient(NoContext, src)
	ghClient := github.NewClient(client)
	owner := "docker-library"
	repository := "official-images"

	pulls, _, err := ghClient.PullRequests.List(owner,repository, nil)
	if err != nil {
		fmt.Printf("%v", err)
		return
	}

	for _, pr := range pulls {
		commitFiles, _, err := ghClient.PullRequests.ListFiles(owner, repository, *pr.Number, nil)
		if err != nil {
			fmt.Printf("%v", err)
			continue
		}

		labels := []string{}
		for _, commitFile := range commitFiles {
			// fmt.Printf("%s, %s: %v\n", *pr.URL, *commitFile.Filename, commitFile)
			// fmt.Printf("PR: %s, %s\n", *pr.URL, *commitFile.Filename)
			if strings.HasPrefix(*commitFile.Filename, "library/") {
				labels = append(labels, *commitFile.Filename)
			}
		}
		fmt.Printf("%d: %v\n", *pr.Number, labels)
		// add labels
		labelObjs, _, err := ghClient.Issues.AddLabelsToIssue(owner, repository, *pr.Number, labels)
		if err != nil {
			fmt.Printf("%v", err)
			continue
		}
		fmt.Printf("%v\n", labelObjs)
	}

	return
}
<commit_msg>do all the PRs<commit_after>package main

import (
	"fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"os"
	"strings"
)

type source struct {
	myToken *oauth2.Token
}

func (t source) Token() (*oauth2.Token, error) {
	return t.myToken, nil
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("missing github access token as avg 1\n")
		return
	}

	var NoContext oauth2.Context = nil
	src := source{
		myToken: &oauth2.Token{AccessToken: os.Args[1]},
	}

	client := oauth2.NewClient(NoContext, src)
	ghClient := github.NewClient(client)
	owner := "docker-library"
	repository := "official-images"

	options := &github.PullRequestListOptions{
		State: "all",
	}

	pulls, _, err := ghClient.PullRequests.List(owner, repository, options)
	if err != nil {
		fmt.Printf("%v", err)
		return
	}

	for _, pr := range pulls {
		commitFiles, _, err := ghClient.PullRequests.ListFiles(owner, repository, *pr.Number, nil)
		if err != nil {
			fmt.Printf("%v", err)
			continue
		}

		labels := []string{}
		for _, commitFile := range commitFiles {
			// fmt.Printf("%s, %s: %v\n", *pr.URL, *commitFile.Filename, commitFile)
			// fmt.Printf("PR: %s, %s\n", *pr.URL, *commitFile.Filename)
			if strings.HasPrefix(*commitFile.Filename, "library/") {
				labels = append(labels, *commitFile.Filename)
			}
		}
		fmt.Printf("%d: %v\n", *pr.Number, labels)
		// add labels
		labelObjs, _, err := ghClient.Issues.AddLabelsToIssue(owner, repository, *pr.Number, labels)
		if err != nil {
			fmt.Printf("%v", err)
			continue
		}
		fmt.Printf("%v\n", labelObjs)
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"github.com/ryanmoran/inspector/commands"
	"github.com/ryanmoran/inspector/flags"
	"github.com/ryanmoran/inspector/tiles"
)

func main() {
	stdout := log.New(os.Stdout, "", 0)

	var global struct {
		Help bool   `short:"h" long:"help" description:"prints this usage information" default:"false"`
		Path string `short:"p" long:"path" description:"path to the product file"`
	}

	args, err := flags.Parse(&global, os.Args[1:])
	if err != nil {
		stdout.Fatal(err)
	}

	globalFlagsUsage, err := flags.Usage(global)
	if err != nil {
		stdout.Fatal(err)
	}

	var command string
	if len(args) > 0 {
		command, args = args[0], args[1:]
	}

	if command == "" {
		command = "help"
	}

	productParser := tiles.NewParser(global.Path, os.Stdout)

	commandSet := commands.Set{}
	commandSet["help"] = commands.NewHelp(os.Stdout, globalFlagsUsage, commandSet)
	commandSet["deadweight"] = commands.NewDeadweight(productParser, os.Stdout)
	commandSet["pkg-dep"] = commands.NewPkgDep(productParser, os.Stdout)

	err = commandSet.Execute(command, args)
	if err != nil {
		stdout.Fatal(err)
	}
}
<commit_msg>logs errors to stderr<commit_after>package main

import (
	"log"
	"os"

	"github.com/ryanmoran/inspector/commands"
	"github.com/ryanmoran/inspector/flags"
	"github.com/ryanmoran/inspector/tiles"
)

func main() {
	stderr := log.New(os.Stderr, "", 0)

	var global struct {
		Help bool   `short:"h" long:"help" description:"prints this usage information" default:"false"`
		Path string `short:"p" long:"path" description:"path to the product file"`
	}

	args, err := flags.Parse(&global, os.Args[1:])
	if err != nil {
		stderr.Fatal(err)
	}

	globalFlagsUsage, err := flags.Usage(global)
	if err != nil {
		stderr.Fatal(err)
	}

	var command string
	if len(args) > 0 {
		command, args = args[0], args[1:]
	}

	if command == "" {
		command = "help"
	}

	productParser := tiles.NewParser(global.Path, os.Stdout)

	commandSet := commands.Set{}
	commandSet["help"] = commands.NewHelp(os.Stdout, globalFlagsUsage, commandSet)
	commandSet["deadweight"] = commands.NewDeadweight(productParser, os.Stdout)
	commandSet["pkg-dep"] = commands.NewPkgDep(productParser, os.Stdout)

	err = commandSet.Execute(command, args)
	if err != nil {
		stderr.Fatal(err)
	}
}
<|endoftext|>


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

package main

import (
	"fmt"
	"os"
	"os/user"
	"runtime"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/minio/cli"
)

func init() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		Fatalf("Unable to obtain user's home directory. \nError: %s\n", err)
	}

	if os.Geteuid() == 0 {
		Fatalln("Please run ‘minio’ as a non-root user.")
	}

	// Check if minio was compiled using a supported version of Golang.
	checkGolangRuntimeVersion()
}

func migrate() {
	// Migrate config file
	migrateConfig()
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		humanize.Bytes(memstats.Alloc),
		humanize.Bytes(memstats.TotalAlloc),
		humanize.Bytes(memstats.HeapAlloc),
		humanize.Bytes(memstats.HeapSys))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

func findClosestCommands(command string) []string {
	var closestCommands []string
	for _, value := range commandsTree.PrefixMatch(command) {
		closestCommands = append(closestCommands, value.(string))
	}
	return closestCommands
}

func registerApp() *cli.App {
	// register all commands
	registerCommand(serverCmd)
	registerCommand(configCmd)
	registerCommand(versionCmd)
	registerCommand(updateCmd)

	// register all flags
	registerFlag(addressFlag)
	registerFlag(accessLogFlag)
	registerFlag(ratelimitFlag)
	registerFlag(anonymousFlag)
	registerFlag(certFlag)
	registerFlag(keyFlag)
	registerFlag(jsonFlag)

	// set up app
	app := cli.NewApp()
	app.Name = "Minio"
	// hide --version flag, version is a command
	app.HideVersion = true
	app.Author = "Minio.io"
	app.Usage = "Cloud Storage Server for Micro Services."
	app.Description = `Micro services environment provisions one Minio server per application instance. Scalability is achieved to through large number of smaller personalized instances. This version of the Minio binary is built using Filesystem storage backend for magnetic and solid state disks.`
	app.Flags = flags
	app.Commands = commands

	app.CustomAppHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

USAGE:
  minio {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

DESCRIPTION:
  {{.Description}}

COMMANDS:
  {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  {{end}}{{if .Flags}}
FLAGS:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
VERSION:
  ` + minioVersion +
		`
{{range $key, $value := ExtraInfo}}
{{$key}}:
  {{$value}}
{{end}}
`
	app.CommandNotFound = func(ctx *cli.Context, command string) {
		msg := fmt.Sprintf("‘%s’ is not a minio sub-command. See ‘minio help’.", command)
		closestCommands := findClosestCommands(command)
		if len(closestCommands) > 0 {
			msg += fmt.Sprintf("\n\nDid you mean one of these?\n")
			for _, cmd := range closestCommands {
				msg += fmt.Sprintf("        ‘%s’\n", cmd)
			}
		}
		Fatalln(msg)
	}

	return app
}

func main() {
	app := registerApp()
	app.Before = func(c *cli.Context) error {
		globalJSONFlag = c.GlobalBool("json")
		migrate()
		return nil
	}
	app.ExtraInfo = func() map[string]string {
		return getSystemData()
	}

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

package main

import (
	"fmt"
	"os"
	"os/user"
	"runtime"
	"strconv"

	"github.com/dustin/go-humanize"
	"github.com/minio/cli"
)

func init() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		Fatalf("Unable to obtain user's home directory. \nError: %s\n", err)
	}

	if os.Geteuid() == 0 {
		Fatalln("Please run ‘minio’ as a non-root user.")
	}

	// Check if minio was compiled using a supported version of Golang.
	checkGolangRuntimeVersion()
}

func migrate() {
	// Migrate config file
	migrateConfig()
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		humanize.Bytes(memstats.Alloc),
		humanize.Bytes(memstats.TotalAlloc),
		humanize.Bytes(memstats.HeapAlloc),
		humanize.Bytes(memstats.HeapSys))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

func findClosestCommands(command string) []string {
	var closestCommands []string
	for _, value := range commandsTree.PrefixMatch(command) {
		closestCommands = append(closestCommands, value.(string))
	}
	return closestCommands
}

func registerApp() *cli.App {
	// register all commands
	registerCommand(serverCmd)
	registerCommand(configCmd)
	registerCommand(versionCmd)
	registerCommand(updateCmd)

	// register all flags
	registerFlag(addressFlag)
	registerFlag(accessLogFlag)
	registerFlag(ratelimitFlag)
	registerFlag(anonymousFlag)
	registerFlag(certFlag)
	registerFlag(keyFlag)
	registerFlag(jsonFlag)

	// set up app
	app := cli.NewApp()
	app.Name = "Minio"
	// hide --version flag, version is a command
	app.HideVersion = true
	app.Author = "Minio.io"
	app.Usage = "Cloud Storage Server for Micro Services."
	app.Description = `Micro services environment provisions one Minio server per application instance. Scalability is achieved to through large number of smaller personalized instances. This version of the Minio binary is built using Filesystem storage backend for magnetic and solid state disks.`
	app.Flags = flags
	app.Commands = commands

	app.CustomAppHelpTemplate = `NAME:
  {{.Name}} - {{.Usage}}

DESCRIPTION:
  {{.Description}}

USAGE:
  minio {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
  {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  {{end}}{{if .Flags}}
FLAGS:
  {{range .Flags}}{{.}}
  {{end}}{{end}}
VERSION:
  ` + minioVersion +
		`
{{range $key, $value := ExtraInfo}}
{{$key}}:
  {{$value}}
{{end}}
`
	app.CommandNotFound = func(ctx *cli.Context, command string) {
		msg := fmt.Sprintf("‘%s’ is not a minio sub-command. See ‘minio help’.", command)
		closestCommands := findClosestCommands(command)
		if len(closestCommands) > 0 {
			msg += fmt.Sprintf("\n\nDid you mean one of these?\n")
			for _, cmd := range closestCommands {
				msg += fmt.Sprintf("        ‘%s’\n", cmd)
			}
		}
		Fatalln(msg)
	}

	return app
}

func main() {
	app := registerApp()
	app.Before = func(c *cli.Context) error {
		globalJSONFlag = c.GlobalBool("json")
		migrate()
		return nil
	}
	app.ExtraInfo = func() map[string]string {
		return getSystemData()
	}

	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"

	"github.com/czerwonk/bird_exporter/protocol"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
)

const version string = "1.2.4"

var (
	showVersion   = flag.Bool("version", false, "Print version information.")
	listenAddress = flag.String("web.listen-address", ":9324", "Address on which to expose metrics and web interface.")
	metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	birdSocket    = flag.String("bird.socket", "/var/run/bird.ctl", "Socket to communicate with bird routing daemon")
	birdV2        = flag.Bool("bird.v2", false, "Bird major version >= 2.0 (multi channel protocols)")
	newFormat     = flag.Bool("format.new", false, "New metric format (more convenient / generic)")
	enableBgp     = flag.Bool("proto.bgp", true, "Enables metrics for protocol BGP")
	enableOspf    = flag.Bool("proto.ospf", true, "Enables metrics for protocol OSPF")
	enableKernel  = flag.Bool("proto.kernel", true, "Enables metrics for protocol Kernel")
	enableStatic  = flag.Bool("proto.static", true, "Enables metrics for protocol Static")
	enableDirect  = flag.Bool("proto.direct", true, "Enables metrics for protocol Direct")
	// pre bird 2.0
	bird6Socket       = flag.String("bird.socket6", "/var/run/bird6.ctl", "Socket to communicate with bird6 routing daemon (not compatible with -bird.v2)")
	birdEnabled       = flag.Bool("bird.ipv4", true, "Get protocols from bird (not compatible with -bird.v2)")
	bird6Enabled      = flag.Bool("bird.ipv6", true, "Get protocols from bird6 (not compatible with -bird.v2)")
	descriptionLabels = flag.Bool("format.description-labels", false, "Add labels from protocol descriptions.")
)

func init() {
	flag.Usage = func() {
		fmt.Println("Usage: bird_exporter [ ... ]\n\nParameters:")
		fmt.Println()
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()

	if *showVersion {
		printVersion()
		os.Exit(0)
	}

	startServer()
}

func printVersion() {
	fmt.Println("bird_exporter")
	fmt.Printf("Version: %s\n", version)
	fmt.Println("Author(s): Daniel Czerwonk")
	fmt.Println("Metric exporter for bird routing daemon")
}

func startServer() {
	log.Infof("Starting bird exporter (Version: %s)\n", version)

	if !*newFormat {
		log.Info("INFO: You are using the old metric format. Please consider using the new (more convenient one) by setting -format.new=true.")
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>Bird Routing Daemon Exporter (Version ` + version + `)</title>

Bird Routing Daemon Exporter

<a href="` + *metricsPath + `">Metrics

More information:

github.com/czerwonk/bird_exporter

			`))
	})
	http.HandleFunc(*metricsPath, handleMetricsRequest)

	log.Infof("Listening for %s on %s\n", *metricsPath, *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func handleMetricsRequest(w http.ResponseWriter, r *http.Request) {
	reg := prometheus.NewRegistry()
	p := enabledProtocols()
	c := NewMetricCollector(*newFormat, p, *descriptionLabels)
	reg.MustRegister(c)

	promhttp.HandlerFor(reg, promhttp.HandlerOpts{
		ErrorLog:      log.NewErrorLogger(),
		ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(w, r)
}
func enabledProtocols() int {
	res := 0

	if *enableBgp {
		res |= protocol.BGP
	}
	if *enableOspf {
		res |= protocol.OSPF
	}
	if *enableKernel {
		res |= protocol.Kernel
	}
	if *enableStatic {
		res |= protocol.Static
	}
	if *enableDirect {
		res |= protocol.Direct
	}

	return res
}
<commit_msg>Update main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"

	"github.com/czerwonk/bird_exporter/protocol"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/common/log"
)

const version string = "1.2.5"

var (
	showVersion   = flag.Bool("version", false, "Print version information.")
	listenAddress = flag.String("web.listen-address", ":9324", "Address on which to expose metrics and web interface.")
	metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
	birdSocket    = flag.String("bird.socket", "/var/run/bird.ctl", "Socket to communicate with bird routing daemon")
	birdV2        = flag.Bool("bird.v2", false, "Bird major version >= 2.0 (multi channel protocols)")
	newFormat     = flag.Bool("format.new", false, "New metric format (more convenient / generic)")
	enableBgp     = flag.Bool("proto.bgp", true, "Enables metrics for protocol BGP")
	enableOspf    = flag.Bool("proto.ospf", true, "Enables metrics for protocol OSPF")
	enableKernel  = flag.Bool("proto.kernel", true, "Enables metrics for protocol Kernel")
	enableStatic  = flag.Bool("proto.static", true, "Enables metrics for protocol Static")
	enableDirect  = flag.Bool("proto.direct", true, "Enables metrics for protocol Direct")
	// pre bird 2.0
	bird6Socket       = flag.String("bird.socket6", "/var/run/bird6.ctl", "Socket to communicate with bird6 routing daemon (not compatible with -bird.v2)")
	birdEnabled       = flag.Bool("bird.ipv4", true, "Get protocols from bird (not compatible with -bird.v2)")
	bird6Enabled      = flag.Bool("bird.ipv6", true, "Get protocols from bird6 (not compatible with -bird.v2)")
	descriptionLabels = flag.Bool("format.description-labels", false, "Add labels from protocol descriptions.")
)

func init() {
	flag.Usage = func() {
		fmt.Println("Usage: bird_exporter [ ... ]\n\nParameters:")
		fmt.Println()
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()

	if *showVersion {
		printVersion()
		os.Exit(0)
	}

	startServer()
}

func printVersion() {
	fmt.Println("bird_exporter")
	fmt.Printf("Version: %s\n", version)
	fmt.Println("Author(s): Daniel Czerwonk")
	fmt.Println("Metric exporter for bird routing daemon")
}

func startServer() {
	log.Infof("Starting bird exporter (Version: %s)\n", version)

	if !*newFormat {
		log.Info("INFO: You are using the old metric format. Please consider using the new (more convenient one) by setting -format.new=true.")
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
			<title>Bird Routing Daemon Exporter (Version ` + version + `)</title>

Bird Routing Daemon Exporter

<a href="` + *metricsPath + `">Metrics

More information:

github.com/czerwonk/bird_exporter

			`))
	})
	http.HandleFunc(*metricsPath, handleMetricsRequest)

	log.Infof("Listening for %s on %s\n", *metricsPath, *listenAddress)
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func handleMetricsRequest(w http.ResponseWriter, r *http.Request) {
	reg := prometheus.NewRegistry()
	p := enabledProtocols()
	c := NewMetricCollector(*newFormat, p, *descriptionLabels)
	reg.MustRegister(c)

	promhttp.HandlerFor(reg, promhttp.HandlerOpts{
		ErrorLog:      log.NewErrorLogger(),
		ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(w, r)
}
func enabledProtocols() int {
	res := 0

	if *enableBgp {
		res |= protocol.BGP
	}
	if *enableOspf {
		res |= protocol.OSPF
	}
	if *enableKernel {
		res |= protocol.Kernel
	}
	if *enableStatic {
		res |= protocol.Static
	}
	if *enableDirect {
		res |= protocol.Direct
	}

	return res
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016, Simon J Mudd <sjmudd@pobox.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Package main searches for images given a path matching the specific filename
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/sjmudd/findphoto/log"
)

const myVersion = "0.0.4"

var (
	myName           string // program name taken from os.Args[0]
	cameraModel      string // e.g. Camera Model Name : Canon PowerShot S100
	searchFile       string // file containing photo names
	progressInterval int    // interval at which to give progress on the search
	version          bool   // show the program version
	symlinkDir       string // directory where to make symlinks
)

func init() {
	myName = os.Args[0]
	// Split at /
	stuff := strings.Split(myName, "/")
	if len(stuff) > 1 {
		myName = stuff[len(stuff)-1]
	}
}

// given a filename to collect names from return a list of names
func getFiles(filename string) []string {
	var filenames []string

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		entry := scanner.Text()
		// log.MsgVerbose("Entry: %s\n", entry)
		filenames = append(filenames, entry)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	return filenames
}

// showVersion shows the program version and exits
func showVersion() {
	fmt.Printf("%s version %s\n", myName, myVersion)
	os.Exit(0)
}

// usage returns a usage message and exits with the requested exit code
func usage(exitCode int) {
	log.Printf("Usage: %s <options> <directory_to_search>\n\n", myName)
	flag.PrintDefaults()

	os.Exit(exitCode)
}

func checkSymlinkDir(name string) {
	info, err := os.Stat(name)
	if err != nil {
		log.Fatal("Failed to stat symlink-dir %s: %v", name, err)
	}
	if !info.Mode().IsDir() {
		log.Fatal("symlinkdir %s is not a directory", name)
	}

	log.Printf("symlink dir: %s\n", symlinkDir)
}

func main() {
	// get options
	flag.BoolVar(&log.Verbose, "verbose", false, "Enable verbose logging")
	flag.StringVar(&searchFile, "search-file", "", "Required: File to use containing a line of the base filesnames to search for")
	flag.StringVar(&cameraModel, "camera-model", "", "camera model (in exif data e.g. 'Canon PowerShot S100'")
	flag.IntVar(&progressInterval, "progress-interval", 60, "time in verbose mode to give an indication of progress")
	flag.BoolVar(&version, "version", false, "shows the program version and exits")
	flag.StringVar(&symlinkDir, "symlink-dir", "", "directory to symlink found files against")
	flag.Parse()

	if version {
		showVersion()
	}
	// show the version when running in verbose mode
	log.Printf("%s version %s\n", myName, myVersion)

	if cameraModel != "" {
		log.Printf("camera-model: %s\n", cameraModel)
	}
	if symlinkDir != "" {
		checkSymlinkDir(symlinkDir)
	}
	if searchFile == "" {
		log.Printf("missing option --search-file=XXXX\n")
		usage(1)
	}
	if progressInterval <= 0 {
		log.Printf("--progress-interval should be a positive number of seconds\n")
		usage(1)
	}
	log.MsgVerbose("progress interval: %d\n", progressInterval)

	// check we have all needed parameters
	if len(flag.Args()) != 1 {
		log.Printf("Wrong number of parameters. Got %d, expected: %d\n", len(flag.Args()), 1)
		usage(1)
	}

	// [optionally] log what we are going to do
	filenames := getFiles(searchFile)
	log.MsgVerbose("Found %d filename(s) in %q\n", len(filenames), searchFile)

	searchPath := flag.Args()[0]
	log.MsgVerbose("Search path: %q\n", searchPath)

	search(searchPath, filenames)
}
<commit_msg>v0.0.5 - add --help and show copyright message<commit_after>/*
Copyright (c) 2016, Simon J Mudd <sjmudd@pobox.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Package main searches for images given a path matching the specific filename
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/sjmudd/findphoto/log"
)

const (
	copyright = "(C) 2016 Simon J Mudd <sjmudd@pobox.com>"
	myVersion = "0.0.5"
)

var (
	help             bool   // do we want help?
	myName           string // program name taken from os.Args[0]
	cameraModel      string // e.g. Camera Model Name : Canon PowerShot S100
	searchFile       string // file containing photo names
	progressInterval int    // interval at which to give progress on the search
	version          bool   // show the program version
	symlinkDir       string // directory where to make symlinks
)

func init() {
	myName = os.Args[0]
	// Split at /
	stuff := strings.Split(myName, "/")
	if len(stuff) > 1 {
		myName = stuff[len(stuff)-1]
	}
}

// given a filename to collect names from return a list of names
func getFiles(filename string) []string {
	var filenames []string

	file, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		entry := scanner.Text()
		// log.MsgVerbose("Entry: %s\n", entry)
		filenames = append(filenames, entry)
	}

	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
	return filenames
}

// showVersion shows the program version and exits
func showVersion() {
	fmt.Printf("%s version %s\n", myName, myVersion)
	os.Exit(0)
}

// usage returns a usage message and exits with the requested exit code
func usage(exitCode int) {
	log.Printf("%s %s\n\n", myName, copyright)
	log.Printf("Find photo files given various search criteria\n\n")
	log.Printf("Usage: %s <options> <directory_to_search>\n", myName)
	flag.PrintDefaults()

	os.Exit(exitCode)
}

func checkSymlinkDir(name string) {
	info, err := os.Stat(name)
	if err != nil {
		log.Fatal("Failed to stat symlink-dir %s: %v", name, err)
	}
	if !info.Mode().IsDir() {
		log.Fatal("symlinkdir %s is not a directory", name)
	}

	log.Printf("symlink dir: %s\n", symlinkDir)
}

func main() {
	// get options
	flag.BoolVar(&log.Verbose, "verbose", false, "Enable verbose logging")
	flag.StringVar(&searchFile, "search-file", "", "Required: File to use containing a line of the base filesnames to search for")
	flag.StringVar(&cameraModel, "camera-model", "", "camera model (in exif data e.g. 'Canon PowerShot S100'")
	flag.IntVar(&progressInterval, "progress-interval", 60, "time in verbose mode to give an indication of progress")
	flag.BoolVar(&help, "help", false, "shows this help message")
	flag.BoolVar(&version, "version", false, "shows the program version and exits")
	flag.StringVar(&symlinkDir, "symlink-dir", "", "directory to symlink found files against")
	flag.Parse()

	if help {
		usage(0)
	}
	if version {
		showVersion()
	}
	// show the version when running in verbose mode
	log.Printf("%s version %s\n", myName, myVersion)

	if cameraModel != "" {
		log.Printf("camera-model: %s\n", cameraModel)
	}
	if symlinkDir != "" {
		checkSymlinkDir(symlinkDir)
	}
	if searchFile == "" {
		log.Printf("missing option --search-file=XXXX\n")
		usage(1)
	}
	if progressInterval <= 0 {
		log.Printf("--progress-interval should be a positive number of seconds\n")
		usage(1)
	}
	log.MsgVerbose("progress interval: %d\n", progressInterval)

	// check we have all needed parameters
	if len(flag.Args()) != 1 {
		log.Printf("Wrong number of parameters. Got %d, expected: %d\n", len(flag.Args()), 1)
		usage(1)
	}

	// [optionally] log what we are going to do
	filenames := getFiles(searchFile)
	log.MsgVerbose("Found %d filename(s) in %q\n", len(filenames), searchFile)

	searchPath := flag.Args()[0]
	log.MsgVerbose("Search path: %q\n", searchPath)

	search(searchPath, filenames)
}
<|endoftext|>